From 9d0253b543b3b7b26e3643ebb6cf16dcda0330fb Mon Sep 17 00:00:00 2001 From: Blixibon Date: Mon, 21 Dec 2020 17:09:35 -0600 Subject: [PATCH] Initial Mapbase MP branch port --- mp/src/devtools/gcc9+support.cpp | 59 + mp/src/devtools/makefile_base_posix.mak | 481 +- .../devtools/makefile_base_posix.mak.default | 509 ++ mp/src/devtools/makefile_base_posix.mak.gcc8 | 507 ++ mp/src/fgdlib/gamedata.cpp | 98 + mp/src/game/client/C_Env_Projected_Texture.h | 100 + mp/src/game/client/c_baseanimating.cpp | 147 +- mp/src/game/client/c_baseanimating.h | 16 + mp/src/game/client/c_basecombatweapon.cpp | 73 +- mp/src/game/client/c_baseentity.cpp | 260 + mp/src/game/client/c_baseentity.h | 80 + mp/src/game/client/c_baseflex.cpp | 5 +- mp/src/game/client/c_baseflex.h | 2 +- mp/src/game/client/c_baselesson.cpp | 3894 +++++++++++++++ mp/src/game/client/c_baselesson.h | 460 ++ mp/src/game/client/c_baseplayer.cpp | 123 +- mp/src/game/client/c_baseplayer.h | 31 +- mp/src/game/client/c_effects.cpp | 539 +- mp/src/game/client/c_effects.h | 169 + mp/src/game/client/c_env_dof_controller.cpp | 88 + mp/src/game/client/c_env_global_light.cpp | 338 ++ mp/src/game/client/c_env_projectedtexture.cpp | 552 ++ mp/src/game/client/c_func_dust.cpp | 6 +- mp/src/game/client/c_func_lod.cpp | 13 + .../game/client/c_func_reflective_glass.cpp | 131 + mp/src/game/client/c_func_reflective_glass.h | 5 + mp/src/game/client/c_gameinstructor.cpp | 1312 +++++ mp/src/game/client/c_gameinstructor.h | 118 + mp/src/game/client/c_lightglow.cpp | 11 + mp/src/game/client/c_particle_system.cpp | 15 + mp/src/game/client/c_pixel_visibility.cpp | 2 + mp/src/game/client/c_playerlocaldata.h | 3 + mp/src/game/client/c_point_camera.cpp | 45 + mp/src/game/client/c_point_camera.h | 37 + .../game/client/c_postprocesscontroller.cpp | 63 + mp/src/game/client/c_postprocesscontroller.h | 33 + mp/src/game/client/c_rope.cpp | 543 +- mp/src/game/client/c_rope.h | 18 + mp/src/game/client/c_sceneentity.cpp | 75 +- mp/src/game/client/c_shadowcontrol.cpp | 14 + mp/src/game/client/c_soundscape.cpp | 22 + mp/src/game/client/c_soundscape.h | 4 + mp/src/game/client/c_te_effect_dispatch.cpp | 4 + mp/src/game/client/c_te_effect_dispatch.h | 1 + mp/src/game/client/c_te_largefunnel.cpp | 5 + mp/src/game/client/c_te_legacytempents.cpp | 9 + mp/src/game/client/c_team_train_watcher.cpp | 2 +- .../game/client/c_vehicle_choreo_generic.cpp | 8 + mp/src/game/client/c_world.cpp | 15 + mp/src/game/client/c_world.h | 10 + mp/src/game/client/cbase.h | 4 + mp/src/game/client/cdll_client_int.cpp | 60 + mp/src/game/client/cdll_client_int.h | 15 + mp/src/game/client/cdll_util.cpp | 43 +- mp/src/game/client/cdll_util.h | 13 +- mp/src/game/client/client_base.vpc | 8 + mp/src/game/client/client_episodic.vpc | 132 + mp/src/game/client/client_mapbase.vpc | 82 + mp/src/game/client/clientleafsystem.cpp | 2 + mp/src/game/client/clientleafsystem.h | 4 + mp/src/game/client/clientmode_shared.cpp | 144 + mp/src/game/client/clientmode_shared.h | 15 + mp/src/game/client/clientshadowmgr.cpp | 584 +++ mp/src/game/client/detailobjectsystem.cpp | 31 + mp/src/game/client/flashlighteffect.cpp | 15 + mp/src/game/client/fx.cpp | 7 + mp/src/game/client/fx_tracer.cpp | 12 +- mp/src/game/client/hl2/c_basehlplayer.cpp | 10 + mp/src/game/client/hl2/c_basehlplayer.h | 10 +- mp/src/game/client/hl2/c_env_starfield.cpp | 5 + mp/src/game/client/hl2/c_script_intro.cpp | 175 + .../game/client/hl2/c_weapon__stubs_hl2.cpp | 2 + mp/src/game/client/hl2/hud_credits.cpp | 64 + mp/src/game/client/hl2/hud_suitpower.cpp | 68 + mp/src/game/client/hud.cpp | 240 +- mp/src/game/client/hud.h | 31 + mp/src/game/client/hud_closecaption.cpp | 19 +- mp/src/game/client/hud_hintdisplay.cpp | 29 + mp/src/game/client/hud_locator_target.cpp | 2207 ++++++++ mp/src/game/client/hud_locator_target.h | 184 + mp/src/game/client/iclientshadowmgr.h | 9 + mp/src/game/client/in_main.cpp | 3 + .../game/client/mapbase/c_func_clientclip.cpp | 93 + .../mapbase/c_func_fake_worldportal.cpp | 226 + .../client/mapbase/c_func_fake_worldportal.h | 64 + mp/src/game/client/mapbase/c_point_glow.cpp | 103 + mp/src/game/client/message.cpp | 79 + mp/src/game/client/panelmetaclassmgr.cpp | 8 + mp/src/game/client/particles_simple.cpp | 4 + mp/src/game/client/prediction.cpp | 13 + mp/src/game/client/vgui_video.cpp | 7 +- mp/src/game/client/view.cpp | 12 + mp/src/game/client/viewpostprocess.cpp | 627 ++- mp/src/game/client/viewpostprocess.h | 14 +- mp/src/game/client/viewrender.cpp | 720 ++- mp/src/game/client/viewrender.h | 25 + mp/src/game/client/vscript_client.cpp | 615 +++ mp/src/game/client/vscript_client.h | 26 + mp/src/game/client/vscript_client.nut | 22 + mp/src/game/client/weapon_selection.cpp | 9 +- mp/src/game/client/worldlight.cpp | 314 ++ mp/src/game/client/worldlight.h | 50 + mp/src/game/server/AI_Criteria.cpp | 68 +- mp/src/game/server/AI_Criteria.h | 41 + mp/src/game/server/AI_ResponseSystem.cpp | 548 ++ mp/src/game/server/BasePropDoor.h | 8 + mp/src/game/server/CRagdollMagnet.cpp | 45 + mp/src/game/server/CRagdollMagnet.h | 13 + mp/src/game/server/CommentarySystem.cpp | 11 + mp/src/game/server/EnvBeam.cpp | 64 + mp/src/game/server/EnvFade.cpp | 14 + mp/src/game/server/EnvHudHint.cpp | 15 + mp/src/game/server/EnvLaser.cpp | 10 + mp/src/game/server/EnvLaser.h | 7 + mp/src/game/server/EnvMessage.cpp | 21 + mp/src/game/server/RagdollBoogie.cpp | 210 + mp/src/game/server/RagdollBoogie.h | 12 + mp/src/game/server/SkyCamera.cpp | 315 ++ mp/src/game/server/SkyCamera.h | 56 + mp/src/game/server/TemplateEntities.cpp | 74 + mp/src/game/server/ai_activity.cpp | 90 +- mp/src/game/server/ai_baseactor.cpp | 133 +- mp/src/game/server/ai_baseactor.h | 22 + mp/src/game/server/ai_basenpc.cpp | 1931 ++++++- mp/src/game/server/ai_basenpc.h | 246 + mp/src/game/server/ai_basenpc_schedule.cpp | 119 +- mp/src/game/server/ai_behavior.cpp | 11 + mp/src/game/server/ai_behavior.h | 51 + mp/src/game/server/ai_behavior_assault.cpp | 5 + mp/src/game/server/ai_behavior_fear.cpp | 157 + mp/src/game/server/ai_behavior_fear.h | 42 + mp/src/game/server/ai_behavior_follow.cpp | 27 + mp/src/game/server/ai_behavior_follow.h | 3 + mp/src/game/server/ai_behavior_lead.cpp | 14 + mp/src/game/server/ai_behavior_lead.h | 4 + mp/src/game/server/ai_behavior_rappel.cpp | 14 + mp/src/game/server/ai_behavior_standoff.cpp | 17 + mp/src/game/server/ai_concommands.cpp | 290 ++ mp/src/game/server/ai_default.cpp | 66 + mp/src/game/server/ai_dynamiclink.cpp | 394 ++ mp/src/game/server/ai_dynamiclink.h | 10 + mp/src/game/server/ai_goalentity.cpp | 9 + mp/src/game/server/ai_goalentity.h | 3 + mp/src/game/server/ai_hint.cpp | 75 + mp/src/game/server/ai_hint.h | 20 + mp/src/game/server/ai_memory.cpp | 23 + mp/src/game/server/ai_memory.h | 23 + mp/src/game/server/ai_moveprobe.cpp | 6 + mp/src/game/server/ai_network.cpp | 50 + mp/src/game/server/ai_network.h | 11 + mp/src/game/server/ai_networkmanager.cpp | 19 + mp/src/game/server/ai_pathfinder.cpp | 18 + mp/src/game/server/ai_playerally.cpp | 149 + mp/src/game/server/ai_playerally.h | 21 + mp/src/game/server/ai_relationship.cpp | 157 + mp/src/game/server/ai_scriptconditions.cpp | 35 + mp/src/game/server/ai_scriptconditions.h | 3 + mp/src/game/server/ai_speech.cpp | 369 +- mp/src/game/server/ai_speech.h | 53 + mp/src/game/server/ai_squad.cpp | 123 +- mp/src/game/server/ai_squad.h | 38 + mp/src/game/server/ai_task.cpp | 3 + mp/src/game/server/ai_task.h | 5 + mp/src/game/server/baseanimating.cpp | 313 ++ mp/src/game/server/baseanimating.h | 46 + mp/src/game/server/basebludgeonweapon.cpp | 25 + mp/src/game/server/basecombatcharacter.cpp | 1213 +++++ mp/src/game/server/basecombatcharacter.h | 113 +- mp/src/game/server/basecombatweapon.cpp | 13 + mp/src/game/server/baseentity.cpp | 3158 +++++++++++- mp/src/game/server/baseentity.h | 391 +- mp/src/game/server/baseflex.cpp | 173 +- mp/src/game/server/baseflex.h | 22 + mp/src/game/server/bmodels.cpp | 48 +- mp/src/game/server/buttons.cpp | 7 + mp/src/game/server/cbase.cpp | 343 +- mp/src/game/server/cbase.h | 3 + mp/src/game/server/client.cpp | 40 + mp/src/game/server/effects.cpp | 253 +- mp/src/game/server/enginecallback.h | 2 + mp/src/game/server/entitylist.cpp | 82 +- mp/src/game/server/entitylist.h | 18 + mp/src/game/server/entityoutput.h | 30 + mp/src/game/server/env_dof_controller.cpp | 186 + mp/src/game/server/env_dof_controller.h | 56 + mp/src/game/server/env_entity_maker.cpp | 109 + mp/src/game/server/env_global_light.cpp | 336 ++ mp/src/game/server/env_instructor_hint.cpp | 229 + mp/src/game/server/env_projectedtexture.cpp | 521 ++ mp/src/game/server/env_projectedtexture.h | 119 + mp/src/game/server/env_tonemap_controller.cpp | 123 + mp/src/game/server/env_zoom.cpp | 34 + mp/src/game/server/envmicrophone.cpp | 75 + mp/src/game/server/envmicrophone.h | 11 + .../ai_behavior_passenger_companion.cpp | 4 + mp/src/game/server/episodic/npc_hunter.cpp | 69 + .../server/episodic/vehicle_jeep_episodic.cpp | 37 + .../server/episodic/vehicle_jeep_episodic.h | 10 + mp/src/game/server/eventqueue.h | 15 +- mp/src/game/server/explode.cpp | 24 + mp/src/game/server/filters.cpp | 1684 ++++++- mp/src/game/server/filters.h | 74 + mp/src/game/server/fire.cpp | 75 +- mp/src/game/server/fish.cpp | 98 + mp/src/game/server/fish.h | 15 + mp/src/game/server/fogcontroller.cpp | 12 + .../game/server/fourwheelvehiclephysics.cpp | 33 + mp/src/game/server/func_areaportal.cpp | 199 + mp/src/game/server/func_areaportalbase.h | 4 + mp/src/game/server/func_break.cpp | 7 +- mp/src/game/server/func_breakablesurf.cpp | 11 + mp/src/game/server/func_lod.cpp | 15 + mp/src/game/server/func_movelinear.cpp | 82 +- mp/src/game/server/func_movelinear.h | 9 + mp/src/game/server/func_reflective_glass.cpp | 31 + mp/src/game/server/game_ui.cpp | 139 + mp/src/game/server/gameinterface.cpp | 157 +- mp/src/game/server/gameinterface.h | 17 + mp/src/game/server/genericactor.cpp | 175 + mp/src/game/server/genericmonster.cpp | 100 + mp/src/game/server/globalstate.cpp | 34 + .../game/server/hl2/ai_behavior_actbusy.cpp | 224 + mp/src/game/server/hl2/ai_behavior_actbusy.h | 34 + .../game/server/hl2/ai_behavior_functank.cpp | 61 + mp/src/game/server/hl2/ai_behavior_functank.h | 15 + mp/src/game/server/hl2/ai_behavior_police.cpp | 65 + mp/src/game/server/hl2/ai_behavior_police.h | 3 + mp/src/game/server/hl2/cbasehelicopter.cpp | 173 + mp/src/game/server/hl2/cbasehelicopter.h | 4 + mp/src/game/server/hl2/combine_mine.cpp | 295 +- mp/src/game/server/hl2/combine_mine.h | 41 + .../game/server/hl2/env_headcrabcanister.cpp | 27 + mp/src/game/server/hl2/env_speaker.cpp | 105 + mp/src/game/server/hl2/env_speaker.h | 11 + mp/src/game/server/hl2/func_recharge.cpp | 146 +- mp/src/game/server/hl2/func_tank.cpp | 942 +++- mp/src/game/server/hl2/func_tank.h | 63 +- mp/src/game/server/hl2/grenade_ar2.cpp | 11 + mp/src/game/server/hl2/grenade_bugbait.cpp | 5 + mp/src/game/server/hl2/grenade_frag.cpp | 26 + mp/src/game/server/hl2/grenade_spit.cpp | 12 + mp/src/game/server/hl2/hl2_client.cpp | 15 + mp/src/game/server/hl2/hl2_player.cpp | 1157 +++++ mp/src/game/server/hl2/hl2_player.h | 70 + mp/src/game/server/hl2/hl2_triggers.cpp | 50 + mp/src/game/server/hl2/item_ammo.cpp | 117 +- .../game/server/hl2/item_dynamic_resupply.cpp | 32 + mp/src/game/server/hl2/item_healthkit.cpp | 179 +- mp/src/game/server/hl2/item_itemcrate.cpp | 244 + mp/src/game/server/hl2/item_suit.cpp | 4 + mp/src/game/server/hl2/npc_BaseZombie.cpp | 140 +- mp/src/game/server/hl2/npc_BaseZombie.h | 8 + mp/src/game/server/hl2/npc_PoisonZombie.cpp | 10 + mp/src/game/server/hl2/npc_alyx.cpp | 8 + mp/src/game/server/hl2/npc_alyx.h | 7 + mp/src/game/server/hl2/npc_alyx_episodic.cpp | 103 +- mp/src/game/server/hl2/npc_alyx_episodic.h | 5 + mp/src/game/server/hl2/npc_antlion.cpp | 110 + mp/src/game/server/hl2/npc_antlion.h | 3 + mp/src/game/server/hl2/npc_antliongrub.cpp | 24 + mp/src/game/server/hl2/npc_antlionguard.cpp | 43 + mp/src/game/server/hl2/npc_attackchopper.cpp | 99 + mp/src/game/server/hl2/npc_barnacle.cpp | 88 +- mp/src/game/server/hl2/npc_barnacle.h | 20 + mp/src/game/server/hl2/npc_barney.cpp | 7 + mp/src/game/server/hl2/npc_basescanner.cpp | 17 + mp/src/game/server/hl2/npc_basescanner.h | 5 + mp/src/game/server/hl2/npc_bullsquid.cpp | 14 +- mp/src/game/server/hl2/npc_citizen17.cpp | 466 ++ mp/src/game/server/hl2/npc_citizen17.h | 45 + mp/src/game/server/hl2/npc_combine.cpp | 632 ++- mp/src/game/server/hl2/npc_combine.h | 82 + mp/src/game/server/hl2/npc_combinecamera.cpp | 12 + .../game/server/hl2/npc_combinedropship.cpp | 152 + mp/src/game/server/hl2/npc_combinegunship.cpp | 36 + mp/src/game/server/hl2/npc_combines.cpp | 21 + mp/src/game/server/hl2/npc_combines.h | 2 + mp/src/game/server/hl2/npc_enemyfinder.cpp | 17 + mp/src/game/server/hl2/npc_fastzombie.cpp | 28 + mp/src/game/server/hl2/npc_headcrab.cpp | 29 + mp/src/game/server/hl2/npc_headcrab.h | 4 + mp/src/game/server/hl2/npc_launcher.cpp | 20 + mp/src/game/server/hl2/npc_manhack.cpp | 99 + mp/src/game/server/hl2/npc_manhack.h | 16 + mp/src/game/server/hl2/npc_metropolice.cpp | 1022 +++- mp/src/game/server/hl2/npc_metropolice.h | 86 + mp/src/game/server/hl2/npc_monk.cpp | 24 + .../game/server/hl2/npc_playercompanion.cpp | 757 ++- mp/src/game/server/hl2/npc_playercompanion.h | 73 +- mp/src/game/server/hl2/npc_rollermine.cpp | 11 + mp/src/game/server/hl2/npc_scanner.cpp | 75 + mp/src/game/server/hl2/npc_scanner.h | 7 + mp/src/game/server/hl2/npc_stalker.cpp | 69 + mp/src/game/server/hl2/npc_stalker.h | 13 + mp/src/game/server/hl2/npc_strider.cpp | 77 + mp/src/game/server/hl2/npc_strider.h | 16 + mp/src/game/server/hl2/npc_turret_ceiling.cpp | 627 ++- mp/src/game/server/hl2/npc_turret_floor.cpp | 66 + mp/src/game/server/hl2/npc_turret_floor.h | 17 + mp/src/game/server/hl2/npc_turret_ground.cpp | 2 + .../server/hl2/npc_vortigaunt_episodic.cpp | 27 + mp/src/game/server/hl2/npc_zombie.cpp | 291 ++ mp/src/game/server/hl2/npc_zombine.cpp | 19 + mp/src/game/server/hl2/prop_combine_ball.cpp | 151 +- mp/src/game/server/hl2/prop_combine_ball.h | 4 + mp/src/game/server/hl2/proto_sniper.cpp | 235 + mp/src/game/server/hl2/script_intro.cpp | 270 +- mp/src/game/server/hl2/script_intro.h | 10 + mp/src/game/server/hl2/vehicle_airboat.cpp | 2 + mp/src/game/server/hl2/vehicle_jeep.cpp | 30 + mp/src/game/server/hl2/vehicle_jeep.h | 4 + mp/src/game/server/hl2/weapon_357.cpp | 113 + mp/src/game/server/hl2/weapon_annabelle.cpp | 3 + mp/src/game/server/hl2/weapon_ar2.cpp | 83 + mp/src/game/server/hl2/weapon_bugbait.cpp | 8 + mp/src/game/server/hl2/weapon_cguard.cpp | 48 + mp/src/game/server/hl2/weapon_crossbow.cpp | 292 ++ mp/src/game/server/hl2/weapon_flaregun.cpp | 23 + mp/src/game/server/hl2/weapon_physcannon.cpp | 45 + mp/src/game/server/hl2/weapon_physcannon.h | 7 + mp/src/game/server/hl2/weapon_pistol.cpp | 38 + mp/src/game/server/hl2/weapon_rpg.cpp | 47 + mp/src/game/server/hl2/weapon_rpg.h | 11 + mp/src/game/server/hl2/weapon_shotgun.cpp | 12 + mp/src/game/server/hl2/weapon_smg1.cpp | 71 + mp/src/game/server/hl2/weapon_stunstick.h | 13 + mp/src/game/server/hl2mp/grenade_tripmine.cpp | 94 + mp/src/game/server/hl2mp/grenade_tripmine.h | 18 + mp/src/game/server/item_world.cpp | 67 + mp/src/game/server/items.h | 21 + mp/src/game/server/lightglow.cpp | 16 + mp/src/game/server/logic_measure_movement.cpp | 646 ++- mp/src/game/server/logic_random_outputs.cpp | 221 + mp/src/game/server/logic_random_outputs.h | 54 + mp/src/game/server/logicentities.cpp | 4430 +++++++++++++++++ mp/src/game/server/logicrelay.cpp | 318 ++ mp/src/game/server/logicrelay.h | 77 + mp/src/game/server/mapbase/GlobalStrings.cpp | 100 + mp/src/game/server/mapbase/GlobalStrings.h | 92 + .../game/server/mapbase/SystemConvarMod.cpp | 352 ++ mp/src/game/server/mapbase/SystemConvarMod.h | 49 + mp/src/game/server/mapbase/ai_grenade.cpp | 13 + mp/src/game/server/mapbase/ai_grenade.h | 662 +++ mp/src/game/server/mapbase/ai_monitor.cpp | 733 +++ .../game/server/mapbase/ai_weaponmodifier.cpp | 250 + .../server/mapbase/closecaption_entity.cpp | 81 + mp/src/game/server/mapbase/datadesc_mod.cpp | 199 + mp/src/game/server/mapbase/datadesc_mod.h | 11 + .../game/server/mapbase/expandedrs_combine.h | 173 + .../game/server/mapbase/func_clientclip.cpp | 191 + .../server/mapbase/func_fake_worldportal.cpp | 100 + .../server/mapbase/logic_eventlistener.cpp | 270 + .../server/mapbase/logic_externaldata.cpp | 363 ++ .../mapbase/logic_register_activator.cpp | 145 + mp/src/game/server/mapbase/logic_skill.cpp | 66 + .../server/mapbase/point_advanced_finder.cpp | 393 ++ .../game/server/mapbase/point_copy_size.cpp | 143 + .../game/server/mapbase/point_damageinfo.cpp | 394 ++ .../server/mapbase/point_entity_replace.cpp | 496 ++ mp/src/game/server/mapbase/point_glow.cpp | 73 + .../game/server/mapbase/point_projectile.cpp | 197 + .../server/mapbase/point_radiation_source.cpp | 140 + mp/src/game/server/mapbase/variant_tools.h | 43 + mp/src/game/server/mapentities.cpp | 4 +- mp/src/game/server/maprules.cpp | 89 + mp/src/game/server/message_entity.cpp | 149 + mp/src/game/server/monstermaker.cpp | 12 + mp/src/game/server/npc_talker.cpp | 4 + mp/src/game/server/npc_vehicledriver.cpp | 6 + mp/src/game/server/particle_system.cpp | 23 + mp/src/game/server/particle_system.h | 6 + mp/src/game/server/pathtrack.cpp | 6 + mp/src/game/server/physconstraint.cpp | 17 + mp/src/game/server/physics.cpp | 49 +- mp/src/game/server/physics_impact_damage.cpp | 48 +- mp/src/game/server/physics_prop_ragdoll.cpp | 137 +- mp/src/game/server/physics_prop_ragdoll.h | 18 + mp/src/game/server/physobj.cpp | 206 + mp/src/game/server/physobj.h | 12 + mp/src/game/server/player.cpp | 791 ++- mp/src/game/server/player.h | 81 +- mp/src/game/server/player_command.cpp | 41 +- mp/src/game/server/playerinfomanager.cpp | 93 +- mp/src/game/server/playerinfomanager.h | 6 + mp/src/game/server/playerlocaldata.cpp | 47 + mp/src/game/server/playerlocaldata.h | 4 + mp/src/game/server/point_camera.cpp | 233 + mp/src/game/server/point_camera.h | 57 + mp/src/game/server/point_devshot_camera.cpp | 22 +- mp/src/game/server/point_entity_finder.cpp | 207 + mp/src/game/server/point_spotlight.cpp | 19 + mp/src/game/server/point_template.cpp | 209 +- mp/src/game/server/point_template.h | 23 + mp/src/game/server/pointhurt.cpp | 22 + mp/src/game/server/pointteleport.cpp | 87 + mp/src/game/server/postprocesscontroller.cpp | 207 + mp/src/game/server/postprocesscontroller.h | 80 + mp/src/game/server/props.cpp | 861 ++++ mp/src/game/server/props.h | 49 + mp/src/game/server/rope.cpp | 122 + mp/src/game/server/rope.h | 49 + mp/src/game/server/saverestore_gamedll.cpp | 13 + mp/src/game/server/sceneentity.cpp | 699 ++- mp/src/game/server/sceneentity.h | 16 + mp/src/game/server/scripted.cpp | 320 +- mp/src/game/server/scripted.h | 17 + mp/src/game/server/server_base.vpc | 8 + mp/src/game/server/server_mapbase.vpc | 106 + mp/src/game/server/shadowcontrol.cpp | 20 + mp/src/game/server/skyboxswapper.cpp | 80 + mp/src/game/server/sound.cpp | 126 + mp/src/game/server/soundent.cpp | 71 + mp/src/game/server/soundent.h | 16 + mp/src/game/server/soundscape_system.cpp | 10 + mp/src/game/server/subs.cpp | 5 + mp/src/game/server/triggers.cpp | 721 ++- mp/src/game/server/triggers.h | 144 + mp/src/game/server/util.cpp | 231 +- mp/src/game/server/util.h | 57 +- mp/src/game/server/variant_t.cpp | 265 + mp/src/game/server/variant_t.h | 42 + mp/src/game/server/vehicle_base.cpp | 145 + mp/src/game/server/vehicle_base.h | 23 + mp/src/game/server/vehicle_choreo_generic.cpp | 12 + mp/src/game/server/vscript_server.cpp | 1390 ++++++ mp/src/game/server/vscript_server.h | 59 + mp/src/game/server/vscript_server.nut | 171 + mp/src/game/server/wcedit.cpp | 16 + mp/src/game/server/world.cpp | 57 +- mp/src/game/server/world.h | 27 + .../Multiplayer/multiplayer_animstate.h | 9 +- mp/src/game/shared/SoundEmitterSystem.cpp | 61 + mp/src/game/shared/achievementmgr.cpp | 11 + mp/src/game/shared/activitylist.cpp | 44 + mp/src/game/shared/ai_activity.h | 86 + mp/src/game/shared/ammodef.cpp | 35 + mp/src/game/shared/ammodef.h | 8 + .../shared/basecombatcharacter_shared.cpp | 14 + .../game/shared/basecombatweapon_shared.cpp | 436 ++ mp/src/game/shared/basecombatweapon_shared.h | 135 + mp/src/game/shared/baseentity_shared.cpp | 51 + mp/src/game/shared/baseentity_shared.h | 14 + mp/src/game/shared/basegrenade_shared.cpp | 62 + mp/src/game/shared/basegrenade_shared.h | 25 + mp/src/game/shared/baseplayer_shared.cpp | 36 +- mp/src/game/shared/baseplayer_shared.h | 14 + mp/src/game/shared/baseviewmodel_shared.cpp | 40 +- mp/src/game/shared/baseviewmodel_shared.h | 4 + mp/src/game/shared/beam_shared.cpp | 2 + mp/src/game/shared/choreoevent.cpp | 2 + mp/src/game/shared/choreoevent.h | 6 + mp/src/game/shared/choreoscene.h | 3 + mp/src/game/shared/env_wind_shared.cpp | 130 +- mp/src/game/shared/env_wind_shared.h | 37 + mp/src/game/shared/eventlist.cpp | 5 + mp/src/game/shared/eventlist.h | 5 + mp/src/game/shared/func_ladder.cpp | 35 + mp/src/game/shared/func_ladder.h | 5 + mp/src/game/shared/gamemovement.cpp | 19 +- mp/src/game/shared/gamemovement.h | 7 + mp/src/game/shared/gamerules.cpp | 85 + mp/src/game/shared/gamerules.h | 10 + mp/src/game/shared/gamestringpool.cpp | 13 +- mp/src/game/shared/hl2/hl2_gamerules.cpp | 334 ++ mp/src/game/shared/hl2/hl2_gamerules.h | 62 + mp/src/game/shared/hl2/hl2_usermessages.cpp | 5 + mp/src/game/shared/hl2/hl_gamemovement.cpp | 152 + mp/src/game/shared/hl2/hl_gamemovement.h | 9 + .../game/shared/hl2mp/weapon_physcannon.cpp | 13 + mp/src/game/shared/hl2mp/weapon_slam.cpp | 23 + mp/src/game/shared/hl2mp/weapon_slam.h | 8 + mp/src/game/shared/hl2mp/weapon_smg1.cpp | 13 + mp/src/game/shared/hl2mp/weapon_stunstick.cpp | 192 +- mp/src/game/shared/hl2mp/weapon_stunstick.h | 116 + mp/src/game/shared/igamesystem.cpp | 9 + mp/src/game/shared/igamesystem.h | 7 + mp/src/game/shared/in_buttons.h | 9 + mp/src/game/shared/mapbase/MapEdit.cpp | 897 ++++ mp/src/game/shared/mapbase/MapEdit.h | 14 + .../game/shared/mapbase/mapbase_game_log.cpp | 243 + mp/src/game/shared/mapbase/mapbase_rpc.cpp | 606 +++ mp/src/game/shared/mapbase/mapbase_shared.cpp | 618 +++ mp/src/game/shared/mapbase/matchers.cpp | 239 + mp/src/game/shared/mapbase/matchers.h | 68 + .../shared/mapbase/vscript_consts_shared.cpp | 512 ++ .../shared/mapbase/vscript_consts_weapons.cpp | 98 + .../game/shared/mapbase/vscript_funcs_hl2.cpp | 63 + .../shared/mapbase/vscript_funcs_math.cpp | 308 ++ .../shared/mapbase/vscript_funcs_shared.cpp | 861 ++++ .../shared/mapbase/vscript_funcs_shared.h | 124 + .../shared/mapbase/vscript_singletons.cpp | 1356 +++++ .../game/shared/mapbase/vscript_singletons.h | 16 + .../shared/mapbase/weapon_custom_scripted.cpp | 591 +++ .../shared/mapbase/weapon_custom_scripted.h | 201 + mp/src/game/shared/multiplay_gamerules.cpp | 7 +- mp/src/game/shared/physics_shared.cpp | 4 + mp/src/game/shared/playernet_vars.h | 55 + mp/src/game/shared/point_posecontroller.cpp | 7 + mp/src/game/shared/point_posecontroller.h | 4 + mp/src/game/shared/postprocess_shared.h | 54 + mp/src/game/shared/precipitation_shared.h | 21 + mp/src/game/shared/predicted_viewmodel.cpp | 74 +- mp/src/game/shared/predicted_viewmodel.h | 1 + mp/src/game/shared/props_shared.h | 3 + mp/src/game/shared/ragdoll_shared.cpp | 36 +- mp/src/game/shared/sceneentity_shared.cpp | 6 +- mp/src/game/shared/sceneentity_shared.h | 3 + mp/src/game/shared/shareddefs.h | 107 +- mp/src/game/shared/takedamageinfo.cpp | 120 + mp/src/game/shared/takedamageinfo.h | 17 + mp/src/game/shared/teamplay_round_timer.cpp | 4 +- mp/src/game/shared/usercmd.h | 33 + mp/src/game/shared/util_shared.cpp | 114 + mp/src/game/shared/util_shared.h | 27 + mp/src/game/shared/vscript_shared.cpp | 276 + mp/src/game/shared/vscript_shared.h | 39 + mp/src/game/shared/weapon_parse.cpp | 56 + mp/src/game/shared/weapon_parse.h | 10 + mp/src/game/shared/weapon_proficiency.h | 5 + mp/src/lib/public/fgdlib.lib | Bin 2672696 -> 2777804 bytes mp/src/lib/public/tier1.lib | Bin 8047502 -> 8326086 bytes .../stdshaders/BaseVSShader.cpp | 199 +- .../materialsystem/stdshaders/BaseVSShader.h | 23 +- .../stdshaders/MonitorScreen_dx9.cpp | 182 + .../stdshaders/SDK_BlurFilter_ps2x.fxc | 91 + .../stdshaders/SDK_Refract_vs20.fxc | 140 + .../stdshaders/SDK_ShatteredGlass_ps2x.fxc | 159 + .../stdshaders/SDK_ShatteredGlass_vs20.fxc | 67 + .../stdshaders/SDK_WaterCheap_ps2x.fxc | 151 + .../stdshaders/SDK_WaterCheap_vs20.fxc | 87 + .../stdshaders/SDK_Water_vs20.fxc | 118 + .../SDK_WorldVertexTransition_ps2x.fxc | 47 + .../SDK_WorldVertexTransition_vs20.fxc | 64 + .../stdshaders/SDK_cable_ps2x.fxc | 54 + .../stdshaders/SDK_cable_vs20.fxc | 80 + .../SDK_cloak_blended_pass_ps2x.fxc | 106 + .../SDK_cloak_blended_pass_vs20.fxc | 132 + .../stdshaders/SDK_decalmodulate_ps2x.fxc | 101 + .../stdshaders/SDK_decalmodulate_vs20.fxc | 155 + .../stdshaders/SDK_depthwrite_ps2x.fxc | 44 + .../stdshaders/SDK_depthwrite_vs20.fxc | 121 + .../SDK_emissive_scroll_blended_pass_ps2x.fxc | 51 + .../SDK_emissive_scroll_blended_pass_vs20.fxc | 68 + .../stdshaders/SDK_engine_post_ps20b.fxc | 394 ++ .../stdshaders/SDK_eye_refract_ps2x.fxc | 494 ++ .../stdshaders/SDK_eye_refract_vs20.fxc | 217 + .../stdshaders/SDK_eyes_flashlight_inc.fxc | 92 + .../stdshaders/SDK_eyes_flashlight_ps11.fxc | 9 + .../stdshaders/SDK_eyes_flashlight_ps2x.fxc | 15 + .../stdshaders/SDK_eyes_flashlight_vs20.fxc | 145 + .../stdshaders/SDK_eyes_ps2x.fxc | 68 + .../stdshaders/SDK_eyes_vs20.fxc | 145 + .../stdshaders/SDK_flashlight_ps11.fxc | 69 + .../stdshaders/SDK_flashlight_ps2x.fxc | 238 + .../SDK_flesh_interior_blended_pass_ps2x.fxc | 127 + .../SDK_flesh_interior_blended_pass_vs20.fxc | 155 + .../SDK_lightmappedgeneric_decal_ps2x.fxc | 59 + .../SDK_lightmappedgeneric_decal_vs20.fxc | 74 + ...SDK_lightmappedgeneric_flashlight_ps2x.fxc | 330 ++ ...SDK_lightmappedgeneric_flashlight_vs11.fxc | 122 + ...SDK_lightmappedgeneric_flashlight_vs20.fxc | 217 + ...dgeneric_lightingonly_overbright2_ps11.fxc | 12 + ...K_lightmappedgeneric_lightingonly_vs11.fxc | 51 + .../SDK_lightmappedgeneric_ps11.fxc | 122 + .../SDK_lightmappedgeneric_ps20b.fxc | 51 + .../SDK_lightmappedgeneric_ps2x.fxc | 59 + .../SDK_lightmappedgeneric_vs20.fxc | 268 + .../SDK_lightmappedreflective_ps2x.fxc | 189 + .../SDK_lightmappedreflective_vs20.fxc | 105 + .../stdshaders/SDK_monitorscreen_ps2x.fxc | 55 + .../stdshaders/SDK_refract_ps2x.fxc | 250 + .../stdshaders/SDK_skin_ps20b.fxc | 432 ++ .../stdshaders/SDK_skin_vs20.fxc | 173 + .../stdshaders/SDK_splinerope_ps2x.fxc | 52 + .../stdshaders/SDK_splinerope_vs20.fxc | 78 + .../stdshaders/SDK_sprite_ps2x.fxc | 61 + .../stdshaders/SDK_sprite_vs20.fxc | 65 + .../stdshaders/SDK_teeth_bump_ps2x.fxc | 101 + .../stdshaders/SDK_teeth_bump_vs20.fxc | 152 + .../stdshaders/SDK_teeth_flashlight_ps2x.fxc | 66 + .../stdshaders/SDK_teeth_flashlight_vs20.fxc | 149 + .../stdshaders/SDK_teeth_ps2x.fxc | 48 + .../stdshaders/SDK_teeth_vs20.fxc | 127 + .../stdshaders/SDK_unlitgeneric_ps11.fxc | 12 + .../stdshaders/SDK_unlitgeneric_ps2x.fxc | 19 + .../stdshaders/SDK_unlitgeneric_vs20.fxc | 91 + .../stdshaders/SDK_unlittwotexture_ps2x.fxc | 59 + .../stdshaders/SDK_unlittwotexture_vs20.fxc | 66 + ...vertexlit_and_unlit_generic_bump_ps20b.fxc | 381 ++ ..._vertexlit_and_unlit_generic_bump_ps2x.fxc | 372 ++ ..._vertexlit_and_unlit_generic_bump_vs20.fxc | 199 + .../SDK_vertexlit_and_unlit_generic_ps20b.fxc | 503 ++ .../SDK_vertexlit_and_unlit_generic_ps2x.fxc | 511 ++ .../SDK_vertexlit_and_unlit_generic_vs20.fxc | 293 ++ .../SDK_vertexlit_lighting_only_ps2x.fxc | 68 + ...tgeneric_lightingonly_overbright2_ps11.fxc | 9 + .../stdshaders/SDK_water_ps2x.fxc | 113 + .../stdshaders/SDK_windowimposter_ps2x.fxc | 64 + .../stdshaders/SDK_windowimposter_vs20.fxc | 61 + .../SDK_worldtwotextureblend_ps2x.fxc | 217 + .../stdshaders/blurgaussian_3x3_ps2x.fxc | 22 + .../stdshaders/buildhl2mpshaders.bat | 5 +- .../stdshaders/buildsdkshaders.bat | 2 +- .../stdshaders/buildshaders.bat | 2 +- .../materialsystem/stdshaders/cable_dx9.cpp | 36 +- .../stdshaders/cloak_blended_pass_helper.cpp | 358 ++ .../stdshaders/cloak_blended_pass_helper.h | 46 + .../stdshaders/common_flashlight_fxc.h | 219 +- mp/src/materialsystem/stdshaders/common_fxc.h | 14 + .../materialsystem/stdshaders/common_ps_fxc.h | 44 +- .../stdshaders/common_splinerope_fxc.h | 26 + .../stdshaders/common_vertexlitgeneric_dx9.h | 12 +- .../materialsystem/stdshaders/common_vs_fxc.h | 51 +- .../stdshaders/decalmodulate.cpp | 63 + .../stdshaders/decalmodulate_dx9.cpp | 331 ++ .../stdshaders/depth_of_field_ps20b.fxc | 137 + .../stdshaders/depth_of_field_vs20.fxc | 29 + .../stdshaders/depthoffield_dx9.cpp | 280 ++ .../materialsystem/stdshaders/depthwrite.cpp | 114 +- .../emissive_scroll_blended_pass_helper.cpp | 278 ++ .../emissive_scroll_blended_pass_helper.h | 45 + .../stdshaders/engine_post_dx9.cpp | 636 +++ .../stdshaders/example_model_ps20b.fxc | 2 +- .../materialsystem/stdshaders/eye_refract.cpp | 4 +- .../stdshaders/eye_refract_helper.cpp | 56 +- mp/src/materialsystem/stdshaders/eyeball.cpp | 2 +- .../stdshaders/eyeglint_dx9.cpp | 22 +- mp/src/materialsystem/stdshaders/eyes.cpp | 6 +- .../stdshaders/eyes_dx8_dx9_helper.cpp | 124 +- mp/src/materialsystem/stdshaders/eyes_dx9.cpp | 4 +- .../flesh_interior_blended_pass_helper.cpp | 30 +- .../stdshaders/fxctmp9/SDK_Refract_vs20.inc | 137 + .../fxctmp9/SDK_ShatteredGlass_ps20.inc | 212 + .../fxctmp9/SDK_ShatteredGlass_ps20b.inc | 212 + .../fxctmp9/SDK_ShatteredGlass_vs20.inc | 87 + .../fxctmp9/SDK_WaterCheap_ps20.inc | 237 + .../fxctmp9/SDK_WaterCheap_ps20b.inc | 262 + .../fxctmp9/SDK_WaterCheap_vs20.inc | 60 + .../stdshaders/fxctmp9/SDK_Water_ps20.inc | 212 + .../stdshaders/fxctmp9/SDK_Water_ps20b.inc | 287 ++ .../stdshaders/fxctmp9/SDK_Water_vs20.inc | 85 + .../SDK_WorldVertexTransition_ps20.inc | 60 + .../SDK_WorldVertexTransition_ps20b.inc | 85 + .../SDK_WorldVertexTransition_vs20.inc | 60 + .../stdshaders/fxctmp9/SDK_cable_ps20.inc | 60 + .../stdshaders/fxctmp9/SDK_cable_ps20b.inc | 112 + .../stdshaders/fxctmp9/SDK_cable_vs20.inc | 60 + .../fxctmp9/SDK_cloak_blended_pass_ps20.inc | 60 + .../fxctmp9/SDK_cloak_blended_pass_ps20b.inc | 85 + .../fxctmp9/SDK_cloak_blended_pass_ps30.inc | 85 + .../fxctmp9/SDK_cloak_blended_pass_vs20.inc | 112 + .../fxctmp9/SDK_cloak_blended_pass_vs30.inc | 137 + .../fxctmp9/SDK_decalmodulate_ps20.inc | 112 + .../fxctmp9/SDK_decalmodulate_ps20b.inc | 162 + .../fxctmp9/SDK_decalmodulate_ps30.inc | 162 + .../fxctmp9/SDK_decalmodulate_vs20.inc | 187 + .../fxctmp9/SDK_decalmodulate_vs30.inc | 212 + .../fxctmp9/SDK_depthwrite_ps20.inc | 87 + .../fxctmp9/SDK_depthwrite_ps20b.inc | 87 + .../fxctmp9/SDK_depthwrite_ps30.inc | 87 + .../fxctmp9/SDK_depthwrite_vs20.inc | 162 + .../fxctmp9/SDK_depthwrite_vs30.inc | 187 + .../SDK_emissive_scroll_blended_pass_ps20.inc | 33 + ...SDK_emissive_scroll_blended_pass_ps20b.inc | 60 + .../SDK_emissive_scroll_blended_pass_ps30.inc | 60 + .../SDK_emissive_scroll_blended_pass_vs20.inc | 85 + .../SDK_emissive_scroll_blended_pass_vs30.inc | 110 + .../fxctmp9/SDK_engine_post_ps20b.inc | 362 ++ .../fxctmp9/SDK_eye_refract_ps20.inc | 112 + .../fxctmp9/SDK_eye_refract_ps20b.inc | 212 + .../fxctmp9/SDK_eye_refract_ps30.inc | 212 + .../fxctmp9/SDK_eye_refract_vs20.inc | 287 ++ .../fxctmp9/SDK_eye_refract_vs30.inc | 312 ++ .../stdshaders/fxctmp9/SDK_eyeglint_ps20.inc | 33 + .../stdshaders/fxctmp9/SDK_eyeglint_ps20b.inc | 33 + .../stdshaders/fxctmp9/SDK_eyeglint_vs20.inc | 33 + .../fxctmp9/SDK_eyes_flashlight_ps11.inc | 33 + .../fxctmp9/SDK_eyes_flashlight_ps20.inc | 60 + .../fxctmp9/SDK_eyes_flashlight_ps20b.inc | 112 + .../fxctmp9/SDK_eyes_flashlight_ps30.inc | 112 + .../fxctmp9/SDK_eyes_flashlight_vs20.inc | 110 + .../fxctmp9/SDK_eyes_flashlight_vs30.inc | 135 + .../stdshaders/fxctmp9/SDK_eyes_ps20.inc | 60 + .../stdshaders/fxctmp9/SDK_eyes_ps20b.inc | 85 + .../stdshaders/fxctmp9/SDK_eyes_ps30.inc | 85 + .../stdshaders/fxctmp9/SDK_eyes_vs20.inc | 262 + .../stdshaders/fxctmp9/SDK_eyes_vs30.inc | 237 + .../fxctmp9/SDK_flashlight_ps11.inc | 85 + .../fxctmp9/SDK_flashlight_ps20.inc | 212 + .../fxctmp9/SDK_flashlight_ps20b.inc | 262 + .../fxctmp9/SDK_flashlight_ps30.inc | 262 + .../SDK_flesh_interior_blended_pass_ps20.inc | 33 + .../SDK_flesh_interior_blended_pass_ps20b.inc | 60 + .../SDK_flesh_interior_blended_pass_vs20.inc | 237 + .../SDK_lightmappedgeneric_decal_ps20.inc | 60 + .../SDK_lightmappedgeneric_decal_ps20b.inc | 87 + .../SDK_lightmappedgeneric_decal_vs20.inc | 60 + ...SDK_lightmappedgeneric_flashlight_ps20.inc | 262 + ...DK_lightmappedgeneric_flashlight_ps20b.inc | 362 ++ ...SDK_lightmappedgeneric_flashlight_ps30.inc | 362 ++ ...SDK_lightmappedgeneric_flashlight_vs11.inc | 137 + ...SDK_lightmappedgeneric_flashlight_vs20.inc | 212 + ...SDK_lightmappedgeneric_flashlight_vs30.inc | 212 + ...dgeneric_lightingonly_overbright2_ps11.inc | 33 + ...K_lightmappedgeneric_lightingonly_vs11.inc | 60 + .../fxctmp9/SDK_lightmappedgeneric_ps11.inc | 160 + .../fxctmp9/SDK_lightmappedgeneric_ps20b.inc | 762 +++ .../fxctmp9/SDK_lightmappedgeneric_ps30.inc | 737 +++ .../fxctmp9/SDK_lightmappedgeneric_vs20.inc | 362 ++ .../fxctmp9/SDK_lightmappedgeneric_vs30.inc | 362 ++ .../SDK_lightmappedreflective_ps20.inc | 162 + .../SDK_lightmappedreflective_ps20b.inc | 212 + .../SDK_lightmappedreflective_vs20.inc | 60 + .../fxctmp9/SDK_monitorscreen_ps20.inc | 87 + .../fxctmp9/SDK_monitorscreen_ps20b.inc | 112 + .../stdshaders/fxctmp9/SDK_refract_ps20.inc | 262 + .../stdshaders/fxctmp9/SDK_refract_ps20b.inc | 337 ++ .../stdshaders/fxctmp9/SDK_skin_ps20b.inc | 587 +++ .../stdshaders/fxctmp9/SDK_skin_ps30.inc | 587 +++ .../stdshaders/fxctmp9/SDK_skin_vs20.inc | 187 + .../stdshaders/fxctmp9/SDK_skin_vs30.inc | 187 + .../fxctmp9/SDK_splinerope_ps20.inc | 137 + .../fxctmp9/SDK_splinerope_ps20b.inc | 137 + .../fxctmp9/SDK_splinerope_ps30.inc | 112 + .../fxctmp9/SDK_splinerope_vs20.inc | 60 + .../fxctmp9/SDK_splinerope_vs30.inc | 60 + .../stdshaders/fxctmp9/SDK_sprite_ps20.inc | 187 + .../stdshaders/fxctmp9/SDK_sprite_ps20b.inc | 237 + .../stdshaders/fxctmp9/SDK_sprite_vs20.inc | 112 + .../fxctmp9/SDK_teeth_bump_ps20.inc | 110 + .../fxctmp9/SDK_teeth_bump_ps20b.inc | 162 + .../fxctmp9/SDK_teeth_bump_ps30.inc | 162 + .../fxctmp9/SDK_teeth_bump_vs20.inc | 212 + .../fxctmp9/SDK_teeth_bump_vs30.inc | 187 + .../fxctmp9/SDK_teeth_flashlight_ps20.inc | 60 + .../fxctmp9/SDK_teeth_flashlight_ps20b.inc | 137 + .../fxctmp9/SDK_teeth_flashlight_ps30.inc | 137 + .../fxctmp9/SDK_teeth_flashlight_vs20.inc | 137 + .../fxctmp9/SDK_teeth_flashlight_vs30.inc | 162 + .../stdshaders/fxctmp9/SDK_teeth_ps20.inc | 60 + .../stdshaders/fxctmp9/SDK_teeth_ps20b.inc | 112 + .../stdshaders/fxctmp9/SDK_teeth_ps30.inc | 112 + .../stdshaders/fxctmp9/SDK_teeth_vs20.inc | 237 + .../stdshaders/fxctmp9/SDK_teeth_vs30.inc | 212 + .../fxctmp9/SDK_unlitgeneric_ps11.inc | 33 + .../fxctmp9/SDK_unlitgeneric_ps20.inc | 33 + .../fxctmp9/SDK_unlitgeneric_ps20b.inc | 60 + .../fxctmp9/SDK_unlitgeneric_vs20.inc | 137 + .../fxctmp9/SDK_unlittwotexture_ps20.inc | 87 + .../fxctmp9/SDK_unlittwotexture_ps20b.inc | 112 + .../fxctmp9/SDK_unlittwotexture_vs20.inc | 110 + ..._vertexlit_and_unlit_generic_bump_ps20.inc | 437 ++ ...vertexlit_and_unlit_generic_bump_ps20b.inc | 437 ++ ..._vertexlit_and_unlit_generic_bump_ps30.inc | 437 ++ ..._vertexlit_and_unlit_generic_bump_vs20.inc | 212 + ..._vertexlit_and_unlit_generic_bump_vs30.inc | 237 + .../SDK_vertexlit_and_unlit_generic_ps20.inc | 587 +++ .../SDK_vertexlit_and_unlit_generic_ps20b.inc | 687 +++ .../SDK_vertexlit_and_unlit_generic_ps30.inc | 662 +++ .../SDK_vertexlit_and_unlit_generic_vs20.inc | 487 ++ .../SDK_vertexlit_and_unlit_generic_vs30.inc | 512 ++ .../SDK_vertexlit_lighting_only_ps20.inc | 137 + .../SDK_vertexlit_lighting_only_ps20b.inc | 162 + ...tgeneric_lightingonly_overbright2_ps11.inc | 33 + .../fxctmp9/SDK_windowimposter_ps20.inc | 87 + .../fxctmp9/SDK_windowimposter_ps20b.inc | 87 + .../fxctmp9/SDK_windowimposter_vs20.inc | 87 + .../fxctmp9/SDK_worldtwotextureblend_ps20.inc | 287 ++ .../SDK_worldtwotextureblend_ps20b.inc | 387 ++ .../fxctmp9/blurgaussian_3x3_ps20.inc | 33 + .../fxctmp9/blurgaussian_3x3_ps20b.inc | 33 + .../fxctmp9/depth_of_field_ps20b.inc | 60 + .../fxctmp9/depth_of_field_vs20.inc | 33 + .../stdshaders/game_shader_dx9_base.vpc | 5 +- .../stdshaders/game_shader_dx9_mapbase.vpc | 71 + .../stdshaders/lightmappedgeneric_dx9.cpp | 78 +- .../lightmappedgeneric_dx9_helper.cpp | 877 +++- .../lightmappedgeneric_dx9_helper.h | 53 +- .../stdshaders/lightmappedgeneric_ps2_3_x.h | 140 +- .../stdshaders/lightmappedreflective.cpp | 282 ++ mp/src/materialsystem/stdshaders/refract.cpp | 4 +- .../stdshaders/refract_dx9_helper.cpp | 43 +- .../stdshaders/sdk_eyeglint_ps2x.fxc | 32 + .../stdshaders/sdk_eyeglint_vs20.fxc | 38 + .../stdshaders/shatteredglass.cpp | 349 ++ .../stdshaders/skin_dx9_helper.cpp | 152 +- mp/src/materialsystem/stdshaders/spline_fxc.h | 19 + .../materialsystem/stdshaders/splinerope.cpp | 169 + .../materialsystem/stdshaders/sprite_dx9.cpp | 58 +- .../stdshaders/stdshader_dx9_20b.txt | 20 +- .../stdshaders/stdshader_dx9_30.txt | 5 +- mp/src/materialsystem/stdshaders/teeth.cpp | 154 +- mp/src/materialsystem/stdshaders/tree_sway.h | 89 + .../stdshaders/unlitgeneric_dx9.cpp | 45 +- .../stdshaders/unlittwotexture_dx9.cpp | 265 + .../stdshaders/vertexlitgeneric_dx9.cpp | 63 +- .../vertexlitgeneric_dx9_helper.cpp | 364 +- .../stdshaders/vertexlitgeneric_dx9_helper.h | 23 + .../stdshaders/vortwarp_vs20_helper.h | 41 + mp/src/materialsystem/stdshaders/water.cpp | 95 +- .../stdshaders/water_ps2x_helper.h | 4 +- .../stdshaders/windowimposter_dx90.cpp | 181 + .../stdshaders/worldtwotextureblend.cpp | 41 +- .../stdshaders/worldvertextransition.cpp | 77 +- .../worldvertextransition_dx8_helper.cpp | 10 +- mp/src/public/bspfile.h | 12 + mp/src/public/const.h | 19 +- mp/src/public/discord_register.h | 26 + mp/src/public/discord_rpc.h | 87 + mp/src/public/engine/ishadowmgr.h | 1 + mp/src/public/fgdlib/gamedata.h | 10 + mp/src/public/fgdlib/gdvar.h | 8 + .../public/materialsystem/imaterialsystem.h | 38 + mp/src/public/mathlib/vector.h | 5 + mp/src/public/mathlib/vector4d.h | 22 + mp/src/public/particles/particles.h | 6 +- mp/src/public/renderparm.h | 10 + mp/src/public/rope_shared.h | 4 + mp/src/public/stdstring.h | 14 + mp/src/public/tier0/basetypes.h | 5 + mp/src/public/tier1/convar.h | 35 + mp/src/public/tier1/mapbase_con_groups.h | 38 + mp/src/public/tier1/utlblockmemory.h | 8 +- mp/src/public/tier1/utlbuffer.h | 29 +- mp/src/public/tier1/utlsoacontainer.h | 97 +- mp/src/public/tier1/utlsymbol.h | 83 +- mp/src/public/vphysics_interface.h | 24 + mp/src/public/vscript/ivscript.h | 1754 +++++++ mp/src/public/vscript/vscript_templates.h | 414 ++ mp/src/raytrace/raytrace.cpp | 4 + mp/src/tier1/mapbase_con_groups.cpp | 179 + mp/src/tier1/tier1.vpc | 2 + mp/src/utils/common/threads.cpp | 6 + mp/src/utils/common/threads.h | 6 + mp/src/utils/common/utilmatlib.cpp | 1 + mp/src/utils/vbsp/csg.cpp | 7 + mp/src/utils/vbsp/cubemap.cpp | 103 +- mp/src/utils/vbsp/detail.cpp | 3 + mp/src/utils/vbsp/manifest.cpp | 122 +- mp/src/utils/vbsp/manifest.h | 14 + mp/src/utils/vbsp/map.cpp | 376 ++ mp/src/utils/vbsp/matrixinvert.h | 117 + mp/src/utils/vbsp/portals.cpp | 11 + mp/src/utils/vbsp/staticprop.cpp | 2 + mp/src/utils/vbsp/textures.cpp | 7 + mp/src/utils/vbsp/vbsp.cpp | 144 + mp/src/utils/vbsp/vbsp.h | 41 + mp/src/utils/vbsp/vbsp.vpc | 12 + mp/src/utils/vbsp/vscript_funcs_vis.cpp | 29 + mp/src/utils/vbsp/vscript_funcs_vis.h | 16 + mp/src/utils/vbsp/vscript_funcs_vmfs.cpp | 156 + mp/src/utils/vbsp/vscript_funcs_vmfs.h | 16 + mp/src/utils/vbsp/vscript_vbsp.cpp | 333 ++ mp/src/utils/vbsp/vscript_vbsp.h | 27 + mp/src/utils/vbsp/vscript_vbsp.nut | 52 + mp/src/utils/vrad/vrad_dll.vpc | 1 + mp/src/utils/vrad_launcher/vrad_launcher.cpp | 12 + mp/src/utils/vvis/vvis_dll.vpc | 1 + mp/src/utils/vvis_launcher/vvis_launcher.cpp | 26 + mp/src/vpc_scripts/groups.vgc | 2 + mp/src/vpc_scripts/projects.vgc | 5 + mp/src/vpc_scripts/source_base.vpc | 22 + mp/src/vpc_scripts/source_dll_win32_base.vpc | 3 + mp/src/vscript/sqstdtime.h | 88 + mp/src/vscript/squirrel/.gitignore | 6 + mp/src/vscript/squirrel/.travis.yml | 17 + mp/src/vscript/squirrel/CMakeLists.txt | 109 + mp/src/vscript/squirrel/COMPILE | 86 + mp/src/vscript/squirrel/COPYRIGHT | 21 + mp/src/vscript/squirrel/HISTORY | 533 ++ mp/src/vscript/squirrel/Makefile | 22 + mp/src/vscript/squirrel/README | 33 + mp/src/vscript/squirrel/appveyor.yml | 28 + mp/src/vscript/squirrel/doc/Makefile | 216 + mp/src/vscript/squirrel/doc/make.bat | 263 + .../squirrel/doc/source/_static/nut.ico | Bin 0 -> 318 bytes .../doc/source/_static/simple_nut.png | Bin 0 -> 393 bytes mp/src/vscript/squirrel/doc/source/conf.py | 288 ++ mp/src/vscript/squirrel/doc/source/index.rst | 24 + .../reference/api/bytecode_serialization.rst | 32 + .../doc/source/reference/api/calls.rst | 130 + .../doc/source/reference/api/compiler.rst | 79 + .../source/reference/api/debug_interface.rst | 72 + .../reference/api/garbage_collector.rst | 27 + .../api/object_creation_and_handling.rst | 695 +++ .../reference/api/object_manipulation.rst | 451 ++ .../reference/api/raw_object_handling.rst | 163 + .../source/reference/api/stack_operations.rst | 107 + .../source/reference/api/virtual_machine.rst | 341 ++ .../doc/source/reference/api_reference.rst | 18 + .../embedding/build_configuration.rst | 59 + .../embedding/calling_a_function.rst | 27 + .../embedding/compiling_a_script.rst | 58 + .../embedding/creating_a_c_function.rst | 106 + .../reference/embedding/debug_interface.rst | 58 + .../reference/embedding/error_conventions.rst | 16 + .../reference/embedding/memory_management.rst | 28 + .../reference/embedding/references_from_c.rst | 21 + .../embedding/runtime_error_handling.rst | 17 + .../tables_and_arrays_manipulation.rst | 70 + .../embedding/the_registry_table.rst | 14 + .../source/reference/embedding/the_stack.rst | 104 + .../embedding/userdata_and_userpointers.rst | 33 + .../reference/embedding/vm_initialization.rst | 21 + .../source/reference/embedding_squirrel.rst | 29 + .../squirrel/doc/source/reference/index.rst | 34 + .../doc/source/reference/introduction.rst | 16 + .../doc/source/reference/language.rst | 23 + .../doc/source/reference/language/arrays.rst | 19 + .../reference/language/builtin_functions.rst | 693 +++ .../doc/source/reference/language/classes.rst | 429 ++ .../language/constants_and_enumerations.rst | 97 + .../source/reference/language/datatypes.rst | 162 + .../source/reference/language/delegation.rst | 35 + .../reference/language/execution_context.rst | 95 + .../source/reference/language/expressions.rst | 374 ++ .../source/reference/language/functions.rst | 272 + .../source/reference/language/generators.rst | 51 + .../reference/language/lexical_structure.rst | 154 + .../source/reference/language/metamethods.rst | 270 + .../source/reference/language/statements.rst | 386 ++ .../doc/source/reference/language/tables.rst | 71 + .../doc/source/reference/language/threads.rst | 106 + .../reference/language/weak_references.rst | 61 + .../squirrel/doc/source/stdlib/index.rst | 39 + .../doc/source/stdlib/introduction.rst | 22 + .../squirrel/doc/source/stdlib/stdauxlib.rst | 31 + .../squirrel/doc/source/stdlib/stdbloblib.rst | 213 + .../squirrel/doc/source/stdlib/stdiolib.rst | 264 + .../squirrel/doc/source/stdlib/stdmathlib.rst | 111 + .../doc/source/stdlib/stdstringlib.rst | 319 ++ .../doc/source/stdlib/stdsystemlib.rst | 82 + mp/src/vscript/squirrel/etc/minimal.c | 78 + mp/src/vscript/squirrel/etc/test.nut | 4 + mp/src/vscript/squirrel/include/sqconfig.h | 146 + mp/src/vscript/squirrel/include/sqstdaux.h | 18 + mp/src/vscript/squirrel/include/sqstdblob.h | 20 + mp/src/vscript/squirrel/include/sqstdio.h | 54 + mp/src/vscript/squirrel/include/sqstdmath.h | 15 + mp/src/vscript/squirrel/include/sqstdstring.h | 35 + mp/src/vscript/squirrel/include/sqstdsystem.h | 15 + mp/src/vscript/squirrel/include/squirrel.h | 411 ++ mp/src/vscript/squirrel/samples/ackermann.nut | 23 + mp/src/vscript/squirrel/samples/array.nut | 29 + mp/src/vscript/squirrel/samples/class.nut | 49 + .../squirrel/samples/classattributes.nut | 35 + .../vscript/squirrel/samples/coroutines.nut | 25 + .../vscript/squirrel/samples/delegation.nut | 54 + mp/src/vscript/squirrel/samples/fibonacci.nut | 15 + mp/src/vscript/squirrel/samples/flow.nut | 33 + .../vscript/squirrel/samples/generators.nut | 42 + mp/src/vscript/squirrel/samples/hello.nut | 1 + mp/src/vscript/squirrel/samples/list.nut | 40 + mp/src/vscript/squirrel/samples/loops.nut | 32 + mp/src/vscript/squirrel/samples/matrix.nut | 44 + .../vscript/squirrel/samples/metamethods.nut | 115 + mp/src/vscript/squirrel/samples/methcall.nut | 68 + mp/src/vscript/squirrel/samples/regex.nut | 10 + mp/src/vscript/squirrel/samples/tailstate.nut | 24 + mp/src/vscript/squirrel/sq/CMakeLists.txt | 38 + mp/src/vscript/squirrel/sq/Makefile | 21 + mp/src/vscript/squirrel/sq/sq.c | 349 ++ mp/src/vscript/squirrel/sq/sq.dsp | 101 + .../vscript/squirrel/sqstdlib/CMakeLists.txt | 52 + mp/src/vscript/squirrel/sqstdlib/Makefile | 44 + mp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp | 151 + .../vscript/squirrel/sqstdlib/sqstdblob.cpp | 283 ++ .../vscript/squirrel/sqstdlib/sqstdblobimpl.h | 108 + mp/src/vscript/squirrel/sqstdlib/sqstdio.cpp | 489 ++ mp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp | 131 + .../vscript/squirrel/sqstdlib/sqstdmath.cpp | 107 + mp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp | 666 +++ .../vscript/squirrel/sqstdlib/sqstdstream.cpp | 336 ++ .../vscript/squirrel/sqstdlib/sqstdstream.h | 18 + .../vscript/squirrel/sqstdlib/sqstdstring.cpp | 538 ++ .../vscript/squirrel/sqstdlib/sqstdsystem.cpp | 154 + .../vscript/squirrel/squirrel-config.cmake.in | 4 + mp/src/vscript/squirrel/squirrel.dsw | 77 + .../vscript/squirrel/squirrel/CMakeLists.txt | 55 + mp/src/vscript/squirrel/squirrel/Makefile | 53 + mp/src/vscript/squirrel/squirrel/sqapi.cpp | 1631 ++++++ mp/src/vscript/squirrel/squirrel/sqarray.h | 94 + .../vscript/squirrel/squirrel/sqbaselib.cpp | 1370 +++++ mp/src/vscript/squirrel/squirrel/sqclass.cpp | 210 + mp/src/vscript/squirrel/squirrel/sqclass.h | 162 + mp/src/vscript/squirrel/squirrel/sqclosure.h | 201 + .../vscript/squirrel/squirrel/sqcompiler.cpp | 1611 ++++++ mp/src/vscript/squirrel/squirrel/sqcompiler.h | 79 + mp/src/vscript/squirrel/squirrel/sqdebug.cpp | 118 + .../vscript/squirrel/squirrel/sqfuncproto.h | 154 + .../vscript/squirrel/squirrel/sqfuncstate.cpp | 649 +++ .../vscript/squirrel/squirrel/sqfuncstate.h | 91 + mp/src/vscript/squirrel/squirrel/sqlexer.cpp | 563 +++ mp/src/vscript/squirrel/squirrel/sqlexer.h | 55 + mp/src/vscript/squirrel/squirrel/sqmem.cpp | 11 + mp/src/vscript/squirrel/squirrel/sqobject.cpp | 677 +++ mp/src/vscript/squirrel/squirrel/sqobject.h | 353 ++ mp/src/vscript/squirrel/squirrel/sqopcodes.h | 132 + mp/src/vscript/squirrel/squirrel/sqpcheader.h | 20 + mp/src/vscript/squirrel/squirrel/sqstate.cpp | 647 +++ mp/src/vscript/squirrel/squirrel/sqstate.h | 136 + mp/src/vscript/squirrel/squirrel/sqstring.h | 31 + mp/src/vscript/squirrel/squirrel/sqtable.cpp | 221 + mp/src/vscript/squirrel/squirrel/sqtable.h | 110 + mp/src/vscript/squirrel/squirrel/squirrel.dsp | 302 ++ mp/src/vscript/squirrel/squirrel/squserdata.h | 40 + mp/src/vscript/squirrel/squirrel/squtils.h | 116 + mp/src/vscript/squirrel/squirrel/sqvm.cpp | 1791 +++++++ mp/src/vscript/squirrel/squirrel/sqvm.h | 213 + mp/src/vscript/vscript.cpp | 82 + mp/src/vscript/vscript.vpc | 69 + mp/src/vscript/vscript_bindings_base.cpp | 640 +++ mp/src/vscript/vscript_bindings_base.h | 75 + mp/src/vscript/vscript_bindings_math.cpp | 456 ++ mp/src/vscript/vscript_bindings_math.h | 62 + mp/src/vscript/vscript_squirrel.cpp | 3557 +++++++++++++ mp/src/vscript/vscript_squirrel.nut | 336 ++ 1017 files changed, 154991 insertions(+), 2254 deletions(-) create mode 100644 mp/src/devtools/gcc9+support.cpp create mode 100644 mp/src/devtools/makefile_base_posix.mak.default create mode 100644 mp/src/devtools/makefile_base_posix.mak.gcc8 create mode 100644 mp/src/game/client/c_baselesson.cpp create mode 100644 mp/src/game/client/c_baselesson.h create mode 100644 mp/src/game/client/c_env_dof_controller.cpp create mode 100644 mp/src/game/client/c_env_global_light.cpp create mode 100644 mp/src/game/client/c_gameinstructor.cpp create mode 100644 mp/src/game/client/c_gameinstructor.h create mode 100644 mp/src/game/client/c_postprocesscontroller.cpp create mode 100644 mp/src/game/client/c_postprocesscontroller.h create mode 100644 mp/src/game/client/client_episodic.vpc create mode 100644 mp/src/game/client/client_mapbase.vpc create mode 100644 mp/src/game/client/hud_locator_target.cpp create mode 100644 mp/src/game/client/hud_locator_target.h create mode 100644 mp/src/game/client/mapbase/c_func_clientclip.cpp create mode 100644 mp/src/game/client/mapbase/c_func_fake_worldportal.cpp create mode 100644 mp/src/game/client/mapbase/c_func_fake_worldportal.h create mode 100644 mp/src/game/client/mapbase/c_point_glow.cpp create mode 100644 mp/src/game/client/vscript_client.cpp create mode 100644 mp/src/game/client/vscript_client.h create mode 100644 mp/src/game/client/vscript_client.nut create mode 100644 mp/src/game/client/worldlight.cpp create mode 100644 mp/src/game/client/worldlight.h create mode 100644 mp/src/game/server/env_dof_controller.cpp create mode 100644 mp/src/game/server/env_dof_controller.h create mode 100644 mp/src/game/server/env_global_light.cpp create mode 100644 mp/src/game/server/env_instructor_hint.cpp create mode 100644 mp/src/game/server/env_projectedtexture.h create mode 100644 mp/src/game/server/logic_random_outputs.cpp create mode 100644 mp/src/game/server/logic_random_outputs.h create mode 100644 mp/src/game/server/mapbase/GlobalStrings.cpp create mode 100644 mp/src/game/server/mapbase/GlobalStrings.h create mode 100644 mp/src/game/server/mapbase/SystemConvarMod.cpp create mode 100644 mp/src/game/server/mapbase/SystemConvarMod.h create mode 100644 mp/src/game/server/mapbase/ai_grenade.cpp create mode 100644 mp/src/game/server/mapbase/ai_grenade.h create mode 100644 mp/src/game/server/mapbase/ai_monitor.cpp create mode 100644 mp/src/game/server/mapbase/ai_weaponmodifier.cpp create mode 100644 mp/src/game/server/mapbase/closecaption_entity.cpp create mode 100644 mp/src/game/server/mapbase/datadesc_mod.cpp create mode 100644 mp/src/game/server/mapbase/datadesc_mod.h create mode 100644 mp/src/game/server/mapbase/expandedrs_combine.h create mode 100644 mp/src/game/server/mapbase/func_clientclip.cpp create mode 100644 mp/src/game/server/mapbase/func_fake_worldportal.cpp create mode 100644 mp/src/game/server/mapbase/logic_eventlistener.cpp create mode 100644 mp/src/game/server/mapbase/logic_externaldata.cpp create mode 100644 mp/src/game/server/mapbase/logic_register_activator.cpp create mode 100644 mp/src/game/server/mapbase/logic_skill.cpp create mode 100644 mp/src/game/server/mapbase/point_advanced_finder.cpp create mode 100644 mp/src/game/server/mapbase/point_copy_size.cpp create mode 100644 mp/src/game/server/mapbase/point_damageinfo.cpp create mode 100644 mp/src/game/server/mapbase/point_entity_replace.cpp create mode 100644 mp/src/game/server/mapbase/point_glow.cpp create mode 100644 mp/src/game/server/mapbase/point_projectile.cpp create mode 100644 mp/src/game/server/mapbase/point_radiation_source.cpp create mode 100644 mp/src/game/server/mapbase/variant_tools.h create mode 100644 mp/src/game/server/point_entity_finder.cpp create mode 100644 mp/src/game/server/postprocesscontroller.cpp create mode 100644 mp/src/game/server/postprocesscontroller.h create mode 100644 mp/src/game/server/server_mapbase.vpc create mode 100644 mp/src/game/server/skyboxswapper.cpp create mode 100644 mp/src/game/server/vscript_server.cpp create mode 100644 mp/src/game/server/vscript_server.h create mode 100644 mp/src/game/server/vscript_server.nut create mode 100644 mp/src/game/shared/hl2mp/weapon_stunstick.h create mode 100644 mp/src/game/shared/mapbase/MapEdit.cpp create mode 100644 mp/src/game/shared/mapbase/MapEdit.h create mode 100644 mp/src/game/shared/mapbase/mapbase_game_log.cpp create mode 100644 mp/src/game/shared/mapbase/mapbase_rpc.cpp create mode 100644 mp/src/game/shared/mapbase/mapbase_shared.cpp create mode 100644 mp/src/game/shared/mapbase/matchers.cpp create mode 100644 mp/src/game/shared/mapbase/matchers.h create mode 100644 mp/src/game/shared/mapbase/vscript_consts_shared.cpp create mode 100644 mp/src/game/shared/mapbase/vscript_consts_weapons.cpp create mode 100644 mp/src/game/shared/mapbase/vscript_funcs_hl2.cpp create mode 100644 mp/src/game/shared/mapbase/vscript_funcs_math.cpp create mode 100644 mp/src/game/shared/mapbase/vscript_funcs_shared.cpp create mode 100644 mp/src/game/shared/mapbase/vscript_funcs_shared.h create mode 100644 mp/src/game/shared/mapbase/vscript_singletons.cpp create mode 100644 mp/src/game/shared/mapbase/vscript_singletons.h create mode 100644 mp/src/game/shared/mapbase/weapon_custom_scripted.cpp create mode 100644 mp/src/game/shared/mapbase/weapon_custom_scripted.h create mode 100644 mp/src/game/shared/postprocess_shared.h create mode 100644 mp/src/game/shared/vscript_shared.cpp create mode 100644 mp/src/game/shared/vscript_shared.h create mode 100644 mp/src/materialsystem/stdshaders/MonitorScreen_dx9.cpp create mode 100644 mp/src/materialsystem/stdshaders/SDK_BlurFilter_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_Refract_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_WaterCheap_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_WaterCheap_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_Water_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_cable_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_cable_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_decalmodulate_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_decalmodulate_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_depthwrite_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_depthwrite_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_engine_post_ps20b.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eye_refract_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eye_refract_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_inc.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eyes_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_eyes_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_flashlight_ps11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_vs11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps20b.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_monitorscreen_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_refract_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_skin_ps20b.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_skin_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_splinerope_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_splinerope_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_sprite_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_sprite_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_teeth_bump_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_teeth_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_teeth_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_unlitgeneric_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_unlittwotexture_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_unlittwotexture_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps20b.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps20b.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlit_lighting_only_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_water_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_worldtwotextureblend_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/blurgaussian_3x3_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.cpp create mode 100644 mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.h create mode 100644 mp/src/materialsystem/stdshaders/common_splinerope_fxc.h create mode 100644 mp/src/materialsystem/stdshaders/decalmodulate.cpp create mode 100644 mp/src/materialsystem/stdshaders/decalmodulate_dx9.cpp create mode 100644 mp/src/materialsystem/stdshaders/depth_of_field_ps20b.fxc create mode 100644 mp/src/materialsystem/stdshaders/depth_of_field_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/depthoffield_dx9.cpp create mode 100644 mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.cpp create mode 100644 mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.h create mode 100644 mp/src/materialsystem/stdshaders/engine_post_dx9.cpp create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_Refract_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_engine_post_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_vs11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs30.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc create mode 100644 mp/src/materialsystem/stdshaders/lightmappedreflective.cpp create mode 100644 mp/src/materialsystem/stdshaders/sdk_eyeglint_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/sdk_eyeglint_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/shatteredglass.cpp create mode 100644 mp/src/materialsystem/stdshaders/spline_fxc.h create mode 100644 mp/src/materialsystem/stdshaders/splinerope.cpp create mode 100644 mp/src/materialsystem/stdshaders/tree_sway.h create mode 100644 mp/src/materialsystem/stdshaders/unlittwotexture_dx9.cpp create mode 100644 mp/src/materialsystem/stdshaders/vortwarp_vs20_helper.h create mode 100644 mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp create mode 100644 mp/src/public/discord_register.h create mode 100644 mp/src/public/discord_rpc.h create mode 100644 mp/src/public/tier1/mapbase_con_groups.h create mode 100644 mp/src/public/vscript/ivscript.h create mode 100644 mp/src/public/vscript/vscript_templates.h create mode 100644 mp/src/tier1/mapbase_con_groups.cpp create mode 100644 mp/src/utils/vbsp/matrixinvert.h create mode 100644 mp/src/utils/vbsp/vscript_funcs_vis.cpp create mode 100644 mp/src/utils/vbsp/vscript_funcs_vis.h create mode 100644 mp/src/utils/vbsp/vscript_funcs_vmfs.cpp create mode 100644 mp/src/utils/vbsp/vscript_funcs_vmfs.h create mode 100644 mp/src/utils/vbsp/vscript_vbsp.cpp create mode 100644 mp/src/utils/vbsp/vscript_vbsp.h create mode 100644 mp/src/utils/vbsp/vscript_vbsp.nut create mode 100644 mp/src/vscript/sqstdtime.h create mode 100644 mp/src/vscript/squirrel/.gitignore create mode 100644 mp/src/vscript/squirrel/.travis.yml create mode 100644 mp/src/vscript/squirrel/CMakeLists.txt create mode 100644 mp/src/vscript/squirrel/COMPILE create mode 100644 mp/src/vscript/squirrel/COPYRIGHT create mode 100644 mp/src/vscript/squirrel/HISTORY create mode 100644 mp/src/vscript/squirrel/Makefile create mode 100644 mp/src/vscript/squirrel/README create mode 100644 mp/src/vscript/squirrel/appveyor.yml create mode 100644 mp/src/vscript/squirrel/doc/Makefile create mode 100644 mp/src/vscript/squirrel/doc/make.bat create mode 100644 mp/src/vscript/squirrel/doc/source/_static/nut.ico create mode 100644 mp/src/vscript/squirrel/doc/source/_static/simple_nut.png create mode 100644 mp/src/vscript/squirrel/doc/source/conf.py create mode 100644 mp/src/vscript/squirrel/doc/source/index.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/calls.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/compiler.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api/virtual_machine.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/api_reference.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/build_configuration.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/calling_a_function.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/index.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/introduction.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/arrays.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/classes.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/delegation.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/expressions.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/functions.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/generators.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/statements.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/tables.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/threads.rst create mode 100644 mp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/index.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/introduction.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst create mode 100644 mp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst create mode 100644 mp/src/vscript/squirrel/etc/minimal.c create mode 100644 mp/src/vscript/squirrel/etc/test.nut create mode 100644 mp/src/vscript/squirrel/include/sqconfig.h create mode 100644 mp/src/vscript/squirrel/include/sqstdaux.h create mode 100644 mp/src/vscript/squirrel/include/sqstdblob.h create mode 100644 mp/src/vscript/squirrel/include/sqstdio.h create mode 100644 mp/src/vscript/squirrel/include/sqstdmath.h create mode 100644 mp/src/vscript/squirrel/include/sqstdstring.h create mode 100644 mp/src/vscript/squirrel/include/sqstdsystem.h create mode 100644 mp/src/vscript/squirrel/include/squirrel.h create mode 100644 mp/src/vscript/squirrel/samples/ackermann.nut create mode 100644 mp/src/vscript/squirrel/samples/array.nut create mode 100644 mp/src/vscript/squirrel/samples/class.nut create mode 100644 mp/src/vscript/squirrel/samples/classattributes.nut create mode 100644 mp/src/vscript/squirrel/samples/coroutines.nut create mode 100644 mp/src/vscript/squirrel/samples/delegation.nut create mode 100644 mp/src/vscript/squirrel/samples/fibonacci.nut create mode 100644 mp/src/vscript/squirrel/samples/flow.nut create mode 100644 mp/src/vscript/squirrel/samples/generators.nut create mode 100644 mp/src/vscript/squirrel/samples/hello.nut create mode 100644 mp/src/vscript/squirrel/samples/list.nut create mode 100644 mp/src/vscript/squirrel/samples/loops.nut create mode 100644 mp/src/vscript/squirrel/samples/matrix.nut create mode 100644 mp/src/vscript/squirrel/samples/metamethods.nut create mode 100644 mp/src/vscript/squirrel/samples/methcall.nut create mode 100644 mp/src/vscript/squirrel/samples/regex.nut create mode 100644 mp/src/vscript/squirrel/samples/tailstate.nut create mode 100644 mp/src/vscript/squirrel/sq/CMakeLists.txt create mode 100644 mp/src/vscript/squirrel/sq/Makefile create mode 100644 mp/src/vscript/squirrel/sq/sq.c create mode 100644 mp/src/vscript/squirrel/sq/sq.dsp create mode 100644 mp/src/vscript/squirrel/sqstdlib/CMakeLists.txt create mode 100644 mp/src/vscript/squirrel/sqstdlib/Makefile create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdio.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdstream.h create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp create mode 100644 mp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp create mode 100644 mp/src/vscript/squirrel/squirrel-config.cmake.in create mode 100644 mp/src/vscript/squirrel/squirrel.dsw create mode 100644 mp/src/vscript/squirrel/squirrel/CMakeLists.txt create mode 100644 mp/src/vscript/squirrel/squirrel/Makefile create mode 100644 mp/src/vscript/squirrel/squirrel/sqapi.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqarray.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqbaselib.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqclass.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqclass.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqclosure.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqcompiler.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqcompiler.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqdebug.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqfuncproto.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqfuncstate.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqfuncstate.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqlexer.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqlexer.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqmem.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqobject.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqobject.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqopcodes.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqpcheader.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqstate.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqstate.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqstring.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqtable.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqtable.h create mode 100644 mp/src/vscript/squirrel/squirrel/squirrel.dsp create mode 100644 mp/src/vscript/squirrel/squirrel/squserdata.h create mode 100644 mp/src/vscript/squirrel/squirrel/squtils.h create mode 100644 mp/src/vscript/squirrel/squirrel/sqvm.cpp create mode 100644 mp/src/vscript/squirrel/squirrel/sqvm.h create mode 100644 mp/src/vscript/vscript.cpp create mode 100644 mp/src/vscript/vscript.vpc create mode 100644 mp/src/vscript/vscript_bindings_base.cpp create mode 100644 mp/src/vscript/vscript_bindings_base.h create mode 100644 mp/src/vscript/vscript_bindings_math.cpp create mode 100644 mp/src/vscript/vscript_bindings_math.h create mode 100644 mp/src/vscript/vscript_squirrel.cpp create mode 100644 mp/src/vscript/vscript_squirrel.nut diff --git a/mp/src/devtools/gcc9+support.cpp b/mp/src/devtools/gcc9+support.cpp new file mode 100644 index 00000000..6ab7c971 --- /dev/null +++ b/mp/src/devtools/gcc9+support.cpp @@ -0,0 +1,59 @@ +// compatibility with old ABI +#if __GNUC__ >= 9 +#include + +extern "C" double __pow_finite(double a, double b) +{ + return pow(a, b); +} + +extern "C" double __log_finite(double a) +{ + return log(a); +} + +extern "C" double __exp_finite(double a) +{ + return exp(a); +} + +extern "C" double __atan2_finite(double a, double b) +{ + return atan2(a, b); +} + +extern "C" double __atan2f_finite(double a, double b) +{ + return atan2f(a, b); +} + +extern "C" float __powf_finite(float a, float b) +{ + return powf(a, b); +} + +extern "C" float __logf_finite(float a) +{ + return logf(a); +} + +extern "C" float __expf_finite(float a) +{ + return expf(a); +} + +extern "C" float __acosf_finite(float a) +{ + return acosf(a); +} + +extern "C" double __asin_finite(double a) +{ + return asin(a); +} + +extern "C" double __acos_finite(double a) +{ + return acos(a); +} +#endif diff --git a/mp/src/devtools/makefile_base_posix.mak b/mp/src/devtools/makefile_base_posix.mak index c76a1b1f..9dc88cf9 100644 --- a/mp/src/devtools/makefile_base_posix.mak +++ b/mp/src/devtools/makefile_base_posix.mak @@ -1,472 +1,15 @@ -# -# Base makefile for Linux. -# -# !!!!! Note to future editors !!!!! -# -# before you make changes, make sure you grok: -# 1. the difference between =, :=, +=, and ?= -# 2. how and when this base makefile gets included in the generated makefile(s) -# ( see http://www.gnu.org/software/make/manual/make.html#Flavors ) -# -# Command line prefixes: -# - errors are ignored -# @ command is not printed to stdout before being executed -# + command is executed even if Make is invoked in "do not exec" mode +SRCROOT?=.. -OS := $(shell uname) -HOSTNAME := $(shell hostname) +THISFILE:=$(SRCROOT)/devtools/makefile_base_posix.mak +MAKEFILE_BASE:=$(notdir $(THISFILE)) +MAKEFILE_LINK:=$(THISFILE).link --include $(SRCROOT)/devtools/steam_def.mak --include $(SRCROOT)/devtools/sourcesdk_def.mak +-include $(MAKEFILE_LINK) -# To build with clang, set the following in your environment: -# CC = clang -# CXX = clang++ -ifneq (,$(findstring clang,$(CXX))) - CLANG_BUILD = 1 -endif - -ifeq ($(OS),Darwin) - $(error This file should never be used for Mac - use base.xconfig) -endif - -ifeq ($(CFG), release) - # With gcc 4.6.3, engine.so went from 7,383,765 to 8,429,109 when building with -O3. - # There also was no speed difference running at 1280x1024. May 2012, mikesart. - # tonyp: The size increase was likely caused by -finline-functions and -fipa-cp-clone getting switched on with -O3. - # -fno-omit-frame-pointer: need this for stack traces with perf. - OptimizerLevel_CompilerSpecific = -O2 -fno-strict-aliasing -ffast-math -fno-omit-frame-pointer -ftree-vectorize - ifeq ($(CLANG_BUILD),1) - # These aren't supported wit Clang 3.5. Need to remove when we update that. - OptimizerLevel_CompilerSpecific += -fpredictive-commoning -funswitch-loops - else - OptimizerLevel_CompilerSpecific += -fpredictive-commoning -funswitch-loops - endif -else - OptimizerLevel_CompilerSpecific = -O0 - #-O1 -finline-functions -endif - -# CPPFLAGS == "c/c++ *preprocessor* flags" - not "cee-plus-plus flags" -ARCH_FLAGS = -BUILDING_MULTI_ARCH = 0 -# Preserve cflags set in environment -ENV_CFLAGS := $(CFLAGS) -ENV_CXXFLAGS := $(CXXFLAGS) -CPPFLAGS = $(DEFINES) $(addprefix -I, $(abspath $(INCLUDEDIRS) )) -BASE_CFLAGS = $(ARCH_FLAGS) $(CPPFLAGS) $(WARN_FLAGS) -fvisibility=$(SymbolVisibility) $(OptimizerLevel) -pipe $(GCC_ExtraCompilerFlags) -Usprintf -Ustrncpy -UPROTECTED_THINGS_ENABLE -CFLAGS = $(BASE_CFLAGS) $(ENV_CFLAGS) -# In -std=gnu++0x mode we get lots of errors about "error: narrowing conversion". -fpermissive -# turns these into warnings in gcc, and -Wno-c++11-narrowing suppresses them entirely in clang 3.1+. -ifeq ($(CLANG_BUILD),1) - CXXFLAGS = $(BASE_CFLAGS) -std=gnu++0x -Wno-c++11-narrowing -Wno-dangling-else $(ENV_CXXFLAGS) -else - CXXFLAGS = $(BASE_CFLAGS) -std=gnu++0x -fpermissive $(ENV_CXXFLAGS) -endif -DEFINES += -DVPROF_LEVEL=1 -DGNUC -DNO_HOOK_MALLOC -DNO_MALLOC_OVERRIDE - -## TODO: This cases build errors in cstrike/bin right now. Need to debug. -# This causes all filesystem interfaces to default to their 64bit versions on -# 32bit systems, which means we don't break on filesystems with inodes > 32bit. -# DEFINES += -D_FILE_OFFSET_BITS=64 - -LDFLAGS = $(CFLAGS) $(GCC_ExtraLinkerFlags) $(OptimizerLevel) -GENDEP_CXXFLAGS = -MMD -MP -MF $(@:.o=.P) -MAP_FLAGS = -Srv_GAMEOUTPUTFILE = -COPY_DLL_TO_SRV = 0 - -# We should always specify -Wl,--build-id, as documented at: -# http://linux.die.net/man/1/ld and http://fedoraproject.org/wiki/Releases/FeatureBuildId.http://fedoraproject.org/wiki/Releases/FeatureBuildId -LDFLAGS += -Wl,--build-id - -# -# If we should be running in a chroot, check to see if we are. If not, then prefix everything with the -# required chroot -# -ifdef MAKE_CHROOT - export STEAM_RUNTIME_PATH := /usr - ifneq ("$(SCHROOT_CHROOT_NAME)", "$(CHROOT_NAME)") - $(info '$(SCHROOT_CHROOT_NAME)' is not '$(CHROOT_NAME)') - $(error This makefile should be run from within a chroot. 'schroot --chroot $(CHROOT_NAME) -- $(MAKE) $(MAKEFLAGS)') - endif - GCC_VER = -4.8 - P4BIN = $(SRCROOT)/devtools/bin/linux/p4 - CRYPTOPPDIR=ubuntu12_32_gcc48 -else ifeq ($(USE_VALVE_BINDIR),1) - # Using /valve/bin directory. - export STEAM_RUNTIME_PATH ?= /valve - GCC_VER = -4.6 - P4BIN = p4 - CRYPTOPPDIR=linux32 -else - # Not using chroot, use old steam-runtime. (gcc 4.6.3) - export STEAM_RUNTIME_PATH ?= /valve/steam-runtime - GCC_VER = - P4BIN = p4 - CRYPTOPPDIR=ubuntu12_32 -endif - -ifeq ($(TARGET_PLATFORM),linux64) - MARCH_TARGET = core2 -else - MARCH_TARGET = pentium4 -endif - -ifeq ($(USE_VALVE_BINDIR),1) - # On dedicated servers, some plugins depend on global variable symbols in addition to functions. - # So symbols like _Z16ClearMultiDamagev should show up when you do "nm server_srv.so" in TF2. - STRIP_FLAGS = -else - # Linux desktop client (or client/dedicated server in chroot). - STRIP_FLAGS = -x -endif - -ifeq ($(CLANG_BUILD),1) - # Clang does not support -mfpmath=sse because it uses whatever - # instruction set extensions are available by default. - SSE_GEN_FLAGS = -msse2 -else - SSE_GEN_FLAGS = -msse2 -mfpmath=sse -endif - -CCACHE := $(SRCROOT)/devtools/bin/linux/ccache - -ifeq ($(origin AR), default) - AR = $(STEAM_RUNTIME_PATH)/bin/ar crs -endif -ifeq ($(origin CC), default) - CC = $(CCACHE) $(STEAM_RUNTIME_PATH)/bin/gcc$(GCC_VER) -endif -ifeq ($(origin CXX), default) - CXX = $(CCACHE) $(STEAM_RUNTIME_PATH)/bin/g++$(GCC_VER) -endif - -# Support ccache with clang. Add -Qunused-arguments to avoid excessive warnings due to -# a ccache quirk. Could also upgrade ccache. -# http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html -ifeq ($(CLANG_BUILD),1) - CC := $(CCACHE) $(CC) -Qunused-arguments -fcolor-diagnostics - CXX := $(CCACHE) $(CXX) -Qunused-arguments -fcolor-diagnostics -endif -LINK ?= $(CC) - -ifeq ($(STEAM_BRANCH),1) - WARN_FLAGS = -Wall -Wextra -Wshadow -Wno-invalid-offsetof -else - WARN_FLAGS = -Wall -Wno-invalid-offsetof -Wno-multichar -Wno-overloaded-virtual - WARN_FLAGS += -Wno-write-strings - WARN_FLAGS += -Wno-unused-variable - WARN_FLAGS += -Wno-unused-but-set-variable - WARN_FLAGS += -Wno-unused-function -endif - -ifeq ($(CLANG_BUILD),1) - # Clang specific flags -else ifeq ($(GCC_VER),-4.8) - WARN_FLAGS += -Wno-unused-local-typedefs - WARN_FLAGS += -Wno-unused-result - WARN_FLAGS += -Wno-narrowing - # WARN_FLAGS += -Wno-unused-function -endif - -WARN_FLAGS += -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -WARN_FLAGS += -Wno-sign-compare -Wno-reorder -Wno-invalid-offsetof -Wno-float-equal -Werror=return-type -WARN_FLAGS += -fdiagnostics-show-option -Wformat -Wformat-security - -ifeq ($(TARGET_PLATFORM),linux64) - # nocona = pentium4 + 64bit + MMX, SSE, SSE2, SSE3 - no SSSE3 (that's three s's - added in core2) - ARCH_FLAGS += -march=$(MARCH_TARGET) -mtune=core2 - LD_SO = ld-linux-x86_64.so.2 - LIBSTDCXX := $(shell $(CXX) -print-file-name=libstdc++.a) - LIBSTDCXXPIC := $(shell $(CXX) -print-file-name=libstdc++-pic.a) -else - # pentium4 = MMX, SSE, SSE2 - no SSE3 (added in prescott) # -msse3 -mfpmath=sse - ARCH_FLAGS += -m32 -march=$(MARCH_TARGET) -mtune=core2 $(SSE_GEN_FLAGS) - LD_SO = ld-linux.so.2 - LIBSTDCXX := $(shell $(CXX) $(ARCH_FLAGS) -print-file-name=libstdc++.so) - LIBSTDCXXPIC := $(shell $(CXX) $(ARCH_FLAGS) -print-file-name=libstdc++.so) - LDFLAGS += -m32 -endif - -GEN_SYM ?= $(SRCROOT)/devtools/gendbg.sh -ifeq ($(CFG),release) - STRIP ?= strip $(STRIP_FLAGS) -S -# CFLAGS += -ffunction-sections -fdata-sections -# LDFLAGS += -Wl,--gc-sections -Wl,--print-gc-sections -else - STRIP ?= true -endif -VSIGN ?= true - -ifeq ($(SOURCE_SDK), 1) - Srv_GAMEOUTPUTFILE := $(GAMEOUTPUTFILE:.so=_srv.so) - COPY_DLL_TO_SRV := 1 -endif - -LINK_MAP_FLAGS = -Wl,-Map,$(@:.so=).map - -SHLIBLDFLAGS = -shared $(LDFLAGS) -Wl,--no-undefined - -_WRAP := -Xlinker --wrap= -PATHWRAP = $(_WRAP)fopen $(_WRAP)freopen $(_WRAP)open $(_WRAP)creat $(_WRAP)access $(_WRAP)__xstat \ - $(_WRAP)stat $(_WRAP)lstat $(_WRAP)fopen64 $(_WRAP)open64 $(_WRAP)opendir $(_WRAP)__lxstat \ - $(_WRAP)chmod $(_WRAP)chown $(_WRAP)lchown $(_WRAP)symlink $(_WRAP)link $(_WRAP)__lxstat64 \ - $(_WRAP)mknod $(_WRAP)utimes $(_WRAP)unlink $(_WRAP)rename $(_WRAP)utime $(_WRAP)__xstat64 \ - $(_WRAP)mount $(_WRAP)mkfifo $(_WRAP)mkdir $(_WRAP)rmdir $(_WRAP)scandir $(_WRAP)realpath - -LIB_START_EXE = $(PATHWRAP) -static-libgcc -Wl,--start-group -LIB_END_EXE = -Wl,--end-group -lm -ldl $(LIBSTDCXX) -lpthread - -LIB_START_SHLIB = $(PATHWRAP) -static-libgcc -Wl,--start-group -LIB_END_SHLIB = -Wl,--end-group -lm -ldl $(LIBSTDCXXPIC) -lpthread -l:$(LD_SO) -Wl,--version-script=$(SRCROOT)/devtools/version_script.linux.txt - -# -# Profile-directed optimizations. -# Note: Last time these were tested 3/5/08, it actually slowed down the server benchmark by 5%! -# -# First, uncomment these, build, and test. It will generate .gcda and .gcno files where the .o files are. -# PROFILE_LINKER_FLAG=-fprofile-arcs -# PROFILE_COMPILER_FLAG=-fprofile-arcs -# -# Then, comment the above flags out again and rebuild with this flag uncommented: -# PROFILE_COMPILER_FLAG=-fprofile-use -# - -############################################################################# -# The compiler command lne for each src code file to compile -############################################################################# - -OBJ_DIR = ./obj_$(NAME)_$(TARGET_PLATFORM)$(TARGET_PLATFORM_EXT)/$(CFG) -CPP_TO_OBJ = $(CPPFILES:.cpp=.o) -CXX_TO_OBJ = $(CPP_TO_OBJ:.cxx=.o) -CC_TO_OBJ = $(CXX_TO_OBJ:.cc=.o) -MM_TO_OBJ = $(CC_TO_OBJ:.mm=.o) -C_TO_OBJ = $(MM_TO_OBJ:.c=.o) -OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(C_TO_OBJ))) - -ifeq ($(MAKE_VERBOSE),1) - QUIET_PREFIX = - QUIET_ECHO_POSTFIX = -else - QUIET_PREFIX = @ - QUIET_ECHO_POSTFIX = > /dev/null -endif - -ifeq ($(MAKE_CC_VERBOSE),1) -CC += -v -endif - -ifeq ($(CONFTYPE),lib) - LIB_File = $(OUTPUTFILE) -endif - -ifeq ($(CONFTYPE),dll) - SO_File = $(OUTPUTFILE) -endif - -ifeq ($(CONFTYPE),exe) - EXE_File = $(OUTPUTFILE) -endif - -# we generate dependencies as a side-effect of compilation now -GEN_DEP_FILE= - -PRE_COMPILE_FILE = - -POST_COMPILE_FILE = - -ifeq ($(BUILDING_MULTI_ARCH),1) - SINGLE_ARCH_CXXFLAGS=$(subst -arch x86_64,,$(CXXFLAGS)) - COMPILE_FILE = \ - $(QUIET_PREFIX) \ - echo "---- $(lastword $(subst /, ,$<)) as MULTIARCH----";\ - mkdir -p $(OBJ_DIR) && \ - $(CXX) $(SINGLE_ARCH_CXXFLAGS) $(GENDEP_CXXFLAGS) -o $@ -c $< && \ - $(CXX) $(CXXFLAGS) -o $@ -c $< -else - COMPILE_FILE = \ - $(QUIET_PREFIX) \ - echo "---- $(lastword $(subst /, ,$<)) ----";\ - mkdir -p $(OBJ_DIR) && \ - $(CXX) $(CXXFLAGS) $(GENDEP_CXXFLAGS) -o $@ -c $< -endif - -ifneq "$(origin VALVE_NO_AUTO_P4)" "undefined" - P4_EDIT_START = chmod -R +w - P4_EDIT_END = || true - P4_REVERT_START = true - P4_REVERT_END = -else - ifndef P4_EDIT_CHANGELIST - # You can use an environment variable to specify what changelist to check the Linux Binaries out into. Normally the default - # setting is best, but here is an alternate example: - # export P4_EDIT_CHANGELIST_CMD="echo 1424335" - # ?= means that if P4_EDIT_CHANGELIST_CMD is already set it won't be changed. - P4_EDIT_CHANGELIST_CMD ?= $(P4BIN) changes -c `$(P4BIN) client -o | grep ^Client | cut -f 2` -s pending | fgrep 'POSIX Auto Checkout' | cut -d' ' -f 2 | tail -n 1 - P4_EDIT_CHANGELIST := $(shell $(P4_EDIT_CHANGELIST_CMD)) - endif - ifeq ($(P4_EDIT_CHANGELIST),) - # If we haven't found a changelist to check out to then create one. The name must match the one from a few - # lines above or else a new changelist will be created each time. - # Warning: the behavior of 'echo' is not consistent. In bash you need the "-e" option in order for \n to be - # interpreted as a line-feed, but in dash you do not, and if "-e" is passed along then it is printed, which - # confuses p4. So, if you run this command from the bash shell don't forget to add "-e" to the echo command. - P4_EDIT_CHANGELIST = $(shell echo -e "Change: new\nDescription: POSIX Auto Checkout" | $(P4BIN) change -i | cut -f 2 -d ' ') - endif - - P4_EDIT_START := for f in - P4_EDIT_END := ; do if [ -n $$f ]; then if [ -d $$f ]; then find $$f -type f -print | $(P4BIN) -x - edit -c $(P4_EDIT_CHANGELIST); else $(P4BIN) edit -c $(P4_EDIT_CHANGELIST) $$f; fi; fi; done $(QUIET_ECHO_POSTFIX) - P4_REVERT_START := for f in - P4_REVERT_END := ; do if [ -n $$f ]; then if [ -d $$f ]; then find $$f -type f -print | $(P4BIN) -x - revert; else $(P4BIN) revert $$f; fi; fi; done $(QUIET_ECHO_POSTFIX) -endif - -ifeq ($(CONFTYPE),dll) -all: $(OTHER_DEPENDENCIES) $(OBJS) $(GAMEOUTPUTFILE) - @echo $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX) -else -all: $(OTHER_DEPENDENCIES) $(OBJS) $(OUTPUTFILE) - @echo $(OUTPUTFILE) $(QUIET_ECHO_POSTFIX) -endif - -.PHONY: clean cleantargets cleanandremove rebuild relink RemoveOutputFile SingleFile - - -rebuild : - $(MAKE) -f $(firstword $(MAKEFILE_LIST)) cleanandremove - $(MAKE) -f $(firstword $(MAKEFILE_LIST)) - - -# Use the relink target to force to relink the project. -relink: RemoveOutputFile all - -RemoveOutputFile: - rm -f $(OUTPUTFILE) - - -# This rule is so you can say "make SingleFile SingleFilename=/home/myname/valve_main/src/engine/language.cpp" and have it only build that file. -# It basically just translates the full filename to create a dependency on the appropriate .o file so it'll build that. -SingleFile : RemoveSingleFile $(OBJ_DIR)/$(basename $(notdir $(SingleFilename))).o - @echo "" - -RemoveSingleFile: - $(QUIET_PREFIX) rm -f $(OBJ_DIR)/$(basename $(notdir $(SingleFilename))).o - -clean: -ifneq "$(OBJ_DIR)" "" - $(QUIET_PREFIX) echo "rm -rf $(OBJ_DIR)" - $(QUIET_PREFIX) rm -rf $(OBJ_DIR) -endif -ifneq "$(OUTPUTFILE)" "" - $(QUIET_PREFIX) if [ -e $(OUTPUTFILE) ]; then \ - echo "$(P4BIN) revert $(OUTPUTFILE)"; \ - $(P4_REVERT_START) $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT) $(P4_REVERT_END); \ - fi; -endif -ifneq "$(OTHER_DEPENDENCIES)" "" - $(QUIET_PREFIX) echo "rm -f $(OTHER_DEPENDENCIES)" - $(QUIET_PREFIX) rm -f $(OTHER_DEPENDENCIES) -endif -ifneq "$(GAMEOUTPUTFILE)" "" - $(QUIET_PREFIX) echo "$(P4BIN) revert $(GAMEOUTPUTFILE)" - $(QUIET_PREFIX) $(P4_REVERT_START) $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_REVERT_END) -endif - - -# Do the above cleaning, except with p4 edit and rm. Reason being ar crs adds and replaces obj files to the -# archive. However if you've renamed or deleted a source file, $(AR) won't remove it. This can leave -# us with archive files that have extra unused symbols, and also potentially cause compilation errors -# when you rename a file and have many duplicate symbols. -cleanandremove: -ifneq "$(OBJ_DIR)" "" - $(QUIET_PREFIX) echo "rm -rf $(OBJ_DIR)" - $(QUIET_PREFIX) -rm -rf $(OBJ_DIR) -endif -ifneq "$(OUTPUTFILE)" "" - $(QUIET_PREFIX) if [ -e $(OUTPUTFILE) ]; then \ - echo "$(P4BIN) edit and rm -f $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT)"; \ - $(P4_EDIT_START) $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); \ - fi; - $(QUIET_PREFIX) -rm -f $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT); -endif -ifneq "$(OTHER_DEPENDENCIES)" "" - $(QUIET_PREFIX) echo "rm -f $(OTHER_DEPENDENCIES)" - $(QUIET_PREFIX) -rm -f $(OTHER_DEPENDENCIES) -endif -ifneq "$(GAMEOUTPUTFILE)" "" - $(QUIET_PREFIX) echo "$(P4BIN) edit and rm -f $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT)" - $(QUIET_PREFIX) $(P4_EDIT_START) $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END) - $(QUIET_PREFIX) -rm -f $(GAMEOUTPUTFILE) -endif - - -# This just deletes the final targets so it'll do a relink next time we build. -cleantargets: - $(QUIET_PREFIX) rm -f $(OUTPUTFILE) $(GAMEOUTPUTFILE) - - -$(LIB_File): $(OTHER_DEPENDENCIES) $(OBJS) - $(QUIET_PREFIX) -$(P4_EDIT_START) $(LIB_File) $(P4_EDIT_END); - $(QUIET_PREFIX) $(AR) $(LIB_File) $(OBJS) $(LIBFILES); - -SO_GameOutputFile = $(GAMEOUTPUTFILE) - -# Remove the target before installing a file over it; this prevents existing -# instances of the game from crashing due to the overwrite. -$(SO_GameOutputFile): $(SO_File) - $(QUIET_PREFIX) \ - $(P4_EDIT_START) $(GAMEOUTPUTFILE) $(P4_EDIT_END) && \ - echo "----" $(QUIET_ECHO_POSTFIX);\ - echo "---- COPYING TO $@ [$(CFG)] ----";\ - echo "----" $(QUIET_ECHO_POSTFIX); - $(QUIET_PREFIX) -$(P4_EDIT_START) $(GAMEOUTPUTFILE) $(P4_EDIT_END); - $(QUIET_PREFIX) -mkdir -p `dirname $(GAMEOUTPUTFILE)` > /dev/null; - $(QUIET_PREFIX) rm -f $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX); - $(QUIET_PREFIX) cp -v $(OUTPUTFILE) $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX); - $(QUIET_PREFIX) -$(P4_EDIT_START) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); - $(QUIET_PREFIX) $(GEN_SYM) $(GAMEOUTPUTFILE); - $(QUIET_PREFIX) -$(STRIP) $(GAMEOUTPUTFILE); - $(QUIET_PREFIX) $(VSIGN) -signvalve $(GAMEOUTPUTFILE); - $(QUIET_PREFIX) if [ "$(COPY_DLL_TO_SRV)" = "1" ]; then\ - echo "----" $(QUIET_ECHO_POSTFIX);\ - echo "---- COPYING TO $(Srv_GAMEOUTPUTFILE) ----";\ - echo "----" $(QUIET_ECHO_POSTFIX);\ - cp -v $(GAMEOUTPUTFILE) $(Srv_GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX);\ - cp -v $(GAMEOUTPUTFILE)$(SYM_EXT) $(Srv_GAMEOUTPUTFILE)$(SYM_EXT) $(QUIET_ECHO_POSTFIX);\ - fi; - $(QUIET_PREFIX) if [ "$(IMPORTLIBRARY)" != "" ]; then\ - echo "----" $(QUIET_ECHO_POSTFIX);\ - echo "---- COPYING TO IMPORT LIBRARY $(IMPORTLIBRARY) ----";\ - echo "----" $(QUIET_ECHO_POSTFIX);\ - $(P4_EDIT_START) $(IMPORTLIBRARY) $(P4_EDIT_END) && \ - mkdir -p `dirname $(IMPORTLIBRARY)` > /dev/null && \ - cp -v $(OUTPUTFILE) $(IMPORTLIBRARY); \ - fi; - - -$(SO_File): $(OTHER_DEPENDENCIES) $(OBJS) $(LIBFILENAMES) - $(QUIET_PREFIX) \ - echo "----" $(QUIET_ECHO_POSTFIX);\ - echo "---- LINKING $@ [$(CFG)] ----";\ - echo "----" $(QUIET_ECHO_POSTFIX);\ - \ - $(LINK) $(LINK_MAP_FLAGS) $(SHLIBLDFLAGS) $(PROFILE_LINKER_FLAG) -o $(OUTPUTFILE) $(LIB_START_SHLIB) $(OBJS) $(LIBFILES) $(SystemLibraries) $(LIB_END_SHLIB); - $(VSIGN) -signvalve $(OUTPUTFILE); - - -$(EXE_File) : $(OTHER_DEPENDENCIES) $(OBJS) $(LIBFILENAMES) - $(QUIET_PREFIX) \ - echo "----" $(QUIET_ECHO_POSTFIX);\ - echo "---- LINKING EXE $@ [$(CFG)] ----";\ - echo "----" $(QUIET_ECHO_POSTFIX);\ - \ - $(P4_EDIT_START) $(OUTPUTFILE) $(P4_EDIT_END);\ - $(LINK) $(LINK_MAP_FLAGS) $(LDFLAGS) $(PROFILE_LINKER_FLAG) -o $(OUTPUTFILE) $(LIB_START_EXE) $(OBJS) $(LIBFILES) $(SystemLibraries) $(LIB_END_EXE); - $(QUIET_PREFIX) -$(P4_EDIT_START) $(OUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); - $(QUIET_PREFIX) $(GEN_SYM) $(OUTPUTFILE); - $(QUIET_PREFIX) -$(STRIP) $(OUTPUTFILE); - $(QUIET_PREFIX) $(VSIGN) -signvalve $(OUTPUTFILE); - - -tags: - etags -a -C -o $(SRCROOT)/TAGS *.cpp *.cxx *.h *.hxx +$(MAKEFILE_LINK): $(shell which $(CC)) $(THISFILE) + if [ "$(shell printf "$(shell $(CC) -dumpversion)\n8" | sort -Vr | head -1)" = 8 ]; then \ + $(COMPILE.cpp) -o gcc9+support.o gcc9+support.c ;\ + ln -sf $(MAKEFILE_BASE).default $@ ;\ + else \ + ln -sf $(MAKEFILE_BASE).gcc8 $@ ;\ + fi diff --git a/mp/src/devtools/makefile_base_posix.mak.default b/mp/src/devtools/makefile_base_posix.mak.default new file mode 100644 index 00000000..d86efb38 --- /dev/null +++ b/mp/src/devtools/makefile_base_posix.mak.default @@ -0,0 +1,509 @@ +# +# Base makefile for Linux and OSX +# +# !!!!! Note to future editors !!!!! +# +# before you make changes, make sure you grok: +# 1. the difference between =, :=, +=, and ?= +# 2. how and when this base makefile gets included in the generated makefile(s) +# ( see http://www.gnu.org/software/make/manual/make.html#Flavors ) +# +# Command line prefixes: +# - errors are ignored +# @ command is not printed to stdout before being executed +# + command is executed even if Make is invoked in "do not exec" mode + +OS := $(shell uname) +HOSTNAME := $(shell hostname) + +-include $(SRCROOT)/devtools/steam_def.mak +-include $(SRCROOT)/devtools/sourcesdk_def.mak + +# To build with clang, set the following in your environment: +# CC = clang +# CXX = clang++ + +ifeq ($(CFG), release) + # With gcc 4.6.3, engine.so went from 7,383,765 to 8,429,109 when building with -O3. + # There also was no speed difference running at 1280x1024. May 2012, mikesart. + # tonyp: The size increase was likely caused by -finline-functions and -fipa-cp-clone getting switched on with -O3. + # -fno-omit-frame-pointer: need this for stack traces with perf. + OptimizerLevel_CompilerSpecific = -O2 -fno-strict-aliasing -ffast-math -fno-omit-frame-pointer -ftree-vectorize -fpredictive-commoning -funswitch-loops +else + OptimizerLevel_CompilerSpecific = -O0 + #-O1 -finline-functions +endif + +# CPPFLAGS == "c/c++ *preprocessor* flags" - not "cee-plus-plus flags" +ARCH_FLAGS = +BUILDING_MULTI_ARCH = 0 +CPPFLAGS = $(DEFINES) $(addprefix -I, $(abspath $(INCLUDEDIRS) )) +CFLAGS = $(ARCH_FLAGS) $(CPPFLAGS) $(WARN_FLAGS) -fvisibility=$(SymbolVisibility) $(OptimizerLevel) -pipe $(GCC_ExtraCompilerFlags) -Usprintf -Ustrncpy -UPROTECTED_THINGS_ENABLE +# In -std=gnu++0x mode we get lots of errors about "error: narrowing conversion". -fpermissive +# turns these into warnings in gcc, and -Wno-c++11-narrowing suppresses them entirely in clang 3.1+. +ifeq ($(CXX),clang++) + CXXFLAGS = $(CFLAGS) -std=gnu++0x -Wno-c++11-narrowing -Wno-dangling-else +else + CXXFLAGS = $(CFLAGS) -std=gnu++0x -fpermissive +endif +DEFINES += -DVPROF_LEVEL=1 -DGNUC -DNO_HOOK_MALLOC -DNO_MALLOC_OVERRIDE +LDFLAGS = $(CFLAGS) $(GCC_ExtraLinkerFlags) $(OptimizerLevel) +GENDEP_CXXFLAGS = -MD -MP -MF $(@:.o=.P) +MAP_FLAGS = +Srv_GAMEOUTPUTFILE = +COPY_DLL_TO_SRV = 0 + + +ifeq ($(STEAM_BRANCH),1) + WARN_FLAGS = -Wall -Wextra -Wshadow -Wno-invalid-offsetof +else + WARN_FLAGS = -Wno-write-strings -Wno-multichar +endif + +WARN_FLAGS += -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-sign-compare -Wno-reorder -Wno-invalid-offsetof -Wno-float-equal -Werror=return-type -fdiagnostics-show-option -Wformat -Wformat-security + + +ifeq ($(OS),Linux) + # We should always specify -Wl,--build-id, as documented at: + # http://linux.die.net/man/1/ld and http://fedoraproject.org/wiki/Releases/FeatureBuildId.http://fedoraproject.org/wiki/Releases/FeatureBuildId + LDFLAGS += -Wl,--build-id + # Set USE_VALVE_BINDIR to build with /Steam/tools/linux in the /valve/bin path. + # Dedicated server uses this. + ifeq ($(USE_VALVE_BINDIR),1) + # dedicated server flags + ifeq ($(TARGET_PLATFORM),linux64) + VALVE_BINDIR = /valve/bin64/ + MARCH_TARGET = nocona + else + VALVE_BINDIR = /valve/bin/ + MARCH_TARGET = pentium4 + endif + STRIP_FLAGS = + else + # linux desktop client flags + VALVE_BINDIR = + # If the steam-runtime is available, use it. We should just default to using it when + # buildbot and everyone has a bit of time to get it installed. + ifneq "$(wildcard /valve/steam-runtime/bin/)" "" + # The steam-runtime is incompatible with clang at this point, so disable it + # if clang is enabled. + ifneq ($(CXX),clang++) + VALVE_BINDIR = /valve/steam-runtime/bin/ + endif + endif + GCC_VER = + MARCH_TARGET = pentium4 + # On dedicated servers, some plugins depend on global variable symbols in addition to functions. + # So symbols like _Z16ClearMultiDamagev should show up when you do "nm server_srv.so" in TF2. + STRIP_FLAGS = -x + endif + + ifeq ($(CXX),clang++) + # Clang does not support -mfpmath=sse because it uses whatever + # instruction set extensions are available by default. + SSE_GEN_FLAGS = -msse2 + else + SSE_GEN_FLAGS = -msse2 -mfpmath=sse + endif + + CCACHE := $(SRCROOT)/devtools/bin/linux/ccache + + ifeq ($(origin GCC_VER), undefined) + GCC_VER=-4.6 + endif + ifeq ($(origin AR), default) + AR = $(VALVE_BINDIR)ar crs + endif + ifeq ($(origin CC),default) + CC = $(CCACHE) $(VALVE_BINDIR)gcc$(GCC_VER) + endif + ifeq ($(origin CXX), default) + CXX = $(CCACHE) $(VALVE_BINDIR)g++$(GCC_VER) + endif + # Support ccache with clang. Add -Qunused-arguments to avoid excessive warnings due to + # a ccache quirk. Could also upgrade ccache. + # http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html + ifeq ($(CC),clang) + CC = $(CCACHE) $(VALVE_BINDIR)clang -Qunused-arguments + endif + ifeq ($(CXX),clang++) + CXX = $(CCACHE) $(VALVE_BINDIR)clang++ -Qunused-arguments + endif + LINK ?= $(CC) + + ifeq ($(TARGET_PLATFORM),linux64) + # nocona = pentium4 + 64bit + MMX, SSE, SSE2, SSE3 - no SSSE3 (that's three s's - added in core2) + ARCH_FLAGS += -march=$(MARCH_TARGET) -mtune=core2 + LD_SO = ld-linux-x86_64.so.2 + LIBSTDCXX := $(shell $(CXX) -print-file-name=libstdc++.a) + LIBSTDCXXPIC := $(shell $(CXX) -print-file-name=libstdc++-pic.a) + else + # pentium4 = MMX, SSE, SSE2 - no SSE3 (added in prescott) # -msse3 -mfpmath=sse + ARCH_FLAGS += -m32 -march=$(MARCH_TARGET) -mtune=core2 $(SSE_GEN_FLAGS) + LD_SO = ld-linux.so.2 + LIBSTDCXX := $(shell $(CXX) $(ARCH_FLAGS) -print-file-name=libstdc++.so) + LIBSTDCXXPIC := $(shell $(CXX) $(ARCH_FLAGS) -print-file-name=libstdc++.so) + LDFLAGS += -m32 + endif + + GEN_SYM ?= $(SRCROOT)/devtools/gendbg.sh + ifeq ($(CFG),release) + STRIP ?= strip $(STRIP_FLAGS) -S + # CFLAGS += -ffunction-sections -fdata-sections + # LDFLAGS += -Wl,--gc-sections -Wl,--print-gc-sections + else + STRIP ?= true + endif + VSIGN ?= true + + ifeq ($(SOURCE_SDK), 1) + Srv_GAMEOUTPUTFILE := $(GAMEOUTPUTFILE:.so=_srv.so) + COPY_DLL_TO_SRV := 1 + endif + + LINK_MAP_FLAGS = -Wl,-Map,$(@:.so=).map + + SHLIBLDFLAGS = -shared $(LDFLAGS) -Wl,--no-undefined + + _WRAP := -Xlinker --wrap= + PATHWRAP = $(_WRAP)fopen $(_WRAP)freopen $(_WRAP)open $(_WRAP)creat $(_WRAP)access $(_WRAP)__xstat \ + $(_WRAP)stat $(_WRAP)lstat $(_WRAP)fopen64 $(_WRAP)open64 $(_WRAP)opendir $(_WRAP)__lxstat \ + $(_WRAP)chmod $(_WRAP)chown $(_WRAP)lchown $(_WRAP)symlink $(_WRAP)link $(_WRAP)__lxstat64 \ + $(_WRAP)mknod $(_WRAP)utimes $(_WRAP)unlink $(_WRAP)rename $(_WRAP)utime $(_WRAP)__xstat64 \ + $(_WRAP)mount $(_WRAP)mkfifo $(_WRAP)mkdir $(_WRAP)rmdir $(_WRAP)scandir $(_WRAP)realpath + + LIB_START_EXE = $(PATHWRAP) -static-libgcc -Wl,--start-group + LIB_END_EXE = -Wl,--end-group -lm -ldl $(LIBSTDCXX) -lpthread + + LIB_START_SHLIB = $(PATHWRAP) -static-libgcc -Wl,--start-group + LIB_END_SHLIB = -Wl,--end-group -lm -ldl $(LIBSTDCXXPIC) -lpthread -l:$(LD_SO) -Wl,--version-script=$(SRCROOT)/devtools/version_script.linux.txt + +endif + +ifeq ($(OS),Darwin) + CCACHE := $(SRCROOT)/devtools/bin/osx32/ccache + MAC_SDK_VER ?= 10.6 + MAC_SDK := macosx$(MAC_SDK_VER) + SYSROOT := $(shell xcodebuild -sdk $(MAC_SDK) -version Path) + + ifneq ($(origin MAC_SDK_VER), file) + $(warning Attempting build with SDK version $(MAC_SDK_VER), only 10.6 is supported and recommended!) + endif + + ifeq ($(SYSROOT),) + FIRSTSDK := $(firstword $(sort $(shell xcodebuild -showsdks | grep macosx | sed 's/.*macosx//'))) + $(error Could not find SDK version $(MAC_SDK_VER). Install and configure Xcode 4.3, or build with: make MAC_SDK_VER=$(FIRSTSDK)) + endif + + ifeq ($(origin CC), default) + # Test to see if you have a compiler in the right place, if you + # don't abort with an error + CLANG := $(shell xcrun -sdk $(MAC_SDK) -find clang) + ifeq ($(wildcard $(CLANG)),) + $(error Unable to find C compiler, install and configure Xcode 4.3) + endif + + CC := $(CCACHE) $(CLANG) -Qunused-arguments + endif + + ifeq ($(origin CXX), default) + CXXLANG := $(shell xcrun -sdk $(MAC_SDK) -find clang++) + ifeq ($(wildcard $(CXXLANG)),) + $(error Unable to find C++ compiler, install and configure Xcode 4.3) + endif + + CXX := $(CCACHE) $(CXXLANG) -Qunused-arguments + endif + LINK ?= $(CXX) + + ifeq ($(origin AR), default) + AR := $(shell xcrun -sdk $(MAC_SDK) -find libtool) -static -o + endif + + ifeq ($(TARGET_PLATFORM),osx64) + ARCH_FLAGS += -arch x86_64 -m64 -march=core2 + else ifeq (,$(findstring -arch x86_64,$(GCC_ExtraCompilerFlags))) + ARCH_FLAGS += -arch i386 -m32 -march=prescott -momit-leaf-frame-pointer -mtune=core2 + else + # dirty hack to build a universal binary - don't specify the architecture + ARCH_FLAGS += -arch i386 -Xarch_i386 -march=prescott -Xarch_i386 -mtune=core2 -Xarch_i386 -momit-leaf-frame-pointer -Xarch_x86_64 -march=core2 + endif + + GEN_SYM ?= $(shell xcrun -sdk $(MAC_SDK) -find dsymutil) + ifeq ($(CFG),release) + STRIP ?= strip -S + else + STRIP ?= true + endif + ifeq ($(SOURCE_SDK), 1) + VSIGN ?= true + else + VSIGN ?= $(SRCROOT)/devtools/bin/vsign + endif + + CPPFLAGS += -I$(SYSROOT)/usr/include/malloc + CFLAGS += -isysroot $(SYSROOT) -mmacosx-version-min=10.5 -fasm-blocks + + LIB_START_EXE = -lm -ldl -lpthread + LIB_END_EXE = + + LIB_START_SHLIB = + LIB_END_SHLIB = + + SHLIBLDFLAGS = $(LDFLAGS) -bundle -flat_namespace -undefined suppress -Wl,-dead_strip -Wl,-no_dead_strip_inits_and_terms + + ifeq (lib,$(findstring lib,$(GAMEOUTPUTFILE))) + SHLIBLDFLAGS = $(LDFLAGS) -dynamiclib -current_version 1.0 -compatibility_version 1.0 -install_name @rpath/$(basename $(notdir $(GAMEOUTPUTFILE))).dylib $(SystemLibraries) -Wl,-dead_strip -Wl,-no_dead_strip_inits_and_terms + endif + +endif + +# +# Profile-directed optimizations. +# Note: Last time these were tested 3/5/08, it actually slowed down the server benchmark by 5%! +# +# First, uncomment these, build, and test. It will generate .gcda and .gcno files where the .o files are. +# PROFILE_LINKER_FLAG=-fprofile-arcs +# PROFILE_COMPILER_FLAG=-fprofile-arcs +# +# Then, comment the above flags out again and rebuild with this flag uncommented: +# PROFILE_COMPILER_FLAG=-fprofile-use +# + +############################################################################# +# The compiler command lne for each src code file to compile +############################################################################# + +OBJ_DIR = ./obj_$(NAME)_$(TARGET_PLATFORM)$(TARGET_PLATFORM_EXT)/$(CFG) +CPP_TO_OBJ = $(CPPFILES:.cpp=.o) +CXX_TO_OBJ = $(CPP_TO_OBJ:.cxx=.o) +CC_TO_OBJ = $(CXX_TO_OBJ:.cc=.o) +MM_TO_OBJ = $(CC_TO_OBJ:.mm=.o) +C_TO_OBJ = $(MM_TO_OBJ:.c=.o) +OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(C_TO_OBJ))) + +ifeq ($(MAKE_VERBOSE),1) + QUIET_PREFIX = + QUIET_ECHO_POSTFIX = +else + QUIET_PREFIX = @ + QUIET_ECHO_POSTFIX = > /dev/null +endif + +ifeq ($(MAKE_CC_VERBOSE),1) +CC += -v +endif + +ifeq ($(CONFTYPE),lib) + LIB_File = $(OUTPUTFILE) +endif + +ifeq ($(CONFTYPE),dll) + SO_File = $(OUTPUTFILE) +endif + +ifeq ($(CONFTYPE),exe) + EXE_File = $(OUTPUTFILE) +endif + +# we generate dependencies as a side-effect of compilation now +GEN_DEP_FILE= + +PRE_COMPILE_FILE = + +POST_COMPILE_FILE = + +ifeq ($(BUILDING_MULTI_ARCH),1) + SINGLE_ARCH_CXXFLAGS=$(subst -arch x86_64,,$(CXXFLAGS)) + COMPILE_FILE = \ + $(QUIET_PREFIX) \ + echo "---- $(lastword $(subst /, ,$<)) as MULTIARCH----";\ + mkdir -p $(OBJ_DIR) && \ + $(CXX) $(SINGLE_ARCH_CXXFLAGS) $(GENDEP_CXXFLAGS) -o $@ -c $< && \ + $(CXX) $(CXXFLAGS) -o $@ -c $< +else + COMPILE_FILE = \ + $(QUIET_PREFIX) \ + echo "---- $(lastword $(subst /, ,$<)) ----";\ + mkdir -p $(OBJ_DIR) && \ + $(CXX) $(CXXFLAGS) $(GENDEP_CXXFLAGS) -o $@ -c $< +endif + +ifneq "$(origin VALVE_NO_AUTO_P4)" "undefined" + P4_EDIT_START = chmod -R +w + P4_EDIT_END = || true + P4_REVERT_START = true + P4_REVERT_END = +else + ifndef P4_EDIT_CHANGELIST + # You can use an environment variable to specify what changelist to check the Linux Binaries out into. Normally the default + # setting is best, but here is an alternate example: + # export P4_EDIT_CHANGELIST_CMD="echo 1424335" + # ?= means that if P4_EDIT_CHANGELIST_CMD is already set it won't be changed. + P4_EDIT_CHANGELIST_CMD ?= p4 changes -c `p4 client -o | grep ^Client | cut -f 2` -s pending | fgrep 'POSIX Auto Checkout' | cut -d' ' -f 2 | tail -n 1 + P4_EDIT_CHANGELIST := $(shell $(P4_EDIT_CHANGELIST_CMD)) + endif + ifeq ($(P4_EDIT_CHANGELIST),) + # If we haven't found a changelist to check out to then create one. The name must match the one from a few + # lines above or else a new changelist will be created each time. + # Warning: the behavior of 'echo' is not consistent. In bash you need the "-e" option in order for \n to be + # interpreted as a line-feed, but in dash you do not, and if "-e" is passed along then it is printed, which + # confuses p4. So, if you run this command from the bash shell don't forget to add "-e" to the echo command. + P4_EDIT_CHANGELIST = $(shell echo "Change: new\nDescription: POSIX Auto Checkout" | p4 change -i | cut -f 2 -d ' ') + endif + + P4_EDIT_START := for f in + P4_EDIT_END := ; do if [ -n $$f ]; then if [ -d $$f ]; then find $$f -type f -print | p4 -x - edit -c $(P4_EDIT_CHANGELIST); else p4 edit -c $(P4_EDIT_CHANGELIST) $$f; fi; fi; done $(QUIET_ECHO_POSTFIX) + P4_REVERT_START := for f in + P4_REVERT_END := ; do if [ -n $$f ]; then if [ -d $$f ]; then find $$f -type f -print | p4 -x - revert; else p4 revert $$f; fi; fi; done $(QUIET_ECHO_POSTFIX) +endif + +ifeq ($(CONFTYPE),dll) +all: $(OTHER_DEPENDENCIES) $(OBJS) $(GAMEOUTPUTFILE) + @echo $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX) +else +all: $(OTHER_DEPENDENCIES) $(OBJS) $(OUTPUTFILE) + @echo $(OUTPUTFILE) $(QUIET_ECHO_POSTFIX) +endif + +.PHONY: clean cleantargets cleanandremove rebuild relink RemoveOutputFile SingleFile + + +rebuild : + $(MAKE) -f $(firstword $(MAKEFILE_LIST)) cleanandremove + $(MAKE) -f $(firstword $(MAKEFILE_LIST)) + + +# Use the relink target to force to relink the project. +relink: RemoveOutputFile all + +RemoveOutputFile: + rm -f $(OUTPUTFILE) + + +# This rule is so you can say "make SingleFile SingleFilename=/home/myname/valve_main/src/engine/language.cpp" and have it only build that file. +# It basically just translates the full filename to create a dependency on the appropriate .o file so it'll build that. +SingleFile : RemoveSingleFile $(OBJ_DIR)/$(basename $(notdir $(SingleFilename))).o + @echo "" + +RemoveSingleFile: + $(QUIET_PREFIX) rm -f $(OBJ_DIR)/$(basename $(notdir $(SingleFilename))).o + +clean: +ifneq "$(OBJ_DIR)" "" + $(QUIET_PREFIX) echo "rm -rf $(OBJ_DIR)" + $(QUIET_PREFIX) rm -rf $(OBJ_DIR) +endif +ifneq "$(OUTPUTFILE)" "" + $(QUIET_PREFIX) if [ -e $(OUTPUTFILE) ]; then \ + echo "p4 revert $(OUTPUTFILE)"; \ + $(P4_REVERT_START) $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT) $(P4_REVERT_END); \ + fi; +endif +ifneq "$(OTHER_DEPENDENCIES)" "" + $(QUIET_PREFIX) echo "rm -f $(OTHER_DEPENDENCIES)" + $(QUIET_PREFIX) rm -f $(OTHER_DEPENDENCIES) +endif +ifneq "$(GAMEOUTPUTFILE)" "" + $(QUIET_PREFIX) echo "p4 revert $(GAMEOUTPUTFILE)" + $(QUIET_PREFIX) $(P4_REVERT_START) $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_REVERT_END) +endif + + +# Do the above cleaning, except with p4 edit and rm. Reason being ar crs adds and replaces obj files to the +# archive. However if you've renamed or deleted a source file, $(AR) won't remove it. This can leave +# us with archive files that have extra unused symbols, and also potentially cause compilation errors +# when you rename a file and have many duplicate symbols. +cleanandremove: +ifneq "$(OBJ_DIR)" "" + $(QUIET_PREFIX) echo "rm -rf $(OBJ_DIR)" + $(QUIET_PREFIX) -rm -rf $(OBJ_DIR) +endif +ifneq "$(OUTPUTFILE)" "" + $(QUIET_PREFIX) if [ -e $(OUTPUTFILE) ]; then \ + echo "p4 edit and rm -f $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT)"; \ + $(P4_EDIT_START) $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); \ + fi; + $(QUIET_PREFIX) -rm -f $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT); +endif +ifneq "$(OTHER_DEPENDENCIES)" "" + $(QUIET_PREFIX) echo "rm -f $(OTHER_DEPENDENCIES)" + $(QUIET_PREFIX) -rm -f $(OTHER_DEPENDENCIES) +endif +ifneq "$(GAMEOUTPUTFILE)" "" + $(QUIET_PREFIX) echo "p4 edit and rm -f $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT)" + $(QUIET_PREFIX) $(P4_EDIT_START) $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END) + $(QUIET_PREFIX) -rm -f $(GAMEOUTPUTFILE) +endif + + +# This just deletes the final targets so it'll do a relink next time we build. +cleantargets: + $(QUIET_PREFIX) rm -f $(OUTPUTFILE) $(GAMEOUTPUTFILE) + + +$(LIB_File): $(OTHER_DEPENDENCIES) $(OBJS) + $(QUIET_PREFIX) -$(P4_EDIT_START) $(LIB_File) $(P4_EDIT_END); + $(QUIET_PREFIX) $(AR) $(LIB_File) $(OBJS) $(LIBFILES); + +SO_GameOutputFile = $(GAMEOUTPUTFILE) + +# Remove the target before installing a file over it; this prevents existing +# instances of the game from crashing due to the overwrite. +$(SO_GameOutputFile): $(SO_File) + $(QUIET_PREFIX) \ + $(P4_EDIT_START) $(GAMEOUTPUTFILE) $(P4_EDIT_END) && \ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- COPYING TO $@ [$(CFG)] ----";\ + echo "----" $(QUIET_ECHO_POSTFIX); + $(QUIET_PREFIX) -$(P4_EDIT_START) $(GAMEOUTPUTFILE) $(P4_EDIT_END); + $(QUIET_PREFIX) -mkdir -p `dirname $(GAMEOUTPUTFILE)` > /dev/null; + $(QUIET_PREFIX) rm -f $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX); + $(QUIET_PREFIX) cp -v $(OUTPUTFILE) $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX); + $(QUIET_PREFIX) -$(P4_EDIT_START) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); + $(QUIET_PREFIX) $(GEN_SYM) $(GAMEOUTPUTFILE); + $(QUIET_PREFIX) -$(STRIP) $(GAMEOUTPUTFILE); + $(QUIET_PREFIX) $(VSIGN) -signvalve $(GAMEOUTPUTFILE); + $(QUIET_PREFIX) if [ "$(COPY_DLL_TO_SRV)" = "1" ]; then\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- COPYING TO $(Srv_GAMEOUTPUTFILE) ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + cp -v $(GAMEOUTPUTFILE) $(Srv_GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX);\ + cp -v $(GAMEOUTPUTFILE)$(SYM_EXT) $(Srv_GAMEOUTPUTFILE)$(SYM_EXT) $(QUIET_ECHO_POSTFIX);\ + fi; + $(QUIET_PREFIX) if [ "$(IMPORTLIBRARY)" != "" ]; then\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- COPYING TO IMPORT LIBRARY $(IMPORTLIBRARY) ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + $(P4_EDIT_START) $(IMPORTLIBRARY) $(P4_EDIT_END) && \ + mkdir -p `dirname $(IMPORTLIBRARY)` > /dev/null && \ + cp -v $(OUTPUTFILE) $(IMPORTLIBRARY); \ + fi; + + +$(SO_File): $(OTHER_DEPENDENCIES) $(OBJS) $(LIBFILENAMES) + $(QUIET_PREFIX) \ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- LINKING $@ [$(CFG)] ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + \ + $(LINK) $(LINK_MAP_FLAGS) $(SHLIBLDFLAGS) $(PROFILE_LINKER_FLAG) -o $(OUTPUTFILE) $(LIB_START_SHLIB) $(OBJS) $(LIBFILES) $(SystemLibraries) $(LIB_END_SHLIB); + $(VSIGN) -signvalve $(OUTPUTFILE); + + +$(EXE_File) : $(OTHER_DEPENDENCIES) $(OBJS) $(LIBFILENAMES) + $(QUIET_PREFIX) \ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- LINKING EXE $@ [$(CFG)] ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + \ + $(P4_EDIT_START) $(OUTPUTFILE) $(P4_EDIT_END);\ + $(LINK) $(LINK_MAP_FLAGS) $(LDFLAGS) $(PROFILE_LINKER_FLAG) -o $(OUTPUTFILE) $(LIB_START_EXE) $(OBJS) $(LIBFILES) $(SystemLibraries) $(LIB_END_EXE); + $(QUIET_PREFIX) -$(P4_EDIT_START) $(OUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); + $(QUIET_PREFIX) $(GEN_SYM) $(OUTPUTFILE); + $(QUIET_PREFIX) -$(STRIP) $(OUTPUTFILE); + $(QUIET_PREFIX) $(VSIGN) -signvalve $(OUTPUTFILE); + + +tags: + etags -a -C -o $(SRCROOT)/TAGS *.cpp *.cxx *.h *.hxx diff --git a/mp/src/devtools/makefile_base_posix.mak.gcc8 b/mp/src/devtools/makefile_base_posix.mak.gcc8 new file mode 100644 index 00000000..97178245 --- /dev/null +++ b/mp/src/devtools/makefile_base_posix.mak.gcc8 @@ -0,0 +1,507 @@ +# +# Base makefile for Linux and OSX +# +# !!!!! Note to future editors !!!!! +# +# before you make changes, make sure you grok: +# 1. the difference between =, :=, +=, and ?= +# 2. how and when this base makefile gets included in the generated makefile(s) +# ( see http://www.gnu.org/software/make/manual/make.html#Flavors ) +# +# Command line prefixes: +# - errors are ignored +# @ command is not printed to stdout before being executed +# + command is executed even if Make is invoked in "do not exec" mode + +OS := $(shell uname) +HOSTNAME := $(shell hostname) + +-include $(SRCROOT)/devtools/steam_def.mak +-include $(SRCROOT)/devtools/sourcesdk_def.mak + +# To build with clang, set the following in your environment: +# CC = clang +# CXX = clang++ + +ifeq ($(CFG), release) + # With gcc 4.6.3, engine.so went from 7,383,765 to 8,429,109 when building with -O3. + # There also was no speed difference running at 1280x1024. May 2012, mikesart. + # tonyp: The size increase was likely caused by -finline-functions and -fipa-cp-clone getting switched on with -O3. + # -fno-omit-frame-pointer: need this for stack traces with perf. + OptimizerLevel_CompilerSpecific = -O2 -fno-strict-aliasing -ffast-math -fno-omit-frame-pointer -ftree-vectorize -fpredictive-commoning -funswitch-loops +else + OptimizerLevel_CompilerSpecific = -Og + #-O1 -finline-functions +endif + +# CPPFLAGS == "c/c++ *preprocessor* flags" - not "cee-plus-plus flags" +ARCH_FLAGS = -fabi-compat-version=2 -fabi-version=2 -fpic -fno-plt -fcf-protection=none -fno-stack-protector -fno-stack-clash-protection +BUILDING_MULTI_ARCH = 0 +CPPFLAGS = $(DEFINES) $(addprefix -I, $(abspath $(INCLUDEDIRS) )) +CFLAGS = $(ARCH_FLAGS) $(CPPFLAGS) $(WARN_FLAGS) -fvisibility=$(SymbolVisibility) $(OptimizerLevel) -pipe $(GCC_ExtraCompilerFlags) -Usprintf -Ustrncpy -UPROTECTED_THINGS_ENABLE +# In -std=gnu++0x mode we get lots of errors about "error: narrowing conversion". -fpermissive +# turns these into warnings in gcc, and -Wno-c++11-narrowing suppresses them entirely in clang 3.1+. +ifeq ($(CXX),clang++) + CXXFLAGS = $(CFLAGS) -std=gnu++0x -Wno-c++11-narrowing -Wno-dangling-else +else + CXXFLAGS = $(CFLAGS) -std=gnu++0x -Wno-narrowing -fpermissive +endif +DEFINES += -DVPROF_LEVEL=1 -DGNUC -DNO_HOOK_MALLOC -DNO_MALLOC_OVERRIDE -D_GLIBCXX_USE_CXX11_ABI=0 +LDFLAGS = $(CFLAGS) $(GCC_ExtraLinkerFlags) $(OptimizerLevel) -fuse-ld=gold +GENDEP_CXXFLAGS = -MD -MP -MF $(@:.o=.P) +MAP_FLAGS = +Srv_GAMEOUTPUTFILE = +COPY_DLL_TO_SRV = 0 + + +ifeq ($(STEAM_BRANCH),1) + WARN_FLAGS = -Wall -Wextra -Wshadow -Wno-invalid-offsetof +else + WARN_FLAGS = -Wno-write-strings -Wno-multichar +endif + +WARN_FLAGS += -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-sign-compare -Wno-reorder -Wno-invalid-offsetof -Wno-float-equal -Werror=return-type -fdiagnostics-show-option -Wformat -Wformat-security + + +ifeq ($(OS),Linux) + # We should always specify -Wl,--build-id, as documented at: + # http://linux.die.net/man/1/ld and http://fedoraproject.org/wiki/Releases/FeatureBuildId.http://fedoraproject.org/wiki/Releases/FeatureBuildId + LDFLAGS += -Wl,--build-id + # Set USE_VALVE_BINDIR to build with /Steam/tools/linux in the /valve/bin path. + # Dedicated server uses this. + ifeq ($(USE_VALVE_BINDIR),1) + # dedicated server flags + ifeq ($(TARGET_PLATFORM),linux64) + VALVE_BINDIR = /valve/bin64/ + else + VALVE_BINDIR = /valve/bin/ + endif + STRIP_FLAGS = + else + # linux desktop client flags + VALVE_BINDIR = + # If the steam-runtime is available, use it. We should just default to using it when + # buildbot and everyone has a bit of time to get it installed. + ifneq "$(wildcard /valve/steam-runtime/bin/)" "" + # The steam-runtime is incompatible with clang at this point, so disable it + # if clang is enabled. + ifneq ($(CXX),clang++) + VALVE_BINDIR = /valve/steam-runtime/bin/ + endif + endif + GCC_VER = + # On dedicated servers, some plugins depend on global variable symbols in addition to functions. + # So symbols like _Z16ClearMultiDamagev should show up when you do "nm server_srv.so" in TF2. + STRIP_FLAGS = -x + endif + # nocona = MMX, SSE, SSE2, SSE3 + MARCH_TARGET = nocona + MTUNE_TARGET = generic + + ifeq ($(CXX),clang++) + # Clang does not support -mfpmath=sse because it uses whatever + # instruction set extensions are available by default. + SSE_GEN_FLAGS = -msse2 + else + SSE_GEN_FLAGS = -msse2 -mfpmath=sse + endif + + CCACHE := $(SRCROOT)/devtools/bin/linux/ccache + + ifeq ($(origin GCC_VER), undefined) + GCC_VER=-4.6 + endif + ifeq ($(origin AR), default) + AR = $(VALVE_BINDIR)ar crs + endif + ifeq ($(origin CC),default) + CC = $(CCACHE) $(VALVE_BINDIR)gcc$(GCC_VER) + endif + ifeq ($(origin CXX), default) + CXX = $(CCACHE) $(VALVE_BINDIR)g++$(GCC_VER) + endif + # Support ccache with clang. Add -Qunused-arguments to avoid excessive warnings due to + # a ccache quirk. Could also upgrade ccache. + # http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html + ifeq ($(CC),clang) + CC = $(CCACHE) $(VALVE_BINDIR)clang -Qunused-arguments + endif + ifeq ($(CXX),clang++) + CXX = $(CCACHE) $(VALVE_BINDIR)clang++ -Qunused-arguments + endif + LINK ?= $(CC) + + ifeq ($(TARGET_PLATFORM),linux64) + ARCH_FLAGS += -march=$(MARCH_TARGET) -mtune=$(MTUNE_TARGET) + LD_SO = ld-linux-x86_64.so.2 + LIBSTDCXX := $(shell $(CXX) -print-file-name=libstdc++.a) + LIBSTDCXXPIC := $(shell $(CXX) -print-file-name=libstdc++-pic.a) + else + ARCH_FLAGS += -m32 -march=$(MARCH_TARGET) -mtune=$(MTUNE_TARGET) + LD_SO = ld-linux.so.2 + LIBSTDCXX := $(shell $(CXX) $(ARCH_FLAGS) -print-file-name=libstdc++.so) + LIBSTDCXXPIC := $(shell $(CXX) $(ARCH_FLAGS) -print-file-name=libstdc++.so) + LDFLAGS += -m32 + endif + + GEN_SYM ?= $(SRCROOT)/devtools/gendbg.sh + ifeq ($(CFG),release) + STRIP ?= strip $(STRIP_FLAGS) -S + # CFLAGS += -ffunction-sections -fdata-sections + # LDFLAGS += -Wl,--gc-sections -Wl,--print-gc-sections + else + STRIP ?= true + endif + VSIGN ?= true + + ifeq ($(SOURCE_SDK), 1) + Srv_GAMEOUTPUTFILE := $(GAMEOUTPUTFILE:.so=_srv.so) + COPY_DLL_TO_SRV := 1 + endif + + LINK_MAP_FLAGS = -Wl,-Map,$(@:.so=).map + + SHLIBLDFLAGS = -shared $(LDFLAGS) -Wl,--no-undefined + + _WRAP := -Xlinker --wrap= + PATHWRAP = $(_WRAP)fopen $(_WRAP)freopen $(_WRAP)open $(_WRAP)creat $(_WRAP)access $(_WRAP)__xstat \ + $(_WRAP)stat $(_WRAP)lstat $(_WRAP)fopen64 $(_WRAP)open64 $(_WRAP)opendir $(_WRAP)__lxstat \ + $(_WRAP)chmod $(_WRAP)chown $(_WRAP)lchown $(_WRAP)symlink $(_WRAP)link $(_WRAP)__lxstat64 \ + $(_WRAP)mknod $(_WRAP)utimes $(_WRAP)unlink $(_WRAP)rename $(_WRAP)utime $(_WRAP)__xstat64 \ + $(_WRAP)mount $(_WRAP)mkfifo $(_WRAP)mkdir $(_WRAP)rmdir $(_WRAP)scandir $(_WRAP)realpath + + LIB_START_EXE = $(PATHWRAP) -static-libgcc -Wl,--start-group + LIB_END_EXE = -Wl,--end-group -lm -ldl $(LIBSTDCXX) -lpthread + + LIB_START_SHLIB = $(PATHWRAP) -static-libgcc -Wl,--start-group + LIB_END_SHLIB = -Wl,--end-group $(SRCROOT)/devtools/gcc9+support.o -lm -ldl $(LIBSTDCXXPIC) -lpthread -l:$(LD_SO) -Wl,--version-script=$(SRCROOT)/devtools/version_script.linux.txt + +endif + +ifeq ($(OS),Darwin) + CCACHE := $(SRCROOT)/devtools/bin/osx32/ccache + MAC_SDK_VER ?= 10.6 + MAC_SDK := macosx$(MAC_SDK_VER) + SYSROOT := $(shell xcodebuild -sdk $(MAC_SDK) -version Path) + + ifneq ($(origin MAC_SDK_VER), file) + $(warning Attempting build with SDK version $(MAC_SDK_VER), only 10.6 is supported and recommended!) + endif + + ifeq ($(SYSROOT),) + FIRSTSDK := $(firstword $(sort $(shell xcodebuild -showsdks | grep macosx | sed 's/.*macosx//'))) + $(error Could not find SDK version $(MAC_SDK_VER). Install and configure Xcode 4.3, or build with: make MAC_SDK_VER=$(FIRSTSDK)) + endif + + ifeq ($(origin CC), default) + # Test to see if you have a compiler in the right place, if you + # don't abort with an error + CLANG := $(shell xcrun -sdk $(MAC_SDK) -find clang) + ifeq ($(wildcard $(CLANG)),) + $(error Unable to find C compiler, install and configure Xcode 4.3) + endif + + CC := $(CCACHE) $(CLANG) -Qunused-arguments + endif + + ifeq ($(origin CXX), default) + CXXLANG := $(shell xcrun -sdk $(MAC_SDK) -find clang++) + ifeq ($(wildcard $(CXXLANG)),) + $(error Unable to find C++ compiler, install and configure Xcode 4.3) + endif + + CXX := $(CCACHE) $(CXXLANG) -Qunused-arguments + endif + LINK ?= $(CXX) + + ifeq ($(origin AR), default) + AR := $(shell xcrun -sdk $(MAC_SDK) -find libtool) -static -o + endif + + ifeq ($(TARGET_PLATFORM),osx64) + ARCH_FLAGS += -arch x86_64 -m64 -march=core2 + else ifeq (,$(findstring -arch x86_64,$(GCC_ExtraCompilerFlags))) + ARCH_FLAGS += -arch i386 -m32 -march=prescott -momit-leaf-frame-pointer -mtune=core2 + else + # dirty hack to build a universal binary - don't specify the architecture + ARCH_FLAGS += -arch i386 -Xarch_i386 -march=prescott -Xarch_i386 -mtune=core2 -Xarch_i386 -momit-leaf-frame-pointer -Xarch_x86_64 -march=core2 + endif + + GEN_SYM ?= $(shell xcrun -sdk $(MAC_SDK) -find dsymutil) + ifeq ($(CFG),release) + STRIP ?= strip -S + else + STRIP ?= true + endif + ifeq ($(SOURCE_SDK), 1) + VSIGN ?= true + else + VSIGN ?= $(SRCROOT)/devtools/bin/vsign + endif + + CPPFLAGS += -I$(SYSROOT)/usr/include/malloc + CFLAGS += -isysroot $(SYSROOT) -mmacosx-version-min=10.5 -fasm-blocks + + LIB_START_EXE = -lm -ldl -lpthread + LIB_END_EXE = + + LIB_START_SHLIB = + LIB_END_SHLIB = + + SHLIBLDFLAGS = $(LDFLAGS) -bundle -flat_namespace -undefined suppress -Wl,-dead_strip -Wl,-no_dead_strip_inits_and_terms + + ifeq (lib,$(findstring lib,$(GAMEOUTPUTFILE))) + SHLIBLDFLAGS = $(LDFLAGS) -dynamiclib -current_version 1.0 -compatibility_version 1.0 -install_name @rpath/$(basename $(notdir $(GAMEOUTPUTFILE))).dylib $(SystemLibraries) -Wl,-dead_strip -Wl,-no_dead_strip_inits_and_terms + endif + +endif + +# +# Profile-directed optimizations. +# Note: Last time these were tested 3/5/08, it actually slowed down the server benchmark by 5%! +# +# First, uncomment these, build, and test. It will generate .gcda and .gcno files where the .o files are. +# PROFILE_LINKER_FLAG=-fprofile-arcs +# PROFILE_COMPILER_FLAG=-fprofile-arcs +# +# Then, comment the above flags out again and rebuild with this flag uncommented: +# PROFILE_COMPILER_FLAG=-fprofile-use +# + +############################################################################# +# The compiler command lne for each src code file to compile +############################################################################# + +OBJ_DIR = ./obj_$(NAME)_$(TARGET_PLATFORM)$(TARGET_PLATFORM_EXT)/$(CFG) +CPP_TO_OBJ = $(CPPFILES:.cpp=.o) +CXX_TO_OBJ = $(CPP_TO_OBJ:.cxx=.o) +CC_TO_OBJ = $(CXX_TO_OBJ:.cc=.o) +MM_TO_OBJ = $(CC_TO_OBJ:.mm=.o) +C_TO_OBJ = $(MM_TO_OBJ:.c=.o) +OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(C_TO_OBJ))) + +ifeq ($(MAKE_VERBOSE),1) + QUIET_PREFIX = + QUIET_ECHO_POSTFIX = +else + QUIET_PREFIX = @ + QUIET_ECHO_POSTFIX = > /dev/null +endif + +ifeq ($(MAKE_CC_VERBOSE),1) +CC += -v +endif + +ifeq ($(CONFTYPE),lib) + LIB_File = $(OUTPUTFILE) +endif + +ifeq ($(CONFTYPE),dll) + SO_File = $(OUTPUTFILE) +endif + +ifeq ($(CONFTYPE),exe) + EXE_File = $(OUTPUTFILE) +endif + +# we generate dependencies as a side-effect of compilation now +GEN_DEP_FILE= + +PRE_COMPILE_FILE = + +POST_COMPILE_FILE = + +ifeq ($(BUILDING_MULTI_ARCH),1) + SINGLE_ARCH_CXXFLAGS=$(subst -arch x86_64,,$(CXXFLAGS)) + COMPILE_FILE = \ + $(QUIET_PREFIX) \ + echo "---- $(lastword $(subst /, ,$<)) as MULTIARCH----";\ + mkdir -p $(OBJ_DIR) && \ + $(CXX) $(SINGLE_ARCH_CXXFLAGS) $(GENDEP_CXXFLAGS) -o $@ -c $< && \ + $(CXX) $(CXXFLAGS) -o $@ -c $< +else + COMPILE_FILE = \ + $(QUIET_PREFIX) \ + echo "---- $(lastword $(subst /, ,$<)) ----";\ + mkdir -p $(OBJ_DIR) && \ + $(CXX) $(CXXFLAGS) $(GENDEP_CXXFLAGS) -o $@ -c $< +endif + +ifneq "$(origin VALVE_NO_AUTO_P4)" "undefined" + P4_EDIT_START = chmod -R +w + P4_EDIT_END = || true + P4_REVERT_START = true + P4_REVERT_END = +else + ifndef P4_EDIT_CHANGELIST + # You can use an environment variable to specify what changelist to check the Linux Binaries out into. Normally the default + # setting is best, but here is an alternate example: + # export P4_EDIT_CHANGELIST_CMD="echo 1424335" + # ?= means that if P4_EDIT_CHANGELIST_CMD is already set it won't be changed. + P4_EDIT_CHANGELIST_CMD ?= p4 changes -c `p4 client -o | grep ^Client | cut -f 2` -s pending | fgrep 'POSIX Auto Checkout' | cut -d' ' -f 2 | tail -n 1 + P4_EDIT_CHANGELIST := $(shell $(P4_EDIT_CHANGELIST_CMD)) + endif + ifeq ($(P4_EDIT_CHANGELIST),) + # If we haven't found a changelist to check out to then create one. The name must match the one from a few + # lines above or else a new changelist will be created each time. + # Warning: the behavior of 'echo' is not consistent. In bash you need the "-e" option in order for \n to be + # interpreted as a line-feed, but in dash you do not, and if "-e" is passed along then it is printed, which + # confuses p4. So, if you run this command from the bash shell don't forget to add "-e" to the echo command. + P4_EDIT_CHANGELIST = $(shell echo "Change: new\nDescription: POSIX Auto Checkout" | p4 change -i | cut -f 2 -d ' ') + endif + + P4_EDIT_START := for f in + P4_EDIT_END := ; do if [ -n $$f ]; then if [ -d $$f ]; then find $$f -type f -print | p4 -x - edit -c $(P4_EDIT_CHANGELIST); else p4 edit -c $(P4_EDIT_CHANGELIST) $$f; fi; fi; done $(QUIET_ECHO_POSTFIX) + P4_REVERT_START := for f in + P4_REVERT_END := ; do if [ -n $$f ]; then if [ -d $$f ]; then find $$f -type f -print | p4 -x - revert; else p4 revert $$f; fi; fi; done $(QUIET_ECHO_POSTFIX) +endif + +ifeq ($(CONFTYPE),dll) +all: $(OTHER_DEPENDENCIES) $(OBJS) $(GAMEOUTPUTFILE) + @echo $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX) +else +all: $(OTHER_DEPENDENCIES) $(OBJS) $(OUTPUTFILE) + @echo $(OUTPUTFILE) $(QUIET_ECHO_POSTFIX) +endif + +.PHONY: clean cleantargets cleanandremove rebuild relink RemoveOutputFile SingleFile + + +rebuild : + $(MAKE) -f $(firstword $(MAKEFILE_LIST)) cleanandremove + $(MAKE) -f $(firstword $(MAKEFILE_LIST)) + + +# Use the relink target to force to relink the project. +relink: RemoveOutputFile all + +RemoveOutputFile: + rm -f $(OUTPUTFILE) + + +# This rule is so you can say "make SingleFile SingleFilename=/home/myname/valve_main/src/engine/language.cpp" and have it only build that file. +# It basically just translates the full filename to create a dependency on the appropriate .o file so it'll build that. +SingleFile : RemoveSingleFile $(OBJ_DIR)/$(basename $(notdir $(SingleFilename))).o + @echo "" + +RemoveSingleFile: + $(QUIET_PREFIX) rm -f $(OBJ_DIR)/$(basename $(notdir $(SingleFilename))).o + +clean: +ifneq "$(OBJ_DIR)" "" + $(QUIET_PREFIX) echo "rm -rf $(OBJ_DIR)" + $(QUIET_PREFIX) rm -rf $(OBJ_DIR) +endif +ifneq "$(OUTPUTFILE)" "" + $(QUIET_PREFIX) if [ -e $(OUTPUTFILE) ]; then \ + echo "p4 revert $(OUTPUTFILE)"; \ + $(P4_REVERT_START) $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT) $(P4_REVERT_END); \ + fi; +endif +ifneq "$(OTHER_DEPENDENCIES)" "" + $(QUIET_PREFIX) echo "rm -f $(OTHER_DEPENDENCIES)" + $(QUIET_PREFIX) rm -f $(OTHER_DEPENDENCIES) +endif +ifneq "$(GAMEOUTPUTFILE)" "" + $(QUIET_PREFIX) echo "p4 revert $(GAMEOUTPUTFILE)" + $(QUIET_PREFIX) $(P4_REVERT_START) $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_REVERT_END) +endif + + +# Do the above cleaning, except with p4 edit and rm. Reason being ar crs adds and replaces obj files to the +# archive. However if you've renamed or deleted a source file, $(AR) won't remove it. This can leave +# us with archive files that have extra unused symbols, and also potentially cause compilation errors +# when you rename a file and have many duplicate symbols. +cleanandremove: +ifneq "$(OBJ_DIR)" "" + $(QUIET_PREFIX) echo "rm -rf $(OBJ_DIR)" + $(QUIET_PREFIX) -rm -rf $(OBJ_DIR) +endif +ifneq "$(OUTPUTFILE)" "" + $(QUIET_PREFIX) if [ -e $(OUTPUTFILE) ]; then \ + echo "p4 edit and rm -f $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT)"; \ + $(P4_EDIT_START) $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); \ + fi; + $(QUIET_PREFIX) -rm -f $(OUTPUTFILE) $(OUTPUTFILE)$(SYM_EXT); +endif +ifneq "$(OTHER_DEPENDENCIES)" "" + $(QUIET_PREFIX) echo "rm -f $(OTHER_DEPENDENCIES)" + $(QUIET_PREFIX) -rm -f $(OTHER_DEPENDENCIES) +endif +ifneq "$(GAMEOUTPUTFILE)" "" + $(QUIET_PREFIX) echo "p4 edit and rm -f $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT)" + $(QUIET_PREFIX) $(P4_EDIT_START) $(GAMEOUTPUTFILE) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END) + $(QUIET_PREFIX) -rm -f $(GAMEOUTPUTFILE) +endif + + +# This just deletes the final targets so it'll do a relink next time we build. +cleantargets: + $(QUIET_PREFIX) rm -f $(OUTPUTFILE) $(GAMEOUTPUTFILE) + + +$(LIB_File): $(OTHER_DEPENDENCIES) $(OBJS) + $(QUIET_PREFIX) -$(P4_EDIT_START) $(LIB_File) $(P4_EDIT_END); + $(QUIET_PREFIX) $(AR) $(LIB_File) $(OBJS) $(LIBFILES); + +SO_GameOutputFile = $(GAMEOUTPUTFILE) + +# Remove the target before installing a file over it; this prevents existing +# instances of the game from crashing due to the overwrite. +$(SO_GameOutputFile): $(SO_File) + $(QUIET_PREFIX) \ + $(P4_EDIT_START) $(GAMEOUTPUTFILE) $(P4_EDIT_END) && \ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- COPYING TO $@ [$(CFG)] ----";\ + echo "----" $(QUIET_ECHO_POSTFIX); + $(QUIET_PREFIX) -$(P4_EDIT_START) $(GAMEOUTPUTFILE) $(P4_EDIT_END); + $(QUIET_PREFIX) -mkdir -p `dirname $(GAMEOUTPUTFILE)` > /dev/null; + $(QUIET_PREFIX) rm -f $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX); + $(QUIET_PREFIX) cp -v $(OUTPUTFILE) $(GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX); + $(QUIET_PREFIX) -$(P4_EDIT_START) $(GAMEOUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); + $(QUIET_PREFIX) $(GEN_SYM) $(GAMEOUTPUTFILE); + $(QUIET_PREFIX) -$(STRIP) $(GAMEOUTPUTFILE); + $(QUIET_PREFIX) $(VSIGN) -signvalve $(GAMEOUTPUTFILE); + $(QUIET_PREFIX) if [ "$(COPY_DLL_TO_SRV)" = "1" ]; then\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- COPYING TO $(Srv_GAMEOUTPUTFILE) ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + cp -v $(GAMEOUTPUTFILE) $(Srv_GAMEOUTPUTFILE) $(QUIET_ECHO_POSTFIX);\ + cp -v $(GAMEOUTPUTFILE)$(SYM_EXT) $(Srv_GAMEOUTPUTFILE)$(SYM_EXT) $(QUIET_ECHO_POSTFIX);\ + fi; + $(QUIET_PREFIX) if [ "$(IMPORTLIBRARY)" != "" ]; then\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- COPYING TO IMPORT LIBRARY $(IMPORTLIBRARY) ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + $(P4_EDIT_START) $(IMPORTLIBRARY) $(P4_EDIT_END) && \ + mkdir -p `dirname $(IMPORTLIBRARY)` > /dev/null && \ + cp -v $(OUTPUTFILE) $(IMPORTLIBRARY); \ + fi; + + +$(SO_File): $(OTHER_DEPENDENCIES) $(OBJS) $(LIBFILENAMES) + $(QUIET_PREFIX) \ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- LINKING $@ [$(CFG)] ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + \ + $(LINK) $(LINK_MAP_FLAGS) $(SHLIBLDFLAGS) $(PROFILE_LINKER_FLAG) -o $(OUTPUTFILE) $(LIB_START_SHLIB) $(OBJS) $(LIBFILES) $(SystemLibraries) $(LIB_END_SHLIB); + $(VSIGN) -signvalve $(OUTPUTFILE); + + +$(EXE_File) : $(OTHER_DEPENDENCIES) $(OBJS) $(LIBFILENAMES) + $(QUIET_PREFIX) \ + echo "----" $(QUIET_ECHO_POSTFIX);\ + echo "---- LINKING EXE $@ [$(CFG)] ----";\ + echo "----" $(QUIET_ECHO_POSTFIX);\ + \ + $(P4_EDIT_START) $(OUTPUTFILE) $(P4_EDIT_END);\ + $(LINK) $(LINK_MAP_FLAGS) $(LDFLAGS) $(PROFILE_LINKER_FLAG) -o $(OUTPUTFILE) $(LIB_START_EXE) $(OBJS) $(LIBFILES) $(SystemLibraries) $(LIB_END_EXE); + $(QUIET_PREFIX) -$(P4_EDIT_START) $(OUTPUTFILE)$(SYM_EXT) $(P4_EDIT_END); + $(QUIET_PREFIX) $(GEN_SYM) $(OUTPUTFILE); + $(QUIET_PREFIX) -$(STRIP) $(OUTPUTFILE); + $(QUIET_PREFIX) $(VSIGN) -signvalve $(OUTPUTFILE); + + +tags: + etags -a -C -o $(SRCROOT)/TAGS *.cpp *.cxx *.h *.hxx diff --git a/mp/src/fgdlib/gamedata.cpp b/mp/src/fgdlib/gamedata.cpp index f3689ee9..4138ab4b 100644 --- a/mp/src/fgdlib/gamedata.cpp +++ b/mp/src/fgdlib/gamedata.cpp @@ -12,6 +12,9 @@ #include "filesystem_tools.h" #include "tier1/strtools.h" #include "utlmap.h" +#ifdef MAPBASE +#include "fmtstr.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -579,6 +582,34 @@ GDclass *GameData::BeginInstanceRemap( const char *pszClassName, const char *psz return m_InstanceClass; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets up for additional instance remap fixes from Mapbase +//----------------------------------------------------------------------------- +void GameData::SetupInstanceRemapParams( int iStartNodes, int iStartBrushSide, bool bRemapVecLines ) +{ + // Set the numer of nodes in the level + m_InstanceStartAINodes = iStartNodes; + + // If we have a "nodeid" key, set it to ivNodeDest so it's properly recognized + // during AI node remapping + GDinputvariable *var = m_InstanceClass->VarForName( "nodeid" ); + if ( var ) + { + var->ForceSetType( ivNodeDest ); + } + + //--------------------------------------------- + + // Set the number of brush sides in the level + m_InstanceStartSide = iStartBrushSide; + + //--------------------------------------------- + + m_bRemapVecLines = bRemapVecLines; +} +#endif + enum tRemapOperation { @@ -586,6 +617,13 @@ enum tRemapOperation REMAP_POSITION, REMAP_ANGLE, REMAP_ANGLE_NEGATIVE_PITCH, +#ifdef MAPBASE + // Remaps the node ID for instance/manifest AI node support + REMAP_NODE_ID, + + // Remaps brush sides and sidelists + REMAP_SIDES, +#endif }; @@ -624,6 +662,12 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * RemapOperation.Insert( ivOrigin, REMAP_POSITION ); RemapOperation.Insert( ivAxis, REMAP_ANGLE ); RemapOperation.Insert( ivAngleNegativePitch, REMAP_ANGLE_NEGATIVE_PITCH ); +#ifdef MAPBASE + RemapOperation.Insert( ivNodeDest, REMAP_NODE_ID ); + RemapOperation.Insert( ivSide, REMAP_SIDES ); + RemapOperation.Insert( ivSideList, REMAP_SIDES ); + RemapOperation.Insert( ivVecLine, REMAP_POSITION ); +#endif } if ( !m_InstanceClass ) @@ -657,6 +701,12 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * case REMAP_POSITION: { +#ifdef MAPBASE + // Only remap ivVecLine if the keyvalue is enabled + if (KVType == ivVecLine && !m_bRemapVecLines) + break; +#endif + Vector inPoint( 0.0f, 0.0f, 0.0f ), outPoint; sscanf ( pszInValue, "%f %f %f", &inPoint.x, &inPoint.y, &inPoint.z ); @@ -697,6 +747,54 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * sprintf( pszOutValue, "%g", -outAngles.x ); // just the pitch } break; + +#ifdef MAPBASE + case REMAP_NODE_ID: + { + int value = atoi( pszInValue ); + if (value == -1) + break; + + //Warning( " %s %s: Remapped %i to %i", m_InstanceClass->GetName(), KVVar->GetName(), value, value + m_InstanceStartAINodes ); + + value += m_InstanceStartAINodes; + + sprintf( pszOutValue, "%i", value ); + } + break; + + case REMAP_SIDES: + { + CUtlStringList sideList; + V_SplitString( pszInValue, " ", sideList ); + + // Convert sides + CUtlStringList newSideList; + for (int i = 0; i < sideList.Count(); i++) + { + int iSide = atoi( sideList[i] ); + + //Warning( " %s %s: Remapped %i to %i", m_InstanceClass->GetName(), KVVar->GetName(), iSide, iSide + m_InstanceStartSide ); + + iSide += m_InstanceStartSide; + + newSideList.AddToTail( const_cast( CNumStr( iSide ).String() ) ); + } + + // Initial side + strcpy( pszOutValue, newSideList[0] ); + + // Start at 1 for subsequent sides + for (int i = 1; i < newSideList.Count(); i++) + { + // Any subsequent sides are spaced + sprintf( pszOutValue, "%s %s", pszOutValue, newSideList[i] ); + } + + //Warning("Old side list: \"%s\", new side list: \"%s\"\n", pszInValue, pszOutValue); + } + break; +#endif } return ( strcmpi( pszInValue, pszOutValue ) != 0 ); diff --git a/mp/src/game/client/C_Env_Projected_Texture.h b/mp/src/game/client/C_Env_Projected_Texture.h index b15ea6ef..6fbb6f12 100644 --- a/mp/src/game/client/C_Env_Projected_Texture.h +++ b/mp/src/game/client/C_Env_Projected_Texture.h @@ -14,6 +14,104 @@ #include "c_baseentity.h" #include "basetypes.h" +#ifdef ASW_PROJECTED_TEXTURES + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class C_EnvProjectedTexture : public C_BaseEntity +{ + DECLARE_CLASS( C_EnvProjectedTexture, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + void SetMaterial( IMaterial *pMaterial ); + void SetLightColor( byte r, byte g, byte b, byte a ); + void SetSize( float flSize ); + void SetRotation( float flRotation ); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + void ShutDownLightHandle( void ); + +#ifdef MAPBASE + virtual void Simulate(); +#else + virtual bool Simulate(); +#endif + + void UpdateLight( void ); + + C_EnvProjectedTexture(); + ~C_EnvProjectedTexture(); + + static void SetVisibleBBoxMinHeight( float flVisibleBBoxMinHeight ) { m_flVisibleBBoxMinHeight = flVisibleBBoxMinHeight; } + static float GetVisibleBBoxMinHeight( void ) { return m_flVisibleBBoxMinHeight; } + static C_EnvProjectedTexture *Create( ); + +private: + + inline bool IsBBoxVisible( void ); + bool IsBBoxVisible( Vector vecExtentsMin, + Vector vecExtentsMax ); + + ClientShadowHandle_t m_LightHandle; + bool m_bForceUpdate; + + EHANDLE m_hTargetEntity; +#ifdef MAPBASE + bool m_bDontFollowTarget; +#endif + + bool m_bState; + bool m_bAlwaysUpdate; + float m_flLightFOV; +#ifdef MAPBASE + float m_flLightHorFOV; +#endif + bool m_bEnableShadows; + bool m_bLightOnlyTarget; + bool m_bLightWorld; + bool m_bCameraSpace; + float m_flBrightnessScale; + color32 m_LightColor; + Vector m_CurrentLinearFloatLightColor; + float m_flCurrentLinearFloatLightAlpha; +#ifdef MAPBASE + float m_flCurrentBrightnessScale; +#endif + float m_flColorTransitionTime; + float m_flAmbient; + float m_flNearZ; + float m_flFarZ; + char m_SpotlightTextureName[ MAX_PATH ]; + CTextureReference m_SpotlightTexture; + int m_nSpotlightTextureFrame; + int m_nShadowQuality; +#ifdef MAPBASE + float m_flConstantAtten; + float m_flLinearAtten; + float m_flQuadraticAtten; + float m_flShadowAtten; + + bool m_bAlwaysDraw; + //bool m_bProjectedTextureVersion; +#endif + + Vector m_vecExtentsMin; + Vector m_vecExtentsMax; + + static float m_flVisibleBBoxMinHeight; +}; + + + +bool C_EnvProjectedTexture::IsBBoxVisible( void ) +{ + return IsBBoxVisible( GetAbsOrigin() + m_vecExtentsMin, GetAbsOrigin() + m_vecExtentsMax ); +} + +#else + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -62,4 +160,6 @@ public: C_EnvProjectedTexture* GetEnvProjectedTextureList(); +#endif + #endif // C_ENVPROJECTEDTEXTURE_H diff --git a/mp/src/game/client/c_baseanimating.cpp b/mp/src/game/client/c_baseanimating.cpp index 78bc38be..4fb2e679 100644 --- a/mp/src/game/client/c_baseanimating.cpp +++ b/mp/src/game/client/c_baseanimating.cpp @@ -54,6 +54,9 @@ #include "replay/replay_ragdoll.h" #include "studio_stats.h" #include "tier1/callqueue.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif #ifdef TF_CLIENT_DLL #include "c_tf_player.h" @@ -283,6 +286,31 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() +BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPoseParameter, "GetPoseParameter", "Get the specified pose parameter's value" ) +#endif + DEFINE_SCRIPTFUNC_NAMED( ScriptSetPoseParameter, "SetPoseParameter", "Set the specified pose parameter to the specified value" ) + DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") + DEFINE_SCRIPTFUNC( GetBodygroup, "Gets a bodygroup" ) + DEFINE_SCRIPTFUNC( GetBodygroupName, "Gets a bodygroup name" ) + DEFINE_SCRIPTFUNC( FindBodygroupByName, "Finds a bodygroup by name" ) + DEFINE_SCRIPTFUNC( GetBodygroupCount, "Gets the number of models in a bodygroup" ) + DEFINE_SCRIPTFUNC( GetNumBodyGroups, "Gets the number of bodygroups" ) + + DEFINE_SCRIPTFUNC( GetSequence, "Gets the current sequence" ) + DEFINE_SCRIPTFUNC( SetSequence, "Sets the current sequence" ) + DEFINE_SCRIPTFUNC( SequenceLoops, "Loops the current sequence" ) + DEFINE_SCRIPTFUNC( LookupSequence, "Gets the index of the specified sequence name" ) + DEFINE_SCRIPTFUNC( LookupActivity, "Gets the ID of the specified activity name" ) + DEFINE_SCRIPTFUNC( GetSequenceName, "Gets the name of the specified sequence index" ) + DEFINE_SCRIPTFUNC( GetSequenceActivityName, "Gets the activity name of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceActivity, "GetSequenceActivity", "Gets the activity ID of the specified sequence index" ) +#endif +END_SCRIPTDESC(); + C_ClientRagdoll::C_ClientRagdoll( bool bRestoring ) { m_iCurrentFriction = 0; @@ -1432,6 +1460,27 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping ) return flCycle; } +#ifdef MAPBASE_VSCRIPT +float C_BaseAnimating::ScriptGetPoseParameter( const char* szName ) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return 0.0f; + + int iPoseParam = LookupPoseParameter( pHdr, szName ); + return GetPoseParameter( iPoseParam ); +} +#endif + +void C_BaseAnimating::ScriptSetPoseParameter(const char* szName, float fValue) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return; + + int iPoseParam = LookupPoseParameter(pHdr, szName); + SetPoseParameter(pHdr, iPoseParam, fValue); +} void C_BaseAnimating::GetCachedBoneMatrix( int boneIndex, matrix3x4_t &out ) { @@ -3137,6 +3186,17 @@ int C_BaseAnimating::DrawModel( int flags ) int drawn = 0; +#ifdef MAPBASE + if (m_iViewHideFlags > 0) + { + // Hide this entity if it's not supposed to be drawn in this view. + if (m_iViewHideFlags & (1 << CurrentViewID())) + { + return 0; + } + } +#endif + #ifdef TF_CLIENT_DLL ValidateModelIndex(); #endif @@ -3604,7 +3664,7 @@ bool C_BaseAnimating::DispatchMuzzleEffect( const char *options, bool isFirstPer int weaponType = 0; // Get the first parameter - p = nexttoken( token, p, ' ' ); + p = nexttoken( token, p, ' ' , sizeof(token) ); // Find the weapon type if ( token[0] ) @@ -3648,7 +3708,7 @@ bool C_BaseAnimating::DispatchMuzzleEffect( const char *options, bool isFirstPer } // Get the second parameter - p = nexttoken( token, p, ' ' ); + p = nexttoken( token, p, ' ' , sizeof(token) ); int attachmentIndex = -1; @@ -3759,7 +3819,7 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int // Get the particle effect name const char *p = options; - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); const char* mtoken = ModifyEventParticles( token ); if ( !mtoken || mtoken[0] == '\0' ) @@ -3767,7 +3827,7 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int Q_strncpy( szParticleEffect, mtoken, sizeof(szParticleEffect) ); // Get the attachment type - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); iAttachType = GetAttachTypeFromString( token ); if ( iAttachType == -1 ) @@ -3777,7 +3837,7 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int } // Get the attachment point index - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if ( token[0] ) { iAttachment = atoi(token); @@ -3897,18 +3957,33 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int // Eject brass case CL_EVENT_EJECTBRASS1: - if ( m_Attachments.Count() > 0 ) { - if ( MainViewOrigin().DistToSqr( GetAbsOrigin() ) < (256 * 256) ) + // Check if we're a weapon, if we belong to the local player, and if the local player is in third person - if all are true, don't do a muzzleflash in this instance, because + // we're using the view models dispatch for smoothness. + if ( dynamic_cast< C_BaseCombatWeapon *>(this) != NULL ) { - Vector attachOrigin; - QAngle attachAngles; - - if( GetAttachment( 2, attachOrigin, attachAngles ) ) + C_BaseCombatWeapon *pWeapon = dynamic_cast< C_BaseCombatWeapon *>(this); + if ( pWeapon && pWeapon->GetOwner() == C_BasePlayer::GetLocalPlayer() && ::input->CAM_IsThirdPerson() ) + break; + } + + if ( ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) ) + break; + + if ( m_Attachments.Count() > 0 ) + { + if ( MainViewOrigin().DistToSqr( GetAbsOrigin() ) < (256 * 256) ) { - tempents->EjectBrass( attachOrigin, attachAngles, GetAbsAngles(), atoi( options ) ); + Vector attachOrigin; + QAngle attachAngles; + + if( GetAttachment( 2, attachOrigin, attachAngles ) ) + { + tempents->EjectBrass( attachOrigin, attachAngles, GetAbsAngles(), atoi( options ) ); + } } } + break; } break; @@ -3922,6 +3997,36 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int case AE_NPC_MUZZLEFLASH: { // Send out the effect for an NPC +#if defined ( HL2MP ) || defined ( SDK_DLL ) // works for the modified CSS weapons included in the new template sdk. + // HL2MP - Make third person muzzleflashes as reliable as the first person ones + // while in third person the view model dispatches the muzzleflash event - note: the weapon models dispatch them too, but not frequently. + if ( IsViewModel() ) + { + C_BasePlayer *pPlayer = ToBasePlayer( dynamic_cast(this)->GetOwner() ); + if ( pPlayer && pPlayer == C_BasePlayer::GetLocalPlayer()) + { + if ( ::input->CAM_IsThirdPerson() ) + { + // Dispatch on the weapon - the player model doesn't have the attachments in hl2mp. + C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); + if ( !pWeapon ) + break; + pWeapon->DispatchMuzzleEffect( options, false ); + break; + } + } + } + + // Check if we're a weapon, if we belong to the local player, and if the local player is in third person - if all are true, don't do a muzzleflash in this instance, because + // we're using the view models dispatch for smoothness. + if ( dynamic_cast< C_BaseCombatWeapon *>(this) != NULL ) + { + C_BaseCombatWeapon *pWeapon = dynamic_cast< C_BaseCombatWeapon *>(this); + if ( pWeapon && pWeapon->GetOwner() == C_BasePlayer::GetLocalPlayer() && ::input->CAM_IsThirdPerson() ) + break; + } + +#endif DispatchMuzzleEffect( options, false ); break; } @@ -3981,11 +4086,11 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int const char *p = options; // Bodygroup Name - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); Q_strncpy( szBodygroupName, token, sizeof(szBodygroupName) ); // Get the desired value - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); value = token[0] ? atoi( token ) : 0; int index = FindBodygroupByName( szBodygroupName ); @@ -4022,13 +4127,13 @@ void C_BaseAnimating::FireObsoleteEvent( const Vector& origin, const QAngle& ang const char *p = options; - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); Q_strncpy( effectFunc, token, sizeof(effectFunc) ); - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); iAttachment = token[0] ? atoi(token) : -1; - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); iParam = token[0] ? atoi(token) : 0; if ( iAttachment != -1 && m_Attachments.Count() >= iAttachment ) @@ -4667,6 +4772,14 @@ C_BaseAnimating *C_BaseAnimating::CreateRagdollCopy() pRagdoll->m_nForceBone = m_nForceBone; pRagdoll->SetNextClientThink( CLIENT_THINK_ALWAYS ); +#ifdef MAPBASE + pRagdoll->m_iViewHideFlags = m_iViewHideFlags; + + pRagdoll->m_fadeMinDist = m_fadeMinDist; + pRagdoll->m_fadeMaxDist = m_fadeMaxDist; + pRagdoll->m_flFadeScale = m_flFadeScale; +#endif + pRagdoll->SetModelName( AllocPooledString(pModelName) ); pRagdoll->SetModelScale( GetModelScale() ); return pRagdoll; diff --git a/mp/src/game/client/c_baseanimating.h b/mp/src/game/client/c_baseanimating.h index f1b0467c..af382aec 100644 --- a/mp/src/game/client/c_baseanimating.h +++ b/mp/src/game/client/c_baseanimating.h @@ -95,6 +95,7 @@ public: DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); DECLARE_INTERPOLATION(); + DECLARE_ENT_SCRIPTDESC(); enum { @@ -163,6 +164,13 @@ public: virtual void FireObsoleteEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); virtual const char* ModifyEventParticles( const char* token ) { return token; } +#if defined ( SDK_DLL ) || defined ( HL2MP ) + virtual void ResetEventsParity() { m_nPrevResetEventsParity = -1; } // used to force animation events to function on players so the muzzleflashes and other events occur + // so new functions don't have to be made to parse the models like CSS does in ProcessMuzzleFlashEvent + // allows the multiplayer world weapon models to declare the muzzleflashes, and other effects like sp + // without the need to script it and add extra parsing code. +#endif + // Parses and distributes muzzle flash events virtual bool DispatchMuzzleEffect( const char *options, bool isFirstPerson ); @@ -447,6 +455,10 @@ public: virtual bool IsViewModel() const; virtual void UpdateOnRemove( void ); +#ifdef MAPBASE_VSCRIPT + float ScriptGetPoseParameter(const char* szName); +#endif + void ScriptSetPoseParameter(const char* szName, float fValue); protected: // View models scale their attachment positions to account for FOV. To get the unmodified // attachment position (like if you're rendering something else during the view model's DrawModel call), @@ -465,6 +477,10 @@ protected: virtual bool CalcAttachments(); +#ifdef MAPBASE_VSCRIPT + int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); } +#endif + private: // This method should return true if the bones have changed + SetupBones needs to be called virtual float LastBoneChangedTime() { return FLT_MAX; } diff --git a/mp/src/game/client/c_basecombatweapon.cpp b/mp/src/game/client/c_basecombatweapon.cpp index ef4c845e..03373db5 100644 --- a/mp/src/game/client/c_basecombatweapon.cpp +++ b/mp/src/game/client/c_basecombatweapon.cpp @@ -16,6 +16,9 @@ #include "tier1/KeyValues.h" #include "toolframework/itoolframework.h" #include "toolframework_client.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -82,6 +85,24 @@ static inline bool ShouldDrawLocalPlayerViewModel( void ) { #if defined( PORTAL ) return false; +#elif MAPBASE + // We shouldn't draw the viewmodel externally. + C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); + if (localplayer) + { + if (localplayer->m_bDrawPlayerModelExternally) + { + // If this isn't the main view, draw the weapon. + view_id_t viewID = CurrentViewID(); + if (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA) + return false; + } + + // Since we already have the local player, check its own ShouldDrawThisPlayer() to avoid extra checks + return !localplayer->ShouldDrawThisPlayer(); + } + else + return false; #else return !C_BasePlayer::ShouldDrawLocalPlayer(); #endif @@ -156,11 +177,8 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) } else // weapon carried by other player or not at all { - int overrideModelIndex = CalcOverrideModelIndex(); - if( overrideModelIndex != -1 && overrideModelIndex != GetModelIndex() ) - { - SetModelIndex( overrideModelIndex ); - } + // See comment below + EnsureCorrectRenderingModel(); } if ( updateType == DATA_UPDATE_CREATED ) @@ -435,6 +453,12 @@ bool C_BaseCombatWeapon::ShouldDraw( void ) if ( !ShouldDrawLocalPlayerViewModel() ) return true; +#ifdef MAPBASE + // We're drawing this in non-main views, handle it in DrawModel() + if ( pLocalPlayer->m_bDrawPlayerModelExternally ) + return true; +#endif + // don't draw active weapon if not in some kind of 3rd person mode, the viewmodel will do that return false; } @@ -483,6 +507,16 @@ int C_BaseCombatWeapon::DrawModel( int flags ) if ( localplayer && localplayer->IsObserver() && GetOwner() ) { +#ifdef MAPBASE + if (localplayer->m_bDrawPlayerModelExternally) + { + // If this isn't the main view, draw the weapon. + view_id_t viewID = CurrentViewID(); + if (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA) + return BaseClass::DrawModel( flags ); + } +#endif + // don't draw weapon if chasing this guy as spectator // we don't check that in ShouldDraw() since this may change // without notification @@ -517,6 +551,35 @@ int C_BaseCombatWeapon::CalcOverrideModelIndex() } } +//----------------------------------------------------------------------------- +// If the local player is visible (thirdperson mode, tf2 taunts, etc., then make sure that we are using the +// w_ (world) model not the v_ (view) model or else the model can flicker, etc. +// Otherwise, if we're not the local player, always use the world model +//----------------------------------------------------------------------------- +void C_BaseCombatWeapon::EnsureCorrectRenderingModel() +{ + C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); + if ( localplayer && + localplayer == GetOwner() && + !ShouldDrawUsingViewModel() ) + { + return; + } + + // BRJ 10/14/02 + // FIXME: Remove when Yahn's client-side prediction is done + // It's a hacky workaround for the model indices fighting + // (GetRenderBounds uses the model index, which is for the view model) + SetModelIndex( GetWorldModelIndex() ); + + // Validate our current sequence just in case ( in theory the view and weapon models should have the same sequences for sequences that overlap at least ) + CStudioHdr *pStudioHdr = GetModelPtr(); + if ( pStudioHdr && + GetSequence() >= pStudioHdr->GetNumSeq() ) + { + SetSequence( 0 ); + } +} //----------------------------------------------------------------------------- // tool recording diff --git a/mp/src/game/client/c_baseentity.cpp b/mp/src/game/client/c_baseentity.cpp index 338f986c..02516130 100644 --- a/mp/src/game/client/c_baseentity.cpp +++ b/mp/src/game/client/c_baseentity.cpp @@ -40,6 +40,14 @@ #include "cdll_bounded_cvars.h" #include "inetchannelinfo.h" #include "proto_version.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif +#ifdef MAPBASE_VSCRIPT +#include "vscript_client.h" +#endif + +#include "gamestringpool.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -420,6 +428,52 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst ) RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ), END_RECV_TABLE() +BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRight, "GetRightVector", "Get the right vector of the entity" ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "Gets this entity's team" ) +#endif + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeft, "GetLeftVector", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeamNumber", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) + + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEmitSound, "EmitSound", "Plays a sound from this entity." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptPrecacheScriptSound, "PrecacheSoundScript", "Precache a sound for later playing." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSoundDuration, "GetSoundDuration", "Returns float duration of the sound. Takes soundname and optional actormodelname." ) + + DEFINE_SCRIPTFUNC( GetClassname, "" ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityName, "GetName", "" ) + + DEFINE_SCRIPTFUNC_NAMED( WorldSpaceCenter, "GetCenter", "Get vector to center of object - absolute coords" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEyePosition, "EyePosition", "Get vector to eye position - absolute coords" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMins, "GetBoundingMins", "Get a vector containing min bounds, centered on object" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMaxs, "GetBoundingMaxs", "Get a vector containing max bounds, centered on object" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNextMovePeer, "NextMovePeer", "" ) + + DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + + DEFINE_SCRIPTFUNC_NAMED( GetEntityIndex, "entindex", "" ) +#endif +END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_PredictableId ) @@ -451,6 +505,9 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt(RECVINFO(m_nRenderMode)), RecvPropInt(RECVINFO(m_nRenderFX)), RecvPropInt(RECVINFO(m_clrRender)), +#ifdef MAPBASE + RecvPropInt(RECVINFO(m_iViewHideFlags)), +#endif RecvPropInt(RECVINFO(m_iTeamNum)), RecvPropInt(RECVINFO(m_CollisionGroup)), RecvPropFloat(RECVINFO(m_flElasticity)), @@ -460,6 +517,8 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ), RecvPropInt( RECVINFO( m_iParentAttachment ) ), + RecvPropString(RECVINFO(m_iName)), + RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ), RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ), RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ), @@ -1094,6 +1153,8 @@ bool C_BaseEntity::Init( int entnum, int iSerialNum ) m_nCreationTick = gpGlobals->tickcount; + m_hScriptInstance = NULL; + return true; } @@ -1171,6 +1232,7 @@ void C_BaseEntity::Term() g_Predictables.RemoveFromPredictablesList( GetClientHandle() ); } + // If it's play simulated, remove from simulation list if the player still exists... if ( IsPlayerSimulated() && C_BasePlayer::GetLocalPlayer() ) { @@ -1207,6 +1269,12 @@ void C_BaseEntity::Term() RemoveFromLeafSystem(); RemoveFromAimEntsList(); + + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } } @@ -1977,6 +2045,17 @@ int C_BaseEntity::DrawModel( int flags ) return drawn; } +#ifdef MAPBASE + if (m_iViewHideFlags > 0) + { + // Hide this entity if it's not supposed to be drawn in this view. + if (m_iViewHideFlags & (1 << CurrentViewID())) + { + return 0; + } + } +#endif + int modelType = modelinfo->GetModelType( model ); switch ( modelType ) { @@ -6447,6 +6526,187 @@ int C_BaseEntity::GetCreationTick() const return m_nCreationTick; } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Using my edict, cook up a unique VScript scope that's private to me, and +// persistent. +//----------------------------------------------------------------------------- +bool C_BaseEntity::ValidateScriptScope() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + // Force instance creation + GetScriptInstance(); + + EHANDLE hThis; + hThis.Set(this); + + bool bResult = m_ScriptScope.Init(STRING(m_iszScriptId)); + + if (!bResult) + { + DevMsg("%s couldn't create ScriptScope!\n", GetDebugName()); + return false; + } + g_pScriptVM->SetValue(m_ScriptScope, "self", GetScriptInstance()); + } + return true; +} + +//----------------------------------------------------------------------------- +// Returns true if the function was located and called. false otherwise. +// NOTE: Assumes the function takes no parameters at the moment. +//----------------------------------------------------------------------------- +bool C_BaseEntity::CallScriptFunction( const char* pFunctionName, ScriptVariant_t* pFunctionReturn ) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + + HSCRIPT hFunc = m_ScriptScope.LookupFunction(pFunctionName); + + if (hFunc) + { + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Gets a function handle +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::LookupScriptFunction( const char* pFunctionName ) +{ + if (!m_ScriptScope.IsInitialized()) + { + return NULL; + } + + return m_ScriptScope.LookupFunction(pFunctionName); +} + +//----------------------------------------------------------------------------- +// Calls and releases a function handle (ASSUMES SCRIPT SCOPE AND FUNCTION ARE VALID!) +//----------------------------------------------------------------------------- +bool C_BaseEntity::CallScriptFunctionHandle( HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn ) +{ + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Load, compile, and run a script file from disk. +// Input : *pScriptFile - The filename of the script file. +// bUseRootScope - If true, runs this script in the root scope, not +// in this entity's private scope. +//----------------------------------------------------------------------------- +bool C_BaseEntity::RunScriptFile( const char* pScriptFile, bool bUseRootScope ) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (bUseRootScope) + { + return VScriptRunScript(pScriptFile); + } + else + { + return VScriptRunScript(pScriptFile, m_ScriptScope, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Compile and execute a discrete string of script source code +// Input : *pScriptText - A string containing script code to compile and run +//----------------------------------------------------------------------------- +bool C_BaseEntity::RunScript( const char* pScriptText, const char* pDebugFilename ) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (m_ScriptScope.Run(pScriptText, pDebugFilename) == SCRIPT_ERROR) + { + DevWarning(" Entity %s encountered an error in RunScript()\n", GetDebugName()); + } + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetMoveParent( void ) +{ + return ToHScript( GetMoveParent() ); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript( GetRootMoveParent() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptFirstMoveChild( void ) +{ + return ToHScript( FirstMoveChild() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptNextMovePeer( void ) +{ + return ToHScript( NextMovePeer() ); +} +#endif + //------------------------------------------------------------------------------ void CC_CL_Find_Ent( const CCommand& args ) { diff --git a/mp/src/game/client/c_baseentity.h b/mp/src/game/client/c_baseentity.h index e90d1e32..18331915 100644 --- a/mp/src/game/client/c_baseentity.h +++ b/mp/src/game/client/c_baseentity.h @@ -36,6 +36,9 @@ #include "toolframework/itoolentity.h" #include "tier0/threadtools.h" +#include "vscript/ivscript.h" +#include "vscript_shared.h" + class C_Team; class IPhysicsObject; class IClientVehicle; @@ -184,6 +187,8 @@ public: DECLARE_DATADESC(); DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); C_BaseEntity(); virtual ~C_BaseEntity(); @@ -257,6 +262,28 @@ public: string_t m_iClassname; +#ifdef MAPBASE_VSCRIPT + // VSCRIPT + bool ValidateScriptScope(); + bool CallScriptFunction( const char* pFunctionName, ScriptVariant_t* pFunctionReturn ); + + HSCRIPT GetScriptScope() { return m_ScriptScope; } + + HSCRIPT LookupScriptFunction(const char* pFunctionName); + bool CallScriptFunctionHandle(HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn); + + bool RunScriptFile( const char* pScriptFile, bool bUseRootScope = false ); + bool RunScript( const char* pScriptText, const char* pDebugFilename = "C_BaseEntity::RunScript" ); +#endif + + HSCRIPT GetScriptInstance(); + + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; +#ifdef MAPBASE_VSCRIPT + CScriptScope m_ScriptScope; +#endif + // IClientUnknown overrides. public: @@ -358,6 +385,11 @@ public: bool IsMarkedForDeletion( void ); virtual int entindex( void ) const; + +#ifdef MAPBASE_VSCRIPT + // "I don't know why but wrapping entindex() works, while calling it directly crashes." + inline int C_BaseEntity::GetEntityIndex() const { return entindex(); } +#endif // This works for client-only entities and returns the GetEntryIndex() of the entity's handle, // so the sound system can get an IClientEntity from it. @@ -862,6 +894,7 @@ public: void SetSize( const Vector &vecMin, const Vector &vecMax ); // UTIL_SetSize( pev, mins, maxs ); char const *GetClassname( void ); char const *GetDebugName( void ); + virtual const char *GetPlayerName() const { return NULL; } static int PrecacheModel( const char *name ); static bool PrecacheSound( const char *name ); static void PrefetchSound( const char *name ); @@ -1004,6 +1037,7 @@ public: ///////////////// virtual bool IsPlayer( void ) const { return false; }; + virtual bool IsBot( void ) const { return ((GetFlags() & FL_FAKECLIENT) == FL_FAKECLIENT) ? true : false; } virtual bool IsBaseCombatCharacter( void ) { return false; }; virtual C_BaseCombatCharacter *MyCombatCharacterPointer( void ) { return NULL; } virtual bool IsNPC( void ) { return false; } @@ -1021,6 +1055,11 @@ public: virtual Vector EyePosition( void ); virtual const QAngle& EyeAngles( void ); // Direction of eyes virtual const QAngle& LocalEyeAngles( void ); // Direction of eyes in local space (pl.v_angle) + +#ifdef MAPBASE + // Created for script_intro and info_player_view_proxy + virtual void GetEyePosition( Vector &vecOrigin, QAngle &angAngles ) { vecOrigin = EyePosition(); angAngles = EyeAngles(); } +#endif // position of ears virtual Vector EarPosition( void ); @@ -1118,6 +1157,34 @@ public: virtual int GetBody() { return 0; } virtual int GetSkin() { return 0; } + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } +#ifdef MAPBASE_VSCRIPT + const Vector& ScriptGetRight(void) { static Vector vecRight; GetVectors(NULL, &vecRight, NULL); return vecRight; } +#endif + const Vector& ScriptGetLeft(void) { static Vector vecRight; GetVectors(NULL, &vecRight, NULL); return vecRight; } + + const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } + +#ifdef MAPBASE_VSCRIPT + const char* ScriptGetModelName( void ) const { return STRING(GetModelName()); } + + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + void VScriptPrecacheScriptSound(const char* soundname); + + const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } + const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } + + const Vector& ScriptGetBoundingMins( void ) { return m_Collision.OBBMins(); } + const Vector& ScriptGetBoundingMaxs( void ) { return m_Collision.OBBMaxs(); } + + HSCRIPT ScriptGetMoveParent( void ); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild( void ); + HSCRIPT ScriptNextMovePeer( void ); +#endif + // Stubs on client void NetworkStateManualMode( bool activate ) { } void NetworkStateChanged() { } @@ -1275,6 +1342,7 @@ public: void SetRenderMode( RenderMode_t nRenderMode, bool bForceUpdate = false ); RenderMode_t GetRenderMode() const; + const char* GetEntityName(); public: // Determine what entity this corresponds to @@ -1289,6 +1357,10 @@ public: CNetworkColor32( m_clrRender ); +#ifdef MAPBASE + int m_iViewHideFlags; +#endif + private: // Model for rendering @@ -1656,6 +1728,8 @@ private: // The owner! EHANDLE m_hOwnerEntity; EHANDLE m_hEffectEntity; + + char m_iName[MAX_PATH]; // This is a random seed used by the networking code to allow client - side prediction code // randon number generators to spit out the same random numbers on both sides for a particular @@ -2211,6 +2285,12 @@ inline bool C_BaseEntity::ShouldRecordInTools() const #endif } +inline const char *C_BaseEntity::GetEntityName() +{ + return m_iName; +} + + C_BaseEntity *CreateEntityByName( const char *className ); #endif // C_BASEENTITY_H diff --git a/mp/src/game/client/c_baseflex.cpp b/mp/src/game/client/c_baseflex.cpp index 7e6c4bfc..fc1add9e 100644 --- a/mp/src/game/client/c_baseflex.cpp +++ b/mp/src/game/client/c_baseflex.cpp @@ -1149,7 +1149,9 @@ void C_BaseFlex::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightC { // hack in an initialization LinkToGlobalFlexControllers( GetModelPtr() ); +#ifndef MAPBASE m_iBlink = AddGlobalFlexController( "UH" ); +#endif if ( SetupGlobalWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ) ) { @@ -1478,7 +1480,7 @@ bool C_BaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool can // expression - // duration - //----------------------------------------------------------------------------- -void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide ) +void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide, C_SceneEntity* pSceneEntity) { if ( !scene || !event ) { @@ -1503,6 +1505,7 @@ void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseE info.m_hTarget = pTarget; info.m_bStarted = false; info.m_bClientSide = bClientSide; + info.m_hSceneEntity = pSceneEntity; if (StartSceneEvent( &info, scene, event, actor, pTarget )) { diff --git a/mp/src/game/client/c_baseflex.h b/mp/src/game/client/c_baseflex.h index 71cee3d8..6d9f1b54 100644 --- a/mp/src/game/client/c_baseflex.h +++ b/mp/src/game/client/c_baseflex.h @@ -214,7 +214,7 @@ public: virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); // Add the event to the queue for this actor - void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false ); + void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false, C_SceneEntity* pSceneEntity = NULL); // Remove the event from the queue for this actor void RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill ); diff --git a/mp/src/game/client/c_baselesson.cpp b/mp/src/game/client/c_baselesson.cpp new file mode 100644 index 00000000..8a2d9617 --- /dev/null +++ b/mp/src/game/client/c_baselesson.cpp @@ -0,0 +1,3894 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler implementations for instruction players how to play +// +//=============================================================================// + +#include "cbase.h" + +#include "c_baselesson.h" +#include "c_gameinstructor.h" + +#include "hud_locator_target.h" +#include "c_world.h" +#include "iinput.h" +#include "ammodef.h" +#include "vprof.h" +#include "view.h" +#include "vstdlib/ikeyvaluessystem.h" +#ifdef MAPBASE +#include "usermessages.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Configuración +//========================================================= + +#define LESSON_PRIORITY_MAX 1000 +#define LESSON_PRIORITY_NONE 0 +#define LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED 1.5f +#define LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED 0.1f +#define LESSON_DISTANCE_UPDATE_RATE 0.25f + +// See comments in UtlSymbol on why this is useful and how it works +IMPLEMENT_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol ); + +extern ConVar gameinstructor_verbose; +extern ConVar gameinstructor_verbose_lesson; +extern ConVar gameinstructor_find_errors; + +#ifdef MAPBASE +// Mapbase was originally going to use a HL2-style default color (245,232,179). +// This is no longer the case, but mods are free to change this cvar in their config files. +ConVar gameinstructor_default_captioncolor( "gameinstructor_default_captioncolor", "255,255,255", FCVAR_NONE ); +ConVar gameinstructor_default_bindingcolor( "gameinstructor_default_bindingcolor", "0,0,0", FCVAR_NONE ); +#endif + +// +// CGameInstructorLesson +// + +Color CBaseLesson::m_rgbaVerboseHeader = Color( 255, 128, 64, 255 ); +Color CBaseLesson::m_rgbaVerbosePlain = Color( 64, 128, 255, 255 ); +Color CBaseLesson::m_rgbaVerboseName = Color( 255, 255, 255, 255 ); +Color CBaseLesson::m_rgbaVerboseOpen = Color( 0, 255, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseClose = Color( 255, 0, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseSuccess = Color( 255, 255, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseUpdate = Color( 255, 0, 255, 255 ); + + +//========================================================= +// Constructor +//========================================================= +CBaseLesson::CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity ) +{ + COMPILE_TIME_ASSERT( sizeof( CGameInstructorSymbol ) == sizeof( CUtlSymbol ) ); + + m_stringName = pchName; + m_stringReplaceKey = ""; + m_bIsDefaultHolder = bIsDefaultHolder; + m_bIsOpenOpportunity = bIsOpenOpportunity; + + Init(); +} + +//========================================================= +// Destructor +//========================================================= +CBaseLesson::~CBaseLesson() +{ + // Remove from root's children list + if ( m_pRoot ) + m_pRoot->m_OpenOpportunities.FindAndRemove(this); + + else + { + for ( int i = 0; i < m_OpenOpportunities.Count(); ++i ) + { + // Remove from children if they are still around + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + pLesson->m_pRoot = NULL; + } + } +} + +//========================================================= +//========================================================= +void CBaseLesson::AddPrerequisite( const char *pchLessonName ) +{ + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "\t%s: ", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Adding prereq " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pchLessonName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + const CBaseLesson *pPrerequisite = GetGameInstructor().GetLesson( pchLessonName ); + + if ( !pPrerequisite ) + { + DevWarning( "Prerequisite %s added by lesson %s doesn't exist!\n", pchLessonName, GetName() ); + return; + } + + m_Prerequisites.AddToTail(pPrerequisite); +} + +//========================================================= +//========================================================= +void CBaseLesson::SetRoot( CBaseLesson *pRoot ) +{ + m_pRoot = pRoot; + + if ( m_pRoot->m_OpenOpportunities.Find( this ) == -1 ) + m_pRoot->m_OpenOpportunities.AddToTail( this ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::ShouldShowSpew() +{ + // @DEBUG + return true; + + if ( gameinstructor_verbose_lesson.GetString()[ 0 ] == '\0' ) + return false; + + return ( Q_stristr( GetName(), gameinstructor_verbose_lesson.GetString() ) != NULL ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::NoPriority() const +{ + return ( m_iPriority == LESSON_PRIORITY_NONE ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsLocked() const +{ + if ( m_fLockDuration == 0.0f ) + return false; + + if ( !IsInstructing() || !IsVisible() ) + return false; + + float fLockTime = m_fLockTime; + + if ( fLockTime == 0.0f ) + fLockTime = m_fStartTime; + + return ( gpGlobals->curtime > m_fStartTime + LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED && gpGlobals->curtime < fLockTime + m_fLockDuration ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsLearned() const +{ + if ( m_iDisplayLimit > 0 && m_iDisplayCount >= m_iDisplayLimit ) + return true; + + if ( m_iSuccessLimit > 0 && m_iSuccessCount >= m_iSuccessLimit ) + return true; + + return false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::PrerequisitesHaveBeenMet() const +{ + for ( int i = 0; i < m_Prerequisites.Count(); ++i ) + { + if ( !m_Prerequisites[ i ]->IsLearned() ) + { + // Failed a prereq + return false; + } + } + + // All prereqs passed + return true; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsTimedOut() +{ + VPROF_BUDGET( "CBaseLesson::IsTimedOut", "GameInstructor" ); + + // Check for no timeout + if ( m_fTimeout == 0.0f ) + return false; + + float fStartTime = m_fStartTime; + + if ( GetRoot()->IsLearned() ) + { + if ( !m_bBumpWithTimeoutWhenLearned ) + { + // Time out instantly if we've learned this and don't want to keep it open for priority bumping + return true; + } + else + { + // It'll never be active, so lets use timeout based on when it was initialized + fStartTime = m_fInitTime; + } + } + + if ( !fStartTime ) + { + if ( !m_bCanTimeoutWhileInactive ) + { + return false; + } + + // Not active, so lets use timeout based on when it was initialized + fStartTime = m_fInitTime; + } + + bool bTimedOut = ( fStartTime + m_fTimeout < gpGlobals->curtime ); + + if ( bTimedOut ) + SetCloseReason( "Timed out." ); + + return bTimedOut; +} + +//========================================================= +//========================================================= +void CBaseLesson::ResetDisplaysAndSuccesses() +{ + m_iDisplayCount = 0; + m_bSuccessCounted = false; + m_iSuccessCount = 0; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IncDisplayCount() +{ + if ( m_iDisplayCount < m_iDisplayLimit ) + { + m_iDisplayCount++; + return true; + } + + return false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IncSuccessCount() +{ + if ( m_iSuccessCount < m_iSuccessLimit ) + { + m_iSuccessCount++; + return true; + } + + return false; +} + +//========================================================= +//========================================================= +void CBaseLesson::Init() +{ + m_pRoot = NULL; + m_bSuccessCounted = false; + + SetCloseReason( "None given." ); + + m_iPriority = LESSON_PRIORITY_MAX; // Set to invalid value to ensure that it is actually set later on + m_iInstanceType = LESSON_INSTANCE_MULTIPLE; + m_iFixedInstancesMax = 1; + m_bReplaceOnlyWhenStopped = false; + m_iTeam = TEAM_ANY; + m_bOnlyKeyboard = false; + m_bOnlyGamepad = false; + + m_iDisplayLimit = 0; + m_iDisplayCount = 0; + m_bWasDisplayed = false; + + m_iSuccessLimit = 0; + m_iSuccessCount = 0; + + m_fLockDuration = 0.0f; + m_bCanOpenWhenDead = false; + m_bBumpWithTimeoutWhenLearned = false; + m_bCanTimeoutWhileInactive = false; + m_fTimeout = 0.0f; + + m_fInitTime = gpGlobals->curtime; + m_fStartTime = 0.0f; + m_fLockTime = 0.0f; + + m_fUpdateInterval = 0.5; + m_bHasPlayedSound = false; + + m_szStartSound = "Instructor.LessonStart"; + m_szLessonGroup = ""; + + m_iNumDelayedPlayerSwaps = 0; +} + +//========================================================= +//========================================================= +void CBaseLesson::TakePlaceOf( CBaseLesson *pLesson ) +{ + // Transfer over marked as displayed so a replaced lesson won't count as an extra display + m_bWasDisplayed = pLesson->m_bWasDisplayed; + pLesson->m_bWasDisplayed = false; +} + +//========================================================= +//========================================================= +void CBaseLesson::MarkSucceeded() +{ + if ( !m_bSuccessCounted ) + { + GetGameInstructor().MarkSucceeded( GetName() ); + m_bSuccessCounted = true; + } +} + +//========================================================= +//========================================================= +void CBaseLesson::CloseOpportunity( const char *pchReason ) +{ + SetCloseReason(pchReason); + m_bIsOpenOpportunity = false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::DoDelayedPlayerSwaps() const +{ + // A bot has swapped places with a player or player with a bot... + // At the time of the actual swap there was no client representation for the new player... + // So that swap was queued up and now we're going to make things right! + while ( m_iNumDelayedPlayerSwaps ) + { + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].iNewUserID ); + + if ( !pNewPlayer ) + { + // There is still no client representation of the new player, we'll have to try again later + if ( gameinstructor_verbose.GetInt() > 1 ) + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tFailed delayed player swap!" ); + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 1 ) + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tSuccessful delayed player swap!" ); + + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].phHandleToChange->Set( pNewPlayer ); + m_iNumDelayedPlayerSwaps--; + } + + return true; +} + + +// +// CTextLesson +// + +//========================================================= +//========================================================= +void CTextLesson::Init() +{ + m_szDisplayText = ""; + m_szDisplayParamText = ""; + m_szBinding = ""; + m_szGamepadBinding = ""; +} + +//========================================================= +//========================================================= +void CTextLesson::Start() +{ + // TODO: Display some text + //m_szDisplayText +} + +//========================================================= +//========================================================= +void CTextLesson::Stop() +{ + // TODO: Clean up text +} + +// +// CIconLesson +// + +void CIconLesson::Init() +{ + m_hIconTarget = NULL; + m_szVguiTargetName = ""; + m_szVguiTargetLookup = ""; + m_nVguiTargetEdge = 0; + + m_hLocatorTarget = -1; + m_bFixedPosition = false; + m_bNoIconTarget = false; + m_bAllowNodrawTarget = false; + + m_bVisible = true; + m_bShowWhenOccluded = true; + m_bNoOffscreen = false; + m_bForceCaption = false; + + m_szOnscreenIcon = ""; + m_szOffscreenIcon = ""; + + m_flUpOffset = 0.0f; + m_flRelativeUpOffset = 0.0f; + m_fFixedPositionX = 0.0f; + m_fFixedPositionY = 0.0f; + + m_fRange = 0.0f; + m_fCurrentDistance = 0.0f; + + m_fOnScreenStartTime = 0.0f; + m_fUpdateDistanceTime = 0.0f; + + m_iFlags = LOCATOR_ICON_FX_NONE; +#ifdef MAPBASE + m_szCaptionColor = gameinstructor_default_captioncolor.GetString(); + + m_iIconTargetPos = ICON_TARGET_EYE_POSITION; + m_szHudHint = ""; +#else + m_szCaptionColor = "255,255,255";// Default to white +#endif +} + +//========================================================= +//========================================================= +void CIconLesson::Start() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + // Display some text + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + // Wanted one, but couldn't get it + CloseOpportunity( "Icon Target handle went invalid before the lesson started!" ); + } + + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + CLocatorTarget *pLocatorTarget = NULL; + + if ( m_hLocatorTarget != -1 ) + { + // Lets try the handle that we've held on to + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + { + // It's gone stale, get a new target + m_hLocatorTarget = Locator_AddTarget(); + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + } + } + else + { + // Get a new target + m_hLocatorTarget = Locator_AddTarget(); + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + } + + if( m_hLocatorTarget == -1 || !pLocatorTarget ) + { + CloseOpportunity( "Could not get a handle for new locator target. Too many targets in use!" ); + return; + } + + pLocatorTarget->AddIconEffects( m_iFlags ); + pLocatorTarget->SetCaptionColor( GetCaptionColorString() ); + UpdateLocatorTarget( pLocatorTarget, pIconTarget ); + + // Update occlusion data + Locator_ComputeTargetIconPositionFromHandle( m_hLocatorTarget ); +} + +//========================================================= +//========================================================= +void CIconLesson::Stop() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + if ( m_hLocatorTarget != -1 ) + Locator_RemoveTarget( m_hLocatorTarget ); + + m_fOnScreenStartTime = 0.0f; +} + +//========================================================= +//========================================================= +void CIconLesson::Update() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + CloseOpportunity( "Lost our icon target handle returned NULL." ); + } + + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + if ( !pLocatorTarget ) + { + // Temp instrumentation to catch a bug - possibly calling Update without having called Start? + Warning( "Problem in lesson %s: Locator_GetTargetFromHandle returned null for handle %d.\n IsInstanceActive: %s. IsInstructing: %s. IsLearned: %s\n", + GetName(), m_hLocatorTarget, + (IsInstanceActive() ? "yes" : "no"), + (IsInstructing() ? "yes" : "no"), + (IsLearned() ? "yes" : "no") ); + CloseOpportunity( "Lost locator target handle." ); + return; + } + + UpdateLocatorTarget( pLocatorTarget, pIconTarget ); + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + // Check if it has been onscreen long enough to count as being displayed + if ( m_fOnScreenStartTime == 0.0f ) + { + if ( pLocatorTarget->IsOnScreen() && ( IsPresentComplete() || ( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) ) + { + // Is either static or has finished presenting and is on screen + m_fOnScreenStartTime = gpGlobals->curtime; + } + } + else + { + if ( !pLocatorTarget->IsOnScreen() ) + { + // Was visible before, but it isn't now + m_fOnScreenStartTime = 0.0f; + } + else if ( gpGlobals->curtime - m_fOnScreenStartTime >= LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED ) + { + // Lesson on screen long enough to be counted as displayed + m_bWasDisplayed = true; + } + } + + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + // Update it's distance from the local player + C_BaseEntity *pTarget = m_hIconTarget.Get(); + + if ( !pLocalPlayer || !pTarget || pLocalPlayer == pTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pTarget->WorldSpaceCenter() ); + } + + m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE; + } +} + +void CIconLesson::UpdateInactive() +{ + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + CloseOpportunity( "Lost our icon target handle returned NULL." ); + } + + m_fCurrentDistance = 0.0f; + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + // Update it's distance from the local player + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( !pLocalPlayer || pLocalPlayer == pIconTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pIconTarget->WorldSpaceCenter() ); + } + +#ifdef MAPBASE + if (m_szHudHint.String()[0] != '\0' && GetRoot()->IsLearned()) + { + DevMsg("Showing hint\n"); + CUtlBuffer msg_data; + msg_data.PutChar( 1 ); + msg_data.PutString( m_szHudHint.String() ); + usermessages->DispatchUserMessage( usermessages->LookupUserMessage( "KeyHintText" ), bf_read( msg_data.Base(), msg_data.TellPut() ) ); + } +#endif + + m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE; + } +} + +bool CIconLesson::ShouldDisplay() const +{ + VPROF_BUDGET( "CIconLesson::ShouldDisplay", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return false; + } + + if ( m_fRange > 0.0f && m_fCurrentDistance > m_fRange ) + { + // Distance to target is more than the max range + return false; + } + + if ( !m_bShowWhenOccluded && m_hLocatorTarget >= 0 ) + { + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( pLocatorTarget && pLocatorTarget->IsOccluded() ) + { + // Target is occluded and doesn't want to be shown when occluded + return false; + } + } + + // Ok to display + return true; +} + +bool CIconLesson::IsVisible() const +{ + VPROF_BUDGET( "CIconLesson::IsVisible", "GameInstructor" ); + + if( m_hLocatorTarget == -1 ) + { + // If it doesn't want a target, it's "visible" otherwise we'll have to call it invisible + return m_bNoIconTarget; + } + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + if ( !pLocatorTarget ) + { + return false; + } + + return pLocatorTarget->IsVisible(); +} + +void CIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID ) +{ + BaseClass::SwapOutPlayers( iOldUserID, iNewUserID ); + + if ( m_bNoIconTarget ) + return; + + // Get the player pointers from the user IDs + C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID ); + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID ); + + if ( pOldPlayer == m_hIconTarget.Get() ) + { + if ( pNewPlayer ) + { + m_hIconTarget = pNewPlayer; + } + else + { + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hIconTarget; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } +} + +void CIconLesson::TakePlaceOf( CBaseLesson *pLesson ) +{ + BaseClass::TakePlaceOf( pLesson ); + + const CIconLesson *pIconLesson = dynamic_cast( pLesson ); + + if ( pIconLesson ) + { + if ( pIconLesson->m_hLocatorTarget != -1 ) + { + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( pIconLesson->m_hLocatorTarget ); + + if ( pLocatorTarget ) + { + // This one draw right to the hud... use it's icon target handle + m_hLocatorTarget = pIconLesson->m_hLocatorTarget; + } + } + + m_fOnScreenStartTime = pIconLesson->m_fOnScreenStartTime; + } +} + +void CIconLesson::SetLocatorBinding( CLocatorTarget * pLocatorTarget ) +{ + if ( IsX360() /*|| input->ControllerModeActive()*/ ) + { + // Try to use gamepad bindings first + if ( m_szGamepadBinding.String()[ 0 ] != '\0' ) + { + // Found gamepad binds! + pLocatorTarget->SetBinding( m_szGamepadBinding.String() ); + } + else + { + // No gamepad binding, so fallback to the regular binding + pLocatorTarget->SetBinding( m_szBinding.String() ); + } + } + else + { + // Always use the regular binding when the gamepad is disabled + pLocatorTarget->SetBinding( m_szBinding.String() ); + } +} + +bool CIconLesson::IsPresentComplete() +{ + if ( m_hLocatorTarget == -1 ) + return false; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return false; + + return !pLocatorTarget->IsPresenting(); +} + +void CIconLesson::PresentStart() +{ + if ( m_hLocatorTarget == -1 ) + return; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return; + + pLocatorTarget->StartPresent(); +} + +void CIconLesson::PresentEnd() +{ + if ( m_hLocatorTarget == -1 ) + return; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return; + + pLocatorTarget->EndPresent(); +} + +void CIconLesson::UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget ) +{ + if ( m_bFixedPosition ) + { + pLocatorTarget->m_bOriginInScreenspace = true; + pLocatorTarget->m_vecOrigin.x = m_fFixedPositionX; + pLocatorTarget->m_vecOrigin.y = m_fFixedPositionY; + pLocatorTarget->SetVguiTargetName( m_szVguiTargetName.String() ); + pLocatorTarget->SetVguiTargetLookup( m_szVguiTargetLookup.String() ); + pLocatorTarget->SetVguiTargetEdge( m_nVguiTargetEdge ); + } + else + { + pLocatorTarget->m_bOriginInScreenspace = false; +#ifdef MAPBASE + pLocatorTarget->m_vecOrigin = GetIconTargetPosition( pIconTarget ) + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset ); +#else + pLocatorTarget->m_vecOrigin = pIconTarget->EyePosition() + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset ); +#endif + pLocatorTarget->SetVguiTargetName( "" ); + } + + const char *pchDisplayParamText = m_szDisplayParamText.String(); +#ifdef INFESTED_DLL + char szCustomName[ 256 ]; +#endif + + // Check if the parameter is the be the player display name + if ( Q_stricmp( pchDisplayParamText, "use_name" ) == 0 ) + { + // Fix up the player display name + C_BasePlayer *pPlayer = ToBasePlayer( pIconTarget ); + if ( pPlayer ) + { + pchDisplayParamText = pPlayer->GetPlayerName(); + } + else + { + bool bNoName = true; + +#ifdef INFESTED_DLL + C_ASW_Marine *pMarine = dynamic_cast< C_ASW_Marine* >( pIconTarget ); + if ( pMarine ) + { + C_ASW_Marine_Resource *pMR = pMarine->GetMarineResource(); + if ( pMR ) + { + pMR->GetDisplayName( szCustomName, sizeof( szCustomName ) ); + pchDisplayParamText = szCustomName; + bNoName = false; + } + } +#endif + + if ( bNoName ) + { + // It's not a player! + pchDisplayParamText = ""; + } + } + } + + pLocatorTarget->SetCaptionText( m_szDisplayText.String(), pchDisplayParamText ); + SetLocatorBinding( pLocatorTarget ); + pLocatorTarget->SetOnscreenIconTextureName( m_szOnscreenIcon.String() ); + pLocatorTarget->SetOffscreenIconTextureName( m_szOffscreenIcon.String() ); + pLocatorTarget->SetVisible( m_bVisible ); + + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if( !m_bFixedPosition && + ( ( pLocalPlayer != NULL && pLocalPlayer == m_hIconTarget ) || + GetClientWorldEntity() == m_hIconTarget ) ) + { + // Mark this icon as a static icon that draws in a fixed + // location on the hud rather than tracking an object + // in 3D space. + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_STATIC ); + } + else + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NONE ); + } + + if ( m_bNoOffscreen ) + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN ); + } + else + { + pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN ); + } + + if( m_bForceCaption || IsLocked() ) + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION ); + } + else + { + pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION ); + } + + pLocatorTarget->Update(); + + if ( pLocatorTarget->m_bIsDrawing ) + { + if ( !m_bHasPlayedSound ) + { + GetGameInstructor().PlaySound( m_szStartSound.String() ); + m_bHasPlayedSound = true; + } + } +} + +#ifdef MAPBASE +Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget ) +{ + switch (m_iIconTargetPos) + { + default: + case ICON_TARGET_EYE_POSITION: + return pIconTarget->EyePosition(); + + case ICON_TARGET_ORIGIN: + return pIconTarget->GetAbsOrigin(); + + case ICON_TARGET_CENTER: + return pIconTarget->WorldSpaceCenter(); + } +} +#endif + +// +// CScriptedIconLesson +// + +// Linking variables to scriptable entries is done here! +// The first parameter correlates to the case insensitive string name read from scripts. +// This macro generates code that passes this consistent variable data in to other macros +#define LESSON_VARIABLE_FACTORY \ + LESSON_VARIABLE_MACRO_EHANDLE( VOID, m_hLocalPlayer, EHANDLE ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( LOCAL_PLAYER, m_hLocalPlayer, EHANDLE ) \ + LESSON_VARIABLE_MACRO( OUTPUT, m_fOutput, float ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( ENTITY1, m_hEntity1, EHANDLE ) \ + LESSON_VARIABLE_MACRO_EHANDLE( ENTITY2, m_hEntity2, EHANDLE ) \ + LESSON_VARIABLE_MACRO_STRING( STRING1, m_szString1, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( STRING2, m_szString2, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( INTEGER1, m_iInteger1, int ) \ + LESSON_VARIABLE_MACRO( INTEGER2, m_iInteger2, int ) \ + LESSON_VARIABLE_MACRO( FLOAT1, m_fFloat1, float ) \ + LESSON_VARIABLE_MACRO( FLOAT2, m_fFloat2, float ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( ICON_TARGET, m_hIconTarget, EHANDLE ) \ + LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_NAME, m_szVguiTargetName, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_LOOKUP, m_szVguiTargetLookup, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( VGUI_TARGET_EDGE, m_nVguiTargetEdge, int ) \ + LESSON_VARIABLE_MACRO( FIXED_POSITION_X, m_fFixedPositionX, float ) \ + LESSON_VARIABLE_MACRO( FIXED_POSITION_Y, m_fFixedPositionY, float ) \ + LESSON_VARIABLE_MACRO_BOOL( FIXED_POSITION, m_bFixedPosition, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( NO_ICON_TARGET, m_bNoIconTarget, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( ALLOW_NODRAW_TARGET, m_bAllowNodrawTarget, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( VISIBLE, m_bVisible, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( SHOW_WHEN_OCCLUDED, m_bShowWhenOccluded, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( NO_OFFSCREEN, m_bNoOffscreen, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( FORCE_CAPTION, m_bForceCaption, bool ) \ + LESSON_VARIABLE_MACRO_STRING( ONSCREEN_ICON, m_szOnscreenIcon, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( OFFSCREEN_ICON, m_szOffscreenIcon, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( ICON_OFFSET, m_flUpOffset, float ) \ + LESSON_VARIABLE_MACRO( ICON_RELATIVE_OFFSET, m_flRelativeUpOffset, float ) \ + LESSON_VARIABLE_MACRO( RANGE, m_fRange, float ) \ + \ + LESSON_VARIABLE_MACRO( FLAGS, m_iFlags, int ) \ + LESSON_VARIABLE_MACRO_STRING( CAPTION_COLOR, m_szCaptionColor, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( GROUP, m_szLessonGroup, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO_STRING( CAPTION, m_szDisplayText, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( CAPTION_PARAM, m_szDisplayParamText, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( BINDING, m_szBinding, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( GAMEPAD_BINDING, m_szGamepadBinding, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( PRIORITY, m_iPriority, int ) \ + LESSON_VARIABLE_MACRO_STRING( REPLACE_KEY, m_stringReplaceKey, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( LOCK_DURATION, m_fLockDuration, float ) \ + LESSON_VARIABLE_MACRO_BOOL( CAN_OPEN_WHEN_DEAD, m_bCanOpenWhenDead, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( BUMP_WITH_TIMEOUT_WHEN_LEARNED, m_bBumpWithTimeoutWhenLearned, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( CAN_TIMEOUT_WHILE_INACTIVE, m_bCanTimeoutWhileInactive, bool ) \ + LESSON_VARIABLE_MACRO( TIMEOUT, m_fTimeout, float ) \ + LESSON_VARIABLE_MACRO( UPDATE_INTERVAL, m_fUpdateInterval, float ) \ + LESSON_VARIABLE_MACRO_STRING( START_SOUND, m_szStartSound, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( ICON_TARGET_POS, m_iIconTargetPos, int ) \ + LESSON_VARIABLE_MACRO_STRING( HUD_HINT_AFTER_LEARNED, m_szHudHint, CGameInstructorSymbol ) \ + + +// Create keyvalues name symbol +#define LESSON_VARIABLE_SYMBOL( _varEnum, _varName, _varType ) static int g_n##_varEnum##Symbol; + +#define LESSON_VARIABLE_INIT_SYMBOL( _varEnum, _varName, _varType ) g_n##_varEnum##Symbol = KeyValuesSystem()->GetSymbolForString( #_varEnum ); + +#define LESSON_SCRIPT_STRING_ADD_TO_MAP( _varEnum, _varName, _varType ) g_NameToTypeMap.Insert( #_varEnum, LESSON_VARIABLE_##_varEnum## ); + +// Create enum value +#define LESSON_VARIABLE_ENUM( _varEnum, _varName, _varType ) LESSON_VARIABLE_##_varEnum##, + +// Init info call +#define LESSON_VARIABLE_INIT_INFO_CALL( _varEnum, _varName, _varType ) g_pLessonVariableInfo[ LESSON_VARIABLE_##_varEnum## ].Init_##_varEnum##(); + +// Init info +#define LESSON_VARIABLE_INIT_INFO( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = LessonParamTypeFromString( #_varType ); \ + } + +#define LESSON_VARIABLE_INIT_INFO_BOOL( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = FIELD_BOOLEAN; \ + } + +#define LESSON_VARIABLE_INIT_INFO_EHANDLE( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = FIELD_EHANDLE; \ + } + +#define LESSON_VARIABLE_INIT_INFO_STRING( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = FIELD_STRING; \ + } + +// Copy defaults into this scripted lesson into a new one +#define LESSON_VARIABLE_DEFAULT( _varEnum, _varName, _varType ) ( _varName = m_pDefaultHolder->_varName ); + +// Copy a variable from this scripted lesson into a new one +#define LESSON_VARIABLE_COPY( _varEnum, _varName, _varType ) ( pOpenLesson->_varName = _varName ); + +// Return the first param if pchName is the same as the second param +#define LESSON_SCRIPT_STRING( _type, _string ) \ + if ( Q_stricmp( pchName, _string ) == 0 )\ + {\ + return _type;\ + } + +// Wrapper for using this macro in the factory +#define LESSON_SCRIPT_STRING_GENERAL( _varEnum, _varName, _varType ) LESSON_SCRIPT_STRING( LESSON_VARIABLE_##_varEnum##, #_varEnum ) + +// Process the element action on this variable +#define PROCESS_LESSON_ACTION( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum##:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float ); + +#define PROCESS_LESSON_ACTION_EHANDLE( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum##:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float, eventParam_BaseEntity, eventParam_string ); + +#define PROCESS_LESSON_ACTION_STRING( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum##:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, &_varName, &pLessonElement->szParam, eventParam_string ); + +// Init the variable from the script (or a convar) +#define LESSON_VARIABLE_INIT( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + const char *pchParam = pSubKey->GetString(); \ + if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \ + { \ + ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \ + if ( tempCVar.IsValid() ) \ + { \ + _varName = static_cast<_varType>( tempCVar.GetFloat() ); \ + } \ + else \ + { \ + _varName = static_cast<_varType>( 0.0f ); \ + } \ + } \ + else \ + { \ + _varName = static_cast<_varType>( pSubKey->GetFloat() ); \ + } \ + } + +#define LESSON_VARIABLE_INIT_BOOL( _varEnum, _varName, _varType ) \ + else if ( Q_stricmp( #_varEnum, pSubKey->GetName() ) == 0 ) \ + { \ + _varName = pSubKey->GetBool(); \ + } + +#define LESSON_VARIABLE_INIT_EHANDLE( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + DevWarning( "Can't initialize an EHANDLE from the instructor lesson script." ); \ + } + +#define LESSON_VARIABLE_INIT_STRING( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + const char *pchParam = pSubKey->GetString(); \ + if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \ + { \ + ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \ + if ( tempCVar.IsValid() ) \ + { \ + _varName = tempCVar.GetString(); \ + } \ + else \ + { \ + _varName = ""; \ + } \ + } \ + else \ + { \ + _varName = pSubKey->GetString(); \ + } \ + } + +// Gets a scripted variable by offset and casts it to the proper type +#define LESSON_VARIABLE_GET_FROM_OFFSET( _type, _offset ) *static_cast<_type*>( static_cast( static_cast( static_cast( this ) ) + _offset ) ) + + +// Enum of scripted variables +enum LessonVariable +{ + // Run enum macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_ENUM + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + LESSON_VARIABLE_TOTAL +}; + +// Declare the keyvalues symbols for the keynames +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_SYMBOL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + +// String lookup prototypes +LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames = true ); +_fieldtypes LessonParamTypeFromString( const char *pchName ); +int LessonActionFromString( const char *pchName ); + + +// This is used to get type info an variable offsets from the variable enumerated value +class LessonVariableInfo +{ +public: + + LessonVariableInfo() + : iOffset( 0 ), varType( FIELD_VOID ) + { + } + + // Run init info macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_BOOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_STRING + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + +public: + + int iOffset; + _fieldtypes varType; +}; + +LessonVariableInfo g_pLessonVariableInfo[ LESSON_VARIABLE_TOTAL ]; + + +const LessonVariableInfo *GetLessonVariableInfo( int iLessonVariable ) +{ + Assert( iLessonVariable >= 0 && iLessonVariable < LESSON_VARIABLE_TOTAL ); + + if ( g_pLessonVariableInfo[ 0 ].varType == FIELD_VOID ) + { + // Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_CALL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + return &(g_pLessonVariableInfo[ iLessonVariable ]); +} + +static CUtlDict< LessonVariable, int > g_NameToTypeMap; +static CUtlDict< fieldtype_t, int > g_TypeToParamTypeMap; +CUtlDict< int, int > CScriptedIconLesson::LessonActionMap; + +CScriptedIconLesson::~CScriptedIconLesson() +{ + if ( m_pDefaultHolder ) + { + delete m_pDefaultHolder; + m_pDefaultHolder = NULL; + } +} + + +void CScriptedIconLesson::Init() +{ + m_hLocalPlayer.Set( NULL ); + m_fOutput = 0.0f; + m_hEntity1.Set( NULL ); + m_hEntity2.Set( NULL ); + m_szString1 = ""; + m_szString2 = ""; + m_iInteger1 = 0; + m_iInteger2 = 0; + m_fFloat1 = 0.0f; + m_fFloat2 = 0.0f; + + m_fUpdateEventTime = 0.0f; + m_pDefaultHolder = NULL; + m_iScopeDepth = 0; + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing scripted lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + if ( !IsDefaultHolder() ) + { + if ( !IsOpenOpportunity() ) + { + // Initialize from the key value file + InitFromKeys( GetGameInstructor().GetScriptKeys() ); + + if ( m_iPriority >= LESSON_PRIORITY_MAX ) + { + DevWarning( "Priority level not set for lesson: %s\n", GetName() ); + } + + // We use this to remember variable defaults to be reset before each open attempt + m_pDefaultHolder = new CScriptedIconLesson( GetName(), true, false ); + CScriptedIconLesson *pOpenLesson = m_pDefaultHolder; + + // Run copy macros on all default scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // Listen for open events + for ( int iLessonEvent = 0; iLessonEvent < m_OpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_OpenEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + + // Listen for close events + for ( int iLessonEvent = 0; iLessonEvent < m_CloseEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_CloseEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + + // Listen for success events + for ( int iLessonEvent = 0; iLessonEvent < m_SuccessEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_SuccessEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String()); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + } + else + { + // This is an open lesson! Get the root for reference + const CScriptedIconLesson *pLesson = static_cast( GetGameInstructor().GetLesson( GetName() ) ); + SetRoot( const_cast( pLesson ) ); + } + } +} + +void CScriptedIconLesson::InitPrerequisites() +{ + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing prereqs for scripted lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + for ( int iPrerequisit = 0; iPrerequisit < m_PrerequisiteNames.Count(); ++iPrerequisit ) + { + const char *pPrerequisiteName = m_PrerequisiteNames[ iPrerequisit ].String(); + AddPrerequisite( pPrerequisiteName ); + } +} + +void CScriptedIconLesson::OnOpen() +{ + VPROF_BUDGET( "CScriptedIconLesson::OnOpen", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + const CScriptedIconLesson *pLesson = static_cast( GetRoot() ); + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_OnOpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->m_OnOpenEvents[ iLessonEvent ]); + + if ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "GAME INSTRUCTOR: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "OnOpen event " ); + ConColorMsg( Color( 0, 255, 0, 255 ), "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( Color( 64, 128, 255, 255 ), "received for lesson \"%s\"...\n", GetName() ); + } + + ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + BaseClass::OnOpen(); +} + +void CScriptedIconLesson::Update() +{ + VPROF_BUDGET( "CScriptedIconLesson::Update", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + const CScriptedIconLesson *pLesson = static_cast( GetRoot() ); + + if ( gpGlobals->curtime >= m_fUpdateEventTime ) + { + bool bShowSpew = ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() ); + + int iVerbose = gameinstructor_verbose.GetInt(); + if ( gameinstructor_verbose.GetInt() == 1 ) + { + // Force the verbose level from 1 to 0 for update events + gameinstructor_verbose.SetValue( 0 ); + } + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_UpdateEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->m_UpdateEvents[ iLessonEvent ]); + + if ( bShowSpew ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Update event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + gameinstructor_verbose.SetValue( iVerbose ); + + // Wait before doing update events again + m_fUpdateEventTime = gpGlobals->curtime + m_fUpdateInterval; + } + + BaseClass::Update(); +} + +void CScriptedIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID ) +{ + BaseClass::SwapOutPlayers( iOldUserID, iNewUserID ); + + // Get the player pointers from the user IDs + C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID ); + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID ); + + if ( pOldPlayer == m_hEntity1.Get() ) + { + if ( pNewPlayer ) + { + m_hEntity1 = pNewPlayer; + } + else + { + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity1; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } + + if ( pOldPlayer == m_hEntity2.Get() ) + { + if ( pNewPlayer ) + { + m_hEntity2 = pNewPlayer; + } + else + { + + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity2; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } +} + +void CScriptedIconLesson::FireGameEvent( IGameEvent *event ) +{ + VPROF_BUDGET( "CScriptedIconLesson::FireGameEvent", "GameInstructor" ); + + if ( m_bDisabled ) + return; + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + if ( !C_BasePlayer::GetLocalPlayer() ) + return; + + // Check that this lesson is allowed for the current input device + if( m_bOnlyKeyboard /*&& input->ControllerModeActive()*/ ) + return; + + if( m_bOnlyGamepad /*&& !input->ControllerModeActive()*/ ) + return; + + // Check that this lesson is for the proper team + CBasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( m_iTeam != TEAM_ANY && pLocalPlayer && pLocalPlayer->GetTeamNumber() != m_iTeam ) + { + // This lesson is intended for a different team + return; + } + + const char *name = event->GetName(); + + // Open events run on the root + ProcessOpenGameEvents( this, name, event ); + + // Close and success events run on the children + const CUtlVector < CBaseLesson * > *pChildren = GetChildren(); + for ( int iChild = 0; iChild < pChildren->Count(); ++iChild ) + { + CScriptedIconLesson *pScriptedChild = dynamic_cast( (*pChildren)[ iChild ] ); + + pScriptedChild->ProcessCloseGameEvents( this, name, event ); + pScriptedChild->ProcessSuccessGameEvents( this, name, event ); + } +} + +void CScriptedIconLesson::ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN && GetGameInstructor().IsLessonOfSameTypeOpen( this ) ) + { + // We don't want more than one of this type, and there is already one open + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pRootLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "open events NOT processed (there is already an open lesson of this type).\n" ); + } + + return; + } + + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_OpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_OpenEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( m_pDefaultHolder ) + { + // Run copy from default macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_DEFAULT + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\tAll elements returned true. Opening!\n" ); + } + + MEM_ALLOC_CREDIT(); + CScriptedIconLesson *pOpenLesson = new CScriptedIconLesson( GetName(), false, true ); + + // Run copy macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + if ( GetGameInstructor().OpenOpportunity( pOpenLesson ) ) + { + pOpenLesson->OnOpen(); + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN ) + { + // This one is open and we only want one! So, we're done. + // Other open events may be listening for the same events... skip them! + return; + } + } + } + } + } +} + +void CScriptedIconLesson::ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_CloseEvents.Count() && IsOpenOpportunity(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_CloseEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tAll elements returned true. Closing!\n" ); + } + + CloseOpportunity( "Close event elements completed." ); + } + } + } +} + +void CScriptedIconLesson::ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_SuccessEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_SuccessEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\tAll elements returned true. Succeeding!\n" ); + } + + MarkSucceeded(); + } + } + } +} + +LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames ) +{ + int slot = g_NameToTypeMap.Find( pchName ); + if ( slot != g_NameToTypeMap.InvalidIndex() ) + return g_NameToTypeMap[ slot ]; + + if ( bWarnOnInvalidNames ) + { + AssertMsg( 0, "Invalid scripted lesson variable!" ); + DevWarning( "Invalid scripted lesson variable: %s\n", pchName ); + } + + return LESSON_VARIABLE_TOTAL; +} + +_fieldtypes LessonParamTypeFromString( const char *pchName ) +{ + int slot = g_TypeToParamTypeMap.Find( pchName ); + if ( slot != g_TypeToParamTypeMap.InvalidIndex() ) + return g_TypeToParamTypeMap[ slot ]; + + DevWarning( "Invalid scripted lesson variable/param type: %s\n", pchName ); + return FIELD_VOID; +} + +int LessonActionFromString( const char *pchName ) +{ + int slot = CScriptedIconLesson::LessonActionMap.Find( pchName ); + if ( slot != CScriptedIconLesson::LessonActionMap.InvalidIndex() ) + return CScriptedIconLesson::LessonActionMap[ slot ]; + + DevWarning( "Invalid scripted lesson action: %s\n", pchName ); + return LESSON_ACTION_NONE; +} + +void CScriptedIconLesson::InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ) +{ + KeyValues *pSubKey = NULL; + for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + char szSubKeyName[ 256 ]; + Q_strcpy( szSubKeyName, pSubKey->GetName() ); + + char *pchToken = strtok( szSubKeyName, " " ); + LessonVariable iVariable = LessonVariableFromString( pchToken ); + + pchToken = strtok ( NULL, "" ); + int iAction = LESSON_ACTION_NONE; + bool bNot = false; + bool bOptionalParam = false; + + if ( !pchToken || pchToken[ 0 ] == '\0' ) + { + DevWarning( "No action specified for variable: \"%s\"\n", pSubKey->GetName() ); + } + else + { + if ( pchToken[ 0 ] == '?' ) + { + pchToken++; + bOptionalParam = true; + } + + if ( pchToken[ 0 ] == '!' ) + { + pchToken++; + bNot = true; + } + + iAction = LessonActionFromString( pchToken ); + } + + Q_strcpy( szSubKeyName, pSubKey->GetString() ); + + pchToken = strtok( szSubKeyName, " " ); + _fieldtypes paramType = LessonParamTypeFromString( pchToken ); + + char *pchParam = ""; + + if ( paramType != FIELD_VOID ) + { + pchToken = strtok ( NULL, "" ); + pchParam = pchToken; + } + + if ( !pchParam ) + { + DevWarning( "No parameter specified for action: \"%s\"\n", pSubKey->GetName() ); + } + else + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t\tElement \"%s %s\" added.\n", pSubKey->GetName(), pSubKey->GetString() ); + } + + // See if our param is a scripted var + LessonVariable iParamVarIndex = LessonVariableFromString( pchParam, false ); + + pLessonElements->AddToTail( LessonElement_t( iVariable, iAction, bNot, bOptionalParam, pchParam, iParamVarIndex, paramType ) ); + } + } +} + +void CScriptedIconLesson::InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ) +{ + for ( int i = 0; i < pLessonElements2->Count(); ++i ) + { + pLessonElements->AddToTail( LessonElement_t( (*pLessonElements2)[ i ] ) ); + } +} + +void CScriptedIconLesson::InitFromKeys( KeyValues *pKey ) +{ + if ( !pKey ) + return; + + static int s_nInstanceTypeSymbol = KeyValuesSystem()->GetSymbolForString( "instance_type" ); + static int s_nReplaceKeySymbol = KeyValuesSystem()->GetSymbolForString( "replace_key" ); + static int s_nFixedInstancesMaxSymbol = KeyValuesSystem()->GetSymbolForString( "fixed_instances_max" ); + static int s_nReplaceOnlyWhenStopped = KeyValuesSystem()->GetSymbolForString( "replace_only_when_stopped" ); + static int s_nTeamSymbol = KeyValuesSystem()->GetSymbolForString( "team" ); + static int s_nOnlyKeyboardSymbol = KeyValuesSystem()->GetSymbolForString( "only_keyboard" ); + static int s_nOnlyGamepadSymbol = KeyValuesSystem()->GetSymbolForString( "only_gamepad" ); + static int s_nDisplayLimitSymbol = KeyValuesSystem()->GetSymbolForString( "display_limit" ); + static int s_nSuccessLimitSymbol = KeyValuesSystem()->GetSymbolForString( "success_limit" ); + static int s_nPreReqSymbol = KeyValuesSystem()->GetSymbolForString( "prereq" ); + static int s_nOpenSymbol = KeyValuesSystem()->GetSymbolForString( "open" ); + static int s_nCloseSymbol = KeyValuesSystem()->GetSymbolForString( "close" ); + static int s_nSuccessSymbol = KeyValuesSystem()->GetSymbolForString( "success" ); + static int s_nOnOpenSymbol = KeyValuesSystem()->GetSymbolForString( "onopen" ); + static int s_nUpdateSymbol = KeyValuesSystem()->GetSymbolForString( "update" ); + + KeyValues *pSubKey = NULL; + for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + if ( pSubKey->GetNameSymbol() == s_nInstanceTypeSymbol ) + { + m_iInstanceType = LessonInstanceType( pSubKey->GetInt() ); + } + else if ( pSubKey->GetNameSymbol() == s_nReplaceKeySymbol ) + { + m_stringReplaceKey = pSubKey->GetString(); + } + else if ( pSubKey->GetNameSymbol() == s_nFixedInstancesMaxSymbol ) + { + m_iFixedInstancesMax = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nReplaceOnlyWhenStopped ) + { + m_bReplaceOnlyWhenStopped = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nTeamSymbol ) + { + m_iTeam = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nOnlyKeyboardSymbol ) + { + m_bOnlyKeyboard = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nOnlyGamepadSymbol ) + { + m_bOnlyGamepad = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nDisplayLimitSymbol ) + { + m_iDisplayLimit = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nSuccessLimitSymbol ) + { + m_iSuccessLimit = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nPreReqSymbol ) + { + CGameInstructorSymbol pName; + pName = pSubKey->GetString(); + m_PrerequisiteNames.AddToTail( pName ); + } + else if ( pSubKey->GetNameSymbol() == s_nOpenSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddOpenEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nCloseSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddCloseEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nSuccessSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddSuccessEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nOnOpenSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddOnOpenEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding onopen event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nUpdateSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddUpdateEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding update event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + + // Run intialize from key macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_BOOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_STRING + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } +} + +bool CScriptedIconLesson::ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ) +{ + VPROF_BUDGET( "CScriptedIconLesson::ProcessElements", "GameInstructor" ); + + m_hLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + bool bSuccess = true; + int nContinueScope = -1; + m_iScopeDepth = 0; + + if ( gameinstructor_find_errors.GetBool() ) + { + // Just run them all to check for errors! + for ( int iElement = 0; iElement < pElements->Count(); ++iElement ) + { + ProcessElement( event, &((*pElements)[ iElement ] ), false ); + } + + return false; + } + + // Process each element until a step fails + for ( int iElement = 0; iElement < pElements->Count(); ++iElement ) + { + if ( nContinueScope == m_iScopeDepth ) + { + nContinueScope = -1; + } + + if ( !ProcessElement( event, &((*pElements)[ iElement ]), nContinueScope != -1 ) ) + { + // This element failed + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tPrevious element returned false.\n" ); + } + + nContinueScope = m_iScopeDepth - 1; + + if ( nContinueScope < 0 ) + { + // No outer scope to worry about, we're done + bSuccess = false; + break; + } + } + } + + return bSuccess; +} + +bool CScriptedIconLesson::ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope ) +{ + VPROF_BUDGET( "CScriptedIconLesson::ProcessElement", "GameInstructor" ); + + if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_IN ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeIn()\n" ); + } + + m_iScopeDepth++; + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_OUT ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeOut()\n" ); + } + + m_iScopeDepth--; + return true; + } + + if ( bInFailedScope ) + { + // Only scope upkeep is done when we're in a failing scope... bail! + return true; + } + + if ( pLessonElement->iAction == LESSON_ACTION_CLOSE ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tCloseOpportunity()\n" ); + } + + CloseOpportunity( "Close action." ); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_SUCCESS ) + { + // Special case for succeeding (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tMarkSucceeded()\n" ); + } + + MarkSucceeded(); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_LOCK ) + { + // Special case for setting the starting point for the lesson to stay locked from (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tm_fLockTime = gpGlobals->curtime\n" ); + } + + m_fLockTime = gpGlobals->curtime; + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_COMPLETE ) + { + // Special case for checking presentation status (we don't need variables for this) + bool bPresentComplete = IsPresentComplete(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tIsPresentComplete() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", ( bPresentComplete ) ? ( "true" ) : ( "false" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( pLessonElement->bNot ) ? ( "!= true\n" ) : ( "== true\n" ) ); + } + + return ( pLessonElement->bNot ) ? ( !bPresentComplete ) : ( bPresentComplete ); + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_START ) + { + // Special case for setting presentation status (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentStart()\n" ); + } + + PresentStart(); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_END ) + { + // Special case for setting presentation status (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentEnd()\n" ); + } + + PresentEnd(); + return true; + } + + // These values temporarily hold the parameter's value + const char *pParamName = pLessonElement->szParam.String(); + float eventParam_float = 0.0f; + char eventParam_string[ 256 ]; + eventParam_string[ 0 ] = '\0'; + C_BaseEntity *eventParam_BaseEntity = NULL; + + // Get the value from the event parameter based on its type + switch ( pLessonElement->paramType ) + { + case FIELD_FLOAT: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ); + break; + case FIELD_INTEGER: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ); + break; + case FIELD_BOOLEAN: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ); + break; + case FIELD_STRING: + eventParam_float = static_cast( atoi( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ) ); + break; + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = event->GetFloat( pParamName ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' || pParamName[ 0 ] == '.' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = Q_atof( pParamName ); + } + else + { + DevWarning( "Invalid event field name and not a float \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_INTEGER: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ); + break; + case FIELD_INTEGER: + eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ); + break; + case FIELD_BOOLEAN: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ); + break; + case FIELD_STRING: + eventParam_float = atof( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ); + break; + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = static_cast( event->GetInt( pParamName ) ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = static_cast( Q_atoi( pParamName ) ); + } + else + { + DevWarning( "Invalid event field name and not an integer \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_STRING: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_STRING: + Q_strncpy( eventParam_string, &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String(), sizeof( eventParam_string ) ); + break; + case FIELD_FLOAT: + Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%f", LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ); + break; + case FIELD_INTEGER: + Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%i", LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ); + break; + case FIELD_BOOLEAN: + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else + { + const char *pchEventString = NULL; + + if ( event && !(event->IsEmpty( pParamName )) ) + { + pchEventString = event->GetString( pParamName ); + } + + if ( pchEventString && pchEventString[0] ) + { + Q_strcpy( eventParam_string, pchEventString ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else + { + // This param doesn't exist, try parsing the string + Q_strncpy( eventParam_string, pParamName, sizeof( eventParam_string ) ); + } + } + break; + + case FIELD_BOOLEAN: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_INTEGER: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_BOOLEAN: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_EHANDLE: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = ( event->GetBool( pParamName ) ) ? ( 1.0f ) : ( 0.0f ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( pParamName[ 0 ] == '0' || pParamName[ 0 ] == '1' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = Q_atof( pParamName ) != 0.0f; + } + else + { + DevWarning( "Invalid event field name and not an boolean \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_CUSTOM: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_EHANDLE: + eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get(); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + break; + case FIELD_FLOAT: + case FIELD_INTEGER: + case FIELD_BOOLEAN: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_BaseEntity = UTIL_PlayerByUserId( event->GetInt( pParamName ) ); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( Q_stricmp( pParamName, "null" ) == 0 ) + { + // They explicitly want a null pointer + eventParam_BaseEntity = NULL; + } + else + { + DevWarning( "Invalid event field name \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_EHANDLE: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_EHANDLE: + eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get(); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + break; + case FIELD_FLOAT: + case FIELD_INTEGER: + case FIELD_BOOLEAN: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + int iEntID = event->GetInt( pParamName ); + if ( iEntID >= NUM_ENT_ENTRIES ) + { + AssertMsg( 0, "Invalid entity ID used in game event field!" ); + DevWarning( "Invalid entity ID used in game event (%s) for param (%s).", event->GetName(), pParamName ); + return false; + } + + eventParam_BaseEntity = C_BaseEntity::Instance( iEntID ); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( Q_stricmp( pParamName, "null" ) == 0 ) + { + // They explicitly want a null pointer + eventParam_BaseEntity = NULL; + } + else if ( Q_stricmp( pParamName, "world" ) == 0 ) + { + // They explicitly want the world + eventParam_BaseEntity = GetClientWorldEntity(); + } + else + { + DevWarning( "Invalid event field name \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_EMBEDDED: + { + // The parameter is a convar + ConVarRef tempCVar( pParamName ); + if ( tempCVar.IsValid() ) + { + eventParam_float = tempCVar.GetFloat(); + Q_strncpy( eventParam_string, tempCVar.GetString(), sizeof( eventParam_string ) ); + } + else + { + DevWarning( "Invalid convar name \"%s\".\n", pParamName ); + return false; + } + } + break; + } + + // Do the action to the specified variable + switch ( pLessonElement->iVariable ) + { + // Run process action macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO PROCESS_LESSON_ACTION +#define LESSON_VARIABLE_MACRO_BOOL PROCESS_LESSON_ACTION +#define LESSON_VARIABLE_MACRO_EHANDLE PROCESS_LESSON_ACTION_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING PROCESS_LESSON_ACTION_STRING + LESSON_VARIABLE_FACTORY; +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + return true; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &fVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + switch ( iAction ) + { + case LESSON_ACTION_SET: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar = fParam; + return true; + + case LESSON_ACTION_ADD: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] += [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar += fParam; + return true; + + case LESSON_ACTION_SUBTRACT: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] -= [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar -= fParam; + return true; + + case LESSON_ACTION_MULTIPLY: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] *= [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar *= fParam; + return true; + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + return ( bNot ) ? ( fVar != fParam ) : ( fVar == fParam ); + + case LESSON_ACTION_LESS_THAN: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + return ( bNot ) ? ( fVar >= fParam ) : ( fVar < fParam ); + + case LESSON_ACTION_HAS_BIT: + { + int iTemp1 = static_cast( fVar ); + int iTemp2 = ( 1 << static_cast( fParam ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "& [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X", iTemp2 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == 0\n" ) : ( ") != 0\n" ) ); + } + + return ( bNot ) ? ( ( iTemp1 & iTemp2 ) == 0 ) : ( ( iTemp1 & iTemp2 ) != 0 ); + } + + case LESSON_ACTION_BIT_COUNT_IS: + { + int iTemp1 = UTIL_CountNumBitsSet( static_cast( fVar ) ); + int iTemp2 = static_cast( fParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 ); + } + + return ( bNot ) ? ( iTemp1 != iTemp2 ) : ( iTemp1 == iTemp2 ); + } + + case LESSON_ACTION_BIT_COUNT_LESS_THAN: + { + int iTemp1 = UTIL_CountNumBitsSet( static_cast( fVar ) ); + int iTemp2 = static_cast( fParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " >= [%s] " ) : ( " < [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 ); + } + + return ( bNot ) ? ( iTemp1 >= iTemp2 ) : ( iTemp1 < iTemp2 ); + } + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &iVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + float fTemp = static_cast( iVar ); + bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam ); + + iVar = static_cast( fTemp ); + return bRetVal; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + float fTemp = ( bVar ) ? ( 1.0f ) : ( 0.0f ); + bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam ); + + bVar = ( fTemp != 0.0f ); + return bRetVal; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam ) +{ + // First try to let the mod act on the action + /*bool bModHandled = false; + bool bModReturn = Mod_ProcessElementAction( iAction, bNot, pchVarName, hVar, pchParamName, fParam, pParam, pchParam, bModHandled ); + + if ( bModHandled ) + { + return bModReturn; + }*/ + + C_BaseEntity *pVar = hVar.Get(); + + switch ( iAction ) + { + case LESSON_ACTION_SET: + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]\n", pchVarName, pchParamName->String() ); + } + + hVar = pParam; + return true; + } + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t[%s] != [%s]\n" ) : ( "\t[%s] == [%s]\n" ), pchVarName, pchParamName->String() ); + } + + return ( bNot ) ? ( pVar != pParam ) : ( pVar == pParam ); + + case LESSON_ACTION_GET_DISTANCE: + { + if ( !pVar || !pParam ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] )", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" ); + } + + return false; + } + + C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL ); + C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL ); + + Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() ); + Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() ); + + m_fOutput = vVarPos.DistTo( vParamPos ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] ) ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + } + + return true; + } + + case LESSON_ACTION_GET_ANGULAR_DISTANCE: + { + if ( !pVar || !pParam ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] )", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" ); + } + + return false; + } + + C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL ); + C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL ); + + Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() ); + Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() ); + + Vector vVarToParam = vParamPos - vVarPos; + VectorNormalize( vVarToParam ); + + Vector vVarForward; + + if ( pVar->IsPlayer() ) + { + AngleVectors( static_cast< C_BasePlayer* >( pVar )->EyeAngles(), &vVarForward, NULL, NULL ); + } + else + { + pVar->GetVectors( &vVarForward, NULL, NULL ); + } + + // Set the distance in degrees + m_fOutput = ( vVarToParam.Dot( vVarForward ) - 1.0f ) * -90.0f; + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] ) ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + } + + return true; + } + + case LESSON_ACTION_GET_PLAYER_DISPLAY_NAME: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [stringINVALID], [%s]->GetPlayerName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use string2 if it was specified, otherwise, use string1 + CGameInstructorSymbol *pString; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pString = &m_szString2; + pchParamNameTemp = "string2"; + } + else + { + pString = &m_szString1; + pchParamNameTemp = "string1"; + } + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + *pString = pVar->GetPlayerName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pString->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return true; + } + + case LESSON_ACTION_CLASSNAME_IS: + { + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pVar->GetClassname() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return ( bNot ) ? ( !FClassnameIs( pVar, pchParam ) ) : ( FClassnameIs( pVar, pchParam ) ); + } + + case LESSON_ACTION_TEAM_IS: + { + int iTemp = static_cast( fParam ); + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetTeamNumber() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + } + + return ( bNot ) ? ( pVar->GetTeamNumber() != iTemp ) : ( pVar->GetTeamNumber() == iTemp ); + } + + case LESSON_ACTION_MODELNAME_IS: + { + C_BaseAnimating *pBaseAnimating = dynamic_cast( pVar ); + + if ( !pBaseAnimating ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseAnimating returned NULL!\n" ); + } + + return false; + } + + const char *pchModelName = "-no model-"; + CStudioHdr *pModel = pBaseAnimating->GetModelPtr(); + if ( pModel ) + { + const studiohdr_t *pRenderHDR = pModel->GetRenderHdr(); + if ( pRenderHDR ) + { + pchModelName = pRenderHDR->name; + } + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pchModelName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + } + + return ( bNot ) ? ( Q_stricmp( pchModelName, pchParam ) != 0 ) : ( Q_stricmp( pchModelName, pchParam ) == 0 ); + } + + case LESSON_ACTION_HEALTH_LESS_THAN: + { + int iTemp = static_cast( fParam ); + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetHealth() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + } + + return ( bNot ) ? ( pVar->GetHealth() >= iTemp ) : ( pVar->GetHealth() < iTemp ); + } + + case LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN: + { + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", pVar->HealthFraction() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + float fHealthPercentage = 1.0f; + + if ( pVar->GetMaxHealth() != 0.0f ) + { + fHealthPercentage = pVar->HealthFraction(); + } + + return ( bNot ) ? ( fHealthPercentage >= fParam ) : ( fHealthPercentage < fParam ); + } + + case LESSON_ACTION_GET_ACTIVE_WEAPON: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = [%s]->GetActiveWeapon()\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( pBaseCombatCharacter->GetActiveWeapon() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + } + + return true; + } + + case LESSON_ACTION_WEAPON_IS: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pBaseCombatWeapon->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + } + + return ( bNot ) ? ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) != 0 ) : ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) == 0 ); + } + + case LESSON_ACTION_WEAPON_HAS: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return ( bNot ) ? ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) == NULL ) : ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) != NULL ); + } + + case LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + C_BaseCombatWeapon *pWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + // @TODO + /*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pWeapon->GetWpnData().szClassName ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + }*/ + + return true; + } + + case LESSON_ACTION_GET_WEAPON_SLOT: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") ...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + // @TODO + /*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pchParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + }*/ + + return true; + } + + case LESSON_ACTION_GET_WEAPON_IN_SLOT: + { + int nTemp = static_cast( fParam ); + + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + m_hEntity1 = pBaseCombatCharacter->GetWeapon( nTemp ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return true; + } + + case LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + float fClip1Percentage = 100.0f; + + if ( pBaseCombatWeapon->UsesClipsForAmmo1() ) + { + fClip1Percentage = 100.0f * ( static_cast( pBaseCombatWeapon->Clip1() ) / static_cast( pBaseCombatWeapon->GetMaxClip1() ) ); + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f ", fClip1Percentage ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + } + + return ( bNot ) ? ( fClip1Percentage >= fParam ) : ( fClip1Percentage < fParam ); + } + + case LESSON_ACTION_WEAPON_AMMO_LOW: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is full + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ ); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoLow = ( iPlayerAmmo < ( iMaxAmmo / 3 ) ); + + if ( bNot ) + { + bAmmoLow = !bAmmoLow; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30 " ) : ( ")->AmmoPercentage() < 30 " ) ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bAmmoLow ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return bAmmoLow; + } + + case LESSON_ACTION_WEAPON_AMMO_FULL: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is full + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ ); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoFull = ( iPlayerAmmo >= iMaxAmmo ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoFull ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return ( bNot ) ? ( !bAmmoFull ) : ( bAmmoFull ); + } + + case LESSON_ACTION_WEAPON_AMMO_EMPTY: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is empty + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoEmpty = ( iPlayerAmmo <= 0 ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoEmpty ) ? ( "true" ) : ( "false" ) ); + ConColorMsg(CBaseLesson::m_rgbaVerbosePlain, " )\n" ); + } + + return ( bNot ) ? ( !bAmmoEmpty ) : ( bAmmoEmpty ); + } + + /*case LESSON_ACTION_WEAPON_CAN_USE: + { + C_BaseCombatWeapon *pBaseCombatWeapon = dynamic_cast( pParam ); + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam BaseCombatWeapon returned NULL!\n" ); + } + + return false; + } + + bool bCanEquip = pBasePlayer->Weapon_CanUse( pBaseCombatWeapon ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s]) " ) : ( "\t[%s]->Weapon_CanUse([%s]) " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bCanEquip ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return ( bNot ) ? ( !bCanEquip ) : ( bCanEquip ); + }*/ + + case LESSON_ACTION_USE_TARGET_IS: + { + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() ); + } + + return ( bNot ) ? ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) != pParam ) : ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) == pParam ); + } + + case LESSON_ACTION_GET_USE_TARGET: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName ); + } + + return true; + } + + /*case LESSON_ACTION_GET_POTENTIAL_USE_TARGET: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetPotentialUseEntity() ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName ); + } + + return true; + }*/ + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam ) +{ + switch ( iAction ) + { + case LESSON_ACTION_REFERENCE_OPEN: + { + const CBaseLesson *pLesson = GetGameInstructor().GetLesson( pchParamName->String() ); + if ( !pLesson ) + { + DevWarning( "Invalid lesson specified: \"%s\".", pchParamName->String() ); + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( Color( 64, 128, 255, 255 ), ( bNot ) ? ( "\t!( [\"%s\"]->IsInstanceActive() " ) : ( "\t( [\"%s\"]->IsInstanceActive() " ), pchParamName->String() ); + ConColorMsg( Color( 255, 255, 255, 255 ), "\"%s\"", (pLesson->IsInstanceActive() ? "true" : "false") ); + ConColorMsg( Color( 64, 128, 255, 255 ), " )\n" ); + } + + return ( bNot ) ? ( !pLesson->IsInstanceActive() ) : ( pLesson->IsInstanceActive() ); + } + + case LESSON_ACTION_SET: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy([%s], [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + *pchVar = pchParam; + return true; + + case LESSON_ACTION_ADD: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcat([%s], [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + char szTemp[ 256 ]; + Q_strncpy( szTemp, pchVar->String(), sizeof( szTemp ) ); + Q_strncat( szTemp, pchParam, sizeof( szTemp ) ); + + *pchVar = szTemp; + return true; + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + } + + return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) != 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) == 0 ); + + case LESSON_ACTION_HAS_PREFIX: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tStringHasPrefix([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == false\n" ) : ( ") == true\n" ) ); + } + + return ( bNot ) ? ( !StringHasPrefix( pchVar->String(), pchParam ) ) : ( StringHasPrefix( pchVar->String(), pchParam ) ); + + case LESSON_ACTION_LESS_THAN: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") >= 0\n" ) : ( ") < 0\n" ) ); + } + + return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) >= 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) < 0 ); + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +LessonEvent_t * CScriptedIconLesson::AddOpenEvent() +{ + int iNewLessonEvent = m_OpenEvents.AddToTail(); + return &(m_OpenEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddCloseEvent() +{ + int iNewLessonEvent = m_CloseEvents.AddToTail(); + return &(m_CloseEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddSuccessEvent() +{ + int iNewLessonEvent = m_SuccessEvents.AddToTail(); + return &(m_SuccessEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddOnOpenEvent() +{ + int iNewLessonEvent = m_OnOpenEvents.AddToTail(); + return &(m_OnOpenEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddUpdateEvent() +{ + int iNewLessonEvent = m_UpdateEvents.AddToTail(); + return &(m_UpdateEvents[ iNewLessonEvent ]); +} + +// Static method to init the keyvalues symbols used for comparisons +void CScriptedIconLesson::PreReadLessonsFromFile() +{ + static bool bFirstTime = true; + if ( !bFirstTime ) + return; + bFirstTime = false; + + // Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_SYMBOL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // And build the map of variable name to enum + // Run string to int macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_BOOL LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_STRING LESSON_SCRIPT_STRING_ADD_TO_MAP + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // Set up mapping of field types + g_TypeToParamTypeMap.Insert( "float", FIELD_FLOAT ); + g_TypeToParamTypeMap.Insert( "string", FIELD_STRING ); + g_TypeToParamTypeMap.Insert( "int", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "integer", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "short", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "long", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "bool", FIELD_BOOLEAN ); + g_TypeToParamTypeMap.Insert( "player", FIELD_CUSTOM ); + g_TypeToParamTypeMap.Insert( "entity", FIELD_EHANDLE ); + g_TypeToParamTypeMap.Insert( "convar", FIELD_EMBEDDED ); + g_TypeToParamTypeMap.Insert( "void", FIELD_VOID ); + + // Set up the lesson action map + + CScriptedIconLesson::LessonActionMap.Insert( "scope in", LESSON_ACTION_SCOPE_IN ); + CScriptedIconLesson::LessonActionMap.Insert( "scope out", LESSON_ACTION_SCOPE_OUT ); + CScriptedIconLesson::LessonActionMap.Insert( "close", LESSON_ACTION_CLOSE ); + CScriptedIconLesson::LessonActionMap.Insert( "success", LESSON_ACTION_SUCCESS ); + CScriptedIconLesson::LessonActionMap.Insert( "lock", LESSON_ACTION_LOCK ); + CScriptedIconLesson::LessonActionMap.Insert( "present complete", LESSON_ACTION_PRESENT_COMPLETE ); + CScriptedIconLesson::LessonActionMap.Insert( "present start", LESSON_ACTION_PRESENT_START ); + CScriptedIconLesson::LessonActionMap.Insert( "present end", LESSON_ACTION_PRESENT_END ); + + CScriptedIconLesson::LessonActionMap.Insert( "reference open", LESSON_ACTION_REFERENCE_OPEN ); + + CScriptedIconLesson::LessonActionMap.Insert( "set", LESSON_ACTION_SET ); + CScriptedIconLesson::LessonActionMap.Insert( "add", LESSON_ACTION_ADD ); + CScriptedIconLesson::LessonActionMap.Insert( "subtract", LESSON_ACTION_SUBTRACT ); + CScriptedIconLesson::LessonActionMap.Insert( "multiply", LESSON_ACTION_MULTIPLY ); + CScriptedIconLesson::LessonActionMap.Insert( "is", LESSON_ACTION_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "less than", LESSON_ACTION_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "has prefix", LESSON_ACTION_HAS_PREFIX ); + CScriptedIconLesson::LessonActionMap.Insert( "has bit", LESSON_ACTION_HAS_BIT ); + CScriptedIconLesson::LessonActionMap.Insert( "bit count is", LESSON_ACTION_BIT_COUNT_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "bit count less than", LESSON_ACTION_BIT_COUNT_LESS_THAN ); + + CScriptedIconLesson::LessonActionMap.Insert( "get distance", LESSON_ACTION_GET_DISTANCE ); + CScriptedIconLesson::LessonActionMap.Insert( "get angular distance", LESSON_ACTION_GET_ANGULAR_DISTANCE ); + CScriptedIconLesson::LessonActionMap.Insert( "get player display name", LESSON_ACTION_GET_PLAYER_DISPLAY_NAME ); + CScriptedIconLesson::LessonActionMap.Insert( "classname is", LESSON_ACTION_CLASSNAME_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "modelname is", LESSON_ACTION_MODELNAME_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "team is", LESSON_ACTION_TEAM_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "health less than", LESSON_ACTION_HEALTH_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "health percentage less than", LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "get active weapon", LESSON_ACTION_GET_ACTIVE_WEAPON ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon is", LESSON_ACTION_WEAPON_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon has", LESSON_ACTION_WEAPON_HAS ); + CScriptedIconLesson::LessonActionMap.Insert( "get active weapon slot", LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "get weapon slot", LESSON_ACTION_GET_WEAPON_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "get weapon in slot", LESSON_ACTION_GET_WEAPON_IN_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "clip percentage less than", LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo low", LESSON_ACTION_WEAPON_AMMO_LOW ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo full", LESSON_ACTION_WEAPON_AMMO_FULL ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo empty", LESSON_ACTION_WEAPON_AMMO_EMPTY ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon can use", LESSON_ACTION_WEAPON_CAN_USE ); + CScriptedIconLesson::LessonActionMap.Insert( "use target is", LESSON_ACTION_USE_TARGET_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "get use target", LESSON_ACTION_GET_USE_TARGET ); + CScriptedIconLesson::LessonActionMap.Insert( "get potential use target", LESSON_ACTION_GET_POTENTIAL_USE_TARGET ); + + // Add mod actions to the map + //Mod_PreReadLessonsFromFile(); +} diff --git a/mp/src/game/client/c_baselesson.h b/mp/src/game/client/c_baselesson.h new file mode 100644 index 00000000..2cad41ca --- /dev/null +++ b/mp/src/game/client/c_baselesson.h @@ -0,0 +1,460 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler for instruction players how to play +// +//=============================================================================// + +#ifndef _C_BASELESSON_H_ +#define _C_BASELESSON_H_ + + +#include "GameEventListener.h" +#include "hud_locator_target.h" + + +#define DECLARE_LESSON( _lessonClassName, _baseLessonClassName ) \ + typedef _baseLessonClassName BaseClass;\ + typedef _lessonClassName ThisClass;\ + _lessonClassName( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity )\ + : _baseLessonClassName( pchName, bIsDefaultHolder, bIsOpenOpportunity )\ + {\ + Init();\ + } + + +enum LessonInstanceType +{ + LESSON_INSTANCE_MULTIPLE, + LESSON_INSTANCE_SINGLE_OPEN, + LESSON_INSTANCE_FIXED_REPLACE, + LESSON_INSTANCE_SINGLE_ACTIVE, + + LESSON_INSTANCE_TYPE_TOTAL +}; + + +// This is used to solve a problem where bots can take the place of a player, where on or the other don't have valid entities on the client at the same time +#define MAX_DELAYED_PLAYER_SWAPS 8 + +struct delayed_player_swap_t +{ + CHandle *phHandleToChange; + int iNewUserID; + + delayed_player_swap_t( void ) + { + phHandleToChange = NULL; + iNewUserID = -1; + } +}; + + +abstract_class CBaseLesson : public CGameEventListener +{ +public: + CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity ); + virtual ~CBaseLesson( void ); + + void AddPrerequisite( const char *pchLessonName ); + + const CGameInstructorSymbol& GetNameSymbol( void ) const { return m_stringName; } + const char * GetName( void ) const { return m_stringName.String(); } + int GetPriority( void ) const { return m_iPriority; } + const char * GetCloseReason( void ) const { return m_stringCloseReason.String(); } + void SetCloseReason( const char *pchReason ) { m_stringCloseReason = pchReason; } + + CBaseLesson* GetRoot( void ) const { return m_pRoot; } + void SetRoot( CBaseLesson *pRoot ); + const CUtlVector < CBaseLesson * >* GetChildren( void ) const { return &m_OpenOpportunities; } + + float GetInitTime( void ) { return m_fInitTime; } + void SetStartTime( void ) { m_fStartTime = gpGlobals->curtime; } + void ResetStartTime( void ) { m_fStartTime = 0.0f; m_bHasPlayedSound = false; } + + bool ShouldShowSpew( void ); + bool NoPriority( void ) const; + bool IsDefaultHolder( void ) const { return m_bIsDefaultHolder; } + bool IsOpenOpportunity( void ) const { return m_bIsOpenOpportunity; } + bool IsLocked( void ) const; + bool CanOpenWhenDead( void ) const { return m_bCanOpenWhenDead; } + bool IsInstructing( void ) const { return ( m_fStartTime > 0.0f ); } + bool IsLearned( void ) const; + bool PrerequisitesHaveBeenMet( void ) const; + bool IsTimedOut( void ); + + int InstanceType( void ) const { return m_iInstanceType; } + const CGameInstructorSymbol& GetReplaceKeySymbol( void ) const { return m_stringReplaceKey; } + const char* GetReplaceKey( void ) const { return m_stringReplaceKey.String(); } + int GetFixedInstancesMax( void ) const { return m_iFixedInstancesMax; } + bool ShouldReplaceOnlyWhenStopped( void ) const { return m_bReplaceOnlyWhenStopped; } + void SetInstanceActive( bool bInstanceActive ) { m_bInstanceActive = bInstanceActive; } + bool IsInstanceActive( void ) const { return m_bInstanceActive; } + + void ResetDisplaysAndSuccesses( void ); + bool IncDisplayCount( void ); + bool IncSuccessCount( void ); + void SetDisplayCount( int iDisplayCount ) { m_iDisplayCount = iDisplayCount; } + void SetSuccessCount( int iSuccessCount ) { m_iSuccessCount = iSuccessCount; } + int GetDisplayCount( void ) const { return m_iDisplayCount; } + int GetSuccessCount( void ) const { return m_iSuccessCount; } + int GetDisplayLimit( void ) const { return m_iDisplayLimit; } + int GetSuccessLimit( void ) const { return m_iSuccessLimit; } + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void InitPrerequisites( void ) {}; + virtual void Start( void ) = 0; + virtual void Stop( void ) = 0; + virtual void OnOpen( void ) {}; + virtual void Update( void ) {}; + virtual void UpdateInactive( void ) {}; + + virtual bool ShouldDisplay( void ) const { return true; } + virtual bool IsVisible( void ) const { return true; } + virtual bool WasDisplayed( void ) const { return m_bWasDisplayed ? true : false; } + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ) {} + virtual void TakePlaceOf( CBaseLesson *pLesson ); + + const char *GetGroup() { return m_szLessonGroup.String(); } + void SetEnabled( bool bEnabled ) { m_bDisabled = !bEnabled; } + +protected: + void MarkSucceeded( void ); + void CloseOpportunity( const char *pchReason ); + bool DoDelayedPlayerSwaps( void ) const; + +private: + + CBaseLesson *m_pRoot; + CUtlVector < CBaseLesson * > m_OpenOpportunities; + CUtlVector < const CBaseLesson * > m_Prerequisites; + + CGameInstructorSymbol m_stringCloseReason; + CGameInstructorSymbol m_stringName; + + bool m_bInstanceActive : 1; + bool m_bSuccessCounted : 1; + bool m_bIsDefaultHolder : 1; + bool m_bIsOpenOpportunity : 1; + +protected: + LessonInstanceType m_iInstanceType; + + int m_iPriority; + CGameInstructorSymbol m_stringReplaceKey; + int m_iFixedInstancesMax; + bool m_bReplaceOnlyWhenStopped; + int m_iTeam; + bool m_bOnlyKeyboard; + bool m_bOnlyGamepad; + + int m_iDisplayLimit; + int m_iDisplayCount; + bool m_bWasDisplayed; + int m_iSuccessLimit; + int m_iSuccessCount; + + float m_fLockDuration; + float m_fTimeout; + float m_fInitTime; + float m_fStartTime; + float m_fLockTime; + float m_fUpdateInterval; + bool m_bHasPlayedSound; + + CGameInstructorSymbol m_szStartSound; + CGameInstructorSymbol m_szLessonGroup; + + bool m_bCanOpenWhenDead; + bool m_bBumpWithTimeoutWhenLearned; + bool m_bCanTimeoutWhileInactive; + bool m_bDisabled; + + // Right now we can only queue up 4 swaps... + // this number can be increased if more entity handle scripted variables are added + mutable delayed_player_swap_t m_pDelayedPlayerSwap[ MAX_DELAYED_PLAYER_SWAPS ]; + mutable int m_iNumDelayedPlayerSwaps; + +public: + + // Colors for console spew in verbose mode + static Color m_rgbaVerboseHeader; + static Color m_rgbaVerbosePlain; + static Color m_rgbaVerboseName; + static Color m_rgbaVerboseOpen; + static Color m_rgbaVerboseClose; + static Color m_rgbaVerboseSuccess; + static Color m_rgbaVerboseUpdate; +}; + + +class CTextLesson : public CBaseLesson +{ +public: + DECLARE_LESSON( CTextLesson, CBaseLesson ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + +protected: + + CGameInstructorSymbol m_szDisplayText; + CGameInstructorSymbol m_szDisplayParamText; + CGameInstructorSymbol m_szBinding; + CGameInstructorSymbol m_szGamepadBinding; +}; + + +class CIconLesson : public CTextLesson +{ +public: + DECLARE_LESSON( CIconLesson, CTextLesson ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + virtual void Update( void ); + virtual void UpdateInactive( void ); + + virtual bool ShouldDisplay( void ) const; + virtual bool IsVisible( void ) const; + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ); + virtual void TakePlaceOf( CBaseLesson *pLesson ); + + void SetLocatorBinding( CLocatorTarget * pLocatorTarget ); + + const char *GetCaptionColorString() { return m_szCaptionColor.String(); } + + bool IsPresentComplete( void ); + void PresentStart( void ); + void PresentEnd( void ); + +private: + virtual void UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget ); + +#ifdef MAPBASE + Vector GetIconTargetPosition( C_BaseEntity *pIconTarget ); +#endif + +protected: + CHandle m_hIconTarget; + CGameInstructorSymbol m_szVguiTargetName; + CGameInstructorSymbol m_szVguiTargetLookup; + int m_nVguiTargetEdge; + float m_flUpOffset; + float m_flRelativeUpOffset; + float m_fFixedPositionX; + float m_fFixedPositionY; + + int m_hLocatorTarget; + int m_iFlags; + + float m_fRange; + float m_fCurrentDistance; + float m_fOnScreenStartTime; + float m_fUpdateDistanceTime; + + CGameInstructorSymbol m_szOnscreenIcon; + CGameInstructorSymbol m_szOffscreenIcon; + CGameInstructorSymbol m_szCaptionColor; + + bool m_bFixedPosition; + bool m_bNoIconTarget; + bool m_bAllowNodrawTarget; + bool m_bVisible; + bool m_bShowWhenOccluded; + bool m_bNoOffscreen; + bool m_bForceCaption; + +#ifdef MAPBASE + int m_iIconTargetPos; + + enum + { + ICON_TARGET_EYE_POSITION, + ICON_TARGET_ORIGIN, + ICON_TARGET_CENTER, + }; + + CGameInstructorSymbol m_szHudHint; +#endif +}; + +enum LessonAction +{ + LESSON_ACTION_NONE, + + LESSON_ACTION_SCOPE_IN, + LESSON_ACTION_SCOPE_OUT, + LESSON_ACTION_CLOSE, + LESSON_ACTION_SUCCESS, + LESSON_ACTION_LOCK, + LESSON_ACTION_PRESENT_COMPLETE, + LESSON_ACTION_PRESENT_START, + LESSON_ACTION_PRESENT_END, + + LESSON_ACTION_REFERENCE_OPEN, + + LESSON_ACTION_SET, + LESSON_ACTION_ADD, + LESSON_ACTION_SUBTRACT, + LESSON_ACTION_MULTIPLY, + LESSON_ACTION_IS, + LESSON_ACTION_LESS_THAN, + LESSON_ACTION_HAS_PREFIX, + LESSON_ACTION_HAS_BIT, + LESSON_ACTION_BIT_COUNT_IS, + LESSON_ACTION_BIT_COUNT_LESS_THAN, + + LESSON_ACTION_GET_DISTANCE, + LESSON_ACTION_GET_ANGULAR_DISTANCE, + LESSON_ACTION_GET_PLAYER_DISPLAY_NAME, + LESSON_ACTION_CLASSNAME_IS, + LESSON_ACTION_MODELNAME_IS, + LESSON_ACTION_TEAM_IS, + LESSON_ACTION_HEALTH_LESS_THAN, + LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN, + LESSON_ACTION_GET_ACTIVE_WEAPON, + LESSON_ACTION_WEAPON_IS, + LESSON_ACTION_WEAPON_HAS, + LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT, + LESSON_ACTION_GET_WEAPON_SLOT, + LESSON_ACTION_GET_WEAPON_IN_SLOT, + LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN, + LESSON_ACTION_WEAPON_AMMO_LOW, + LESSON_ACTION_WEAPON_AMMO_FULL, + LESSON_ACTION_WEAPON_AMMO_EMPTY, + LESSON_ACTION_WEAPON_CAN_USE, + LESSON_ACTION_USE_TARGET_IS, + LESSON_ACTION_GET_USE_TARGET, + LESSON_ACTION_GET_POTENTIAL_USE_TARGET, + + // Enum continued in Mod_LessonAction + LESSON_ACTION_MOD_START, +}; + +struct LessonElement_t +{ + int iVariable; + int iParamVarIndex; + int iAction; + _fieldtypes paramType; + CGameInstructorSymbol szParam; + bool bNot : 1; + bool bOptionalParam : 1; + + LessonElement_t( int p_iVariable, int p_iAction, bool p_bNot, bool p_bOptionalParam, const char *pchParam, int p_iParamVarIndex, _fieldtypes p_paramType ) + { + iVariable = p_iVariable; + iAction = p_iAction; + bNot = p_bNot; + bOptionalParam = p_bOptionalParam; + szParam = pchParam; + iParamVarIndex = p_iParamVarIndex; + paramType = p_paramType; + } + + LessonElement_t( const LessonElement_t &p_LessonElement ) + { + iVariable = p_LessonElement.iVariable; + iAction = p_LessonElement.iAction; + bNot = p_LessonElement.bNot; + bOptionalParam = p_LessonElement.bOptionalParam; + szParam = p_LessonElement.szParam; + iParamVarIndex = p_LessonElement.iParamVarIndex; + paramType = p_LessonElement.paramType; + } +}; + +struct LessonEvent_t +{ + CUtlVector< LessonElement_t > elements; + CGameInstructorSymbol szEventName; +}; + +class CScriptedIconLesson : public CIconLesson +{ +public: + DECLARE_LESSON( CScriptedIconLesson, CIconLesson ) + + virtual ~CScriptedIconLesson( void ); + + static void PreReadLessonsFromFile( void ); + static void Mod_PreReadLessonsFromFile( void ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void InitPrerequisites( void ); + virtual void OnOpen( void ); + virtual void Update( void ); + + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ); + + virtual void FireGameEvent( IGameEvent *event ); + virtual void ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + virtual void ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + virtual void ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + + CUtlVector< LessonEvent_t >& GetOpenEvents( void ) { return m_OpenEvents; } + CUtlVector< LessonEvent_t >& GetCloseEvents( void ) { return m_CloseEvents; } + CUtlVector< LessonEvent_t >& GetSuccessEvents( void ) { return m_SuccessEvents; } + CUtlVector< LessonEvent_t >& GetOnOpenEvents( void ) { return m_OnOpenEvents; } + CUtlVector< LessonEvent_t >& GetUpdateEvents( void ) { return m_UpdateEvents; } + + bool ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ); + +private: + void InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ); + void InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ); + + void InitFromKeys( KeyValues *pKey ); + + bool ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope ); + + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam ); + + // Implemented per mod so they can have custom actions + bool Mod_ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam, bool &bModHandled ); + + LessonEvent_t * AddOpenEvent( void ); + LessonEvent_t * AddCloseEvent( void ); + LessonEvent_t * AddSuccessEvent( void ); + LessonEvent_t * AddOnOpenEvent( void ); + LessonEvent_t * AddUpdateEvent( void ); + +private: + static CUtlDict< int, int > CScriptedIconLesson::LessonActionMap; + + EHANDLE m_hLocalPlayer; + float m_fOutput; + CHandle m_hEntity1; + CHandle m_hEntity2; + CGameInstructorSymbol m_szString1; + CGameInstructorSymbol m_szString2; + int m_iInteger1; + int m_iInteger2; + float m_fFloat1; + float m_fFloat2; + + CUtlVector< CGameInstructorSymbol > m_PrerequisiteNames; + CUtlVector< LessonEvent_t > m_OpenEvents; + CUtlVector< LessonEvent_t > m_CloseEvents; + CUtlVector< LessonEvent_t > m_SuccessEvents; + CUtlVector< LessonEvent_t > m_OnOpenEvents; + CUtlVector< LessonEvent_t > m_UpdateEvents; + + float m_fUpdateEventTime; + CScriptedIconLesson *m_pDefaultHolder; + + int m_iScopeDepth; + + // Need this to get offsets to scripted variables + friend class LessonVariableInfo; + friend int LessonActionFromString( const char *pchName ); +}; + + +#endif // _C_BASELESSON_H_ diff --git a/mp/src/game/client/c_baseplayer.cpp b/mp/src/game/client/c_baseplayer.cpp index a6c682d0..284da6cc 100644 --- a/mp/src/game/client/c_baseplayer.cpp +++ b/mp/src/game/client/c_baseplayer.cpp @@ -54,6 +54,10 @@ #include "econ_wearable.h" #endif +#ifdef MAPBASE +#include "viewrender.h" +#endif + // NVNT haptics system interface #include "haptics/ihaptics.h" @@ -132,6 +136,16 @@ void RecvProxy_LocalVelocityZ( const CRecvProxyData *pData, void *pStruct, void void RecvProxy_ObserverTarget( const CRecvProxyData *pData, void *pStruct, void *pOut ); void RecvProxy_ObserverMode ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +#ifdef MAPBASE +// Needs to shift bits back +void RecvProxy_ShiftPlayerSpawnflags( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_BasePlayer *pPlayer = (C_BasePlayer *)pStruct; + + pPlayer->m_spawnflags = (pData->m_Value.m_Int) << 16; +} +#endif + // -------------------------------------------------------------------------------- // // RecvTable for CPlayerState. // -------------------------------------------------------------------------------- // @@ -179,6 +193,11 @@ BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local ) // 3d skybox data RecvPropInt(RECVINFO(m_skybox3d.scale)), RecvPropVector(RECVINFO(m_skybox3d.origin)), +#ifdef MAPBASE + RecvPropVector(RECVINFO(m_skybox3d.angles)), + RecvPropEHandle(RECVINFO(m_skybox3d.skycamera)), + RecvPropInt( RECVINFO( m_skybox3d.skycolor ), 0, RecvProxy_IntToColor32 ), +#endif RecvPropInt(RECVINFO(m_skybox3d.area)), // 3d skybox fog data @@ -190,6 +209,9 @@ BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local ) RecvPropFloat( RECVINFO( m_skybox3d.fog.start ) ), RecvPropFloat( RECVINFO( m_skybox3d.fog.end ) ), RecvPropFloat( RECVINFO( m_skybox3d.fog.maxdensity ) ), +#ifdef MAPBASE + RecvPropFloat( RECVINFO( m_skybox3d.fog.farz ) ), +#endif // fog data RecvPropEHandle( RECVINFO( m_PlayerFog.m_hCtrl ) ), @@ -206,6 +228,14 @@ BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local ) RecvPropInt( RECVINFO( m_audio.soundscapeIndex ) ), RecvPropInt( RECVINFO( m_audio.localBits ) ), RecvPropEHandle( RECVINFO( m_audio.ent ) ), + + //Tony; tonemap stuff! -- TODO! Optimize this with bit sizes from env_tonemap_controller. + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flTonemapScale ) ), + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flTonemapRate ) ), + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flBloomScale ) ), + + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flAutoExposureMin ) ), + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flAutoExposureMax ) ), END_RECV_TABLE() // -------------------------------------------------------------------------------- // @@ -248,6 +278,14 @@ END_RECV_TABLE() RecvPropInt ( RECVINFO( m_nWaterLevel ) ), RecvPropFloat ( RECVINFO( m_flLaggedMovementValue )), +#ifdef MAPBASE + // Transmitted from the server for internal player spawnflags. + // See baseplayer_shared.h for more details. + RecvPropInt ( RECVINFO( m_spawnflags ), 0, RecvProxy_ShiftPlayerSpawnflags ), + + RecvPropBool ( RECVINFO( m_bDrawPlayerModelExternally ) ), +#endif + END_RECV_TABLE() @@ -296,6 +334,8 @@ END_RECV_TABLE() RecvPropString( RECVINFO(m_szLastPlaceName) ), + RecvPropEHandle(RECVINFO(m_hPostProcessCtrl)), // Send to everybody - for spectating + #if defined USES_ECON_ITEMS RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ), #endif @@ -399,7 +439,10 @@ BEGIN_PREDICTION_DATA( C_BasePlayer ) END_PREDICTION_DATA() +// link this in each derived player class, like the server!! +#if 0 LINK_ENTITY_TO_CLASS( player, C_BasePlayer ); +#endif // -------------------------------------------------------------------------------- // // Functions. @@ -455,6 +498,13 @@ C_BasePlayer::~C_BasePlayer() s_pLocalPlayer = NULL; } +#ifdef MAPBASE_VSCRIPT + if ( IsLocalPlayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } +#endif + delete m_pFlashlight; } @@ -963,6 +1013,16 @@ void C_BasePlayer::OnRestore() input->ClearInputButton( IN_ATTACK | IN_ATTACK2 ); // GetButtonBits() has to be called for the above to take effect input->GetButtonBits( 0 ); + +#ifdef MAPBASE_VSCRIPT + // HACK: (03/25/09) Then the player goes across a transition it doesn't spawn and register + // it's instance. We're hacking around this for now, but this will go away when we get around to + // having entities cross transitions and keep their script state. + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } +#endif } // For ammo history icons to current value so they don't flash on level transtions @@ -1072,6 +1132,16 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // If we're in vgui mode *and* we're holding down mouse buttons, // stay in vgui mode even if we're outside the screen bounds +#ifdef VGUI_SCREEN_FIX + if (m_pCurrentVguiScreen.Get() && (pCmd->buttons & (IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT))) + { + SetVGuiScreenButtonState( m_pCurrentVguiScreen.Get(), pCmd->buttons ); + + // Kill all attack inputs if we're in vgui screen mode + pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT); + return; + } +#else if (m_pCurrentVguiScreen.Get() && (pCmd->buttons & (IN_ATTACK | IN_ATTACK2)) ) { SetVGuiScreenButtonState( m_pCurrentVguiScreen.Get(), pCmd->buttons ); @@ -1080,6 +1150,7 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); return; } +#endif // We're not in vgui input mode if we're moving, or have hit a key // that will make us move... @@ -1201,7 +1272,12 @@ bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd ) m_vecOldViewAngles = pCmd->viewangles; // Check to see if we're in vgui input mode... +#ifdef VGUI_SCREEN_FIX + if(pCmd->buttons & IN_VALIDVGUIINPUT) + DetermineVguiInputMode( pCmd ); +#else DetermineVguiInputMode( pCmd ); +#endif return true; } @@ -1403,11 +1479,34 @@ bool C_BasePlayer::ShouldInterpolate() bool C_BasePlayer::ShouldDraw() { +#ifdef MAPBASE + // We have to "always draw" a player with m_bDrawPlayerModelExternally in order to show up in whatever rendering list all of the views use, + // but we can't put this in ShouldDrawThisPlayer() because we would have no way of knowing if it stomps the other checks that draw the player model anyway. + // As a result, we have to put it here in the central ShouldDraw() function. DrawModel() makes sure we only draw in non-main views and nothing's drawing the model anyway. + return (ShouldDrawThisPlayer() || m_bDrawPlayerModelExternally) && BaseClass::ShouldDraw(); +#else return ShouldDrawThisPlayer() && BaseClass::ShouldDraw(); +#endif } int C_BasePlayer::DrawModel( int flags ) { +#ifdef MAPBASE + if (m_bDrawPlayerModelExternally) + { + // Draw the player in any view except the main or "intro" view, both of which are default first-person views. + view_id_t viewID = CurrentViewID(); + if (viewID == VIEW_MAIN || viewID == VIEW_INTRO_CAMERA) + { + // Make sure the player model wouldn't draw anyway... + if (!ShouldDrawThisPlayer()) + return 0; + } + + return BaseClass::DrawModel( flags ); + } +#endif + #ifndef PORTAL // In Portal this check is already performed as part of // C_Portal_Player::DrawModel() @@ -1416,6 +1515,7 @@ int C_BasePlayer::DrawModel( int flags ) return 0; } #endif + return BaseClass::DrawModel( flags ); } @@ -1540,7 +1640,14 @@ void C_BasePlayer::CalcChaseCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& } } - if ( target && !target->IsPlayer() && target->IsNextBot() ) + // SDK TODO + if ( target && target->IsBaseTrain() ) + { + // if this is a train, we want to be back a little further so we can see more of it + flMaxDistance *= 2.5f; + m_flObserverChaseDistance = flMaxDistance; + } + else if ( target && !target->IsPlayer() && target->IsNextBot() ) { // if this is a boss, we want to be back a little further so we can see more of it flMaxDistance *= 2.5f; @@ -1873,6 +1980,12 @@ void C_BasePlayer::ThirdPersonSwitch( bool bThirdperson ) } } } + else + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ( pWeapon ) + pWeapon->ThirdPersonSwitch( bThirdperson ); + } } @@ -2821,6 +2934,14 @@ void C_BasePlayer::UpdateFogBlend( void ) } } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +C_PostProcessController* C_BasePlayer::GetActivePostProcessController() const +{ + return m_hPostProcessCtrl.Get(); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/c_baseplayer.h b/mp/src/game/client/c_baseplayer.h index 5e4372bc..528c8408 100644 --- a/mp/src/game/client/c_baseplayer.h +++ b/mp/src/game/client/c_baseplayer.h @@ -23,6 +23,7 @@ #include "hintsystem.h" #include "SoundEmitterSystem/isoundemittersystembase.h" #include "c_env_fog_controller.h" +#include "c_postprocesscontroller.h" #include "igameevents.h" #include "GameEventListener.h" @@ -37,6 +38,7 @@ class C_BaseViewModel; class C_FuncLadder; class CFlashlightEffect; class C_EconWearable; +class C_PostProcessController; extern int g_nKillCamMode; extern int g_nKillCamTarget1; @@ -183,7 +185,7 @@ public: // Flashlight void Flashlight( void ); - void UpdateFlashlight( void ); + virtual void UpdateFlashlight( void ); // Weapon selection code virtual bool IsAllowedToSwitchWeapons( void ) { return !IsObserver(); } @@ -379,6 +381,8 @@ public: void UpdateFogController( void ); void UpdateFogBlend( void ); + C_PostProcessController* GetActivePostProcessController() const; + float GetFOVTime( void ){ return m_flFOVTime; } virtual void OnAchievementAchieved( int iAchievement ) {} @@ -443,20 +447,33 @@ public: float m_flConstraintWidth; float m_flConstraintSpeedFactor; +#ifdef MAPBASE + // Transmitted from the server for internal player spawnflags. + // See baseplayer_shared.h for more details. + int m_spawnflags; + + inline bool HasSpawnFlags( int flags ) { return (m_spawnflags & flags) != 0; } + inline void RemoveSpawnFlags( int flags ) { m_spawnflags &= ~flags; } + inline void AddSpawnFlags( int flags ) { m_spawnflags |= flags; } + + // Allows the player's model to draw on non-main views, like monitors or mirrors. + bool m_bDrawPlayerModelExternally; +#endif + protected: - void CalcPlayerView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); - void CalcVehicleView(IClientVehicle *pVehicle, Vector& eyeOrigin, QAngle& eyeAngles, - float& zNear, float& zFar, float& fov ); + //Tony; made all of these virtual so mods can override. + virtual void CalcPlayerView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); + virtual void CalcVehicleView(IClientVehicle *pVehicle, Vector& eyeOrigin, QAngle& eyeAngles, float& zNear, float& zFar, float& fov ); virtual void CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); virtual Vector GetChaseCamViewOffset( CBaseEntity *target ); - void CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); + virtual void CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); virtual void CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); virtual float GetDeathCamInterpolationTime(); virtual void CalcDeathCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); - void CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov); + virtual void CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov); virtual void CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); // Check to see if we're in vgui input mode... @@ -629,6 +646,8 @@ private: // One for left and one for right side of step StepSoundCache_t m_StepSoundCache[ 2 ]; + CNetworkHandle(C_PostProcessController, m_hPostProcessCtrl); // active postprocessing controller + public: const char *GetLastKnownPlaceName( void ) const { return m_szLastPlaceName; } // return the last nav place name the player occupied diff --git a/mp/src/game/client/c_effects.cpp b/mp/src/game/client/c_effects.cpp index 14a90a4c..afd5bcf9 100644 --- a/mp/src/game/client/c_effects.cpp +++ b/mp/src/game/client/c_effects.cpp @@ -6,6 +6,7 @@ // //=============================================================================// #include "cbase.h" +#include "c_effects.h" #include "c_tracer.h" #include "view.h" #include "initializer.h" @@ -22,6 +23,7 @@ #include "collisionutils.h" #include "tier0/vprof.h" #include "viewrender.h" +#include "raytrace.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -35,6 +37,12 @@ float g_flSplashLifetime = 0.5f; float g_flSplashAlpha = 0.3f; ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // N% chance of a rain particle making a splash. +ConVar r_RainParticleDensity( "r_RainParticleDensity", "1", FCVAR_NONE, "Density of Particle Rain 0-1" ); + +#ifdef MAPBASE +ConVar r_RainParticleClampOffset( "r_RainParticleClampOffset", "112", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems" ); +ConVar r_RainParticleClampDebug( "r_RainParticleClampDebug", "0", FCVAR_NONE, "Enables debug code for precipitation particle system clamping" ); +#endif float GUST_INTERVAL_MIN = 1; float GUST_INTERVAL_MAX = 2; @@ -60,151 +68,14 @@ CLIENTEFFECT_MATERIAL( "particle/rain" ) CLIENTEFFECT_MATERIAL( "particle/snow" ) CLIENTEFFECT_REGISTER_END() -//----------------------------------------------------------------------------- -// Precipitation particle type -//----------------------------------------------------------------------------- - -class CPrecipitationParticle -{ -public: - Vector m_Pos; - Vector m_Velocity; - float m_SpawnTime; // Note: Tweak with this to change lifetime - float m_Mass; - float m_Ramp; - - float m_flCurLifetime; - float m_flMaxLifetime; -}; - - -class CClient_Precipitation; -static CUtlVector g_Precipitations; - -//=========== -// Snow fall -//=========== -class CSnowFallManager; -static CSnowFallManager *s_pSnowFallMgr = NULL; -bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ); -void SnowFallManagerDestroy( void ); - -class AshDebrisEffect : public CSimpleEmitter -{ -public: - AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} - - static AshDebrisEffect* Create( const char *pDebugName ); - - virtual float UpdateAlpha( const SimpleParticle *pParticle ); - virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta ); - -private: - AshDebrisEffect( const AshDebrisEffect & ); -}; - -//----------------------------------------------------------------------------- -// Precipitation base entity -//----------------------------------------------------------------------------- - -class CClient_Precipitation : public C_BaseEntity -{ -class CPrecipitationEffect; -friend class CClient_Precipitation::CPrecipitationEffect; - -public: - DECLARE_CLASS( CClient_Precipitation, C_BaseEntity ); - DECLARE_CLIENTCLASS(); - - CClient_Precipitation(); - virtual ~CClient_Precipitation(); - - // Inherited from C_BaseEntity - virtual void Precache( ); - - void Render(); - -private: - - // Creates a single particle - CPrecipitationParticle* CreateParticle(); - - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual void ClientThink(); - - void Simulate( float dt ); - - // Renders the particle - void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ); - - void CreateWaterSplashes(); - - // Emits the actual particles - void EmitParticles( float fTimeDelta ); - - // Computes where we're gonna emit - bool ComputeEmissionArea( Vector& origin, Vector2D& size ); - - // Gets the tracer width and speed - float GetWidth() const; - float GetLength() const; - float GetSpeed() const; - - // Gets the remaining lifetime of the particle - float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const; - - // Computes the wind vector - static void ComputeWindVector( ); - - // simulation methods - bool SimulateRain( CPrecipitationParticle* pParticle, float dt ); - bool SimulateSnow( CPrecipitationParticle* pParticle, float dt ); - - void CreateAshParticle( void ); - void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); - - // Information helpful in creating and rendering particles - IMaterial *m_MatHandle; // material used - - float m_Color[4]; // precip color - float m_Lifetime; // Precip lifetime - float m_InitialRamp; // Initial ramp value - float m_Speed; // Precip speed - float m_Width; // Tracer width - float m_Remainder; // particles we should render next time - PrecipitationType_t m_nPrecipType; // Precip type - float m_flHalfScreenWidth; // Precalculated each frame. - - float m_flDensity; - - // Some state used in rendering and simulation - // Used to modify the rain density and wind from the console - static ConVar s_raindensity; - static ConVar s_rainwidth; - static ConVar s_rainlength; - static ConVar s_rainspeed; - - static Vector s_WindVector; // Stores the wind speed vector - - CUtlLinkedList m_Particles; - CUtlVector m_Splashes; - - CSmartPtr m_pAshEmitter; - TimedEvent m_tAshParticleTimer; - TimedEvent m_tAshParticleTraceTimer; - bool m_bActiveAshEmitter; - Vector m_vAshSpawnOrigin; - - int m_iAshCount; - -private: - CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible -}; - +CUtlVector< RayTracingEnvironment* > g_RayTraceEnvironments; // Just receive the normal data table stuff IMPLEMENT_CLIENTCLASS_DT(CClient_Precipitation, DT_Precipitation, CPrecipitation) - RecvPropInt( RECVINFO( m_nPrecipType ) ) + RecvPropInt( RECVINFO( m_nPrecipType ) ), +#ifdef MAPBASE + RecvPropInt( RECVINFO( m_spawnflags ) ), +#endif END_RECV_TABLE() static ConVar r_SnowEnable( "r_SnowEnable", "1", FCVAR_CHEAT, "Snow Enable" ); @@ -396,6 +267,12 @@ inline bool CClient_Precipitation::SimulateSnow( CPrecipitationParticle* pPartic void CClient_Precipitation::Simulate( float dt ) { + if ( IsParticleRainType(m_nPrecipType) ) + { + CreateParticlePrecip(); + return; + } + // NOTE: When client-side prechaching works, we need to remove this Precache(); @@ -472,6 +349,9 @@ inline void CClient_Precipitation::RenderParticle( CPrecipitationParticle* pPart float scale; Vector start, delta; + if ( IsParticleRainType(m_nPrecipType) ) + return; + if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) return; @@ -562,6 +442,9 @@ void CClient_Precipitation::Render() if ( !r_DrawRain.GetInt() ) return; + if ( IsParticleRainType(m_nPrecipType) ) + return; + // Don't render in monitors or in reflections or refractions. if ( CurrentViewID() == VIEW_MONITOR ) return; @@ -632,6 +515,11 @@ CClient_Precipitation::CClient_Precipitation() : m_Remainder(0.0f) m_nPrecipType = PRECIPITATION_TYPE_RAIN; m_MatHandle = INVALID_MATERIAL_HANDLE; m_flHalfScreenWidth = 1; + + m_pParticlePrecipInnerNear = NULL; + m_pParticlePrecipInnerFar = NULL; + m_pParticlePrecipOuter = NULL; + m_bActiveParticlePrecipEmitter = false; g_Precipitations.AddToTail( this ); } @@ -1011,6 +899,363 @@ void CClient_Precipitation::CreateAshParticle( void ) } } +void CClient_Precipitation::PrecacheParticlePrecip( void ) +{ + if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH ) + { + PrecacheParticleSystem( "ash" ); + PrecacheParticleSystem( "ash_outer" ); + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW ) + { + PrecacheParticleSystem( "snow" ); + PrecacheParticleSystem( "snow_outer" ); + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM ) + { + PrecacheParticleSystem( "rain_storm" ); + PrecacheParticleSystem( "rain_storm_screen" ); + PrecacheParticleSystem( "rain_storm_outer" ); + } + else //default to rain + { + PrecacheParticleSystem( "rain" ); + PrecacheParticleSystem( "rain_outer" ); + } +} + +void CClient_Precipitation::CreateParticlePrecip( void ) +{ + if ( !m_bParticlePrecipInitialized ) + { + PrecacheParticlePrecip(); + InitializeParticlePrecip(); + } + + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( pPlayer == NULL ) + return; + + // Make sure the emitter is setup + if ( !m_bActiveParticlePrecipEmitter ) + { + //Update 8 times per second. + m_tParticlePrecipTraceTimer.Init( 8 ); + DestroyInnerParticlePrecip(); + DestroyOuterParticlePrecip(); + m_bActiveParticlePrecipEmitter = true; + } + + UpdateParticlePrecip( pPlayer ); +} + +void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer ) +{ + if ( !pPlayer ) + return; + + Vector vForward; + Vector vRight; + + pPlayer->GetVectors( &vForward, &vRight, NULL ); + vForward.z = 0.0f; + vForward.NormalizeInPlace(); + Vector vForward45Right = vForward + vRight; + Vector vForward45Left = vForward - vRight; + vForward45Right.NormalizeInPlace(); + vForward45Left.NormalizeInPlace(); + fltx4 TMax = ReplicateX4( 320.0f ); + SubFloat( TMax, 3 ) = FLT_MAX; + float curTime = gpGlobals->frametime; + + while ( m_tParticlePrecipTraceTimer.NextEvent( curTime ) ) + { + Vector vPlayerPos = pPlayer->EyePosition(); + Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 180 ); + Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 32 ); + Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 100 ); + +#ifdef MAPBASE + if (m_spawnflags & SF_PRECIP_PARTICLE_CLAMP) + { + Vector mins, maxs; + modelinfo->GetModelBounds( GetModel(), mins, maxs ); + + // Account for precipitation height + maxs.z += 180; + + Vector vecOrigin; //= WorldSpaceCenter(); + VectorLerp( mins, maxs, 0.5f, vecOrigin ); + + maxs -= vecOrigin; + mins -= vecOrigin; + + float flMax = r_RainParticleClampOffset.GetFloat(); + Vector addend( flMax, flMax, 0 ); + mins += addend; + maxs -= addend; + + if (flMax > 0) + { + // Unless this is extruding outwards, make sure the offset isn't inverting the bounds. + // This means precipitation triggers with bounds less than offset*2 will turn into a thin line + // and the involved precipitation will pretty much be spatial at all times, which is okay. + mins.x = clamp( mins.x, -FLT_MAX, -1 ); + mins.y = clamp( mins.y, -FLT_MAX, -1 ); + maxs.x = clamp( maxs.x, 1, FLT_MAX ); + maxs.y = clamp( maxs.y, 1, FLT_MAX ); + } + + if (r_RainParticleClampDebug.GetBool()) + debugoverlay->AddBoxOverlay( vecOrigin, mins, maxs, vec3_angle, 255, 0, 0, 128, 0.15f ); + + maxs += vecOrigin; + mins += vecOrigin; + + CalcClosestPointOnAABB( mins, maxs, vPlayerPos, vPlayerPos ); + CalcClosestPointOnAABB( mins, maxs, vOffsetPos, vOffsetPos ); + CalcClosestPointOnAABB( mins, maxs, vOffsetPosNear, vOffsetPosNear ); + CalcClosestPointOnAABB( mins, maxs, vOffsetPosFar, vOffsetPosFar ); + } +#endif + + Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity; + + // Get the rain volume Ray Tracing Environment. Currently hard coded to 0, should have this lookup + RayTracingEnvironment *RtEnv = g_RayTraceEnvironments.Element( 0 ); + + // Our 4 Rays are forward, off to the left and right, and directly up. + // Use the first three to determine if there's generally visible rain where we're looking. + // The forth, straight up, tells us if we're standing inside a rain volume + // (based on the normal that we hit or if we miss entirely) + FourRays frRays; + FourVectors fvDirection; + fvDirection = FourVectors( vForward, vForward45Left, vForward45Right, Vector( 0, 0, 1 ) ); + frRays.direction = fvDirection; + frRays.origin.DuplicateVector( vPlayerPos ); + RayTracingResult Result; + + RtEnv->Trace4Rays( frRays, Four_Zeros, TMax, &Result ); + + i32x4 in4HitIds = LoadAlignedIntSIMD( Result.HitIds ); + fltx4 fl4HitIds = SignedIntConvertToFltSIMD ( in4HitIds ); + + fltx4 fl4Tolerance = ReplicateX4( 300.0f ); + // ignore upwards test for tolerance, as we may be below an area which is raining, but with it not visible in front of us + //SubFloat( fl4Tolerance, 3 ) = 0.0f; + + bool bInside = ( Result.HitIds[3] != -1 && Result.surface_normal.Vec( 3 ).z < 0.0f ); + bool bNearby = ( IsAnyNegative ( CmpGeSIMD ( fl4HitIds, Four_Zeros ) ) && IsAnyNegative( CmpGeSIMD( fl4Tolerance, Result.HitDistance ) ) ); + + if ( bInside || bNearby ) + { + //We can see a rain volume, but it's farther than 180 units away, only use far effect. + if ( !bInside && SubFloat( FindLowestSIMD3( Result.HitDistance ), 0 ) >= m_flParticleInnerDist ) + { + // Kill the inner rain if it's previously been in use + if ( m_pParticlePrecipInnerNear != NULL ) + { + DestroyInnerParticlePrecip(); + } + // Update if we've already got systems, otherwise, create them. + if ( m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } + else + { + DispatchOuterParticlePrecip( pPlayer, vForward ); + } + } + else //We're close enough to use the near effect. + { + // Update if we've already got systems, otherwise, create them. +#ifdef MAPBASE + // The outer can now be suppressed without interfering with other functionality + if ( m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } + if ( m_pParticlePrecipInnerNear != NULL && m_pParticlePrecipInnerFar != NULL ) + { + m_pParticlePrecipInnerNear->SetControlPoint( 1, vOffsetPosNear ); + m_pParticlePrecipInnerFar->SetControlPoint( 1, vOffsetPosFar ); + m_pParticlePrecipInnerNear->SetControlPoint( 3, vDensity ); + m_pParticlePrecipInnerFar->SetControlPoint( 3, vDensity ); + } +#else + if ( m_pParticlePrecipInnerNear != NULL && m_pParticlePrecipInnerFar != NULL && m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipInnerNear->SetControlPoint( 1, vOffsetPosNear ); + m_pParticlePrecipInnerFar->SetControlPoint( 1, vOffsetPosFar ); + m_pParticlePrecipInnerNear->SetControlPoint( 3, vDensity ); + m_pParticlePrecipInnerFar->SetControlPoint( 3, vDensity ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } +#endif + else + { + DispatchInnerParticlePrecip( pPlayer, vForward ); + } + } + } + else // No rain in the area, kill any leftover systems. + { + DestroyInnerParticlePrecip(); + DestroyOuterParticlePrecip(); + } + } +} + +void CClient_Precipitation::InitializeParticlePrecip( void ) +{ + //Set up which type of precipitation particle we'll use + if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH ) + { + m_pParticleInnerNearDef = "ash"; + m_pParticleInnerFarDef = "ash"; + m_pParticleOuterDef = "ash_outer"; + m_flParticleInnerDist = 280.0; + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW ) + { + m_pParticleInnerNearDef = "snow"; + m_pParticleInnerFarDef = "snow"; + m_pParticleOuterDef = "snow_outer"; + m_flParticleInnerDist = 280.0; + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM ) + { + m_pParticleInnerNearDef = "rain_storm"; + m_pParticleInnerFarDef = "rain_storm_screen"; + m_pParticleOuterDef = "rain_storm_outer"; + m_flParticleInnerDist = 0.0; + } + else //default to rain + { + m_pParticleInnerNearDef = "rain"; + m_pParticleInnerFarDef = "rain"; + m_pParticleOuterDef = "rain_outer"; + m_flParticleInnerDist = 180.0; + } + + Assert( m_pParticleInnerFarDef != NULL ); + + //We'll want to change this if/when we add more raytrace environments. + g_RayTraceEnvironments.PurgeAndDeleteElements(); + + // Sets up ray tracing environments for all func_precipitations and func_precipitation_blockers + RayTracingEnvironment *rtEnvRainEmission = new RayTracingEnvironment(); + g_RayTraceEnvironments.AddToTail( rtEnvRainEmission ); + RayTracingEnvironment *rtEnvRainBlocker = new RayTracingEnvironment(); + g_RayTraceEnvironments.AddToTail( rtEnvRainBlocker ); + + rtEnvRainEmission->Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram + rtEnvRainBlocker->Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram + + int nTriCount = 1; + for ( int i=0; iGetVCollide( volume->GetModelIndex() ); + + if ( !pCollide || pCollide->solidCount <= 0 ) + continue; + + Vector *outVerts; + int vertCount = physcollision->CreateDebugMesh( pCollide->solids[0], &outVerts ); + + if ( vertCount ) + { + for ( int j = 0; j < vertCount; j += 3 ) + { + rtEnvRainEmission->AddTriangle( nTriCount++, outVerts[j], outVerts[j + 1], outVerts[j + 2], Vector( 1, 1, 1 ) ); + } + } + physcollision->DestroyDebugMesh( vertCount, outVerts ); + } + rtEnvRainEmission->SetupAccelerationStructure(); + + m_bParticlePrecipInitialized = true; +} + +void CClient_Precipitation::DestroyInnerParticlePrecip( void ) +{ + if ( m_pParticlePrecipInnerFar != NULL ) + { + m_pParticlePrecipInnerFar->StopEmission(); + m_pParticlePrecipInnerFar = NULL; + } + if ( m_pParticlePrecipInnerNear != NULL ) + { + m_pParticlePrecipInnerNear->StopEmission(); + m_pParticlePrecipInnerNear = NULL; + } +} + +void CClient_Precipitation::DestroyOuterParticlePrecip( void ) +{ + if ( m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->StopEmission(); + m_pParticlePrecipOuter = NULL; + } +} + +void CClient_Precipitation::DispatchOuterParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ) +{ + DestroyOuterParticlePrecip(); + +#ifdef MAPBASE + if (m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER) + return; +#endif + + Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity; + Vector vPlayerPos = pPlayer->EyePosition(); + + m_pParticlePrecipOuter = ParticleProp()->Create( m_pParticleOuterDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipOuter->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipOuter->SetControlPoint( 1, vPlayerPos + Vector (0, 0, 180 ) ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); +} + +void CClient_Precipitation::DispatchInnerParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ) +{ + DestroyInnerParticlePrecip(); + DestroyOuterParticlePrecip(); + Vector vPlayerPos = pPlayer->EyePosition(); + Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 180 ); + Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 32 ); + Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * m_flParticleInnerDist ); // 100.0 + Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity; + +#ifdef MAPBASE + if (!(m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER)) +#endif + { + m_pParticlePrecipOuter = ParticleProp()->Create( m_pParticleOuterDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipOuter->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } + + m_pParticlePrecipInnerNear = ParticleProp()->Create( m_pParticleInnerNearDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipInnerFar = ParticleProp()->Create( m_pParticleInnerFarDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipInnerNear->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipInnerFar->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipInnerNear->SetControlPoint( 1, vOffsetPosNear ); + m_pParticlePrecipInnerFar->SetControlPoint( 1, vOffsetPosFar ); + m_pParticlePrecipInnerNear->SetControlPoint( 3, vDensity ); + m_pParticlePrecipInnerFar->SetControlPoint( 3, vDensity ); +} + void CClient_Precipitation::CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ) { // Create the particle @@ -1206,6 +1451,12 @@ BEGIN_RECV_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared) RecvPropFloat (RECVINFO(m_flStartTime)), RecvPropFloat (RECVINFO(m_flGustDuration)), // RecvPropInt (RECVINFO(m_iszGustSound)), +#ifdef MAPBASE + RecvPropFloat (RECVINFO(m_windRadius)), + RecvPropFloat (RECVINFO(m_windRadiusInner)), + RecvPropVector (RECVINFO(m_location)), + RecvPropFloat (RECVINFO(m_flTreeSwayScale)), +#endif END_RECV_TABLE() IMPLEMENT_CLIENTCLASS_DT( C_EnvWind, DT_EnvWind, CEnvWind ) @@ -1540,8 +1791,12 @@ public: pParticle->m_vecVelocity *= flSpeed; +#ifdef MAPBASE + Vector vecWindVelocity = GetWindspeedAtLocation( pParticle->m_Pos ); +#else Vector vecWindVelocity; GetWindspeedAtTime( gpGlobals->curtime, vecWindVelocity ); +#endif pParticle->m_vecVelocity += ( vecWindVelocity * r_SnowWindScale.GetFloat() ); } diff --git a/mp/src/game/client/c_effects.h b/mp/src/game/client/c_effects.h index 27bb501e..6012dff7 100644 --- a/mp/src/game/client/c_effects.h +++ b/mp/src/game/client/c_effects.h @@ -10,9 +10,178 @@ #pragma once #endif +#include "cbase.h" +#include "precipitation_shared.h" // Draw rain effects. void DrawPrecipitation(); +//----------------------------------------------------------------------------- +// Precipitation particle type +//----------------------------------------------------------------------------- + +class CPrecipitationParticle +{ +public: + Vector m_Pos; + Vector m_Velocity; + float m_SpawnTime; // Note: Tweak with this to change lifetime + float m_Mass; + float m_Ramp; + + float m_flCurLifetime; + float m_flMaxLifetime; +}; + + +class CClient_Precipitation; +static CUtlVector g_Precipitations; + +//=========== +// Snow fall +//=========== +class CSnowFallManager; +static CSnowFallManager *s_pSnowFallMgr = NULL; +bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ); +void SnowFallManagerDestroy( void ); + +class AshDebrisEffect : public CSimpleEmitter +{ +public: + AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} + + static AshDebrisEffect* Create( const char *pDebugName ); + + virtual float UpdateAlpha( const SimpleParticle *pParticle ); + virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta ); + +private: + AshDebrisEffect( const AshDebrisEffect & ); +}; + +//----------------------------------------------------------------------------- +// Precipitation base entity +//----------------------------------------------------------------------------- + +class CClient_Precipitation : public C_BaseEntity +{ +class CPrecipitationEffect; +friend class CClient_Precipitation::CPrecipitationEffect; + +public: + DECLARE_CLASS( CClient_Precipitation, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + CClient_Precipitation(); + virtual ~CClient_Precipitation(); + + // Inherited from C_BaseEntity + virtual void Precache( ); + + void Render(); + +private: + + // Creates a single particle + CPrecipitationParticle* CreateParticle(); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void ClientThink(); + + void Simulate( float dt ); + + // Renders the particle + void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ); + + void CreateWaterSplashes(); + + // Emits the actual particles + void EmitParticles( float fTimeDelta ); + + // Computes where we're gonna emit + bool ComputeEmissionArea( Vector& origin, Vector2D& size ); + + // Gets the tracer width and speed + float GetWidth() const; + float GetLength() const; + float GetSpeed() const; + + // Gets the remaining lifetime of the particle + float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const; + + // Computes the wind vector + static void ComputeWindVector( ); + + // simulation methods + bool SimulateRain( CPrecipitationParticle* pParticle, float dt ); + bool SimulateSnow( CPrecipitationParticle* pParticle, float dt ); + + void PrecacheParticlePrecip( void ); + void CreateParticlePrecip( void ); + void InitializeParticlePrecip( void ); + void DispatchOuterParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ); + void DispatchInnerParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ); + void DestroyOuterParticlePrecip( void ); + void DestroyInnerParticlePrecip( void ); + + void UpdateParticlePrecip( C_BasePlayer *pPlayer ); + +private: + void CreateAshParticle( void ); + void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); + + // Information helpful in creating and rendering particles + IMaterial *m_MatHandle; // material used + + float m_Color[4]; // precip color + float m_Lifetime; // Precip lifetime + float m_InitialRamp; // Initial ramp value + float m_Speed; // Precip speed + float m_Width; // Tracer width + float m_Remainder; // particles we should render next time + PrecipitationType_t m_nPrecipType; // Precip type + float m_flHalfScreenWidth; // Precalculated each frame. + + float m_flDensity; + +#ifdef MAPBASE + int m_spawnflags; +#endif + + // Some state used in rendering and simulation + // Used to modify the rain density and wind from the console + static ConVar s_raindensity; + static ConVar s_rainwidth; + static ConVar s_rainlength; + static ConVar s_rainspeed; + + static Vector s_WindVector; // Stores the wind speed vector + + CUtlLinkedList m_Particles; + CUtlVector m_Splashes; + + CSmartPtr m_pAshEmitter; + TimedEvent m_tAshParticleTimer; + TimedEvent m_tAshParticleTraceTimer; + bool m_bActiveAshEmitter; + Vector m_vAshSpawnOrigin; + + int m_iAshCount; + +protected: + float m_flParticleInnerDist; //The distance at which to start drawing the inner system + char *m_pParticleInnerNearDef; //Name of the first inner system + char *m_pParticleInnerFarDef; //Name of the second inner system + char *m_pParticleOuterDef; //Name of the outer system + HPARTICLEFFECT m_pParticlePrecipInnerNear; + HPARTICLEFFECT m_pParticlePrecipInnerFar; + HPARTICLEFFECT m_pParticlePrecipOuter; + TimedEvent m_tParticlePrecipTraceTimer; + bool m_bActiveParticlePrecipEmitter; + bool m_bParticlePrecipInitialized; + +private: + CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible +}; #endif // C_EFFECTS_H diff --git a/mp/src/game/client/c_env_dof_controller.cpp b/mp/src/game/client/c_env_dof_controller.cpp new file mode 100644 index 00000000..274820e7 --- /dev/null +++ b/mp/src/game/client/c_env_dof_controller.cpp @@ -0,0 +1,88 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: Depth of field controller entity +// +//============================================================================= + +#include "cbase.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +extern bool g_bDOFEnabled; +extern float g_flDOFNearBlurDepth; +extern float g_flDOFNearFocusDepth; +extern float g_flDOFFarFocusDepth; +extern float g_flDOFFarBlurDepth; +extern float g_flDOFNearBlurRadius; +extern float g_flDOFFarBlurRadius; + +EHANDLE g_hDOFControllerInUse = NULL; + +class C_EnvDOFController : public C_BaseEntity +{ + DECLARE_CLASS( C_EnvDOFController, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + C_EnvDOFController(); + ~C_EnvDOFController(); + virtual void OnDataChanged( DataUpdateType_t updateType ); + +private: + bool m_bDOFEnabled; + float m_flNearBlurDepth; + float m_flNearFocusDepth; + float m_flFarFocusDepth; + float m_flFarBlurDepth; + float m_flNearBlurRadius; + float m_flFarBlurRadius; + +private: + C_EnvDOFController( const C_EnvDOFController & ); +}; + +IMPLEMENT_CLIENTCLASS_DT( C_EnvDOFController, DT_EnvDOFController, CEnvDOFController ) + RecvPropInt( RECVINFO(m_bDOFEnabled) ), + RecvPropFloat( RECVINFO(m_flNearBlurDepth) ), + RecvPropFloat( RECVINFO(m_flNearFocusDepth) ), + RecvPropFloat( RECVINFO(m_flFarFocusDepth) ), + RecvPropFloat( RECVINFO(m_flFarBlurDepth) ), + RecvPropFloat( RECVINFO(m_flNearBlurRadius) ), + RecvPropFloat( RECVINFO(m_flFarBlurRadius) ) +END_RECV_TABLE() + +C_EnvDOFController::C_EnvDOFController() +: m_bDOFEnabled( true ), + m_flNearBlurDepth( 20.0f ), + m_flNearFocusDepth( 100.0f ), + m_flFarFocusDepth( 250.0f ), + m_flFarBlurDepth( 1000.0f ), + m_flNearBlurRadius( 0.0f ), // no near blur by default + m_flFarBlurRadius( 5.0f ) +{ +} + +C_EnvDOFController::~C_EnvDOFController() +{ + if ( g_hDOFControllerInUse == this ) + { + g_bDOFEnabled = false; + } +} + +void C_EnvDOFController::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + g_bDOFEnabled = m_bDOFEnabled && ( ( m_flNearBlurRadius > 0.0f ) || ( m_flFarBlurRadius > 0.0f ) ); + g_flDOFNearBlurDepth = m_flNearBlurDepth; + g_flDOFNearFocusDepth = m_flNearFocusDepth; + g_flDOFFarFocusDepth = m_flFarFocusDepth; + g_flDOFFarBlurDepth = m_flFarBlurDepth; + g_flDOFNearBlurRadius = m_flNearBlurRadius; + g_flDOFFarBlurRadius = m_flFarBlurRadius; + + g_hDOFControllerInUse = this; +} diff --git a/mp/src/game/client/c_env_global_light.cpp b/mp/src/game/client/c_env_global_light.cpp new file mode 100644 index 00000000..c48803ae --- /dev/null +++ b/mp/src/game/client/c_env_global_light.cpp @@ -0,0 +1,338 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Sunlight shadow control entity. +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" + +#include "c_baseplayer.h" +#include "tier0/vprof.h" +#ifdef MAPBASE +#include "materialsystem/itexture.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +extern ConVar cl_sunlight_ortho_size; +extern ConVar cl_sunlight_depthbias; + +ConVar cl_globallight_freeze( "cl_globallight_freeze", "0" ); +#ifdef MAPBASE +// I imagine these values might've been designed for the ASW view. +// You can set these as KV anyway. +ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "0" ); +ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "0" ); +#else +ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "-800" ); +ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "1600" ); +#endif + +//------------------------------------------------------------------------------ +// Purpose : Sunlights shadow control entity +//------------------------------------------------------------------------------ +class C_GlobalLight : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_GlobalLight, C_BaseEntity ); + + DECLARE_CLIENTCLASS(); + + virtual ~C_GlobalLight(); + + void OnDataChanged( DataUpdateType_t updateType ); + void Spawn(); + bool ShouldDraw(); + + void ClientThink(); + +private: + Vector m_shadowDirection; + bool m_bEnabled; + char m_TextureName[ MAX_PATH ]; +#ifdef MAPBASE + int m_nSpotlightTextureFrame; +#endif + CTextureReference m_SpotlightTexture; + color32 m_LightColor; +#ifdef MAPBASE + float m_flBrightnessScale; + float m_flCurrentBrightnessScale; +#endif + Vector m_CurrentLinearFloatLightColor; + float m_flCurrentLinearFloatLightAlpha; + float m_flColorTransitionTime; + float m_flSunDistance; + float m_flFOV; + float m_flNearZ; + float m_flNorthOffset; +#ifdef MAPBASE + float m_flEastOffset; + float m_flForwardOffset; + float m_flOrthoSize; +#endif + bool m_bEnableShadows; + bool m_bOldEnableShadows; + + static ClientShadowHandle_t m_LocalFlashlightHandle; +}; + + +ClientShadowHandle_t C_GlobalLight::m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + + +IMPLEMENT_CLIENTCLASS_DT(C_GlobalLight, DT_GlobalLight, CGlobalLight) + RecvPropVector(RECVINFO(m_shadowDirection)), + RecvPropBool(RECVINFO(m_bEnabled)), + RecvPropString(RECVINFO(m_TextureName)), +#ifdef MAPBASE + RecvPropInt(RECVINFO(m_nSpotlightTextureFrame)), +#endif + /*RecvPropInt(RECVINFO(m_LightColor), 0, RecvProxy_Int32ToColor32),*/ + RecvPropInt(RECVINFO(m_LightColor), 0, RecvProxy_IntToColor32), +#ifdef MAPBASE + RecvPropFloat(RECVINFO(m_flBrightnessScale)), +#endif + RecvPropFloat(RECVINFO(m_flColorTransitionTime)), + RecvPropFloat(RECVINFO(m_flSunDistance)), + RecvPropFloat(RECVINFO(m_flFOV)), + RecvPropFloat(RECVINFO(m_flNearZ)), + RecvPropFloat(RECVINFO(m_flNorthOffset)), +#ifdef MAPBASE + RecvPropFloat(RECVINFO(m_flEastOffset)), + RecvPropFloat(RECVINFO(m_flForwardOffset)), + RecvPropFloat(RECVINFO(m_flOrthoSize)), +#endif + RecvPropBool(RECVINFO(m_bEnableShadows)), +END_RECV_TABLE() + + +C_GlobalLight::~C_GlobalLight() +{ + if ( m_LocalFlashlightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LocalFlashlightHandle ); + m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + } +} + +void C_GlobalLight::OnDataChanged( DataUpdateType_t updateType ) +{ + if ( updateType == DATA_UPDATE_CREATED ) + { + m_SpotlightTexture.Init( m_TextureName, TEXTURE_GROUP_OTHER, true ); + } +#ifdef MAPBASE + else //if ( updateType == DATA_UPDATE_DATATABLE_CHANGED ) + { + // It could've been changed via input + if( !FStrEq(m_SpotlightTexture->GetName(), m_TextureName) ) + { + m_SpotlightTexture.Init( m_TextureName, TEXTURE_GROUP_OTHER, true ); + } + } +#endif + + BaseClass::OnDataChanged( updateType ); +} + +void C_GlobalLight::Spawn() +{ + BaseClass::Spawn(); + + m_bOldEnableShadows = m_bEnableShadows; + + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +//------------------------------------------------------------------------------ +// We don't draw... +//------------------------------------------------------------------------------ +bool C_GlobalLight::ShouldDraw() +{ + return false; +} + +void C_GlobalLight::ClientThink() +{ + VPROF("C_GlobalLight::ClientThink"); + + bool bSupressWorldLights = false; + + if ( cl_globallight_freeze.GetBool() == true ) + { + return; + } + + if ( m_bEnabled ) + { + Vector vLinearFloatLightColor( m_LightColor.r, m_LightColor.g, m_LightColor.b ); + float flLinearFloatLightAlpha = m_LightColor.a; + +#ifdef MAPBASE + if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha || m_flCurrentBrightnessScale != m_flBrightnessScale ) + { + if (m_flColorTransitionTime != 0.0f) + { + float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f; + + m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed ); + m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed ); + m_flCurrentBrightnessScale = Approach( m_flBrightnessScale, m_flCurrentBrightnessScale, flColorTransitionSpeed ); + } + else + { + // Just do it instantly + m_CurrentLinearFloatLightColor.x = vLinearFloatLightColor.x; + m_CurrentLinearFloatLightColor.y = vLinearFloatLightColor.y; + m_CurrentLinearFloatLightColor.z = vLinearFloatLightColor.z; + m_flCurrentLinearFloatLightAlpha = flLinearFloatLightAlpha; + m_flCurrentBrightnessScale = m_flBrightnessScale; + } + } +#else + if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha ) + { + float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f; + + m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed ); + m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed ); + } +#endif + + FlashlightState_t state; + + Vector vDirection = m_shadowDirection; + VectorNormalize( vDirection ); + + //Vector vViewUp = Vector( 0.0f, 1.0f, 0.0f ); + Vector vSunDirection2D = vDirection; + vSunDirection2D.z = 0.0f; + + /*HACK_GETLOCALPLAYER_GUARD( "C_GlobalLight::ClientThink" );*/ + + if ( !C_BasePlayer::GetLocalPlayer() ) + return; + + Vector vPos; + QAngle EyeAngles; + float flZNear, flZFar, flFov; + + C_BasePlayer::GetLocalPlayer()->CalcView( vPos, EyeAngles, flZNear, flZFar, flFov ); +// Vector vPos = C_BasePlayer::GetLocalPlayer()->GetAbsOrigin(); + +// vPos = Vector( 0.0f, 0.0f, 500.0f ); + vPos = ( vPos + vSunDirection2D * m_flNorthOffset ) - vDirection * m_flSunDistance; +#ifdef MAPBASE + vPos += Vector( m_flEastOffset + cl_globallight_xoffset.GetFloat(), m_flForwardOffset + cl_globallight_yoffset.GetFloat(), 0.0f ); +#else + vPos += Vector( cl_globallight_xoffset.GetFloat(), cl_globallight_yoffset.GetFloat(), 0.0f ); +#endif + + QAngle angAngles; + VectorAngles( vDirection, angAngles ); + + Vector vForward, vRight, vUp; + AngleVectors( angAngles, &vForward, &vRight, &vUp ); + + state.m_fHorizontalFOVDegrees = m_flFOV; + state.m_fVerticalFOVDegrees = m_flFOV; + + state.m_vecLightOrigin = vPos; + BasisToQuaternion( vForward, vRight, vUp, state.m_quatOrientation ); + + state.m_fQuadraticAtten = 0.0f; + state.m_fLinearAtten = m_flSunDistance * 2.0f; + state.m_fConstantAtten = 0.0f; + state.m_FarZAtten = m_flSunDistance * 2.0f; +#ifdef MAPBASE + float flAlpha = m_flCurrentLinearFloatLightAlpha * (1.0f / 255.0f); + state.m_Color[0] = (m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[1] = (m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[2] = (m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; +#else + state.m_Color[0] = m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * m_flCurrentLinearFloatLightAlpha; + state.m_Color[1] = m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * m_flCurrentLinearFloatLightAlpha; + state.m_Color[2] = m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * m_flCurrentLinearFloatLightAlpha; +#endif + state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient; + state.m_NearZ = 4.0f; + state.m_FarZ = m_flSunDistance * 2.0f; + state.m_fBrightnessScale = 2.0f; + state.m_bGlobalLight = true; + +#ifdef MAPBASE + float flOrthoSize = m_flOrthoSize; +#else + float flOrthoSize = 1000.0f; +#endif + + if ( flOrthoSize > 0 ) + { + state.m_bOrtho = true; + state.m_fOrthoLeft = -flOrthoSize; + state.m_fOrthoTop = -flOrthoSize; + state.m_fOrthoRight = flOrthoSize; + state.m_fOrthoBottom = flOrthoSize; + } + else + { + state.m_bOrtho = false; + } + +#ifndef MAPBASE // Don't draw that huge debug thing + state.m_bDrawShadowFrustum = true; +#endif + /*state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();; + state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();*/ + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = m_SpotlightTexture; +#ifdef MAPBASE + state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; +#else + state.m_nSpotlightTextureFrame = 0; +#endif + + state.m_nShadowQuality = 1; // Allow entity to affect shadow quality +// state.m_bShadowHighRes = true; + + if ( m_bOldEnableShadows != m_bEnableShadows ) + { + // If they change the shadow enable/disable, we need to make a new handle + if ( m_LocalFlashlightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LocalFlashlightHandle ); + m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + } + + m_bOldEnableShadows = m_bEnableShadows; + } + + if( m_LocalFlashlightHandle == CLIENTSHADOW_INVALID_HANDLE ) + { + m_LocalFlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state ); + } + else + { + g_pClientShadowMgr->UpdateFlashlightState( m_LocalFlashlightHandle, state ); + g_pClientShadowMgr->UpdateProjectedTexture( m_LocalFlashlightHandle, true ); + } + + bSupressWorldLights = m_bEnableShadows; + } + else if ( m_LocalFlashlightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LocalFlashlightHandle ); + m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + } + + g_pClientShadowMgr->SetShadowFromWorldLightsEnabled( !bSupressWorldLights ); + + BaseClass::ClientThink(); +} \ No newline at end of file diff --git a/mp/src/game/client/c_env_projectedtexture.cpp b/mp/src/game/client/c_env_projectedtexture.cpp index 310c3a27..c78cddb2 100644 --- a/mp/src/game/client/c_env_projectedtexture.cpp +++ b/mp/src/game/client/c_env_projectedtexture.cpp @@ -5,6 +5,13 @@ //============================================================================= #include "cbase.h" +#ifdef ASW_PROJECTED_TEXTURES +#include "C_Env_Projected_Texture.h" +#include "vprof.h" +#endif +#ifdef MAPBASE +#include "materialsystem/itexture.h" +#endif #include "shareddefs.h" #include "materialsystem/imesh.h" #include "materialsystem/imaterial.h" @@ -17,8 +24,475 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef ASW_PROJECTED_TEXTURES +static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); +static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.00001", FCVAR_CHEAT ); + +float C_EnvProjectedTexture::m_flVisibleBBoxMinHeight = -FLT_MAX; + + +IMPLEMENT_CLIENTCLASS_DT( C_EnvProjectedTexture, DT_EnvProjectedTexture, CEnvProjectedTexture ) + RecvPropEHandle( RECVINFO( m_hTargetEntity ) ), +#ifdef MAPBASE + RecvPropBool( RECVINFO( m_bDontFollowTarget )), +#endif + RecvPropBool( RECVINFO( m_bState ) ), + RecvPropBool( RECVINFO( m_bAlwaysUpdate ) ), + RecvPropFloat( RECVINFO( m_flLightFOV ) ), +#ifdef MAPBASE + RecvPropFloat( RECVINFO( m_flLightHorFOV ) ), +#endif + RecvPropBool( RECVINFO( m_bEnableShadows ) ), + RecvPropBool( RECVINFO( m_bLightOnlyTarget ) ), + RecvPropBool( RECVINFO( m_bLightWorld ) ), + RecvPropBool( RECVINFO( m_bCameraSpace ) ), + RecvPropFloat( RECVINFO( m_flBrightnessScale ) ), + RecvPropInt( RECVINFO( m_LightColor ), 0, RecvProxy_IntToColor32 ), + RecvPropFloat( RECVINFO( m_flColorTransitionTime ) ), + RecvPropFloat( RECVINFO( m_flAmbient ) ), + RecvPropString( RECVINFO( m_SpotlightTextureName ) ), + RecvPropInt( RECVINFO( m_nSpotlightTextureFrame ) ), + RecvPropFloat( RECVINFO( m_flNearZ ) ), + RecvPropFloat( RECVINFO( m_flFarZ ) ), + RecvPropInt( RECVINFO( m_nShadowQuality ) ), +#ifdef MAPBASE + RecvPropFloat( RECVINFO( m_flConstantAtten ) ), + RecvPropFloat( RECVINFO( m_flLinearAtten ) ), + RecvPropFloat( RECVINFO( m_flQuadraticAtten ) ), + RecvPropFloat( RECVINFO( m_flShadowAtten ) ), + RecvPropBool( RECVINFO( m_bAlwaysDraw ) ), + + // Not needed on the client right now, change when it actually is needed + //RecvPropBool( RECVINFO( m_bProjectedTextureVersion ) ), +#endif +END_RECV_TABLE() + +C_EnvProjectedTexture *C_EnvProjectedTexture::Create( ) +{ + C_EnvProjectedTexture *pEnt = new C_EnvProjectedTexture(); + + pEnt->m_flNearZ = 4.0f; + pEnt->m_flFarZ = 2000.0f; +// strcpy( pEnt->m_SpotlightTextureName, "particle/rj" ); + pEnt->m_bLightWorld = true; + pEnt->m_bLightOnlyTarget = false; + pEnt->m_nShadowQuality = 1; + pEnt->m_flLightFOV = 10.0f; +#ifdef MAPBASE + pEnt->m_flLightHorFOV = 10.0f; +#endif + pEnt->m_LightColor.r = 255; + pEnt->m_LightColor.g = 255; + pEnt->m_LightColor.b = 255; + pEnt->m_LightColor.a = 255; + pEnt->m_bEnableShadows = false; + pEnt->m_flColorTransitionTime = 1.0f; + pEnt->m_bCameraSpace = false; + pEnt->SetAbsAngles( QAngle( 90, 0, 0 ) ); + pEnt->m_bAlwaysUpdate = true; + pEnt->m_bState = true; +#ifdef MAPBASE + pEnt->m_bAlwaysDraw = false; + pEnt->m_flConstantAtten = 0.0f; + pEnt->m_flLinearAtten = 100.0f; + pEnt->m_flQuadraticAtten = 0.0f; + pEnt->m_flShadowAtten = 0.0f; + //pEnt->m_bProjectedTextureVersion = 1; +#endif + + return pEnt; +} + +C_EnvProjectedTexture::C_EnvProjectedTexture( void ) +{ + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + m_bForceUpdate = true; +#ifndef MAPBASE + AddToEntityList( ENTITY_LIST_SIMULATE ); +#endif +} + +C_EnvProjectedTexture::~C_EnvProjectedTexture( void ) +{ + ShutDownLightHandle(); +} + +void C_EnvProjectedTexture::ShutDownLightHandle( void ) +{ + // Clear out the light + if( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LightHandle ); + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + } +} + + +void C_EnvProjectedTexture::SetLightColor( byte r, byte g, byte b, byte a ) +{ + m_LightColor.r = r; + m_LightColor.g = g; + m_LightColor.b = b; + m_LightColor.a = a; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : updateType - +//----------------------------------------------------------------------------- +void C_EnvProjectedTexture::OnDataChanged( DataUpdateType_t updateType ) +{ + if ( updateType == DATA_UPDATE_CREATED ) + { + m_SpotlightTexture.Init( m_SpotlightTextureName, TEXTURE_GROUP_OTHER, true ); + } +#ifdef MAPBASE + else //if ( updateType == DATA_UPDATE_DATATABLE_CHANGED ) + { + // It could've been changed via input + if( !FStrEq(m_SpotlightTexture->GetName(), m_SpotlightTextureName) ) + { + m_SpotlightTexture.Init( m_SpotlightTextureName, TEXTURE_GROUP_OTHER, true ); + } + } +#endif + + m_bForceUpdate = true; + UpdateLight(); + BaseClass::OnDataChanged( updateType ); +} + +static ConVar asw_perf_wtf("asw_perf_wtf", "0", FCVAR_DEVELOPMENTONLY, "Disable updating of projected shadow textures from UpdateLight" ); +void C_EnvProjectedTexture::UpdateLight( void ) +{ + VPROF("C_EnvProjectedTexture::UpdateLight"); + bool bVisible = true; + + Vector vLinearFloatLightColor( m_LightColor.r, m_LightColor.g, m_LightColor.b ); + float flLinearFloatLightAlpha = m_LightColor.a; + + if ( m_bAlwaysUpdate ) + { + m_bForceUpdate = true; + } + +#ifdef MAPBASE + if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha || m_flCurrentBrightnessScale != m_flBrightnessScale ) + { + if (m_flColorTransitionTime != 0.0f) + { + float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f; + + m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed ); + m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed ); + m_flCurrentBrightnessScale = Approach( m_flBrightnessScale, m_flCurrentBrightnessScale, flColorTransitionSpeed ); + } + else + { + // Just do it instantly + m_CurrentLinearFloatLightColor.x = vLinearFloatLightColor.x; + m_CurrentLinearFloatLightColor.y = vLinearFloatLightColor.y; + m_CurrentLinearFloatLightColor.z = vLinearFloatLightColor.z; + m_flCurrentLinearFloatLightAlpha = flLinearFloatLightAlpha; + m_flCurrentBrightnessScale = m_flBrightnessScale; + } + + m_bForceUpdate = true; + } +#else + if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha ) + { + float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f; + + m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed ); + m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed ); + + m_bForceUpdate = true; + } +#endif + + if ( !m_bForceUpdate ) + { + bVisible = IsBBoxVisible(); + } + + if ( m_bState == false || !bVisible ) + { + // Spotlight's extents aren't in view + ShutDownLightHandle(); + + return; + } + + if ( m_LightHandle == CLIENTSHADOW_INVALID_HANDLE || m_hTargetEntity != NULL || m_bForceUpdate ) + { + Vector vForward, vRight, vUp, vPos = GetAbsOrigin(); + FlashlightState_t state; + +#ifdef MAPBASE + if ( m_hTargetEntity != NULL && !m_bDontFollowTarget ) +#else + if ( m_hTargetEntity != NULL ) +#endif + { + if ( m_bCameraSpace ) + { + const QAngle &angles = GetLocalAngles(); + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if( pPlayer ) + { + const QAngle playerAngles = pPlayer->GetAbsAngles(); + + Vector vPlayerForward, vPlayerRight, vPlayerUp; + AngleVectors( playerAngles, &vPlayerForward, &vPlayerRight, &vPlayerUp ); + + matrix3x4_t mRotMatrix; + AngleMatrix( angles, mRotMatrix ); + + VectorITransform( vPlayerForward, mRotMatrix, vForward ); + VectorITransform( vPlayerRight, mRotMatrix, vRight ); + VectorITransform( vPlayerUp, mRotMatrix, vUp ); + + float dist = (m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin()).Length(); + vPos = m_hTargetEntity->GetAbsOrigin() - vForward*dist; + + VectorNormalize( vForward ); + VectorNormalize( vRight ); + VectorNormalize( vUp ); + } + } + else + { + vForward = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin(); + VectorNormalize( vForward ); + + // JasonM - unimplemented + Assert (0); + + //Quaternion q = DirectionToOrientation( dir ); + + + // + // JasonM - set up vRight, vUp + // + + // VectorNormalize( vRight ); + // VectorNormalize( vUp ); + } + } + else + { + AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp ); + } + +#ifdef MAPBASE + float fHighFOV; + if( m_flLightFOV > m_flLightHorFOV ) + fHighFOV = m_flLightFOV; + else + fHighFOV = m_flLightHorFOV; + + state.m_fHorizontalFOVDegrees = m_flLightHorFOV; +#else + state.m_fHorizontalFOVDegrees = m_flLightFOV; +#endif + state.m_fVerticalFOVDegrees = m_flLightFOV; + + state.m_vecLightOrigin = vPos; + BasisToQuaternion( vForward, vRight, vUp, state.m_quatOrientation ); + state.m_NearZ = m_flNearZ; + state.m_FarZ = m_flFarZ; + + // quickly check the proposed light's bbox against the view frustum to determine whether we + // should bother to create it, if it doesn't exist, or cull it, if it does. + // get the half-widths of the near and far planes, + // based on the FOV which is in degrees. Remember that + // on planet Valve, x is forward, y left, and z up. +#ifdef MAPBASE + const float tanHalfAngle = tan( fHighFOV * ( M_PI/180.0f ) * 0.5f ); +#else + const float tanHalfAngle = tan( m_flLightFOV * ( M_PI/180.0f ) * 0.5f ); +#endif + const float halfWidthNear = tanHalfAngle * m_flNearZ; + const float halfWidthFar = tanHalfAngle * m_flFarZ; + // now we can build coordinates in local space: the near rectangle is eg + // (0, -halfWidthNear, -halfWidthNear), (0, halfWidthNear, -halfWidthNear), + // (0, halfWidthNear, halfWidthNear), (0, -halfWidthNear, halfWidthNear) + + VectorAligned vNearRect[4] = { + VectorAligned( m_flNearZ, -halfWidthNear, -halfWidthNear), VectorAligned( m_flNearZ, halfWidthNear, -halfWidthNear), + VectorAligned( m_flNearZ, halfWidthNear, halfWidthNear), VectorAligned( m_flNearZ, -halfWidthNear, halfWidthNear) + }; + + VectorAligned vFarRect[4] = { + VectorAligned( m_flFarZ, -halfWidthFar, -halfWidthFar), VectorAligned( m_flFarZ, halfWidthFar, -halfWidthFar), + VectorAligned( m_flFarZ, halfWidthFar, halfWidthFar), VectorAligned( m_flFarZ, -halfWidthFar, halfWidthFar) + }; + + matrix3x4_t matOrientation( vForward, -vRight, vUp, vPos ); + + enum + { + kNEAR = 0, + kFAR = 1, + }; + VectorAligned vOutRects[2][4]; + + for ( int i = 0 ; i < 4 ; ++i ) + { + VectorTransform( vNearRect[i].Base(), matOrientation, vOutRects[0][i].Base() ); + } + for ( int i = 0 ; i < 4 ; ++i ) + { + VectorTransform( vFarRect[i].Base(), matOrientation, vOutRects[1][i].Base() ); + } + + // now take the min and max extents for the bbox, and see if it is visible. + Vector mins = **vOutRects; + Vector maxs = **vOutRects; + for ( int i = 1; i < 8 ; ++i ) + { + VectorMin( mins, *(*vOutRects+i), mins ); + VectorMax( maxs, *(*vOutRects+i), maxs ); + } + +#if 0 //for debugging the visibility frustum we just calculated + NDebugOverlay::Triangle( vOutRects[0][0], vOutRects[0][1], vOutRects[0][2], 255, 0, 0, 100, true, 0.0f ); //first tri + NDebugOverlay::Triangle( vOutRects[0][2], vOutRects[0][1], vOutRects[0][0], 255, 0, 0, 100, true, 0.0f ); //make it double sided + NDebugOverlay::Triangle( vOutRects[0][2], vOutRects[0][3], vOutRects[0][0], 255, 0, 0, 100, true, 0.0f ); //second tri + NDebugOverlay::Triangle( vOutRects[0][0], vOutRects[0][3], vOutRects[0][2], 255, 0, 0, 100, true, 0.0f ); //make it double sided + + NDebugOverlay::Triangle( vOutRects[1][0], vOutRects[1][1], vOutRects[1][2], 0, 0, 255, 100, true, 0.0f ); //first tri + NDebugOverlay::Triangle( vOutRects[1][2], vOutRects[1][1], vOutRects[1][0], 0, 0, 255, 100, true, 0.0f ); //make it double sided + NDebugOverlay::Triangle( vOutRects[1][2], vOutRects[1][3], vOutRects[1][0], 0, 0, 255, 100, true, 0.0f ); //second tri + NDebugOverlay::Triangle( vOutRects[1][0], vOutRects[1][3], vOutRects[1][2], 0, 0, 255, 100, true, 0.0f ); //make it double sided + + NDebugOverlay::Box( vec3_origin, mins, maxs, 0, 255, 0, 100, 0.0f ); +#endif + + bool bVisible = IsBBoxVisible( mins, maxs ); + if (!bVisible) + { + // Spotlight's extents aren't in view + if ( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + ShutDownLightHandle(); + } + + return; + } + + float flAlpha = m_flCurrentLinearFloatLightAlpha * ( 1.0f / 255.0f ); + +#ifdef MAPBASE + state.m_fConstantAtten = m_flConstantAtten; + state.m_fLinearAtten = m_flLinearAtten; + state.m_fQuadraticAtten = m_flQuadraticAtten; + state.m_FarZAtten = m_flFarZ; + state.m_Color[0] = (m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[1] = (m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[2] = (m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient; + state.m_flShadowSlopeScaleDepthBias = mat_slopescaledepthbias_shadowmap.GetFloat(); + state.m_flShadowDepthBias = mat_depthbias_shadowmap.GetFloat(); + state.m_flShadowAtten = m_flShadowAtten; +#else + state.m_fQuadraticAtten = 0.0; + state.m_fLinearAtten = 100; + state.m_fConstantAtten = 0.0f; + state.m_FarZAtten = m_flFarZ; + state.m_fBrightnessScale = m_flBrightnessScale; + state.m_Color[0] = m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * flAlpha; + state.m_Color[1] = m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * flAlpha; + state.m_Color[2] = m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * flAlpha; + state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient; + state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias(); + state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias(); +#endif + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = m_SpotlightTexture; + state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; + + state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality + +#ifdef MAPBASE + state.m_bAlwaysDraw = m_bAlwaysDraw; +#endif + + if( m_LightHandle == CLIENTSHADOW_INVALID_HANDLE ) + { + m_LightHandle = g_pClientShadowMgr->CreateFlashlight( state ); + + if ( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + m_bForceUpdate = false; + } + } + else + { + g_pClientShadowMgr->UpdateFlashlightState( m_LightHandle, state ); + m_bForceUpdate = false; + } + + g_pClientShadowMgr->GetFrustumExtents( m_LightHandle, m_vecExtentsMin, m_vecExtentsMax ); + + m_vecExtentsMin = m_vecExtentsMin - GetAbsOrigin(); + m_vecExtentsMax = m_vecExtentsMax - GetAbsOrigin(); + } + + if( m_bLightOnlyTarget ) + { + g_pClientShadowMgr->SetFlashlightTarget( m_LightHandle, m_hTargetEntity ); + } + else + { + g_pClientShadowMgr->SetFlashlightTarget( m_LightHandle, NULL ); + } + + g_pClientShadowMgr->SetFlashlightLightWorld( m_LightHandle, m_bLightWorld ); + + if ( !asw_perf_wtf.GetBool() && !m_bForceUpdate ) + { + g_pClientShadowMgr->UpdateProjectedTexture( m_LightHandle, true ); + } +} + +void C_EnvProjectedTexture::Simulate( void ) +{ + UpdateLight(); + + BaseClass::Simulate(); +} + +bool C_EnvProjectedTexture::IsBBoxVisible( Vector vecExtentsMin, Vector vecExtentsMax ) +{ +#ifdef MAPBASE + if (m_bAlwaysDraw) + return true; +#endif + + // Z position clamped to the min height (but must be less than the max) + float flVisibleBBoxMinHeight = MIN( vecExtentsMax.z - 1.0f, m_flVisibleBBoxMinHeight ); + vecExtentsMin.z = MAX( vecExtentsMin.z, flVisibleBBoxMinHeight ); + + // Check if the bbox is in the view + return !engine->CullBox( vecExtentsMin, vecExtentsMax ); +} + +#else + +#ifndef MAPBASE static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT ); +#else +static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "4", FCVAR_CHEAT ); +static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.00001", FCVAR_CHEAT ); +#endif //----------------------------------------------------------------------------- // Purpose: @@ -34,7 +508,11 @@ public: virtual void Simulate(); +#ifdef MAPBASE + void UpdateLight(); +#else void UpdateLight( bool bForceUpdate ); +#endif C_EnvProjectedTexture(); ~C_EnvProjectedTexture(); @@ -42,10 +520,16 @@ public: private: ClientShadowHandle_t m_LightHandle; +#ifdef MAPBASE + bool m_bForceUpdate; +#endif EHANDLE m_hTargetEntity; bool m_bState; +#ifdef MAPBASE + bool m_bAlwaysUpdate; +#endif float m_flLightFOV; bool m_bEnableShadows; bool m_bLightOnlyTarget; @@ -63,6 +547,9 @@ private: IMPLEMENT_CLIENTCLASS_DT( C_EnvProjectedTexture, DT_EnvProjectedTexture, CEnvProjectedTexture ) RecvPropEHandle( RECVINFO( m_hTargetEntity ) ), RecvPropBool( RECVINFO( m_bState ) ), +#ifdef MAPBASE + RecvPropBool( RECVINFO( m_bAlwaysUpdate ) ), +#endif RecvPropFloat( RECVINFO( m_flLightFOV ) ), RecvPropBool( RECVINFO( m_bEnableShadows ) ), RecvPropBool( RECVINFO( m_bLightOnlyTarget ) ), @@ -103,12 +590,22 @@ void C_EnvProjectedTexture::ShutDownLightHandle( void ) //----------------------------------------------------------------------------- void C_EnvProjectedTexture::OnDataChanged( DataUpdateType_t updateType ) { +#ifdef MAPBASE + m_bForceUpdate = true; + UpdateLight(); +#else UpdateLight( true ); +#endif BaseClass::OnDataChanged( updateType ); } +#ifndef MAPBASE void C_EnvProjectedTexture::UpdateLight( bool bForceUpdate ) +#else +void C_EnvProjectedTexture::UpdateLight() +#endif { +#ifndef MAPBASE if ( m_bState == false ) { if ( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE ) @@ -118,7 +615,25 @@ void C_EnvProjectedTexture::UpdateLight( bool bForceUpdate ) return; } +#else + if ( m_bAlwaysUpdate ) + { + m_bForceUpdate = true; + } + if ( m_bState == false ) + { + // Spotlight's extents aren't in view + ShutDownLightHandle(); + + return; + } +#endif + +#ifdef MAPBASE + if ( m_LightHandle == CLIENTSHADOW_INVALID_HANDLE || m_hTargetEntity != NULL || m_bForceUpdate ) + { +#endif Vector vForward, vRight, vUp, vPos = GetAbsOrigin(); FlashlightState_t state; @@ -153,8 +668,24 @@ void C_EnvProjectedTexture::UpdateLight( bool bForceUpdate ) } else { +#ifndef MAPBASE vForward = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin(); VectorNormalize( vForward ); +#else + // VXP: Fixing targeting + Vector vecToTarget; + QAngle vecAngles; + if (m_hTargetEntity == NULL) + { + vecAngles = GetAbsAngles(); + } + else + { + vecToTarget = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin(); + VectorAngles(vecToTarget, vecAngles); + } + AngleVectors(vecAngles, &vForward, &vRight, &vUp); +#endif // JasonM - unimplemented Assert (0); @@ -204,11 +735,19 @@ void C_EnvProjectedTexture::UpdateLight( bool bForceUpdate ) } else { +#ifndef MAPBASE if ( m_hTargetEntity != NULL || bForceUpdate == true ) { g_pClientShadowMgr->UpdateFlashlightState( m_LightHandle, state ); } +#else + g_pClientShadowMgr->UpdateFlashlightState( m_LightHandle, state ); + m_bForceUpdate = false; +#endif } +#ifdef MAPBASE + } +#endif if( m_bLightOnlyTarget ) { @@ -221,16 +760,29 @@ void C_EnvProjectedTexture::UpdateLight( bool bForceUpdate ) g_pClientShadowMgr->SetFlashlightLightWorld( m_LightHandle, m_bLightWorld ); +#ifndef MAPBASE if ( bForceUpdate == false ) { g_pClientShadowMgr->UpdateProjectedTexture( m_LightHandle, true ); } +#else + if ( !m_bForceUpdate ) + { + g_pClientShadowMgr->UpdateProjectedTexture( m_LightHandle, true ); + } +#endif } void C_EnvProjectedTexture::Simulate( void ) { +#ifndef MAPBASE UpdateLight( false ); +#else + UpdateLight(); +#endif BaseClass::Simulate(); } +#endif + diff --git a/mp/src/game/client/c_func_dust.cpp b/mp/src/game/client/c_func_dust.cpp index 94f114e4..c3c3d79d 100644 --- a/mp/src/game/client/c_func_dust.cpp +++ b/mp/src/game/client/c_func_dust.cpp @@ -88,8 +88,9 @@ void CDustEffect::RenderParticles( CParticleRenderIterator *pIterator ) void CDustEffect::SimulateParticles( CParticleSimulateIterator *pIterator ) { Vector vecWind; +#ifndef MAPBASE GetWindspeedAtTime( gpGlobals->curtime, vecWind ); - +#endif CFuncDustParticle *pParticle = (CFuncDustParticle*)pIterator->GetFirst(); while ( pParticle ) @@ -105,6 +106,9 @@ void CDustEffect::SimulateParticles( CParticleSimulateIterator *pIterator ) } else { +#ifdef MAPBASE + vecWind = GetWindspeedAtLocation( pParticle->m_Pos ); +#endif for ( int i = 0 ; i < 2 ; i++ ) { if ( pParticle->m_vVelocity[i] < vecWind[i] ) diff --git a/mp/src/game/client/c_func_lod.cpp b/mp/src/game/client/c_func_lod.cpp index 766cced3..89998824 100644 --- a/mp/src/game/client/c_func_lod.cpp +++ b/mp/src/game/client/c_func_lod.cpp @@ -30,6 +30,9 @@ public: // These are documented in the server-side entity. public: float m_fDisappearDist; +#ifdef MAPBASE + float m_fDisappearMaxDist; +#endif }; @@ -43,6 +46,9 @@ ConVar lod_TransitionDist("lod_TransitionDist", "800"); // Datatable.. IMPLEMENT_CLIENTCLASS_DT(C_Func_LOD, DT_Func_LOD, CFunc_LOD) RecvPropFloat(RECVINFO(m_fDisappearDist)), +#ifdef MAPBASE + RecvPropFloat(RECVINFO(m_fDisappearMaxDist)), +#endif END_RECV_TABLE() @@ -54,6 +60,9 @@ END_RECV_TABLE() C_Func_LOD::C_Func_LOD() { m_fDisappearDist = 5000.0f; +#ifdef MAPBASE + m_fDisappearMaxDist = 0.0f; +#endif } //----------------------------------------------------------------------------- @@ -61,7 +70,11 @@ C_Func_LOD::C_Func_LOD() //----------------------------------------------------------------------------- unsigned char C_Func_LOD::GetClientSideFade() { +#ifdef MAPBASE + return UTIL_ComputeEntityFade( this, m_fDisappearDist, m_fDisappearDist + (m_fDisappearMaxDist != 0 ? m_fDisappearMaxDist : lod_TransitionDist.GetFloat()), 1.0f ); +#else return UTIL_ComputeEntityFade( this, m_fDisappearDist, m_fDisappearDist + lod_TransitionDist.GetFloat(), 1.0f ); +#endif } diff --git a/mp/src/game/client/c_func_reflective_glass.cpp b/mp/src/game/client/c_func_reflective_glass.cpp index 994d62ba..4e3040d3 100644 --- a/mp/src/game/client/c_func_reflective_glass.cpp +++ b/mp/src/game/client/c_func_reflective_glass.cpp @@ -6,6 +6,9 @@ //===========================================================================// #include "cbase.h" #include "view_shared.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -23,10 +26,27 @@ public: virtual bool ShouldDraw(); +#ifdef MAPBASE + virtual void OnDataChanged( DataUpdateType_t type ); + ITexture *ReflectionRenderTarget(); + ITexture *RefractionRenderTarget(); + + char m_iszReflectRenderTarget[64]; + char m_iszRefractRenderTarget[64]; + ITexture *m_pReflectRenderTarget; + ITexture *m_pRefractRenderTarget; +#endif + C_FuncReflectiveGlass *m_pNext; }; IMPLEMENT_CLIENTCLASS_DT( C_FuncReflectiveGlass, DT_FuncReflectiveGlass, CFuncReflectiveGlass ) + +#ifdef MAPBASE + RecvPropString( RECVINFO( m_iszReflectRenderTarget ) ), + RecvPropString( RECVINFO( m_iszRefractRenderTarget ) ), +#endif + END_RECV_TABLE() @@ -47,6 +67,11 @@ C_FuncReflectiveGlass* GetReflectiveGlassList() //----------------------------------------------------------------------------- C_FuncReflectiveGlass::C_FuncReflectiveGlass() { +#ifdef MAPBASE + m_iszReflectRenderTarget[0] = '\0'; + m_iszRefractRenderTarget[0] = '\0'; +#endif + g_ReflectiveGlassList.Insert( this ); } @@ -114,5 +139,111 @@ bool IsReflectiveGlassInView( const CViewSetup& view, cplane_t &plane ) return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Iterates through reflective glass instead of just picking one +//----------------------------------------------------------------------------- +C_BaseEntity *NextReflectiveGlass( C_BaseEntity *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum, ITexture **pRenderTargets ) +{ + // Early out if no cameras + C_FuncReflectiveGlass *pReflectiveGlass = NULL; + if (!pStart) + pReflectiveGlass = GetReflectiveGlassList(); + else + pReflectiveGlass = ((C_FuncReflectiveGlass*)pStart)->m_pNext; + + cplane_t localPlane; + Vector vecOrigin, vecWorld, vecDelta; + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) + { + if ( pReflectiveGlass->IsDormant() ) + continue; + + if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) + continue; + + Vector vecMins, vecMaxs; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + if ( R_CullBox( vecMins, vecMaxs, frustum ) ) + continue; + + const model_t *pModel = pReflectiveGlass->GetModel(); + const matrix3x4_t& mat = pReflectiveGlass->EntityToWorldTransform(); + + int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); + for ( int i = 0; i < nCount; ++i ) + { + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + + MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space + VectorTransform( vecOrigin, mat, vecWorld ); + + if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + continue; + + VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull + if ( vecDelta.Dot( plane.normal ) >= 0 ) + continue; + + if (pRenderTargets != NULL) + { + pRenderTargets[0] = pReflectiveGlass->ReflectionRenderTarget(); + pRenderTargets[1] = pReflectiveGlass->RefractionRenderTarget(); + } + + return pReflectiveGlass; + } + } + + return NULL; +} + +void C_FuncReflectiveGlass::OnDataChanged( DataUpdateType_t type ) +{ + // Reset render textures + m_pReflectRenderTarget = NULL; + m_pRefractRenderTarget = NULL; + + return BaseClass::OnDataChanged( type ); +} + +ITexture *C_FuncReflectiveGlass::ReflectionRenderTarget() +{ + if (m_iszReflectRenderTarget[0] != '\0') + { + if (!m_pReflectRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pReflectRenderTarget = materials->FindTexture( m_iszReflectRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pReflectRenderTarget) + return m_pReflectRenderTarget; + } + + return NULL; + //return GetWaterReflectionTexture(); +} + +ITexture *C_FuncReflectiveGlass::RefractionRenderTarget() +{ + if (m_iszRefractRenderTarget[0] != '\0') + { + if (!m_pRefractRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pRefractRenderTarget = materials->FindTexture( m_iszRefractRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pRefractRenderTarget) + return m_pRefractRenderTarget; + } + + return NULL; + //return GetWaterRefractionTexture(); +} +#endif + diff --git a/mp/src/game/client/c_func_reflective_glass.h b/mp/src/game/client/c_func_reflective_glass.h index 48e1491e..ab52ab88 100644 --- a/mp/src/game/client/c_func_reflective_glass.h +++ b/mp/src/game/client/c_func_reflective_glass.h @@ -21,6 +21,11 @@ class CViewSetup; //----------------------------------------------------------------------------- bool IsReflectiveGlassInView( const CViewSetup& view, cplane_t &plane ); +#ifdef MAPBASE +C_BaseEntity *NextReflectiveGlass( C_BaseEntity *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum, ITexture **pRenderTargets = NULL ); +#endif + #endif // C_FUNC_REFLECTIVE_GLASS diff --git a/mp/src/game/client/c_gameinstructor.cpp b/mp/src/game/client/c_gameinstructor.cpp new file mode 100644 index 00000000..ff5f0e53 --- /dev/null +++ b/mp/src/game/client/c_gameinstructor.cpp @@ -0,0 +1,1312 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler implementations for instruction players how to play +// +//=============================================================================// + +#include "cbase.h" + +#include "c_gameinstructor.h" +#include "c_baselesson.h" +#include "filesystem.h" +#include "vprof.h" +#include "ixboxsystem.h" +#include "tier0/icommandline.h" +#include "iclientmode.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Configuration +//========================================================= + +#define MOD_DIR "MOD" +#define GAMEINSTRUCTOR_SCRIPT_FILE "scripts/instructor_lessons.txt" +#define GAMEINSTRUCTOR_MOD_SCRIPT_FILE "scripts/mod_lessons.txt" + +// Game instructor auto game system instantiation +C_GameInstructor g_GameInstructor; +C_GameInstructor &GetGameInstructor() +{ + return g_GameInstructor; +} + +void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ); +void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ); + +extern ConVar sv_gameinstructor_disable; + +//========================================================= +// Comandos de consola +//========================================================= + +ConVar gameinstructor_verbose("gameinstructor_verbose", "0", FCVAR_CHEAT, "Set to 1 for standard debugging or 2 (in combo with gameinstructor_verbose_lesson) to show update actions."); +ConVar gameinstructor_verbose_lesson("gameinstructor_verbose_lesson", "", FCVAR_CHEAT, "Display more verbose information for lessons have this name." ); +ConVar gameinstructor_find_errors("gameinstructor_find_errors", "1", FCVAR_CHEAT, "Set to 1 and the game instructor will run EVERY scripted command to uncover errors." ); + +ConVar gameinstructor_enable( "gameinstructor_enable", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Display in game lessons that teach new players.", GameInstructorEnable_ChangeCallback ); +ConVar gameinstructor_start_sound_cooldown( "gameinstructor_start_sound_cooldown", "4.0", FCVAR_NONE, "Number of seconds forced between similar lesson start sounds." ); + +ConVar sv_gameinstructor_disable( "sv_gameinstructor_disable", "0", FCVAR_REPLICATED, "Force all clients to disable their game instructors.", SVGameInstructorDisable_ChangeCallback ); + +//========================================================= +// Activa o Desactiva el Instructor del lado del cliente. +//========================================================= +void EnableDisableInstructor() +{ + bool bEnabled = ( !sv_gameinstructor_disable.GetBool() && gameinstructor_enable.GetBool() ); + + // Game instructor has been enabled, so init it! + if ( bEnabled ) + GetGameInstructor().Init(); + + // Game instructor has been disabled, so shut it down! + else + GetGameInstructor().Shutdown(); +} + +//========================================================= +//========================================================= +void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if ( ( flOldValue != 0.0f ) != gameinstructor_enable.GetBool() ) + EnableDisableInstructor(); +} + +//========================================================= +//========================================================= +void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if ( !engine ) + return; + + EnableDisableInstructor(); +} + + +//========================================================= +// Initialize the Instructor +//========================================================= +bool C_GameInstructor::Init() +{ +// if ( &GetGameInstructor() == this ) + // return true; + + // Instructor deactivated, don't initialize. + if ( !gameinstructor_enable.GetBool() || sv_gameinstructor_disable.GetBool() ) + return true; + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing...\n" ); + } + + m_bNoDraw = false; + m_bHiddenDueToOtherElements = false; + + m_iCurrentPriority = 0; + m_hLastSpectatedPlayer = NULL; + m_bSpectatedPlayerChanged = false; + + m_szPreviousStartSound[0] = '\0'; + m_fNextStartSoundTime = 0; + + ReadLessonsFromFile( GAMEINSTRUCTOR_MOD_SCRIPT_FILE ); + ReadLessonsFromFile( GAMEINSTRUCTOR_SCRIPT_FILE ); + + InitLessonPrerequisites(); + ReadSaveData(); + + ListenForGameEvent("gameinstructor_draw"); + ListenForGameEvent("gameinstructor_nodraw"); + + ListenForGameEvent("round_end"); + ListenForGameEvent("round_start"); + ListenForGameEvent("player_death"); + ListenForGameEvent("player_team"); + ListenForGameEvent("player_disconnect"); + ListenForGameEvent("map_transition"); + ListenForGameEvent("game_newmap"); + ListenForGameEvent("set_instructor_group_enabled"); + + EvaluateLessonsForGameRules(); + return true; +} + +//========================================================= +// Shut down the instructor +//========================================================= +void C_GameInstructor::Shutdown() +{ + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Shutting down...\n" ); + } + + CloseAllOpenOpportunities(); + WriteSaveData(); + + // Removemos todas las lecciones. + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + if ( m_Lessons[ i ] ) + { + m_Lessons[ i ]->StopListeningForAllEvents(); + delete m_Lessons[ i ]; + m_Lessons[ i ] = NULL; + } + } + + m_Lessons.RemoveAll(); + m_LessonGroupConVarToggles.RemoveAll(); + + // Paramos de escuchar eventos. + StopListeningForAllEvents(); +} + +//========================================================= +//========================================================= +void C_GameInstructor::UpdateHiddenByOtherElements() +{ + //bool bHidden = Mod_HiddenByOtherElements(); + bool bHidden = false; + + if ( bHidden && !m_bHiddenDueToOtherElements ) + StopAllLessons(); + + m_bHiddenDueToOtherElements = bHidden; +} + +//========================================================= +//========================================================= +void C_GameInstructor::Update( float frametime ) +{ + VPROF_BUDGET( "C_GameInstructor::Update", "GameInstructor" ); + + UpdateHiddenByOtherElements(); + + // Instructor deactivated. + if ( !gameinstructor_enable.GetBool() || m_bNoDraw || m_bHiddenDueToOtherElements ) + return; + + if ( gameinstructor_find_errors.GetBool() ) + { + FindErrors(); + gameinstructor_find_errors.SetValue(0); + } + + if ( IsConsole() ) + { + // On X360 we want to save when they're not connected + // They aren't in game + if ( !engine->IsInGame() ) + WriteSaveData(); + else + { + const char *levelName = engine->GetLevelName(); + + // The are in game, but it's a background map + if ( levelName && levelName[0] && engine->IsLevelMainMenuBackground() ) + WriteSaveData(); + } + } + + if ( m_bSpectatedPlayerChanged ) + { + // Safe spot to clean out stale lessons if spectator changed + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Spectated player changed...\n" ); + } + + CloseAllOpenOpportunities(); + m_bSpectatedPlayerChanged = false; + } + + // Loop through all the lesson roots and reset their active status + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_ACTIVE ) + pRootLesson->SetInstanceActive(false); + } + + int iCurrentPriority = 0; + + // Loop through all the open lessons + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + + // This opportunity has closed + if ( !pLesson->IsOpenOpportunity() || pLesson->IsTimedOut() ) + { + CloseOpportunity( pLesson ); + continue; + } + + // Lesson should be displayed, so it can affect priority + CBaseLesson *pRootLesson = pLesson->GetRoot(); + bool bShouldDisplay = pLesson->ShouldDisplay(); + bool bIsLocked = pLesson->IsLocked(); + + if ( ( bShouldDisplay || bIsLocked ) && + ( pLesson->GetPriority() >= m_iCurrentPriority || pLesson->NoPriority() || bIsLocked ) && + ( pRootLesson && ( pRootLesson->InstanceType() != LESSON_INSTANCE_SINGLE_ACTIVE || !pRootLesson->IsInstanceActive() ) ) ) + { + // Lesson is at the highest priority level, isn't violating instance rules, and has met all the prerequisites + if ( UpdateActiveLesson( pLesson, pRootLesson ) || pRootLesson->IsLearned() ) + { + // Lesson is active + if ( pLesson->IsVisible() || pRootLesson->IsLearned() ) + { + pRootLesson->SetInstanceActive( true ); + + // This active or learned lesson has the highest priority so far + if ( iCurrentPriority < pLesson->GetPriority() && !pLesson->NoPriority() ) + iCurrentPriority = pLesson->GetPriority(); + } + } + else + { + // On second thought, this shouldn't have been displayed + bShouldDisplay = false; + } + } + else + { + // Lesson shouldn't be displayed right now + UpdateInactiveLesson( pLesson ); + } + } + + // Set the priority for next frame + if ( gameinstructor_verbose.GetInt() > 1 && m_iCurrentPriority != iCurrentPriority ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Priority changed from " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%i ", m_iCurrentPriority ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "to " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "%i", iCurrentPriority ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + m_iCurrentPriority = iCurrentPriority; +} + +//========================================================= +//========================================================= +void C_GameInstructor::FireGameEvent( IGameEvent *event ) +{ + VPROF_BUDGET( "C_GameInstructor::FireGameEvent", "GameInstructor" ); + const char *name = event->GetName(); + + if ( Q_strcmp( name, "gameinstructor_draw" ) == 0 ) + { + if ( m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to draw...\n" ); + } + + m_bNoDraw = false; + } + } + else if ( Q_strcmp( name, "gameinstructor_nodraw" ) == 0 ) + { + if ( !m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to not draw...\n" ); + } + + m_bNoDraw = true; + StopAllLessons(); + } + } + else if ( Q_strcmp( name, "round_end" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round ended...\n" ); + } + + CloseAllOpenOpportunities(); + + if ( IsPC() ) + { + // Good place to backup our counts + WriteSaveData(); + } + } + else if ( Q_strcmp( name, "round_start" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round started...\n" ); + } + + CloseAllOpenOpportunities(); + + EvaluateLessonsForGameRules(); + } + else if ( Q_strcmp( name, "player_death" ) == 0 ) + { + #if !defined(NO_STEAM) && defined(USE_CEG) + Steamworks_TestSecret(); + Steamworks_SelfCheck(); + #endif + + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + + if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player died...\n" ); + } + + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + if ( !pRootLesson->CanOpenWhenDead() ) + CloseOpportunity( pLesson ); + } + } + } + else if ( Q_strcmp( name, "player_team" ) == 0 ) + { + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + + if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) && + ( event->GetInt( "team" ) != event->GetInt( "oldteam" ) || event->GetBool( "disconnect" ) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player changed team (or disconnected)...\n" ); + } + + CloseAllOpenOpportunities(); + } + + EvaluateLessonsForGameRules(); + } + else if ( Q_strcmp( name, "player_disconnect" ) == 0 ) + { + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player disconnected...\n" ); + } + + CloseAllOpenOpportunities(); + } + } + else if ( Q_strcmp( name, "map_transition" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Map transition...\n" ); + } + + CloseAllOpenOpportunities(); + + if ( m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "[INSTRUCTOR]: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" ); + } + + m_bNoDraw = false; + } + + if ( IsPC() ) + { + // Good place to backup our counts + WriteSaveData(); + } + } + else if ( Q_strcmp( name, "game_newmap" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "New map...\n" ); + } + + CloseAllOpenOpportunities(); + + if ( m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "[INSTRUCTOR]: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" ); + } + + m_bNoDraw = false; + } + + if ( IsPC() ) + { + // Good place to backup our counts + WriteSaveData(); + } + } + + else if ( Q_strcmp( name, "set_instructor_group_enabled" ) == 0 ) + { + const char *pszGroup = event->GetString( "group" ); + bool bEnabled = event->GetInt( "enabled" ) != 0; + + if ( pszGroup && pszGroup[0] ) + SetLessonGroupEnabled(pszGroup, bEnabled); + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::DefineLesson( CBaseLesson *pLesson ) +{ + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "defined.\n" ); + } + + m_Lessons.AddToTail( pLesson ); +} + +//========================================================= +//========================================================= +const CBaseLesson * C_GameInstructor::GetLesson( const char *pchLessonName ) +{ + return GetLesson_Internal( pchLessonName ); +} + +//========================================================= +//========================================================= +bool C_GameInstructor::IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const +{ + for ( int i = 0; i < m_OpenOpportunities.Count(); ++i ) + { + CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ]; + + if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() ) + return true; + } + + return false; +} + +//========================================================= +//========================================================= +bool C_GameInstructor::ReadSaveData() +{ + // for external playtests, don't ever read in persisted instructor state, always start fresh + if ( CommandLine()->FindParm( "-playtest" ) ) + return true; + + if ( m_bHasLoadedSaveData ) + return true; + + // Always reset state first in case storage device + // was declined or ends up in faulty state + ResetDisplaysAndSuccesses(); + + m_bHasLoadedSaveData = true; + +#ifdef _X360 + DevMsg( "Read Game Instructor for splitscreen slot %d\n", m_nSplitScreenSlot ); + + if ( m_nSplitScreenSlot < 0 ) + return false; + + if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() ) + return false; + + int iController = XBX_GetUserId( m_nSplitScreenSlot ); + + if ( iController < 0 || XBX_GetUserIsGuest( iController ) ) + { + // Can't read data for guests + return false; + } + + DWORD nStorageDevice = XBX_GetStorageDeviceId( iController ); + if ( !XBX_DescribeStorageDevice( nStorageDevice ) ) + return false; +#endif + + char szFilename[_MAX_PATH]; + +#ifdef _X360 + if ( IsX360() ) + { + XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) ); + int nLen = strlen( szFilename ); + Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" ); + } + else +#endif + { + Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" ); + } + + KeyValues *data = new KeyValues( "Game Instructor Counts" ); + KeyValues::AutoDelete autoDelete(data); + + if ( data->LoadFromFile( g_pFullFileSystem, szFilename, NULL ) ) + { + int nVersion = 0; + + for ( KeyValues *pKey = data->GetFirstSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() ) + { + CBaseLesson *pLesson = GetLesson_Internal( pKey->GetName() ); + + if ( pLesson ) + { + pLesson->SetDisplayCount( pKey->GetInt( "display", 0 ) ); + pLesson->SetSuccessCount( pKey->GetInt( "success", 0 ) ); + + if ( Q_strcmp( pKey->GetName(), "version number" ) == 0 ) + { + nVersion = pLesson->GetSuccessCount(); + } + } + } + + CBaseLesson *pLessonVersionNumber = GetLesson_Internal( "version number" ); + if ( pLessonVersionNumber && !pLessonVersionNumber->IsLearned() ) + { + ResetDisplaysAndSuccesses(); + pLessonVersionNumber->SetSuccessCount( pLessonVersionNumber->GetSuccessLimit() ); + m_bDirtySaveData = true; + } + + + return true; + } + + // Couldn't read from the file + return false; +} + +//========================================================= +//========================================================= +bool C_GameInstructor::WriteSaveData() +{ + if ( engine->IsPlayingDemo() ) + return false; + + if ( !m_bDirtySaveData ) + return true; + +#ifdef _X360 + float flPlatTime = Plat_FloatTime(); + + static ConVarRef host_write_last_time( "host_write_last_time" ); + if ( host_write_last_time.IsValid() ) + { + float flTimeSinceLastWrite = flPlatTime - host_write_last_time.GetFloat(); + if ( flTimeSinceLastWrite < 3.5f ) + { + // Prevent writing to the same storage device twice in less than 3 second succession for TCR success! + // This happens after leaving a game in splitscreen. + //DevMsg( "Waiting to write Game Instructor for splitscreen slot %d... (%.1f seconds remain)\n", m_nSplitScreenSlot, 3.5f - flTimeSinceLastWrite ); + return false; + } + } +#endif + + // Always mark as clean state to avoid re-entry on + // subsequent frames when storage device might be + // in a yet-unmounted state. + m_bDirtySaveData = false; + +#ifdef _X360 + DevMsg( "Write Game Instructor for splitscreen slot %d at time: %.1f\n", m_nSplitScreenSlot, flPlatTime ); + + if ( m_nSplitScreenSlot < 0 ) + return false; + + if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() ) + return false; + + int iController = XBX_GetUserId( m_nSplitScreenSlot ); + + if ( iController < 0 || XBX_GetUserIsGuest( iController ) ) + { + // Can't save data for guests + return false; + } + + DWORD nStorageDevice = XBX_GetStorageDeviceId( iController ); + if ( !XBX_DescribeStorageDevice( nStorageDevice ) ) + return false; +#endif + + // Build key value data to save + KeyValues *data = new KeyValues( "Game Instructor Counts" ); + KeyValues::AutoDelete autoDelete(data); + + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + CBaseLesson *pLesson = m_Lessons[i]; + + int iDisplayCount = pLesson->GetDisplayCount(); + int iSuccessCount = pLesson->GetSuccessCount(); + + if ( iDisplayCount || iSuccessCount ) + { + // We've got some data worth saving + KeyValues *pKVData = new KeyValues( pLesson->GetName() ); + + if ( iDisplayCount ) + pKVData->SetInt( "display", iDisplayCount ); + + if ( iSuccessCount ) + pKVData->SetInt( "success", iSuccessCount ); + + data->AddSubKey( pKVData ); + } + } + + // Save it! + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + data->RecursiveSaveToFile( buf, 0 ); + + char szFilename[_MAX_PATH]; + +#ifdef _X360 + if ( IsX360() ) + { + XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) ); + int nLen = strlen( szFilename ); + Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" ); + } + else +#endif + { + Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" ); + filesystem->CreateDirHierarchy( "save", "MOD" ); + } + + bool bWriteSuccess = filesystem->WriteFile( szFilename, MOD_DIR, buf ); + +#ifdef _X360 + if ( xboxsystem ) + { + xboxsystem->FinishContainerWrites( iController ); + } +#endif + + return bWriteSuccess; +} + +//========================================================= +//========================================================= +void C_GameInstructor::RefreshDisplaysAndSuccesses() +{ + m_bHasLoadedSaveData = false; + ReadSaveData(); +} + +//========================================================= +//========================================================= +void C_GameInstructor::ResetDisplaysAndSuccesses() +{ + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Reset all lesson display and success counts.\n" ); + } + + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + m_Lessons[ i ]->ResetDisplaysAndSuccesses(); + } + + m_bDirtySaveData = false; +} + +//========================================================= +//========================================================= +void C_GameInstructor::MarkDisplayed( const char *pchLessonName ) +{ + CBaseLesson *pLesson = GetLesson_Internal(pchLessonName); + + if ( !pLesson ) + return; + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as displayed.\n" ); + } + + if ( pLesson->IncDisplayCount() ) + m_bDirtySaveData = true; +} + +//========================================================= +//========================================================= +void C_GameInstructor::MarkSucceeded(const char *pchLessonName) +{ + CBaseLesson *pLesson = GetLesson_Internal(pchLessonName); + + if ( !pLesson ) + return; + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as succeeded.\n" ); + } + + if ( pLesson->IncSuccessCount() ) + m_bDirtySaveData = true; +} + +//========================================================= +//========================================================= +void C_GameInstructor::PlaySound( const char *pchSoundName ) +{ + // emit alert sound + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( pLocalPlayer ) + { + // Local player exists + if ( pchSoundName[ 0 ] != '\0' && Q_strcmp( m_szPreviousStartSound, pchSoundName ) != 0 ) + { + Q_strcpy( m_szPreviousStartSound, pchSoundName ); + m_fNextStartSoundTime = 0.0f; + } + + if ( gpGlobals->curtime >= m_fNextStartSoundTime && pchSoundName[ 0 ] != '\0' ) + { + // A sound was specified, so play it! + pLocalPlayer->EmitSound( pchSoundName ); + m_fNextStartSoundTime = gpGlobals->curtime + gameinstructor_start_sound_cooldown.GetFloat(); + } + } +} + +//========================================================= +//========================================================= +bool C_GameInstructor::OpenOpportunity( CBaseLesson *pLesson ) +{ + // Get the root lesson + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + if ( !pRootLesson ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because root lesson could not be found).\n" ); + } + + delete pLesson; + return false; + } + + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + + if ( !pRootLesson->CanOpenWhenDead() && ( !pLocalPlayer || !pLocalPlayer->IsAlive() ) ) + { + // If the player is dead don't allow lessons that can't be opened when dead + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because player is dead and can_open_when_dead not set).\n" ); + } + + delete pLesson; + return false; + } + + if ( !pRootLesson->PrerequisitesHaveBeenMet() ) + { + // If the prereqs haven't been met, don't open it + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because prereqs haven't been met).\n" ); + } + + delete pLesson; + return false; + } + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_FIXED_REPLACE ) + { + CBaseLesson *pLessonToReplace = NULL; + CBaseLesson *pLastReplacableLesson = NULL; + + int iInstanceCount = 0; + + // Check how many are already open + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ]; + + if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() && + pOpenOpportunity->GetReplaceKeySymbol() == pLesson->GetReplaceKeySymbol() ) + { + iInstanceCount++; + + if ( pRootLesson->ShouldReplaceOnlyWhenStopped() ) + { + if ( !pOpenOpportunity->IsInstructing() ) + { + pLastReplacableLesson = pOpenOpportunity; + } + } + else + { + pLastReplacableLesson = pOpenOpportunity; + } + + if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() ) + { + pLessonToReplace = pLastReplacableLesson; + break; + } + } + } + + if ( pLessonToReplace ) + { + // Take the place of the previous instance + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "replacing open lesson of same type.\n" ); + } + + pLesson->TakePlaceOf( pLessonToReplace ); + CloseOpportunity( pLessonToReplace ); + } + else if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() ) + { + // Don't add another lesson of this type + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (there is too many started lessons of this type).\n" ); + } + + delete pLesson; + return false; + } + } + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "opened.\n" ); + } + + m_OpenOpportunities.AddToTail( pLesson ); + + return true; +} + +//========================================================= +//========================================================= +void C_GameInstructor::DumpOpenOpportunities() +{ + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open lessons...\n" ); + + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + Color color; + + if ( pLesson->IsInstructing() ) + { + // Green + color = CBaseLesson::m_rgbaVerboseOpen; + } + else if ( pRootLesson->IsLearned() && pLesson->GetPriority() >= m_iCurrentPriority ) + { + // Yellow + color = CBaseLesson::m_rgbaVerboseSuccess; + } + else + { + // Red + color = CBaseLesson::m_rgbaVerboseClose; + } + + ConColorMsg( color, "\t%s\n", pLesson->GetName() ); + } +} + +//========================================================= +//========================================================= +KeyValues * C_GameInstructor::GetScriptKeys() +{ + return m_pScriptKeys; +} + +//========================================================= +//========================================================= +C_BasePlayer * C_GameInstructor::GetLocalPlayer() +{ + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + // If we're not a developer, don't do the special spectator hook ups + if ( !developer.GetBool() ) + return pLocalPlayer; + + // If there is no local player and we're not spectating, just return that + if ( !pLocalPlayer || pLocalPlayer->GetTeamNumber() != TEAM_SPECTATOR ) + return pLocalPlayer; + + // We're purely a spectator let's get lessons of the person we're spectating + C_BasePlayer *pSpectatedPlayer = NULL; + + if ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE ) + pSpectatedPlayer = ToBasePlayer( pLocalPlayer->GetObserverTarget() ); + + if ( m_hLastSpectatedPlayer != pSpectatedPlayer ) + { + // We're spectating someone new! Close all the stale lessons! + m_bSpectatedPlayerChanged = true; + m_hLastSpectatedPlayer = pSpectatedPlayer; + } + + return pSpectatedPlayer; +} + +//========================================================= +//========================================================= +void C_GameInstructor::EvaluateLessonsForGameRules() +{ + // Enable everything by default + for ( int i = 0; i < m_Lessons.Count(); ++i ) + m_Lessons[ i ]->SetEnabled(true); + + // Then see if we should disable anything + for ( int nConVar = 0; nConVar < m_LessonGroupConVarToggles.Count(); ++nConVar ) + { + LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[ nConVar ]); + + if ( pLessonGroupConVarToggle->var.IsValid() ) + { + if ( pLessonGroupConVarToggle->var.GetBool() ) + SetLessonGroupEnabled( pLessonGroupConVarToggle->szLessonGroupName, false ); + } + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::SetLessonGroupEnabled( const char *pszGroup, bool bEnabled ) +{ + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + if ( !Q_stricmp(pszGroup, m_Lessons[i]->GetGroup()) ) + m_Lessons[i]->SetEnabled( bEnabled ); + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::FindErrors() +{ + // Loop through all the lesson and run all their scripted actions + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + CScriptedIconLesson *pLesson = dynamic_cast( m_Lessons[ i ] ); + if ( pLesson ) + { + // Process all open events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOpenEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetOpenEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all close events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetCloseEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetCloseEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all success events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetSuccessEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetSuccessEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all on open events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOnOpenEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetOnOpenEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetUpdateEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetUpdateEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + } + } +} + +//========================================================= +//========================================================= +bool C_GameInstructor::UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson ) +{ + VPROF_BUDGET( "C_GameInstructor::UpdateActiveLesson", "GameInstructor" ); + + bool bIsOpen = pLesson->IsInstructing(); + + if ( !bIsOpen && !pRootLesson->IsLearned() ) + { + pLesson->SetStartTime(); + pLesson->Start(); + + // Check to see if it successfully started + bIsOpen = ( pLesson->IsOpenOpportunity() && pLesson->ShouldDisplay() ); + + if ( bIsOpen ) + { + // Lesson hasn't been started and hasn't been learned + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Started lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + else + { + pLesson->Stop(); + pLesson->ResetStartTime(); + } + } + + if ( bIsOpen ) + { + // Update the running lesson + pLesson->Update(); + return true; + } + else + { + pLesson->UpdateInactive(); + return false; + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::UpdateInactiveLesson( CBaseLesson *pLesson ) +{ + VPROF_BUDGET( "C_GameInstructor::UpdateInactiveLesson", "GameInstructor" ); + + if ( pLesson->IsInstructing() ) + { + // Lesson hasn't been stopped + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Stopped lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + pLesson->Stop(); + pLesson->ResetStartTime(); + } + + pLesson->UpdateInactive(); +} + +//========================================================= +//========================================================= +CBaseLesson * C_GameInstructor::GetLesson_Internal( const char *pchLessonName ) +{ + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + CBaseLesson *pLesson = m_Lessons[ i ]; + + if ( Q_strcmp( pLesson->GetName(), pchLessonName ) == 0 ) + { + return pLesson; + } + } + + return NULL; +} + +//========================================================= +//========================================================= +void C_GameInstructor::StopAllLessons() +{ + // Stop all the current lessons + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + UpdateInactiveLesson( pLesson ); + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::CloseAllOpenOpportunities() +{ + // Clear out all the open opportunities + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CloseOpportunity( pLesson ); + } + + Assert( m_OpenOpportunities.Count() == 0 ); +} + +//========================================================= +//========================================================= +void C_GameInstructor::CloseOpportunity( CBaseLesson *pLesson ) +{ + UpdateInactiveLesson( pLesson ); + + if ( pLesson->WasDisplayed() ) + MarkDisplayed( pLesson->GetName() ); + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "closed for reason: " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%s\n", pLesson->GetCloseReason() ); + } + + pLesson->StopListeningForAllEvents(); + + m_OpenOpportunities.FindAndRemove( pLesson ); + delete pLesson; +} + +//========================================================= +//========================================================= +void C_GameInstructor::ReadLessonsFromFile( const char *pchFileName ) +{ + // Static init function + CScriptedIconLesson::PreReadLessonsFromFile(); + MEM_ALLOC_CREDIT(); + + KeyValues *pLessonKeys = new KeyValues("instructor_lessons"); + KeyValues::AutoDelete autoDelete(pLessonKeys); + + pLessonKeys->LoadFromFile(g_pFullFileSystem, pchFileName, NULL); + + for ( m_pScriptKeys = pLessonKeys->GetFirstTrueSubKey(); m_pScriptKeys; m_pScriptKeys = m_pScriptKeys->GetNextTrueSubKey() ) + { + if ( Q_stricmp(m_pScriptKeys->GetName(), "GroupConVarToggle") == 0 ) + { + // Add convar group toggler to the list + int nLessonGroupConVarToggle = m_LessonGroupConVarToggles.AddToTail( LessonGroupConVarToggle_t( m_pScriptKeys->GetString( "convar" ) ) ); + LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[nLessonGroupConVarToggle]); + + Q_strcpy( pLessonGroupConVarToggle->szLessonGroupName, m_pScriptKeys->GetString("group") ); + continue; + } + + // Ensure that lessons aren't added twice + if ( GetLesson_Internal(m_pScriptKeys->GetName()) ) + { + DevWarning("Lesson \"%s\" defined twice!\n", m_pScriptKeys->GetName()); + continue; + } + + CScriptedIconLesson *pNewLesson = new CScriptedIconLesson(m_pScriptKeys->GetName(), false, false); + GetGameInstructor().DefineLesson(pNewLesson); + } + + m_pScriptKeys = NULL; +} + +//========================================================= +//========================================================= +void C_GameInstructor::InitLessonPrerequisites() +{ + for ( int i = 0; i < m_Lessons.Count(); ++i ) + m_Lessons[ i ]->InitPrerequisites(); +} + +//========================================================= +// Commands +//========================================================= + +CON_COMMAND_F( gameinstructor_reload_lessons, "Shuts down all open lessons and reloads them from the script file.", FCVAR_CHEAT ) +{ + GetGameInstructor().Shutdown(); + GetGameInstructor().Init(); +} + +CON_COMMAND_F( gameinstructor_reset_counts, "Resets all display and success counts to zero.", FCVAR_NONE ) +{ + GetGameInstructor().ResetDisplaysAndSuccesses(); +} + +CON_COMMAND_F( gameinstructor_dump_open_lessons, "Gives a list of all currently open lessons.", FCVAR_CHEAT ) +{ + GetGameInstructor().DumpOpenOpportunities(); +} diff --git a/mp/src/game/client/c_gameinstructor.h b/mp/src/game/client/c_gameinstructor.h new file mode 100644 index 00000000..00c97c66 --- /dev/null +++ b/mp/src/game/client/c_gameinstructor.h @@ -0,0 +1,118 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler for instruction players how to play +// +//=============================================================================// + +#ifndef _C_GAMEINSTRUCTOR_H_ +#define _C_GAMEINSTRUCTOR_H_ + + +#include "GameEventListener.h" +#include "vgui_controls/phandle.h" + +class CBaseLesson; + + +struct LessonGroupConVarToggle_t +{ + ConVarRef var; + char szLessonGroupName[ 64 ]; + + LessonGroupConVarToggle_t( const char *pchConVarName ) : + var( pchConVarName ) + { + } +}; + + +class C_GameInstructor : public CAutoGameSystemPerFrame, public CGameEventListener +{ +public: + C_GameInstructor() : CAutoGameSystemPerFrame( "C_GameInstructor" ) + { + m_bHasLoadedSaveData = false; + m_bDirtySaveData = false; + } + + // Methods of IGameSystem + virtual bool Init( void ); + virtual void Shutdown( void ); + virtual void Update( float frametime ); + + void UpdateHiddenByOtherElements( void ); + bool Mod_HiddenByOtherElements( void ); + + virtual void FireGameEvent( IGameEvent *event ); + + void DefineLesson( CBaseLesson *pLesson ); + + const CBaseLesson * GetLesson( const char *pchLessonName ); + bool IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const; + + bool ReadSaveData( void ); + bool WriteSaveData( void ); + void RefreshDisplaysAndSuccesses( void ); + void ResetDisplaysAndSuccesses( void ); + void MarkDisplayed( const char *pchLessonName ); + void MarkSucceeded( const char *pchLessonName ); + + void PlaySound( const char *pchSoundName ); + + bool OpenOpportunity( CBaseLesson *pLesson ); + + void DumpOpenOpportunities( void ); + + KeyValues * GetScriptKeys( void ); + C_BasePlayer * GetLocalPlayer( void ); + + void EvaluateLessonsForGameRules( void ); + void SetLessonGroupEnabled( const char *pszGroup, bool bEnabled ); + + // Mapbase needs this to be public for map-specific file system + void ReadLessonsFromFile( const char *pchFileName ); + +private: + void FindErrors( void ); + + bool UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson ); + void UpdateInactiveLesson( CBaseLesson *pLesson ); + + CBaseLesson * GetLesson_Internal( const char *pchLessonName ); + + void StopAllLessons( void ); + + void CloseAllOpenOpportunities( void ); + void CloseOpportunity( CBaseLesson *pLesson ); + + void InitLessonPrerequisites( void ); + +private: + CUtlVector < CBaseLesson* > m_Lessons; + CUtlVector < CBaseLesson* > m_OpenOpportunities; + + CUtlVector < LessonGroupConVarToggle_t > m_LessonGroupConVarToggles; + + KeyValues *m_pScriptKeys; + + bool m_bNoDraw; + bool m_bHiddenDueToOtherElements; + + int m_iCurrentPriority; + EHANDLE m_hLastSpectatedPlayer; + bool m_bSpectatedPlayerChanged; + + char m_szPreviousStartSound[ 128 ]; + float m_fNextStartSoundTime; + + bool m_bHasLoadedSaveData; + bool m_bDirtySaveData; +}; + +C_GameInstructor &GetGameInstructor(); + +void GameInstructor_Init(); +void GameInstructor_Shutdown(); + + +#endif // _C_GAMEINSTRUCTOR_H_ diff --git a/mp/src/game/client/c_lightglow.cpp b/mp/src/game/client/c_lightglow.cpp index 06f20a6c..501a2612 100644 --- a/mp/src/game/client/c_lightglow.cpp +++ b/mp/src/game/client/c_lightglow.cpp @@ -102,6 +102,10 @@ public: C_LightGlowOverlay m_Glow; float m_flGlowProxySize; + +#ifdef MAPBASE + bool m_bDisabled; +#endif }; static void RecvProxy_HDRColorScale( const CRecvProxyData *pData, void *pStruct, void *pOut ) @@ -123,6 +127,9 @@ IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_LightGlow, DT_LightGlow, CLightGlow ) RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ), RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ), RecvPropFloat(RECVINFO(m_flGlowProxySize)), +#ifdef MAPBASE + RecvPropBool( RECVINFO( m_bDisabled ) ), +#endif RecvPropFloat("HDRColorScale", 0, SIZEOF_IGNORE, 0, RecvProxy_HDRColorScale), END_RECV_TABLE() @@ -202,7 +209,11 @@ void C_LightGlow::OnDataChanged( DataUpdateType_t updateType ) void C_LightGlow::ClientThink( void ) { Vector mins = GetAbsOrigin(); +#ifdef MAPBASE + if ( engine->IsBoxVisible( mins, mins ) && !m_bDisabled ) +#else if ( engine->IsBoxVisible( mins, mins ) ) +#endif { m_Glow.Activate(); } diff --git a/mp/src/game/client/c_particle_system.cpp b/mp/src/game/client/c_particle_system.cpp index e0aee808..2ac9ef61 100644 --- a/mp/src/game/client/c_particle_system.cpp +++ b/mp/src/game/client/c_particle_system.cpp @@ -32,6 +32,9 @@ public: protected: int m_iEffectIndex; bool m_bActive; +#ifdef MAPBASE + bool m_bDestroyImmediately; +#endif bool m_bOldActive; float m_flStartTime; // Time at which the effect started @@ -56,6 +59,9 @@ BEGIN_RECV_TABLE_NOBASE( C_ParticleSystem, DT_ParticleSystem ) RecvPropInt( RECVINFO( m_iEffectIndex ) ), RecvPropBool( RECVINFO( m_bActive ) ), +#ifdef MAPBASE + RecvPropBool( RECVINFO( m_bDestroyImmediately ) ), +#endif RecvPropFloat( RECVINFO( m_flStartTime ) ), RecvPropArray3( RECVINFO_ARRAY(m_hControlPointEnts), RecvPropEHandle( RECVINFO( m_hControlPointEnts[0] ) ) ), @@ -108,9 +114,18 @@ void C_ParticleSystem::PostDataUpdate( DataUpdateType_t updateType ) SetNextClientThink( gpGlobals->curtime ); } else +#ifdef MAPBASE + { + if (!m_bDestroyImmediately) + ParticleProp()->StopEmission(); + else + ParticleProp()->StopEmissionAndDestroyImmediately(); + } +#else { ParticleProp()->StopEmission(); } +#endif } } } diff --git a/mp/src/game/client/c_pixel_visibility.cpp b/mp/src/game/client/c_pixel_visibility.cpp index 8ff507b1..340616fd 100644 --- a/mp/src/game/client/c_pixel_visibility.cpp +++ b/mp/src/game/client/c_pixel_visibility.cpp @@ -429,8 +429,10 @@ void CPixelVisibilityQuery::IssueQuery( IMatRenderContext *pRenderContext, float return; } } +#ifndef MAPBASE // Mapbase can also query visibility several times via multiple point_cameras, etc. #ifndef PORTAL // FIXME: In portal we query visibility multiple times per frame because of portal renders! Assert ( ( m_frameIssued != gpGlobals->framecount ) || UseVR() ); +#endif #endif m_frameIssued = gpGlobals->framecount; diff --git a/mp/src/game/client/c_playerlocaldata.h b/mp/src/game/client/c_playerlocaldata.h index 95b6d48d..1efb8021 100644 --- a/mp/src/game/client/c_playerlocaldata.h +++ b/mp/src/game/client/c_playerlocaldata.h @@ -75,6 +75,9 @@ public: bool m_bSlowMovement; + //Tony; added so tonemap controller can work in multiplayer with inputs. + tonemap_params_t m_TonemapParams; + }; #endif // C_PLAYERLOCALDATA_H diff --git a/mp/src/game/client/c_point_camera.cpp b/mp/src/game/client/c_point_camera.cpp index 3d6eaa15..30c109a3 100644 --- a/mp/src/game/client/c_point_camera.cpp +++ b/mp/src/game/client/c_point_camera.cpp @@ -25,6 +25,10 @@ IMPLEMENT_CLIENTCLASS_DT( C_PointCamera, DT_PointCamera, CPointCamera ) RecvPropFloat( RECVINFO( m_flFogMaxDensity ) ), RecvPropInt( RECVINFO( m_bActive ) ), RecvPropInt( RECVINFO( m_bUseScreenAspectRatio ) ), +#ifdef MAPBASE + RecvPropInt( RECVINFO( m_iSkyMode ) ), + RecvPropString( RECVINFO( m_iszRenderTarget ) ), +#endif END_RECV_TABLE() C_EntityClassList g_PointCameraList; @@ -40,6 +44,10 @@ C_PointCamera::C_PointCamera() m_bActive = false; m_bFogEnable = false; +#ifdef MAPBASE + m_iszRenderTarget[0] = '\0'; +#endif + g_PointCameraList.Insert( this ); } @@ -53,6 +61,16 @@ bool C_PointCamera::ShouldDraw() return false; } +void C_PointCamera::OnDataChanged( DataUpdateType_t type ) +{ +#ifdef MAPBASE + // Reset render texture + m_pRenderTarget = NULL; +#endif + + return BaseClass::OnDataChanged( type ); +} + float C_PointCamera::GetFOV() { return m_FOV; @@ -113,4 +131,31 @@ void C_PointCamera::GetToolRecordingState( KeyValues *msg ) msg->SetPtr( "monitor", &state ); } +#ifdef MAPBASE +extern ITexture *GetCameraTexture( void ); +extern void AddReleaseFunc( void ); + +ITexture *C_PointCamera::RenderTarget() +{ + if (m_iszRenderTarget[0] != '\0') + { + if (!m_pRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pRenderTarget = materials->FindTexture( m_iszRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pRenderTarget) + return m_pRenderTarget; + } + + return GetCameraTexture(); +} + +IMPLEMENT_CLIENTCLASS_DT( C_PointCameraOrtho, DT_PointCameraOrtho, CPointCameraOrtho ) + RecvPropInt( RECVINFO( m_bOrtho ) ), + RecvPropArray( RecvPropFloat( RECVINFO( m_OrthoDimensions[0] ) ), m_OrthoDimensions ), +END_RECV_TABLE() +#endif + diff --git a/mp/src/game/client/c_point_camera.h b/mp/src/game/client/c_point_camera.h index 8bca63af..c77e9c20 100644 --- a/mp/src/game/client/c_point_camera.h +++ b/mp/src/game/client/c_point_camera.h @@ -29,6 +29,9 @@ public: // C_BaseEntity. virtual bool ShouldDraw(); + // Mapbase uses this for m_iszRenderTarget + virtual void OnDataChanged( DataUpdateType_t type ); + float GetFOV(); float GetResolution(); bool IsFogEnabled(); @@ -37,6 +40,14 @@ public: float GetFogMaxDensity(); float GetFogEnd(); bool UseScreenAspectRatio() const { return m_bUseScreenAspectRatio; } +#ifdef MAPBASE + virtual bool IsOrtho() const { return false; } + virtual void GetOrthoDimensions(float &up, float &dn, float &lf, float &rt) const {} + + SkyboxVisibility_t SkyMode() { return m_iSkyMode; } + + ITexture *RenderTarget(); +#endif virtual void GetToolRecordingState( KeyValues *msg ); @@ -50,11 +61,37 @@ private: float m_flFogMaxDensity; bool m_bActive; bool m_bUseScreenAspectRatio; +#ifdef MAPBASE + SkyboxVisibility_t m_iSkyMode; + ITexture *m_pRenderTarget; + char m_iszRenderTarget[64]; +#endif public: C_PointCamera *m_pNext; }; +#ifdef MAPBASE +class C_PointCameraOrtho : public C_PointCamera +{ +public: + DECLARE_CLASS( C_PointCameraOrtho, C_PointCamera ); + DECLARE_CLIENTCLASS(); + +public: + bool IsOrtho() const { return m_bOrtho; } + void GetOrthoDimensions( float &up, float &dn, float &lf, float &rt ) const + { + up = m_OrthoDimensions[0], dn = m_OrthoDimensions[1]; + lf = m_OrthoDimensions[2], rt = m_OrthoDimensions[3]; + } + +private: + bool m_bOrtho; + float m_OrthoDimensions[4]; +}; +#endif + C_PointCamera *GetPointCameraList(); #endif // C_POINTCAMERA_H diff --git a/mp/src/game/client/c_postprocesscontroller.cpp b/mp/src/game/client/c_postprocesscontroller.cpp new file mode 100644 index 00000000..91a2d1df --- /dev/null +++ b/mp/src/game/client/c_postprocesscontroller.cpp @@ -0,0 +1,63 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: stores map postprocess params +// +//============================================================================= +#include "cbase.h" +#include "c_postprocesscontroller.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT( C_PostProcessController, DT_PostProcessController, CPostProcessController ) + RecvPropArray3( RECVINFO_NAME( m_PostProcessParameters.m_flParameters[0], m_flPostProcessParameters ), POST_PROCESS_PARAMETER_COUNT, RecvPropFloat( RECVINFO_NAME( m_PostProcessParameters.m_flParameters[0], m_flPostProcessParameters[0] ) ) ), + RecvPropBool( RECVINFO(m_bMaster) ) +END_RECV_TABLE() + +C_PostProcessController* C_PostProcessController::ms_pMasterController = nullptr; + +//----------------------------------------------------------------------------- +C_PostProcessController::C_PostProcessController() +: m_bMaster( false ) +{ + if ( ms_pMasterController == nullptr) + { + ms_pMasterController = this; + } +} + +//----------------------------------------------------------------------------- +C_PostProcessController::~C_PostProcessController() +{ + if ( ms_pMasterController == this ) + { + ms_pMasterController = nullptr; + } +} + +void C_PostProcessController::PostDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PostDataUpdate( updateType ); + + if ( m_bMaster ) + { + ms_pMasterController = this; + } +} + +#ifdef MAPBASE +// Prevents parameters from fading after a save/restore +bool g_bPostProcessNeedsRestore = false; + +void C_PostProcessController::OnRestore() +{ + BaseClass::OnRestore(); + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pPlayer && pPlayer->GetActivePostProcessController() == this ) + { + // Tell clientmode this is part of a save/restore + g_bPostProcessNeedsRestore = true; + } +} +#endif diff --git a/mp/src/game/client/c_postprocesscontroller.h b/mp/src/game/client/c_postprocesscontroller.h new file mode 100644 index 00000000..c07673f7 --- /dev/null +++ b/mp/src/game/client/c_postprocesscontroller.h @@ -0,0 +1,33 @@ +#pragma once + +#include "postprocess_shared.h" + +//============================================================================= +// +// Class Postprocess Controller: +// +class C_PostProcessController : public C_BaseEntity +{ + DECLARE_CLASS( C_PostProcessController, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + C_PostProcessController(); + virtual ~C_PostProcessController(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + + static C_PostProcessController* GetMasterController() { return ms_pMasterController; } + + PostProcessParameters_t m_PostProcessParameters; + +#ifdef MAPBASE + // Prevents fade time from being used in save/restore + virtual void OnRestore(); +#endif + +private: + bool m_bMaster; + + static C_PostProcessController* ms_pMasterController; +}; diff --git a/mp/src/game/client/c_rope.cpp b/mp/src/game/client/c_rope.cpp index 0c84778c..0898857e 100644 --- a/mp/src/game/client/c_rope.cpp +++ b/mp/src/game/client/c_rope.cpp @@ -29,6 +29,14 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +static void PrecacheCable( void* ) +{ + PrecacheMaterial( "cable/rope_shadowdepth" ); +} +PRECACHE_REGISTER_FN( PrecacheCable ); +#endif + void RecvProxy_RecomputeSprings( const CRecvProxyData *pData, void *pStruct, void *pOut ) { // Have the regular proxy store the data. @@ -40,6 +48,9 @@ void RecvProxy_RecomputeSprings( const CRecvProxyData *pData, void *pStruct, voi IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_RopeKeyframe, DT_RopeKeyframe, CRopeKeyframe ) +#ifdef MAPBASE + RecvPropInt( RECVINFO( m_nChangeCount ) ), +#endif RecvPropInt( RECVINFO(m_iRopeMaterialModelIndex) ), RecvPropEHandle( RECVINFO(m_hStartPoint) ), RecvPropEHandle( RECVINFO(m_hEndPoint) ), @@ -81,7 +92,9 @@ static ConVar rope_smooth_maxalpha( "rope_smooth_maxalpha", "0.5", 0, "Alpha for static ConVar mat_fullbright( "mat_fullbright", "0", FCVAR_CHEAT ); // get it from the engine static ConVar r_drawropes( "r_drawropes", "1", FCVAR_CHEAT ); +#ifndef MAPBASE static ConVar r_queued_ropes( "r_queued_ropes", "1" ); +#endif static ConVar r_ropetranslucent( "r_ropetranslucent", "1"); static ConVar r_rope_holiday_light_scale( "r_rope_holiday_light_scale", "0.055", FCVAR_DEVELOPMENTONLY ); static ConVar r_ropes_holiday_lights_allowed( "r_ropes_holiday_lights_allowed", "1", FCVAR_DEVELOPMENTONLY ); @@ -99,11 +112,17 @@ static ConVar rope_solid_minalpha( "rope_solid_minalpha", "0.0" ); static ConVar rope_solid_maxalpha( "rope_solid_maxalpha", "1" ); +#ifndef MAPBASE static CCycleCount g_RopeCollideTicks; static CCycleCount g_RopeDrawTicks; static CCycleCount g_RopeSimulateTicks; +#endif static int g_nRopePointsSimulated; +#ifdef MAPBASE +static IMaterial *g_pSplineCableShadowdepth = NULL; +#endif + // Active ropes. CUtlLinkedList g_Ropes; @@ -119,6 +138,7 @@ public: } } g_FullBrightLightValuesInit; +#ifndef MAPBASE // Precalculated info for rope subdivision. static Vector g_RopeSubdivs[MAX_ROPE_SUBDIVS][MAX_ROPE_SUBDIVS]; class CSubdivInit @@ -142,6 +162,7 @@ static int g_nBarbedSubdivs = 3; static Vector g_BarbedSubdivs[MAX_ROPE_SUBDIVS] = { Vector(1.5, 1.5*1.5, 1.5*1.5*1.5), Vector(-0.5, -0.5 * -0.5, -0.5*-0.5*-0.5), Vector(0.5, 0.5*0.5, 0.5*0.5*0.5) }; +#endif // This can be exposed through the entity if we ever care. static float g_flLockAmount = 0.1; @@ -149,7 +170,7 @@ static float g_flLockFalloff = 0.3; - +#ifndef MAPBASE class CQueuedRopeMemoryManager { public: @@ -219,6 +240,7 @@ struct RopeSegData_t // If this is less than rope_solid_minwidth and rope_solid_minalpha is 0, then we can avoid drawing.. float m_flMaxBackWidth; }; +#endif class CRopeManager : public IRopeManager { @@ -230,15 +252,18 @@ public: void ResetRenderCache( void ); void AddToRenderCache( C_RopeKeyframe *pRope ); void DrawRenderCache( bool bShadowDepth ); +#ifndef MAPBASE void OnRenderStart( void ) { m_QueuedModeMemory.SwitchStack(); } +#endif void SetHolidayLightMode( bool bHoliday ) { m_bDrawHolidayLights = bHoliday; } bool IsHolidayLightMode( void ); int GetHolidayLightStyle( void ); +#ifndef MAPBASE private: struct RopeRenderData_t; public: @@ -246,26 +271,32 @@ public: void ResetSegmentCache( int nMaxSegments ); RopeSegData_t *GetNextSegmentFromCache( void ); +#endif enum { MAX_ROPE_RENDERCACHE = 128 }; void RemoveRopeFromQueuedRenderCaches( C_RopeKeyframe *pRope ); +#ifndef MAPBASE private: void RenderNonSolidRopes( IMatRenderContext *pRenderContext, IMaterial *pMaterial, int nVertCount, int nIndexCount ); void RenderSolidRopes( IMatRenderContext *pRenderContext, IMaterial *pMaterial, int nVertCount, int nIndexCount, bool bRenderNonSolid ); +#endif private: struct RopeRenderData_t { IMaterial *m_pSolidMaterial; +#ifndef MAPBASE IMaterial *m_pBackMaterial; +#endif int m_nCacheCount; C_RopeKeyframe *m_aCache[MAX_ROPE_RENDERCACHE]; }; +#ifndef MAPBASE CUtlVector m_aRenderCache; int m_nSegmentCacheCount; CUtlVector m_aSegmentCache; @@ -275,20 +306,37 @@ private: CQueuedRopeMemoryManager m_QueuedModeMemory; IMaterial* m_pDepthWriteMaterial; +#endif struct RopeQueuedRenderCache_t { RopeRenderData_t *pCaches; int iCacheCount; +#ifdef MAPBASE + CThreadFastMutex *m_pRopeDataMutex; +#endif RopeQueuedRenderCache_t( void ) : pCaches(NULL), iCacheCount(0) { }; }; +#ifdef MAPBASE + void DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_t *pRenderCache, int nRenderCacheCount, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedData, CThreadFastMutex *pRopeDataMutex ); +#else CUtlLinkedList m_RopeQueuedRenderCaches; +#endif bool m_bDrawHolidayLights; bool m_bHolidayInitialized; int m_nHolidayLightsStyle; + +#ifdef MAPBASE + CUtlVector m_aRenderCache; + + //in queued material system mode we need to store off data for later use. + IMaterial* m_pDepthWriteMaterial; + CUtlLinkedList m_RopeQueuedRenderCaches; + CThreadFastMutex m_RopeQueuedRenderCaches_Mutex; //mutex just for changing m_RopeQueuedRenderCaches +#endif }; static CRopeManager s_RopeManager; @@ -298,11 +346,12 @@ IRopeManager *RopeManager() return &s_RopeManager; } - +#ifndef MAPBASE inline bool ShouldUseFakeAA( IMaterial *pBackMaterial ) { return pBackMaterial && rope_smooth.GetInt() && engine->GetDXSupportLevel() > 70 && !g_pMaterialSystemHardwareConfig->IsAAEnabled(); } +#endif //----------------------------------------------------------------------------- @@ -311,8 +360,10 @@ inline bool ShouldUseFakeAA( IMaterial *pBackMaterial ) CRopeManager::CRopeManager() { m_aRenderCache.Purge(); +#ifndef MAPBASE m_aSegmentCache.Purge(); m_nSegmentCacheCount = 0; +#endif m_pDepthWriteMaterial = NULL; m_bDrawHolidayLights = false; m_bHolidayInitialized = false; @@ -324,6 +375,9 @@ CRopeManager::CRopeManager() //----------------------------------------------------------------------------- CRopeManager::~CRopeManager() { +#ifdef MAPBASE + m_aRenderCache.Purge(); +#else int nRenderCacheCount = m_aRenderCache.Count(); for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache ) { @@ -339,6 +393,7 @@ CRopeManager::~CRopeManager() m_aRenderCache.Purge(); m_aSegmentCache.Purge(); +#endif } //----------------------------------------------------------------------------- @@ -368,8 +423,12 @@ void CRopeManager::AddToRenderCache( C_RopeKeyframe *pRope ) int nRenderCacheCount = m_aRenderCache.Count(); for ( ; iRenderCache < nRenderCacheCount; ++iRenderCache ) { +#ifdef MAPBASE + if ( pRope->GetSolidMaterial() == m_aRenderCache[iRenderCache].m_pSolidMaterial ) +#else if ( ( pRope->GetSolidMaterial() == m_aRenderCache[iRenderCache].m_pSolidMaterial ) && ( pRope->GetBackMaterial() == m_aRenderCache[iRenderCache].m_pBackMaterial ) ) +#endif break; } @@ -379,6 +438,7 @@ void CRopeManager::AddToRenderCache( C_RopeKeyframe *pRope ) { int iRenderCache = m_aRenderCache.AddToTail(); m_aRenderCache[iRenderCache].m_pSolidMaterial = pRope->GetSolidMaterial(); +#ifndef MAPBASE if ( m_aRenderCache[iRenderCache].m_pSolidMaterial ) { m_aRenderCache[iRenderCache].m_pSolidMaterial->IncrementReferenceCount(); @@ -388,6 +448,7 @@ void CRopeManager::AddToRenderCache( C_RopeKeyframe *pRope ) { m_aRenderCache[iRenderCache].m_pBackMaterial->IncrementReferenceCount(); } +#endif m_aRenderCache[iRenderCache].m_nCacheCount = 0; } @@ -401,6 +462,257 @@ void CRopeManager::AddToRenderCache( C_RopeKeyframe *pRope ) ++m_aRenderCache[iRenderCache].m_nCacheCount; } +#ifdef MAPBASE +#define OUTPUT_2SPLINE_VERTS( t, u ) \ + meshBuilder.Color4ub( nRed, nGreen, nBlue, nAlpha ); \ + meshBuilder.Position3f( (t), u, 0 ); \ + meshBuilder.TexCoord4fv( 0, vecP0.Base() ); \ + meshBuilder.TexCoord4fv( 1, vecP1.Base() ); \ + meshBuilder.TexCoord4fv( 2, vecP2.Base() ); \ + meshBuilder.TexCoord4fv( 3, vecP3.Base() ); \ + meshBuilder.AdvanceVertexF(); \ + meshBuilder.Color4ub( nRed, nGreen, nBlue, nAlpha ); \ + meshBuilder.Position3f( (t), u, 1 ); \ + meshBuilder.TexCoord4fv( 0, vecP0.Base() ); \ + meshBuilder.TexCoord4fv( 1, vecP1.Base() ); \ + meshBuilder.TexCoord4fv( 2, vecP2.Base() ); \ + meshBuilder.TexCoord4fv( 3, vecP3.Base() ); \ + meshBuilder.AdvanceVertexF(); + + +void CRopeManager::DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_t *pRenderCache, int nRenderCacheCount, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedData, CThreadFastMutex *pRopeDataMutex ) +{ + VPROF_BUDGET( "CRopeManager::DrawRenderCache", VPROF_BUDGETGROUP_ROPES ); + + CThreadFastMutex dummyMutex; + if( pRopeDataMutex == NULL ) + pRopeDataMutex = &dummyMutex; + + if ( bShadowDepth && !m_pDepthWriteMaterial && g_pMaterialSystem ) + { + KeyValues *pVMTKeyValues = new KeyValues( "SDK_DepthWrite" ); + pVMTKeyValues->SetInt( "$no_fullbright", 1 ); + pVMTKeyValues->SetInt( "$alphatest", 0 ); + pVMTKeyValues->SetInt( "$nocull", 1 ); + m_pDepthWriteMaterial = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite01", TEXTURE_GROUP_OTHER, pVMTKeyValues ); + } + CMatRenderContextPtr pRenderContext( materials ); + + // UNDONE: needs to use the queued data + { + AUTO_LOCK_FM( *pRopeDataMutex ); + int defaultSubdiv = rope_subdiv.GetInt(); + for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache ) + { + int nCacheCount = pRenderCache[iRenderCache].m_nCacheCount; + + int nTotalVerts = 0; + int nTotalIndices = 0; + for ( int iCache = 0; iCache < nCacheCount; ++iCache ) + { + C_RopeKeyframe *pRope = pRenderCache[iRenderCache].m_aCache[iCache]; + if ( pRope ) + { + int segs = pRope->m_RopePhysics.NumNodes()-1; + int nSubdivCount = (pRope->m_Subdiv != 255 ? pRope->m_Subdiv : defaultSubdiv) + 1; + nTotalVerts += ((2 * nSubdivCount) * segs) + 2; + nTotalIndices += (6 * nSubdivCount) * segs; + } + } + if ( nTotalVerts == 0 ) + continue; + + IMaterial *pMaterial = bShadowDepth ? g_pSplineCableShadowdepth : pRenderCache[iRenderCache].m_pSolidMaterial; + + // Need to make sure that all rope materials use the splinerope shader since there are a lot of assumptions about how the shader interfaces with this code. + AssertOnce( V_strstr( pMaterial->GetShaderName(), "SDK_Cable" ) != NULL ); // splinerope + + pRenderContext->Bind( pMaterial ); + + int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial ); + int nMaxIndices = pRenderContext->GetMaxIndicesToRender(); + + IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); + CMeshBuilder meshBuilder; + int meshVertCount = MIN(nTotalVerts, nMaxVertices); + int meshIndexCount = MIN(nTotalIndices, nMaxIndices); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, meshVertCount, meshIndexCount ); + int nCurIDX = 0; + + int availableVerts = meshVertCount; + int availableIndices = meshIndexCount; + float flLastU = 1.0f; + + for ( int iCache = 0; iCache < nCacheCount; ++iCache ) + { + C_RopeKeyframe *pRope = pRenderCache[iRenderCache].m_aCache[iCache]; + if ( pRope ) + { + CSimplePhysics::CNode *pNode = pRope->m_RopePhysics.GetFirstNode(); + int nSegmentsToRender = pRope->m_RopePhysics.NumNodes()-1; + if ( !nSegmentsToRender ) + continue; + + int nParticles = pRope->m_RopePhysics.NumNodes(); + int nSubdivCount = (pRope->m_Subdiv != 255 ? pRope->m_Subdiv : defaultSubdiv) + 1; + + int nNumIndicesPerSegment = 6 * nSubdivCount; + int nNumVerticesPerSegment = 2 * nSubdivCount; + + int nSegmentsAvailableInBuffer = MIN( ( availableVerts - 2 ) / nNumVerticesPerSegment, + ( availableIndices ) / nNumIndicesPerSegment ); + + int segmentsInBuffer = MIN(nSegmentsAvailableInBuffer,nSegmentsToRender); + availableIndices -= nNumIndicesPerSegment * segmentsInBuffer; + availableVerts -= 2 + (nNumVerticesPerSegment * segmentsInBuffer); + + float width = pRope->m_Width; + Vector vModColor = pRope->m_vColorMod; + Vector *pColors = pRope->m_LightValues; + + // Figure out texture scale. + float flPixelsPerInch = 4.0f / pRope->m_TextureScale; + // This is the total number of texels for the length of the whole rope. + float flTotalTexCoord = flPixelsPerInch * ( pRope->m_RopeLength + pRope->m_Slack + ROPESLACK_FUDGEFACTOR ); + int nTotalPoints = (nSegmentsToRender * (nSubdivCount-1)) + 1; + float flDU = ( flTotalTexCoord / nTotalPoints ) / ( float )pRope->m_TextureHeight; + float flU = pRope->m_flCurScroll; + float m_flTStep = 1.0f / float(nSubdivCount); + + bool bFirstPoint = true; + + // initialize first spline segment + Vector4D vecP1; + Vector4D vecP2; + vecP1.Init( pNode[0].m_vPredicted, pRope->m_Width ); + vecP2.Init( pNode[1].m_vPredicted, pRope->m_Width ); + Vector4D vecP0 = vecP1; + + uint8 nRed = 0; + uint8 nGreen = 0; + uint8 nBlue = 0; + uint8 nAlpha = 255; + + Vector4D vecDelta = vecP2; + vecDelta -= vecP1; + vecP0 -= vecDelta; + + Vector4D vecP3; + + if ( nParticles < 3 ) + { + vecP3 = vecP2; + vecP3 += vecDelta; + } + else + { + vecP3.Init( pNode[2].m_vPredicted, width ); + } + int nPnt = 3; + int nColor = 1; + Vector vColor0( pColors[0].x * vModColor.x, pColors[0].y * vModColor.y, pColors[0].z * vModColor.z ); + Vector vColor1( pColors[1].x * vModColor.x, pColors[1].y * vModColor.y, pColors[1].z * vModColor.z ); + + float flT = 0; + do + { + if ( ! nSegmentsAvailableInBuffer ) + { + meshBuilder.End(); + pMesh->Draw(); + nTotalVerts -= (meshVertCount - availableVerts); + nTotalIndices -= (meshIndexCount - availableIndices); + meshVertCount = MIN(nTotalVerts, nMaxVertices); + meshIndexCount = MIN(nTotalIndices, nMaxIndices); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, meshVertCount, meshIndexCount ); + availableVerts = meshVertCount; + availableIndices = meshIndexCount; + // copy the last emitted points + OUTPUT_2SPLINE_VERTS( flT, flLastU ); + + nSegmentsAvailableInBuffer = MIN( ( availableVerts - 2 ) / nNumVerticesPerSegment, + availableIndices / nNumIndicesPerSegment ); + + nCurIDX = 0; + } + nSegmentsAvailableInBuffer--; + flT = 0.; + for( int nSlice = 0 ; nSlice < nSubdivCount; nSlice++ ) + { + float omt = 1.0f - flT; + nRed = FastFToC( (vColor0.x * omt) + (vColor1.x*flT) ); + nGreen = FastFToC( (vColor0.y * omt) + (vColor1.y*flT) ); + nBlue = FastFToC( (vColor0.z * omt) + (vColor1.z*flT) ); + OUTPUT_2SPLINE_VERTS( flT, flU ); + flT += m_flTStep; + flU += flDU; + if ( ! bFirstPoint ) + { + meshBuilder.FastIndex( nCurIDX ); + meshBuilder.FastIndex( nCurIDX+1 ); + meshBuilder.FastIndex( nCurIDX+2 ); + meshBuilder.FastIndex( nCurIDX+1 ); + meshBuilder.FastIndex( nCurIDX+3 ); + meshBuilder.FastIndex( nCurIDX+2 ); + nCurIDX += 2; + } + bFirstPoint = false; + } + // next segment + vColor0 = vColor1; + if ( nColor < nParticles-1 ) + { + nColor++; + vColor1.Init( pColors[nColor].x * vModColor.x, pColors[nColor].y * vModColor.y, pColors[nColor].z * vModColor.z ); + } + if ( nSegmentsToRender > 1 ) + { + vecP0 = vecP1; + vecP1 = vecP2; + vecP2 = vecP3; + + if ( nPnt < nParticles ) + { + vecP3.AsVector3D() = pNode[nPnt].m_vPredicted; + nPnt++; + } + else + { + // fake last point by extrapolating + vecP3 += vecP2; + vecP3 -= vecP1; + } + } + } while( --nSegmentsToRender ); + + // output last piece + OUTPUT_2SPLINE_VERTS( 1.0, flU ); + meshBuilder.FastIndex( nCurIDX ); + meshBuilder.FastIndex( nCurIDX+1 ); + meshBuilder.FastIndex( nCurIDX+2 ); + meshBuilder.FastIndex( nCurIDX+1 ); + meshBuilder.FastIndex( nCurIDX+3 ); + meshBuilder.FastIndex( nCurIDX+2 ); + nCurIDX += 4; + flLastU = flU; + } + } + + meshBuilder.End(); + pMesh->Draw(); + } + } + + m_RopeQueuedRenderCaches_Mutex.Lock(); + if( pBuildRopeQueuedData && (m_RopeQueuedRenderCaches.Count() != 0) ) + { + unsigned short iHeadIndex = m_RopeQueuedRenderCaches.Head(); + delete m_RopeQueuedRenderCaches[iHeadIndex].m_pRopeDataMutex; + m_RopeQueuedRenderCaches.Remove( iHeadIndex ); + } + m_RopeQueuedRenderCaches_Mutex.Unlock(); +} +#else void CRopeManager::DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_t *pRenderCache, int nRenderCacheCount, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedData ) { VPROF_BUDGET( "CRopeManager::DrawRenderCache", VPROF_BUDGETGROUP_ROPES ); @@ -519,6 +831,11 @@ void CRopeManager::DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_ m_RopeQueuedRenderCaches.Remove( m_RopeQueuedRenderCaches.Head() ); } } +#endif + +#ifdef MAPBASE +ConVar r_queued_ropes( "r_queued_ropes", "1" ); +#endif //----------------------------------------------------------------------------- // Purpose: @@ -530,15 +847,30 @@ void CRopeManager::DrawRenderCache( bool bShadowDepth ) if( iRenderCacheCount == 0 ) return; +#ifdef MAPBASE + // Check to see if we want to render the ropes. + if( !r_drawropes.GetBool() ) + return; +#endif + Vector vForward = CurrentViewForward(); Vector vOrigin = CurrentViewOrigin(); +#ifdef MAPBASE + CMatRenderContextPtr pRenderContext(materials); +#endif ICallQueue *pCallQueue; +#ifdef MAPBASE + if( r_queued_ropes.GetBool() && (pCallQueue = pRenderContext->GetCallQueue()) != NULL ) +#else if( r_queued_ropes.GetBool() && (pCallQueue = materials->GetRenderContext()->GetCallQueue()) != NULL ) +#endif { //material queue available and desired CRopeManager::RopeRenderData_t *pRenderCache = m_aRenderCache.Base(); +#ifndef MAPBASE AUTO_LOCK( m_RenderCacheMutex ); +#endif int iRopeCount = 0; int iNodeCount = 0; @@ -564,7 +896,12 @@ void CRopeManager::DrawRenderCache( bool bShadowDepth ) (iRopeCount * sizeof(C_RopeKeyframe::BuildRopeQueuedData_t)) + (iNodeCount * (sizeof(Vector) * 2)); +#ifdef MAPBASE + CMatRenderData< byte > rd(pRenderContext, iMemoryNeeded); + void *pMemory = rd.Base(); +#else void *pMemory = m_QueuedModeMemory.Alloc( iMemoryNeeded ); +#endif CRopeManager::RopeRenderData_t *pRenderCachesStart = (CRopeManager::RopeRenderData_t *)pMemory; C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedDataStart = (C_RopeKeyframe::BuildRopeQueuedData_t *)(pRenderCachesStart + iRenderCacheCount); @@ -575,7 +912,11 @@ void CRopeManager::DrawRenderCache( bool bShadowDepth ) RopeQueuedRenderCache_t cache; cache.pCaches = pRenderCachesStart; cache.iCacheCount = iRenderCacheCount; +#ifdef MAPBASE + cache.m_pRopeDataMutex = new CThreadFastMutex; +#else m_RopeQueuedRenderCaches.AddToTail( cache ); +#endif C_RopeKeyframe::BuildRopeQueuedData_t *pWriteRopeQueuedData = pBuildRopeQueuedDataStart; Vector *pVectorWrite = (Vector *)pVectorDataStart; @@ -588,7 +929,9 @@ void CRopeManager::DrawRenderCache( bool bShadowDepth ) int iCacheCount = pReadCache->m_nCacheCount; pWriteCache->m_nCacheCount = 0; pWriteCache->m_pSolidMaterial = pReadCache->m_pSolidMaterial; +#ifndef MAPBASE pWriteCache->m_pBackMaterial = pReadCache->m_pBackMaterial; +#endif for( int j = 0; j != iCacheCount; ++j ) { C_RopeKeyframe *pRope = pReadCache->m_aCache[j]; @@ -619,6 +962,21 @@ void CRopeManager::DrawRenderCache( bool bShadowDepth ) pVectorWrite += iNodes; //so we don't overwrite the light values with the next rope's predicted positions } } +#ifdef MAPBASE + m_RopeQueuedRenderCaches_Mutex.Lock(); + unsigned short iLLIndex = m_RopeQueuedRenderCaches.AddToTail( cache ); + CThreadFastMutex *pRopeDataMutex = m_RopeQueuedRenderCaches[iLLIndex].m_pRopeDataMutex; + m_RopeQueuedRenderCaches_Mutex.Unlock(); + + Assert( ((void *)pVectorWrite == (void *)(((uint8 *)pMemory) + iMemoryNeeded)) && ((void *)pWriteRopeQueuedData == (void *)pVectorDataStart)); + pCallQueue->QueueCall( this, &CRopeManager::DrawRenderCache_NonQueued, bShadowDepth, pRenderCachesStart, iRenderCacheCount, vForward, vOrigin, pBuildRopeQueuedDataStart, pRopeDataMutex ); + + if ( IsHolidayLightMode() ) + { + // With holiday lights we need to also build the ropes non-queued without rendering them + DrawRenderCache_NonQueued( bShadowDepth, m_aRenderCache.Base(), iRenderCacheCount, vForward, vOrigin, NULL, NULL ); + } +#else Assert( ((void *)pVectorWrite == (void *)(((uint8 *)pMemory) + iMemoryNeeded)) && ((void *)pWriteRopeQueuedData == (void *)pVectorDataStart)); pCallQueue->QueueCall( this, &CRopeManager::DrawRenderCache_NonQueued, bShadowDepth, pRenderCachesStart, iRenderCacheCount, vForward, vOrigin, pBuildRopeQueuedDataStart ); @@ -627,10 +985,15 @@ void CRopeManager::DrawRenderCache( bool bShadowDepth ) // With holiday lights we need to also build the ropes non-queued without rendering them DrawRenderCache_NonQueued( bShadowDepth, m_aRenderCache.Base(), iRenderCacheCount, vForward, vOrigin, NULL ); } +#endif } else { +#ifdef MAPBASE + DrawRenderCache_NonQueued( bShadowDepth, m_aRenderCache.Base(), iRenderCacheCount, vForward, vOrigin, NULL, NULL ); +#else DrawRenderCache_NonQueued( bShadowDepth, m_aRenderCache.Base(), iRenderCacheCount, vForward, vOrigin, NULL ); +#endif } } @@ -681,6 +1044,7 @@ int CRopeManager::GetHolidayLightStyle( void ) return m_nHolidayLightsStyle; } +#ifndef MAPBASE //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -804,13 +1168,18 @@ RopeSegData_t *CRopeManager::GetNextSegmentFromCache( void ) ++m_nSegmentCacheCount; return &m_aSegmentCache[m_nSegmentCacheCount-1]; } +#endif void CRopeManager::RemoveRopeFromQueuedRenderCaches( C_RopeKeyframe *pRope ) { //remove this rope from queued render caches +#ifdef MAPBASE + AUTO_LOCK_FM( m_RopeQueuedRenderCaches_Mutex ); +#else AUTO_LOCK( m_RenderCacheMutex ); +#endif int index = m_RopeQueuedRenderCaches.Head(); while( m_RopeQueuedRenderCaches.IsValidIndex( index ) ) { @@ -822,7 +1191,13 @@ void CRopeManager::RemoveRopeFromQueuedRenderCaches( C_RopeKeyframe *pRope ) { if( pCache->m_aCache[j] == pRope ) { +#ifdef MAPBASE + RenderCacheData.m_pRopeDataMutex->Lock(); pCache->m_aCache[j] = NULL; + RenderCacheData.m_pRopeDataMutex->Unlock(); +#else + pCache->m_aCache[j] = NULL; +#endif } } } @@ -839,9 +1214,11 @@ void CRopeManager::RemoveRopeFromQueuedRenderCaches( C_RopeKeyframe *pRope ) void Rope_ResetCounters() { +#ifndef MAPBASE g_RopeCollideTicks.Init(); g_RopeDrawTicks.Init(); g_RopeSimulateTicks.Init(); +#endif g_nRopePointsSimulated = 0; } @@ -885,8 +1262,12 @@ void C_RopeKeyframe::CPhysicsDelegate::GetNodeForces( CSimplePhysics::CNode *pNo if( !m_pKeyframe->m_LinksTouchingSomething[iNode] && m_pKeyframe->m_bApplyWind) { +#ifdef MAPBASE + Vector vecWindVel = GetWindspeedAtLocation( m_pKeyframe->m_RopePhysics.GetNode( iNode )->m_vPos ); +#else Vector vecWindVel; GetWindspeedAtTime(gpGlobals->curtime, vecWindVel); +#endif if ( vecWindVel.LengthSqr() > 0 ) { Vector vecWindAccel; @@ -894,7 +1275,11 @@ void C_RopeKeyframe::CPhysicsDelegate::GetNodeForces( CSimplePhysics::CNode *pNo } else { +#ifdef MAPBASE + if ( ( m_pKeyframe->m_flCurrentGustLifetime != 0.0f ) && ( m_pKeyframe->m_flCurrentGustTimer < m_pKeyframe->m_flCurrentGustLifetime ) ) +#else if (m_pKeyframe->m_flCurrentGustTimer < m_pKeyframe->m_flCurrentGustLifetime ) +#endif { float div = m_pKeyframe->m_flCurrentGustTimer / m_pKeyframe->m_flCurrentGustLifetime; float scale = 1 - cos( div * M_PI ); @@ -912,8 +1297,17 @@ void C_RopeKeyframe::CPhysicsDelegate::GetNodeForces( CSimplePhysics::CNode *pNo } // Apply any instananeous forces and reset +#ifdef MAPBASE + *pAccel += ROPE_IMPULSE_SCALE * m_pKeyframe->m_vecImpulse; + m_pKeyframe->m_vecImpulse *= ROPE_IMPULSE_DECAY; + if ( m_pKeyframe->m_vecImpulse.LengthSqr() < 0.1f ) + { + m_pKeyframe->m_vecImpulse = vec3_origin; + } +#else *pAccel += ROPE_IMPULSE_SCALE * m_pKeyframe->m_flImpulse; m_pKeyframe->m_flImpulse *= ROPE_IMPULSE_DECAY; +#endif } @@ -950,21 +1344,31 @@ void C_RopeKeyframe::CPhysicsDelegate::ApplyConstraints( CSimplePhysics::CNode * { VPROF( "CPhysicsDelegate::ApplyConstraints" ); +#ifndef MAPBASE CTraceFilterWorldOnly traceFilter; +#endif // Collide with the world. if( ((m_pKeyframe->m_RopeFlags & ROPE_COLLIDE) && rope_collide.GetInt()) || (rope_collide.GetInt() == 2) ) { +#ifdef MAPBASE + CTraceFilterWorldOnly traceFilter; +#else CTimeAdder adder( &g_RopeCollideTicks ); +#endif for( int i=0; i < nNodes; i++ ) { CSimplePhysics::CNode *pNode = &pNodes[i]; int iIteration; +#ifdef MAPBASE + const int nIterations = 10; +#else int nIterations = 10; +#endif for( iIteration=0; iIteration < nIterations; iIteration++ ) { trace_t trace; @@ -982,7 +1386,11 @@ void C_RopeKeyframe::CPhysicsDelegate::ApplyConstraints( CSimplePhysics::CNode * } // Apply some friction. +#ifdef MAPBASE + const float flSlowFactor = 0.3f; +#else static float flSlowFactor = 0.3f; +#endif pNode->m_vPos -= (pNode->m_vPos - pNode->m_vPrevPos) * flSlowFactor; // Move it out along the face normal. @@ -1046,6 +1454,10 @@ C_RopeKeyframe::C_RopeKeyframe() m_vColorMod.Init( 1, 1, 1 ); m_nLinksTouchingSomething = 0; m_Subdiv = 255; // default to using the cvar +#ifdef MAPBASE + m_flCurrentGustLifetime = 0.0f; + m_flCurrentGustTimer = 0.0f; +#endif m_fLockedPoints = 0; m_fPrevLockedPoints = 0; @@ -1053,7 +1465,11 @@ C_RopeKeyframe::C_RopeKeyframe() m_iForcePointMoveCounter = 0; m_flCurScroll = m_flScrollSpeed = 0; m_TextureScale = 4; // 4:1 +#ifdef MAPBASE + m_vecImpulse.Init(); +#else m_flImpulse.Init(); +#endif g_Ropes.AddToTail( this ); } @@ -1064,11 +1480,13 @@ C_RopeKeyframe::~C_RopeKeyframe() s_RopeManager.RemoveRopeFromQueuedRenderCaches( this ); g_Ropes.FindAndRemove( this ); +#ifndef MAPBASE if ( m_pBackMaterial ) { m_pBackMaterial->DecrementReferenceCount(); m_pBackMaterial = NULL; } +#endif } @@ -1129,6 +1547,11 @@ C_RopeKeyframe* C_RopeKeyframe::CreateFromKeyValues( C_BaseAnimating *pEnt, KeyV pRope->m_RopeFlags |= ROPE_NO_GRAVITY; } +#ifdef MAPBASE + // Model ropes need wind to move + pRope->m_RopeFlags |= ROPE_USE_WIND; +#endif + pRope->m_RopeLength = pValues->GetInt( "Length" ); pRope->m_TextureScale = pValues->GetFloat( "TextureScale", pRope->m_TextureScale ); pRope->m_Slack = 0; @@ -1258,6 +1681,9 @@ void C_RopeKeyframe::RecomputeSprings() void C_RopeKeyframe::ShakeRope( const Vector &vCenter, float flRadius, float flMagnitude ) { // Sum up whatever it would apply to all of our points. +#ifdef MAPBASE + bool bWantsThink = false; +#endif for ( int i=0; i < m_nSegments; i++ ) { CSimplePhysics::CNode *pNode = m_RopePhysics.GetNode( i ); @@ -1267,9 +1693,21 @@ void C_RopeKeyframe::ShakeRope( const Vector &vCenter, float flRadius, float flM float flShakeAmount = 1.0f - flDist / flRadius; if ( flShakeAmount >= 0 ) { +#ifdef MAPBASE + m_vecImpulse.z += flShakeAmount * flMagnitude; + bWantsThink = true; +#else m_flImpulse.z += flShakeAmount * flMagnitude; +#endif } } + +#ifdef MAPBASE + if ( bWantsThink ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } +#endif } @@ -1278,6 +1716,9 @@ void C_RopeKeyframe::OnDataChanged( DataUpdateType_t updateType ) BaseClass::OnDataChanged( updateType ); m_bNewDataThisFrame = true; +#ifdef MAPBASE + SetNextClientThink( CLIENT_THINK_ALWAYS ); +#endif if( updateType != DATA_UPDATE_CREATED ) return; @@ -1296,7 +1737,11 @@ void C_RopeKeyframe::OnDataChanged( DataUpdateType_t updateType ) } else { +#ifdef MAPBASE + Q_strncpy( str, "missing_rope_material", sizeof( str ) ); +#else Q_strncpy( str, "asdf", sizeof( str ) ); +#endif } FinishInit( str ); @@ -1307,11 +1752,21 @@ void C_RopeKeyframe::FinishInit( const char *pMaterialName ) { // Get the material from the material system. m_pMaterial = materials->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER ); + +#ifdef MAPBASE + if ( !g_pSplineCableShadowdepth ) + { + g_pSplineCableShadowdepth = g_pMaterialSystem->FindMaterial( "cable/rope_shadowdepth", TEXTURE_GROUP_OTHER ); + g_pSplineCableShadowdepth->IncrementReferenceCount(); + } +#endif + if( m_pMaterial ) m_TextureHeight = m_pMaterial->GetMappingHeight(); else m_TextureHeight = 1; +#ifndef MAPBASE char backName[512]; Q_snprintf( backName, sizeof( backName ), "%s_back", pMaterialName ); @@ -1324,6 +1779,7 @@ void C_RopeKeyframe::FinishInit( const char *pMaterialName ) m_pBackMaterial->IncrementReferenceCount(); m_pBackMaterial->GetMappingWidth(); } +#endif // Init rope physics. m_nSegments = clamp( m_nSegments, 2, ROPE_MAX_SEGMENTS ); @@ -1402,10 +1858,24 @@ void C_RopeKeyframe::ClientThink() if( !InitRopePhysics() ) // init if not already return; +#ifdef MAPBASE + if( DetectRestingState( m_bApplyWind ) ) +#else if( !DetectRestingState( m_bApplyWind ) ) +#endif { +#ifdef MAPBASE + if ( ( m_RopeFlags & ROPE_USE_WIND ) == 0 ) + { + SetNextClientThink( CLIENT_THINK_NEVER ); + } + return; + } +#endif // Update the simulation. +#ifndef MAPBASE CTimeAdder adder( &g_RopeSimulateTicks ); +#endif RunRopeSimulation( gpGlobals->frametime ); @@ -1414,6 +1884,10 @@ void C_RopeKeyframe::ClientThink() m_bNewDataThisFrame = false; // Setup a new wind gust? +#ifdef MAPBASE + if ( m_bApplyWind ) + { +#endif m_flCurrentGustTimer += gpGlobals->frametime; m_flTimeToNextGust -= gpGlobals->frametime; if( m_flTimeToNextGust <= 0 ) @@ -1431,8 +1905,13 @@ void C_RopeKeyframe::ClientThink() m_flTimeToNextGust = RandomFloat( 3.0f, 4.0f ); } +#ifdef MAPBASE + } + UpdateBBox(); +#else UpdateBBox(); } +#endif } @@ -1555,16 +2034,41 @@ bool C_RopeKeyframe::GetAttachment( int number, Vector &origin, QAngle &angles ) bool C_RopeKeyframe::AnyPointsMoved() { +#ifdef MAPBASE + int nNodeCount = m_RopePhysics.NumNodes(); + for( int i=0; i < nNodeCount; i++ ) +#else for( int i=0; i < m_RopePhysics.NumNodes(); i++ ) +#endif { CSimplePhysics::CNode *pNode = m_RopePhysics.GetNode( i ); +#ifdef MAPBASE + float flMoveDistSqr = pNode->m_vPos.DistToSqr( pNode->m_vPrevPos ); + if( flMoveDistSqr > 0.25f ) + { + if ( m_iForcePointMoveCounter < 5 ) + { + m_iForcePointMoveCounter = 5; + } + return true; + } +#else float flMoveDistSqr = (pNode->m_vPos - pNode->m_vPrevPos).LengthSqr(); if( flMoveDistSqr > 0.03f ) return true; +#endif } +#ifdef MAPBASE + if( m_iForcePointMoveCounter >= 0 ) + { + --m_iForcePointMoveCounter; + return true; + } +#else if( --m_iForcePointMoveCounter > 0 ) return true; +#endif return false; } @@ -1621,6 +2125,22 @@ bool C_RopeKeyframe::DetectRestingState( bool &bApplyWind ) Vector &vEnd1 = m_RopePhysics.GetFirstNode()->m_vPos; Vector &vEnd2 = m_RopePhysics.GetLastNode()->m_vPos; +#ifdef MAPBASE + if ( m_RopeFlags & ROPE_USE_WIND ) + { + // Don't apply wind if more than half of the nodes are touching something. + if( m_nLinksTouchingSomething < (m_RopePhysics.NumNodes() >> 1) ) + { + bApplyWind = CalcDistanceToLineSegment( MainViewOrigin(), vEnd1, vEnd2 ) < rope_wind_dist.GetFloat(); + } + } + + if ( m_vecPreviousImpulse != m_vecImpulse ) + { + m_vecPreviousImpulse = m_vecImpulse; + return false; + } +#else if ( !( m_RopeFlags & ROPE_NO_WIND ) ) { // Don't apply wind if more than half of the nodes are touching something. @@ -1634,6 +2154,7 @@ bool C_RopeKeyframe::DetectRestingState( bool &bApplyWind ) m_flPreviousImpulse = m_flImpulse; return false; } +#endif return !AnyPointsMoved() && !bApplyWind && !rope_shake.GetInt(); } @@ -1666,7 +2187,7 @@ inline void Catmull_Rom_Eval( const catmull_t &spline, const Vector &t, Vector & output = spline.c + (t.x * spline.t) + (t.y*spline.t2) + (t.z * spline.t3); } - +#ifndef MAPBASE //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1833,6 +2354,7 @@ void C_RopeKeyframe::BuildRope( RopeSegData_t *pSegmentData, const Vector &vCurr } } } +#endif void C_RopeKeyframe::UpdateBBox() { @@ -1920,7 +2442,11 @@ bool C_RopeKeyframe::CalculateEndPointAttachment( C_BaseEntity *pEnt, int iAttac if ( m_RopeFlags & ROPE_PLAYER_WPN_ATTACH ) { +#ifdef MAPBASE + C_BasePlayer *pPlayer = ToBasePlayer( pEnt ); +#else C_BasePlayer *pPlayer = dynamic_cast< C_BasePlayer* >( pEnt ); +#endif if ( pPlayer ) { C_BaseAnimating *pModel = pPlayer->GetRenderedWeaponModel(); @@ -1986,10 +2512,12 @@ IMaterial* C_RopeKeyframe::GetSolidMaterial( void ) return m_pMaterial; } +#ifndef MAPBASE IMaterial* C_RopeKeyframe::GetBackMaterial( void ) { return m_pBackMaterial; } +#endif bool C_RopeKeyframe::GetEndPointAttachment( int iPt, Vector &vPos, QAngle &angle ) { @@ -2008,7 +2536,7 @@ bool C_RopeKeyframe::GetEndPointAttachment( int iPt, Vector &vPos, QAngle &angle return true; } - +#ifndef MAPBASE // Look at the global cvar and recalculate rope subdivision data if necessary. Vector *C_RopeKeyframe::GetRopeSubdivVectors( int *nSubdivs ) { @@ -2032,6 +2560,7 @@ Vector *C_RopeKeyframe::GetRopeSubdivVectors( int *nSubdivs ) return g_RopeSubdivs[subdiv]; } } +#endif void C_RopeKeyframe::CalcLightValues() @@ -2080,7 +2609,13 @@ void C_RopeKeyframe::ReceiveMessage( int classID, bf_read &msg ) } // Read instantaneous fore data +#ifdef MAPBASE + m_vecImpulse.x = msg.ReadFloat(); + m_vecImpulse.y = msg.ReadFloat(); + m_vecImpulse.z = msg.ReadFloat(); +#else m_flImpulse.x = msg.ReadFloat(); m_flImpulse.y = msg.ReadFloat(); m_flImpulse.z = msg.ReadFloat(); +#endif } diff --git a/mp/src/game/client/c_rope.h b/mp/src/game/client/c_rope.h index f04372f9..3d821081 100644 --- a/mp/src/game/client/c_rope.h +++ b/mp/src/game/client/c_rope.h @@ -107,7 +107,9 @@ public: // Get the rope material data. IMaterial *GetSolidMaterial( void ); +#ifndef MAPBASE IMaterial *GetBackMaterial( void ); +#endif struct BuildRopeQueuedData_t { @@ -119,7 +121,11 @@ public: float m_Slack; }; +#ifdef MAPBASE + void BuildRope( RopeSegData_t *pRopeSegment, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, BuildRopeQueuedData_t *pQueuedData ); +#else void BuildRope( RopeSegData_t *pRopeSegment, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, BuildRopeQueuedData_t *pQueuedData, bool bQueued ); +#endif // C_BaseEntity overrides. public: @@ -196,19 +202,29 @@ private: float m_TextureScale; // pixels per inch int m_fLockedPoints; // Which points are locked down. +#ifdef MAPBASE + int m_nChangeCount; +#endif float m_Width; CPhysicsDelegate m_PhysicsDelegate; IMaterial *m_pMaterial; +#ifndef MAPBASE IMaterial *m_pBackMaterial; // Optional translucent background material for the rope to help reduce aliasing. +#endif int m_TextureHeight; // Texture height, for texture scale calculations. // Instantaneous force +#ifdef MAPBASE + Vector m_vecImpulse; + Vector m_vecPreviousImpulse; +#else Vector m_flImpulse; Vector m_flPreviousImpulse; +#endif // Simulated wind gusts. float m_flCurrentGustTimer; @@ -250,7 +266,9 @@ public: virtual void ResetRenderCache( void ) = 0; virtual void AddToRenderCache( C_RopeKeyframe *pRope ) = 0; virtual void DrawRenderCache( bool bShadowDepth ) = 0; +#ifndef MAPBASE virtual void OnRenderStart( void ) = 0; +#endif virtual void SetHolidayLightMode( bool bHoliday ) = 0; virtual bool IsHolidayLightMode( void ) = 0; virtual int GetHolidayLightStyle( void ) = 0; diff --git a/mp/src/game/client/c_sceneentity.cpp b/mp/src/game/client/c_sceneentity.cpp index abd211c0..5ce06891 100644 --- a/mp/src/game/client/c_sceneentity.cpp +++ b/mp/src/game/client/c_sceneentity.cpp @@ -676,7 +676,7 @@ void C_SceneEntity::DispatchStartSpeak( CChoreoScene *scene, C_BaseFlex *actor, es.m_pSoundName = event->GetParameters(); EmitSound( filter, actor->entindex(), es ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); // Close captioning only on master token no matter what... if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) @@ -806,20 +806,72 @@ CChoreoStringPool g_ChoreoStringPool; CChoreoScene *C_SceneEntity::LoadScene( const char *filename ) { +#ifdef MAPBASE + char loadfile[MAX_PATH]; +#else char loadfile[ 512 ]; +#endif Q_strncpy( loadfile, filename, sizeof( loadfile ) ); Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) ); Q_FixSlashes( loadfile ); +#ifdef MAPBASE + // + // Raw scene file support + // + void *pBuffer = 0; + size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile ); + CChoreoScene *pScene = NULL; + if ( bufsize > 0 ) + { + // Definitely in scenes.image + pBuffer = malloc( bufsize ); + if ( !scenefilecache->GetSceneData( filename, (byte *)pBuffer, bufsize ) ) + { + free( pBuffer ); + return NULL; + } + + + if ( IsBufferBinaryVCD( (char*)pBuffer, bufsize ) ) + { + pScene = new CChoreoScene( this ); + CUtlBuffer buf( pBuffer, bufsize, CUtlBuffer::READ_ONLY ); + if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) ) + { + Warning( "Unable to restore scene '%s'\n", loadfile ); + delete pScene; + pScene = NULL; + } + } + } + else if (filesystem->ReadFileEx( loadfile, "MOD", &pBuffer, true )) + { + // Not in scenes.image, but it's a raw file + g_TokenProcessor.SetBuffer((char*)pBuffer); + pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf ); + } + else + { + // Abandon ship + return NULL; + } + + if(pScene) + { + pScene->SetPrintFunc( Scene_Printf ); + pScene->SetEventCallbackInterface( this ); + } +#else char *pBuffer = NULL; size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile ); if ( bufsize <= 0 ) return NULL; - pBuffer = new char[ bufsize ]; + pBuffer = malloc( bufsize ); if ( !scenefilecache->GetSceneData( filename, (byte *)pBuffer, bufsize ) ) { - delete[] pBuffer; + free( pBuffer ); return NULL; } @@ -845,8 +897,9 @@ CChoreoScene *C_SceneEntity::LoadScene( const char *filename ) g_TokenProcessor.SetBuffer( pBuffer ); pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf ); } +#endif - delete[] pBuffer; + free( pBuffer ); return pScene; } @@ -913,7 +966,7 @@ void C_SceneEntity::UnloadScene( void ) //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -933,7 +986,7 @@ void C_SceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, C_BaseFlex *a //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -957,7 +1010,7 @@ void C_SceneEntity::DispatchStartGesture( CChoreoScene *scene, C_BaseFlex *actor if ( !Q_stricmp( event->GetName(), "NULL" ) ) return; - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -972,7 +1025,7 @@ void C_SceneEntity::DispatchProcessGesture( CChoreoScene *scene, C_BaseFlex *act return; actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -995,7 +1048,7 @@ void C_SceneEntity::DispatchEndGesture( CChoreoScene *scene, C_BaseFlex *actor, //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1005,7 +1058,7 @@ void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor void C_SceneEntity::DispatchProcessSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1210,4 +1263,4 @@ void C_SceneEntity::PrefetchAnimBlocks( CChoreoScene *pScene ) return; Msg( "%d of %d animations resident\n", nResident, nChecked ); -} \ No newline at end of file +} diff --git a/mp/src/game/client/c_shadowcontrol.cpp b/mp/src/game/client/c_shadowcontrol.cpp index 80169241..c725a2ba 100644 --- a/mp/src/game/client/c_shadowcontrol.cpp +++ b/mp/src/game/client/c_shadowcontrol.cpp @@ -32,13 +32,24 @@ private: color32 m_shadowColor; float m_flShadowMaxDist; bool m_bDisableShadows; +#ifdef MAPBASE + bool m_bEnableLocalLightShadows; +#endif }; IMPLEMENT_CLIENTCLASS_DT(C_ShadowControl, DT_ShadowControl, CShadowControl) RecvPropVector(RECVINFO(m_shadowDirection)), +#ifdef MAPBASE + /*RecvPropInt(RECVINFO(m_shadowColor), 0, RecvProxy_Int32ToColor32),*/ + RecvPropInt(RECVINFO(m_shadowColor), 0, RecvProxy_IntToColor32), +#else RecvPropInt(RECVINFO(m_shadowColor)), +#endif RecvPropFloat(RECVINFO(m_flShadowMaxDist)), RecvPropBool(RECVINFO(m_bDisableShadows)), +#ifdef MAPBASE + RecvPropBool(RECVINFO(m_bEnableLocalLightShadows)), +#endif END_RECV_TABLE() @@ -54,6 +65,9 @@ void C_ShadowControl::OnDataChanged(DataUpdateType_t updateType) g_pClientShadowMgr->SetShadowColor( m_shadowColor.r, m_shadowColor.g, m_shadowColor.b ); g_pClientShadowMgr->SetShadowDistance( m_flShadowMaxDist ); g_pClientShadowMgr->SetShadowsDisabled( m_bDisableShadows ); +#ifdef DYNAMIC_RTT_SHADOWS + g_pClientShadowMgr->SetShadowFromWorldLightsEnabled( m_bEnableLocalLightShadows ); +#endif } //------------------------------------------------------------------------------ diff --git a/mp/src/game/client/c_soundscape.cpp b/mp/src/game/client/c_soundscape.cpp index a9baa479..dcbee3c6 100644 --- a/mp/src/game/client/c_soundscape.cpp +++ b/mp/src/game/client/c_soundscape.cpp @@ -207,6 +207,9 @@ public: // "dsp_volume" void ProcessDSPVolume( KeyValues *pKey, subsoundscapeparams_t ¶ms ); +#ifdef MAPBASE // Moved to public space + void AddSoundScapeFile( const char *filename ); +#endif private: @@ -215,7 +218,9 @@ private: return gpGlobals->framecount == m_nRestoreFrame ? true : false; } +#ifndef MAPBASE // Moved to public space void AddSoundScapeFile( const char *filename ); +#endif void TouchPlayLooping( KeyValues *pAmbient ); void TouchPlayRandom( KeyValues *pPlayRandom ); @@ -265,6 +270,13 @@ void Soundscape_Update( audioparams_t &audio ) g_SoundscapeSystem.UpdateAudioParams( audio ); } +#ifdef MAPBASE +void Soundscape_AddFile( const char *szFile ) +{ + g_SoundscapeSystem.AddSoundScapeFile(szFile); +} +#endif + #define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt" void C_SoundscapeSystem::AddSoundScapeFile( const char *filename ) @@ -310,6 +322,16 @@ bool C_SoundscapeSystem::Init() mapSoundscapeFilename = VarArgs( "scripts/soundscapes_%s.txt", mapname ); } +#ifdef MAPBASE + if (filesystem->FileExists(VarArgs("maps/%s_soundscapes.txt", mapname))) + { + // A Mapbase-specific file exists. Load that instead. + // Any additional soundscape files, like the original scripts/soundscapes version, + // could be loaded through #include and/or #base. + mapSoundscapeFilename = VarArgs("maps/%s_soundscapes.txt", mapname); + } +#endif + KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE ); if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) ) { diff --git a/mp/src/game/client/c_soundscape.h b/mp/src/game/client/c_soundscape.h index 8c5f45e9..b42d4b42 100644 --- a/mp/src/game/client/c_soundscape.h +++ b/mp/src/game/client/c_soundscape.h @@ -24,4 +24,8 @@ extern void Soundscape_Update( audioparams_t &audio ); // sounds are still playing when they're not. void Soundscape_OnStopAllSounds(); +#ifdef MAPBASE +void Soundscape_AddFile( const char *szFile ); +#endif + #endif // C_SOUNDSCAPE_H diff --git a/mp/src/game/client/c_te_effect_dispatch.cpp b/mp/src/game/client/c_te_effect_dispatch.cpp index 160e11b7..e0688a2d 100644 --- a/mp/src/game/client/c_te_effect_dispatch.cpp +++ b/mp/src/game/client/c_te_effect_dispatch.cpp @@ -179,6 +179,10 @@ void DispatchEffect( const char *pName, const CEffectData &data ) te->DispatchEffect( filter, 0.0, data.m_vOrigin, pName, data ); } +void DispatchEffect( const char *pName, const CEffectData &data, IRecipientFilter &filter ) +{ + te->DispatchEffect( filter, 0.0, data.m_vOrigin, pName, data ); +} //----------------------------------------------------------------------------- // Playback diff --git a/mp/src/game/client/c_te_effect_dispatch.h b/mp/src/game/client/c_te_effect_dispatch.h index 63fa6173..8abaaeab 100644 --- a/mp/src/game/client/c_te_effect_dispatch.h +++ b/mp/src/game/client/c_te_effect_dispatch.h @@ -42,5 +42,6 @@ public: void DispatchEffectToCallback( const char *pEffectName, const CEffectData &m_EffectData ); void DispatchEffect( const char *pName, const CEffectData &data ); +void DispatchEffect( const char *pName, const CEffectData &data, IRecipientFilter &filter ); #endif // C_TE_EFFECT_DISPATCH_H diff --git a/mp/src/game/client/c_te_largefunnel.cpp b/mp/src/game/client/c_te_largefunnel.cpp index 25430db0..f8db20c4 100644 --- a/mp/src/game/client/c_te_largefunnel.cpp +++ b/mp/src/game/client/c_te_largefunnel.cpp @@ -64,7 +64,12 @@ void C_TELargeFunnel::CreateFunnel( void ) float ratio = 0.25; float invratio = 1 / ratio; +#ifdef MAPBASE + // Uh...figure out how to fix this PMaterialHandle hMaterial = pSimple->GetPMaterial( "sprites/flare6" ); +#else + PMaterialHandle hMaterial = pSimple->GetPMaterial( "sprites/flare6" ); +#endif for ( i = -256 ; i <= 256 ; i += 24 ) //24 from 32.. little more dense { diff --git a/mp/src/game/client/c_te_legacytempents.cpp b/mp/src/game/client/c_te_legacytempents.cpp index 14f6d0e8..31c65665 100644 --- a/mp/src/game/client/c_te_legacytempents.cpp +++ b/mp/src/game/client/c_te_legacytempents.cpp @@ -597,8 +597,12 @@ bool C_LocalTempEntity::Frame( float frametime, int framenumber ) if ( flags & FTENT_WINDBLOWN ) { +#ifdef MAPBASE + Vector vecWind = GetWindspeedAtLocation( GetAbsOrigin() ); +#else Vector vecWind; GetWindspeedAtTime( gpGlobals->curtime, vecWind ); +#endif for ( int i = 0 ; i < 2 ; i++ ) { @@ -1829,6 +1833,9 @@ void CTempEnts::MuzzleFlash( const Vector& pos1, const QAngle& angles, int type, // UNDONE: These need their own effects/sprites. For now use the pistol // SMG1 +#if defined ( HL2MP ) // HACK for hl2mp, make the default muzzleflash the smg muzzleflash for weapons like the RPG that are using 'type 0' + default: +#endif // HL2MP case MUZZLEFLASH_SMG1: if ( firstPerson ) { @@ -1866,10 +1873,12 @@ void CTempEnts::MuzzleFlash( const Vector& pos1, const QAngle& angles, int type, } break; +#if !defined ( HL2MP ) // HACK for hl2mp, make the default muzzleflash the smg muzzleflash for weapons like the RPG that are using 'type 0' default: // There's no supported muzzle flash for the type specified! Assert(0); break; +#endif // HL2MP } #endif diff --git a/mp/src/game/client/c_team_train_watcher.cpp b/mp/src/game/client/c_team_train_watcher.cpp index e58aafb1..43964898 100644 --- a/mp/src/game/client/c_team_train_watcher.cpp +++ b/mp/src/game/client/c_team_train_watcher.cpp @@ -8,10 +8,10 @@ #include "c_team_train_watcher.h" #include "igameevents.h" #include "c_team_objectiveresource.h" +#include "teamplayroundbased_gamerules.h" #ifdef TF_CLIENT_DLL #include "tf_shareddefs.h" -#include "teamplayroundbased_gamerules.h" #endif // memdbgon must be the last include file in a .cpp file!!! diff --git a/mp/src/game/client/c_vehicle_choreo_generic.cpp b/mp/src/game/client/c_vehicle_choreo_generic.cpp index 52eba09e..82abc224 100644 --- a/mp/src/game/client/c_vehicle_choreo_generic.cpp +++ b/mp/src/game/client/c_vehicle_choreo_generic.cpp @@ -54,7 +54,12 @@ public: flFOV = m_flFOV; } virtual void DrawHudElements(); +#ifdef MAPBASE + virtual bool IsPassengerUsingStandardWeapons( int nRole = VEHICLE_ROLE_DRIVER ) { return m_bAllowStandardWeapons; } + bool m_bAllowStandardWeapons; +#else virtual bool IsPassengerUsingStandardWeapons( int nRole = VEHICLE_ROLE_DRIVER ) { return false; } +#endif virtual void UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd ); virtual C_BaseCombatCharacter *GetPassenger( int nRole ); virtual int GetPassengerRole( C_BaseCombatCharacter *pPassenger ); @@ -107,6 +112,9 @@ IMPLEMENT_CLIENTCLASS_DT(C_PropVehicleChoreoGeneric, DT_PropVehicleChoreoGeneric RecvPropFloat( RECVINFO( m_vehicleView.flYawMax ) ), RecvPropFloat( RECVINFO( m_vehicleView.flPitchMin ) ), RecvPropFloat( RECVINFO( m_vehicleView.flPitchMax ) ), +#ifdef MAPBASE + RecvPropBool( RECVINFO( m_bAllowStandardWeapons ) ), +#endif END_RECV_TABLE() diff --git a/mp/src/game/client/c_world.cpp b/mp/src/game/client/c_world.cpp index 3ae6bdcc..cf48d47c 100644 --- a/mp/src/game/client/c_world.cpp +++ b/mp/src/game/client/c_world.cpp @@ -59,8 +59,18 @@ BEGIN_RECV_TABLE( C_World, DT_World ) RecvPropFloat(RECVINFO(m_flMinPropScreenSpaceWidth)), RecvPropString(RECVINFO(m_iszDetailSpriteMaterial)), RecvPropInt(RECVINFO(m_bColdWorld)), +#ifdef MAPBASE + RecvPropString(RECVINFO(m_iszChapterTitle)), +#endif +#ifdef MAPBASE_VSCRIPT + RecvPropInt(RECVINFO(m_iScriptLanguageClient)), +#endif END_RECV_TABLE() +#ifdef MAPBASE_VSCRIPT +extern bool VScriptClientInit(); +#endif + C_World::C_World( void ) { @@ -119,6 +129,11 @@ void C_World::OnDataChanged( DataUpdateType_t updateType ) engine->SetOcclusionParameters( params ); modelinfo->SetLevelScreenFadeRange( m_flMinPropScreenSpaceWidth, m_flMaxPropScreenSpaceWidth ); + +#ifdef MAPBASE_VSCRIPT + // This is now here so that C_World has time to receive the selected script language + VScriptClientInit(); +#endif } } diff --git a/mp/src/game/client/c_world.h b/mp/src/game/client/c_world.h index 3cdbd6a0..a0cc315c 100644 --- a/mp/src/game/client/c_world.h +++ b/mp/src/game/client/c_world.h @@ -41,6 +41,10 @@ public: float GetWaveHeight() const; const char *GetDetailSpriteMaterial() const; +#ifdef MAPBASE_VSCRIPT + ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)m_iScriptLanguageClient; } +#endif + public: enum { @@ -56,6 +60,12 @@ public: float m_flMinPropScreenSpaceWidth; float m_flMaxPropScreenSpaceWidth; bool m_bColdWorld; +#ifdef MAPBASE + char m_iszChapterTitle[64]; +#endif +#ifdef MAPBASE_VSCRIPT + int m_iScriptLanguageClient; +#endif private: void RegisterSharedActivities( void ); diff --git a/mp/src/game/client/cbase.h b/mp/src/game/client/cbase.h index 57e3260b..53322ca4 100644 --- a/mp/src/game/client/cbase.h +++ b/mp/src/game/client/cbase.h @@ -37,6 +37,10 @@ struct studiohdr_t; #include #include +#ifdef MAPBASE +#include "tier1/mapbase_con_groups.h" +#endif + // This is a precompiled header. Include a bunch of common stuff. // This is kind of ugly in that it adds a bunch of dependency where it isn't needed. diff --git a/mp/src/game/client/cdll_client_int.cpp b/mp/src/game/client/cdll_client_int.cpp index 5b8f10d9..af3e7334 100644 --- a/mp/src/game/client/cdll_client_int.cpp +++ b/mp/src/game/client/cdll_client_int.cpp @@ -215,6 +215,11 @@ IEngineReplay *g_pEngineReplay = NULL; IEngineClientReplay *g_pEngineClientReplay = NULL; IReplaySystem *g_pReplay = NULL; #endif +#ifdef MAPBASE +IVEngineServer *serverengine = NULL; +#endif + +IScriptManager *scriptmanager = NULL; IHaptics* haptics = NULL;// NVNT haptics system interface singleton @@ -336,6 +341,13 @@ static ConVar s_cl_class("cl_class", "default", FCVAR_USERINFO|FCVAR_ARCHIVE, "D static ConVar s_cl_load_hl1_content("cl_load_hl1_content", "0", FCVAR_ARCHIVE, "Mount the content from Half-Life: Source if possible"); #endif +#ifdef MAPBASE_RPC +// Mapbase stuff +extern void MapbaseRPC_Init(); +extern void MapbaseRPC_Shutdown(); +extern void MapbaseRPC_Update( int iType, const char *pMapName ); +#endif + // Physics system bool g_bLevelInitialized; @@ -847,6 +859,7 @@ CHLClient::CHLClient() } + extern IGameSystem *ViewportClientSystem(); @@ -940,9 +953,28 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physi return false; #endif +#ifdef MAPBASE + // Implements the server engine interface on the client. + // I'm extremely confused as to how this is even possible, but Saul Rennison's worldlight did it. + // If it's really this possible, why wasn't it available before? + // Hopefully there's no SP-only magic going on here, because I want to use this for RPC. + if ( (serverengine = (IVEngineServer*)appSystemFactory(INTERFACEVERSION_VENGINESERVER, NULL )) == NULL ) + return false; +#endif + if (!g_pMatSystemSurface) return false; + if ( !CommandLine()->CheckParm( "-noscripting") ) + { + scriptmanager = (IScriptManager *)appSystemFactory( VSCRIPT_INTERFACE_VERSION, NULL ); + + if (scriptmanager == nullptr) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + } + } + #ifdef WORKSHOP_IMPORT_ENABLED if ( !ConnectDataModel( appSystemFactory ) ) return false; @@ -1089,6 +1121,14 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physi HookHapticMessages(); // Always hook the messages #endif +#ifdef MAPBASE_RPC + MapbaseRPC_Init(); +#endif + +#ifdef MAPBASE + CommandLine()->AppendParm( "+r_hunkalloclightmaps", "0" ); +#endif + return true; } @@ -1213,6 +1253,10 @@ void CHLClient::Shutdown( void ) DisconnectDataModel(); ShutdownFbx(); #endif + +#ifdef MAPBASE_RPC + MapbaseRPC_Shutdown(); +#endif // This call disconnects the VGui libraries which we rely on later in the shutdown path, so don't do it // DisconnectTier3Libraries( ); @@ -1626,6 +1670,13 @@ void CHLClient::LevelInitPreEntity( char const* pMapName ) } #endif +#ifdef MAPBASE_RPC + if (!g_bTextMode) + { + MapbaseRPC_Update(RPCSTATE_LEVEL_INIT, pMapName); + } +#endif + // Check low violence settings for this map g_RagdollLVManager.SetLowViolence( pMapName ); @@ -1717,6 +1768,13 @@ void CHLClient::LevelShutdown( void ) gHUD.LevelShutdown(); +#ifdef MAPBASE_RPC + if (!g_bTextMode) + { + MapbaseRPC_Update(RPCSTATE_LEVEL_SHUTDOWN, NULL); + } +#endif + internalCenterPrint->Clear(); messagechars->Clear(); @@ -2147,7 +2205,9 @@ void OnRenderStart() // are at the correct location view->OnRenderStart(); +#ifndef MAPBASE RopeManager()->OnRenderStart(); +#endif // This will place all entities in the correct position in world space and in the KD-tree C_BaseAnimating::UpdateClientSideAnimations(); diff --git a/mp/src/game/client/cdll_client_int.h b/mp/src/game/client/cdll_client_int.h index c2456332..5cbd087e 100644 --- a/mp/src/game/client/cdll_client_int.h +++ b/mp/src/game/client/cdll_client_int.h @@ -110,6 +110,9 @@ extern IReplayManager *g_pReplayManager; extern IReplayScreenshotManager *g_pReplayScreenshotManager; extern IEngineReplay *g_pEngineReplay; extern IEngineClientReplay *g_pEngineClientReplay; +#ifdef MAPBASE +extern IVEngineServer *serverengine; +#endif //============================================================================= // HPE_BEGIN @@ -177,4 +180,16 @@ extern CSteamID GetSteamIDForPlayerIndex( int iPlayerIndex ); #endif +#ifdef MAPBASE +// Mapbase RPC stuff +enum +{ + RPCSTATE_INIT, + RPCSTATE_LEVEL_INIT, + RPCSTATE_LEVEL_SHUTDOWN, + + RPCSTATE_UPDATE, +}; +#endif + #endif // CDLL_CLIENT_INT_H diff --git a/mp/src/game/client/cdll_util.cpp b/mp/src/game/client/cdll_util.cpp index 4e877530..5117eaa7 100644 --- a/mp/src/game/client/cdll_util.cpp +++ b/mp/src/game/client/cdll_util.cpp @@ -703,6 +703,24 @@ int UTIL_EntitiesAlongRay( C_BaseEntity **pList, int listMax, const Ray_t &ray, return rayEnum.GetCount(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted +// Input : **pList - +// listMax - +// &point - +// flagMask - +// Output : int +//----------------------------------------------------------------------------- +int UTIL_EntitiesAtPoint( C_BaseEntity **pList, int listMax, const Vector &point, int flagMask, int partitionMask ) +{ + CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask ); + partition->EnumerateElementsAtPoint( partitionMask, point, false, &rayEnum ); + + return rayEnum.GetCount(); +} +#endif + CEntitySphereQuery::CEntitySphereQuery( const Vector ¢er, float radius, int flagMask, int partitionMask ) { m_listIndex = 0; @@ -729,11 +747,14 @@ CBaseEntity *CEntitySphereQuery::GetCurrentEntity() // sep - Character to use as separator. UNDONE: allow multiple separator chars // Output : Returns a pointer to the next token to be parsed. //----------------------------------------------------------------------------- -const char *nexttoken(char *token, const char *str, char sep) +const char *nexttoken(char *token, const char *str, char sep, size_t tokenLen) { if ((str == NULL) || (*str == '\0')) { - *token = '\0'; + if(tokenLen) + { + *token = '\0'; + } return(NULL); } @@ -741,11 +762,25 @@ const char *nexttoken(char *token, const char *str, char sep) // Copy everything up to the first separator into the return buffer. // Do not include separators in the return buffer. // - while ((*str != sep) && (*str != '\0')) + while ((*str != sep) && (*str != '\0') && (tokenLen > 1)) { *token++ = *str++; + tokenLen--; + } + + // + // If token is to big for return buffer, skip rest of token. + // + while ((*str != sep) && (*str != '\0')) + { + str++; + } + + if(tokenLen) + { + *token = '\0'; + tokenLen--; } - *token = '\0'; // // Advance the pointer unless we hit the end of the input string. diff --git a/mp/src/game/client/cdll_util.h b/mp/src/game/client/cdll_util.h index 64beb5fb..44f559b0 100644 --- a/mp/src/game/client/cdll_util.h +++ b/mp/src/game/client/cdll_util.h @@ -89,7 +89,7 @@ void NormalizeAngles( QAngle& angles ); void InterpolateAngles( const QAngle& start, const QAngle& end, QAngle& output, float frac ); void InterpolateVector( float frac, const Vector& src, const Vector& dest, Vector& output ); -const char *nexttoken(char *token, const char *str, char sep); +const char *nexttoken(char *token, const char *str, char sep, size_t tokenLen); //----------------------------------------------------------------------------- // Base light indices to avoid index collision @@ -119,6 +119,9 @@ void ClientPrint( C_BasePlayer *player, int msg_dest, const char *msg_name, cons int UTIL_EntitiesInBox( C_BaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask, int partitionMask = PARTITION_CLIENT_NON_STATIC_EDICTS ); int UTIL_EntitiesInSphere( C_BaseEntity **pList, int listMax, const Vector ¢er, float radius, int flagMask, int partitionMask = PARTITION_CLIENT_NON_STATIC_EDICTS ); int UTIL_EntitiesAlongRay( C_BaseEntity **pList, int listMax, const Ray_t &ray, int flagMask, int partitionMask = PARTITION_CLIENT_NON_STATIC_EDICTS ); +#ifdef MAPBASE +int UTIL_EntitiesAtPoint( C_BaseEntity **pList, int listMax, const Vector &point, int flagMask, int partitionMask = PARTITION_CLIENT_NON_STATIC_EDICTS ); +#endif // make this a fixed size so it just sits on the stack #define MAX_SPHERE_QUERY 256 @@ -161,7 +164,13 @@ T *_CreateEntity( T *newClass, const char *className ) // Misc useful inline bool FStrEq(const char *sz1, const char *sz2) { - return (sz1 == sz2 || V_stricmp(sz1, sz2) == 0); +#ifdef MAPBASE + // V_stricmp() already checks if the pointers are equal, so having a comparison here is pointless. + // I had few reasons to do this, but maybe you'll thank me later. + return ( V_stricmp(sz1, sz2) == 0 ); +#else + return ( sz1 == sz2 || V_stricmp(sz1, sz2) == 0 ); +#endif } // Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h diff --git a/mp/src/game/client/client_base.vpc b/mp/src/game/client/client_base.vpc index b1e5ae53..9d222159 100644 --- a/mp/src/game/client/client_base.vpc +++ b/mp/src/game/client/client_base.vpc @@ -18,6 +18,9 @@ $include "$SRCDIR\vpc_scripts\protobuf_builder.vpc" $Include "$SRCDIR\vpc_scripts\source_replay.vpc" [$TF] $Include "$SRCDIR\game\protobuf_include.vpc" +// Mapbase stuff +$Include "$SRCDIR\game\client\client_mapbase.vpc" [$MAPBASE] + $Configuration "Debug" { $General @@ -489,6 +492,11 @@ $Project $File "viewrender.cpp" $File "$SRCDIR\game\shared\voice_banmgr.cpp" $File "$SRCDIR\game\shared\voice_status.cpp" + $File "vscript_client.cpp" + $File "vscript_client.h" + $File "vscript_client.nut" + $File "$SRCDIR\game\shared\vscript_shared.cpp" + $File "$SRCDIR\game\shared\vscript_shared.h" $File "warp_overlay.cpp" $File "WaterLODMaterialProxy.cpp" $File "$SRCDIR\game\shared\weapon_parse.cpp" diff --git a/mp/src/game/client/client_episodic.vpc b/mp/src/game/client/client_episodic.vpc new file mode 100644 index 00000000..3c6f20dd --- /dev/null +++ b/mp/src/game/client/client_episodic.vpc @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// CLIENT_EPISODIC.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro GAMENAME "episodic" [!$SOURCESDK] +$Macro GAMENAME "mod_episodic" [$SOURCESDK] + +$Include "$SRCDIR\game\client\client_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories ".\hl2;.\hl2\elements;$SRCDIR\game\shared\hl2;$SRCDIR\game\shared\episodic;..\..\public;$BASE" + $PreprocessorDefinitions "$BASE;HL2_CLIENT_DLL;HL2_EPISODIC" + } +} + +$Project "Client (Episodic)" +{ + $Folder "Source Files" + { + $File "hud_chat.cpp" + $File "c_team_objectiveresource.cpp" + $File "c_team_objectiveresource.h" + + $Folder "HL2 DLL" + { + $File "$SRCDIR\game\shared\hl2\basehlcombatweapon_shared.cpp" + $File "$SRCDIR\game\shared\episodic\achievements_ep1.cpp" + $File "$SRCDIR\game\shared\episodic\achievements_ep2.cpp" + $File "$SRCDIR\game\shared\episodic\achievements_epx.cpp" + $File "hl2\c_antlion_dust.cpp" + $File "hl2\c_ar2_explosion.cpp" + $File "hl2\c_barnacle.cpp" + $File "hl2\c_barney.cpp" + $File "hl2\c_basehelicopter.cpp" + $File "hl2\c_basehelicopter.h" + $File "hl2\c_basehlcombatweapon.cpp" + $File "hl2\c_basehlcombatweapon.h" + $File "hl2\c_basehlplayer.cpp" + $File "hl2\c_basehlplayer.h" + $File "hl2\c_citadel_effects.cpp" + $File "hl2\c_corpse.cpp" + $File "hl2\c_corpse.h" + $File "hl2\c_env_alyxtemp.cpp" + $File "hl2\c_env_headcrabcanister.cpp" + $File "hl2\c_env_starfield.cpp" + $File "hl2\c_func_tankmortar.cpp" + $File "hl2\c_hl2_playerlocaldata.cpp" + $File "hl2\c_hl2_playerlocaldata.h" + $File "hl2\c_info_teleporter_countdown.cpp" + $File "hl2\c_npc_antlionguard.cpp" + $File "hl2\c_npc_combinegunship.cpp" + $File "hl2\c_npc_manhack.cpp" + $File "hl2\c_npc_rollermine.cpp" + $File "hl2\c_plasma_beam_node.cpp" + $File "hl2\c_prop_combine_ball.cpp" + $File "hl2\c_prop_combine_ball.h" + $File "episodic\c_prop_scalable.cpp" + $File "hl2\c_rotorwash.cpp" + $File "hl2\c_script_intro.cpp" + $File "$SRCDIR\game\shared\script_intro_shared.cpp" + $File "hl2\c_strider.cpp" + $File "hl2\c_te_concussiveexplosion.cpp" + $File "hl2\c_te_flare.cpp" + $File "hl2\c_thumper_dust.cpp" + $File "hl2\c_vehicle_airboat.cpp" + $File "hl2\c_vehicle_cannon.cpp" + $File "hl2\c_vehicle_crane.cpp" + $File "hl2\c_vehicle_crane.h" + $File "episodic\c_vehicle_jeep_episodic.cpp" + $File "hl2\c_vehicle_prisoner_pod.cpp" + $File "episodic\c_vort_charge_token.cpp" + $File "hl2\c_weapon__stubs_hl2.cpp" + $File "hl2\c_weapon_crossbow.cpp" + $File "episodic\c_weapon_hopwire.cpp" + $File "hl2\c_weapon_physcannon.cpp" + $File "hl2\c_weapon_stunstick.cpp" [!$MAPBASE] // See client_mapbase.vpc + $File "$SRCDIR\game\shared\hl2\citadel_effects_shared.h" + $File "hl2\clientmode_hlnormal.cpp" + $File "hl2\clientmode_hlnormal.h" + $File "death.cpp" + $File "$SRCDIR\game\shared\hl2\env_headcrabcanister_shared.cpp" + $File "$SRCDIR\game\shared\hl2\env_headcrabcanister_shared.h" + $File "$SRCDIR\game\shared\episodic\npc_advisor_shared.h" + $File "episodic\c_npc_advisor.cpp" + $File "episodic\episodic_screenspaceeffects.cpp" + $File "episodic\episodic_screenspaceeffects.h" + $File "episodic\flesh_internal_material_proxy.cpp" + $File "hl2\fx_antlion.cpp" + $File "hl2\fx_bugbait.cpp" + $File "hl2\fx_hl2_impacts.cpp" + $File "hl2\fx_hl2_tracers.cpp" + $File "hl2\hl2_clientmode.cpp" + $File "$SRCDIR\game\shared\hl2\hl2_gamerules.cpp" + $File "$SRCDIR\game\shared\hl2\hl2_gamerules.h" + $File "$SRCDIR\game\shared\hl2\hl2_shareddefs.h" + $File "$SRCDIR\game\shared\hl2\hl2_usermessages.cpp" + $File "$SRCDIR\game\shared\hl2\hl_gamemovement.cpp" + $File "$SRCDIR\game\shared\hl2\hl_gamemovement.h" + $File "hl2\hl_in_main.cpp" + $File "hl2\hl_prediction.cpp" + $File "hl2\hud_ammo.cpp" + $File "hl2\hud_battery.cpp" + $File "hl2\hud_blood.cpp" + $File "hl2\hud_credits.cpp" + $File "hl2\hud_damageindicator.cpp" + $File "hl2\hud_flashlight.cpp" + $File "hl2\hud_locator.cpp" + $File "hl2\hud_health.cpp" + $File "hl2\hud_poisondamageindicator.cpp" + $File "hud_posture.cpp" + $File "hl2\hud_quickinfo.cpp" + $File "hl2\hud_radar.cpp" + $File "hl2\hud_radar.h" + $File "hud_squadstatus.cpp" + $File "hl2\hud_suitpower.cpp" + $File "hl2\hud_suitpower.h" + $File "hl2\hud_weaponselection.cpp" + $File "hl2\hud_zoom.cpp" + $File "hl2\shieldproxy.cpp" + $File "$SRCDIR\game\shared\hl2\survival_gamerules.cpp" + $File "hl2\vgui_rootpanel_hl2.cpp" + $File "episodic\c_npc_puppet.cpp" + } + } + +} diff --git a/mp/src/game/client/client_mapbase.vpc b/mp/src/game/client/client_mapbase.vpc new file mode 100644 index 00000000..3c2eb00f --- /dev/null +++ b/mp/src/game/client/client_mapbase.vpc @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// CLIENT_MAPBASE.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Configuration +{ + $Compiler + { + $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS;GLOWS_ENABLE" + + $PreprocessorDefinitions "$BASE;MAPBASE_RPC;DISCORD_RPC;STEAM_RPC" [$MAPBASE_RPC] + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] + } +} + +$Project +{ + $Folder "Source Files" + { + $File "c_env_global_light.cpp" + $File "worldlight.cpp" + $File "worldlight.h" + $File "c_baselesson.cpp" + $File "c_baselesson.h" + $File "c_gameinstructor.cpp" + $File "c_gameinstructor.h" + $File "hud_locator_target.cpp" + $File "hud_locator_target.h" + $File "c_postprocesscontroller.cpp" + $File "c_postprocesscontroller.h" + $File "c_env_dof_controller.cpp" + + $Folder "Mapbase" + { + $File "$SRCDIR\game\shared\mapbase\mapbase_shared.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_rpc.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_game_log.cpp" + $File "$SRCDIR\game\shared\mapbase\MapEdit.cpp" + $File "$SRCDIR\game\shared\mapbase\MapEdit.h" + $File "$SRCDIR\game\shared\mapbase\matchers.cpp" + $File "$SRCDIR\game\shared\mapbase\matchers.h" + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_singletons.h" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_consts_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_consts_weapons.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.h" [$MAPBASE_VSCRIPT] + + $File "mapbase\c_func_clientclip.cpp" + $File "mapbase\c_func_fake_worldportal.cpp" + $File "mapbase\c_func_fake_worldportal.h" + $File "mapbase\c_point_glow.cpp" + } + + $Folder "HL2 DLL" + { + // Original stunstick files are conditional'd out in the HL2 VPCs + $File "$SRCDIR\game\shared\hl2mp\weapon_stunstick.cpp" + $File "$SRCDIR\game\shared\hl2mp\weapon_stunstick.h" + } + + $Folder "HL2MP" + { + $Folder "Weapons" + { + $File "$SRCDIR\game\shared\hl2mp\weapon_slam.cpp" + $File "$SRCDIR\game\shared\hl2mp\weapon_slam.h" + } + } + } + + $Folder "Link Libraries" + { + $Lib "vscript" [$MAPBASE_VSCRIPT] + $Lib "raytrace" + } +} diff --git a/mp/src/game/client/clientleafsystem.cpp b/mp/src/game/client/clientleafsystem.cpp index fa3a0097..11ac28d1 100644 --- a/mp/src/game/client/clientleafsystem.cpp +++ b/mp/src/game/client/clientleafsystem.cpp @@ -1481,10 +1481,12 @@ inline void AddRenderableToRenderList( CClientRenderablesList &renderList, IClie pEntry->m_RenderHandle = renderHandle; curCount++; } +#ifndef MAPBASE // According to ficool2, this message can cause significant lag else { engine->Con_NPrintf( 10, "Warning: overflowed CClientRenderablesList group %d", group ); } +#endif } diff --git a/mp/src/game/client/clientleafsystem.h b/mp/src/game/client/clientleafsystem.h index dd48ec0b..5caa8914 100644 --- a/mp/src/game/client/clientleafsystem.h +++ b/mp/src/game/client/clientleafsystem.h @@ -52,7 +52,11 @@ class CClientRenderablesList : public CRefCounted<> public: enum { +#ifdef MAPBASE + MAX_GROUP_ENTITIES = 16834 // According to ficool2, this limit is bogus/not enforced by the engine and can be "safely" raised. +#else MAX_GROUP_ENTITIES = 4096 +#endif }; struct CEntry diff --git a/mp/src/game/client/clientmode_shared.cpp b/mp/src/game/client/clientmode_shared.cpp index 622208a4..827a8207 100644 --- a/mp/src/game/client/clientmode_shared.cpp +++ b/mp/src/game/client/clientmode_shared.cpp @@ -36,6 +36,7 @@ #include #include "hud_vote.h" #include "ienginevgui.h" +#include "viewpostprocess.h" #include "sourcevr/isourcevirtualreality.h" #if defined( _X360 ) #include "xbox/xbox_console.h" @@ -65,6 +66,10 @@ extern ConVar replay_rendersetting_renderglow; #include "econ_item_description.h" #endif +#ifdef GLOWS_ENABLE +#include "clienteffectprecachesystem.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -77,6 +82,9 @@ class CHudVote; static vgui::HContext s_hVGuiContext = DEFAULT_VGUI_CONTEXT; ConVar cl_drawhud( "cl_drawhud", "1", FCVAR_CHEAT, "Enable the rendering of the hud" ); +#ifdef DEMO_AUTORECORD +ConVar cl_autorecord("cl_autorecord", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Start recording demos automatically with an incremental name based on this value."); +#endif ConVar hud_takesshots( "hud_takesshots", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Auto-save a scoreboard screenshot at the end of a map." ); ConVar hud_freezecamhide( "hud_freezecamhide", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Hide the HUD during freeze-cam" ); ConVar cl_show_num_particle_systems( "cl_show_num_particle_systems", "0", FCVAR_CLIENTDLL, "Display the number of active particle systems." ); @@ -92,6 +100,13 @@ CON_COMMAND( cl_reload_localization_files, "Reloads all localization files" ) g_pVGuiLocalize->ReloadLocalizationFiles(); } +#ifdef GLOWS_ENABLE +CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffectsGlow ) +CLIENTEFFECT_MATERIAL( "dev/glow_color" ) +CLIENTEFFECT_MATERIAL( "dev/halo_add_to_screen" ) +CLIENTEFFECT_REGISTER_END_CONDITIONAL( engine->GetDXSupportLevel() >= 90 ) +#endif + #ifdef VOICE_VOX_ENABLE void VoxCallback( IConVar *var, const char *oldString, float oldFloat ) { @@ -283,6 +298,9 @@ ClientModeShared::ClientModeShared() m_pWeaponSelection = NULL; m_nRootSize[ 0 ] = m_nRootSize[ 1 ] = -1; + m_pCurrentPostProcessController = NULL; + m_PostProcessLerpTimer.Invalidate(); + #if defined( REPLAY_ENABLED ) m_pReplayReminderPanel = NULL; m_flReplayStartRecordTime = 0.0f; @@ -611,6 +629,8 @@ void ClientModeShared::Update() m_pViewport->SetVisible( cl_drawhud.GetBool() ); } + UpdatePostProcessingEffects(); + UpdateRumbleEffects(); if ( cl_show_num_particle_systems.GetBool() ) @@ -782,6 +802,10 @@ int ClientModeShared::HudElementKeyInput( int down, ButtonCode_t keynum, const c //----------------------------------------------------------------------------- bool ClientModeShared::DoPostScreenSpaceEffects( const CViewSetup *pSetup ) { +#ifdef GLOWS_ENABLE + g_GlowObjectManager.RenderGlowEffects( pSetup, 0 ); +#endif + #if defined( REPLAY_ENABLED ) if ( engine->IsPlayingDemo() ) { @@ -851,8 +875,54 @@ void ClientModeShared::LevelInit( const char *newmap ) // Reset any player explosion/shock effects CLocalPlayerFilter filter; enginesound->SetPlayerDSP( filter, 0, true ); + +#ifdef DEMO_AUTORECORD + AutoRecord(newmap); +#endif } +#ifdef DEMO_AUTORECORD +void ClientModeShared::AutoRecord(const char *map) +{ + if (!cl_autorecord.GetBool()) { + return; + } + + if (map == nullptr) { + Warning("null map in ClientModeShared::AutoRecord"); + return; + } + + // stop any demo to make sure they're saved + engine->ClientCmd("stop"); + + // KLEMS: sanitize space in client name because having to type "" while playing back lots of demos is annoying + ConVarRef name("name"); + char nameStr[128]; + memset(nameStr, 0, sizeof(nameStr)); + Q_snprintf(nameStr, sizeof(nameStr), "%s", name.GetString()); + int i = 0; + while (nameStr[i]) { + char c = nameStr[i]; + if (!( (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z'))) { + nameStr[i] = '_'; + } + i++; + } + nameStr[127] = 0; + + char cmd[256]; + Q_snprintf(cmd, sizeof(cmd), "record \"%s_%04d_%s\"", nameStr, cl_autorecord.GetInt(), map); + cl_autorecord.SetValue(cl_autorecord.GetInt() + 1); + engine->ClientCmd(cmd); + + // write to config to make sure the cvar is recorded + engine->ClientCmd("host_writeconfig"); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -871,6 +941,17 @@ void ClientModeShared::LevelShutdown( void ) s_hVGuiContext = DEFAULT_VGUI_CONTEXT; } +#ifdef MAPBASE + // Always reset post-processing on level unload + //if (m_pCurrentPostProcessController) + { + m_CurrentPostProcessParameters = PostProcessParameters_t(); + m_LerpEndPostProcessParameters = PostProcessParameters_t(); + m_pCurrentPostProcessController = NULL; + SetPostProcessParams( &m_CurrentPostProcessParameters ); + } +#endif + // Reset any player explosion/shock effects CLocalPlayerFilter filter; enginesound->SetPlayerDSP( filter, 0, true ); @@ -945,6 +1026,69 @@ float ClientModeShared::GetViewModelFOV( void ) return v_viewmodel_fov.GetFloat(); } +#ifdef MAPBASE +extern bool g_bPostProcessNeedsRestore; +#endif + +void ClientModeShared::UpdatePostProcessingEffects() +{ + C_PostProcessController* pNewPostProcessController = NULL; + C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); + + if (pPlayer) + pNewPostProcessController = pPlayer->GetActivePostProcessController(); + + if (!pNewPostProcessController) + { + m_CurrentPostProcessParameters = PostProcessParameters_t(); + m_pCurrentPostProcessController = NULL; + SetPostProcessParams( &m_CurrentPostProcessParameters ); + return; + } + + if (pNewPostProcessController != m_pCurrentPostProcessController) + m_pCurrentPostProcessController = pNewPostProcessController; + + // Start a lerp timer if the parameters changed, regardless of whether the controller changed + if (m_LerpEndPostProcessParameters != pNewPostProcessController->m_PostProcessParameters) + { + m_LerpStartPostProcessParameters = m_CurrentPostProcessParameters; + m_LerpEndPostProcessParameters = pNewPostProcessController ? pNewPostProcessController->m_PostProcessParameters : m_CurrentPostProcessParameters; + + float flFadeTime = pNewPostProcessController ? pNewPostProcessController->m_PostProcessParameters.m_flParameters[PPPN_FADE_TIME] : 0.0f; + if (flFadeTime <= 0.0f) + { + flFadeTime = 0.001f; + } + + m_PostProcessLerpTimer.Start( flFadeTime ); + } +#ifdef MAPBASE + // HACKHACK: Needs to be checked here because OnRestore() doesn't seem to run before a lerp begins + else if (g_bPostProcessNeedsRestore) + { + // The player just loaded a saved game. + // Don't fade parameters from 0; instead, take what's already there and assume they were already active. + // (we have no way of knowing if they were in the middle of a lerp) + m_PostProcessLerpTimer.Invalidate(); + g_bPostProcessNeedsRestore = false; + } +#endif + + // Lerp between old and new parameters + float flLerpFactor = 1.0f - m_PostProcessLerpTimer.GetRemainingRatio(); + for (int nParameter = 0; nParameter < POST_PROCESS_PARAMETER_COUNT; ++nParameter) + { + m_CurrentPostProcessParameters.m_flParameters[nParameter] = + Lerp( + flLerpFactor, + m_LerpStartPostProcessParameters.m_flParameters[nParameter], + m_LerpEndPostProcessParameters.m_flParameters[nParameter] ); + } + + SetPostProcessParams( &m_CurrentPostProcessParameters ); +} + class CHudChat; bool PlayerNameNotSetYet( const char *pszName ) diff --git a/mp/src/game/client/clientmode_shared.h b/mp/src/game/client/clientmode_shared.h index beef0ad9..76ec95c8 100644 --- a/mp/src/game/client/clientmode_shared.h +++ b/mp/src/game/client/clientmode_shared.h @@ -42,6 +42,10 @@ class CReplayReminderPanel; #define USERID2PLAYER(i) ToBasePlayer( ClientEntityList().GetEnt( engine->GetPlayerForUserID( i ) ) ) +#ifdef MAPBASE +#define DEMO_AUTORECORD 1 +#endif + extern IClientMode *GetClientModeNormal(); // must be implemented // This class implements client mode functionality common to HL2 and TF2. @@ -62,6 +66,10 @@ public: virtual void LevelInit( const char *newmap ); virtual void LevelShutdown( void ); +#ifdef DEMO_AUTORECORD + virtual void AutoRecord( const char *map ); +#endif + virtual void Enable(); virtual void Disable(); virtual void Layout(); @@ -156,6 +164,13 @@ private: vgui::HCursor m_CursorNone; CBaseHudWeaponSelection *m_pWeaponSelection; int m_nRootSize[2]; + + void UpdatePostProcessingEffects(); + + const C_PostProcessController* m_pCurrentPostProcessController; + PostProcessParameters_t m_CurrentPostProcessParameters; + PostProcessParameters_t m_LerpStartPostProcessParameters, m_LerpEndPostProcessParameters; + CountdownTimer m_PostProcessLerpTimer; }; #endif // CLIENTMODE_NORMAL_H diff --git a/mp/src/game/client/clientshadowmgr.cpp b/mp/src/game/client/clientshadowmgr.cpp index bd3e2c2a..fcd0155d 100644 --- a/mp/src/game/client/clientshadowmgr.cpp +++ b/mp/src/game/client/clientshadowmgr.cpp @@ -81,6 +81,16 @@ #include "toolframework_client.h" #include "bonetoworldarray.h" #include "cmodel.h" +#ifdef MAPBASE +#include "renderparm.h" +#endif +#ifdef ASW_PROJECTED_TEXTURES +#include "flashlighteffect.h" +#endif +#ifdef DYNAMIC_RTT_SHADOWS +#include "debugoverlay_shared.h" +#include "worldlight.h" +#endif // memdbgon must be the last include file in a .cpp file!!! @@ -89,6 +99,12 @@ static ConVar r_flashlightdrawfrustum( "r_flashlightdrawfrustum", "0" ); static ConVar r_flashlightmodels( "r_flashlightmodels", "1" ); static ConVar r_shadowrendertotexture( "r_shadowrendertotexture", "0" ); +#ifdef ASW_PROJECTED_TEXTURES +static ConVar r_shadow_lightpos_lerptime( "r_shadow_lightpos_lerptime", "0.5" ); +static ConVar r_shadowfromworldlights_debug( "r_shadowfromworldlights_debug", "0", FCVAR_CHEAT ); +static ConVar r_shadow_shortenfactor( "r_shadow_shortenfactor", "2" , 0, "Makes shadows cast from local lights shorter" ); +static ConVar r_shadow_mincastintensity( "r_shadow_mincastintensity", "0.3", FCVAR_CHEAT, "Minimum brightness of a light to be classed as shadow casting", true, 0, false, 0 ); +#endif static ConVar r_flashlight_version2( "r_flashlight_version2", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); ConVar r_flashlightdepthtexture( "r_flashlightdepthtexture", "1" ); @@ -96,10 +112,18 @@ ConVar r_flashlightdepthtexture( "r_flashlightdepthtexture", "1" ); #if defined( _X360 ) ConVar r_flashlightdepthres( "r_flashlightdepthres", "512" ); #else +#ifdef MAPBASE +ConVar r_flashlightdepthres( "r_flashlightdepthres", "2048" ); +#else ConVar r_flashlightdepthres( "r_flashlightdepthres", "1024" ); #endif +#endif +#ifdef ASW_PROJECTED_TEXTURES +ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "1" ); +#else ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" ); +#endif #ifdef _WIN32 #pragma warning( disable: 4701 ) @@ -782,7 +806,11 @@ public: void RestoreRenderState(); // Computes a rough bounding box encompassing the volume of the shadow +#ifdef DYNAMIC_RTT_SHADOWS + void ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs ); +#else void ComputeShadowBBox( IClientRenderable *pRenderable, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs ); +#endif bool WillParentRenderBlobbyShadow( IClientRenderable *pRenderable ); @@ -794,6 +822,13 @@ public: r_shadows_gamecontrol.SetValue( bDisabled != 1 ); } +#ifdef DYNAMIC_RTT_SHADOWS + // Toggle shadow casting from world light sources + virtual void SetShadowFromWorldLightsEnabled( bool bEnable ); + void SuppressShadowFromWorldLights( bool bSuppress ); + bool IsShadowingFromWorldLights() const { return m_bShadowFromWorldLights && !m_bSuppressShadowFromWorldLights; } +#endif + private: enum { @@ -811,8 +846,16 @@ private: unsigned short m_Flags; VMatrix m_WorldToShadow; Vector2D m_WorldSize; +#ifdef DYNAMIC_RTT_SHADOWS + Vector m_ShadowDir; +#endif Vector m_LastOrigin; QAngle m_LastAngles; +#ifdef DYNAMIC_RTT_SHADOWS + Vector m_CurrentLightPos; // When shadowing from local lights, stores the position of the currently shadowing light + Vector m_TargetLightPos; // When shadowing from local lights, stores the position of the new shadowing light + float m_LightPosLerp; // Lerp progress when going from current to target light +#endif TextureHandle_t m_ShadowTexture; CTextureReference m_ShadowDepthTexture; int m_nRenderFrame; @@ -825,6 +868,11 @@ private: void UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle ); void UpdateShadow( ClientShadowHandle_t handle, bool force ); +#ifdef DYNAMIC_RTT_SHADOWS + // Updates shadow cast direction when shadowing from world lights + void UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle ); +#endif + // Gets the entity whose shadow this shadow will render into IClientRenderable *GetParentShadowEntity( ClientShadowHandle_t handle ); @@ -842,6 +890,10 @@ private: void BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState ); +#ifdef ASW_PROJECTED_TEXTURES + void BuildOrthoWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState ); +#endif + // Update a shadow void UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force ); @@ -910,6 +962,9 @@ private: // Returns renderable-specific shadow info float GetShadowDistance( IClientRenderable *pRenderable ) const; const Vector &GetShadowDirection( IClientRenderable *pRenderable ) const; +#ifdef DYNAMIC_RTT_SHADOWS + const Vector &GetShadowDirection( ClientShadowHandle_t shadowHandle ) const; +#endif // Initialize, shutdown render-to-texture shadows void InitDepthTextureShadows(); @@ -933,6 +988,11 @@ private: // Set and clear flashlight target renderable void SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity ); +#ifdef ASW_PROJECTED_TEXTURES + // Get current frustum extents + void GetFrustumExtents( ClientShadowHandle_t handle, Vector &vecMin, Vector &vecMax ); +#endif + // Set flashlight light world flag void SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld ); @@ -941,9 +1001,18 @@ private: // Builds a list of active shadows requiring shadow depth renders int BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows ); +#ifdef ASW_PROJECTED_TEXTURES + // Builds a list of active flashlights + int BuildActiveFlashlightList( const CViewSetup &viewSetup, int nMaxFlashlights, ClientShadowHandle_t *pActiveFlashlights ); +#endif + // Sets the view's active flashlight render state void SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights ); +#ifdef DYNAMIC_RTT_SHADOWS + void UpdateDirtyShadow( ClientShadowHandle_t handle ); +#endif + private: Vector m_SimpleShadowDir; color32 m_AmbientLightColor; @@ -963,6 +1032,10 @@ private: CUtlRBTree< ClientShadowHandle_t, unsigned short > m_DirtyShadows; CUtlVector< ClientShadowHandle_t > m_TransparentShadows; +#ifdef ASW_PROJECTED_TEXTURES + int m_nPrevFrameCount; +#endif + // These members maintain current state of depth texturing (size and global active state) // If either changes in a frame, PreRender() will catch it and do the appropriate allocation, deallocation or reallocation bool m_bDepthTextureActive; @@ -972,6 +1045,11 @@ private: CUtlVector< bool > m_DepthTextureCacheLocks; int m_nMaxDepthTextureShadows; +#ifdef DYNAMIC_RTT_SHADOWS + bool m_bShadowFromWorldLights; + bool m_bSuppressShadowFromWorldLights; +#endif + friend class CVisibleShadowList; friend class CVisibleShadowFrustumList; }; @@ -1064,6 +1142,12 @@ void CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle ) if ( shadow.m_nRenderFrame == gpGlobals->framecount ) return; +#ifdef ASW_PROJECTED_TEXTURES + // Don't bother with flashlights + if ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 ) + return; +#endif + // We don't need to bother with it if it's not render-to-texture if ( s_ClientShadowMgr.GetActualShadowCastType( clientShadowHandle ) != SHADOWS_RENDER_TO_TEXTURE ) return; @@ -1088,7 +1172,11 @@ void CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle ) // Compute a box surrounding the shadow Vector vecAbsMins, vecAbsMaxs; +#ifdef DYNAMIC_RTT_SHADOWS + s_ClientShadowMgr.ComputeShadowBBox( pRenderable, shadow.m_ShadowHandle, vecAbsCenter, flRadius, &vecAbsMins, &vecAbsMaxs ); +#else s_ClientShadowMgr.ComputeShadowBBox( pRenderable, vecAbsCenter, flRadius, &vecAbsMins, &vecAbsMaxs ); +#endif // FIXME: Add distance check here? @@ -1167,7 +1255,14 @@ int CVisibleShadowList::FindShadows( const CViewSetup *pView, int nLeafCount, Le //----------------------------------------------------------------------------- CClientShadowMgr::CClientShadowMgr() : m_DirtyShadows( 0, 0, ShadowHandleCompareFunc ), +#ifdef ASW_PROJECTED_TEXTURES + m_nPrevFrameCount( -1 ), +#endif m_RenderToTextureActive( false ), +#ifdef DYNAMIC_RTT_SHADOWS + m_bShadowFromWorldLights( false ), + m_bSuppressShadowFromWorldLights( false ), +#endif m_bDepthTextureActive( false ) { m_nDepthTextureResolution = r_flashlightdepthres.GetInt(); @@ -1271,6 +1366,15 @@ CON_COMMAND_F( r_shadowblobbycutoff, "some shadow stuff", FCVAR_CHEAT ) } } +#ifdef DYNAMIC_RTT_SHADOWS +void OnShadowFromWorldLights( IConVar *var, const char *pOldValue, float flOldValue ); +static ConVar r_shadowfromworldlights( "r_shadowfromworldlights", "1", FCVAR_NONE, "Enable shadowing from world lights", OnShadowFromWorldLights ); +void OnShadowFromWorldLights( IConVar *var, const char *pOldValue, float flOldValue ) +{ + s_ClientShadowMgr.SuppressShadowFromWorldLights( !r_shadowfromworldlights.GetBool() ); +} +#endif + static void ShadowRestoreFunc( int nChangeFlags ) { s_ClientShadowMgr.RestoreRenderState(); @@ -1290,8 +1394,14 @@ bool CClientShadowMgr::Init() SetShadowBlobbyCutoffArea( 0.005 ); +#ifndef MAPBASE bool bTools = CommandLine()->CheckParm( "-tools" ) != NULL; m_nMaxDepthTextureShadows = bTools ? 4 : 1; // Just one shadow depth texture in games, more in tools +#else + // 5 lets mappers use up to 4 shadow-casting projected textures, which is better than 3. + int iNumShadows = CommandLine()->ParmValue( "-numshadowtextures", 5 ); + m_nMaxDepthTextureShadows = iNumShadows; +#endif bool bLowEnd = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 ); @@ -1336,6 +1446,11 @@ void CClientShadowMgr::InitDepthTextureShadows() { VPROF_BUDGET( "CClientShadowMgr::InitDepthTextureShadows", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ); +#if defined(MAPBASE) //&& !defined(ASW_PROJECTED_TEXTURES) + // SAUL: set m_nDepthTextureResolution to the depth resolution we want + m_nDepthTextureResolution = r_flashlightdepthres.GetInt(); +#endif + if( !m_bDepthTextureActive ) { m_bDepthTextureActive = true; @@ -1351,8 +1466,13 @@ void CClientShadowMgr::InitDepthTextureShadows() // only need the dummy surface, don't care about color results m_DummyColorTexture.InitRenderTargetTexture( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, MATERIAL_RT_DEPTH_SHARED, false, "_rt_ShadowDummy" ); m_DummyColorTexture.InitRenderTargetSurface( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), IMAGE_FORMAT_BGR565, true ); +#else +#if defined(MAPBASE) //&& !defined(ASW_PROJECTED_TEXTURES) + // SAUL: we want to create a render target of specific size, so use RT_SIZE_NO_CHANGE + m_DummyColorTexture.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_NO_CHANGE, nullFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_ShadowDummy" ); #else m_DummyColorTexture.InitRenderTarget( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, nullFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_ShadowDummy" ); +#endif #endif // Create some number of depth-stencil textures @@ -1371,9 +1491,20 @@ void CClientShadowMgr::InitDepthTextureShadows() // surface is effectively never used depthTex.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName ); depthTex.InitRenderTargetSurface( 1, 1, dstFormat, false ); +#else +#if defined(MAPBASE) //&& !defined(ASW_PROJECTED_TEXTURES) + // SAUL: we want to create a *DEPTH TEXTURE* of specific size, so use RT_SIZE_NO_CHANGE and MATERIAL_RT_DEPTH_ONLY + // However, MATERIAL_RT_DEPTH_ONLY forces point filtering to be enabled which negatively affect PCF, so the standard MATERIAL_RT_DEPTH_NONE works better. + depthTex.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_NO_CHANGE, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName ); #else depthTex.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName ); #endif +#endif + +#if defined(MAPBASE) //&& !defined(ASW_PROJECTED_TEXTURES) + // SAUL: ensure the depth texture size wasn't changed + Assert(depthTex->GetActualWidth() == m_nDepthTextureResolution); +#endif if ( i == 0 ) { @@ -1514,6 +1645,15 @@ void CClientShadowMgr::LevelInitPreEntity() { m_bUpdatingDirtyShadows = false; +#ifdef DYNAMIC_RTT_SHADOWS + // Default setting for this, can be overridden by shadow control entities +#ifdef MAPBASE + SetShadowFromWorldLightsEnabled( false ); +#else + SetShadowFromWorldLightsEnabled( true ); +#endif +#endif + Vector ambientColor; engine->GetAmbientLightColor( ambientColor ); ambientColor *= 3; @@ -1698,6 +1838,158 @@ const Vector &CClientShadowMgr::GetShadowDirection( IClientRenderable *pRenderab return vecResult; } +#ifdef DYNAMIC_RTT_SHADOWS +const Vector &CClientShadowMgr::GetShadowDirection( ClientShadowHandle_t shadowHandle ) const +{ + Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE ); + + IClientRenderable* pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[shadowHandle].m_Entity ); + Assert( pRenderable ); + + if ( !IsShadowingFromWorldLights() ) + { + return GetShadowDirection( pRenderable ); + } + + Vector &vecResult = AllocTempVector(); + vecResult = m_Shadows[shadowHandle].m_ShadowDir; + + // Allow the renderable to override the default + pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) ); + + return vecResult; +} + +void CClientShadowMgr::UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle ) +{ + Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE ); + + ClientShadow_t& shadow = m_Shadows[shadowHandle]; + + IClientRenderable* pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity ); + + // TODO: Figure out why this still gets hit + Assert( pRenderable ); + if ( !pRenderable ) + { + DevWarning( "%s(): Skipping shadow with invalid client renderable (shadow handle %d)\n", __FUNCTION__, shadowHandle ); + return; + } + + Vector bbMin, bbMax; + pRenderable->GetRenderBoundsWorldspace( bbMin, bbMax ); + Vector origin( 0.5f * ( bbMin + bbMax ) ); + origin.z = bbMin.z; // Putting origin at the bottom of the bounding box makes the shadows a little shorter + + Vector lightPos; + Vector lightBrightness; + + if ( shadow.m_LightPosLerp >= 1.0f ) // skip finding new light source if we're in the middle of a lerp + { + // Calculate minimum brightness squared + float flMinBrightnessSqr = r_shadow_mincastintensity.GetFloat(); + flMinBrightnessSqr *= flMinBrightnessSqr; + + if(g_pWorldLights->GetBrightestLightSource(pRenderable->GetRenderOrigin(), lightPos, lightBrightness) == false || + lightBrightness.LengthSqr() < flMinBrightnessSqr ) + { + // didn't find a light source at all, use default shadow direction + // TODO: Could switch to using blobby shadow in this case + lightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + } + } + + if ( shadow.m_LightPosLerp == FLT_MAX ) // first light pos ever, just init + { + shadow.m_CurrentLightPos = lightPos; + shadow.m_TargetLightPos = lightPos; + shadow.m_LightPosLerp = 1.0f; + } + else if ( shadow.m_LightPosLerp < 1.0f ) + { + // We're in the middle of a lerp from current to target light. Finish it. + shadow.m_LightPosLerp += gpGlobals->frametime * 1.0f/r_shadow_lightpos_lerptime.GetFloat(); + shadow.m_LightPosLerp = clamp( shadow.m_LightPosLerp, 0.0f, 1.0f ); + + Vector currLightPos( shadow.m_CurrentLightPos ); + Vector targetLightPos( shadow.m_TargetLightPos ); + if ( currLightPos.x == FLT_MAX ) + { + currLightPos = origin - 200.0f * GetShadowDirection(); + } + if ( targetLightPos.x == FLT_MAX ) + { + targetLightPos = origin - 200.0f * GetShadowDirection(); + } + + // lerp light pos + Vector v1 = origin - shadow.m_CurrentLightPos; + v1.NormalizeInPlace(); + + Vector v2 = origin - shadow.m_TargetLightPos; + v2.NormalizeInPlace(); + + // SAULUNDONE: caused over top sweeping far too often +#if 0 + if ( v1.Dot( v2 ) < 0.0f ) + { + // if change in shadow angle is more than 90 degrees, lerp over the renderable's top to avoid long sweeping shadows + Vector fakeOverheadLightPos( origin.x, origin.y, origin.z + 200.0f ); + if( shadow.m_LightPosLerp < 0.5f ) + { + lightPos = Lerp( 2.0f * shadow.m_LightPosLerp, currLightPos, fakeOverheadLightPos ); + } + else + { + lightPos = Lerp( 2.0f * shadow.m_LightPosLerp - 1.0f, fakeOverheadLightPos, targetLightPos ); + } + } + else +#endif + { + lightPos = Lerp( shadow.m_LightPosLerp, currLightPos, targetLightPos ); + } + + if ( shadow.m_LightPosLerp >= 1.0f ) + { + shadow.m_CurrentLightPos = shadow.m_TargetLightPos; + } + } + else if ( shadow.m_LightPosLerp >= 1.0f ) + { + // check if we have a new closest light position and start a new lerp + float flDistSq = ( lightPos - shadow.m_CurrentLightPos ).LengthSqr(); + + if ( flDistSq > 1.0f ) + { + // light position has changed, which means we got a new light source. Initiate a lerp + shadow.m_TargetLightPos = lightPos; + shadow.m_LightPosLerp = 0.0f; + } + + lightPos = shadow.m_CurrentLightPos; + } + + if ( lightPos.x == FLT_MAX ) + { + lightPos = origin - 200.0f * GetShadowDirection(); + } + + Vector vecResult( origin - lightPos ); + vecResult.NormalizeInPlace(); + + vecResult.z *= r_shadow_shortenfactor.GetFloat(); + vecResult.NormalizeInPlace(); + + shadow.m_ShadowDir = vecResult; + + if ( r_shadowfromworldlights_debug.GetBool() ) + { + NDebugOverlay::Line( lightPos, origin, 255, 255, 0, false, 0.0f ); + } +} +#endif + //----------------------------------------------------------------------------- // Sets the shadow distance @@ -1818,6 +2110,12 @@ ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandl shadow.m_ClientLeafShadowHandle = ClientLeafSystem()->AddShadow( h, flags ); shadow.m_Flags = flags; shadow.m_nRenderFrame = -1; +#ifdef DYNAMIC_RTT_SHADOWS + shadow.m_ShadowDir = GetShadowDirection(); + shadow.m_CurrentLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + shadow.m_TargetLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + shadow.m_LightPosLerp = FLT_MAX; +#endif shadow.m_LastOrigin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); shadow.m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX ); Assert( ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 ) != @@ -1837,12 +2135,21 @@ ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandl pShadowProxyData = (void*)(uintp)h; } +#ifdef ASW_PROJECTED_TEXTURES + if( ( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) || ( flags & ( SHADOW_FLAGS_FLASHLIGHT ) ) ) + { + pShadowMaterial = NULL; // these materials aren't used for shadow depth texture shadows. + pShadowModelMaterial = NULL; + pShadowProxyData = (void*)(uintp)h; + } +#else if( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) { pShadowMaterial = m_RenderShadow; pShadowModelMaterial = m_RenderModelShadow; pShadowProxyData = (void*)(uintp)h; } +#endif int createShadowFlags; if( flags & SHADOW_FLAGS_FLASHLIGHT ) @@ -1905,7 +2212,27 @@ void CClientShadowMgr::UpdateFlashlightState( ClientShadowHandle_t shadowHandle, { VPROF_BUDGET( "CClientShadowMgr::UpdateFlashlightState", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ); +#ifdef ASW_PROJECTED_TEXTURES + if( flashlightState.m_bEnableShadows && r_flashlightdepthtexture.GetBool() ) + { + m_Shadows[shadowHandle].m_Flags |= SHADOW_FLAGS_USE_DEPTH_TEXTURE; + } + else + { + m_Shadows[shadowHandle].m_Flags &= ~SHADOW_FLAGS_USE_DEPTH_TEXTURE; + } + + if ( flashlightState.m_bOrtho ) + { + BuildOrthoWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState ); + } + else + { + BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState ); + } +#else BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState ); +#endif shadowmgr->UpdateFlashlightState( m_Shadows[shadowHandle].m_ShadowHandle, flashlightState ); } @@ -2016,6 +2343,40 @@ void CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorl MatrixMultiply( matPerspective, matWorldToShadowView, matWorldToShadow ); } +#ifdef ASW_PROJECTED_TEXTURES +void CClientShadowMgr::BuildOrthoWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState ) +{ + VPROF_BUDGET( "CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ); + + // Buildworld to shadow matrix, then perspective projection and concatenate + VMatrix matWorldToShadowView, matPerspective; + BuildWorldToShadowMatrix( matWorldToShadowView, flashlightState.m_vecLightOrigin, + flashlightState.m_quatOrientation ); + + MatrixBuildOrtho( matPerspective, + flashlightState.m_fOrthoLeft, flashlightState.m_fOrthoTop, flashlightState.m_fOrthoRight, flashlightState.m_fOrthoBottom, + flashlightState.m_NearZ, flashlightState.m_FarZ ); + + // Shift it z/y to 0 to -2 space + VMatrix addW; + addW.Identity(); + addW[0][3] = -1.0f; + addW[1][3] = -1.0f; + addW[2][3] = 0.0f; + MatrixMultiply( addW, matPerspective, matPerspective ); + + // Flip x/y to positive 0 to 1... flip z to negative + VMatrix scaleHalf; + scaleHalf.Identity(); + scaleHalf[0][0] = -0.5f; + scaleHalf[1][1] = -0.5f; + scaleHalf[2][2] = -1.0f; + MatrixMultiply( scaleHalf, matPerspective, matPerspective ); + + MatrixMultiply( matPerspective, matWorldToShadowView, matWorldToShadow ); +} +#endif + //----------------------------------------------------------------------------- // Compute the shadow origin and attenuation start distance //----------------------------------------------------------------------------- @@ -2289,7 +2650,11 @@ void CClientShadowMgr::BuildOrthoShadow( IClientRenderable* pRenderable, AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] ); vec[1] *= -1.0f; +#ifdef DYNAMIC_RTT_SHADOWS + Vector vecShadowDir = GetShadowDirection( handle ); +#else Vector vecShadowDir = GetShadowDirection( pRenderable ); +#endif // Project the shadow casting direction into the space of the object Vector localShadowDir; @@ -2375,7 +2740,11 @@ void CClientShadowMgr::BuildOrthoShadow( IClientRenderable* pRenderable, // Compute extra clip planes to prevent poke-thru // FIXME!!!!!!!!!!!!!! Removing this for now since it seems to mess up the blobby shadows. +#ifdef ASW_PROJECTED_TEXTURES + ComputeExtraClipPlanes( pRenderable, handle, vec, mins, maxs, localShadowDir ); +#else // ComputeExtraClipPlanes( pEnt, handle, vec, mins, maxs, localShadowDir ); +#endif // Add the shadow to the client leaf system so it correctly marks // leafs as being affected by a particular shadow @@ -2472,7 +2841,11 @@ void CClientShadowMgr::BuildRenderToTextureShadow( IClientRenderable* pRenderabl AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] ); vec[1] *= -1.0f; +#ifdef DYNAMIC_RTT_SHADOWS + Vector vecShadowDir = GetShadowDirection( handle ); +#else Vector vecShadowDir = GetShadowDirection( pRenderable ); +#endif // Debugging aid // const model_t *pModel = pRenderable->GetModel(); @@ -2864,6 +3237,13 @@ bool CClientShadowMgr::ShouldUseParentShadow( IClientRenderable *pRenderable ) //----------------------------------------------------------------------------- void CClientShadowMgr::PreRender() { +#ifdef ASW_PROJECTED_TEXTURES + // only update shadows once per frame + if( gpGlobals->framecount == m_nPrevFrameCount ) + return; + m_nPrevFrameCount = gpGlobals->framecount; +#endif + VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_RENDERING ); MDLCACHE_CRITICAL_SECTION(); @@ -2936,8 +3316,12 @@ void CClientShadowMgr::PreRender() { MDLCACHE_CRITICAL_SECTION(); ClientShadowHandle_t& handle = m_DirtyShadows[ i ]; +#ifdef DYNAMIC_RTT_SHADOWS + UpdateDirtyShadow(handle); +#else Assert( m_Shadows.IsValidIndex( handle ) ); UpdateProjectedTextureInternal( handle, false ); +#endif i = m_DirtyShadows.NextInorder(i); } m_DirtyShadows.RemoveAll(); @@ -2953,6 +3337,20 @@ void CClientShadowMgr::PreRender() m_bUpdatingDirtyShadows = false; } +#ifdef DYNAMIC_RTT_SHADOWS +//----------------------------------------------------------------------------- +// Updates a single dirty shadow +//----------------------------------------------------------------------------- +void CClientShadowMgr::UpdateDirtyShadow( ClientShadowHandle_t handle ) +{ + Assert( m_Shadows.IsValidIndex( handle ) ); + if ( IsShadowingFromWorldLights() ) + { + UpdateShadowDirectionFromLocalLightSource( handle ); + } + UpdateProjectedTextureInternal( handle, false ); +} +#endif //----------------------------------------------------------------------------- // Gets the entity whose shadow this shadow will render into @@ -3127,7 +3525,11 @@ void CClientShadowMgr::UpdateShadow( ClientShadowHandle_t handle, bool force ) const Vector& origin = pRenderable->GetRenderOrigin(); const QAngle& angles = pRenderable->GetRenderAngles(); +#ifdef DYNAMIC_RTT_SHADOWS + if (force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles) || shadow.m_LightPosLerp < 1.0f) +#else if (force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles)) +#endif { // Store off the new pos/orientation VectorCopy( origin, shadow.m_LastOrigin ); @@ -3246,12 +3648,20 @@ void CClientShadowMgr::ComputeBoundingSphere( IClientRenderable* pRenderable, Ve //----------------------------------------------------------------------------- // Computes a rough AABB encompassing the volume of the shadow //----------------------------------------------------------------------------- +#ifdef DYNAMIC_RTT_SHADOWS +void CClientShadowMgr::ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs ) +#else void CClientShadowMgr::ComputeShadowBBox( IClientRenderable *pRenderable, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs ) +#endif { // This is *really* rough. Basically we simply determine the // maximum shadow casting length and extrude the box by that distance +#ifdef DYNAMIC_RTT_SHADOWS + Vector vecShadowDir = GetShadowDirection( shadowHandle ); +#else Vector vecShadowDir = GetShadowDirection( pRenderable ); +#endif for (int i = 0; i < 3; ++i) { float flShadowCastDistance = GetShadowDistance( pRenderable ); @@ -3349,7 +3759,11 @@ bool CClientShadowMgr::CullReceiver( ClientShadowHandle_t handle, IClientRendera if (foundSeparatingPlane) { // Compute which side of the plane the renderable is on.. +#ifdef DYNAMIC_RTT_SHADOWS + Vector vecShadowDir = GetShadowDirection( handle ); +#else Vector vecShadowDir = GetShadowDirection( pSourceRenderable ); +#endif float shadowDot = DotProduct( vecShadowDir, plane.normal ); float receiverDot = DotProduct( plane.normal, origin ); float sourceDot = DotProduct( plane.normal, originSource ); @@ -3836,6 +4250,11 @@ void CClientShadowMgr::AdvanceFrame() //----------------------------------------------------------------------------- int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows ) { +#ifdef ASW_PROJECTED_TEXTURES + Frustum_t viewFrustum; + GeneratePerspectiveFrustum( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, viewSetup.m_flAspectRatio, viewFrustum ); +#endif + int nActiveDepthShadowCount = 0; for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) ) { @@ -3860,7 +4279,13 @@ int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, i // FIXME: Could do other sorts of culling here, such as frustum-frustum test, distance etc. // If it's not in the view frustum, move on +#ifdef MAPBASE + if ( !flashlightState.m_bAlwaysDraw && !flashlightState.m_bOrtho && R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) ) +#elif ASW_PROJECTED_TEXTURES + if ( !flashlightState.m_bOrtho && R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) ) +#else if ( R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) ) +#endif { shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 ); continue; @@ -3885,6 +4310,53 @@ int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, i } +#ifdef ASW_PROJECTED_TEXTURES +//----------------------------------------------------------------------------- +// Re-render shadow depth textures that lie in the leaf list +//----------------------------------------------------------------------------- +int CClientShadowMgr::BuildActiveFlashlightList( const CViewSetup &viewSetup, int nMaxFlashlights, ClientShadowHandle_t *pActiveFlashlights ) +{ + int nActiveFlashlightCount = 0; + for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) ) + { + ClientShadow_t& shadow = m_Shadows[i]; + + if ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 ) + continue; + + // Calculate an AABB around the shadow frustum + Vector vecAbsMins, vecAbsMaxs; + CalculateAABBFromProjectionMatrix( shadow.m_WorldToShadow, &vecAbsMins, &vecAbsMaxs ); + + Frustum_t viewFrustum; + GeneratePerspectiveFrustum( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, viewSetup.m_flAspectRatio, viewFrustum ); + + // FIXME: Could do other sorts of culling here, such as frustum-frustum test, distance etc. + // If it's not in the view frustum, move on + if ( R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) ) + { + continue; + } + + if ( nActiveFlashlightCount >= nMaxFlashlights ) + { + static bool s_bOverflowWarning = false; + if ( !s_bOverflowWarning ) + { + Warning( "Too many flashlights rendered in a single view!\n" ); + Assert( 0 ); + s_bOverflowWarning = true; + } + //shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 ); + continue; + } + + pActiveFlashlights[nActiveFlashlightCount++] = i; + } + return nActiveFlashlightCount; +} +#endif + //----------------------------------------------------------------------------- // Sets the view's active flashlight render state //----------------------------------------------------------------------------- @@ -3908,12 +4380,56 @@ void CClientShadowMgr::SetViewFlashlightState( int nActiveFlashlightCount, Clien } } +#ifdef ASW_PROJECTED_TEXTURES +void AddPointToExtentsHelper( const VMatrix &flashlightToWorld, const Vector &vecPos, Vector &vecMin, Vector &vecMax ) +{ + Vector worldSpacePos; + + Vector3DMultiplyPositionProjective( flashlightToWorld, vecPos, worldSpacePos ); + VectorMin( vecMin, worldSpacePos, vecMin ); + VectorMax( vecMax, worldSpacePos, vecMax ); +} + + +void CClientShadowMgr::GetFrustumExtents( ClientShadowHandle_t handle, Vector &vecMin, Vector &vecMax ) +{ + Assert( m_Shadows.IsValidIndex( handle ) ); + + CClientShadowMgr::ClientShadow_t &shadow = m_Shadows[ handle ]; + + VMatrix flashlightToWorld; + MatrixInverseGeneral( shadow.m_WorldToShadow, flashlightToWorld ); + + vecMin = Vector( FLT_MAX, FLT_MAX, FLT_MAX ); + vecMax = Vector( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + + AddPointToExtentsHelper( flashlightToWorld, Vector( 0.0f, 0.0f, 0.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 0.0f, 0.0f, 1.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 0.0f, 1.0f, 0.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 1.0f, 0.0f, 0.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 0.0f, 1.0f, 1.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 1.0f, 0.0f, 1.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 1.0f, 1.0f, 0.0f ), vecMin, vecMax ); + AddPointToExtentsHelper( flashlightToWorld, Vector( 1.0f, 1.0f, 1.0f ), vecMin, vecMax ); +} +#endif //----------------------------------------------------------------------------- // Re-render shadow depth textures that lie in the leaf list //----------------------------------------------------------------------------- void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) { +#ifdef ASW_PROJECTED_TEXTURES + if ( !r_flashlightdepthtexture.GetBool() ) + { + // Build list of active flashlights + ClientShadowHandle_t pActiveFlashlights[16]; + int nActiveFlashlights = BuildActiveFlashlightList( viewSetup, ARRAYSIZE( pActiveFlashlights ), pActiveFlashlights ); + SetViewFlashlightState( nActiveFlashlights, pActiveFlashlights ); + return; + } +#endif + VPROF_BUDGET( "CClientShadowMgr::ComputeShadowDepthTextures", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ); CMatRenderContextPtr pRenderContext( materials ); @@ -3929,6 +4445,10 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) { ClientShadow_t& shadow = m_Shadows[ pActiveDepthShadows[j] ]; +#ifdef ASW_PROJECTED_TEXTURES + FlashlightState_t& flashlightState = const_cast( shadowmgr->GetFlashlightState( shadow.m_ShadowHandle ) ); +#endif + CTextureReference shadowDepthTexture; bool bGotShadowDepthTexture = LockShadowDepthTexture( &shadowDepthTexture ); if ( !bGotShadowDepthTexture ) @@ -3943,6 +4463,12 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) Assert(0); shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 ); +#ifdef MAPBASE + if ( j <= ( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_LAST - INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST ) ) + { + pRenderContext->SetIntRenderingParameter( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST + j, 0 ); + } +#endif continue; } @@ -3951,11 +4477,30 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) shadowView.x = shadowView.y = 0; shadowView.width = shadowDepthTexture->GetActualWidth(); shadowView.height = shadowDepthTexture->GetActualHeight(); +#ifndef ASW_PROJECTED_TEXTURES shadowView.m_bOrtho = false; shadowView.m_bDoBloomAndToneMapping = false; +#endif // Copy flashlight parameters +#ifdef ASW_PROJECTED_TEXTURES + if ( !flashlightState.m_bOrtho ) + { + shadowView.m_bOrtho = false; + } + else + { + shadowView.m_bOrtho = true; + shadowView.m_OrthoLeft = flashlightState.m_fOrthoLeft; + shadowView.m_OrthoTop = flashlightState.m_fOrthoTop; + shadowView.m_OrthoRight = flashlightState.m_fOrthoRight; + shadowView.m_OrthoBottom = flashlightState.m_fOrthoBottom; + } + + shadowView.m_bDoBloomAndToneMapping = false; +#else const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle ); +#endif shadowView.fov = shadowView.fovViewmodel = flashlightState.m_fHorizontalFOVDegrees; shadowView.origin = flashlightState.m_vecLightOrigin; QuaternionAngles( flashlightState.m_quatOrientation, shadowView.angles ); // Convert from Quaternion to QAngle @@ -3976,6 +4521,19 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) // Render to the shadow depth texture with appropriate view view->UpdateShadowDepthTexture( m_DummyColorTexture, shadowDepthTexture, shadowView ); +#ifdef MAPBASE + if ( j <= ( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_LAST - INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST ) ) + { + pRenderContext->SetIntRenderingParameter( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST + j, int((ITexture*)shadowDepthTexture) ); + + FlashlightState_t state = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle ); + + state.m_nShadowQuality = state.m_nShadowQuality | ( ( j + 1 ) << 16 ); + + shadowmgr->UpdateFlashlightState( shadow.m_ShadowHandle, state ); + } +#endif + // Associate the shadow depth texture and stencil bit with the flashlight for use during scene rendering shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, shadowDepthTexture, 0 ); } @@ -4000,7 +4558,11 @@ void CClientShadowMgr::ComputeShadowTextures( const CViewSetup &view, int leafCo if ( !m_RenderToTextureActive || (r_shadows.GetInt() == 0) || r_shadows_gamecontrol.GetInt() == 0 ) return; +#ifdef ASW_PROJECTED_TEXTURES + m_bThreaded = ( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() ); +#else m_bThreaded = false;//( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() ); +#endif MDLCACHE_CRITICAL_SECTION(); // First grab all shadow textures we may want to render @@ -4184,6 +4746,28 @@ bool CClientShadowMgr::IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IC return false; } +#ifdef DYNAMIC_RTT_SHADOWS +void CClientShadowMgr::SetShadowFromWorldLightsEnabled( bool bEnable ) +{ + bool bIsShadowingFromWorldLights = IsShadowingFromWorldLights(); + m_bShadowFromWorldLights = bEnable; + if ( bIsShadowingFromWorldLights != IsShadowingFromWorldLights() ) + { + UpdateAllShadows(); + } +} + +void CClientShadowMgr::SuppressShadowFromWorldLights( bool bSuppress ) +{ + bool bIsShadowingFromWorldLights = IsShadowingFromWorldLights(); + m_bSuppressShadowFromWorldLights = bSuppress; + if ( bIsShadowingFromWorldLights != IsShadowingFromWorldLights() ) + { + UpdateAllShadows(); + } +} +#endif + //----------------------------------------------------------------------------- // A material proxy that resets the base texture to use the rendered shadow //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/detailobjectsystem.cpp b/mp/src/game/client/detailobjectsystem.cpp index 3e211bb0..726e2cd7 100644 --- a/mp/src/game/client/detailobjectsystem.cpp +++ b/mp/src/game/client/detailobjectsystem.cpp @@ -1471,6 +1471,7 @@ void CDetailObjectSystem::LevelInitPreEntity() } } +#ifndef MAPBASE if ( m_DetailObjects.Count() || m_DetailSpriteDict.Count() ) { // There are detail objects in the level, so precache the material @@ -1489,6 +1490,7 @@ void CDetailObjectSystem::LevelInitPreEntity() } } } +#endif int detailPropLightingLump; if( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) @@ -1512,6 +1514,32 @@ void CDetailObjectSystem::LevelInitPreEntity() void CDetailObjectSystem::LevelInitPostEntity() { +#ifdef MAPBASE + if ( m_DetailObjects.Count() || m_DetailSpriteDict.Count() ) + { + const char *pDetailSpriteMaterial = DETAIL_SPRITE_MATERIAL; + C_World *pWorld = GetClientWorldEntity(); + if ( pWorld && pWorld->GetDetailSpriteMaterial() && *(pWorld->GetDetailSpriteMaterial()) ) + pDetailSpriteMaterial = pWorld->GetDetailSpriteMaterial(); + + m_DetailSpriteMaterial.Init( pDetailSpriteMaterial, TEXTURE_GROUP_OTHER ); + PrecacheMaterial( pDetailSpriteMaterial ); + IMaterial *pMat = m_DetailSpriteMaterial; + + // adjust for non-square textures (cropped) + float flRatio = pMat->GetMappingWidth() / pMat->GetMappingHeight(); + if ( flRatio > 1.0 ) + { + for( int i = 0; iGetDetailSpriteMaterial() && *(pWorld->GetDetailSpriteMaterial()) ) @@ -1519,6 +1547,7 @@ void CDetailObjectSystem::LevelInitPostEntity() pDetailSpriteMaterial = pWorld->GetDetailSpriteMaterial(); } m_DetailSpriteMaterial.Init( pDetailSpriteMaterial, TEXTURE_GROUP_OTHER ); +#endif if ( GetDetailController() ) { @@ -1595,12 +1624,14 @@ void CDetailObjectSystem::UnserializeModelDict( CUtlBuffer& buf ) DetailModelDict_t dict; dict.m_pModel = (model_t *)engine->LoadModel( lump.m_Name, true ); +#ifndef MAPBASE // Don't allow vertex-lit models if (modelinfo->IsModelVertexLit(dict.m_pModel)) { Warning("Detail prop model %s is using vertex-lit materials!\nIt must use unlit materials!\n", lump.m_Name ); dict.m_pModel = (model_t *)engine->LoadModel( "models/error.mdl" ); } +#endif m_DetailObjectDict.AddToTail( dict ); } diff --git a/mp/src/game/client/flashlighteffect.cpp b/mp/src/game/client/flashlighteffect.cpp index 6385ad30..6733fc56 100644 --- a/mp/src/game/client/flashlighteffect.cpp +++ b/mp/src/game/client/flashlighteffect.cpp @@ -48,8 +48,16 @@ static ConVar r_flashlightvisualizetrace( "r_flashlightvisualizetrace", "0", FCV static ConVar r_flashlightambient( "r_flashlightambient", "0.0", FCVAR_CHEAT ); static ConVar r_flashlightshadowatten( "r_flashlightshadowatten", "0.35", FCVAR_CHEAT ); static ConVar r_flashlightladderdist( "r_flashlightladderdist", "40.0", FCVAR_CHEAT ); +#ifndef MAPBASE static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT ); +#else +static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "4", FCVAR_CHEAT ); +static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.00001", FCVAR_CHEAT ); +#endif +#ifdef MAPBASE +static ConVar r_flashlighttextureoverride( "r_flashlighttextureoverride", "", FCVAR_CHEAT ); +#endif void r_newflashlightCallback_f( IConVar *pConVar, const char *pOldString, float flOldValue ) @@ -78,6 +86,13 @@ CFlashlightEffect::CFlashlightEffect(int nEntIndex) r_newflashlight.SetValue( 0 ); } +#ifdef MAPBASE + if ( r_flashlighttextureoverride.GetString()[0] != '\0' ) + { + m_FlashlightTexture.Init( r_flashlighttextureoverride.GetString(), TEXTURE_GROUP_OTHER, true ); + } + else +#endif if ( g_pMaterialSystemHardwareConfig->SupportsBorderColor() ) { m_FlashlightTexture.Init( "effects/flashlight_border", TEXTURE_GROUP_OTHER, true ); diff --git a/mp/src/game/client/fx.cpp b/mp/src/game/client/fx.cpp index b7f12cbd..78a70a77 100644 --- a/mp/src/game/client/fx.cpp +++ b/mp/src/game/client/fx.cpp @@ -1256,6 +1256,13 @@ void FX_BuildTeslaHitbox( const CEffectData &data ) { Vector vColor( 1, 1, 1 ); +#ifdef MAPBASE + if ( data.m_bCustomColors ) + { + vColor = data.m_CustomColors.m_vecColor1; + } +#endif + C_BaseEntity *pEntity = ClientEntityList().GetEnt( data.entindex() ); C_BaseAnimating *pAnimating = pEntity ? pEntity->GetBaseAnimating() : NULL; if (!pAnimating) diff --git a/mp/src/game/client/fx_tracer.cpp b/mp/src/game/client/fx_tracer.cpp index cec013a6..79bc513e 100644 --- a/mp/src/game/client/fx_tracer.cpp +++ b/mp/src/game/client/fx_tracer.cpp @@ -40,11 +40,13 @@ Vector GetTracerOrigin( const CEffectData &data ) C_BaseEntity *pEnt = data.GetEntity(); -// This check should probably be for all multiplayer games, investigate later -#if defined( HL2MP ) || defined( TF_CLIENT_DLL ) - if ( pEnt && pEnt->IsDormant() ) - return vecStart; -#endif + // This check should probably be for all multiplayer games, investigate later + // 10/09/2008: It should. + if ( gpGlobals->maxClients > 1 ) + { + if ( pEnt && pEnt->IsDormant() ) + return vecStart; + } C_BaseCombatWeapon *pWpn = dynamic_cast( pEnt ); if ( pWpn && pWpn->ShouldDrawUsingViewModel() ) diff --git a/mp/src/game/client/hl2/c_basehlplayer.cpp b/mp/src/game/client/hl2/c_basehlplayer.cpp index fbc40eb2..17b3cf53 100644 --- a/mp/src/game/client/hl2/c_basehlplayer.cpp +++ b/mp/src/game/client/hl2/c_basehlplayer.cpp @@ -38,6 +38,11 @@ BEGIN_PREDICTION_DATA( C_BaseHLPlayer ) DEFINE_PRED_FIELD( m_fIsSprinting, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), END_PREDICTION_DATA() +// link to the correct class. +#if !defined ( HL2MP ) && !defined ( PORTAL ) +LINK_ENTITY_TO_CLASS( player, C_BaseHLPlayer ); +#endif + //----------------------------------------------------------------------------- // Purpose: Drops player's primary weapon //----------------------------------------------------------------------------- @@ -66,6 +71,11 @@ C_BaseHLPlayer::C_BaseHLPlayer() m_flZoomRate = 0.0f; m_flZoomStartTime = 0.0f; m_flSpeedMod = cl_forwardspeed.GetFloat(); + +#ifdef MAPBASE + ConVarRef scissor("r_flashlightscissor"); + scissor.SetValue("0"); +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/hl2/c_basehlplayer.h b/mp/src/game/client/hl2/c_basehlplayer.h index c6507e95..fe838cad 100644 --- a/mp/src/game/client/hl2/c_basehlplayer.h +++ b/mp/src/game/client/hl2/c_basehlplayer.h @@ -5,7 +5,6 @@ // $Workfile: $ // $NoKeywords: $ //=============================================================================// - #if !defined( C_BASEHLPLAYER_H ) #define C_BASEHLPLAYER_H #ifdef _WIN32 @@ -34,10 +33,17 @@ public: float GetZoom( void ); bool IsZoomed( void ) { return m_HL2Local.m_bZooming; } - bool IsSprinting( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_SPRINT; } + //Tony; minor cosmetic really, fix confusion by simply renaming this one; everything calls IsSprinting(), and this isn't really even used. + bool IsSprintActive( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_SPRINT; } bool IsFlashlightActive( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_FLASHLIGHT; } bool IsBreatherActive( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_BREATHER; } +#ifdef MAPBASE + bool IsCustomDevice0Active( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_CUSTOM0; } + bool IsCustomDevice1Active( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_CUSTOM1; } + bool IsCustomDevice2Active( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_CUSTOM2; } +#endif + virtual int DrawModel( int flags ); virtual void BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ); diff --git a/mp/src/game/client/hl2/c_env_starfield.cpp b/mp/src/game/client/hl2/c_env_starfield.cpp index 632c6f35..123dc0c1 100644 --- a/mp/src/game/client/hl2/c_env_starfield.cpp +++ b/mp/src/game/client/hl2/c_env_starfield.cpp @@ -90,6 +90,11 @@ void C_EnvStarfield::ClientThink( void ) if ( !m_bOn || !m_flDensity ) return; +#ifdef MAPBASE + if ( engine->IsPaused() ) + return; +#endif + PMaterialHandle hParticleMaterial = m_pEmitter->GetPMaterial( "effects/spark_noz" ); // Find a start & end point for the particle diff --git a/mp/src/game/client/hl2/c_script_intro.cpp b/mp/src/game/client/hl2/c_script_intro.cpp index eb97cb4f..835e0a82 100644 --- a/mp/src/game/client/hl2/c_script_intro.cpp +++ b/mp/src/game/client/hl2/c_script_intro.cpp @@ -10,6 +10,9 @@ #include "iviewrender.h" #include "view_shared.h" #include "viewrender.h" +#ifdef MAPBASE +#include "c_point_camera.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -53,6 +56,11 @@ private: float m_flBlendStartTime; bool m_bActive; EHANDLE m_hCameraEntity; +#ifdef MAPBASE + bool m_bDrawSky; + bool m_bDrawSky2; + bool m_bUseEyePosition; +#endif // Fades float m_flFadeColor[3]; // Server's desired fade color @@ -71,6 +79,11 @@ IMPLEMENT_CLIENTCLASS_DT( C_ScriptIntro, DT_ScriptIntro, CScriptIntro ) RecvPropFloat( RECVINFO( m_flNextBlendTime ) ), RecvPropFloat( RECVINFO( m_flBlendStartTime ) ), RecvPropBool( RECVINFO( m_bActive ) ), +#ifdef MAPBASE + RecvPropBool( RECVINFO( m_bDrawSky ) ), + RecvPropBool( RECVINFO( m_bDrawSky2 ) ), + RecvPropBool( RECVINFO( m_bUseEyePosition ) ), +#endif // Fov & fov blends RecvPropInt( RECVINFO( m_iFOV ) ), @@ -140,6 +153,10 @@ void C_ScriptIntro::PostDataUpdate( DataUpdateType_t updateType ) m_IntroData.m_vecCameraViewAngles = m_vecCameraViewAngles; m_IntroData.m_Passes.SetCount( 0 ); +#ifdef MAPBASE + m_IntroData.m_bDrawSky = m_bDrawSky; +#endif + // Find/Create our first pass IntroDataBlendPass_t *pass1; if ( m_IntroData.m_Passes.Count() == 0 ) @@ -161,6 +178,20 @@ void C_ScriptIntro::PostDataUpdate( DataUpdateType_t updateType ) else { m_IntroData.m_bDrawPrimary = true; +#ifdef MAPBASE + m_IntroData.m_bDrawSky2 = m_bDrawSky2; + + // If it's a point_camera and it's ortho, send it to the intro data + // Change this code if the purpose of m_hCameraEntity in intro data ever goes beyond ortho + if ( m_hCameraEntity && Q_strncmp(m_hCameraEntity->GetClassname(), "point_camera", 12) == 0 ) + { + C_PointCamera *pCamera = dynamic_cast(m_hCameraEntity.Get()); + if (pCamera && pCamera->IsOrtho()) + { + m_IntroData.m_hCameraEntity = m_hCameraEntity; + } + } +#endif } // If we're currently blending to a new mode, set the second pass @@ -239,8 +270,20 @@ void C_ScriptIntro::ClientThink( void ) if ( m_hCameraEntity ) { +#ifdef MAPBASE + if ( m_bUseEyePosition ) + { + m_hCameraEntity->GetEyePosition( m_IntroData.m_vecCameraView, m_IntroData.m_vecCameraViewAngles ); + } + else + { + m_IntroData.m_vecCameraView = m_hCameraEntity->GetAbsOrigin(); + m_IntroData.m_vecCameraViewAngles = m_hCameraEntity->GetAbsAngles(); + } +#else m_IntroData.m_vecCameraView = m_hCameraEntity->GetAbsOrigin(); m_IntroData.m_vecCameraViewAngles = m_hCameraEntity->GetAbsAngles(); +#endif } CalculateFOV(); @@ -325,3 +368,135 @@ void C_ScriptIntro::CalculateAlpha( void ) m_IntroData.m_flCurrentFadeColor[3] = flNewAlpha; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class C_PlayerViewProxy : public C_BaseEntity +{ + DECLARE_CLASS( C_PlayerViewProxy, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + Vector EyePosition( void ); // position of eyes + const QAngle &EyeAngles( void ); // Direction of eyes in world space + void GetEyePosition( Vector &vecOrigin, QAngle &angAngles ); + const QAngle &LocalEyeAngles( void ); // Direction of eyes + Vector EarPosition( void ); // position of ears + +#ifdef MAPBASE_MP + C_BasePlayer *GetPlayer() { return m_bEnabled ? (m_hPlayer.Get() ? m_hPlayer.Get() : C_BasePlayer::GetLocalPlayer()) : NULL; } +#else + C_BasePlayer *GetPlayer() { return m_bEnabled ? C_BasePlayer::GetLocalPlayer() : NULL; } +#endif + +public: +#ifdef MAPBASE_MP + CHandle m_hPlayer; +#endif + + bool m_bEnabled; +}; + +IMPLEMENT_CLIENTCLASS_DT( C_PlayerViewProxy, DT_PlayerViewProxy, CPlayerViewProxy ) +#ifdef MAPBASE_MP + RecvPropEHandle( RECVINFO( m_hPlayer ) ), +#endif + RecvPropBool( RECVINFO( m_bEnabled ) ), +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Vector C_PlayerViewProxy::EyePosition( void ) +{ + C_BasePlayer *pPlayer = GetPlayer(); + if (pPlayer) + { + //Vector vecPlayerOffset = m_hPlayer.Get()->EyePosition() - m_hPlayer.Get()->GetAbsOrigin(); + //return GetAbsOrigin() + vecPlayerOffset; + + Vector vecOrigin; + QAngle angAngles; + float fldummy; + pPlayer->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy ); + + return GetAbsOrigin() + (vecOrigin - pPlayer->GetAbsOrigin()); + } + else + return BaseClass::EyePosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const QAngle &C_PlayerViewProxy::EyeAngles( void ) +{ + C_BasePlayer *pPlayer = GetPlayer(); + if (pPlayer) + { + Vector vecOrigin; + static QAngle angAngles; + float fldummy; + pPlayer->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy ); + + angAngles = GetAbsAngles() + (angAngles - pPlayer->GetAbsAngles()); + return angAngles; + + //return m_hPlayer.Get()->EyeAngles(); + } + else + return BaseClass::EyeAngles(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PlayerViewProxy::GetEyePosition( Vector &vecOrigin, QAngle &angAngles ) +{ + C_BasePlayer *pPlayer = GetPlayer(); + if (pPlayer) + { + float fldummy; + pPlayer->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy ); + + vecOrigin = GetAbsOrigin() + (vecOrigin - pPlayer->GetAbsOrigin()); + angAngles = GetAbsAngles() + (angAngles - pPlayer->GetAbsAngles()); + } + else + { + BaseClass::GetEyePosition( vecOrigin, angAngles ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const QAngle &C_PlayerViewProxy::LocalEyeAngles( void ) +{ + C_BasePlayer *pPlayer = GetPlayer(); + if (pPlayer) { + static QAngle angAngles; + angAngles = GetAbsAngles() + (pPlayer->LocalEyeAngles() - pPlayer->GetAbsAngles()); + return angAngles; + } + else + return BaseClass::LocalEyeAngles(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Vector C_PlayerViewProxy::EarPosition( void ) +{ + C_BasePlayer *pPlayer = GetPlayer(); + if (pPlayer) + { + Vector vecPlayerOffset = pPlayer->GetAbsOrigin() - pPlayer->EarPosition(); + return GetAbsOrigin() + vecPlayerOffset; + } + else + return BaseClass::EarPosition(); +} +#endif + diff --git a/mp/src/game/client/hl2/c_weapon__stubs_hl2.cpp b/mp/src/game/client/hl2/c_weapon__stubs_hl2.cpp index 68693bfc..1bb922ee 100644 --- a/mp/src/game/client/hl2/c_weapon__stubs_hl2.cpp +++ b/mp/src/game/client/hl2/c_weapon__stubs_hl2.cpp @@ -33,7 +33,9 @@ STUB_WEAPON_CLASS( weapon_shotgun, WeaponShotgun, C_BaseHLCombatWeapon ); STUB_WEAPON_CLASS( weapon_smg1, WeaponSMG1, C_HLSelectFireMachineGun ); STUB_WEAPON_CLASS( weapon_357, Weapon357, C_BaseHLCombatWeapon ); STUB_WEAPON_CLASS( weapon_crossbow, WeaponCrossbow, C_BaseHLCombatWeapon ); +#ifndef MAPBASE STUB_WEAPON_CLASS( weapon_slam, Weapon_SLAM, C_BaseHLCombatWeapon ); +#endif STUB_WEAPON_CLASS( weapon_crowbar, WeaponCrowbar, C_BaseHLBludgeonWeapon ); #ifdef HL2_EPISODIC STUB_WEAPON_CLASS( weapon_hopwire, WeaponHopwire, C_BaseHLCombatWeapon ); diff --git a/mp/src/game/client/hl2/hud_credits.cpp b/mp/src/game/client/hl2/hud_credits.cpp index 6ad1488b..280e8a44 100644 --- a/mp/src/game/client/hl2/hud_credits.cpp +++ b/mp/src/game/client/hl2/hud_credits.cpp @@ -109,6 +109,9 @@ private: float m_flScrollTime; float m_flSeparation; +#ifdef MAPBASE + int m_iEndLines; +#endif float m_flFadeTime; bool m_bLastOneInPlace; int m_Alpha; @@ -133,6 +136,12 @@ private: char m_szLogo2[256]; Color m_cColor; + +#ifdef MAPBASE + char m_szCreditsFile[MAX_PATH]; + + char m_szLogoFont[64]; +#endif }; @@ -141,7 +150,11 @@ void CHudCredits::PrepareCredits( const char *pKeyName ) Clear(); KeyValues *pKV= new KeyValues( "CreditsFile" ); +#ifdef MAPBASE + if ( !pKV->LoadFromFile( filesystem, m_szCreditsFile, "MOD" ) ) +#else if ( !pKV->LoadFromFile( filesystem, CREDITS_FILE, "MOD" ) ) +#endif { pKV->deleteThis(); @@ -233,6 +246,9 @@ void CHudCredits::ReadParams( KeyValues *pKeyValue ) m_flScrollTime = pKeyValue->GetFloat( "scrolltime", 57 ); m_flSeparation = pKeyValue->GetFloat( "separation", 5 ); +#ifdef MAPBASE + m_iEndLines = pKeyValue->GetInt( "endlines", 1 ); +#endif m_flFadeInTime = pKeyValue->GetFloat( "fadeintime", 1 ); m_flFadeHoldTime = pKeyValue->GetFloat( "fadeholdtime", 3 ); @@ -249,6 +265,10 @@ void CHudCredits::ReadParams( KeyValues *pKeyValue ) Q_strncpy( m_szLogo, pKeyValue->GetString( "logo", "HALF-LIFE'" ), sizeof( m_szLogo ) ); Q_strncpy( m_szLogo2, pKeyValue->GetString( "logo2", "" ), sizeof( m_szLogo2 ) ); + +#ifdef MAPBASE + Q_strncpy( m_szLogoFont, pKeyValue->GetString( "logofont", "" ), sizeof( m_szLogoFont ) ); +#endif } int CHudCredits::GetStringPixelWidth( wchar_t *pString, vgui::HFont hFont ) @@ -296,9 +316,14 @@ void CHudCredits::DrawOutroCreditsName( void ) Color cColor = m_TextColor; +#ifdef MAPBASE + // Some lines should stick around and fade out + if ( i >= m_CreditsList.Count()-m_iEndLines ) +#else //HACKHACK //Last one stays on screen and fades out if ( i == m_CreditsList.Count()-1 ) +#endif { if ( m_bLastOneInPlace == false ) { @@ -418,6 +443,14 @@ void CHudCredits::DrawLogo( void ) char szLogoFont[64]; +#ifdef MAPBASE + if (m_szLogoFont[0] != '\0') + { + // Custom logo font + Q_strncpy( szLogoFont, m_szLogoFont, sizeof( szLogoFont ) ); + } + else +#endif if ( IsXbox() ) { Q_snprintf( szLogoFont, sizeof( szLogoFont ), "WeaponIcons_Small" ); @@ -638,6 +671,20 @@ void CHudCredits::PrepareOutroCredits( void ) int iHeight = iTall; +#ifdef MAPBASE + if (m_iEndLines <= 0) + { + // We need a credit to fade out at the end so we know when the credits are done. + // Add a dummy credit to act as the "end line". + creditname_t DummyCredit; + V_strcpy_safe( DummyCredit.szCreditName, ""); + V_strcpy_safe( DummyCredit.szFontName, "Default" ); + + m_CreditsList.AddToTail(DummyCredit); + m_iEndLines = 1; + } +#endif + for ( int i = 0; i < m_CreditsList.Count(); i++ ) { creditname_t *pCredit = &m_CreditsList[i]; @@ -706,6 +753,13 @@ void CHudCredits::MsgFunc_CreditsMsg( bf_read &msg ) { m_iCreditsType = msg.ReadByte(); +#ifdef MAPBASE + msg.ReadString(m_szCreditsFile, sizeof(m_szCreditsFile)); + + if (m_szCreditsFile[0] == '\0') + Q_strncpy(m_szCreditsFile, CREDITS_FILE, sizeof(m_szCreditsFile)); +#endif + switch ( m_iCreditsType ) { case CREDITS_LOGO: @@ -729,7 +783,17 @@ void CHudCredits::MsgFunc_CreditsMsg( bf_read &msg ) void CHudCredits::MsgFunc_LogoTimeMsg( bf_read &msg ) { m_iCreditsType = CREDITS_LOGO; +#ifdef MAPBASE + float flLogoTime = msg.ReadFloat(); + msg.ReadString(m_szCreditsFile, sizeof(m_szCreditsFile)); + + if (m_szCreditsFile[0] == '\0') + Q_strncpy(m_szCreditsFile, CREDITS_FILE, sizeof(m_szCreditsFile)); + + PrepareLogo(flLogoTime); +#else PrepareLogo( msg.ReadFloat() ); +#endif } diff --git a/mp/src/game/client/hl2/hud_suitpower.cpp b/mp/src/game/client/hl2/hud_suitpower.cpp index 416c9300..f1af00ac 100644 --- a/mp/src/game/client/hl2/hud_suitpower.cpp +++ b/mp/src/game/client/hl2/hud_suitpower.cpp @@ -104,12 +104,27 @@ void CHudSuitPower::OnThink( void ) bool breatherActive = pPlayer->IsBreatherActive(); int activeDevices = (int)flashlightActive + (int)sprintActive + (int)breatherActive; +#ifdef MAPBASE + activeDevices += (int)pPlayer->IsCustomDevice0Active() + (int)pPlayer->IsCustomDevice1Active() + (int)pPlayer->IsCustomDevice2Active(); +#endif + if (activeDevices != m_iActiveSuitDevices) { m_iActiveSuitDevices = activeDevices; switch ( m_iActiveSuitDevices ) { +#ifdef MAPBASE + case 6: + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerSixItemsActive"); + break; + case 5: + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerFiveItemsActive"); + break; + case 4: + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerFourItemsActive"); + break; +#endif default: case 3: g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerThreeItemsActive"); @@ -251,6 +266,59 @@ void CHudSuitPower::Paint() } ypos += text2_gap; } + +#ifdef MAPBASE + if (pPlayer->IsCustomDevice0Active()) + { + tempString = g_pVGuiLocalize->Find("#Mapbase_Hud_DEVICE0"); + + surface()->DrawSetTextPos(text2_xpos, ypos); + + if (tempString) + { + surface()->DrawPrintText(tempString, wcslen(tempString)); + } + else + { + surface()->DrawPrintText(L"CUSTOM 0", wcslen(L"CUSTOM 0")); + } + ypos += text2_gap; + } + + if (pPlayer->IsCustomDevice1Active()) + { + tempString = g_pVGuiLocalize->Find("#Mapbase_Hud_DEVICE1"); + + surface()->DrawSetTextPos(text2_xpos, ypos); + + if (tempString) + { + surface()->DrawPrintText(tempString, wcslen(tempString)); + } + else + { + surface()->DrawPrintText(L"CUSTOM 1", wcslen(L"CUSTOM 1")); + } + ypos += text2_gap; + } + + if (pPlayer->IsCustomDevice2Active()) + { + tempString = g_pVGuiLocalize->Find("#Mapbase_Hud_DEVICE2"); + + surface()->DrawSetTextPos(text2_xpos, ypos); + + if (tempString) + { + surface()->DrawPrintText(tempString, wcslen(tempString)); + } + else + { + surface()->DrawPrintText(L"CUSTOM 2", wcslen(L"CUSTOM 2")); + } + ypos += text2_gap; + } +#endif } } diff --git a/mp/src/game/client/hud.cpp b/mp/src/game/client/hud.cpp index af50b187..a7d77a32 100644 --- a/mp/src/game/client/hud.cpp +++ b/mp/src/game/client/hud.cpp @@ -134,10 +134,13 @@ void LoadHudTextures( CUtlDict< CHudTexture *, int >& list, const char *szFilena pTemp = pTemp->GetNextKey(); } } - } - // Failed for some reason. Delete the Key data and abort. - pKeyValuesData->deleteThis(); + pKeyValuesData->deleteThis(); + } + else + { + Warning( "Unable to read script %s.\n", szFilenameWithoutExtension ); + } } //----------------------------------------------------------------------------- @@ -469,6 +472,8 @@ void CHud::Init( void ) } FreeHudTextureList( textureList ); + + HudIcons().Init(); } //----------------------------------------------------------------------------- @@ -1197,3 +1202,232 @@ CON_COMMAND_F( testhudanim, "Test a hud element animation.\n\tArguments: GetViewportAnimationController()->StartAnimationSequence( args[1] ); } +CHudIcons::CHudIcons() : + m_bHudTexturesLoaded( false ) +{ +} + +CHudIcons::~CHudIcons() +{ + int c = m_Icons.Count(); + for ( int i = c - 1; i >= 0; i-- ) + { + CHudTexture *tex = m_Icons[ i ]; + g_HudTextureMemoryPool.Free( tex ); + } + m_Icons.Purge(); +} + +void CHudIcons::Init() +{ + if ( m_bHudTexturesLoaded ) + return; + + m_bHudTexturesLoaded = true; + CUtlDict< CHudTexture *, int > textureList; + + // check to see if we have sprites for this res; if not, step down + LoadHudTextures( textureList, "scripts/hud_textures", NULL ); + LoadHudTextures( textureList, "scripts/mod_textures", NULL ); + + LoadHudTextures( textureList, "scripts/instructor_textures", NULL ); +#ifdef HL2_CLIENT_DLL + LoadHudTextures( textureList, "scripts/instructor_textures_hl2", NULL ); +#endif + LoadHudTextures( textureList, "scripts/instructor_modtextures", NULL ); + + int c = textureList.Count(); + for ( int index = 0; index < c; index++ ) + { + CHudTexture* tex = textureList[ index ]; + AddSearchableHudIconToList( *tex ); + } + + FreeHudTextureList( textureList ); +} + +void CHudIcons::Shutdown() +{ + m_bHudTexturesLoaded = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture *CHudIcons::AddUnsearchableHudIconToList( CHudTexture& texture ) +{ + // These names are composed based on the texture file name + char composedName[ 512 ]; + + if ( texture.bRenderUsingFont ) + { + Q_snprintf( composedName, sizeof( composedName ), "%s_c%i", + texture.szTextureFile, texture.cCharacterInFont ); + } + else + { + Q_snprintf( composedName, sizeof( composedName ), "%s_%i_%i_%i_%i", + texture.szTextureFile, texture.rc.left, texture.rc.top, texture.rc.right, texture.rc.bottom ); + } + + CHudTexture *icon = GetIcon( composedName ); + if ( icon ) + { + return icon; + } + + CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc(); + *newTexture = texture; + + SetupNewHudTexture( newTexture ); + + int idx = m_Icons.Insert( composedName, newTexture ); + return m_Icons[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture *CHudIcons::AddSearchableHudIconToList( CHudTexture& texture ) +{ + CHudTexture *icon = GetIcon( texture.szShortName ); + if ( icon ) + { + return icon; + } + + CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc(); + *newTexture = texture; + + SetupNewHudTexture( newTexture ); + + int idx = m_Icons.Insert( texture.szShortName, newTexture ); + return m_Icons[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to an icon in the list +//----------------------------------------------------------------------------- +CHudTexture *CHudIcons::GetIcon( const char *szIcon ) +{ + int i = m_Icons.Find( szIcon ); + if ( i == m_Icons.InvalidIndex() ) + return NULL; + + return m_Icons[ i ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets texture handles for the hud icon +//----------------------------------------------------------------------------- +void CHudIcons::SetupNewHudTexture( CHudTexture *t ) +{ + if ( t->bRenderUsingFont ) + { + vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); + t->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( t->szTextureFile, true ); + t->rc.top = 0; + t->rc.left = 0; + t->rc.right = vgui::surface()->GetCharacterWidth( t->hFont, t->cCharacterInFont ); + t->rc.bottom = vgui::surface()->GetFontTall( t->hFont ); + } + else + { + // Set up texture id and texture coordinates + t->textureId = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( t->textureId, t->szTextureFile, false, false ); + + int wide, tall; + vgui::surface()->DrawGetTextureSize( t->textureId, wide, tall ); + + t->texCoords[ 0 ] = (float)(t->rc.left + 0.5f) / (float)wide; + t->texCoords[ 1 ] = (float)(t->rc.top + 0.5f) / (float)tall; + t->texCoords[ 2 ] = (float)(t->rc.right - 0.5f) / (float)wide; + t->texCoords[ 3 ] = (float)(t->rc.bottom - 0.5f) / (float)tall; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudIcons::RefreshHudTextures() +{ + if ( !m_bHudTexturesLoaded ) + { + Assert( 0 ); + return; + } + + CUtlDict< CHudTexture *, int > textureList; + + // check to see if we have sprites for this res; if not, step down + LoadHudTextures( textureList, "scripts/hud_textures", NULL ); + LoadHudTextures( textureList, "scripts/mod_textures", NULL ); + + LoadHudTextures( textureList, "scripts/instructor_textures", NULL ); + + + // fix up all the texture icons first + int c = textureList.Count(); + for ( int index = 0; index < c; index++ ) + { + CHudTexture *tex = textureList[ index ]; + Assert( tex ); + + CHudTexture *icon = GetIcon( tex->szShortName ); + if ( !icon ) + continue; + + // Update file + Q_strncpy( icon->szTextureFile, tex->szTextureFile, sizeof( icon->szTextureFile ) ); + + if ( !icon->bRenderUsingFont ) + { + // Update subrect + icon->rc = tex->rc; + + // Keep existing texture id, but now update texture file and texture coordinates + vgui::surface()->DrawSetTextureFile( icon->textureId, icon->szTextureFile, false, false ); + + // Get new texture dimensions in case it changed + int wide, tall; + vgui::surface()->DrawGetTextureSize( icon->textureId, wide, tall ); + + // Assign coords + icon->texCoords[ 0 ] = (float)(icon->rc.left + 0.5f) / (float)wide; + icon->texCoords[ 1 ] = (float)(icon->rc.top + 0.5f) / (float)tall; + icon->texCoords[ 2 ] = (float)(icon->rc.right - 0.5f) / (float)wide; + icon->texCoords[ 3 ] = (float)(icon->rc.bottom - 0.5f) / (float)tall; + } + } + + FreeHudTextureList( textureList ); + + // fixup all the font icons + vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); + for (int i = m_Icons.First(); m_Icons.IsValidIndex(i); i = m_Icons.Next(i)) + { + CHudTexture *icon = m_Icons[i]; + if ( !icon ) + continue; + + // Update file + if ( icon->bRenderUsingFont ) + { + icon->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( icon->szTextureFile, true ); + icon->rc.top = 0; + icon->rc.left = 0; + icon->rc.right = vgui::surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont ); + icon->rc.bottom = vgui::surface()->GetFontTall( icon->hFont ); + } + } +} + + +static CHudIcons g_HudIcons; + +CHudIcons &HudIcons() +{ + return g_HudIcons; +} + diff --git a/mp/src/game/client/hud.h b/mp/src/game/client/hud.h index b623a3eb..52ff0580 100644 --- a/mp/src/game/client/hud.h +++ b/mp/src/game/client/hud.h @@ -194,6 +194,37 @@ private: extern CHud gHUD; +//----------------------------------------------------------------------------- +// Purpose: CHudIcons +//----------------------------------------------------------------------------- +class CHudIcons +{ +public: + CHudIcons(); + ~CHudIcons(); + + void Init(); + void Shutdown(); + + CHudTexture *GetIcon( const char *szIcon ); + + // loads a new icon into the list, without duplicates + CHudTexture *AddUnsearchableHudIconToList( CHudTexture& texture ); + CHudTexture *AddSearchableHudIconToList( CHudTexture& texture ); + + void RefreshHudTextures(); + +private: + + void SetupNewHudTexture( CHudTexture *t ); + bool m_bHudTexturesLoaded; + // Global list of known icons + CUtlDict< CHudTexture *, int > m_Icons; + +}; + +CHudIcons &HudIcons(); + //----------------------------------------------------------------------------- // Global fonts used in the client DLL //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/hud_closecaption.cpp b/mp/src/game/client/hud_closecaption.cpp index c1a1fa29..7490d00d 100644 --- a/mp/src/game/client/hud_closecaption.cpp +++ b/mp/src/game/client/hud_closecaption.cpp @@ -31,12 +31,20 @@ extern ISoundEmitterSystemBase *soundemitterbase; // Marked as FCVAR_USERINFO so that the server can cull CC messages before networking them down to us!!! +#ifdef MAPBASE +ConVar closecaption( "closecaption", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX | FCVAR_USERINFO, "Enable close captioning." ); +#else ConVar closecaption( "closecaption", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX | FCVAR_USERINFO, "Enable close captioning." ); +#endif extern ConVar cc_lang; static ConVar cc_linger_time( "cc_linger_time", "1.0", FCVAR_ARCHIVE, "Close caption linger time." ); static ConVar cc_predisplay_time( "cc_predisplay_time", "0.25", FCVAR_ARCHIVE, "Close caption delay before showing caption." ); static ConVar cc_captiontrace( "cc_captiontrace", "1", 0, "Show missing closecaptions (0 = no, 1 = devconsole, 2 = show in hud)" ); +#ifdef MAPBASE +static ConVar cc_subtitles( "cc_subtitles", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "If set, don't show sound effect captions, just voice overs (i.e., won't help hearing impaired players)." ); +#else static ConVar cc_subtitles( "cc_subtitles", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "If set, don't show sound effect captions, just voice overs (i.e., won't help hearing impaired players)." ); +#endif ConVar english( "english", "1", FCVAR_USERINFO, "If set to 1, running the english language set of assets." ); static ConVar cc_smallfontlength( "cc_smallfontlength", "300", 0, "If text stream is this long, force usage of small font size." ); @@ -2357,11 +2365,11 @@ bool CHudCloseCaption::AddAsyncWork( const char *tokenstream, bool bIsStream, fl char tokenname[ 512 ]; tokenname[ 0 ] = 0; const char *p = tokenstream; - p = nexttoken( tokenname, p, ' ' ); + p = nexttoken( tokenname, p, ' ' , sizeof(tokenname) ); // p points to reset of sentence tokens, build up a unicode string from them... while ( p && Q_strlen( tokenname ) > 0 ) { - p = nexttoken( tokenname, p, ' ' ); + p = nexttoken( tokenname, p, ' ' , sizeof(tokenname) ); if ( Q_strlen( tokenname ) == 0 ) break; @@ -2396,7 +2404,7 @@ void CHudCloseCaption::ProcessSentenceCaptionStream( const char *tokenstream ) const char *p = tokenstream; - p = nexttoken( tokenname, p, ' ' ); + p = nexttoken( tokenname, p, ' ' , sizeof(tokenname) ); if ( Q_strlen( tokenname ) > 0 ) { @@ -2693,6 +2701,11 @@ CON_COMMAND_F_COMPLETION( cc_emit, "Emits a closed caption", 0, EmitCaptionCompl return; } +#ifdef MAPBASE // 1upD + if (!closecaption.GetBool()) + return; +#endif + CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); if ( hudCloseCaption ) { diff --git a/mp/src/game/client/hud_hintdisplay.cpp b/mp/src/game/client/hud_hintdisplay.cpp index 487633bd..50ab208a 100644 --- a/mp/src/game/client/hud_hintdisplay.cpp +++ b/mp/src/game/client/hud_hintdisplay.cpp @@ -599,10 +599,39 @@ bool CHudHintKeyDisplay::SetHintText( const char *text ) else { const char *key = engine->Key_LookupBinding( *binding == '+' ? binding + 1 : binding ); +#ifdef MAPBASE + if ( !key ) + { + const char *pszNotBound = VarArgs("< %s, not bound >", *binding == '+' ? binding + 1 : binding); + if (strchr(binding, '&')) + { + // "%walk&use%" >> "ALT + E" + char *token = strtok(binding, "&"); + while (token) + { + const char *tokenkey = engine->Key_LookupBinding( *token == '+' ? token + 1 : token ); + + key = VarArgs("%s%s%s", key ? key : "", key ? " + " : "", tokenkey ? tokenkey : pszNotBound); + + token = strtok(NULL, "&"); + } + } + else if (binding[0] == '$') + { + // "%$COOL STRING DUDE%" >> "COOL STRING DUDE" + key = binding + 1; + } + else + { + key = pszNotBound; + } + } +#else if ( !key ) { key = "< not bound >"; } +#endif Q_snprintf( friendlyName, sizeof(friendlyName), "#%s", key ); Q_strupr( friendlyName ); diff --git a/mp/src/game/client/hud_locator_target.cpp b/mp/src/game/client/hud_locator_target.cpp new file mode 100644 index 00000000..8f0d4c0c --- /dev/null +++ b/mp/src/game/client/hud_locator_target.cpp @@ -0,0 +1,2207 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: See header file +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "hud_locator_target.h" +#include "iclientmode.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "iinput.h" +#include "view.h" +#include "hud.h" +#include "hudelement.h" +#include "vgui_int.h" + +#include "hud_macros.h" +#include "iclientmode.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#define ICON_SIZE 0.04f // Icons are ScreenWidth() * ICON_SIZE wide. +#define ICON_GAP 5 // Number of pixels between the icon and the text + +#define OFFSCREEN_ICON_POSITION_RADIUS 100 +#define BUTTON_FONT_HANDLE m_hCaptionFont + +#define ICON_DIST_TOO_FAR (60.0f * 12.0f) + +#define MIN_ICON_ALPHA 0.5 +#define MAX_ICON_ALPHA 1 + +ConVar locator_icon_min_size_non_ss( "locator_icon_min_size_non_ss", "1.0", FCVAR_NONE, "Minimum scale of the icon on the screen" ); +ConVar locator_icon_max_size_non_ss( "locator_icon_max_size_non_ss", "1.5", FCVAR_NONE, "Maximum scale of the icon on the screen" ); + +#define MIN_ICON_SCALE locator_icon_min_size_non_ss.GetFloat() +#define MAX_ICON_SCALE locator_icon_max_size_non_ss.GetFloat() + +#define LOCATOR_OCCLUSION_TEST_RATE 0.25f + +enum +{ + DRAW_ARROW_NO = 0, + DRAW_ARROW_UP, + DRAW_ARROW_DOWN, + DRAW_ARROW_LEFT, + DRAW_ARROW_RIGHT +}; + +ConVar locator_fade_time( "locator_fade_time", "0.3", FCVAR_NONE, "Number of seconds it takes for a lesson to fully fade in/out." ); +ConVar locator_lerp_speed( "locator_lerp_speed", "5.0f", FCVAR_NONE, "Speed that static lessons move along the Y axis." ); +ConVar locator_lerp_rest( "locator_lerp_rest", "2.25f", FCVAR_NONE, "Number of seconds before moving from the center." ); +ConVar locator_lerp_time( "locator_lerp_time", "1.75f", FCVAR_NONE, "Number of seconds to lerp before reaching final destination" ); +ConVar locator_pulse_time( "locator_pulse_time", "1.0f", FCVAR_NONE, "Number of seconds to pulse after changing icon or position" ); +ConVar locator_start_at_crosshair( "locator_start_at_crosshair", "0", FCVAR_NONE, "Start position at the crosshair instead of the top middle of the screen." ); + +ConVar locator_topdown_style( "locator_topdown_style", "0", FCVAR_NONE, "Topdown games set this to handle distance and offscreen location differently." ); + +ConVar locator_background_style( "locator_background_style", "0", FCVAR_NONE, "Setting this to 1 will show rectangle backgrounds behind the items word-bubble pointers." ); +ConVar locator_background_color( "locator_background_color", "255 255 255 5", FCVAR_NONE, "The default color for the background." ); +ConVar locator_background_border_color( "locator_background_border_color", "255 255 255 15", FCVAR_NONE, "The default color for the border." ); +ConVar locator_background_thickness_x( "locator_background_thickness_x", "8", FCVAR_NONE, "How many pixels the background borders the left and right." ); +ConVar locator_background_thickness_y( "locator_background_thickness_y", "0", FCVAR_NONE, "How many pixels the background borders the top and bottom." ); +ConVar locator_background_shift_x( "locator_background_shift_x", "3", FCVAR_NONE, "How many pixels the background is shifted right." ); +ConVar locator_background_shift_y( "locator_background_shift_y", "1", FCVAR_NONE, "How many pixels the background is shifted down." ); +ConVar locator_background_border_thickness( "locator_background_border_thickness", "3", FCVAR_NONE, "How many pixels the background borders the left and right." ); + +ConVar locator_target_offset_x( "locator_target_offset_x", "0", FCVAR_NONE, "How many pixels to offset the locator from the target position." ); +ConVar locator_target_offset_y( "locator_target_offset_y", "0", FCVAR_NONE, "How many pixels to offset the locator from the target position." ); + +ConVar locator_text_drop_shadow( "locator_text_drop_shadow", "1", FCVAR_NONE, "If enabled, a drop shadow is drawn behind caption text. PC only." ); +ConVar locator_text_glow( "locator_text_glow", "0", FCVAR_NONE, "If enabled, a glow is drawn behind caption text" ); +ConVar locator_text_glow_color( "locator_text_glow_color", "255 255 255 255", FCVAR_NONE, "Color of text glow" ); + +ConVar locator_split_maxwide_percent( "locator_split_maxwide_percent", "0.80f", FCVAR_CHEAT ); +ConVar locator_split_len( "locator_split_len", "0.5f", FCVAR_CHEAT ); + +#ifdef MAPBASE +extern ConVar gameinstructor_default_bindingcolor; +#endif + + +//------------------------------------ +CLocatorTarget::CLocatorTarget( void ) +{ + Deactivate( true ); + + PrecacheMaterial("vgui/hud/icon_arrow_left"); + PrecacheMaterial("vgui/hud/icon_arrow_right"); + PrecacheMaterial("vgui/hud/icon_arrow_up"); + PrecacheMaterial("vgui/hud/icon_arrow_down"); + PrecacheMaterial("vgui/hud/icon_arrow_plain"); +} + +//------------------------------------ +void CLocatorTarget::Activate( int serialNumber ) +{ + m_serialNumber = serialNumber; + m_frameLastUpdated = gpGlobals->framecount; + m_isActive = true; + + m_bVisible = true; + m_bOnscreen = true; + m_alpha = 0; + m_fadeStart = gpGlobals->curtime; + + m_offsetX = m_offsetY = 0; + + int iStartX = ScreenWidth() / 2; + int iStartY = ScreenHeight() / 4; + + // We want to start lessons at the players crosshair, cause that's where they're looking! + if ( locator_start_at_crosshair.GetBool() ) + vgui::input()->GetCursorPos( iStartX, iStartY ); + + m_lastXPos = iStartX; + m_lastYPos = iStartY; + + m_drawArrowDirection = DRAW_ARROW_NO; + m_lerpStart = gpGlobals->curtime; + m_pulseStart = gpGlobals->curtime; + m_declutterIndex = 0; + m_lastDeclutterIndex = 0; + + AddIconEffects(LOCATOR_ICON_FX_FADE_IN); + +#ifdef MAPBASE + // Mods are capable of using a custom binding color + CSplitString colorValues( gameinstructor_default_bindingcolor.GetString(), "," ); + + int r,g,b; + r = g = b = 0; + + if (colorValues.Count() == 3) + { + r = atoi( colorValues[0] ); + g = atoi( colorValues[1] ); + b = atoi( colorValues[2] ); + } + + m_bindingColor.SetColor( r, g, b, 255 ); +#endif +} + +//------------------------------------ +void CLocatorTarget::Deactivate( bool bNoFade ) +{ + if ( bNoFade || m_alpha == 0 || + ( m_bOccluded && !( m_iEffectsFlags & LOCATOR_ICON_FX_FORCE_CAPTION ) ) || + ( !m_bOnscreen && ( m_iEffectsFlags & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) ) + { + m_bOriginInScreenspace = false; + + m_serialNumber = -1; + m_isActive = false; + m_frameLastUpdated = 0; + m_pIcon_onscreen = NULL; + m_pIcon_offscreen = NULL; + m_bDrawControllerButton = false; + m_bDrawControllerButtonOffscreen = false; + m_iEffectsFlags = LOCATOR_ICON_FX_NONE; + m_captionWide = 0; + + m_pchDrawBindingName = NULL; + m_pchDrawBindingNameOffscreen = NULL; + m_widthScale_onscreen = 1.0f; + m_bOccluded = false; + m_alpha = 0; + m_bIsDrawing = false; + m_bVisible = false; + + m_szVguiTargetName = ""; + m_szVguiTargetLookup = ""; + m_hVguiTarget = NULL; + m_nVguiTargetEdge = vgui::Label::a_northwest; + + m_szBinding = ""; + m_iBindingTick = 0; + m_flNextBindingTick = 0.0f; + m_flNextOcclusionTest = 0.0f; + m_iBindingChoicesCount = 0; + + m_wszCaption.RemoveAll(); + m_wszCaption.AddToTail( (wchar_t)0 ); + } + else if ( !( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) ) + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( 1.0f - static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN ); + } +} + +//------------------------------------ +void CLocatorTarget::Update() +{ + m_frameLastUpdated = gpGlobals->framecount; + + if ( m_bVisible && ( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) ) + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( 1.0f - static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + } +} + +int CLocatorTarget::GetIconX( void ) +{ + return m_iconX + ( IsOnScreen() ? locator_target_offset_x.GetInt()+m_offsetX : 0 ); +} + +int CLocatorTarget::GetIconY( void ) +{ + return m_iconY + ( IsOnScreen() ? locator_target_offset_y.GetInt()+m_offsetY : 0 ); +} + +int CLocatorTarget::GetIconCenterX( void ) +{ + return m_centerX + locator_target_offset_x.GetInt() + m_offsetX; +} + +int CLocatorTarget::GetIconCenterY( void ) +{ + return m_centerY + locator_target_offset_y.GetInt() + m_offsetY; +} + +void CLocatorTarget::SetVisible( bool bVisible ) +{ + // They are already the same + if ( m_bVisible == bVisible ) + return; + + m_bVisible = bVisible; + + if ( bVisible ) + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_IN ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + } + else + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( 1.0f - static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN ); + } +} + +bool CLocatorTarget::IsVisible( void ) +{ + return m_bVisible; +} + +void CLocatorTarget::SetCaptionText( const char *pszText, const char *pszParam ) +{ + wchar_t outbuf[ 256 ]; + outbuf[ 0 ] = L'\0'; + + if ( pszParam && pszParam[ 0 ] != '\0' ) + { + wchar_t wszParamBuff[ 128 ]; + wchar_t *pLocalizedParam = NULL; + + if ( pszParam[ 0 ] == '#' ) + { + pLocalizedParam = g_pVGuiLocalize->Find( pszParam ); + } + + if ( !pLocalizedParam ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszParam, wszParamBuff, sizeof( wszParamBuff ) ); + pLocalizedParam = wszParamBuff; + } + + wchar_t wszTextBuff[ 128 ]; + wchar_t *pLocalizedText = NULL; + + if ( pszText[ 0 ] == '#' ) + pLocalizedText = g_pVGuiLocalize->Find( pszText ); + + if ( !pLocalizedText ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) ); + pLocalizedText = wszTextBuff; + } + + wchar_t buf[ 256 ]; + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), pLocalizedText, 1, pLocalizedParam ); + + UTIL_ReplaceKeyBindings( buf, sizeof( buf ), outbuf, sizeof( outbuf ) ); + } + else + { + wchar_t wszTextBuff[ 128 ]; + wchar_t *pLocalizedText = NULL; + + if ( pszText[ 0 ] == '#' ) + { + pLocalizedText = g_pVGuiLocalize->Find( pszText ); + } + + if ( !pLocalizedText ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) ); + pLocalizedText = wszTextBuff; + } + + wchar_t buf[ 256 ]; + Q_wcsncpy( buf, pLocalizedText, sizeof( buf ) ); + + UTIL_ReplaceKeyBindings( buf, sizeof(buf), outbuf, sizeof( outbuf ) ); + } + + int len = wcslen( outbuf ) + 1; + m_wszCaption.RemoveAll(); + m_wszCaption.EnsureCount( len ); + Q_wcsncpy( m_wszCaption.Base(), outbuf, len * sizeof( wchar_t ) ); +} + +void CLocatorTarget::SetCaptionColor( const char *pszCaptionColor ) +{ + int r,g,b; + r = g = b = 0; + + CSplitString colorValues( pszCaptionColor, "," ); + + if( colorValues.Count() == 3 ) + { + r = atoi( colorValues[0] ); + g = atoi( colorValues[1] ); + b = atoi( colorValues[2] ); + + m_captionColor.SetColor( r,g,b, 255 ); + } + else + { + DevWarning( "caption_color format incorrect. RRR,GGG,BBB expected.\n"); + } +} + +bool CLocatorTarget::IsStatic() +{ + return ( ( m_iEffectsFlags & LOCATOR_ICON_FX_STATIC ) || IsPresenting() ); +} + +bool CLocatorTarget::IsPresenting() +{ + return ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() ); +} + +void CLocatorTarget::StartTimedLerp() +{ + if ( gpGlobals->curtime - m_lerpStart > locator_lerp_rest.GetFloat() ) + { + m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat(); + } +} + +void CLocatorTarget::StartPresent() +{ + m_lerpStart = gpGlobals->curtime; +} + + +void CLocatorTarget::EndPresent() +{ + if ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() ) + { + m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat(); + } +} + +void CLocatorTarget::UpdateVguiTarget( void ) +{ + const char *pchVguiTargetName = m_szVguiTargetName.String(); + + if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' ) + { + m_hVguiTarget = NULL; + return; + } + + // Get the appropriate token based on the binding + if ( m_iBindingChoicesCount > 0 ) + { + int nTagetToken = m_iBindChoicesOriginalToken[ m_iBindingTick % m_iBindingChoicesCount ]; + + for ( int nToken = 0; nToken < nTagetToken && pchVguiTargetName; ++nToken ) + { + pchVguiTargetName = strchr( pchVguiTargetName, ';' ); + + if ( pchVguiTargetName ) + { + pchVguiTargetName++; + } + } + + if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' ) + { + // There wasn't enough tokens, just use the first + pchVguiTargetName = m_szVguiTargetName.String(); + } + } + + m_hVguiTarget = g_pClientMode->GetViewport(); +} + +void CLocatorTarget::SetVguiTargetName( const char *pchVguiTargetName ) +{ + if ( Q_strcmp( m_szVguiTargetName.String(), pchVguiTargetName ) == 0 ) + return; + + m_szVguiTargetName = pchVguiTargetName; + + UpdateVguiTarget(); +} + +void CLocatorTarget::SetVguiTargetLookup( const char *pchVguiTargetLookup ) +{ + m_szVguiTargetLookup = pchVguiTargetLookup; +} + +void CLocatorTarget::SetVguiTargetEdge( int nVguiEdge ) +{ + m_nVguiTargetEdge = nVguiEdge; +} + +vgui::Panel *CLocatorTarget::GetVguiTarget( void ) +{ + return (vgui::Panel *)m_hVguiTarget.Get(); +} + +//------------------------------------ +void CLocatorTarget::SetOnscreenIconTextureName( const char *pszTexture ) +{ + if ( Q_strcmp( m_szOnscreenTexture.String(), pszTexture ) == 0 ) + return; + + m_szOnscreenTexture = pszTexture; + m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name. + m_pulseStart = gpGlobals->curtime; +} + +//------------------------------------ +void CLocatorTarget::SetOffscreenIconTextureName( const char *pszTexture ) +{ + if ( Q_strcmp( m_szOffscreenTexture.String(), pszTexture ) == 0 ) + return; + + m_szOffscreenTexture = pszTexture; + m_pIcon_offscreen = NULL; // Ditto + m_pulseStart = gpGlobals->curtime; +} + +//------------------------------------ +void CLocatorTarget::SetBinding( const char *pszBinding ) +{ + int iAllowJoystick = -1; + + /*if ( !IsX360() ) + { + // Only show joystick binds if it's enabled and non-joystick if it's disabled + iAllowJoystick = input->ControllerModeActive(); + }*/ + + bool bIsControllerNow = ( iAllowJoystick != 0 ); + + if ( m_bWasControllerLast == bIsControllerNow ) + { + // We haven't toggled joystick enabled recently, so if it's the same bind, bail + if ( Q_strcmp( m_szBinding.String(), pszBinding ) == 0 ) + return; + } + + m_bWasControllerLast = bIsControllerNow; + + m_szBinding = pszBinding; + m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name. + m_pIcon_offscreen = NULL; // ditto. + m_flNextBindingTick = gpGlobals->curtime + 0.75f; + + // Get a list of all the keys bound to these actions + m_iBindingChoicesCount = 0; + + // Tokenize the binding name (could be more than one binding) + int nOriginalToken = 0; + const char *pchToken = m_szBinding.String(); + char szToken[ 128 ]; + + pchToken = nexttoken( szToken, pchToken, ';', sizeof( szToken ) ); + + while ( pchToken ) + { + // Get the first parameter + int iTokenBindingCount = 0; + const char *pchBinding = engine->Key_LookupBindingExact( szToken ); + + while ( m_iBindingChoicesCount < MAX_LOCATOR_BINDINGS_SHOWN && pchBinding ) + { + m_pchBindingChoices[ m_iBindingChoicesCount ] = pchBinding; + m_iBindChoicesOriginalToken[ m_iBindingChoicesCount ] = nOriginalToken; + ++m_iBindingChoicesCount; + ++iTokenBindingCount; + + pchBinding = engine->Key_LookupBindingExact( szToken ); + } + + nOriginalToken++; + pchToken = nexttoken( szToken, pchToken, ';', sizeof( szToken ) ); + } + + m_pulseStart = gpGlobals->curtime; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CLocatorTarget::UseBindingImage( char *pchIconTextureName, size_t bufSize ) +{ + if ( m_iBindingChoicesCount <= 0 ) + { + if ( IsX360() ) + { + Q_strncpy( pchIconTextureName, "icon_blank", bufSize ); + } + else + { + Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize ); + return "#GameUI_Icons_NONE"; + } + + return NULL; + } + + // Cycle through the list of binds at a rate of 2 per second + const char *pchBinding = m_pchBindingChoices[ m_iBindingTick % m_iBindingChoicesCount ]; + + // We counted at least one binding... this should not be NULL! + Assert( pchBinding ); + + if ( IsX360() ) + { + // Use a blank background for the button icons + Q_strncpy( pchIconTextureName, "icon_blank", bufSize ); + return pchBinding; + } + + /*if ( input->ControllerModeActive() && + ( Q_strcmp( pchBinding, "A_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "B_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "X_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "Y_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "L_SHOULDER" ) == 0 || + Q_strcmp( pchBinding, "R_SHOULDER" ) == 0 || + Q_strcmp( pchBinding, "L_TRIGGER" ) == 0 || + Q_strcmp( pchBinding, "R_TRIGGER" ) == 0 || + Q_strcmp( pchBinding, "BACK" ) == 0 || + Q_strcmp( pchBinding, "START" ) == 0 || + Q_strcmp( pchBinding, "STICK1" ) == 0 || + Q_strcmp( pchBinding, "STICK2" ) == 0 || + Q_strcmp( pchBinding, "UP" ) == 0 || + Q_strcmp( pchBinding, "DOWN" ) == 0 || + Q_strcmp( pchBinding, "LEFT" ) == 0 || + Q_strcmp( pchBinding, "RIGHT" ) == 0 ) ) + { + // Use a blank background for the button icons + Q_strncpy( pchIconTextureName, "icon_blank", bufSize ); + return pchBinding; + }*/ + + if ( Q_strcmp( pchBinding, "MOUSE1" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseLeft", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MOUSE2" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseRight", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MOUSE3" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseThree", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MWHEELUP" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseWheel_up", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MWHEELDOWN" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseWheel_down", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "UPARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_up", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "LEFTARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_left", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "DOWNARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_down", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "RIGHTARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_right", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "SEMICOLON" ) == 0 || + Q_strcmp( pchBinding, "INS" ) == 0 || + Q_strcmp( pchBinding, "DEL" ) == 0 || + Q_strcmp( pchBinding, "HOME" ) == 0 || + Q_strcmp( pchBinding, "END" ) == 0 || + Q_strcmp( pchBinding, "PGUP" ) == 0 || + Q_strcmp( pchBinding, "PGDN" ) == 0 || + Q_strcmp( pchBinding, "PAUSE" ) == 0 || + Q_strcmp( pchBinding, "F10" ) == 0 || + Q_strcmp( pchBinding, "F11" ) == 0 || + Q_strcmp( pchBinding, "F12" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize ); + return pchBinding; + } + else if ( Q_strlen( pchBinding ) <= 2 ) + { + Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize ); + return pchBinding; + } + else if ( Q_strlen( pchBinding ) <= 6 ) + { + Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize ); + return pchBinding; + } + else + { + Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize ); + return pchBinding; + } + + return pchBinding; +} + +//----------------------------------------------------------------------------- +int CLocatorTarget::GetIconWidth( void ) +{ + return m_wide; +} + +//----------------------------------------------------------------------------- +int CLocatorTarget::GetIconHeight( void ) +{ + return m_tall; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLocatorPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CLocatorPanel, vgui::EditablePanel ); +public: + CLocatorPanel( vgui::Panel *parent, const char *name ); + ~CLocatorPanel( void ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PerformLayout( void ); + virtual void OnTick( void ); + virtual void PaintBackground( void ); + virtual void Paint( void ); + void ValidateTexture( int *pTextureID, const char *pszTextureName ); + bool ValidateTargetTextures( CLocatorTarget *pTarget ); + bool IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance ); + virtual void PaintTarget( CLocatorTarget *pTarget ); + + void DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer ); + void DrawStaticIcon( CLocatorTarget *pTarget ); + void DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow ); + void DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, int direction ); + void DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline ); + int GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont ); + void DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController ); + void ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition ); + void CalculateOcclusion( CLocatorTarget *pTarget ); + + void DrawSimpleArrow( int x, int y, int iconWide, int iconTall ); + void GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos ); + + CLocatorTarget *GetPointerForHandle( int hTarget ); + int AddTarget(); + void RemoveTarget( int hTarget ); + + void GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ); + + void DeactivateAllTargets(); + void CollectGarbage(); + + // Animation + void AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart ); + void AnimateIconPosition( int flags, int *x, int *y ); + void AnimateIconAlpha( int flags, int *alpha, float fadeStart ); + +private: + + CPanelAnimationVar( vgui::HFont, m_hCaptionFont, "font", "InstructorTitle" ); + CPanelAnimationVar( vgui::HFont, m_hCaptionFont_ss, "font", "InstructorTitle_ss" ); + CPanelAnimationVar( vgui::HFont, m_hCaptionGlowFont, "font", "InstructorTitleGlow" ); + CPanelAnimationVar( vgui::HFont, m_hCaptionGlowFont_ss, "font", "InstructorTitleGlow_ss" ); + + CPanelAnimationVar( vgui::HFont, m_hButtonFont, "font", "InstructorButtons" ); + + CPanelAnimationVar( vgui::HFont, m_hButtonFont_ss, "font", "InstructorButtons_ss" ); + CPanelAnimationVar( vgui::HFont, m_hKeysFont, "font", "InstructorKeyBindings" ); + + + CPanelAnimationVar( int, m_iShouldWrapStaticLocators, "WrapStaticLocators", "0" ); + + static int m_serializer; // Used to issue unique serial numbers to targets, for use as handles + int m_textureID_ArrowRight; + int m_textureID_ArrowLeft; + int m_textureID_ArrowUp; + int m_textureID_ArrowDown; + int m_textureID_SimpleArrow; + + int m_staticIconPosition;// Helps us stack static icons + + CLocatorTarget m_targets[MAX_LOCATOR_TARGETS]; +}; + +//----------------------------------------------------------------------------- +// Local variables +//----------------------------------------------------------------------------- +static CLocatorPanel *s_pLocatorPanel; + +inline CLocatorPanel * GetPlayerLocatorPanel() +{ + //if ( !engine->IsLocalPlayerResolvable() ) + //return NULL; + + Assert( s_pLocatorPanel ); + return s_pLocatorPanel; +} + + +//----------------------------------------------------------------------------- +// Static variable initialization +//----------------------------------------------------------------------------- +int CLocatorPanel::m_serializer = 1000; // Serial numbers start at 1000 + +//----------------------------------------------------------------------------- +// This is the interface function that other systems use to send us targets +//----------------------------------------------------------------------------- +int Locator_AddTarget() +{ + if( s_pLocatorPanel == NULL ) + { + // Locator has not been used yet. Construct it. + CLocatorPanel *pLocator = new CLocatorPanel( g_pClientMode->GetViewport(), "LocatorPanel" ); + vgui::SETUP_PANEL(pLocator); + pLocator->SetBounds( 0, 0, ScreenWidth(), ScreenHeight() ); + pLocator->SetPos( 0, 0 ); + pLocator->SetVisible( true ); + vgui::ivgui()->AddTickSignal( pLocator->GetVPanel() ); + } + + Assert( s_pLocatorPanel != NULL ); + return s_pLocatorPanel ? s_pLocatorPanel->AddTarget() : -1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void Locator_RemoveTarget( int hTarget ) +{ + if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() ) + pPanel->RemoveTarget( hTarget ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CLocatorTarget *Locator_GetTargetFromHandle( int hTarget ) +{ + if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() ) + return pPanel->GetPointerForHandle( hTarget ); + else + return NULL; +} + +void Locator_ComputeTargetIconPositionFromHandle( int hTarget ) +{ + if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() ) + { + if ( CLocatorTarget *pTarget = pPanel->GetPointerForHandle( hTarget ) ) + { + if( !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) + { + // It's not presenting in the middle of the screen, so figure out it's position + pPanel->ComputeTargetIconPosition( pTarget, !pTarget->IsPresenting() ); + pPanel->CalculateOcclusion( pTarget ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CLocatorPanel::CLocatorPanel( Panel *parent, const char *name ) : EditablePanel(parent,name) +{ + Assert( s_pLocatorPanel == NULL ); + DeactivateAllTargets(); + + s_pLocatorPanel = this; + m_textureID_ArrowRight = -1; + m_textureID_ArrowLeft = -1; + m_textureID_ArrowUp = -1; + m_textureID_ArrowDown = -1; + m_textureID_SimpleArrow = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CLocatorPanel::~CLocatorPanel( void ) +{ + Assert( s_pLocatorPanel == this ); + s_pLocatorPanel = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Applies scheme settings +//----------------------------------------------------------------------------- +void CLocatorPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings("resource/UI/Locator.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + vgui::Panel *pPanel = FindChildByName( "LocatorBG" ); + + if ( pPanel ) + pPanel->SetPos( (GetWide() - pPanel->GetWide()) * 0.5, (GetTall() - pPanel->GetTall()) * 0.5 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given an offscreen target position, compute the 'compass position' +// so that we can draw an icon on the imaginary circle around the crosshair +// that indicates which way the player should turn to bring the target into view. +//----------------------------------------------------------------------------- +void CLocatorPanel::GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ) +{ + // Player Data + Vector playerPosition = MainViewOrigin(); + QAngle playerAngles = MainViewAngles(); + + Vector forward, right, up(0,0,1); + AngleVectors (playerAngles, &forward, NULL, NULL ); + forward.z = 0; + VectorNormalize(forward); + CrossProduct( up, forward, right ); + float front = DotProduct(vecDelta, forward); + float side = DotProduct(vecDelta, right); + *xpos = flRadius * -side; + *ypos = flRadius * -front; + + // Get the rotation (yaw) + *flRotation = atan2(*xpos,*ypos) + M_PI; + *flRotation *= 180 / M_PI; + + float yawRadians = -(*flRotation) * M_PI / 180.0f; + float ca = cos( yawRadians ); + float sa = sin( yawRadians ); + + // Rotate it around the circle, squash Y to make an oval rather than a circle + *xpos = (int)((ScreenWidth() / 2) + (flRadius * sa)); + *ypos = (int)((ScreenHeight() / 2) - (flRadius * 0.6f * ca)); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLocatorPanel::DeactivateAllTargets() +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + m_targets[ i ].Deactivate( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Deactivate any target that has not been updated within several frames +//----------------------------------------------------------------------------- +void CLocatorPanel::CollectGarbage() +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + if( m_targets[ i ].m_isActive ) + { + if( gpGlobals->framecount - m_targets[ i ].m_frameLastUpdated > 20 ) + m_targets[ i ].Deactivate(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Provide simple animation by modifying the width and height of the +// icon before it is drawn. +//----------------------------------------------------------------------------- +void CLocatorPanel::AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart ) +{ + float flScale = MIN_ICON_SCALE; + float scaleDelta = MAX_ICON_SCALE - MIN_ICON_SCALE; + + float newWide = *wide; + float newTall = *tall; + + if( flags & LOCATOR_ICON_FX_PULSE_SLOW || gpGlobals->curtime - fPulseStart < locator_pulse_time.GetFloat() ) + { + flScale += scaleDelta * fabs( sin( ( gpGlobals->curtime - fPulseStart ) * M_PI ) ); + } + else if( flags & LOCATOR_ICON_FX_PULSE_FAST ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 2 * M_PI ) ); + } + else if( flags & LOCATOR_ICON_FX_PULSE_URGENT ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 4 * M_PI ) ); + } + + if ( newWide > newTall ) + { + // Get scale to make width change by only the standard height amount of pixels + int iHeightDelta = (int)(newTall * flScale - newTall); + flScale = ( newWide + iHeightDelta ) / newWide; + } + + newWide = newWide * flScale; + newTall = newTall * flScale; + + *wide = newWide; + *tall = newTall; +} + +//----------------------------------------------------------------------------- +// Purpose: Modify the alpha of the icon before it is drawn. +//----------------------------------------------------------------------------- +void CLocatorPanel::AnimateIconAlpha( int flags, int *alpha, float fadeStart ) +{ + float flScale = MIN_ICON_ALPHA; + float scaleDelta = MAX_ICON_ALPHA - MIN_ICON_ALPHA; + + if( flags & LOCATOR_ICON_FX_ALPHA_SLOW ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 3 ) ); + } + else if( flags & LOCATOR_ICON_FX_ALPHA_FAST ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 7 ) ); + } + else if( flags & LOCATOR_ICON_FX_ALPHA_URGENT ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 10 ) ); + } + else + { + flScale = MAX_ICON_ALPHA; + } + + if ( flags & LOCATOR_ICON_FX_FADE_OUT ) + { + flScale *= MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() ); + } + else if ( flags & LOCATOR_ICON_FX_FADE_IN ) + { + flScale *= MAX_ICON_ALPHA - MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() ); + } + + *alpha = static_cast( 255.0f * flScale ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::AnimateIconPosition( int flags, int *x, int *y ) +{ + int newX = *x; + int newY = *y; + + if( flags & LOCATOR_ICON_FX_SHAKE_NARROW ) + { + newX += RandomInt( -2, 2 ); + newY += RandomInt( -2, 2 ); + } + else if( flags & LOCATOR_ICON_FX_SHAKE_WIDE ) + { + newX += RandomInt( -5, 5 ); + newY += RandomInt( -5, 5 ); + } + + *x = newX; + *y = newY; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::OnTick( void ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::PaintBackground( void ) +{ + return; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::Paint( void ) +{ + ValidateTexture( &m_textureID_ArrowLeft, "vgui/hud/icon_arrow_left" ); + ValidateTexture( &m_textureID_ArrowRight, "vgui/hud/icon_arrow_right" ); + ValidateTexture( &m_textureID_ArrowUp, "vgui/hud/icon_arrow_up" ); + ValidateTexture( &m_textureID_ArrowDown, "vgui/hud/icon_arrow_down" ); + ValidateTexture( &m_textureID_SimpleArrow, "vgui/hud/icon_arrow_plain" ); + + // reset the static icon position. This is the y position at which the first + // static icon will be drawn. This value will be incremented by the height of + // each static icon drawn, which forces the next static icon to be drawn below + // the previous one, creating a little fixed, vertical stack below the crosshair. + m_staticIconPosition = ScreenHeight() / 6; + + // Time now to draw the 'dynamic' icons, the icons which help players locate things + // in actual world space. + + //---------- + // Batch 1 + // Go through all of the active locator targets and compute where to draw the icons + // that represent each of them. This builds a poor man's draw list by updating the + // m_iconX, m_iconY members of each locator target. + CUtlVectorFixed< CLocatorTarget *, MAX_LOCATOR_TARGETS > vecValid; + + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + CLocatorTarget *pLocatorTarget = &(m_targets[ i ]); + + // Reset drawing state for this frame... set back to true when it's finally draws + pLocatorTarget->m_bIsDrawing = false; + + if ( ( !pLocatorTarget->m_bVisible && !pLocatorTarget->m_alpha ) || !pLocatorTarget->m_isActive ) + { + // Don't want to be visible and have finished fading + continue; + } + + vecValid.AddToTail( pLocatorTarget ); + + // This prevents an error that if a locator was fading as the map transitioned + pLocatorTarget->m_fadeStart = fpmin( pLocatorTarget->m_fadeStart, gpGlobals->curtime ); + + if( !( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) + { + // It's not presenting in the middle of the screen, so figure out it's position + ComputeTargetIconPosition( pLocatorTarget, !pLocatorTarget->IsPresenting() ); + CalculateOcclusion( pLocatorTarget ); + + pLocatorTarget->m_lastDeclutterIndex = pLocatorTarget->m_declutterIndex; + pLocatorTarget->m_declutterIndex = 0; + } + } + + //---------- + // Batch 2 + // Now that we know where each icon _wants_ to be drawn, we grovel through them and + // push apart any icons that are too close to one another. This helps to unclutter + // the display and ensure the maximum number of legible icons and captions. Obviously + // this process changes where some icons will be drawn. Bubble sort, but tiny data set. + int iTolerance = 1.25 * (ScreenWidth() * ICON_SIZE); + int iterations = 0;// Count iterations, don't go infinite in the event of some weird case. + bool bStillUncluttering = true; + static int MAX_UNCLUTTER_ITERATIONS = 10; + while( iterations < MAX_UNCLUTTER_ITERATIONS && bStillUncluttering ) + { + iterations++; + bStillUncluttering = false; + + for( int i = 0 ; i < vecValid.Count() ; ++i ) + { + CLocatorTarget *pLocatorTarget1 = vecValid[ i ]; + + for( int j = i + 1 ; j < vecValid.Count() ; ++j ) + { + CLocatorTarget *pLocatorTarget2 = vecValid[ j ]; + + // Don't attempt to declutter icons if one or both is attempting to fade out + bool bLocatorsFullyActive = !((pLocatorTarget1->GetIconEffectsFlags()|pLocatorTarget2->GetIconEffectsFlags()) & LOCATOR_ICON_FX_FADE_OUT); + + if ( bLocatorsFullyActive && IconsAreIntersecting( *pLocatorTarget1, *pLocatorTarget2, iTolerance ) ) + { + // Unclutter. Lift whichever icon is highest a bit higher + if( pLocatorTarget1->m_iconY < pLocatorTarget2->m_iconY ) + { + pLocatorTarget1->m_iconY = pLocatorTarget2->m_iconY - iTolerance; + pLocatorTarget1->m_centerY = pLocatorTarget2->m_centerY - iTolerance; + pLocatorTarget1->m_declutterIndex -= 1; + } + else + { + pLocatorTarget2->m_iconY = pLocatorTarget1->m_iconY - iTolerance; + pLocatorTarget2->m_centerY = pLocatorTarget1->m_centerY - iTolerance; + pLocatorTarget2->m_declutterIndex -= 1; + } + + bStillUncluttering = true; + } + } + } + } + + if( iterations == MAX_UNCLUTTER_ITERATIONS ) + { + DevWarning( "Game instructor hit MAX_UNCLUTTER_ITERATIONS!\n"); + } + + float flLocatorLerpRest = locator_lerp_rest.GetFloat(); + float flLocatorLerpTime = locator_lerp_time.GetFloat(); + + //---------- + // Batch 3 + // Draw each of the icons. + for( int i = 0 ; i < vecValid.Count() ; i++ ) + { + CLocatorTarget *pLocatorTarget = vecValid[ i ]; + // Back to lerping for these guys + if ( pLocatorTarget->m_lastDeclutterIndex != pLocatorTarget->m_declutterIndex ) + { + // It wants to be popped to another position... do it smoothly + pLocatorTarget->StartTimedLerp(); + } + + // Lerp to the desired position + float flLerpTime = gpGlobals->curtime - pLocatorTarget->m_lerpStart; + + if ( flLerpTime >= flLocatorLerpRest && flLerpTime < flLocatorLerpRest + flLocatorLerpTime ) + { + // Lerp slow to fast + float fInterp = 1.0f - ( ( flLocatorLerpTime - ( flLerpTime - flLocatorLerpRest ) ) / flLocatorLerpTime ); + + // Get our desired position + float iconX = pLocatorTarget->m_iconX; + float iconY = pLocatorTarget->m_iconY; + + // Get the distance we need to go to reach it + float diffX = fabsf( pLocatorTarget->m_iconX - pLocatorTarget->m_lastXPos ); + float diffY = fabsf( pLocatorTarget->m_iconY - pLocatorTarget->m_lastYPos ); + + // Go from our current position toward the desired position as quick as the interp allows + pLocatorTarget->m_iconX = static_cast( Approach( iconX, pLocatorTarget->m_lastXPos, diffX * fInterp ) ); + pLocatorTarget->m_iconY = static_cast( Approach( iconY, pLocatorTarget->m_lastYPos, diffY * fInterp ) ); + + // Get how much our position changed and apply it to the center values + int iOffsetX = pLocatorTarget->m_iconX - iconX; + int iOffsetY = pLocatorTarget->m_iconY - iconY; + + pLocatorTarget->m_centerX += iOffsetX; + pLocatorTarget->m_centerY += iOffsetY; + + if ( iOffsetX < 3 && iOffsetY < 3 ) + { + // Near our target! Stop lerping! + flLerpTime = flLocatorLerpRest + flLocatorLerpTime; + } + } + + PaintTarget( pLocatorTarget ); + } + + CollectGarbage(); +} + +//----------------------------------------------------------------------------- +// Purpose: A helper function to save on typing. Make sure our texture ID's +// stay valid. +//----------------------------------------------------------------------------- +void CLocatorPanel::ValidateTexture( int *pTextureID, const char *pszTextureName ) +{ + if( *pTextureID == -1 ) + { + *pTextureID = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( *pTextureID, pszTextureName, true, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame before painting the targets. Ensures that the +// target's textures are properly cached. +//----------------------------------------------------------------------------- +bool CLocatorPanel::ValidateTargetTextures( CLocatorTarget *pTarget ) +{ + bool bBindingTick = false; + + if ( gpGlobals->curtime >= pTarget->m_flNextBindingTick ) + { + if ( pTarget->m_iBindingChoicesCount > 1 ) + { + bBindingTick = true; + pTarget->m_iBindingTick++; + } + + pTarget->m_flNextBindingTick = gpGlobals->curtime + 0.75f; + + pTarget->UpdateVguiTarget(); + } + + bool bUsesBinding = ( Q_stricmp( pTarget->GetOnscreenIconTextureName(), "use_binding" ) == 0 ); + + if( !pTarget->m_pIcon_onscreen || !pTarget->m_pIcon_offscreen || ( bUsesBinding && bBindingTick ) ) + { + char szIconTextureName[ 256 ]; + if ( bUsesBinding ) + { + const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) ); + pTarget->m_bDrawControllerButton = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 ); + + pTarget->DrawBindingName( pchDrawBindingName ); + } + else + { + pTarget->m_bDrawControllerButton = false; + Q_strcpy( szIconTextureName, pTarget->GetOnscreenIconTextureName() ); + pTarget->DrawBindingName( NULL ); + } + + // This target's texture ID is dirty, meaning the target is about to be drawn + // for the first time, or about to be drawn for the first time since a texture + // was changed. + if ( Q_strlen(szIconTextureName) == 0 ) + { + DevWarning("Locator Target has no onscreen texture name!\n"); + return false; + } + else + { + pTarget->m_pIcon_onscreen = HudIcons().GetIcon( szIconTextureName ); + if ( pTarget->m_pIcon_onscreen ) + { + pTarget->m_widthScale_onscreen = static_cast< float >( pTarget->m_pIcon_onscreen->Width() ) / pTarget->m_pIcon_onscreen->Height(); + } + else + { + pTarget->m_widthScale_onscreen = 1.0f; + } + } + + if ( Q_stricmp( pTarget->GetOffscreenIconTextureName() , "use_binding" ) == 0 ) + { + const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) ); + pTarget->m_bDrawControllerButtonOffscreen = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 ); + + pTarget->DrawBindingNameOffscreen( pchDrawBindingName ); + } + else + { + pTarget->m_bDrawControllerButtonOffscreen = false; + Q_strcpy( szIconTextureName, pTarget->GetOffscreenIconTextureName() ); + pTarget->DrawBindingNameOffscreen( NULL ); + } + + if( Q_strlen(szIconTextureName) == 0 ) + { + if( !pTarget->m_pIcon_onscreen ) + { + DevWarning("Locator Target has no offscreen texture name and can't fall back!\n"); + } + else + { + // The onscreen texture is valid, so default behavior is to use that. + pTarget->m_pIcon_offscreen = pTarget->m_pIcon_onscreen; + const char *pchDrawBindingName = pTarget->DrawBindingName(); + pTarget->DrawBindingNameOffscreen( pchDrawBindingName ); + } + } + else + { + pTarget->m_pIcon_offscreen = HudIcons().GetIcon( szIconTextureName ); + } + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Compute where on the screen to draw the icon for this target. +//----------------------------------------------------------------------------- +void CLocatorPanel::ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition ) +{ + int iconX; + int iconY; + + // Measure the delta and the dist from this player to this target. + Vector vecTarget = pTarget->m_vecOrigin; + Vector vecDelta = vecTarget - MainViewOrigin(); + + if ( pTarget->m_bOriginInScreenspace ) + { + // Coordinates are already in screenspace + pTarget->m_distFromPlayer = 0.0f; + + iconX = vecTarget.x * ScreenWidth(); + iconY = vecTarget.y * ScreenHeight(); + pTarget->m_targetX = iconX; + pTarget->m_targetY = iconY; + } + else + { + pTarget->m_distFromPlayer = VectorNormalize( vecDelta ); + + if ( GetVectorInScreenSpace( vecTarget, iconX, iconY ) ) + { + // NOTE: GetVectorInScreenSpace returns false in an edge case where the + // target is very far off screen... just us the old values + pTarget->m_targetX = iconX; + pTarget->m_targetY = iconY; + } + } + + pTarget->m_drawArrowDirection = DRAW_ARROW_NO; + + float fTitleSafeInset = ScreenWidth() * 0.075f; + + if( iconX < fTitleSafeInset || iconX > ScreenWidth() - fTitleSafeInset ) + { + // It's off the screen left or right. + if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Back to lerping + pTarget->StartTimedLerp(); + pTarget->m_pulseStart = gpGlobals->curtime; + } + + if ( bSetPosition ) + { + pTarget->m_bOnscreen = false; + } + + GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY ); + + Vector vCenter = pTarget->m_vecOrigin; + if( MainViewRight().Dot( vCenter - MainViewOrigin() ) > 0 ) + pTarget->m_drawArrowDirection = DRAW_ARROW_RIGHT; + else + pTarget->m_drawArrowDirection = DRAW_ARROW_LEFT; + } + else if( iconY < fTitleSafeInset || iconY > ScreenHeight() - fTitleSafeInset ) + { + // It's off the screen up or down. + if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Back to lerping + pTarget->StartTimedLerp(); + pTarget->m_pulseStart = gpGlobals->curtime; + } + + if ( bSetPosition ) + { + pTarget->m_bOnscreen = false; + } + + GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY ); + + Vector vCenter = pTarget->m_vecOrigin; + if( MainViewUp().Dot( vCenter - MainViewOrigin() ) > 0 ) + pTarget->m_drawArrowDirection = DRAW_ARROW_UP; + else + pTarget->m_drawArrowDirection = DRAW_ARROW_DOWN; + } + else + { + if ( !pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Back to lerping + pTarget->StartTimedLerp(); + pTarget->m_pulseStart = gpGlobals->curtime; + } + + pTarget->m_bOnscreen = true; + } + + if ( bSetPosition ) + { + int tall = ScreenWidth() * ICON_SIZE; + int wide = tall * pTarget->m_widthScale_onscreen; + + // Animate the icon + AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart ); + AnimateIconPosition( pTarget->GetIconEffectsFlags(), &iconX, &iconY ); + AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart ); + + if( pTarget->m_distFromPlayer > ICON_DIST_TOO_FAR && !locator_topdown_style.GetBool() ) + { + // Make the icon smaller + wide = wide >> 1; + tall = tall >> 1; + } + + pTarget->m_centerX = iconX; + pTarget->m_centerY = iconY; + + pTarget->m_iconX = pTarget->m_centerX - ( wide >> 1 ); + pTarget->m_iconY = pTarget->m_centerY - ( tall >> 1 ); + pTarget->m_wide = wide; + pTarget->m_tall = tall; + } +} + +void CLocatorPanel::CalculateOcclusion( CLocatorTarget *pTarget ) +{ + if ( gpGlobals->curtime >= pTarget->m_flNextOcclusionTest ) + { + pTarget->m_flNextOcclusionTest = gpGlobals->curtime + LOCATOR_OCCLUSION_TEST_RATE; + + // Assume the target is not occluded. + pTarget->m_bOccluded = false; + + if ( pTarget->m_bOriginInScreenspace ) + return; + + trace_t tr; + UTIL_TraceLine( pTarget->m_vecOrigin, MainViewOrigin(), (CONTENTS_SOLID|CONTENTS_MOVEABLE), NULL, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction < 1.0f ) + { + pTarget->m_bOccluded = true; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is not valid until after you have computed the onscreen +// icon position for each target! +//----------------------------------------------------------------------------- +bool CLocatorPanel::IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance ) +{ + if( first.m_bOnscreen != second.m_bOnscreen ) + { + // We only declutter onscreen icons against other onscreen icons and vice-versa. + return false; + } + + if( first.IsStatic() || second.IsStatic() ) + { + // Static icons don't count. + return false; + } + + if( abs(first.GetIconY() - second.GetIconY()) < iTolerance ) + { + // OK, we need the Y-check first. Now we have to see if these icons and their captions overlap. + int firstWide = iTolerance + first.m_captionWide; + int secondWide = iTolerance + second.m_captionWide; + + if( abs(first.GetIconX() - second.GetIconX()) < (firstWide + secondWide) / 2 ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw this target on the locator. +// +// IF onscreen and visible, draw no icon, draw no arrows +// IF onscreen and occluded, draw icon transparently, draw no arrows +// IF offscreen, draw icon, draw an arrow indicating the direction to the target +//----------------------------------------------------------------------------- +void CLocatorPanel::PaintTarget( CLocatorTarget *pTarget ) +{ + bool bNewTexture = ValidateTargetTextures( pTarget ); + + if ( bNewTexture ) + { + // Refigure the width/height for the new texture + int tall = ScreenWidth() * ICON_SIZE; + int wide = tall * pTarget->m_widthScale_onscreen; + + AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart ); + + pTarget->m_wide = wide; + pTarget->m_tall = tall; + } + + // A static icon just draws with other static icons in a stack under the crosshair. + // Once displayed, they do not move. The are often used for notifiers. + if( pTarget->IsStatic() ) + { + DrawStaticIcon( pTarget ); + return; + } + + if ( !pTarget->m_bOnscreen && ( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Doesn't draw when offscreen... reset it's alpha so it has to fade in again + pTarget->m_fadeStart = gpGlobals->curtime; + pTarget->m_alpha = 0; + } + else + { + // Save these coordinates for later lerping + pTarget->m_lastXPos = pTarget->m_iconX; + pTarget->m_lastYPos = pTarget->m_iconY; + + // Draw when it's on screen or allowed to draw offscreen + DrawDynamicIcon( pTarget, pTarget->HasCaptionText(), pTarget->m_bOnscreen ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the caption-like background with word-bubble style pointer +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer ) +{ + if ( locator_background_style.GetInt() == 0 || pTarget->m_alpha == 0 ) + return; + + /* + int nPosX = pTarget->GetIconX() + locator_background_shift_x.GetInt() - locator_background_thickness_x.GetInt() / 2; + int nPosY = pTarget->GetIconY() + locator_background_shift_y.GetInt() - locator_background_thickness_y.GetInt() / 2; + int nBackgroundWide = nWide + locator_background_thickness_x.GetInt(); + int nBackgroundTall = nTall + locator_background_thickness_y.GetInt(); + + nPointerX = clamp( nPointerX, -0.5f * ScreenWidth(), ScreenWidth() * 1.5f ); + nPointerY = clamp( nPointerY, -0.5f * ScreenHeight(), ScreenHeight() * 1.5f ); + + float fAlpha = static_cast( pTarget->m_alpha ) / 255.0f; + + Color rgbaBackground = locator_background_color.GetColor(); + rgbaBackground[ 3 ] *= fAlpha; + + Color rgbaBorder = locator_background_border_color.GetColor(); + rgbaBorder[ 3 ] *= fAlpha; + */ + + DevMsg("[TODO] vgui::surface()->DrawWordBubble \n"); + + //vgui::surface()->DrawWordBubble( nPosX, nPosY, nPosX + nBackgroundWide, nPosY + nBackgroundTall, locator_background_border_thickness.GetInt(), rgbaBackground, rgbaBorder, bPointer, nPointerX, nPointerY, ScreenWidth() * ICON_SIZE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw an icon with the group of static icons. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawStaticIcon( CLocatorTarget *pTarget ) +{ + int centerX = ScreenWidth() / 2; + int centerY = ScreenHeight() / 2; + centerY += m_staticIconPosition; + + int iconTall = ScreenWidth() * ICON_SIZE; + int iconWide = iconTall * pTarget->m_widthScale_onscreen; + + pTarget->m_centerX = centerX; + pTarget->m_centerY = centerY; + + // Animate the icon + AnimateIconSize( pTarget->GetIconEffectsFlags(), &iconWide, &iconTall, pTarget->m_pulseStart ); + AnimateIconPosition( pTarget->GetIconEffectsFlags(), ¢erX, ¢erY ); + AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart ); + + // Figure out the caption width + pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + bool bDrawMultilineCaption = false; + + if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res + { + if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) ) + { + // we will double-line this + pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat(); + bDrawMultilineCaption = true; + } + } + int totalWide = iconWide + ICON_GAP + pTarget->m_captionWide; + pTarget->m_iconX = centerX - totalWide * 0.5f; + pTarget->m_iconY = centerY - ( iconTall >> 1 ); + + // Lerp by speed on the Y axis + float iconY = pTarget->m_iconY; + + float diffY = fabsf( pTarget->m_iconY - pTarget->m_lastYPos ); + + float flLerpSpeed = gpGlobals->frametime * locator_lerp_speed.GetFloat(); + pTarget->m_iconY = static_cast( Approach( iconY, pTarget->m_lastYPos, MAX( 3.0f, flLerpSpeed * diffY ) ) ); + pTarget->m_centerY += ( pTarget->m_iconY - iconY ); + + pTarget->m_lastXPos = pTarget->m_iconX; + pTarget->m_lastYPos = pTarget->m_iconY; + + pTarget->m_bIsDrawing = true; + + vgui::Panel *pVguiTarget = pTarget->GetVguiTarget(); + + if ( pVguiTarget ) + { + int nPanelX, nPanelY; + nPanelX = 0; + nPanelY = 0; + + vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge(); + + int nWide = pVguiTarget->GetWide(); + int nTall = pVguiTarget->GetTall(); + + /* + const char *pchLookup = pTarget->GetVguiTargetLookup(); + if ( pchLookup[ 0 ] != '\0' ) + { + bool bLookupSuccess = false; + bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nPanelX, nPanelY, nWide, nTall ); + + Assert( bLookupSuccess ); + } + */ + + if ( nVguiTargetEdge == vgui::Label::a_north || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_south ) + { + nPanelX += nWide / 2; + } + else if ( nVguiTargetEdge == vgui::Label::a_northeast || + nVguiTargetEdge == vgui::Label::a_east || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nPanelX += nWide; + } + + if ( nVguiTargetEdge == vgui::Label::a_west || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_east ) + { + nPanelY += nTall / 2; + } + else if ( nVguiTargetEdge == vgui::Label::a_southwest || + nVguiTargetEdge == vgui::Label::a_south || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nPanelY += nTall; + } + + pVguiTarget->LocalToScreen( nPanelX, nPanelY ); + + DrawPointerBackground( pTarget, nPanelX, nPanelY, totalWide, iconTall, true ); + } + else + { + DrawPointerBackground( pTarget, pTarget->m_centerX, pTarget->m_centerY, totalWide, iconTall, false ); + } + + if ( pTarget->m_pIcon_onscreen ) + { + if ( !pTarget->m_bDrawControllerButton ) + { + // Don't draw the icon if we're on 360 and have a binding to draw + pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iconWide, iconTall, Color( 255, 255, 255, pTarget->m_alpha ) ); + } + } + + DrawTargetCaption( pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY(), bDrawMultilineCaption ); + if ( pTarget->DrawBindingName() ) + { + DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iconWide>>1), pTarget->GetIconY() + (iconTall>>1), pTarget->m_bDrawControllerButton ); + } + + // Draw the arrow. + int iArrowSize = ScreenWidth() * ICON_SIZE; // Always square width + DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iArrowSize, iArrowSize, pTarget->m_captionWide + ICON_GAP, pTarget->m_drawArrowDirection ); + + pTarget->m_bOnscreen = true; + + // Move the static icon position so the next static icon drawn this frame below this one. + m_staticIconPosition += iconTall + (iconTall>>2); + // Move down a little more if this one was multi-line + if ( bDrawMultilineCaption ) + { + m_staticIconPosition += (iconTall>>2); + } + return; +} + +//----------------------------------------------------------------------------- +// Purpose: Position and animate this target's icon on the screen. Based on +// options, draw the indicator arrows (arrows that point to the +// direction the player should turn to see the icon), text caption, +// and the 'simple' arrow which just points down to indicate the +// item the icon represents. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow ) +{ + int alpha = pTarget->m_alpha; + + if( pTarget->m_bOccluded && !( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || locator_topdown_style.GetBool() ) ) + { + return; + } + + // Draw the icon! + vgui::surface()->DrawSetColor( 255, 255, 255, alpha ); + + int iWide = pTarget->m_wide; + + if ( !pTarget->m_bOnscreen ) + { + // Width is always square for offscreen icons + iWide /= pTarget->m_widthScale_onscreen; + } + + // Figure out the caption width + pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + bool bDrawMultilineCaption = false; + + if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res + { + if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) ) + { + // we will double-line this + pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat(); + bDrawMultilineCaption = true; + } + } + + int totalWide = iWide; + + bool bShouldDrawCaption = ( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || (!pTarget->m_bOccluded && pTarget->m_distFromPlayer <= ICON_DIST_TOO_FAR) || locator_topdown_style.GetBool() ); + + if( pTarget->m_bOnscreen && bDrawCaption && bShouldDrawCaption ) + { + totalWide += ( ICON_GAP + pTarget->m_captionWide ); + } + + pTarget->m_bIsDrawing = true; + + int nTargetX, nTargetY; + + vgui::Panel *pVguiTarget = pTarget->GetVguiTarget(); + + if ( pVguiTarget ) + { + nTargetX = 0; + nTargetY = 0; + + vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge(); + + int nWide = pVguiTarget->GetWide(); + int nTall = pVguiTarget->GetTall(); + + /* + const char *pchLookup = pTarget->GetVguiTargetLookup(); + + if ( pchLookup[ 0 ] != '\0' ) + { + bool bLookupSuccess = false; + bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nTargetX, nTargetY, nWide, nTall ); + Assert( bLookupSuccess ); + } + */ + + if ( nVguiTargetEdge == vgui::Label::a_north || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_south ) + { + nTargetX += nWide / 2; + } + else if ( nVguiTargetEdge== vgui::Label::a_northeast || + nVguiTargetEdge == vgui::Label::a_east || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nTargetX += nWide; + } + + if ( nVguiTargetEdge == vgui::Label::a_west || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_east ) + { + nTargetY += nTall / 2; + } + else if ( nVguiTargetEdge == vgui::Label::a_southwest || + nVguiTargetEdge == vgui::Label::a_south || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nTargetY += nTall; + } + + pVguiTarget->LocalToScreen( nTargetX, nTargetY ); + } + else if ( !pTarget->m_bOnscreen ) + { + nTargetX = pTarget->m_targetX; + nTargetY = pTarget->m_targetY; + } + else + { + nTargetX = pTarget->m_centerX; + nTargetY = pTarget->m_centerY; + } + + if ( pTarget->m_bOnscreen ) + { + DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true ); + } + else + { + // Offscreen we need to point the pointer toward out offscreen target + DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true ); + } + + if( pTarget->m_bOnscreen && pTarget->m_pIcon_onscreen ) + { + if ( !pTarget->m_bDrawControllerButton ) + { + pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, Color( 255, 255, 255, alpha ) ); + } + } + else if ( pTarget->m_pIcon_offscreen ) + { + if ( !pTarget->m_bDrawControllerButtonOffscreen ) + { + pTarget->m_pIcon_offscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, Color( 255, 255, 255, alpha ) ); + } + } + + if( !pTarget->m_bOnscreen ) + { + if ( pTarget->DrawBindingNameOffscreen() ) + { + DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButtonOffscreen ); + } + + if ( locator_background_style.GetInt() == 0 ) + { + // Draw the arrow. + DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, 0, pTarget->m_drawArrowDirection ); + } + } + else if( bShouldDrawCaption ) + { + if( bDrawCaption ) + { + //ScreenWidth() * * pTarget->m_widthScale_onscreen + DrawTargetCaption( pTarget, pTarget->GetIconCenterX() + ICON_GAP + pTarget->GetIconWidth() * 0.5, pTarget->GetIconCenterY(), bDrawMultilineCaption ); + } + if ( pTarget->DrawBindingName() ) + { + DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButton ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Some targets have text captions. Draw the text. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline ) +{ + // Draw the caption + vgui::surface()->DrawSetTextFont( m_hCaptionFont ); + int fontTall = vgui::surface()->GetFontTall( m_hCaptionFont ); + int iCaptionWidth = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + if ( bDrawMultiline ) + { + iCaptionWidth *= locator_split_len.GetFloat(); + } + + // Mapbase fixes glow and shadow not working in multiline + bool bDrawGlow = locator_text_glow.GetBool(); + bool bDrawShadow = !IsConsole() && locator_text_drop_shadow.GetBool(); // Only draw drop shadow on PC because it looks crappy on a TV + + if ( !bDrawMultiline ) + { + if ( bDrawGlow ) + { + vgui::surface()->DrawSetTextFont( m_hCaptionGlowFont ); + Color glowColor = locator_text_glow_color.GetColor(); + vgui::surface()->DrawSetTextColor( glowColor.r(), glowColor.g(), glowColor.b(), ( glowColor.a() / 255.0f ) * pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 ); + vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() ); + vgui::surface()->DrawSetTextFont( m_hCaptionFont ); + } + + if ( bDrawShadow ) + { + // Draw black text (drop shadow) + vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x, y - (fontTall >>1) ); + vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() ); + } + + // Draw text + vgui::surface()->DrawSetTextColor( pTarget->m_captionColor.r(),pTarget->m_captionColor.g(),pTarget->m_captionColor.b(), pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 ); + vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() ); + } + else + { + int charX = x-1; + int charY = y - ( fontTall >> 1 ) - 1; + + int iWidth = 0; + + const wchar_t *pString = pTarget->GetCaptionText(); + int len = Q_wcslen( pString ); + + Color glowColor = locator_text_glow_color.GetColor(); + for ( int iChar = 0; iChar < len; ++ iChar ) + { + int charW = vgui::surface()->GetCharacterWidth( m_hCaptionFont, pString[ iChar ] ); + iWidth += charW; + + if ( iWidth > pTarget->m_captionWide && pString[iChar] == L' ' ) + { + charY += fontTall; + charX = x-1; + iWidth = 0; + } + + if ( bDrawGlow ) + { + vgui::surface()->DrawSetTextFont( m_hCaptionGlowFont ); + vgui::surface()->DrawSetTextColor( glowColor.r(), glowColor.g(), glowColor.b(), ( glowColor.a() / 255.0f ) * pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( charX - 1, charY ); + vgui::surface()->DrawUnicodeChar( pString[iChar] ); + vgui::surface()->DrawSetTextFont( m_hCaptionFont ); + } + + if ( bDrawShadow ) + { + // Draw black text (drop shadow) + vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( charX, charY + 1 ); + vgui::surface()->DrawUnicodeChar( pString[iChar] ); + } + + // Draw text + vgui::surface()->DrawSetTextColor( pTarget->m_captionColor.r(),pTarget->m_captionColor.g(),pTarget->m_captionColor.b(), pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( charX, charY ); + vgui::surface()->DrawUnicodeChar( pString[iChar] ); + charX += charW; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Figure out how wide (pixels) a string will be if rendered with this font +// +//----------------------------------------------------------------------------- +int CLocatorPanel::GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont ) +{ + int iWidth = 0; + + for ( int iChar = 0; iChar < Q_wcslen( pString ); ++ iChar ) + { + iWidth += vgui::surface()->GetCharacterWidth( hFont, pString[ iChar ] ); + } + + return iWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: Some targets' captions contain information about key bindings that +// should be displayed to the player. Do so. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController ) +{ + if ( !bController && !IsConsole() ) + { + // Draw the caption + vgui::surface()->DrawSetTextFont( m_hKeysFont ); + int fontTall = vgui::surface()->GetFontTall( m_hKeysFont ); + + char szBinding[ 256 ]; + Q_strcpy( szBinding, pchBindingName ? pchBindingName : "" ); + + if ( Q_strcmp( szBinding, "SEMICOLON" ) == 0 ) + { + Q_strcpy( szBinding, ";" ); + } + else if ( Q_strlen( szBinding ) == 1 && szBinding[ 0 ] >= 'a' && szBinding[ 0 ] <= 'z' ) + { + // Make single letters uppercase + szBinding[ 0 ] += ( 'A' - 'a' ); + } + + wchar_t wszCaption[ 64 ]; + g_pVGuiLocalize->ConstructString( wszCaption, sizeof(wchar_t)*64, szBinding, NULL ); + + int iWidth = GetScreenWidthForCaption( wszCaption, m_hKeysFont ); + +#ifdef MAPBASE + // Mods are capable of choosing a custom color + vgui::surface()->DrawSetTextColor( pTarget->m_bindingColor.r(), pTarget->m_bindingColor.g(), pTarget->m_bindingColor.b(), pTarget->m_alpha ); +#else + // Draw black text + vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha ); +#endif + vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - 1, y - (fontTall >>1) - 1 ); + vgui::surface()->DrawUnicodeString( wszCaption ); + } + else + { + // Draw the caption + wchar_t wszCaption[ 64 ]; + + vgui::surface()->DrawSetTextFont( BUTTON_FONT_HANDLE ); + int fontTall = vgui::surface()->GetFontTall( BUTTON_FONT_HANDLE ); + + char szBinding[ 256 ]; + + // Turn localized string into icon character + Q_snprintf( szBinding, sizeof( szBinding ), "#GameUI_Icons_%s", pchBindingName ); + g_pVGuiLocalize->ConstructString( wszCaption, sizeof( wszCaption ), g_pVGuiLocalize->Find( szBinding ), 0 ); + g_pVGuiLocalize->ConvertUnicodeToANSI( wszCaption, szBinding, sizeof( szBinding ) ); + + int iWidth = GetScreenWidthForCaption( wszCaption, BUTTON_FONT_HANDLE ); + + int iLargeIconShift = MAX( 0, iWidth - ( ScreenWidth() * ICON_SIZE + ICON_GAP + ICON_GAP ) ); + + // Draw the button + vgui::surface()->DrawSetTextColor( 255,255,255, pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - iLargeIconShift, y - (fontTall >>1) ); + vgui::surface()->DrawUnicodeString( wszCaption ); + + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw an arrow to indicate that a target is offscreen +// +// iconWide is sent to this function so that the arrow knows how to straddle +// the icon that it is being drawn near. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, int direction ) +{ + int wide = iconWide; + int tall = iconTall; + + //tall = wide = ScreenWidth() * ICON_SIZE; + + switch( direction ) + { + case DRAW_ARROW_LEFT: + vgui::surface()->DrawSetTexture( m_textureID_ArrowLeft ); + x -= wide; + y += iconTall / 2 - tall / 2; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + case DRAW_ARROW_RIGHT: + vgui::surface()->DrawSetTexture( m_textureID_ArrowRight ); + x += iconWide + textWidth; + y += iconTall / 2 - tall / 2; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + case DRAW_ARROW_UP: + vgui::surface()->DrawSetTexture( m_textureID_ArrowUp ); + x += iconWide / 2 - wide / 2; + y -= tall; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + case DRAW_ARROW_DOWN: + vgui::surface()->DrawSetTexture( m_textureID_ArrowDown ); + x += iconWide / 2 - wide / 2; + y += iconTall; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + default: + // Do not draw. + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draws a very simple arrow that points down. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawSimpleArrow( int x, int y, int iconWide, int iconTall ) +{ + vgui::surface()->DrawSetTexture( m_textureID_SimpleArrow ); + + y += iconTall; + + vgui::surface()->DrawTexturedRect( x, y, x + iconWide, y + iconTall ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLocatorPanel::GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos ) +{ + float xpos, ypos; + float flRotation; + float flRadius = YRES(OFFSCREEN_ICON_POSITION_RADIUS); + + if ( locator_topdown_style.GetBool() ) + { + flRadius *= clamp( flDist / 600.0f, 1.75f, 3.0f ); + } + + GetTargetPosition( vecDelta, flRadius, &xpos, &ypos, &flRotation ); + + *pXPos = xpos; + *pYPos = ypos; +} + +//----------------------------------------------------------------------------- +// Purpose: Given a handle, return the pointer to the proper locator target. +//----------------------------------------------------------------------------- +CLocatorTarget *CLocatorPanel::GetPointerForHandle( int hTarget ) +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + if( m_targets[ i ].m_isActive && m_targets[ i ].m_serialNumber == hTarget ) + { + return &m_targets[ i ]; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CLocatorPanel::AddTarget() +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + if( !m_targets[ i ].m_isActive ) + { + m_targets[ i ].Activate( m_serializer ); + m_serializer++; + + return m_targets[ i ].m_serialNumber; + } + } + + DevWarning( "Locator Panel has no free targets!\n" ); + return -1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLocatorPanel::RemoveTarget( int hTarget ) +{ + CLocatorTarget *pTarget = GetPointerForHandle( hTarget ); + + if( pTarget ) + { + pTarget->Deactivate(); + } +} + diff --git a/mp/src/game/client/hud_locator_target.h b/mp/src/game/client/hud_locator_target.h new file mode 100644 index 00000000..06799325 --- /dev/null +++ b/mp/src/game/client/hud_locator_target.h @@ -0,0 +1,184 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: Add entities to this system, and the Locator will maintain an arrow +// on the HUD that points to the entities when they are offscreen. +// +//============================================================================= + +#ifndef L4D_HUD_LOCATOR_H +#define L4D_HUD_LOCATOR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/PHandle.h" + + +#define MAX_LOCATOR_BINDINGS_SHOWN 8 +#define MAX_LOCATOR_TARGETS 10 +#define LOCATOR_FLAGS_NONE 0x00000000 + +#define LOCATOR_ICON_FX_NONE 0x00000000 +#define LOCATOR_ICON_FX_PULSE_SLOW 0x00000001 +#define LOCATOR_ICON_FX_PULSE_FAST 0x00000002 +#define LOCATOR_ICON_FX_PULSE_URGENT 0x00000004 +#define LOCATOR_ICON_FX_ALPHA_SLOW 0x00000008 +#define LOCATOR_ICON_FX_ALPHA_FAST 0x00000010 +#define LOCATOR_ICON_FX_ALPHA_URGENT 0x00000020 +#define LOCATOR_ICON_FX_SHAKE_NARROW 0x00000040 +#define LOCATOR_ICON_FX_SHAKE_WIDE 0x00000080 +#define LOCATOR_ICON_FX_STATIC 0x00000100 // This icon draws at a fixed location on the HUD. +#define LOCATOR_ICON_FX_NO_OFFSCREEN 0x00000200 +#define LOCATOR_ICON_FX_FORCE_CAPTION 0x00000400 // Always draw the caption, even when the icon is occluded. +#define LOCATOR_ICON_FX_FADE_OUT 0x00000800 // Set when deactivated so it can smoothly vanish +#define LOCATOR_ICON_FX_FADE_IN 0x00001000 // Set when activated so it can smoothly appear + +#include "tier1/UtlSymbol.h" + +// See comments in UtlSymbol on why this is useful +DECLARE_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol ); + +//----------------------------------------------------------------------------- +// This class represents a single target to be tracked by the locator +//----------------------------------------------------------------------------- +class CLocatorTarget +{ +public: + bool m_bOriginInScreenspace; + Vector m_vecOrigin; // The location in the world to draw on the locator + + // ONLY the locator panel should fiddle with these fields. + bool m_isActive; + int m_serialNumber; + int m_frameLastUpdated; + bool m_bOnscreen; + bool m_bOccluded; + bool m_bVisible; + bool m_bIsDrawing; + float m_distFromPlayer; + CHudTexture *m_pIcon_onscreen; + CHudTexture *m_pIcon_offscreen; + int m_iBindingTick; + float m_flNextBindingTick; + float m_flNextOcclusionTest; + int m_iBindingChoicesCount; + const char *(m_pchBindingChoices[ MAX_LOCATOR_BINDINGS_SHOWN ]); + int m_iBindChoicesOriginalToken[ MAX_LOCATOR_BINDINGS_SHOWN ]; + + // Fields for drawing + int m_targetX; // screen X position of the actual target + int m_targetY; // screen Y position of the actual target + int m_iconX; // screen X position (top) + int m_iconY; // screen Y position (left) + int m_centerX; // screen X position (center) + int m_centerY; // screen Y position (center) + int m_wide; // draw width of icon (may be different from frame to frame as the icon's size animates, for instance) + int m_tall; // draw height of icon '' '' + float m_widthScale_onscreen; // for icons that are wider than standard + int m_alpha; // + float m_fadeStart; // time stamp when fade out started + float m_lerpStart; // time stamp when lerping started + float m_pulseStart; // time stamp when pulsing started + int m_declutterIndex; // sort order from the declutterer + int m_lastDeclutterIndex; // last sort order from the declutterer + int m_drawArrowDirection; // Whether to draw an arrow indicating this target is off-screen, also tells us which arrow to draw (left, up, etc.) + int m_captionWide; // How wide (pixels) my caption is. + bool m_bDrawControllerButton; + bool m_bDrawControllerButtonOffscreen; + int m_offsetX; // User-specified X offset which is applied in screenspace + int m_offsetY; // User-specified Y offset which is applied in screenspace + + // Fields for interpolating icon position + float m_flTimeLerpDone; // How much time left before this icon arrives where it is supposed to be. + int m_lastXPos; // screen X position last frame + int m_lastYPos; // '' Y + + CLocatorTarget( void ); + void Activate( int serialNumber ); + void Deactivate( bool bNoFade = false ); + void Update(); + + int GetIconX( void ); + int GetIconY( void ); + int GetIconCenterX( void ); + int GetIconCenterY( void ); + int GetIconWidth( void ); + int GetIconHeight( void ); + + void AddIconEffects( int add ) { m_iEffectsFlags |= add; } + void RemoveIconEffects( int remove ) { m_iEffectsFlags &= ~remove; } + int GetIconEffectsFlags() { return m_iEffectsFlags; } + void SetCaptionColor( Color col ) { m_captionColor = col; } + void SetCaptionColor( const char *pszCaptionColor ); + bool IsStatic(); + bool IsPresenting(); + void StartTimedLerp(); + void StartPresent(); + void EndPresent(); + + void UpdateVguiTarget( void ); + vgui::Panel *GetVguiTarget( void ); + void SetVguiTargetName( const char *pchVguiTargetName ); + const char *GetVguiTargetName( void ) { return m_szVguiTargetName.String(); } + void SetVguiTargetLookup( const char *pchVguiTargetLookup ); + const char *GetVguiTargetLookup( void ) { return m_szVguiTargetLookup.String(); } + void SetVguiTargetEdge( int nVguiEdge ); + int GetVguiTargetEdge( void ) const { return m_nVguiTargetEdge; } + + void SetOnscreenIconTextureName( const char *pszTexture ); + void SetOffscreenIconTextureName( const char *pszTexture ); + void SetBinding( const char *pszBinding ); + const char *UseBindingImage( char *pchIconTextureName, size_t bufSize ); + + const char *GetOnscreenIconTextureName() { return m_szOnscreenTexture.String(); } + const char *GetOffscreenIconTextureName() { return m_szOffscreenTexture.String(); } + const char *GetBinding() { return m_szBinding.String(); } + + void SetVisible( bool bVisible ); + bool IsVisible( void ); + + void SetCaptionText( const char *pszText, const char *pszParam ); + const wchar_t *GetCaptionText( void ) { return (const wchar_t *)m_wszCaption.Base(); } + bool HasCaptionText( void ) { return m_wszCaption.Count() > 1; } + + void DrawBindingName( const char *pchDrawName ) { m_pchDrawBindingName = pchDrawName; } + void DrawBindingNameOffscreen( const char *pchDrawName ) { m_pchDrawBindingNameOffscreen = pchDrawName; } + + const char *DrawBindingName( void ) { return m_pchDrawBindingName; } + const char *DrawBindingNameOffscreen( void ) { return m_pchDrawBindingNameOffscreen; } + + bool IsOnScreen() { return m_bOnscreen; } + bool IsOccluded() { return m_bOccluded; } + + +private: + CGameInstructorSymbol m_szVguiTargetName; + CGameInstructorSymbol m_szVguiTargetLookup; + vgui::DHANDLE m_hVguiTarget; + int m_nVguiTargetEdge; + + CGameInstructorSymbol m_szOnscreenTexture; + CGameInstructorSymbol m_szOffscreenTexture; + CGameInstructorSymbol m_szBinding; + + bool m_bWasControllerLast; + const char *m_pchDrawBindingName; + const char *m_pchDrawBindingNameOffscreen; + int m_iEffectsFlags; + CUtlVector< wchar_t > m_wszCaption; + +public: + Color m_captionColor; +#ifdef MAPBASE + Color m_bindingColor; +#endif +}; + +extern int Locator_AddTarget(); +extern void Locator_RemoveTarget( int hTarget ); +CLocatorTarget *Locator_GetTargetFromHandle( int hTarget ); +void Locator_ComputeTargetIconPositionFromHandle( int hTarget ); + + +#endif // L4D_HUD_LOCATOR_H \ No newline at end of file diff --git a/mp/src/game/client/iclientshadowmgr.h b/mp/src/game/client/iclientshadowmgr.h index 174f57a8..df8f9352 100644 --- a/mp/src/game/client/iclientshadowmgr.h +++ b/mp/src/game/client/iclientshadowmgr.h @@ -97,6 +97,15 @@ public: // Set flashlight light world flag virtual void SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld ) = 0; +#ifdef ASW_PROJECTED_TEXTURES + virtual void GetFrustumExtents( ClientShadowHandle_t handle, Vector &vecMin, Vector &vecMax ) = 0; +#endif + +#ifdef DYNAMIC_RTT_SHADOWS + // Toggle shadow casting from world light sources + virtual void SetShadowFromWorldLightsEnabled( bool bEnable ) = 0; +#endif + virtual void SetShadowsDisabled( bool bDisabled ) = 0; virtual void ComputeShadowDepthTextures( const CViewSetup &pView ) = 0; diff --git a/mp/src/game/client/in_main.cpp b/mp/src/game/client/in_main.cpp index 21eb30a0..2cd8a6b5 100644 --- a/mp/src/game/client/in_main.cpp +++ b/mp/src/game/client/in_main.cpp @@ -1219,6 +1219,9 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo } // Let the move manager override anything it wants to. +#ifdef VGUI_SCREEN_FIX + cmd->buttons |= IN_VALIDVGUIINPUT; +#endif if ( g_pClientMode->CreateMove( input_sample_frametime, cmd ) ) { // Get current view angles after the client mode tweaks with it diff --git a/mp/src/game/client/mapbase/c_func_clientclip.cpp b/mp/src/game/client/mapbase/c_func_clientclip.cpp new file mode 100644 index 00000000..f6595949 --- /dev/null +++ b/mp/src/game/client/mapbase/c_func_clientclip.cpp @@ -0,0 +1,93 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//===========================================================================// + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +class C_FuncClientClip : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_FuncClientClip, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + void OnDataChanged( DataUpdateType_t type ); + void ClientThink(); + bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace ); + + bool m_bDisabled; +}; + +IMPLEMENT_CLIENTCLASS_DT( C_FuncClientClip, DT_FuncClientClip, CFuncClientClip ) + RecvPropBool( RECVINFO( m_bDisabled ) ), +END_RECV_TABLE() + +void C_FuncClientClip::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + //if ( type == DATA_UPDATE_CREATED ) + //{ + SetSolid(GetMoveParent() ? SOLID_VPHYSICS : SOLID_BSP); // SOLID_VPHYSICS + //} + + if ( !m_bDisabled ) + { + VPhysicsDestroyObject(); + VPhysicsInitShadow( true, true ); + + // Think constantly updates the shadow + if (GetMoveParent()) + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } + else + { + // Disabling + VPhysicsDestroyObject(); + SetNextClientThink( CLIENT_THINK_NEVER ); + } +} + +void C_FuncClientClip::ClientThink() +{ + // We shouldn't be thinking if we're disabled + Assert(!m_bDisabled); + + if (VPhysicsGetObject()) + { + // Constantly updates the shadow. + // This think function should really only be active when we're parented. + VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), GetAbsAngles(), false, TICK_INTERVAL ); + } + else + { + // This should never happen... + VPhysicsInitShadow( true, true ); + } + + BaseClass::ClientThink(); +} + +bool C_FuncClientClip::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace ) +{ + if ( m_bDisabled ) + return false; + + if ( !VPhysicsGetObject() ) + return false; + + physcollision->TraceBox( ray, VPhysicsGetObject()->GetCollide(), GetAbsOrigin(), GetAbsAngles(), &trace ); + + if ( trace.DidHit() ) + { + trace.surface.surfaceProps = VPhysicsGetObject()->GetMaterialIndex(); + return true; + } + + return false; +} diff --git a/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp b/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp new file mode 100644 index 00000000..9d92675e --- /dev/null +++ b/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp @@ -0,0 +1,226 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// (basically a combination of point_camera and func_reflective_glass) +// +// $NoKeywords: $ +//===========================================================================// +#include "cbase.h" +#include "view_shared.h" +#include "viewrender.h" +#include "c_func_fake_worldportal.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT( C_FuncFakeWorldPortal, DT_FuncFakeWorldPortal, CFuncFakeWorldPortal ) + + RecvPropEHandle( RECVINFO( m_hTargetPlane ) ), + RecvPropVector( RECVINFO( m_PlaneAngles ) ), + RecvPropInt( RECVINFO( m_iSkyMode ) ), + RecvPropFloat( RECVINFO( m_flScale ) ), + RecvPropString( RECVINFO( m_iszRenderTarget ) ), + RecvPropEHandle( RECVINFO( m_hFogController ) ), + +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +C_EntityClassList g_FakeWorldPortalList; +template<> C_FuncFakeWorldPortal *C_EntityClassList::m_pClassList = NULL; + +C_FuncFakeWorldPortal* GetFakeWorldPortalList() +{ + return g_FakeWorldPortalList.m_pClassList; +} + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal::C_FuncFakeWorldPortal() +{ + m_iszRenderTarget[0] = '\0'; + + g_FakeWorldPortalList.Insert( this ); +} + +C_FuncFakeWorldPortal::~C_FuncFakeWorldPortal() +{ + g_FakeWorldPortalList.Remove( this ); +} + + +bool C_FuncFakeWorldPortal::ShouldDraw() +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Do we have a fake world portal in view? +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal *IsFakeWorldPortalInView( const CViewSetup& view, cplane_t &plane ) +{ + // Early out if no cameras + C_FuncFakeWorldPortal *pReflectiveGlass = GetFakeWorldPortalList(); + if ( !pReflectiveGlass ) + return NULL; + + Frustum_t frustum; + GeneratePerspectiveFrustum( view.origin, view.angles, view.zNear, view.zFar, view.fov, view.m_flAspectRatio, frustum ); + + cplane_t localPlane; + Vector vecOrigin, vecWorld, vecDelta, vecForward; + AngleVectors( view.angles, &vecForward, NULL, NULL ); + + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) + { + if ( pReflectiveGlass->IsDormant() ) + continue; + + if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) + continue; + + Vector vecMins, vecMaxs; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + if ( R_CullBox( vecMins, vecMaxs, frustum ) ) + continue; + + const model_t *pModel = pReflectiveGlass->GetModel(); + const matrix3x4_t& mat = pReflectiveGlass->EntityToWorldTransform(); + + int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); + for ( int i = 0; i < nCount; ++i ) + { + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + + MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space + VectorTransform( vecOrigin, mat, vecWorld ); + + if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + continue; + + VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull + if ( vecDelta.Dot( plane.normal ) >= 0 ) + continue; + + // Must have valid plane + if ( !pReflectiveGlass->m_hTargetPlane ) + continue; + + return pReflectiveGlass; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Iterates through fake world portals instead of just picking one +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum ) +{ + // Early out if no cameras + C_FuncFakeWorldPortal *pReflectiveGlass = NULL; + if (!pStart) + pReflectiveGlass = GetFakeWorldPortalList(); + else + pReflectiveGlass = pStart->m_pNext; + + cplane_t localPlane; + Vector vecOrigin, vecWorld, vecDelta; + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) + { + if ( pReflectiveGlass->IsDormant() ) + continue; + + if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) + continue; + + Vector vecMins, vecMaxs; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + if ( R_CullBox( vecMins, vecMaxs, frustum ) ) + continue; + + const model_t *pModel = pReflectiveGlass->GetModel(); + const matrix3x4_t& mat = pReflectiveGlass->EntityToWorldTransform(); + + int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); + for ( int i = 0; i < nCount; ++i ) + { + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + + MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space + VectorTransform( vecOrigin, mat, vecWorld ); + + if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + continue; + + VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull + if ( vecDelta.Dot( plane.normal ) >= 0 ) + continue; + + // Must have valid plane + if ( !pReflectiveGlass->m_hTargetPlane ) + continue; + + return pReflectiveGlass; + } + } + + return NULL; +} + +void C_FuncFakeWorldPortal::OnDataChanged( DataUpdateType_t type ) +{ + // Reset render texture + m_pRenderTarget = NULL; + + // Reset fog + m_pFog = NULL; + + return BaseClass::OnDataChanged( type ); +} + +extern ITexture *GetWaterReflectionTexture( void ); + +ITexture *C_FuncFakeWorldPortal::RenderTarget() +{ + if (m_iszRenderTarget[0] != '\0') + { + if (!m_pRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pRenderTarget = materials->FindTexture( m_iszRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pRenderTarget) + return m_pRenderTarget; + } + + return GetWaterReflectionTexture(); +} + +fogparams_t *C_FuncFakeWorldPortal::GetFog() +{ + if (m_pFog) + return m_pFog; + + if (m_hFogController) + { + C_FogController *pFogController = dynamic_cast(m_hFogController.Get()); + if (pFogController) + { + m_pFog = &pFogController->m_fog; + } + else + { + Warning("%s is not an env_fog_controller\n", m_hFogController->GetEntityName()); + } + } + + return NULL; +} diff --git a/mp/src/game/client/mapbase/c_func_fake_worldportal.h b/mp/src/game/client/mapbase/c_func_fake_worldportal.h new file mode 100644 index 00000000..3fc4418b --- /dev/null +++ b/mp/src/game/client/mapbase/c_func_fake_worldportal.h @@ -0,0 +1,64 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// (basically a combination of point_camera and func_reflective_glass) +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef C_FUNC_FAKE_WORLDPORTAL +#define C_FUNC_FAKE_WORLDPORTAL + +#ifdef _WIN32 +#pragma once +#endif + +struct cplane_t; +class CViewSetup; + +class C_FuncFakeWorldPortal : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_FuncFakeWorldPortal, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_FuncFakeWorldPortal(); + virtual ~C_FuncFakeWorldPortal(); + + virtual bool ShouldDraw(); + virtual void OnDataChanged( DataUpdateType_t type ); + + SkyboxVisibility_t SkyMode() { return m_iSkyMode; } + + ITexture *RenderTarget(); + + fogparams_t *GetFog(); + +public: + + EHANDLE m_hTargetPlane; + QAngle m_PlaneAngles; + SkyboxVisibility_t m_iSkyMode; + float m_flScale; + + EHANDLE m_hFogController; + fogparams_t *m_pFog; + + char m_iszRenderTarget[64]; + ITexture *m_pRenderTarget; + + C_FuncFakeWorldPortal *m_pNext; +}; + +//----------------------------------------------------------------------------- +// Do we have reflective glass in view? If so, what's the reflection plane? +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal *IsFakeWorldPortalInView( const CViewSetup& view, cplane_t &plane ); + +C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum ); + + +#endif // C_FUNC_FAKE_WORLDPORTAL + + diff --git a/mp/src/game/client/mapbase/c_point_glow.cpp b/mp/src/game/client/mapbase/c_point_glow.cpp new file mode 100644 index 00000000..590e0f3e --- /dev/null +++ b/mp/src/game/client/mapbase/c_point_glow.cpp @@ -0,0 +1,103 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase off-shoot of tf_glow (created using SDK code only) +// +//===========================================================================// + +#include "cbase.h" +#include "glow_outline_effect.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +class C_PointGlow : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_PointGlow, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + ~C_PointGlow(); + + enum + { + GLOW_VIS_ALWAYS, + GLOW_VIS_NOT_WHEN_VISIBLE, + GLOW_VIS_ONLY_WHEN_VISIBLE, + }; + + void OnDataChanged( DataUpdateType_t type ); + + CGlowObject *GetGlowObject( void ){ return m_pGlowEffect; } + void UpdateGlowEffect( void ); + void DestroyGlowEffect( void ); + + EHANDLE m_hGlowTarget; + color32 m_GlowColor; + + bool m_bGlowDisabled; + CGlowObject *m_pGlowEffect; +}; + +IMPLEMENT_CLIENTCLASS_DT( C_PointGlow, DT_PointGlow, CPointGlow ) + RecvPropEHandle( RECVINFO( m_hGlowTarget ) ), + RecvPropInt( RECVINFO( m_GlowColor ), 0, RecvProxy_IntToColor32 ), + RecvPropBool( RECVINFO( m_bGlowDisabled ) ), +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_PointGlow::~C_PointGlow() +{ + DestroyGlowEffect(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointGlow::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + UpdateGlowEffect(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointGlow::UpdateGlowEffect( void ) +{ + // destroy the existing effect + if ( m_pGlowEffect ) + { + DestroyGlowEffect(); + } + + // create a new effect + if ( !m_bGlowDisabled ) + { + Vector4D vecColor( m_GlowColor.r, m_GlowColor.g, m_GlowColor.b, m_GlowColor.a ); + for (int i = 0; i < 4; i++) + { + if (vecColor[i] == 0.0f) + continue; + + vecColor[i] /= 255.0f; + } + + m_pGlowEffect = new CGlowObject( m_hGlowTarget, vecColor.AsVector3D(), vecColor.w, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointGlow::DestroyGlowEffect( void ) +{ + if ( m_pGlowEffect ) + { + delete m_pGlowEffect; + m_pGlowEffect = NULL; + } +} diff --git a/mp/src/game/client/message.cpp b/mp/src/game/client/message.cpp index fe341e71..dc41bb00 100644 --- a/mp/src/game/client/message.cpp +++ b/mp/src/game/client/message.cpp @@ -862,6 +862,85 @@ void CHudMessage::MsgFunc_HudMsg(bf_read &msg) // see tmessage.cpp why 512 msg.ReadString( (char*)pNetMessage->pMessage, 512 ); +#ifdef MAPBASE + // + // Mapbase adds a new data entry for custom fonts on entities like game_text. + // Some existing instances of this user message may not have this, so we have to make sure we have any bits left first. + // + if (msg.GetNumBitsLeft() > 0) + { + // Try to have VGui font names for each channel + static char szVGuiFontNames[MAX_NETMESSAGE][512]; + msg.ReadString( szVGuiFontNames[channel], 512 ); + if (szVGuiFontNames[channel][0] != '\0') + { + pNetMessage->pVGuiSchemeFontName = szVGuiFontNames[channel]; + } + } + + // + // Mapbase adds a new data entry for breaking game_text into newline when it goes past the user's screen. + // Some existing instances of this user message may not have this, so we have to make sure we have any bits left first. + // + if (msg.GetNumBitsLeft() > 0) + { + int len = msg.ReadByte(); + + // This is supposed to work around a bug where certain aspect ratios cut off lengthy texts. + //int lineMax = 64 * ((float)ScreenWidth() / 1440.0f); + int lineMax = 100 / engine->GetScreenAspectRatio(); + + int lineMinBreak = lineMax * 0.9; + + CGMsg( 2, CON_GROUP_CHOREO, "Line max is %i from an aspect ratio of %.3f (strlen %i)\n", lineMax, engine->GetScreenAspectRatio(), len ); + + char *curMessage = (char*)pNetMessage->pMessage; + char newMessage[512]; + + int cur = 0; // Current time on this line + int i = 0; // curMessage + int i2 = 0; // newMessage + for (i = 0; i < len; i++) + { + cur++; + newMessage[i2] = curMessage[i]; + + // Check if we're past the point in which we should break the line + if (cur >= lineMinBreak) + { + // Line break at the next space + if (curMessage[i] == ' ') + { + newMessage[i2] = '\n'; + cur = 0; + } + else if (curMessage[i] == '\n') + { + // Already a newline here + cur = 0; + } + else if (cur >= lineMax) + { + // We're at the max and there's no space. Force a newline with a hyphen + newMessage[i2] = '-'; + i2++; + newMessage[i2] = '\n'; + i2++; + newMessage[i2] = curMessage[i]; + cur = 0; + } + } + + i2++; + } + + // Null terminate + newMessage[i2] = '\0'; + + Q_strncpy( (char*)pNetMessage->pMessage, newMessage, 512 ); + } +#endif + MessageAdd( pNetMessage->pName ); } diff --git a/mp/src/game/client/panelmetaclassmgr.cpp b/mp/src/game/client/panelmetaclassmgr.cpp index 6f0b50d0..2b36cc44 100644 --- a/mp/src/game/client/panelmetaclassmgr.cpp +++ b/mp/src/game/client/panelmetaclassmgr.cpp @@ -234,6 +234,14 @@ CPanelMetaClassMgrImp::CPanelMetaClassMgrImp() : m_PanelTypeDict( true, 0, 32 ) CPanelMetaClassMgrImp::~CPanelMetaClassMgrImp() { +#ifdef MAPBASE // VDC Memory Leak Fixes + while (m_MetaClassKeyValues.Count()>0) + { + if (m_MetaClassKeyValues[0]) + m_MetaClassKeyValues[0]->deleteThis(); + m_MetaClassKeyValues.RemoveAt(0); + } +#endif } diff --git a/mp/src/game/client/particles_simple.cpp b/mp/src/game/client/particles_simple.cpp index b2971dbb..20500b32 100644 --- a/mp/src/game/client/particles_simple.cpp +++ b/mp/src/game/client/particles_simple.cpp @@ -308,8 +308,12 @@ void CSimpleEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta { if (pParticle->m_iFlags & SIMPLE_PARTICLE_FLAG_WINDBLOWN) { +#ifdef MAPBASE + Vector vecWind = GetWindspeedAtLocation( pParticle->m_Pos ); +#else Vector vecWind; GetWindspeedAtTime( gpGlobals->curtime, vecWind ); +#endif for ( int i = 0 ; i < 2 ; i++ ) { diff --git a/mp/src/game/client/prediction.cpp b/mp/src/game/client/prediction.cpp index 2a67007b..b669cc1c 100644 --- a/mp/src/game/client/prediction.cpp +++ b/mp/src/game/client/prediction.cpp @@ -30,6 +30,13 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +// This turned out to be causing major issues with VPhysics collision. +// It's deactivated until a fix is found. +// See player_command.cpp as well. +//#define PLAYER_COMMAND_FIX 1 +#endif + IPredictionSystem *IPredictionSystem::g_pPredictionSystems = NULL; #if !defined( NO_ENTITY_PREDICTION ) @@ -909,9 +916,15 @@ void CPrediction::RunCommand( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper pVehicle->ProcessMovement( player, g_pMoveData ); } +#ifdef PLAYER_COMMAND_FIX + RunPostThink( player ); + + FinishMove( player, ucmd, g_pMoveData ); +#else FinishMove( player, ucmd, g_pMoveData ); RunPostThink( player ); +#endif g_pGameMovement->FinishTrackPredictionErrors( player ); diff --git a/mp/src/game/client/vgui_video.cpp b/mp/src/game/client/vgui_video.cpp index 57323f40..3aa08ccd 100644 --- a/mp/src/game/client/vgui_video.cpp +++ b/mp/src/game/client/vgui_video.cpp @@ -24,7 +24,12 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe m_nPlaybackHeight( 0 ), m_bAllowAlternateMedia( allowAlternateMedia ) { + +#ifdef MAPBASE + vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_ROOT ); +#else vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_GAMEUIDLL ); +#endif SetParent( pParent ); SetVisible( false ); @@ -421,4 +426,4 @@ CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command wh Warning( "Unable to play video: %s\n", strFullpath ); engine->ClientCmd( pExitCommand ); } -} \ No newline at end of file +} diff --git a/mp/src/game/client/view.cpp b/mp/src/game/client/view.cpp index d74d326e..04e37e3a 100644 --- a/mp/src/game/client/view.cpp +++ b/mp/src/game/client/view.cpp @@ -126,6 +126,9 @@ ConVar gl_clear( "gl_clear", "0"); ConVar gl_clear_randomcolor( "gl_clear_randomcolor", "0", FCVAR_CHEAT, "Clear the back buffer to random colors every frame. Helps spot open seams in geometry." ); static ConVar r_farz( "r_farz", "-1", FCVAR_CHEAT, "Override the far clipping plane. -1 means to use the value in env_fog_controller." ); +#ifdef MAPBASE +static ConVar r_nearz( "r_nearz", "-1", FCVAR_CHEAT, "Override the near clipping plane. -1 means to use the default value (usually 7)." ); +#endif static ConVar cl_demoviewoverride( "cl_demoviewoverride", "0", 0, "Override view during demo playback" ); @@ -602,6 +605,11 @@ static QAngle s_DbgSetupAngles; //----------------------------------------------------------------------------- float CViewRender::GetZNear() { +#ifdef MAPBASE + if (r_nearz.GetFloat() > 0) + return r_nearz.GetFloat(); +#endif + return VIEW_NEARZ; } @@ -736,7 +744,11 @@ void CViewRender::SetUpViews() float flFOVOffset = fDefaultFov - view.fov; //Adjust the viewmodel's FOV to move with any FOV offsets on the viewer's end +#ifdef MAPBASE + view.fovViewmodel = fabs(g_pClientMode->GetViewModelFOV()) - flFOVOffset; +#else view.fovViewmodel = g_pClientMode->GetViewModelFOV() - flFOVOffset; +#endif if ( UseVR() ) { diff --git a/mp/src/game/client/viewpostprocess.cpp b/mp/src/game/client/viewpostprocess.cpp index 39c2237e..e1845d8d 100644 --- a/mp/src/game/client/viewpostprocess.cpp +++ b/mp/src/game/client/viewpostprocess.cpp @@ -13,8 +13,13 @@ #include "materialsystem/materialsystem_config.h" #include "tier1/callqueue.h" #include "colorcorrectionmgr.h" +#include "postprocess_shared.h" #include "view_scene.h" #include "c_world.h" + +//Tony; new +#include "c_baseplayer.h" + #include "bitmap/tgawriter.h" #include "filesystem.h" #include "tier0/vprof.h" @@ -34,6 +39,15 @@ float g_flCustomAutoExposureMax = 0; float g_flCustomBloomScale = 0.0f; float g_flCustomBloomScaleMinimum = 0.0f; +// mapmaker controlled depth of field +bool g_bDOFEnabled = false; +float g_flDOFNearBlurDepth = 20.0f; +float g_flDOFNearFocusDepth = 100.0f; +float g_flDOFFarFocusDepth = 250.0f; +float g_flDOFFarBlurDepth = 1000.0f; +float g_flDOFNearBlurRadius = 0.0f; +float g_flDOFFarBlurRadius = 10.0f; + bool g_bFlashlightIsOn = false; // hdr parameters @@ -289,9 +303,9 @@ void ApplyPostProcessingPasses(PostProcessingPass *pass_list, // table of effect pRenderContext->SetRenderTarget(NULL); int row=pcount/4; int col=pcount %4; - int dest_width,dest_height; - pRenderContext->GetRenderTargetDimensions( dest_width, dest_height ); - pRenderContext->Viewport( 0, 0, dest_width, dest_height ); + int destwidth,destheight; + pRenderContext->GetRenderTargetDimensions( destwidth, destheight ); + pRenderContext->Viewport( 0, 0, destwidth, destheight ); DrawClippedScreenSpaceRectangle(src_mat,10+col*220,10+row*220, 200,200, 0,0,1,1,1,1,cb); @@ -332,7 +346,7 @@ PostProcessingPass HDRSimulate_NonHDR[] = PPP_END }; -static void SetRenderTargetAndViewPort(ITexture *rt) +void SetRenderTargetAndViewPort(ITexture *rt) { tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); @@ -765,7 +779,19 @@ static float GetCurrentBloomScale( void ) { // Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings. float flCurrentBloomScale = 1.0f; - if ( g_bUseCustomBloomScale ) + + //Tony; get the local player first.. + C_BasePlayer *pLocalPlayer = NULL; + + if ( ( gpGlobals->maxClients > 1 ) ) + pLocalPlayer = (C_BasePlayer*)C_BasePlayer::GetLocalPlayer(); + + //Tony; in multiplayer, get the local player etc. + if ( (pLocalPlayer != NULL && pLocalPlayer->m_Local.m_TonemapParams.m_flAutoExposureMin > 0.0f) ) + { + flCurrentBloomScale = pLocalPlayer->m_Local.m_TonemapParams.m_flAutoExposureMin; + } + else if ( g_bUseCustomBloomScale ) { flCurrentBloomScale = g_flCustomBloomScale; } @@ -778,8 +804,19 @@ static float GetCurrentBloomScale( void ) static void GetExposureRange( float *flAutoExposureMin, float *flAutoExposureMax ) { + //Tony; get the local player first.. + C_BasePlayer *pLocalPlayer = NULL; + + if ( ( gpGlobals->maxClients > 1 ) ) + pLocalPlayer = (C_BasePlayer*)C_BasePlayer::GetLocalPlayer(); + + //Tony; in multiplayer, get the local player etc. + if ( (pLocalPlayer != NULL && pLocalPlayer->m_Local.m_TonemapParams.m_flAutoExposureMin > 0.0f) ) + { + *flAutoExposureMin = pLocalPlayer->m_Local.m_TonemapParams.m_flAutoExposureMin; + } // Get min - if ( ( g_bUseCustomAutoExposureMin ) && ( g_flCustomAutoExposureMin > 0.0f ) ) + else if ( ( g_bUseCustomAutoExposureMin ) && ( g_flCustomAutoExposureMin > 0.0f ) ) { *flAutoExposureMin = g_flCustomAutoExposureMin; } @@ -788,8 +825,13 @@ static void GetExposureRange( float *flAutoExposureMin, float *flAutoExposureMax *flAutoExposureMin = mat_autoexposure_min.GetFloat(); } + //Tony; in multiplayer, get the value from the local player, if it's set. + if ( (pLocalPlayer != NULL && pLocalPlayer->m_Local.m_TonemapParams.m_flAutoExposureMax > 0.0f) ) + { + *flAutoExposureMax = pLocalPlayer->m_Local.m_TonemapParams.m_flAutoExposureMax; + } // Get max - if ( ( g_bUseCustomAutoExposureMax ) && ( g_flCustomAutoExposureMax > 0.0f ) ) + else if ( ( g_bUseCustomAutoExposureMax ) && ( g_flCustomAutoExposureMax > 0.0f ) ) { *flAutoExposureMax = g_flCustomAutoExposureMax; } @@ -1113,6 +1155,32 @@ void CLuminanceHistogramSystem::DisplayHistogram( void ) pRenderContext->PopRenderTargetAndViewport(); } +// Local contrast setting +PostProcessParameters_t s_LocalPostProcessParameters; + +// view fade param settings +static Vector4D s_viewFadeColor; +static bool s_bViewFadeModulate; + +static bool s_bOverridePostProcessParams = false; + +void SetPostProcessParams( const PostProcessParameters_t* pPostProcessParameters ) +{ + if (!s_bOverridePostProcessParams) + s_LocalPostProcessParameters = *pPostProcessParameters; +} + +void SetPostProcessParams( const PostProcessParameters_t* pPostProcessParameters, bool bOverride ) +{ + s_bOverridePostProcessParams = bOverride; + s_LocalPostProcessParameters = *pPostProcessParameters; +} + +void SetViewFadeParams( byte r, byte g, byte b, byte a, bool bModulate ) +{ + s_viewFadeColor.Init( float( r ) / 255.0f, float( g ) / 255.0f, float( b ) / 255.0f, float( a ) / 255.0f ); + s_bViewFadeModulate = bModulate; +} static CLuminanceHistogramSystem g_HDR_HistogramSystem; @@ -1208,15 +1276,32 @@ private: IMaterialVar *m_pMaterialParam_AAValues; IMaterialVar *m_pMaterialParam_AAValues2; IMaterialVar *m_pMaterialParam_BloomEnable; + IMaterialVar *m_pMaterialParam_BloomAmount; IMaterialVar *m_pMaterialParam_BloomUVTransform; IMaterialVar *m_pMaterialParam_ColCorrectEnable; IMaterialVar *m_pMaterialParam_ColCorrectNumLookups; IMaterialVar *m_pMaterialParam_ColCorrectDefaultWeight; IMaterialVar *m_pMaterialParam_ColCorrectLookupWeights; + IMaterialVar *m_pMaterialParam_LocalContrastStrength; + IMaterialVar *m_pMaterialParam_LocalContrastEdgeStrength; + IMaterialVar *m_pMaterialParam_VignetteStart; + IMaterialVar *m_pMaterialParam_VignetteEnd; + IMaterialVar *m_pMaterialParam_VignetteBlurEnable; + IMaterialVar *m_pMaterialParam_VignetteBlurStrength; + IMaterialVar *m_pMaterialParam_FadeToBlackStrength; + IMaterialVar *m_pMaterialParam_DepthBlurFocalDistance; + IMaterialVar *m_pMaterialParam_DepthBlurStrength; + IMaterialVar *m_pMaterialParam_ScreenBlurStrength; + IMaterialVar *m_pMaterialParam_FilmGrainStrength; + IMaterialVar *m_pMaterialParam_VomitEnable; + IMaterialVar *m_pMaterialParam_VomitColor1; + IMaterialVar *m_pMaterialParam_VomitColor2; + IMaterialVar *m_pMaterialParam_FadeColor; + IMaterialVar *m_pMaterialParam_FadeType; public: static IMaterial * SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize, - bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength ); + bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength, float flBloomAmount ); static void SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength ); static void SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D destTexSize ); @@ -1225,12 +1310,14 @@ private: static float s_vBloomAAValues2[4]; static float s_vBloomUVTransform[4]; static int s_PostBloomEnable; + static float s_PostBloomAmount; }; float CEnginePostMaterialProxy::s_vBloomAAValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float CEnginePostMaterialProxy::s_vBloomAAValues2[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float CEnginePostMaterialProxy::s_vBloomUVTransform[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; int CEnginePostMaterialProxy::s_PostBloomEnable = 1; +float CEnginePostMaterialProxy::s_PostBloomAmount = 1.0f; CEnginePostMaterialProxy::CEnginePostMaterialProxy() { @@ -1238,10 +1325,22 @@ CEnginePostMaterialProxy::CEnginePostMaterialProxy() m_pMaterialParam_AAValues2 = NULL; m_pMaterialParam_BloomUVTransform = NULL; m_pMaterialParam_BloomEnable = NULL; + m_pMaterialParam_BloomAmount = NULL; m_pMaterialParam_ColCorrectEnable = NULL; m_pMaterialParam_ColCorrectNumLookups = NULL; m_pMaterialParam_ColCorrectDefaultWeight = NULL; m_pMaterialParam_ColCorrectLookupWeights = NULL; + m_pMaterialParam_LocalContrastStrength = NULL; + m_pMaterialParam_LocalContrastEdgeStrength = NULL; + m_pMaterialParam_VignetteStart = NULL; + m_pMaterialParam_VignetteEnd = NULL; + m_pMaterialParam_VignetteBlurEnable = NULL; + m_pMaterialParam_VignetteBlurStrength = NULL; + m_pMaterialParam_FadeToBlackStrength = NULL; + m_pMaterialParam_DepthBlurFocalDistance = NULL; + m_pMaterialParam_DepthBlurStrength = NULL; + m_pMaterialParam_ScreenBlurStrength = NULL; + m_pMaterialParam_FilmGrainStrength = NULL; } CEnginePostMaterialProxy::~CEnginePostMaterialProxy() @@ -1252,15 +1351,32 @@ CEnginePostMaterialProxy::~CEnginePostMaterialProxy() bool CEnginePostMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool bFoundVar = false; - + m_pMaterialParam_AAValues = pMaterial->FindVar( "$AAInternal1", &bFoundVar, false ); m_pMaterialParam_AAValues2 = pMaterial->FindVar( "$AAInternal3", &bFoundVar, false ); m_pMaterialParam_BloomUVTransform = pMaterial->FindVar( "$AAInternal2", &bFoundVar, false ); m_pMaterialParam_BloomEnable = pMaterial->FindVar( "$bloomEnable", &bFoundVar, false ); + m_pMaterialParam_BloomAmount = pMaterial->FindVar( "$bloomAmount", &bFoundVar, false ); m_pMaterialParam_ColCorrectEnable = pMaterial->FindVar( "$colCorrectEnable", &bFoundVar, false ); m_pMaterialParam_ColCorrectNumLookups = pMaterial->FindVar( "$colCorrect_NumLookups", &bFoundVar, false ); m_pMaterialParam_ColCorrectDefaultWeight = pMaterial->FindVar( "$colCorrect_DefaultWeight", &bFoundVar, false ); m_pMaterialParam_ColCorrectLookupWeights = pMaterial->FindVar( "$colCorrect_LookupWeights", &bFoundVar, false ); + m_pMaterialParam_LocalContrastStrength = pMaterial->FindVar( "$localContrastScale", &bFoundVar, false ); + m_pMaterialParam_LocalContrastEdgeStrength = pMaterial->FindVar( "$localContrastEdgeScale", &bFoundVar, false ); + m_pMaterialParam_VignetteStart = pMaterial->FindVar( "$localContrastVignetteStart", &bFoundVar, false ); + m_pMaterialParam_VignetteEnd = pMaterial->FindVar( "$localContrastVignetteEnd", &bFoundVar, false ); + m_pMaterialParam_VignetteBlurEnable = pMaterial->FindVar( "$blurredVignetteEnable", &bFoundVar, false ); + m_pMaterialParam_VignetteBlurStrength = pMaterial->FindVar( "$blurredVignetteScale", &bFoundVar, false ); + m_pMaterialParam_FadeToBlackStrength = pMaterial->FindVar( "$fadeToBlackScale", &bFoundVar, false ); + m_pMaterialParam_DepthBlurFocalDistance = pMaterial->FindVar( "$depthBlurFocalDistance", &bFoundVar, false ); + m_pMaterialParam_DepthBlurStrength = pMaterial->FindVar( "$depthBlurStrength", &bFoundVar, false ); + m_pMaterialParam_ScreenBlurStrength = pMaterial->FindVar( "$screenBlurStrength", &bFoundVar, false ); + m_pMaterialParam_FilmGrainStrength = pMaterial->FindVar( "$noiseScale", &bFoundVar, false ); + m_pMaterialParam_VomitEnable = pMaterial->FindVar( "$vomitEnable", &bFoundVar, false ); + m_pMaterialParam_VomitColor1 = pMaterial->FindVar( "$vomitColor1", &bFoundVar, false ); + m_pMaterialParam_VomitColor2 = pMaterial->FindVar( "$vomitColor2", &bFoundVar, false ); + m_pMaterialParam_FadeColor = pMaterial->FindVar( "$fadeColor", &bFoundVar, false ); + m_pMaterialParam_FadeType = pMaterial->FindVar( "$fade", &bFoundVar, false ); return true; } @@ -1278,6 +1394,56 @@ void CEnginePostMaterialProxy::OnBind( C_BaseEntity *pEnt ) if ( m_pMaterialParam_BloomEnable ) m_pMaterialParam_BloomEnable->SetIntValue( s_PostBloomEnable ); + + if ( m_pMaterialParam_BloomAmount ) + m_pMaterialParam_BloomAmount->SetFloatValue( s_PostBloomAmount ); + + if ( m_pMaterialParam_LocalContrastStrength ) + m_pMaterialParam_LocalContrastStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_LOCAL_CONTRAST_STRENGTH ] ); + + if ( m_pMaterialParam_LocalContrastEdgeStrength ) + m_pMaterialParam_LocalContrastEdgeStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_LOCAL_CONTRAST_EDGE_STRENGTH ] ); + + if ( m_pMaterialParam_VignetteStart ) + m_pMaterialParam_VignetteStart->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_VIGNETTE_START ] ); + + if ( m_pMaterialParam_VignetteEnd ) + m_pMaterialParam_VignetteEnd->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_VIGNETTE_END ] ); + + if ( m_pMaterialParam_VignetteBlurEnable ) + m_pMaterialParam_VignetteBlurEnable->SetIntValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ] > 0.0f ? 1 : 0 ); + + if ( m_pMaterialParam_VignetteBlurStrength ) + m_pMaterialParam_VignetteBlurStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ] ); + + if ( m_pMaterialParam_FadeToBlackStrength ) + m_pMaterialParam_FadeToBlackStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_FADE_TO_BLACK_STRENGTH ] ); + + if ( m_pMaterialParam_DepthBlurFocalDistance ) + m_pMaterialParam_DepthBlurFocalDistance->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_DEPTH_BLUR_FOCAL_DISTANCE ] ); + + if ( m_pMaterialParam_DepthBlurStrength ) + m_pMaterialParam_DepthBlurStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_DEPTH_BLUR_STRENGTH ] ); + + if ( m_pMaterialParam_ScreenBlurStrength ) + m_pMaterialParam_ScreenBlurStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_SCREEN_BLUR_STRENGTH ] ); + + if ( m_pMaterialParam_FilmGrainStrength ) + m_pMaterialParam_FilmGrainStrength->SetFloatValue( s_LocalPostProcessParameters.m_flParameters[ PPPN_FILM_GRAIN_STRENGTH ] ); + + + + if ( m_pMaterialParam_FadeType ) + { + int nFadeType = ( s_bViewFadeModulate ) ? 2 : 1; + nFadeType = ( s_viewFadeColor[3] > 0.0f ) ? nFadeType : 0; + m_pMaterialParam_FadeType->SetIntValue( nFadeType ); + } + + if ( m_pMaterialParam_FadeColor ) + { + m_pMaterialParam_FadeColor->SetVecValue( s_viewFadeColor.Base(), 4 ); + } } IMaterial *CEnginePostMaterialProxy::GetMaterial() @@ -1350,26 +1516,29 @@ void CEnginePostMaterialProxy::SetupEnginePostMaterialTextureTransform( const Ve } IMaterial * CEnginePostMaterialProxy::SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize, - bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength ) + bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength, float flBloomAmount ) { // Shouldn't get here if none of the effects are enabled Assert( bPerformSoftwareAA || bPerformBloom || bPerformColCorrect ); - s_PostBloomEnable = bPerformBloom ? 1 : 0; + s_PostBloomEnable = bPerformBloom ? 1 : 0; + s_PostBloomAmount = flBloomAmount; SetupEnginePostMaterialAA( bPerformSoftwareAA, flAAStrength ); - if ( bPerformSoftwareAA || bPerformColCorrect ) + //if ( bPerformSoftwareAA || bPerformColCorrect ) { SetupEnginePostMaterialTextureTransform( fullViewportBloomUVs, fullViewportFBUVs, destTexSize ); return materials->FindMaterial( "dev/engine_post", TEXTURE_GROUP_OTHER, true); } + /* else { // Just use the old bloomadd material (which uses additive blending, unlike engine_post) // NOTE: this path is what gets used for DX8 (which cannot enable AA or col-correction) return materials->FindMaterial( "dev/bloomadd", TEXTURE_GROUP_OTHER, true); } + */ } EXPOSE_INTERFACE( CEnginePostMaterialProxy, IMaterialProxy, "engine_post" IMATERIAL_PROXY_INTERFACE_VERSION ); @@ -1520,6 +1689,28 @@ void DumpTGAofRenderTarget( const int width, const int height, const char *pFile static bool s_bScreenEffectTextureIsUpdated = false; +// WARNING: This function sets rendertarget and viewport. Save and restore is left to the caller. +static void DownsampleFBQuarterSize( IMatRenderContext *pRenderContext, int nSrcWidth, int nSrcHeight, ITexture* pDest, + bool bFloatHDR = false ) +{ + Assert( pRenderContext ); + Assert( pDest ); + + IMaterial *downsample_mat = materials->FindMaterial( bFloatHDR ? "dev/downsample" : "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true ); + + // *Everything* in here relies on the small RTs being exactly 1/4 the full FB res + Assert( pDest->GetActualWidth() == nSrcWidth / 4 ); + Assert( pDest->GetActualHeight() == nSrcHeight / 4 ); + + // downsample fb to rt0 + SetRenderTargetAndViewPort( pDest ); + // note the -2's below. Thats because we are downsampling on each axis and the shader + // accesses pixels on both sides of the source coord + pRenderContext->DrawScreenSpaceRectangle( downsample_mat, 0, 0, nSrcWidth/4, nSrcHeight/4, + 0, 0, nSrcWidth-2, nSrcHeight-2, + nSrcWidth, nSrcHeight ); +} + static void Generate8BitBloomTexture( IMatRenderContext *pRenderContext, float flBloomScale, int x, int y, int w, int h ) { @@ -1582,7 +1773,7 @@ static void Generate8BitBloomTexture( IMatRenderContext *pRenderContext, float f // Gaussian blur y rt1 to rt0 SetRenderTargetAndViewPort( dest_rt0 ); IMaterialVar *pBloomAmountVar = yblur_mat->FindVar( "$bloomamount", NULL ); - pBloomAmountVar->SetFloatValue( flBloomScale ); + pBloomAmountVar->SetFloatValue( 1.0f ); // the bloom amount is now applied in engine_post or bloomadd materials pRenderContext->DrawScreenSpaceRectangle( yblur_mat, 0, 0, nSrcWidth / 4, nSrcHeight / 4, 0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, nSrcWidth / 4, nSrcHeight / 4 ); @@ -2325,7 +2516,7 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b // bloom, software-AA and colour-correction (applied in 1 pass, after generation of the bloom texture) bool bPerformSoftwareAA = IsX360() && ( engine->GetDXSupportLevel() >= 90 ) && ( flAAStrength != 0.0f ); - bool bPerformBloom = !bPostVGui && ( flBloomScale > 0.0f ) && ( engine->GetDXSupportLevel() >= 90 ); + bool bPerformBloom = true; //!bPostVGui && ( flBloomScale > 0.0f ) && ( engine->GetDXSupportLevel() >= 90 ); bool bPerformColCorrect = !bPostVGui && ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90) && ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_FLOAT ) && @@ -2385,9 +2576,9 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b partialViewportPostDestRect.height -= 0.50f*fullViewportPostDestRect.height; // This math interprets texel coords as being at corner pixel centers (*not* at corner vertices): - Vector2D uvScale( 1.0f - ( (w / 2) / (float)(w - 1) ), + Vector2D uvScaleCorner( 1.0f - ( (w / 2) / (float)(w - 1) ), 1.0f - ( (h / 2) / (float)(h - 1) ) ); - CenterScaleQuadUVs( partialViewportPostSrcCorners, uvScale ); + CenterScaleQuadUVs( partialViewportPostSrcCorners, uvScaleCorner ); } // Temporary hack... Color correction was crashing on the first frame @@ -2403,7 +2594,7 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b { // Perform post-processing in one combined pass - IMaterial *post_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, bPerformSoftwareAA, bPerformBloom, bPerformColCorrect, flAAStrength ); + IMaterial *post_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, bPerformSoftwareAA, bPerformBloom, bPerformColCorrect, flAAStrength, flBloomScale ); if (bSplitScreenHDR) { @@ -2431,7 +2622,7 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b // Perform post-processing in three separate passes if ( bPerformSoftwareAA ) { - IMaterial *aa_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, bPerformSoftwareAA, false, false, flAAStrength ); + IMaterial *aa_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, bPerformSoftwareAA, false, false, flAAStrength, flBloomScale ); if (bSplitScreenHDR) { @@ -2456,7 +2647,7 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b if ( bPerformBloom ) { - IMaterial *bloom_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, false, bPerformBloom, false, flAAStrength ); + IMaterial *bloom_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, false, bPerformBloom, false, flAAStrength, flBloomScale ); if (bSplitScreenHDR) { @@ -2487,7 +2678,7 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect ); } - IMaterial *colcorrect_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, false, false, bPerformColCorrect, flAAStrength ); + IMaterial *colcorrect_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, false, false, bPerformColCorrect, flAAStrength, flBloomScale ); if (bSplitScreenHDR) { @@ -2635,6 +2826,7 @@ void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, b // Motion Blur Material Proxy ========================================================================================= static float g_vMotionBlurValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; +static float g_vMotionBlurViewportValues[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; class CMotionBlurMaterialProxy : public CEntityMaterialProxy { public: @@ -2646,6 +2838,7 @@ public: private: IMaterialVar *m_pMaterialParam; + IMaterialVar *m_pMaterialParamViewport; }; CMotionBlurMaterialProxy::CMotionBlurMaterialProxy() @@ -2666,6 +2859,10 @@ bool CMotionBlurMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues if ( bFoundVar == false) return false; + m_pMaterialParamViewport = pMaterial->FindVar( "$MotionBlurViewportInternal", &bFoundVar, false ); + if ( bFoundVar == false) + return false; + return true; } @@ -2675,6 +2872,11 @@ void CMotionBlurMaterialProxy::OnBind( C_BaseEntity *pEnt ) { m_pMaterialParam->SetVecValue( g_vMotionBlurValues, 4 ); } + + if ( m_pMaterialParamViewport != NULL ) + { + m_pMaterialParamViewport->SetVecValue( g_vMotionBlurViewportValues, 4 ); + } } IMaterial *CMotionBlurMaterialProxy::GetMaterial() @@ -2691,24 +2893,49 @@ EXPOSE_INTERFACE( CMotionBlurMaterialProxy, IMaterialProxy, "MotionBlur" IMATERI // Image-space Motion Blur ============================================================================================ //===================================================================================================================== ConVar mat_motion_blur_enabled( "mat_motion_blur_enabled", "1", FCVAR_ARCHIVE ); + ConVar mat_motion_blur_forward_enabled( "mat_motion_blur_forward_enabled", "0" ); ConVar mat_motion_blur_falling_min( "mat_motion_blur_falling_min", "10.0" ); + ConVar mat_motion_blur_falling_max( "mat_motion_blur_falling_max", "20.0" ); ConVar mat_motion_blur_falling_intensity( "mat_motion_blur_falling_intensity", "1.0" ); //ConVar mat_motion_blur_roll_intensity( "mat_motion_blur_roll_intensity", "1.0" ); ConVar mat_motion_blur_rotation_intensity( "mat_motion_blur_rotation_intensity", "1.0" ); ConVar mat_motion_blur_strength( "mat_motion_blur_strength", "1.0" ); -void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h ) +struct MotionBlurHistory_t { -#ifdef CSS_PERF_TEST - return; -#endif - if ( ( !mat_motion_blur_enabled.GetInt() ) || ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 ) ) + MotionBlurHistory_t() + { + m_flLastTimeUpdate = 0.0f; + m_flPreviousPitch = 0.0f; + m_flPreviousYaw = 0.0f; + m_vPreviousPositon.Init( 0.0f, 0.0f, 0.0f ); + m_mPreviousFrameBasisVectors; + m_flNoRotationalMotionBlurUntil = 0.0f; + SetIdentityMatrix( m_mPreviousFrameBasisVectors ); + } + + float m_flLastTimeUpdate; + float m_flPreviousPitch; + float m_flPreviousYaw; + Vector m_vPreviousPositon; + matrix3x4_t m_mPreviousFrameBasisVectors; + float m_flNoRotationalMotionBlurUntil; +}; + +void DoImageSpaceMotionBlur( const CViewSetup &viewSetup ) +{ + if ( !mat_motion_blur_enabled.GetInt() ) { return; } + int x = viewSetup.x; + int y = viewSetup.y; + int w = viewSetup.width; + int h = viewSetup.height; + //======================================================================================================// // Get these convars here to make it easier to remove them later and to default each client differently // //======================================================================================================// @@ -2727,22 +2954,19 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h //=====================// // Previous frame data // //=====================// - static float s_flLastTimeUpdate = 0.0f; - static float s_flPreviousPitch = 0.0f; - static float s_flPreviousYaw = 0.0f; - static float s_vPreviousPositon[3] = { 0.0f, 0.0f, 0.0f }; - static matrix3x4_t s_mPreviousFrameBasisVectors; - static float s_flNoRotationalMotionBlurUntil = 0.0f; + static MotionBlurHistory_t history; + //float vPreviousSideVec[3] = { s_mPreviousFrameBasisVectors[0][1], s_mPreviousFrameBasisVectors[1][1], s_mPreviousFrameBasisVectors[2][1] }; //float vPreviousForwardVec[3] = { s_mPreviousFrameBasisVectors[0][0], s_mPreviousFrameBasisVectors[1][0], s_mPreviousFrameBasisVectors[2][0] }; //float vPreviousUpVec[3] = { s_mPreviousFrameBasisVectors[0][2], s_mPreviousFrameBasisVectors[1][2], s_mPreviousFrameBasisVectors[2][2] }; - float flTimeElapsed = gpGlobals->realtime - s_flLastTimeUpdate; + float flTimeElapsed = gpGlobals->realtime - history.m_flLastTimeUpdate; + //===================================// // Get current pitch & wrap to +-180 // //===================================// - float flCurrentPitch = view.angles[PITCH]; + float flCurrentPitch = viewSetup.angles[PITCH]; while ( flCurrentPitch > 180.0f ) flCurrentPitch -= 360.0f; while ( flCurrentPitch < -180.0f ) @@ -2751,35 +2975,39 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h //=================================// // Get current yaw & wrap to +-180 // //=================================// - float flCurrentYaw = view.angles[YAW]; + float flCurrentYaw = viewSetup.angles[YAW]; while ( flCurrentYaw > 180.0f ) flCurrentYaw -= 360.0f; while ( flCurrentYaw < -180.0f ) flCurrentYaw += 360.0f; - //engine->Con_NPrintf( 0, "Blur Pitch: %6.2f Yaw: %6.2f", flCurrentPitch, flCurrentYaw ); - //engine->Con_NPrintf( 1, "Blur FOV: %6.2f Aspect: %6.2f Ortho: %s", view.fov, view.m_flAspectRatio, view.m_bOrtho ? "Yes" : "No" ); + + + /*engine->Con_NPrintf( 0, "Blur Pitch: %6.2f Yaw: %6.2f", flCurrentPitch, flCurrentYaw ); + engine->Con_NPrintf( 1, "Blur FOV: %6.2f Aspect: %6.2f Ortho: %s", view.fov, view.m_flAspectRatio, view.m_bOrtho ? "Yes" : "No" ); + engine->Con_NPrintf( 2, "View Angles: %6.2f %6.2f %6.2f", XYZ(view.angles) );*/ //===========================// // Get current basis vectors // //===========================// matrix3x4_t mCurrentBasisVectors; - AngleMatrix( view.angles, mCurrentBasisVectors ); + AngleMatrix( viewSetup.angles, mCurrentBasisVectors ); - float vCurrentSideVec[3] = { mCurrentBasisVectors[0][1], mCurrentBasisVectors[1][1], mCurrentBasisVectors[2][1] }; - float vCurrentForwardVec[3] = { mCurrentBasisVectors[0][0], mCurrentBasisVectors[1][0], mCurrentBasisVectors[2][0] }; - //float vCurrentUpVec[3] = { mCurrentBasisVectors[0][2], mCurrentBasisVectors[1][2], mCurrentBasisVectors[2][2] }; + + Vector vCurrentSideVec( mCurrentBasisVectors[0][1], mCurrentBasisVectors[1][1], mCurrentBasisVectors[2][1] ); + Vector vCurrentForwardVec( mCurrentBasisVectors[0][0], mCurrentBasisVectors[1][0], mCurrentBasisVectors[2][0] ); + //Vector vCurrentUpVec( mCurrentBasisVectors[0][2], mCurrentBasisVectors[1][2], mCurrentBasisVectors[2][2] ); //======================// // Get current position // //======================// - float vCurrentPosition[3] = { view.origin.x, view.origin.y, view.origin.z }; + Vector vCurrentPosition = viewSetup.origin; //===============================================================// // Evaluate change in position to determine if we need to update // //===============================================================// - float vPositionChange[3] = { 0.0f, 0.0f, 0.0f }; - VectorSubtract( s_vPreviousPositon, vCurrentPosition, vPositionChange ); + Vector vPositionChange( 0.0f, 0.0f, 0.0f ); + VectorSubtract( history.m_vPreviousPositon, vCurrentPosition, vPositionChange ); if ( ( VectorLength( vPositionChange ) > 30.0f ) && ( flTimeElapsed >= 0.5f ) ) { //=======================================================// @@ -2793,7 +3021,7 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h g_vMotionBlurValues[2] = 0.0f; g_vMotionBlurValues[3] = 0.0f; } - else if ( flTimeElapsed > ( 1.0f / 15.0f ) ) + else if ( ( flTimeElapsed > ( 1.0f / 15.0f ) ) ) { //==========================================// // If slower than 15 fps, don't motion blur // @@ -2803,7 +3031,7 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h g_vMotionBlurValues[2] = 0.0f; g_vMotionBlurValues[3] = 0.0f; } - else if ( VectorLength( vPositionChange ) > 50.0f ) + else if ( ( VectorLength( vPositionChange ) > 50.0f ) ) { //================================================================================// // We moved a far distance in a frame, use the same motion blur as last frame // @@ -2811,7 +3039,7 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h //================================================================================// //engine->Con_NPrintf( 8, " Position changed %f units @ %.2f time ", VectorLength( vPositionChange ), gpGlobals->realtime ); - s_flNoRotationalMotionBlurUntil = gpGlobals->realtime + 1.0f; // Wait a second until the portal craziness calms down + history.m_flNoRotationalMotionBlurUntil = gpGlobals->realtime + 1.0f; // Wait a second until the portal craziness calms down } else { @@ -2819,8 +3047,8 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h // Normal update path // //====================// // Compute horizontal and vertical fov - float flHorizontalFov = view.fov; - float flVerticalFov = ( view.m_flAspectRatio <= 0.0f ) ? ( view.fov ) : ( view.fov / view.m_flAspectRatio ); + float flHorizontalFov = viewSetup.fov; + float flVerticalFov = ( viewSetup.m_flAspectRatio <= 0.0f ) ? ( viewSetup.fov ) : ( viewSetup.fov / viewSetup.m_flAspectRatio ); //engine->Con_NPrintf( 2, "Horizontal Fov: %6.2f Vertical Fov: %6.2f", flHorizontalFov, flVerticalFov ); //=====================// @@ -2836,10 +3064,10 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h // Yaw (Compensate for circle strafe) // //====================================// float flSideDotMotion = DotProduct( vCurrentSideVec, vPositionChange ); - float flYawDiffOriginal = s_flPreviousYaw - flCurrentYaw; - if ( ( ( s_flPreviousYaw - flCurrentYaw > 180.0f ) || ( s_flPreviousYaw - flCurrentYaw < -180.0f ) ) && - ( ( s_flPreviousYaw + flCurrentYaw > -180.0f ) && ( s_flPreviousYaw + flCurrentYaw < 180.0f ) ) ) - flYawDiffOriginal = s_flPreviousYaw + flCurrentYaw; + float flYawDiffOriginal = history.m_flPreviousYaw - flCurrentYaw; + if ( ( ( history.m_flPreviousYaw - flCurrentYaw > 180.0f ) || ( history.m_flPreviousYaw - flCurrentYaw < -180.0f ) ) && + ( ( history.m_flPreviousYaw + flCurrentYaw > -180.0f ) && ( history.m_flPreviousYaw + flCurrentYaw < 180.0f ) ) ) + flYawDiffOriginal = history.m_flPreviousYaw + flCurrentYaw; float flYawDiffAdjusted = flYawDiffOriginal + ( flSideDotMotion / 3.0f ); // Yes, 3.0 is a magic number, sue me @@ -2859,7 +3087,7 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h // Pitch (Compensate for forward motion) // //=======================================// float flPitchCompensateMask = 1.0f - ( ( 1.0f - fabs( vCurrentForwardVec[2] ) ) * ( 1.0f - fabs( vCurrentForwardVec[2] ) ) ); - float flPitchDiffOriginal = s_flPreviousPitch - flCurrentPitch; + float flPitchDiffOriginal = history.m_flPreviousPitch - flCurrentPitch; float flPitchDiffAdjusted = flPitchDiffOriginal; if ( flCurrentPitch > 0.0f ) @@ -2909,24 +3137,21 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h //===============================================================// // Dampen motion blur from 100%-0% as fps drops from 50fps-30fps // //===============================================================// - if ( !IsX360() ) // I'm not doing this on the 360 yet since I can't test it - { - float flSlowFps = 30.0f; - float flFastFps = 50.0f; - float flCurrentFps = ( flTimeElapsed > 0.0f ) ? ( 1.0f / flTimeElapsed ) : 0.0f; - float flDampenFactor = clamp( ( ( flCurrentFps - flSlowFps ) / ( flFastFps - flSlowFps ) ), 0.0f, 1.0f ); + float flSlowFps = 30.0f; + float flFastFps = 50.0f; + float flCurrentFps = ( flTimeElapsed > 0.0f ) ? ( 1.0f / flTimeElapsed ) : 0.0f; + float flDampenFactor = clamp( ( ( flCurrentFps - flSlowFps ) / ( flFastFps - flSlowFps ) ), 0.0f, 1.0f ); - //engine->Con_NPrintf( 4, "gpGlobals->realtime %.2f gpGlobals->curtime %.2f", gpGlobals->realtime, gpGlobals->curtime ); - //engine->Con_NPrintf( 5, "flCurrentFps %.2f", flCurrentFps ); - //engine->Con_NPrintf( 7, "flTimeElapsed %.2f", flTimeElapsed ); + //engine->Con_NPrintf( 4, "gpGlobals->realtime %.2f gpGlobals->curtime %.2f", gpGlobals->realtime, gpGlobals->curtime ); + //engine->Con_NPrintf( 5, "flCurrentFps %.2f", flCurrentFps ); + //engine->Con_NPrintf( 7, "flTimeElapsed %.2f", flTimeElapsed ); - g_vMotionBlurValues[0] *= flDampenFactor; - g_vMotionBlurValues[1] *= flDampenFactor; - g_vMotionBlurValues[2] *= flDampenFactor; - g_vMotionBlurValues[3] *= flDampenFactor; + g_vMotionBlurValues[0] *= flDampenFactor; + g_vMotionBlurValues[1] *= flDampenFactor; + g_vMotionBlurValues[2] *= flDampenFactor; + g_vMotionBlurValues[3] *= flDampenFactor; - //engine->Con_NPrintf( 6, "Dampen: %.2f", flDampenFactor ); - } + //engine->Con_NPrintf( 6, "Dampen: %.2f", flDampenFactor ); //engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f ); } @@ -2934,7 +3159,7 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h //============================================// // Zero out blur if still in that time window // //============================================// - if ( gpGlobals->realtime < s_flNoRotationalMotionBlurUntil ) + if ( gpGlobals->realtime < history.m_flNoRotationalMotionBlurUntil ) { //engine->Con_NPrintf( 9, " No Rotation @ %f ", gpGlobals->realtime ); @@ -2945,17 +3170,60 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h } else { - s_flNoRotationalMotionBlurUntil = 0.0f; + history.m_flNoRotationalMotionBlurUntil = 0.0f; } //====================================// // Store current frame for next frame // //====================================// - VectorCopy( vCurrentPosition, s_vPreviousPositon ); - s_mPreviousFrameBasisVectors = mCurrentBasisVectors; - s_flPreviousPitch = flCurrentPitch; - s_flPreviousYaw = flCurrentYaw; - s_flLastTimeUpdate = gpGlobals->realtime; + VectorCopy( vCurrentPosition, history.m_vPreviousPositon ); + history.m_mPreviousFrameBasisVectors = mCurrentBasisVectors; + history.m_flPreviousPitch = flCurrentPitch; + history.m_flPreviousYaw = flCurrentYaw; + history.m_flLastTimeUpdate = gpGlobals->realtime; + } + + //engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f ); + + //==========================================// + // Set global g_vMotionBlurViewportValues[] // + //==========================================// + if ( true ) + { + ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); + float flSrcWidth = ( float )pSrc->GetActualWidth(); + float flSrcHeight = ( float )pSrc->GetActualHeight(); + + // NOTE #1: float4 stored as ( minx, miny, maxy, maxx )...z&w have been swapped to save pixel shader instructions + // NOTE #2: This code should definitely work for 2 players (horizontal or vertical), or 4 players (4 corners), but + // it might have to be modified if we ever want to support other split screen configurations + + int nOffset; // Offset by one pixel to land in the correct half + + // Left + nOffset = ( x > 0 ) ? 1 : 0; + g_vMotionBlurViewportValues[0] = ( float )( x + nOffset ) / ( flSrcWidth - 1 ); + + // Right + nOffset = ( x < ( flSrcWidth - 1 ) ) ? -1 : 0; + g_vMotionBlurViewportValues[3] = ( float )( x + w + nOffset ) / ( flSrcWidth - 1 ); + + // Top + nOffset = ( y > 0 ) ? 1 : 0; // Offset by one pixel to land in the correct half + g_vMotionBlurViewportValues[1] = ( float )( y + nOffset ) / ( flSrcHeight - 1 ); + + // Bottom + nOffset = ( y < ( flSrcHeight - 1 ) ) ? -1 : 0; + g_vMotionBlurViewportValues[2] = ( float )( y + h + nOffset ) / ( flSrcHeight - 1 ); + + // Only allow clamping to happen in the middle of the screen, so nudge the clamp values out if they're on the border of the screen + for ( int i = 0; i < 4; i++ ) + { + if ( g_vMotionBlurViewportValues[i] <= 0.0f ) + g_vMotionBlurViewportValues[i] = -1.0f; + else if ( g_vMotionBlurViewportValues[i] >= 1.0f ) + g_vMotionBlurViewportValues[i] = 2.0f; + } } //=============================================================================================// @@ -2968,13 +3236,10 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); int nSrcWidth = pSrc->GetActualWidth(); int nSrcHeight = pSrc->GetActualHeight(); - int dest_width, dest_height, nDummy; - pRenderContext->GetViewport( nDummy, nDummy, dest_width, dest_height ); + int nViewportWidth, nViewportHeight, nDummy; + pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight ); - if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_FLOAT ) - { - UpdateScreenEffectTexture( 0, x, y, w, h, true ); // Do we need to check if we already did this? - } + UpdateScreenEffectTexture( 0, x, y, w, h, false ); // Get material pointer IMaterial *pMatMotionBlur = materials->FindMaterial( "dev/motion_blur", TEXTURE_GROUP_OTHER, true ); @@ -2986,14 +3251,200 @@ void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h { pRenderContext->DrawScreenSpaceRectangle( pMatMotionBlur, - 0, 0, dest_width, dest_height, - 0, 0, nSrcWidth-1, nSrcHeight-1, + 0, 0, nViewportWidth, nViewportHeight, + x, y, x + w-1, y + h-1, nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() ); - - if ( g_bDumpRenderTargets ) - { - DumpTGAofRenderTarget( dest_width, dest_height, "MotionBlur" ); - } } } } + +//===================================================================================================================== +// Depth of field ===================================================================================================== +//===================================================================================================================== +ConVar mat_dof_enabled( "mat_dof_enabled", "1" ); +ConVar mat_dof_override( "mat_dof_override", "0" ); +ConVar mat_dof_near_blur_depth( "mat_dof_near_blur_depth", "20.0" ); +ConVar mat_dof_near_focus_depth( "mat_dof_near_focus_depth", "100.0" ); +ConVar mat_dof_far_focus_depth( "mat_dof_far_focus_depth", "250.0" ); +ConVar mat_dof_far_blur_depth( "mat_dof_far_blur_depth", "1000.0" ); +ConVar mat_dof_near_blur_radius( "mat_dof_near_blur_radius", "0.0" ); +ConVar mat_dof_far_blur_radius( "mat_dof_far_blur_radius", "10.0" ); +ConVar mat_dof_quality( "mat_dof_quality", "3" ); + +static float GetNearBlurDepth() +{ + return mat_dof_override.GetBool() ? mat_dof_near_blur_depth.GetFloat() : g_flDOFNearBlurDepth; +} + +static float GetNearFocusDepth() +{ + return mat_dof_override.GetBool() ? mat_dof_near_focus_depth.GetFloat() : g_flDOFNearFocusDepth; +} + +static float GetFarFocusDepth() +{ + return mat_dof_override.GetBool() ? mat_dof_far_focus_depth.GetFloat() : g_flDOFFarFocusDepth; +} + +static float GetFarBlurDepth() +{ + return mat_dof_override.GetBool() ? mat_dof_far_blur_depth.GetFloat() : g_flDOFFarBlurDepth; +} + +static float GetNearBlurRadius() +{ + return mat_dof_override.GetBool() ? mat_dof_near_blur_radius.GetFloat() : g_flDOFNearBlurRadius; +} + +static float GetFarBlurRadius() +{ + return mat_dof_override.GetBool() ? mat_dof_far_blur_radius.GetFloat() : g_flDOFFarBlurRadius; +} + +bool IsDepthOfFieldEnabled() +{ + const CViewSetup *pViewSetup = view->GetViewSetup(); + if ( !pViewSetup ) + return false; + + if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 92 ) + return false; + + if ( !mat_dof_enabled.GetBool() ) + return false; + + if ( mat_dof_override.GetBool() == true ) + { + return mat_dof_enabled.GetBool(); + } + else + { + return g_bDOFEnabled; + } +} + +static inline bool SetMaterialVarFloat( IMaterial* pMat, const char* pVarName, float flValue ) +{ + Assert( pMat != NULL ); + Assert( pVarName != NULL ); + if ( pMat == NULL || pVarName == NULL ) + { + return false; + } + + bool bFound = false; + IMaterialVar* pVar = pMat->FindVar( pVarName, &bFound ); + if ( bFound ) + { + pVar->SetFloatValue( flValue ); + } + + return bFound; +} + +static inline bool SetMaterialVarInt( IMaterial* pMat, const char* pVarName, int nValue ) +{ + Assert( pMat != NULL ); + Assert( pVarName != NULL ); + if ( pMat == NULL || pVarName == NULL ) + { + return false; + } + + bool bFound = false; + IMaterialVar* pVar = pMat->FindVar( pVarName, &bFound ); + if ( bFound ) + { + pVar->SetIntValue( nValue ); + } + + return bFound; +} + +void DoDepthOfField( const CViewSetup &viewSetup ) +{ + if ( !IsDepthOfFieldEnabled() ) + { + return; + } + + // Copy from backbuffer to _rt_FullFrameFB + UpdateScreenEffectTexture( 0, viewSetup.x, viewSetup.y, viewSetup.width, viewSetup.height, false ); // Do we need to check if we already did this? + + CMatRenderContextPtr pRenderContext( materials ); + + ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); + int nSrcWidth = pSrc->GetActualWidth(); + int nSrcHeight = pSrc->GetActualHeight(); + + int nViewportWidth = 0; + int nViewportHeight = 0; + int nDummy = 0; + pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight ); + + if ( mat_dof_quality.GetInt() < 2 ) + { + ///////////////////////////////////// + // Downsample backbuffer to 1/4 size + ///////////////////////////////////// + + // Update downsampled framebuffer. TODO: Don't do this again for the bloom if we already did it here... + pRenderContext->PushRenderTargetAndViewport(); + ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET ); + + // *Everything* in here relies on the small RTs being exactly 1/4 the full FB res + Assert( dest_rt0->GetActualWidth() == pSrc->GetActualWidth() / 4 ); + Assert( dest_rt0->GetActualHeight() == pSrc->GetActualHeight() / 4 ); + + // Downsample fb to rt0 + DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0, true ); + + ////////////////////////////////////// + // Additional blur using 3x3 gaussian + ////////////////////////////////////// + + IMaterial *pMat = materials->FindMaterial( "dev/blurgaussian_3x3", TEXTURE_GROUP_OTHER, true ); + + if ( pMat == NULL ) + return; + + SetMaterialVarFloat( pMat, "$c0_x", 0.5f / (float)dest_rt0->GetActualWidth() ); + SetMaterialVarFloat( pMat, "$c0_y", 0.5f / (float)dest_rt0->GetActualHeight() ); + SetMaterialVarFloat( pMat, "$c1_x", -0.5f / (float)dest_rt0->GetActualWidth() ); + SetMaterialVarFloat( pMat, "$c1_y", 0.5f / (float)dest_rt0->GetActualHeight() ); + + ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); + SetRenderTargetAndViewPort( dest_rt1 ); + + pRenderContext->DrawScreenSpaceRectangle( + pMat, 0, 0, nSrcWidth/4, nSrcHeight/4, + 0, 0, dest_rt0->GetActualWidth()-1, dest_rt0->GetActualHeight()-1, + dest_rt0->GetActualWidth(), dest_rt0->GetActualHeight() ); + + pRenderContext->PopRenderTargetAndViewport(); + } + + // Render depth-of-field quad + IMaterial *pMatDOF = materials->FindMaterial( "dev/depth_of_field", TEXTURE_GROUP_OTHER, true ); + + if ( pMatDOF == NULL ) + return; + + SetMaterialVarFloat( pMatDOF, "$nearPlane", viewSetup.zNear ); + SetMaterialVarFloat( pMatDOF, "$farPlane", viewSetup.zFar ); + + // pull from convars/globals + SetMaterialVarFloat( pMatDOF, "$nearBlurDepth", GetNearBlurDepth() ); + SetMaterialVarFloat( pMatDOF, "$nearFocusDepth", GetNearFocusDepth() ); + SetMaterialVarFloat( pMatDOF, "$farFocusDepth", GetFarFocusDepth() ); + SetMaterialVarFloat( pMatDOF, "$farBlurDepth", GetFarBlurDepth() ); + SetMaterialVarFloat( pMatDOF, "$nearBlurRadius", GetNearBlurRadius() ); + SetMaterialVarFloat( pMatDOF, "$farBlurRadius", GetFarBlurRadius() ); + SetMaterialVarInt( pMatDOF, "$quality", mat_dof_quality.GetInt() ); + + pRenderContext->DrawScreenSpaceRectangle( + pMatDOF, + 0, 0, nViewportWidth, nViewportHeight, + 0, 0, nSrcWidth-1, nSrcHeight-1, + nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() ); +} diff --git a/mp/src/game/client/viewpostprocess.h b/mp/src/game/client/viewpostprocess.h index eb2ad9cc..f40a6937 100644 --- a/mp/src/game/client/viewpostprocess.h +++ b/mp/src/game/client/viewpostprocess.h @@ -11,8 +11,20 @@ #pragma once #endif +struct PostProcessParameters_t; + void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, bool bPostVGui = false ); -void DoImageSpaceMotionBlur( const CViewSetup &view, int x, int y, int w, int h ); +void DoImageSpaceMotionBlur( const CViewSetup &viewSetup ); void DumpTGAofRenderTarget( const int width, const int height, const char *pFilename ); +void SetRenderTargetAndViewPort( ITexture *rt ); + +bool IsDepthOfFieldEnabled(); +void DoDepthOfField( const CViewSetup &view ); + +void SetPostProcessParams( const PostProcessParameters_t* pPostProcessParameters ); +void SetPostProcessParams( const PostProcessParameters_t* pPostProcessParameters, bool override ); + +void SetViewFadeParams( byte r, byte g, byte b, byte a, bool bModulate ); + #endif // VIEWPOSTPROCESS_H diff --git a/mp/src/game/client/viewrender.cpp b/mp/src/game/client/viewrender.cpp index 67a8307c..e512a07a 100644 --- a/mp/src/game/client/viewrender.cpp +++ b/mp/src/game/client/viewrender.cpp @@ -74,6 +74,10 @@ #include "c_point_camera.h" #endif // USE_MONITORS +#ifdef MAPBASE +#include "mapbase/c_func_fake_worldportal.h" +#endif + // Projective textures #include "C_Env_Projected_Texture.h" @@ -119,6 +123,10 @@ static ConVar r_drawtranslucentrenderables( "r_drawtranslucentrenderables", "1", static ConVar r_drawopaquerenderables( "r_drawopaquerenderables", "1", FCVAR_CHEAT ); static ConVar r_threaded_renderables( "r_threaded_renderables", "0" ); +#ifdef MAPBASE +static ConVar r_skybox_use_complex_views( "r_skybox_use_complex_views", "0", FCVAR_CHEAT, "Enable complex views in skyboxes, like reflective glass" ); +#endif + // FIXME: This is not static because we needed to turn it off for TF2 playtests ConVar r_DrawDetailProps( "r_DrawDetailProps", "1", FCVAR_NONE, "0=Off, 1=Normal, 2=Wireframe" ); @@ -172,6 +180,10 @@ extern ConVar cl_leveloverview; extern ConVar localplayer_visionflags; +#ifdef MAPBASE +static ConVar r_nearz_skybox( "r_nearz_skybox", "2.0", FCVAR_CHEAT ); +#endif + //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- @@ -396,6 +408,10 @@ protected: void Enable3dSkyboxFog( void ); void DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostRender, ITexture *pRenderTarget, ITexture *pDepthTarget ); +#ifdef MAPBASE + void CalculateSkyAngles( const QAngle &angAngles ); +#endif + sky3dparams_t * PreRender3dSkyboxWorld( SkyboxVisibility_t nSkyboxVisible ); sky3dparams_t *m_pSky3dParams; @@ -480,6 +496,11 @@ protected: void SSAO_DepthPass(); void DrawDepthOfField(); + +#ifdef MAPBASE + virtual ITexture *GetRefractionTexture() { return GetWaterRefractionTexture(); } + virtual ITexture *GetReflectionTexture() { return GetWaterReflectionTexture(); } +#endif }; @@ -666,6 +687,11 @@ public: void Draw(); cplane_t m_ReflectionPlane; + +#ifdef MAPBASE + ITexture *GetReflectionTexture() { return m_pRenderTarget; } + ITexture *m_pRenderTarget; +#endif }; class CRefractiveGlassView : public CSimpleWorldView @@ -683,6 +709,11 @@ public: void Draw(); cplane_t m_ReflectionPlane; + +#ifdef MAPBASE + ITexture *GetRefractionTexture() { return m_pRenderTarget; } + ITexture *m_pRenderTarget; +#endif }; @@ -792,6 +823,8 @@ CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffects ) CLIENTEFFECT_MATERIAL( "dev/copyfullframefb_vanilla" ) CLIENTEFFECT_MATERIAL( "dev/copyfullframefb" ) CLIENTEFFECT_MATERIAL( "dev/engine_post" ) + CLIENTEFFECT_MATERIAL( "dev/depth_of_field" ) + CLIENTEFFECT_MATERIAL( "dev/blurgaussian_3x3" ) CLIENTEFFECT_MATERIAL( "dev/motion_blur" ) CLIENTEFFECT_MATERIAL( "dev/upscale" ) @@ -1327,6 +1360,9 @@ void CViewRender::ViewDrawScene( bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxV if ( r_flashlightdepthtexture.GetBool() && (viewID == VIEW_MAIN) ) { g_pClientShadowMgr->ComputeShadowDepthTextures( view ); +#ifdef ASW_PROJECTED_TEXTURES + CMatRenderContextPtr pRenderContext( materials ); +#endif } m_BaseDrawFlags = baseDrawFlags; @@ -1962,6 +1998,27 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT { CViewSetup viewMiddle = GetView( STEREO_EYE_MONO ); DrawMonitors( viewMiddle ); + +#ifdef MAPBASE + // Any fake world portals? + Frustum_t frustum; + GeneratePerspectiveFrustum( view.origin, view.angles, view.zNear, view.zFar, view.fov, view.m_flAspectRatio, frustum ); + + cplane_t portalPlane; + //C_FuncFakeWorldPortal *pPortalEnt = IsFakeWorldPortalInView( view, portalPlane ); + //if ( pPortalEnt ) + C_FuncFakeWorldPortal *pPortalEnt = NextFakeWorldPortal( NULL, view, portalPlane, frustum ); + while ( pPortalEnt != NULL ) + { + ITexture *pCameraTarget = pPortalEnt->RenderTarget(); + int width = pCameraTarget->GetActualWidth(); + int height = pCameraTarget->GetActualHeight(); + + DrawFakeWorldPortal( pCameraTarget, pPortalEnt, viewMiddle, C_BasePlayer::GetLocalPlayer(), 0, 0, width, height, view, portalPlane ); + + pPortalEnt = NextFakeWorldPortal( pPortalEnt, view, portalPlane, frustum ); + } +#endif } #endif @@ -1980,6 +2037,7 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT bool bDrew3dSkybox = false; SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE; +#ifndef MAPBASE // Moved to respective ViewDrawScenes() for script_intro skybox fix // if the 3d skybox world is drawn, then don't draw the normal skybox CSkyboxView *pSkyView = new CSkyboxView( this ); if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible ) ) != false ) @@ -1987,6 +2045,7 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT AddViewToScene( pSkyView ); } SafeRelease( pSkyView ); +#endif // Force it to clear the framebuffer if they're in solid space. if ( ( nClearFlags & VIEW_CLEAR_COLOR ) == 0 ) @@ -1997,14 +2056,37 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT } } +#ifdef MAPBASE + // For script_intro viewmodel fix + bool bDrawnViewmodel = false; +#endif + // Render world and all entities, particles, etc. if( !g_pIntroData ) { +#ifdef MAPBASE + // Moved here for the script_intro skybox fix. + // We can't put it in ViewDrawScene() directly because other functions use it as well. + + // if the 3d skybox world is drawn, then don't draw the normal skybox + CSkyboxView *pSkyView = new CSkyboxView( this ); + if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible ) ) != false ) + { + AddViewToScene( pSkyView ); + } + SafeRelease( pSkyView ); +#endif + ViewDrawScene( bDrew3dSkybox, nSkyboxVisible, view, nClearFlags, VIEW_MAIN, whatToDraw & RENDERVIEW_DRAWVIEWMODEL ); } else { +#ifdef MAPBASE + ViewDrawScene_Intro( view, nClearFlags, *g_pIntroData, bDrew3dSkybox, nSkyboxVisible, whatToDraw & RENDERVIEW_DRAWVIEWMODEL ); + bDrawnViewmodel = true; +#else ViewDrawScene_Intro( view, nClearFlags, *g_pIntroData ); +#endif } // We can still use the 'current view' stuff set up in ViewDrawScene @@ -2024,14 +2106,24 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT RenderPlayerSprites(); // Image-space motion blur - if ( !building_cubemaps.GetBool() && view.m_bDoBloomAndToneMapping ) // We probably should use a different view. variable here + if ( !building_cubemaps.GetBool() /*&& view.m_bDoBloomAndToneMapping*/ ) // We probably should use a different view. variable here { + if ( IsDepthOfFieldEnabled() ) + { + pRenderContext.GetFrom( materials ); + { + PIXEVENT( pRenderContext, "DoDepthOfField()" ); + DoDepthOfField( view ); + } + pRenderContext.SafeRelease(); + } + if ( ( mat_motion_blur_enabled.GetInt() ) && ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 ) ) { pRenderContext.GetFrom( materials ); { PIXEVENT( pRenderContext, "DoImageSpaceMotionBlur" ); - DoImageSpaceMotionBlur( view, view.x, view.y, view.width, view.height ); + DoImageSpaceMotionBlur( view ); } pRenderContext.SafeRelease(); } @@ -2040,6 +2132,9 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT GetClientModeNormal()->DoPostScreenSpaceEffects( &view ); // Now actually draw the viewmodel +#ifdef MAPBASE + if (!bDrawnViewmodel) +#endif DrawViewModels( view, whatToDraw & RENDERVIEW_DRAWVIEWMODEL ); DrawUnderwaterOverlay(); @@ -2558,6 +2653,34 @@ void CViewRender::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &view { tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "bCheapWater" ); cplane_t glassReflectionPlane; +#ifdef MAPBASE + // New expansions allow for custom render targets and multiple mirror renders + Frustum_t frustum; + GeneratePerspectiveFrustum( viewIn.origin, viewIn.angles, viewIn.zNear, viewIn.zFar, viewIn.fov, viewIn.m_flAspectRatio, frustum ); + + ITexture *pTextureTargets[2]; + C_BaseEntity *pReflectiveGlass = NextReflectiveGlass( NULL, viewIn, glassReflectionPlane, frustum, pTextureTargets ); + while ( pReflectiveGlass != NULL ) + { + if (pTextureTargets[0]) + { + CRefPtr pGlassReflectionView = new CReflectiveGlassView( this ); + pGlassReflectionView->m_pRenderTarget = pTextureTargets[0]; + pGlassReflectionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); + AddViewToScene( pGlassReflectionView ); + } + + if (pTextureTargets[1]) + { + CRefPtr pGlassRefractionView = new CRefractiveGlassView( this ); + pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); + pGlassRefractionView->m_pRenderTarget = pTextureTargets[1]; + AddViewToScene( pGlassRefractionView ); + } + + pReflectiveGlass = NextReflectiveGlass( pReflectiveGlass, viewIn, glassReflectionPlane, frustum, pTextureTargets ); + } +#else if ( IsReflectiveGlassInView( viewIn, glassReflectionPlane ) ) { CRefPtr pGlassReflectionView = new CReflectiveGlassView( this ); @@ -2568,6 +2691,7 @@ void CViewRender::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &view pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); AddViewToScene( pGlassRefractionView ); } +#endif CRefPtr pNoWaterView = new CSimpleWorldView( this ); pNoWaterView->Setup( viewIn, nClearFlags, bDrawSkybox, fogVolumeInfo, info, pCustomVisibility ); @@ -2858,7 +2982,12 @@ void CViewRender::GetWaterLODParams( float &flCheapWaterStartDistance, float &fl // Input : &view - // &introData - //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData, + bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, bool bDrawViewModel, ViewCustomVisibility_t *pCustomVisibility ) +#else void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData ) +#endif { VPROF( "CViewRender::ViewDrawScene" ); @@ -2888,11 +3017,43 @@ void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, CViewSetup playerView( view ); playerView.origin = introData.m_vecCameraView; playerView.angles = introData.m_vecCameraViewAngles; +#ifdef MAPBASE + // Ortho handling (change this code if we ever use m_hCameraEntity for other things) + if (introData.m_hCameraEntity /*&& introData.m_hCameraEntity->IsOrtho()*/) + { + playerView.m_bOrtho = true; + introData.m_hCameraEntity->GetOrthoDimensions( playerView.m_OrthoTop, playerView.m_OrthoBottom, + playerView.m_OrthoLeft, playerView.m_OrthoRight ); + } +#endif if ( introData.m_playerViewFOV ) { playerView.fov = ScaleFOVByWidthRatio( introData.m_playerViewFOV, engine->GetScreenAspectRatio() / ( 4.0f / 3.0f ) ); } +#ifdef MAPBASE + bool drawSkybox; + int nViewFlags; + if (introData.m_bDrawSky2) + { + drawSkybox = r_skybox.GetBool(); + nViewFlags = VIEW_CLEAR_DEPTH; + + // if the 3d skybox world is drawn, then don't draw the normal skybox + CSkyboxView *pSkyView = new CSkyboxView( this ); + if ( ( bDrew3dSkybox = pSkyView->Setup( playerView, &nClearFlags, &nSkyboxVisible ) ) != false ) + { + AddViewToScene( pSkyView ); + } + SafeRelease( pSkyView ); + } + else + { + drawSkybox = false; + nViewFlags = (VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH); + } +#endif + g_pClientShadowMgr->PreRender(); // Shadowed flashlights supported on ps_2_b and up... @@ -2907,10 +3068,38 @@ void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, IGameSystem::PreRenderAllSystems(); // Start view, clear frame/z buffer if necessary +#ifdef MAPBASE + SetupVis( playerView, visFlags, pCustomVisibility ); +#else SetupVis( playerView, visFlags ); +#endif + +#ifdef MAPBASE + if (introData.m_bDrawSky2) + { + if ( !bDrew3dSkybox && + ( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) /*&& ( visFlags & IVRenderView::VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS )*/ ) + { + // This covers the case where we don't see a 3dskybox, yet radial vis is clipping + // the far plane. Need to clear to fog color in this case. + nClearFlags |= VIEW_CLEAR_COLOR; + //SetClearColorToFogColor( ); + } + if ( bDrew3dSkybox || ( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) ) + { + drawSkybox = false; + } + } +#endif + +#ifdef MAPBASE + render->Push3DView( playerView, nViewFlags, NULL, GetFrustum() ); + DrawWorldAndEntities( drawSkybox, playerView, nViewFlags ); +#else render->Push3DView( playerView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH, NULL, GetFrustum() ); DrawWorldAndEntities( true /* drawSkybox */, playerView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH ); +#endif render->PopView( GetFrustum() ); // Free shadow depth textures for use in future view @@ -2926,12 +3115,28 @@ void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, Rect_t actualRect; UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height, false, &actualRect ); +#ifdef MAPBASE + if (introData.m_bDrawSky) + { + // if the 3d skybox world is drawn, then don't draw the normal skybox + CSkyboxView *pSkyView = new CSkyboxView( this ); + if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible ) ) != false ) + { + AddViewToScene( pSkyView ); + } + SafeRelease( pSkyView ); + } +#endif + g_pClientShadowMgr->PreRender(); // Shadowed flashlights supported on ps_2_b and up... if ( r_flashlightdepthtexture.GetBool() ) { g_pClientShadowMgr->ComputeShadowDepthTextures( view ); +#ifdef ASW_PROJECTED_TEXTURES + CMatRenderContextPtr pRenderContext( materials ); +#endif } // ----------------------------------------------------------------------- @@ -2948,7 +3153,41 @@ void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, // Clear alpha to 255 so that masking with the vortigaunts (0) works properly. pRenderContext->ClearColor4ub( 0, 0, 0, 255 ); +#ifdef MAPBASE + bool drawSkybox; + int nViewFlags; + if (introData.m_bDrawSky) + { + drawSkybox = r_skybox.GetBool(); + nViewFlags = VIEW_CLEAR_DEPTH; + + if ( !bDrew3dSkybox && + ( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) /*&& ( visFlags & IVRenderView::VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS )*/ ) + { + // This covers the case where we don't see a 3dskybox, yet radial vis is clipping + // the far plane. Need to clear to fog color in this case. + nViewFlags |= VIEW_CLEAR_COLOR; + //SetClearColorToFogColor( ); + } + + if ( bDrew3dSkybox || ( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) ) + { + drawSkybox = false; + } + } + else + { + drawSkybox = false; + nViewFlags = (VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH); + } + + DrawWorldAndEntities( drawSkybox, view, nViewFlags ); + + // Solution for viewmodels not drawing in script_intro + DrawViewModels( view, bDrawViewModel ); +#else DrawWorldAndEntities( true /* drawSkybox */, view, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH ); +#endif UpdateScreenEffectTexture( 1, view.x, view.y, view.width, view.height ); @@ -3027,6 +3266,11 @@ void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, // Let the particle manager simulate things that haven't been simulated. ParticleMgr()->PostRender(); +#ifdef MAPBASE + // Invoke post-render methods + IGameSystem::PostRenderAllSystems(); +#endif + FinishCurrentView(); // Free shadow depth textures for use in future view @@ -3096,15 +3340,273 @@ bool CViewRender::DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_Poin monitorView.origin = pCameraEnt->GetAbsOrigin(); monitorView.angles = pCameraEnt->GetAbsAngles(); monitorView.fov = pCameraEnt->GetFOV(); +#ifdef MAPBASE + if (pCameraEnt->IsOrtho()) + { + monitorView.m_bOrtho = true; + pCameraEnt->GetOrthoDimensions( monitorView.m_OrthoTop, monitorView.m_OrthoBottom, + monitorView.m_OrthoLeft, monitorView.m_OrthoRight ); + } + else + { + monitorView.m_bOrtho = false; + } +#else monitorView.m_bOrtho = false; +#endif monitorView.m_flAspectRatio = pCameraEnt->UseScreenAspectRatio() ? 0.0f : 1.0f; monitorView.m_bViewToProjectionOverride = false; +#ifdef MAPBASE + // + // Monitor sky handling + // + SkyboxVisibility_t nSkyMode = pCameraEnt->SkyMode(); + if ( nSkyMode == SKYBOX_3DSKYBOX_VISIBLE ) + { + int nClearFlags = (VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR); + bool bDrew3dSkybox = false; + + Frustum frustum; + render->Push3DView( monitorView, nClearFlags, pRenderTarget, (VPlane *)frustum ); + + // if the 3d skybox world is drawn, then don't draw the normal skybox + CSkyboxView *pSkyView = new CSkyboxView( this ); + if ( ( bDrew3dSkybox = pSkyView->Setup( monitorView, &nClearFlags, &nSkyMode ) ) != false ) + { + AddViewToScene( pSkyView ); + } + SafeRelease( pSkyView ); + + ViewDrawScene( bDrew3dSkybox, nSkyMode, monitorView, nClearFlags, VIEW_MONITOR ); + render->PopView( frustum ); + } + else if (nSkyMode == SKYBOX_NOT_VISIBLE) + { + // @MULTICORE (toml 8/11/2006): this should be a renderer.... + Frustum frustum; + render->Push3DView( monitorView, VIEW_CLEAR_DEPTH, pRenderTarget, (VPlane *)frustum ); + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->PushRenderTargetAndViewport( pRenderTarget ); + pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, 1 ); + if ( pRenderTarget ) + { + pRenderContext->OverrideAlphaWriteEnable( true, true ); + } + + ViewDrawScene( false, nSkyMode, monitorView, 0, VIEW_MONITOR ); + + pRenderContext->PopRenderTargetAndViewport(); + render->PopView( frustum ); + } + else + { + // @MULTICORE (toml 8/11/2006): this should be a renderer.... + Frustum frustum; + render->Push3DView( monitorView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pRenderTarget, (VPlane *)frustum ); + ViewDrawScene( false, nSkyMode, monitorView, 0, VIEW_MONITOR ); + render->PopView( frustum ); + } +#else // @MULTICORE (toml 8/11/2006): this should be a renderer.... Frustum frustum; render->Push3DView( monitorView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pRenderTarget, (VPlane *)frustum ); ViewDrawScene( false, SKYBOX_2DSKYBOX_VISIBLE, monitorView, 0, VIEW_MONITOR ); render->PopView( frustum ); +#endif + + // Reset the world fog parameters. + if ( fogEnabled ) + { + if ( pFogParams ) + { + *pFogParams = oldFogParams; + } + monitorView.zFar = flOldZFar; + } +#endif // USE_MONITORS + return true; +} + +#ifdef MAPBASE +ConVar r_fakeworldportal_debug("r_fakeworldportal_debug", "0"); + +//----------------------------------------------------------------------------- +// Purpose: Sets up scene and renders WIP fake world portal view. +// Based on code from monitors, mirrors, and 3D skyboxes. +// It's also terrible right now. +// +// Input : cameraNum - +// &cameraView +// *localPlayer - +// x - +// y - +// width - +// height - +// highend - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldPortal *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, + int x, int y, int width, int height, + const CViewSetup &mainView, cplane_t &ourPlane ) +{ +#ifdef USE_MONITORS + VPROF_INCREMENT_COUNTER( "cameras rendered", 1 ); + // Setup fog state for the camera. + fogparams_t oldFogParams; + float flOldZFar = 0.0f; + + // If fog should be disabled instead of using the player's controller, a blank fog controller can just be used + bool fogEnabled = true; //pCameraEnt->IsFogEnabled(); + + CViewSetup monitorView = cameraView; + + fogparams_t *pFogParams = NULL; + + if ( fogEnabled ) + { + if ( !localPlayer ) + return false; + + pFogParams = localPlayer->GetFogParams(); + + // Save old fog data. + oldFogParams = *pFogParams; + + if ( pCameraEnt->GetFog() ) + { + *pFogParams = *pCameraEnt->GetFog(); + } + } + + monitorView.width = width; + monitorView.height = height; + monitorView.x = x; + monitorView.y = y; + + monitorView.origin = mainView.origin; + monitorView.angles = mainView.angles; + + // Temporary debug stuff + static float flLastDebugTime = 0.0f; + bool bDebug = r_fakeworldportal_debug.GetBool() && gpGlobals->curtime > flLastDebugTime; + + QAngle angTargetAngles = pCameraEnt->m_hTargetPlane->GetAbsAngles() + pCameraEnt->m_PlaneAngles; + + // RED - First origin + if (bDebug) + debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 0, 0, 128, 10.0f ); + + // Make sure the origin and angles are relative to the target plane + monitorView.origin -= pCameraEnt->GetAbsOrigin(); + + // scale origin by sky scale + if ( pCameraEnt->m_flScale > 0 ) + { + float scale = 1.0f / pCameraEnt->m_flScale; + VectorScale( monitorView.origin, scale, monitorView.origin ); + } + + // YELLOW - Main origin + if (bDebug) + debugoverlay->AddBoxOverlay( pCameraEnt->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 224, 0, 128, 10.0f ); + + // Make sure our angles are relative to the main plane, just like the origin + QAngle angOurAngles; + VectorAngles( ourPlane.normal * -1, angOurAngles ); + //angles -= angOurAngles; + + // First, create a matrix for the sky's angles. + matrix3x4_t matSkyAngles; + AngleMatrix( angTargetAngles - angOurAngles, matSkyAngles ); + + Vector vecSkyForward, vecSkyRight, vecSkyUp; + + // Get vectors from our original angles. + Vector vPlayerForward, vPlayerRight, vPlayerUp; + AngleVectors( monitorView.angles, &vPlayerForward, &vPlayerRight, &vPlayerUp ); + + VectorTransform( vPlayerForward, matSkyAngles, vecSkyForward ); + VectorTransform( vPlayerRight, matSkyAngles, vecSkyRight ); + VectorTransform( vPlayerUp, matSkyAngles, vecSkyUp ); + + // Normalize them. + VectorNormalize( vecSkyForward ); + VectorNormalize( vecSkyRight ); + VectorNormalize( vecSkyUp ); + + Quaternion quat; + BasisToQuaternion( vecSkyForward, vecSkyRight, vecSkyUp, quat ); + QuaternionAngles( quat, monitorView.angles ); + + // End of code mostly lifted from projected texture screenspace stuff + // ---------------------------------------------------------------------- + + // Now just rotate our origin with that matrix. + // We create a copy of the origin since VectorRotate doesn't want in1 to be the same variable as the destination. + VectorRotate(Vector(monitorView.origin), matSkyAngles, monitorView.origin); + + // BLUE - Target origin + if (bDebug) + debugoverlay->AddBoxOverlay( pCameraEnt->m_hTargetPlane->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 0, 0, 255, 128, 10.0f ); + + monitorView.origin += pCameraEnt->m_hTargetPlane->GetAbsOrigin(); + + // GREEN - Final origin + if (bDebug) + { + debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 0, 255, 0, 128, 10.0f ); + + flLastDebugTime = gpGlobals->curtime + 5.0f; + } + + + monitorView.fov = mainView.fov; + monitorView.m_bOrtho = false; + monitorView.m_flAspectRatio = 0.0f; + monitorView.m_bViewToProjectionOverride = false; + + // @MULTICORE (toml 8/11/2006): this should be a renderer.... + int nClearFlags = (VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_OBEY_STENCIL); + bool bDrew3dSkybox = false; + SkyboxVisibility_t nSkyMode = pCameraEnt->SkyMode(); + + Frustum frustum; + render->Push3DView( monitorView, nClearFlags, pRenderTarget, (VPlane *)frustum ); + + // + // Monitor sky handling + // + if ( pCameraEnt->SkyMode() == SKYBOX_3DSKYBOX_VISIBLE ) + { + // if the 3d skybox world is drawn, then don't draw the normal skybox + CSkyboxView *pSkyView = new CSkyboxView( this ); + if ( ( bDrew3dSkybox = pSkyView->Setup( monitorView, &nClearFlags, &nSkyMode ) ) != false ) + { + AddViewToScene( pSkyView ); + } + SafeRelease( pSkyView ); + } + + Vector4D plane; + + // Combine the target angles and the plane angles + Vector vecAnglesNormal( angTargetAngles.x, angTargetAngles.y, angTargetAngles.z ); + VectorNormalize( vecAnglesNormal ); + VectorCopy( vecAnglesNormal, plane.AsVector3D() ); + + // TODO: How do we get a good value for this!?!? + //plane.w = m_OurPlane.dist + 0.1f; + plane.w = -32.0f + 0.1f; + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->PushCustomClipPlane( plane.Base() ); + + ViewDrawScene( bDrew3dSkybox, nSkyMode, monitorView, nClearFlags, VIEW_MONITOR ); + + pRenderContext->PopCustomClipPlane(); + render->PopView( frustum ); // Reset the world fog parameters. if ( fogEnabled ) @@ -3118,6 +3620,7 @@ bool CViewRender::DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_Poin #endif // USE_MONITORS return true; } +#endif void CViewRender::DrawMonitors( const CViewSetup &cameraView ) { @@ -3152,6 +3655,17 @@ void CViewRender::DrawMonitors( const CViewSetup &cameraView ) if ( !pCameraEnt->IsActive() || pCameraEnt->IsDormant() ) continue; +#ifdef MAPBASE + // Check if the camera has its own render target + // (Multiple render target support) + if ( pCameraTarget != pCameraEnt->RenderTarget() ) + { + pCameraTarget = pCameraEnt->RenderTarget(); + width = pCameraTarget->GetActualWidth(); + height = pCameraTarget->GetActualHeight(); + } +#endif + if ( !DrawOneMonitor( pCameraTarget, cameraNum, pCameraEnt, cameraView, player, 0, 0, width, height ) ) continue; @@ -4747,7 +5261,20 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR // if you can get really close to the skybox geometry it's possible that you'll be able to clip into it // with this near plane. If so, move it in a bit. It's at 2.0 to give us more precision. That means you // need to keep the eye position at least 2 * scale away from the geometry in the skybox +#ifdef MAPBASE + zNear = r_nearz_skybox.GetFloat(); + + // Use the fog's farz if specified + if (m_pSky3dParams->fog.farz > 0) + { + zFar = ( m_pSky3dParams->scale > 0.0f ? + m_pSky3dParams->fog.farz / m_pSky3dParams->scale : + m_pSky3dParams->fog.farz ); + } + else +#else zNear = 2.0; +#endif zFar = MAX_TRACE_LENGTH; // scale origin by sky scale @@ -4757,13 +5284,44 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR VectorScale( origin, scale, origin ); } Enable3dSkyboxFog(); +#ifdef MAPBASE + // Skybox angle support. + // + // If any of the angles aren't 0, do the rotation code. + if (m_pSky3dParams->skycamera) + { + // Re-use the x coordinate to determine if we shuld do this with angles + if (m_pSky3dParams->angles.GetX() != 0) + { + CalculateSkyAngles( m_pSky3dParams->skycamera->GetAbsAngles() ); + } + + VectorAdd( origin, m_pSky3dParams->skycamera->GetAbsOrigin(), origin ); + } + else + { + if (m_pSky3dParams->angles.GetX() != 0 || + m_pSky3dParams->angles.GetY() != 0 || + m_pSky3dParams->angles.GetZ() != 0) + { + CalculateSkyAngles( m_pSky3dParams->angles.Get() ); + } + + VectorAdd( origin, m_pSky3dParams->origin, origin ); + } +#else VectorAdd( origin, m_pSky3dParams->origin, origin ); +#endif // BUGBUG: Fix this!!! We shouldn't need to call setup vis for the sky if we're connecting // the areas. We'd have to mark all the clusters in the skybox area in the PVS of any // cluster with sky. Then we could just connect the areas to do our vis. //m_bOverrideVisOrigin could hose us here, so call direct +#ifdef MAPBASE + render->ViewSetupVis( false, 1, m_pSky3dParams->skycamera ? &m_pSky3dParams->skycamera->GetAbsOrigin() : &m_pSky3dParams->origin.Get() ); +#else render->ViewSetupVis( false, 1, &m_pSky3dParams->origin.Get() ); +#endif render->Push3DView( (*this), m_ClearFlags, pRenderTarget, GetFrustum(), pDepthTarget ); // Store off view origin and angles @@ -4797,6 +5355,51 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR DrawTranslucentRenderables( true, false ); DrawNoZBufferTranslucentRenderables(); +#ifdef MAPBASE + // Allows reflective glass to be drawn in the skybox. + // New expansions also allow for custom render targets and multiple mirror renders + if (r_skybox_use_complex_views.GetBool()) + { + VisibleFogVolumeInfo_t fogVolumeInfo; + render->GetVisibleFogVolume( origin, &fogVolumeInfo ); + + WaterRenderInfo_t info; + info.m_bCheapWater = true; + info.m_bRefract = false; + info.m_bReflect = false; + info.m_bReflectEntities = false; + info.m_bDrawWaterSurface = false; + info.m_bOpaqueWater = true; + + cplane_t glassReflectionPlane; + Frustum_t frustum; + GeneratePerspectiveFrustum( origin, angles, zNear, zFar, fov, m_flAspectRatio, frustum ); + + ITexture *pTextureTargets[2]; + C_BaseEntity *pReflectiveGlass = NextReflectiveGlass( NULL, (*this), glassReflectionPlane, frustum, pTextureTargets ); + while ( pReflectiveGlass != NULL ) + { + if (pTextureTargets[0]) + { + CRefPtr pGlassReflectionView = new CReflectiveGlassView( m_pMainView ); + pGlassReflectionView->m_pRenderTarget = pTextureTargets[0]; + pGlassReflectionView->Setup( (*this), VIEW_CLEAR_DEPTH, true, fogVolumeInfo, info, glassReflectionPlane ); + m_pMainView->AddViewToScene( pGlassReflectionView ); + } + + if (pTextureTargets[1]) + { + CRefPtr pGlassRefractionView = new CRefractiveGlassView( m_pMainView ); + pGlassRefractionView->m_pRenderTarget = pTextureTargets[1]; + pGlassRefractionView->Setup( (*this), VIEW_CLEAR_DEPTH, true, fogVolumeInfo, info, glassReflectionPlane ); + m_pMainView->AddViewToScene( pGlassRefractionView ); + } + + pReflectiveGlass = NextReflectiveGlass( pReflectiveGlass, (*this), glassReflectionPlane, frustum, pTextureTargets ); + } + } +#endif + m_pMainView->DisableFog(); CGlowOverlay::UpdateSkyOverlays( zFar, m_bCacheFullSceneState ); @@ -4821,6 +5424,55 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR #endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CSkyboxView::CalculateSkyAngles( const QAngle &angAngles ) +{ + // Unfortunately, it's not as simple as "angles += m_pSky3dParams->angles". + // This stuff took a long time to figure out. I'm glad I got it working. + + // First, create a matrix for the sky's angles. + matrix3x4_t matSkyAngles; + AngleMatrix( angAngles, matSkyAngles ); + + // The code in between the lines below was mostly lifted from projected texture screenspace code and was a huge lifesaver. + // The comments are my attempt at explaining the little I understand of what's going on here. + // ---------------------------------------------------------------------- + + // These are the vectors that would eventually become our final angle directions. + Vector vecSkyForward, vecSkyRight, vecSkyUp; + + // Get vectors from our original angles. + Vector vPlayerForward, vPlayerRight, vPlayerUp; + AngleVectors( angles, &vPlayerForward, &vPlayerRight, &vPlayerUp ); + + // Transform them from our sky angles matrix and put the results in those vectors we declared earlier. + VectorTransform( vPlayerForward, matSkyAngles, vecSkyForward ); + VectorTransform( vPlayerRight, matSkyAngles, vecSkyRight ); + VectorTransform( vPlayerUp, matSkyAngles, vecSkyUp ); + + // Normalize them. + VectorNormalize( vecSkyForward ); + VectorNormalize( vecSkyRight ); + VectorNormalize( vecSkyUp ); + + // Now do a bit of quaternion magic and apply that to our original angles. + // This works perfectly, so I'm not gonna touch it. + Quaternion quat; + BasisToQuaternion( vecSkyForward, vecSkyRight, vecSkyUp, quat ); + QuaternionAngles( quat, angles ); + + // End of code mostly lifted from projected texture screenspace stuff + // ---------------------------------------------------------------------- + + // Now just rotate our origin with that matrix. + // We create a copy of the origin since VectorRotate doesn't want in1 to be the same variable as the destination. + VectorRotate(Vector(origin), matSkyAngles, origin); +} +#endif + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -4844,6 +5496,20 @@ bool CSkyboxView::Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibil *pClearFlags |= VIEW_CLEAR_DEPTH; // Need to clear depth after rednering the skybox m_DrawFlags = DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_RENDER_WATER; +#ifdef MAPBASE + if (m_pSky3dParams->skycolor.GetA() != 0 && *pSkyboxVisible != SKYBOX_NOT_VISIBLE) + { + m_ClearFlags |= (VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH); + m_DrawFlags |= DF_CLIP_SKYBOX; + + color32 color = m_pSky3dParams->skycolor.Get(); + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->ClearColor4ub( color.r, color.g, color.b, color.a ); + pRenderContext.SafeRelease(); + } + else +#endif if( r_skybox.GetBool() ) { m_DrawFlags |= DF_DRAWSKYBOX; @@ -5117,7 +5783,7 @@ bool CBaseWorldView::AdjustView( float waterHeight ) { if( m_DrawFlags & DF_RENDER_REFRACTION ) { - ITexture *pTexture = GetWaterRefractionTexture(); + ITexture *pTexture = GetRefractionTexture(); // Use the aspect ratio of the main view! So, don't recompute it here x = y = 0; @@ -5129,7 +5795,7 @@ bool CBaseWorldView::AdjustView( float waterHeight ) if( m_DrawFlags & DF_RENDER_REFLECTION ) { - ITexture *pTexture = GetWaterReflectionTexture(); + ITexture *pTexture = GetReflectionTexture(); // If the main view is overriding the projection matrix (for Stereo or // some other nefarious purpose) make sure to include any Y offset in @@ -5191,14 +5857,14 @@ void CBaseWorldView::PushView( float waterHeight ) pRenderContext->SetHeightClipMode( clipMode ); // Have to re-set up the view since we reset the size - render->Push3DView( *this, m_ClearFlags, GetWaterRefractionTexture(), GetFrustum() ); + render->Push3DView( *this, m_ClearFlags, GetRefractionTexture(), GetFrustum() ); return; } if( m_DrawFlags & DF_RENDER_REFLECTION ) { - ITexture *pTexture = GetWaterReflectionTexture(); + ITexture *pTexture = GetReflectionTexture(); pRenderContext->SetFogZ( waterHeight ); @@ -5252,11 +5918,11 @@ void CBaseWorldView::PopView() // these renders paths used their surfaces, so blit their results if ( m_DrawFlags & DF_RENDER_REFRACTION ) { - pRenderContext->CopyRenderTargetToTextureEx( GetWaterRefractionTexture(), NULL, NULL ); + pRenderContext->CopyRenderTargetToTextureEx( GetRefractionTexture(), NULL, NULL ); } if ( m_DrawFlags & DF_RENDER_REFLECTION ) { - pRenderContext->CopyRenderTargetToTextureEx( GetWaterReflectionTexture(), NULL, NULL ); + pRenderContext->CopyRenderTargetToTextureEx( GetReflectionTexture(), NULL, NULL ); } } @@ -6133,7 +6799,11 @@ void CReflectiveGlassView::Setup( const CViewSetup &view, int nClearFlags, bool bool CReflectiveGlassView::AdjustView( float flWaterHeight ) { +#ifdef MAPBASE + ITexture *pTexture = GetReflectionTexture(); +#else ITexture *pTexture = GetWaterReflectionTexture(); +#endif // Use the aspect ratio of the main view! So, don't recompute it here x = y = 0; @@ -6159,7 +6829,11 @@ bool CReflectiveGlassView::AdjustView( float flWaterHeight ) void CReflectiveGlassView::PushView( float waterHeight ) { +#ifdef MAPBASE + render->Push3DView( *this, m_ClearFlags, GetReflectionTexture(), GetFrustum() ); +#else render->Push3DView( *this, m_ClearFlags, GetWaterReflectionTexture(), GetFrustum() ); +#endif Vector4D plane; VectorCopy( m_ReflectionPlane.normal, plane.AsVector3D() ); @@ -6187,6 +6861,12 @@ void CReflectiveGlassView::Draw() CMatRenderContextPtr pRenderContext( materials ); PIXEVENT( pRenderContext, "CReflectiveGlassView::Draw" ); +#ifdef MAPBASE + // Store off view origin and angles and set the new view + int nSaveViewID = CurrentViewID(); + SetupCurrentView( origin, angles, VIEW_REFLECTION ); +#endif + // Disable occlusion visualization in reflection bool bVisOcclusion = r_visocclusion.GetInt(); r_visocclusion.SetValue( 0 ); @@ -6195,6 +6875,11 @@ void CReflectiveGlassView::Draw() r_visocclusion.SetValue( bVisOcclusion ); +#ifdef MAPBASE + // finish off the view. restore the previous view. + SetupCurrentView( origin, angles, (view_id_t)nSaveViewID ); +#endif + pRenderContext->ClearColor4ub( 0, 0, 0, 255 ); pRenderContext->Flush(); } @@ -6214,7 +6899,11 @@ void CRefractiveGlassView::Setup( const CViewSetup &view, int nClearFlags, bool bool CRefractiveGlassView::AdjustView( float flWaterHeight ) { +#ifdef MAPBASE + ITexture *pTexture = GetRefractionTexture(); +#else ITexture *pTexture = GetWaterRefractionTexture(); +#endif // Use the aspect ratio of the main view! So, don't recompute it here x = y = 0; @@ -6226,7 +6915,11 @@ bool CRefractiveGlassView::AdjustView( float flWaterHeight ) void CRefractiveGlassView::PushView( float waterHeight ) { +#ifdef MAPBASE + render->Push3DView( *this, m_ClearFlags, GetRefractionTexture(), GetFrustum() ); +#else render->Push3DView( *this, m_ClearFlags, GetWaterRefractionTexture(), GetFrustum() ); +#endif Vector4D plane; VectorMultiply( m_ReflectionPlane.normal, -1, plane.AsVector3D() ); @@ -6256,8 +6949,19 @@ void CRefractiveGlassView::Draw() CMatRenderContextPtr pRenderContext( materials ); PIXEVENT( pRenderContext, "CRefractiveGlassView::Draw" ); +#ifdef MAPBASE + // Store off view origin and angles and set the new view + int nSaveViewID = CurrentViewID(); + SetupCurrentView( origin, angles, VIEW_REFRACTION ); +#endif + BaseClass::Draw(); +#ifdef MAPBASE + // finish off the view. restore the previous view. + SetupCurrentView( origin, angles, (view_id_t)nSaveViewID ); +#endif + pRenderContext->ClearColor4ub( 0, 0, 0, 255 ); pRenderContext->Flush(); } diff --git a/mp/src/game/client/viewrender.h b/mp/src/game/client/viewrender.h index 723c3222..2312c4d5 100644 --- a/mp/src/game/client/viewrender.h +++ b/mp/src/game/client/viewrender.h @@ -37,6 +37,10 @@ class CReplayScreenshotTaker; class CStunEffect; #endif // HL2_EPISODIC +#ifdef MAPBASE + class C_FuncFakeWorldPortal; +#endif + //----------------------------------------------------------------------------- // Data specific to intro mode to control rendering. //----------------------------------------------------------------------------- @@ -51,11 +55,22 @@ struct IntroData_t bool m_bDrawPrimary; Vector m_vecCameraView; QAngle m_vecCameraViewAngles; +#ifdef MAPBASE + // Used for ortho views + CHandle m_hCameraEntity; +#endif float m_playerViewFOV; CUtlVector m_Passes; // Fade overriding for the intro float m_flCurrentFadeColor[4]; + +#ifdef MAPBASE + // Draws the skybox. + bool m_bDrawSky; + // Draws the skybox in the secondary camera as well. + bool m_bDrawSky2; +#endif }; // Robin, make this point at something to get intro mode. @@ -436,6 +451,12 @@ private: bool DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_PointCamera *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, int x, int y, int width, int height ); +#ifdef MAPBASE + bool DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldPortal *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, + int x, int y, int width, int height, + const CViewSetup &mainView, cplane_t &ourPlane ); +#endif + // Drawing primitives bool ShouldDrawViewModel( bool drawViewmodel ); void DrawViewModels( const CViewSetup &view, bool drawViewmodel ); @@ -452,7 +473,11 @@ private: // Water-related methods void DrawWorldAndEntities( bool drawSkybox, const CViewSetup &view, int nClearFlags, ViewCustomVisibility_t *pCustomVisibility = NULL ); +#ifdef MAPBASE + virtual void ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData, bool bDrew3dSkybox = false, SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE, bool bDrawViewModel = false, ViewCustomVisibility_t *pCustomVisibility = NULL ); +#else virtual void ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData ); +#endif #ifdef PORTAL // Intended for use in the middle of another ViewDrawScene call, this allows stencils to be drawn after opaques but before translucents are drawn in the main view. diff --git a/mp/src/game/client/vscript_client.cpp b/mp/src/game/client/vscript_client.cpp new file mode 100644 index 00000000..3f7e7cea --- /dev/null +++ b/mp/src/game/client/vscript_client.cpp @@ -0,0 +1,615 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_client.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "characterset.h" +#include "isaverestore.h" +#include "gamerules.h" +#include "vscript_client.nut" +#ifdef MAPBASE_VSCRIPT +#include "mapbase/matchers.h" +#include "c_world.h" +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#endif + +extern IScriptManager *scriptmanager; +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: A clientside variant of CScriptEntityIterator. +//----------------------------------------------------------------------------- +class CScriptClientEntityIterator +{ +public: + HSCRIPT First() { return Next(NULL); } + + HSCRIPT Next( HSCRIPT hStartEntity ) + { + return ToHScript( ClientEntityList().NextBaseEntity( ToEnt( hStartEntity ) ) ); + } + + HSCRIPT CreateByClassname( const char *className ) + { + return ToHScript( CreateEntityByName( className ) ); + } + + HSCRIPT FindByClassname( HSCRIPT hStartEntity, const char *szName ) + { + const CEntInfo *pInfo = hStartEntity ? ClientEntityList().GetEntInfoPtr( ToEnt( hStartEntity )->GetRefEHandle() )->m_pNext : ClientEntityList().FirstEntInfo(); + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + C_BaseEntity *ent = (C_BaseEntity *)pInfo->m_pEntity; + if ( !ent ) + continue; + + if ( Matcher_Match( szName, ent->GetClassname() ) ) + return ToHScript( ent ); + } + + return NULL; + } + + HSCRIPT FindByName( HSCRIPT hStartEntity, const char *szName ) + { + const CEntInfo *pInfo = hStartEntity ? ClientEntityList().GetEntInfoPtr( ToEnt( hStartEntity )->GetRefEHandle() )->m_pNext : ClientEntityList().FirstEntInfo(); + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + C_BaseEntity *ent = (C_BaseEntity *)pInfo->m_pEntity; + if ( !ent ) + continue; + + if ( Matcher_Match( szName, ent->GetEntityName() ) ) + return ToHScript( ent ); + } + + return NULL; + } + +private: +} g_ScriptEntityIterator; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptClientEntityIterator, "CEntities", SCRIPT_SINGLETON "The global list of entities" ) + DEFINE_SCRIPTFUNC( First, "Begin an iteration over the list of entities" ) + DEFINE_SCRIPTFUNC( Next, "Continue an iteration over the list of entities, providing reference to a previously found entity" ) + DEFINE_SCRIPTFUNC( CreateByClassname, "Creates an entity by classname" ) + DEFINE_SCRIPTFUNC( FindByClassname, "Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByName, "Find entities by name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +// Purpose: A base class for VScript-utilizing clientside classes which can persist +// across levels, requiring their scripts to be shut down manually. +//----------------------------------------------------------------------------- +abstract_class IClientScriptPersistable +{ +public: + virtual void TermScript() = 0; +}; + +CUtlVector g_ScriptPersistableList; + +#define SCRIPT_MAT_PROXY_MAX_VARS 8 + +//----------------------------------------------------------------------------- +// Purpose: A material proxy which runs a VScript and allows it to read/write +// to material variables. +//----------------------------------------------------------------------------- +class CScriptMaterialProxy : public IMaterialProxy, public IClientScriptPersistable +{ +public: + CScriptMaterialProxy(); + virtual ~CScriptMaterialProxy(); + + virtual void Release( void ); + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + virtual void OnBind( void *pRenderable ); + virtual IMaterial *GetMaterial() { return NULL; } + + // Proxies can persist across levels and aren't bound to a loaded map. + // The VM, however, is bound to the loaded map, so the proxy's script variables persisting + // causes problems when they're used in a new level with a new VM. + // As a result, we call InitScript() and TermScript() during OnBind and when the level is unloaded respectively. + bool InitScript(); + void TermScript(); + + bool ValidateIndex(int i) + { + if (i > SCRIPT_MAT_PROXY_MAX_VARS || i < 0) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "VScriptProxy: %i out of range", i ); + return false; + } + + return true; + } + + const char *GetVarString( int i ); + int GetVarInt( int i ); + float GetVarFloat( int i ); + const Vector& GetVarVector( int i ); + + void SetVarString( int i, const char *value ); + void SetVarInt( int i, int value ); + void SetVarFloat( int i, float value ); + void SetVarVector( int i, const Vector &value ); + +private: + IMaterialVar *m_MaterialVars[SCRIPT_MAT_PROXY_MAX_VARS]; + + // Save the keyvalue string for InitScript() + char m_szFilePath[MAX_PATH]; + + CScriptScope m_ScriptScope; + HSCRIPT m_hScriptInstance; + HSCRIPT m_hFuncOnBind; +}; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" ) + DEFINE_SCRIPTFUNC( GetVarString, "Gets a material var's string value" ) + DEFINE_SCRIPTFUNC( GetVarInt, "Gets a material var's int value" ) + DEFINE_SCRIPTFUNC( GetVarFloat, "Gets a material var's float value" ) + DEFINE_SCRIPTFUNC( GetVarVector, "Gets a material var's vector value" ) + DEFINE_SCRIPTFUNC( SetVarString, "Sets a material var's string value" ) + DEFINE_SCRIPTFUNC( SetVarInt, "Sets a material var's int value" ) + DEFINE_SCRIPTFUNC( SetVarFloat, "Sets a material var's float value" ) + DEFINE_SCRIPTFUNC( SetVarVector, "Sets a material var's vector value" ) +END_SCRIPTDESC(); + +CScriptMaterialProxy::CScriptMaterialProxy() +{ + m_hScriptInstance = NULL; + m_hFuncOnBind = NULL; +} + +CScriptMaterialProxy::~CScriptMaterialProxy() +{ +} + + +//----------------------------------------------------------------------------- +// Cleanup +//----------------------------------------------------------------------------- +void CScriptMaterialProxy::Release( void ) +{ + if ( m_hScriptInstance && g_pScriptVM ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } + + delete this; +} + +bool CScriptMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + for (KeyValues *pKey = pKeyValues->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey()) + { + // Get each variable we're looking for + if (Q_strnicmp( pKey->GetName(), "var", 3 ) == 0) + { + int index = atoi(pKey->GetName() + 3); + if (index > SCRIPT_MAT_PROXY_MAX_VARS) + { + Warning("VScript material proxy only supports 8 vars (not %i)\n", index); + continue; + } + + bool foundVar; + m_MaterialVars[index] = pMaterial->FindVar( pKey->GetString(), &foundVar ); + + // Don't init if we didn't find the var + if (!foundVar) + return false; + } + else if (FStrEq( pKey->GetName(), "scriptfile" )) + { + Q_strncpy( m_szFilePath, pKey->GetString(), sizeof( m_szFilePath ) ); + } + } + + return true; +} + +bool CScriptMaterialProxy::InitScript() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + char* iszScriptId = (char*)stackalloc( 1024 ); + g_pScriptVM->GenerateUniqueKey("VScriptProxy", iszScriptId, 1024); + + m_hScriptInstance = g_pScriptVM->RegisterInstance( GetScriptDescForClass( CScriptMaterialProxy ), this ); + g_pScriptVM->SetInstanceUniqeId( m_hScriptInstance, iszScriptId ); + + bool bResult = m_ScriptScope.Init( iszScriptId ); + + if (!bResult) + { + CGMsg( 1, CON_GROUP_VSCRIPT, "VScriptProxy couldn't create ScriptScope!\n" ); + return false; + } + + g_pScriptVM->SetValue( m_ScriptScope, "self", m_hScriptInstance ); + } + + // Don't init if we can't run the script + if (!VScriptRunScript( m_szFilePath, m_ScriptScope, true )) + return false; + + m_hFuncOnBind = m_ScriptScope.LookupFunction( "OnBind" ); + + if (!m_hFuncOnBind) + { + // Don't init if we can't find our func + Warning("VScript material proxy can't find OnBind function\n"); + return false; + } + + g_ScriptPersistableList.AddToTail( this ); + return true; +} + +void CScriptMaterialProxy::TermScript() +{ + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } + + m_hFuncOnBind = NULL; + + m_ScriptScope.Term(); +} + +void CScriptMaterialProxy::OnBind( void *pRenderable ) +{ + if( !pRenderable ) + return; + + if (m_hFuncOnBind != NULL) + { + IClientRenderable *pRend = ( IClientRenderable* )pRenderable; + C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity(); + if ( pEnt ) + { + g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() ); + } + else + { + // Needs to register as a null value so the script doesn't break if it looks for an entity + g_pScriptVM->SetValue( m_ScriptScope, "entity", SCRIPT_VARIANT_NULL ); + } + + m_ScriptScope.Call( m_hFuncOnBind, NULL ); + + g_pScriptVM->ClearValue( m_ScriptScope, "entity" ); + } + else + { + // The VM might not exist if we do it from Init(), so we have to do it here. + // TODO: We have no handling for if this fails, how do you cancel a proxy? + if (InitScript()) + OnBind( pRenderable ); + } +} + +const char *CScriptMaterialProxy::GetVarString( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return NULL; + + return m_MaterialVars[i]->GetStringValue(); +} + +int CScriptMaterialProxy::GetVarInt( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return 0; + + return m_MaterialVars[i]->GetIntValue(); +} + +float CScriptMaterialProxy::GetVarFloat( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return 0.0f; + + return m_MaterialVars[i]->GetFloatValue(); +} + +const Vector& CScriptMaterialProxy::GetVarVector( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return vec3_origin; + + if (m_MaterialVars[i]->GetType() != MATERIAL_VAR_TYPE_VECTOR) + return vec3_origin; + + // This is really bad. Too bad! + return *(reinterpret_cast(m_MaterialVars[i]->GetVecValue())); +} + +void CScriptMaterialProxy::SetVarString( int i, const char *value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetStringValue( value ); +} + +void CScriptMaterialProxy::SetVarInt( int i, int value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetIntValue( value ); +} + +void CScriptMaterialProxy::SetVarFloat( int i, float value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetFloatValue( value ); +} + +void CScriptMaterialProxy::SetVarVector( int i, const Vector &value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetVecValue( value.Base(), 3 ); +} + +EXPOSE_INTERFACE( CScriptMaterialProxy, IMaterialProxy, "VScriptProxy" IMATERIAL_PROXY_INTERFACE_VERSION ); +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static float Time() +{ + return gpGlobals->curtime; +} + +static const char *GetMapName() +{ + return engine->GetLevelName(); +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +bool VScriptClientInit() +{ + VMPROF_START + + if( scriptmanager != NULL ) + { + ScriptLanguage_t scriptLanguage = SL_DEFAULT; + + char const *pszScriptLanguage; +#ifdef MAPBASE_VSCRIPT + if (GetClientWorldEntity()->GetScriptLanguage() != SL_NONE) + { + // Allow world entity to override script language + scriptLanguage = GetClientWorldEntity()->GetScriptLanguage(); + + // Less than SL_NONE means the script language should literally be none + if (scriptLanguage < SL_NONE) + scriptLanguage = SL_NONE; + } + else +#endif + if ( CommandLine()->CheckParm( "-scriptlang", &pszScriptLanguage ) ) + { + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + scriptLanguage = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + scriptLanguage = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + scriptLanguage = SL_PYTHON; + } +#ifdef MAPBASE_VSCRIPT + else if( !Q_stricmp(pszScriptLanguage, "lua") ) + { + scriptLanguage = SL_LUA; + } +#endif + else + { + CGWarning( 1, CON_GROUP_VSCRIPT, "-scriptlang does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + scriptLanguage = SL_NONE; + } + + } + if( scriptLanguage != SL_NONE ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( scriptLanguage ); + + if( g_pScriptVM ) + { +#ifdef MAPBASE_VSCRIPT + CGMsg( 0, CON_GROUP_VSCRIPT, "VSCRIPT CLIENT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); +#else + Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); +#endif + ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); + ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + + if ( GameRules() ) + { + GameRules()->RegisterScriptFunctions(); + } + +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->RegisterAllClasses(); + g_pScriptVM->RegisterAllEnums(); + + g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); + + IGameSystem::RegisterVScriptAllSystems(); + + RegisterSharedScriptConstants(); + RegisterSharedScriptFunctions(); +#else + //g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); +#endif + + if (scriptLanguage == SL_SQUIRREL) + { + g_pScriptVM->Run( g_Script_vscript_client ); + } + + VScriptRunScript( "mapspawn", false ); + + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); + + return true; + } + else + { + CGWarning( 1, CON_GROUP_VSCRIPT, "VM Did not start!\n" ); + } + } + } + else + { + CGWarning( 0, CON_GROUP_VSCRIPT, "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptClientTerm() +{ + if( g_pScriptVM != NULL ) + { +#ifdef MAPBASE_VSCRIPT + // Things like proxies can persist across levels, so we have to shut down their scripts manually + for (int i = g_ScriptPersistableList.Count()-1; i >= 0; i--) + { + if (g_ScriptPersistableList[i]) + { + g_ScriptPersistableList[i]->TermScript(); + g_ScriptPersistableList.FastRemove( i ); + } + } +#endif + + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} + + +class CVScriptGameSystem : public CAutoGameSystemPerFrame +{ +public: + // Inherited from IAutoServerSystem + virtual void LevelInitPreEntity( void ) + { + m_bAllowEntityCreationInScripts = true; +#ifndef MAPBASE_VSCRIPT // Now initted in C_World + VScriptClientInit(); +#endif + } + + virtual void LevelInitPostEntity( void ) + { + m_bAllowEntityCreationInScripts = false; +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM) + { + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if (pPlayer) + { + g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); + } + } +#endif + } + + virtual void LevelShutdownPostEntity( void ) + { + VScriptClientTerm(); + } + + virtual void FrameUpdatePostEntityThink() + { + if ( g_pScriptVM ) + g_pScriptVM->Frame( gpGlobals->frametime ); + } + + bool m_bAllowEntityCreationInScripts; +}; + +CVScriptGameSystem g_VScriptGameSystem; + +bool IsEntityCreationAllowedInScripts( void ) +{ + return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; +} + + diff --git a/mp/src/game/client/vscript_client.h b/mp/src/game/client/vscript_client.h new file mode 100644 index 00000000..9b69563e --- /dev/null +++ b/mp/src/game/client/vscript_client.h @@ -0,0 +1,26 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_CLIENT_H +#define VSCRIPT_CLIENT_H + +#include "vscript/ivscript.h" +#include "vscript_shared.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM * g_pScriptVM; + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#ifdef MAPBASE_VSCRIPT +extern IScriptManager * scriptmanager; +#endif + +#endif // VSCRIPT_CLIENT_H diff --git a/mp/src/game/client/vscript_client.nut b/mp/src/game/client/vscript_client.nut new file mode 100644 index 00000000..5fce6ff5 --- /dev/null +++ b/mp/src/game/client/vscript_client.nut @@ -0,0 +1,22 @@ +static char g_Script_vscript_client[] = R"vscript( +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +function UniqueString( string = "" ) +{ + return DoUniqueString( string.tostring() ); +} + +function IncludeScript( name, scope = null ) +{ + if ( scope == null ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +)vscript"; \ No newline at end of file diff --git a/mp/src/game/client/weapon_selection.cpp b/mp/src/game/client/weapon_selection.cpp index dc2adf4e..b869b394 100644 --- a/mp/src/game/client/weapon_selection.cpp +++ b/mp/src/game/client/weapon_selection.cpp @@ -252,9 +252,14 @@ int CBaseHudWeaponSelection::KeyInput( int down, ButtonCode_t keynum, const char return 0; } - if ( down >= 1 && keynum >= KEY_1 && keynum <= KEY_9 ) + //Tony; check 0 as well, otherwise you have to have 0 bound to slot10 no matter what. + if ( down >= 1 && keynum >= KEY_0 && keynum <= KEY_9 ) { - if ( HandleHudMenuInput( keynum - KEY_0 ) ) + //Tony; 0 is actually '10' (slot10) + if (keynum == KEY_0) + keynum = KEY_A; //Dealing with button codes, so just use KEY_A, which is equal to 11 anyway. + + if ( HandleHudMenuInput( keynum - 1 ) ) return 0; } diff --git a/mp/src/game/client/worldlight.cpp b/mp/src/game/client/worldlight.cpp new file mode 100644 index 00000000..66488b11 --- /dev/null +++ b/mp/src/game/client/worldlight.cpp @@ -0,0 +1,314 @@ +//========= Copyright (C) 2018, CSProMod Team, All rights reserved. =========// +// +// Purpose: provide world light related functions to the client +// +// As the engine provides no access to brush/model data (brushdata_t, model_t), +// we hence have no access to dworldlight_t. Therefore, we manually extract the +// world light data from the BSP itself, before entities are initialised on map +// load. +// +// To find the brightest light at a point, all world lights are iterated. +// Lights whose radii do not encompass our sample point are quickly rejected, +// as are lights which are not in our PVS, or visible from the sample point. +// If the sky light is visible from the sample point, then it shall supersede +// all other world lights. +// +// Written: November 2011 +// Author: Saul Rennison +// +//===========================================================================// + +#include "cbase.h" +#include "worldlight.h" +#include "bspfile.h" +#include "filesystem.h" +#include "client_factorylist.h" // FactoryList_Retrieve +#include "eiface.h" // IVEngineServer + +static IVEngineServer *g_pEngineServer = NULL; + +//----------------------------------------------------------------------------- +// Singleton exposure +//----------------------------------------------------------------------------- +static CWorldLights s_WorldLights; +CWorldLights *g_pWorldLights = &s_WorldLights; + +//----------------------------------------------------------------------------- +// Purpose: calculate intensity ratio for a worldlight by distance +// Author: Valve Software +//----------------------------------------------------------------------------- +static float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta ) +{ + float falloff; + + switch (wl->type) + { + case emit_surface: + // Cull out stuff that's too far + if(wl->radius != 0) + { + if(DotProduct( delta, delta ) > (wl->radius * wl->radius)) + return 0.0f; + } + + return InvRSquared(delta); + break; + + case emit_skylight: + return 1.f; + break; + + case emit_quakelight: + // X - r; + falloff = wl->linear_attn - FastSqrt( DotProduct( delta, delta ) ); + if(falloff < 0) + return 0.f; + + return falloff; + break; + + case emit_skyambient: + return 1.f; + break; + + case emit_point: + case emit_spotlight: // directional & positional + { + float dist2, dist; + + dist2 = DotProduct(delta, delta); + dist = FastSqrt(dist2); + + // Cull out stuff that's too far + if(wl->radius != 0 && dist > wl->radius) + return 0.f; + + return 1.f / (wl->constant_attn + wl->linear_attn * dist + wl->quadratic_attn * dist2); + } + + break; + } + + return 1.f; +} + +//----------------------------------------------------------------------------- +// Purpose: initialise game system and members +//----------------------------------------------------------------------------- +CWorldLights::CWorldLights() : CAutoGameSystem("World lights") +{ + m_nWorldLights = 0; + m_pWorldLights = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: clear worldlights, free memory +//----------------------------------------------------------------------------- +void CWorldLights::Clear() +{ + m_nWorldLights = 0; + + if(m_pWorldLights) + { + delete [] m_pWorldLights; + m_pWorldLights = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: get the IVEngineServer, we need this for the PVS functions +//----------------------------------------------------------------------------- +bool CWorldLights::Init() +{ +#ifdef MAPBASE + // Moved to its own clientside interface after I found out it was possible + // (still have no idea how or why this works) + if ((g_pEngineServer = serverengine) == NULL) + return false; + + return true; +#else + factorylist_t factories; + FactoryList_Retrieve(factories); + + if((g_pEngineServer = (IVEngineServer*)factories.appSystemFactory(INTERFACEVERSION_VENGINESERVER, NULL)) == NULL) + return false; + + return true; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: get all world lights from the BSP +//----------------------------------------------------------------------------- +void CWorldLights::LevelInitPreEntity() +{ + // Get the map path + const char *pszMapName = modelinfo->GetModelName(modelinfo->GetModel(1)); + + // Open map + FileHandle_t hFile = g_pFullFileSystem->Open(pszMapName, "rb"); + if(!hFile) + { + Warning("CWorldLights: unable to open map\n"); + return; + } + + // Read the BSP header. We don't need to do any version checks, etc. as we + // can safely assume that the engine did this for us + dheader_t hdr; + g_pFullFileSystem->Read(&hdr, sizeof(hdr), hFile); + + // Grab the light lump and seek to it + lump_t &lightLump = hdr.lumps[LUMP_WORLDLIGHTS]; + + // INSOLENCE: If the worldlights lump is empty, that means theres no normal, LDR lights to extract + // This can happen when, for example, the map is compiled in HDR mode only + // So move on to the HDR worldlights lump + if (lightLump.filelen == 0) + { + lightLump = hdr.lumps[LUMP_WORLDLIGHTS_HDR]; + } + + // If we can't divide the lump data into a whole number of worldlights, + // then the BSP format changed and we're unaware + if(lightLump.filelen % sizeof(dworldlight_t)) + { + Warning("CWorldLights: unknown world light lump\n"); + + // Close file + g_pFullFileSystem->Close(hFile); + return; + } + + g_pFullFileSystem->Seek(hFile, lightLump.fileofs, FILESYSTEM_SEEK_HEAD); + + // Allocate memory for the worldlights + m_nWorldLights = lightLump.filelen / sizeof(dworldlight_t); + m_pWorldLights = new dworldlight_t[m_nWorldLights]; + + // Read worldlights then close + g_pFullFileSystem->Read(m_pWorldLights, lightLump.filelen, hFile); + g_pFullFileSystem->Close(hFile); + + DevMsg("CWorldLights: load successful (%d lights at 0x%p)\n", m_nWorldLights, m_pWorldLights); +} + +//----------------------------------------------------------------------------- +// Purpose: find the brightest light source at a point +//----------------------------------------------------------------------------- +bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness) +{ + if(!m_nWorldLights || !m_pWorldLights) + return false; + + // Default light position and brightness to zero + vecLightBrightness.Init(); + vecLightPos.Init(); + + // Find the size of the PVS for our current position + int nCluster = g_pEngineServer->GetClusterForOrigin(vecPosition); + int nPVSSize = g_pEngineServer->GetPVSForCluster(nCluster, 0, NULL); + + // Get the PVS at our position + byte *pvs = new byte[nPVSSize]; + g_pEngineServer->GetPVSForCluster(nCluster, nPVSSize, pvs); + + // Iterate through all the worldlights + for(int i = 0; i < m_nWorldLights; ++i) + { + dworldlight_t *light = &m_pWorldLights[i]; + + // Skip skyambient + if(light->type == emit_skyambient) + { + //engine->Con_NPrintf(i, "%d: skyambient", i); + continue; + } + + // Handle sun + if(light->type == emit_skylight) + { + // Calculate sun position + Vector vecAbsStart = vecPosition + Vector(0,0,30); + Vector vecAbsEnd = vecAbsStart - (light->normal * MAX_TRACE_LENGTH); + + trace_t tr; + UTIL_TraceLine(vecPosition, vecAbsEnd, MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr); + + // If we didn't hit anything then we have a problem + if(!tr.DidHit()) + { + //engine->Con_NPrintf(i, "%d: skylight: couldn't touch sky", i); + continue; + } + + // If we did hit something, and it wasn't the skybox, then skip + // this worldlight + if(!(tr.surface.flags & SURF_SKY) && !(tr.surface.flags & SURF_SKY2D)) + { + //engine->Con_NPrintf(i, "%d: skylight: no sight to sun", i); + continue; + } + + // Act like we didn't find any valid worldlights, so the shadow + // manager uses the default shadow direction instead (should be the + // sun direction) + + delete[] pvs; + + return false; + } + + // Calculate square distance to this worldlight + Vector vecDelta = light->origin - vecPosition; + float flDistSqr = vecDelta.LengthSqr(); + float flRadiusSqr = light->radius * light->radius; + + // Skip lights that are out of our radius + if(flRadiusSqr > 0 && flDistSqr >= flRadiusSqr) + { + //engine->Con_NPrintf(i, "%d: out-of-radius (dist: %d, radius: %d)", i, sqrt(flDistSqr), light->radius); + continue; + } + + // Is it out of our PVS? + if(!g_pEngineServer->CheckOriginInPVS(light->origin, pvs, nPVSSize)) + { + //engine->Con_NPrintf(i, "%d: out of PVS", i); + continue; + } + + // Calculate intensity at our position + float flRatio = Engine_WorldLightDistanceFalloff(light, vecDelta); + Vector vecIntensity = light->intensity * flRatio; + + // Is this light more intense than the one we already found? + if(vecIntensity.LengthSqr() <= vecLightBrightness.LengthSqr()) + { + //engine->Con_NPrintf(i, "%d: too dim", i); + continue; + } + + // Can we see the light? + trace_t tr; + Vector vecAbsStart = vecPosition + Vector(0,0,30); + UTIL_TraceLine(vecAbsStart, light->origin, MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr); + + if(tr.DidHit()) + { + //engine->Con_NPrintf(i, "%d: trace failed", i); + continue; + } + + vecLightPos = light->origin; + vecLightBrightness = vecIntensity; + + //engine->Con_NPrintf(i, "%d: set (%.2f)", i, vecIntensity.Length()); + } + + delete[] pvs; + + //engine->Con_NPrintf(m_nWorldLights, "result: %d", !vecLightBrightness.IsZero()); + return !vecLightBrightness.IsZero(); +} diff --git a/mp/src/game/client/worldlight.h b/mp/src/game/client/worldlight.h new file mode 100644 index 00000000..65a13ea7 --- /dev/null +++ b/mp/src/game/client/worldlight.h @@ -0,0 +1,50 @@ +//========= Copyright (C) 2018, CSProMod Team, All rights reserved. =========// +// +// Purpose: provide world light related functions to the client +// +// Written: November 2011 +// Author: Saul Rennison +// +//===========================================================================// + +#pragma once + +#include "igamesystem.h" // CAutoGameSystem + +class Vector; +struct dworldlight_t; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CWorldLights : public CAutoGameSystem +{ +public: + CWorldLights(); + ~CWorldLights() { Clear(); } + + //------------------------------------------------------------------------- + // Find the brightest light source at a point + //------------------------------------------------------------------------- + bool GetBrightestLightSource(const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness); +#ifdef MAPBASE + bool GetCumulativeLightSource(const Vector &vecPosition, Vector &vecLightPos, float flMinBrightnessSqr); +#endif + + // CAutoGameSystem overrides +public: + virtual bool Init(); + virtual void LevelInitPreEntity(); + virtual void LevelShutdownPostEntity() { Clear(); } + +private: + void Clear(); + + int m_nWorldLights; + dworldlight_t *m_pWorldLights; +}; + +//----------------------------------------------------------------------------- +// Singleton exposure +//----------------------------------------------------------------------------- +extern CWorldLights *g_pWorldLights; diff --git a/mp/src/game/server/AI_Criteria.cpp b/mp/src/game/server/AI_Criteria.cpp index 128e9d32..95855f1b 100644 --- a/mp/src/game/server/AI_Criteria.cpp +++ b/mp/src/game/server/AI_Criteria.cpp @@ -170,6 +170,22 @@ void AI_CriteriaSet::Describe() } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Merges another AI_CriteriaSet without clearing +// Input : src - +//----------------------------------------------------------------------------- +void AI_CriteriaSet::MergeSet( const AI_CriteriaSet& src ) +{ + for ( short i = src.m_Lookup.FirstInorder(); + i != src.m_Lookup.InvalidIndex(); + i = src.m_Lookup.NextInorder( i ) ) + { + m_Lookup.Insert( src.m_Lookup[ i ] ); + } +} +#endif + BEGIN_SIMPLE_DATADESC( AI_ResponseParams ) DEFINE_FIELD( flags, FIELD_SHORT ), DEFINE_FIELD( odds, FIELD_SHORT ), @@ -195,8 +211,11 @@ AI_Response::AI_Response() m_szResponseName[0] = 0; m_szMatchingRule[0] = 0; - m_pCriteria = NULL; +#ifdef MAPBASE + m_iContextFlags = 0; +#else m_bApplyContextToWorld = false; +#endif } //----------------------------------------------------------------------------- @@ -205,6 +224,10 @@ AI_Response::AI_Response( const AI_Response &from ) { m_pCriteria = NULL; *this = from; +#ifdef MAPBASE + m_iContextFlags = from.m_iContextFlags; +#else +#endif } //----------------------------------------------------------------------------- @@ -235,12 +258,13 @@ AI_Response &AI_Response::operator=( const AI_Response &from ) // Copy criteria. if (from.m_pCriteria) m_pCriteria = new AI_CriteriaSet(*from.m_pCriteria); - m_Params = from.m_Params; - m_szContext = from.m_szContext; +#ifdef MAPBASE + m_iContextFlags = from.m_iContextFlags; +#else m_bApplyContextToWorld = from.m_bApplyContextToWorld; - +#endif return *this; } @@ -265,9 +289,33 @@ void AI_Response::Init( ResponseType_t type, const char *responseName, const AI_ m_Params = responseparams; m_szContext = applyContext; + +#ifdef MAPBASE + bApplyContextToWorld ? m_iContextFlags = APPLYCONTEXT_WORLD : m_iContextFlags = 0; +#else m_bApplyContextToWorld = bApplyContextToWorld; +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : *response - +// *criteria - +//----------------------------------------------------------------------------- +void AI_Response::Init( ResponseType_t type, const char *responseName, const AI_CriteriaSet& criteria, const AI_ResponseParams& responseparams, const char *ruleName, const char *applyContext, int iContextFlags ) +{ + m_Type = type; + Q_strncpy( m_szResponseName, responseName, sizeof( m_szResponseName ) ); + // Copy underlying criteria + m_pCriteria = new AI_CriteriaSet( criteria ); + Q_strncpy( m_szMatchingRule, ruleName ? ruleName : "NULL", sizeof( m_szMatchingRule ) ); + m_Params = responseparams; + SetContext( applyContext ); + m_iContextFlags = iContextFlags; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -281,7 +329,19 @@ void AI_Response::Describe() if ( m_szMatchingRule[ 0 ] ) DevMsg( "Matched rule '%s', ", m_szMatchingRule ); if ( m_szContext.Length() ) +#ifdef MAPBASE + DevMsg( "Contexts to set '%s' on ", m_szContext.Get() ); + if (m_iContextFlags & APPLYCONTEXT_WORLD) + DevMsg("world, "); + else if (m_iContextFlags & APPLYCONTEXT_SQUAD) + DevMsg("squad, "); + else if (m_iContextFlags & APPLYCONTEXT_ENEMY) + DevMsg("enemy, "); + else + DevMsg("speaker, "); +#else DevMsg( "Contexts to set '%s' on %s, ", m_szContext.Get(), m_bApplyContextToWorld ? "world" : "speaker" ); +#endif DevMsg( "response %s = '%s'\n", DescribeResponse( (ResponseType_t)m_Type ), m_szResponseName ); } diff --git a/mp/src/game/server/AI_Criteria.h b/mp/src/game/server/AI_Criteria.h index f10c2d05..e7f84deb 100644 --- a/mp/src/game/server/AI_Criteria.h +++ b/mp/src/game/server/AI_Criteria.h @@ -37,6 +37,10 @@ public: const char *GetValue( int index ) const; float GetWeight( int index ) const; +#ifdef MAPBASE + void MergeSet( const AI_CriteriaSet& src ); +#endif + private: struct CritEntry_t @@ -170,6 +174,24 @@ enum ResponseType_t NUM_RESPONSES, }; +#ifdef MAPBASE +// I wanted to add more options to apply contexts to, +// so I replaced the existing system with a flag-based integer instead of a bunch of booleans. +// +// New ones should be implemented in: +// CResponseSystem::ParseRule() - AI_ResponseSystem.cpp +// AI_Response::Describe() - AI_Criteria.cpp +// CAI_Expresser::SpeakDispatchResponse() - ai_speech.cpp +enum +{ + APPLYCONTEXT_SELF = (1 << 0), // Included for contexts that apply to both self and something else + APPLYCONTEXT_WORLD = (1 << 1), // Apply to world + + APPLYCONTEXT_SQUAD = (1 << 2), // Apply to squad + APPLYCONTEXT_ENEMY = (1 << 3), // Apply to enemy +}; +#endif + class AI_Response { public: @@ -199,7 +221,12 @@ public: void SetContext( const char *context ); const char * GetContext( void ) const { return m_szContext.Length() ? m_szContext.Get() : NULL; } +#ifdef MAPBASE + int GetContextFlags() { return m_iContextFlags; } + bool IsApplyContextToWorld( void ) { return (m_iContextFlags & APPLYCONTEXT_WORLD) != 0; } +#else bool IsApplyContextToWorld( void ) { return m_bApplyContextToWorld; } +#endif void Describe(); @@ -213,6 +240,16 @@ public: const char *applyContext, bool bApplyContextToWorld ); +#ifdef MAPBASE + void Init( ResponseType_t type, + const char *responseName, + const AI_CriteriaSet& criteria, + const AI_ResponseParams& responseparams, + const char *matchingRule, + const char *applyContext, + int iContextFlags ); +#endif + static const char *DescribeResponse( ResponseType_t type ); enum @@ -233,7 +270,11 @@ private: AI_ResponseParams m_Params; CUtlString m_szContext; +#ifdef MAPBASE + int m_iContextFlags; +#else bool m_bApplyContextToWorld; +#endif }; #endif // AI_CRITERIA_H diff --git a/mp/src/game/server/AI_ResponseSystem.cpp b/mp/src/game/server/AI_ResponseSystem.cpp index 510a1d4e..43fc871b 100644 --- a/mp/src/game/server/AI_ResponseSystem.cpp +++ b/mp/src/game/server/AI_ResponseSystem.cpp @@ -22,6 +22,9 @@ #include "stringpool.h" #include "fmtstr.h" #include "multiplay_gamerules.h" +#ifdef MAPBASE +#include "mapbase/matchers.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -30,6 +33,10 @@ ConVar rr_debugresponses( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose ma ConVar rr_debugrule( "rr_debugrule", "", FCVAR_NONE, "If set to the name of the rule, that rule's score will be shown whenever a concept is passed into the response rules system."); ConVar rr_dumpresponses( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" ); +#ifdef MAPBASE +ConVar rr_enhanced_saverestore( "rr_enhanced_saverestore", "0", FCVAR_NONE, "Enables enhanced save/restore capabilities for the Response System." ); +#endif + static CUtlSymbolTable g_RS; inline static char *CopyString( const char *in ) @@ -57,6 +64,9 @@ public: minequals = false; usemax = false; maxequals = false; +#ifdef MAPBASE + isbit = false; +#endif maxval = 0.0f; minval = 0.0f; @@ -99,6 +109,14 @@ public: return; } +#ifdef MAPBASE + if (isbit) + { + DevMsg( " matcher: &%s%s\n", notequal ? "~" : "", GetToken() ); + return; + } +#endif + if ( notequal ) { DevMsg( " matcher: !=%s\n", GetToken() ); @@ -118,6 +136,9 @@ public: bool minequals : 1; //5 bool usemax : 1; //6 bool maxequals : 1; //7 +#ifdef MAPBASE + bool isbit : 1; //8 +#endif void SetToken( char const *s ) { @@ -461,7 +482,11 @@ struct Rule m_bMatchOnce = false; m_bEnabled = true; m_szContext = NULL; +#ifdef MAPBASE + m_iContextFlags = 0; +#else m_bApplyContextToWorld = false; +#endif } Rule& operator =( const Rule& src ) @@ -487,7 +512,11 @@ struct Rule SetContext( src.m_szContext ); m_bMatchOnce = src.m_bMatchOnce; m_bEnabled = src.m_bEnabled; +#ifdef MAPBASE + m_iContextFlags = src.m_iContextFlags; +#else m_bApplyContextToWorld = src.m_bApplyContextToWorld; +#endif return *this; } @@ -511,7 +540,11 @@ struct Rule SetContext( src.m_szContext ); m_bMatchOnce = src.m_bMatchOnce; m_bEnabled = src.m_bEnabled; +#ifdef MAPBASE + m_iContextFlags = src.m_iContextFlags; +#else m_bApplyContextToWorld = src.m_bApplyContextToWorld; +#endif } ~Rule() @@ -530,14 +563,22 @@ struct Rule bool IsEnabled() const { return m_bEnabled; } void Disable() { m_bEnabled = false; } bool IsMatchOnce() const { return m_bMatchOnce; } +#ifdef MAPBASE + bool IsApplyContextToWorld() const { return (m_iContextFlags & APPLYCONTEXT_WORLD) != 0; } +#else bool IsApplyContextToWorld() const { return m_bApplyContextToWorld; } +#endif // Indices into underlying criteria and response dictionaries CUtlVector< unsigned short > m_Criteria; CUtlVector< unsigned short> m_Responses; char *m_szContext; +#ifdef MAPBASE + int m_iContextFlags; +#else bool m_bApplyContextToWorld : 1; +#endif bool m_bMatchOnce : 1; bool m_bEnabled : 1; @@ -668,7 +709,11 @@ public: void ParseOneResponse( const char *responseGroupName, ResponseGroup& group ); +#ifdef MAPBASE + void ParseInclude( CStringPool &includedFiles, const char *scriptfile = NULL ); +#else void ParseInclude( CStringPool &includedFiles ); +#endif void ParseResponse( void ); void ParseCriterion( void ); void ParseRule( void ); @@ -865,6 +910,7 @@ void CResponseSystem::ResolveToken( Matcher& matcher, char *token, size_t bufsiz } +#ifndef MAPBASE // Moved to matchers.h static bool AppearsToBeANumber( char const *token ) { if ( atof( token ) != 0.0f ) @@ -881,6 +927,7 @@ static bool AppearsToBeANumber( char const *token ) return true; } +#endif void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) { @@ -905,6 +952,9 @@ void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) bool lt = false; bool eq = false; bool nt = false; +#ifdef MAPBASE + bool bit = false; +#endif bool done = false; while ( !done ) @@ -937,6 +987,17 @@ void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) // Convert raw token to real token in case token is an enumerated type specifier ResolveToken( matcher, token, sizeof( token ), rawtoken ); +#ifdef MAPBASE + // Bits are an entirely different and independent story + if (bit) + { + matcher.isbit = true; + matcher.notequal = nt; + + matcher.isnumeric = true; + } + else +#endif // Fill in first data set if ( gt ) { @@ -978,6 +1039,13 @@ void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) case '!': nt = true; break; +#ifdef MAPBASE + case '~': + nt = true; + case '&': + bit = true; + break; +#endif default: rawtoken[ n++ ] = *in; break; @@ -1002,6 +1070,19 @@ bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, boo bool found = false; v = LookupEnumeration( setValue, found ); } + +#ifdef MAPBASE + // Bits are always a different story + if (m.isbit) + { + int v1 = v; + int v2 = atoi(m.GetToken()); + if (m.notequal) + return (v1 & v2) == 0; + else + return (v1 & v2) != 0; + } +#endif int minmaxcount = 0; @@ -1052,7 +1133,11 @@ bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, boo } else { +#ifdef MAPBASE + if ( Matcher_NamesMatch(m.GetToken(), setValue) ) +#else if ( !Q_stricmp( setValue, m.GetToken() ) ) +#endif return false; } @@ -1069,7 +1154,12 @@ bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, boo return v == (float)atof( m.GetToken() ); } +#ifdef MAPBASE + // Wildcards. + return Matcher_NamesMatch(m.GetToken(), setValue) ? true : false; +#else return !Q_stricmp( setValue, m.GetToken() ) ? true : false; +#endif } bool CResponseSystem::Compare( const char *setValue, Criteria *c, bool verbose /*= false*/ ) @@ -1745,11 +1835,19 @@ bool CResponseSystem::FindBestResponse( const AI_CriteriaSet& set, AI_Response& char ruleName[ 128 ]; char responseName[ 128 ]; const char *context; +#ifdef MAPBASE + int contextflags; +#else bool bcontexttoworld; +#endif ruleName[ 0 ] = 0; responseName[ 0 ] = 0; context = NULL; +#ifdef MAPBASE + contextflags = 0; +#else bcontexttoworld = false; +#endif if ( bestRule != -1 ) { Rule *r = &m_Rules[ bestRule ]; @@ -1770,12 +1868,20 @@ bool CResponseSystem::FindBestResponse( const AI_CriteriaSet& set, AI_Response& r->Disable(); } context = r->GetContext(); +#ifdef MAPBASE + contextflags = r->m_iContextFlags; +#else bcontexttoworld = r->IsApplyContextToWorld(); +#endif valid = true; } +#ifdef MAPBASE + response.Init( responseType, responseName, set, rp, ruleName, context, contextflags ); +#else response.Init( responseType, responseName, set, rp, ruleName, context, bcontexttoworld ); +#endif if ( showResult ) { @@ -1894,11 +2000,42 @@ void CResponseSystem::Precache() } } +#ifdef MAPBASE +void CResponseSystem::ParseInclude( CStringPool &includedFiles, const char *scriptfile ) +#else void CResponseSystem::ParseInclude( CStringPool &includedFiles ) +#endif { char includefile[ 256 ]; ParseToken(); +#ifdef MAPBASE + if (scriptfile) + { + // Gets first path + // (for example, an #include from a file in resource/script/resp will return resource) + size_t len = strlen(scriptfile)-1; + for (size_t i = 0; i < len; i++) + { + if (scriptfile[i] == CORRECT_PATH_SEPARATOR || scriptfile[i] == INCORRECT_PATH_SEPARATOR) + { + len = i; + } + } + Q_strncpy(includefile, scriptfile, len+1); + + if (len+1 != strlen(scriptfile)) + { + Q_snprintf(includefile, sizeof(includefile), "%s/%s", includefile, token); + } + else + includefile[0] = '\0'; + } + + if (!includefile[0]) + Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); +#else Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); +#endif // check if the file is already included if ( includedFiles.Find( includefile ) != NULL ) @@ -1939,8 +2076,19 @@ void CResponseSystem::LoadFromBuffer( const char *scriptfile, const char *buffer if ( !Q_stricmp( token, "#include" ) ) { +#ifdef MAPBASE + ParseInclude( includedFiles, scriptfile ); +#else ParseInclude( includedFiles ); +#endif } +#ifdef MAPBASE + else if ( !Q_stricmp( token, "#base" ) ) + { + // Actual #base in the future? + ParseInclude( includedFiles, scriptfile ); + } +#endif else if ( !Q_stricmp( token, "response" ) ) { ParseResponse(); @@ -2324,6 +2472,16 @@ void CResponseSystem::ParseResponse( void ) ParseOneResponse( responseGroupName, newGroup ); } +#ifdef MAPBASE + short existing = m_Responses.Find( responseGroupName ); + if ( existing != m_Responses.InvalidIndex() ) + { + //ResponseWarning( "Additional definition for response '%s', overwriting\n", responseGroupName ); + m_Responses[existing] = newGroup; + m_Responses.SetElementName(existing, responseGroupName); + return; + } +#endif m_Responses.Insert( responseGroupName, newGroup ); } @@ -2405,11 +2563,22 @@ int CResponseSystem::ParseOneCriterion( const char *criterionName ) ComputeMatcher( &newCriterion, newCriterion.matcher ); } +#ifdef MAPBASE + short existing = m_Criteria.Find( criterionName ); + if ( existing != m_Criteria.InvalidIndex() ) + { + //ResponseWarning( "Additional definition for criteria '%s', overwriting\n", criterionName ); + m_Criteria[existing] = newCriterion; + m_Criteria.SetElementName(existing, criterionName); + return existing; + } +#else if ( m_Criteria.Find( criterionName ) != m_Criteria.InvalidIndex() ) { ResponseWarning( "Multiple definitions for criteria '%s'\n", criterionName ); return m_Criteria.InvalidIndex(); } +#endif int idx = m_Criteria.Insert( criterionName, newCriterion ); return idx; @@ -2475,12 +2644,22 @@ void CResponseSystem::ParseEnumeration( void ) { m_Enumerations.Insert( sz, newEnum ); } +#ifdef MAPBASE + else + { + short existing = m_Enumerations.Find( sz ); + //ResponseWarning( "Additional definition for enumeration '%s', overwriting\n", sz ); + m_Enumerations[existing] = newEnum; + m_Enumerations.SetElementName(existing, sz); + } +#else /* else { ResponseWarning( "Ignoring duplication enumeration '%s'\n", sz ); } */ +#endif } } @@ -2531,10 +2710,28 @@ void CResponseSystem::ParseRule( void ) if ( !Q_stricmp( token, "applyContextToWorld" ) ) { +#ifdef MAPBASE + newRule.m_iContextFlags |= APPLYCONTEXT_WORLD; +#else newRule.m_bApplyContextToWorld = true; +#endif continue; } +#ifdef MAPBASE + if ( !Q_stricmp( token, "applyContextToSquad" ) ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_SQUAD; + continue; + } + + if ( !Q_stricmp( token, "applyContextToEnemy" ) ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_ENEMY; + continue; + } +#endif + if ( !Q_stricmp( token, "applyContext" ) ) { ParseToken(); @@ -2606,6 +2803,16 @@ void CResponseSystem::ParseRule( void ) if ( validRule ) { +#ifdef MAPBASE + short existing = m_Rules.Find( ruleName ); + if ( existing != m_Rules.InvalidIndex() ) + { + //ResponseWarning( "Additional definition for rule '%s', overwriting\n", ruleName ); + m_Rules[existing] = newRule; + m_Rules.SetElementName(existing, ruleName); + return; + } +#endif m_Rules.Insert( ruleName, newRule ); } else @@ -2791,7 +2998,11 @@ void CResponseSystem::CopyRuleFrom( Rule *pSrcRule, int iRule, CResponseSystem * dstRule.SetContext( pSrcRule->GetContext() ); dstRule.m_bMatchOnce = pSrcRule->m_bMatchOnce; dstRule.m_bEnabled = pSrcRule->m_bEnabled; +#ifdef MAPBASE + dstRule.m_iContextFlags = pSrcRule->m_iContextFlags; +#else dstRule.m_bApplyContextToWorld = pSrcRule->m_bApplyContextToWorld; +#endif // Copy off criteria. CopyCriteriaFrom( pSrcRule, &dstRule, pCustomSystem ); @@ -2806,6 +3017,10 @@ void CResponseSystem::CopyRuleFrom( Rule *pSrcRule, int iRule, CResponseSystem * pCustomSystem->m_Rules.Insert( m_Rules.GetElementName( iRule ), dstRule ); } +#ifdef MAPBASE +ConVar mapbase_rs_clear("mapbase_rs_clear", "1"); +#endif + //----------------------------------------------------------------------------- // Purpose: A special purpose response system associated with a custom entity //----------------------------------------------------------------------------- @@ -2853,6 +3068,32 @@ public: Clear(); delete this; } + +#ifdef MAPBASE + // From an old version of Mapbase's map-specific response system stuff. + /* + #define CopyRSDict(dict) for (unsigned int i = 0; i < dict.Count(); i++) \ + { \ + rs->dict.Insert(dict.GetElementName(i), dict[i]); \ + } \ + + bool MergeWithMain(bool bRemoveThis = true) + { + extern IResponseSystem *g_pResponseSystem; + CResponseSystem *rs = static_cast(g_pResponseSystem); + + CopyRSDict(m_Responses); + CopyRSDict(m_Criteria); + CopyRSDict(m_Rules); + CopyRSDict(m_Enumerations); + + if (mapbase_rs_clear.GetBool()) + Release(); + + return true; + } + */ +#endif private: char *m_pszScriptFile; @@ -2939,6 +3180,9 @@ public: Precache(); } +#ifdef MAPBASE + if (!rr_enhanced_saverestore.GetBool() || gpGlobals->eLoadType != MapLoad_Transition) +#endif ResetResponseGroups(); } @@ -3051,6 +3295,254 @@ CON_COMMAND( rr_reloadresponsesystems, "Reload all response system scripts." ) #endif } +#ifdef MAPBASE +// Designed for extern magic, this gives the <, >, etc. of response system criteria to the outside world. +// Mostly just used for Matcher_Match in matchers.h. +bool ResponseSystemCompare(const char *criterion, const char *value) +{ + Criteria criteria; + criteria.value = CopyString( criterion ); + defaultresponsesytem.ComputeMatcher(&criteria, criteria.matcher); + return defaultresponsesytem.CompareUsingMatcher(value, criteria.matcher, true); + + return false; +} + +// Another version that returns the criterion without the operators. +// I ended up not using this, but feel free to uncomment it. +// Just keep in mind it was scrapped before I could test it... +/* +const char *ResponseSystemCompare(const char *criterion, const char *value, bool bReturnToken) +{ + CResponseSystem *responsesys = dynamic_cast(g_pResponseSystem); + if (responsesys) + { + Criteria criteria; + criteria.value = CopyString( criterion ); + responsesys->ComputeMatcher(&criteria, criteria.matcher); + return responsesys->CompareUsingMatcher(value, criteria.matcher, true) ? criteria.matcher.GetToken() : NULL; + } + return NULL; +} +*/ + +//----------------------------------------------------------------------------- +// CResponseFilePrecacher +// +// Purpose: Precaches a single talker file. That's it. +// +// It copies from a bunch of the original Response System class and therefore it's really messy. +// Despite the horrors a normal programmer might find in here, I think it performs better than anything else I could've come up with. +//----------------------------------------------------------------------------- +class CResponseFilePrecacher +{ +public: + + // Stuff copied from the Response System. + // Direct copy-pastes are very compact, to say the least. + inline bool ParseToken( void ) + { + if ( m_bUnget ) + { m_bUnget = false; return true; } + if ( m_ScriptStack.Count() <= 0 ) + { return false; } + + m_ScriptStack[ 0 ].currenttoken = engine->ParseFile( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); + m_ScriptStack[ 0 ].tokencount++; + return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; + } + + CUtlVector< CResponseSystem::ScriptEntry > m_ScriptStack; + bool m_bUnget; + char token[ 1204 ]; + + + void PrecacheResponse( const char *response, byte type ) + { + switch ( type ) + { + default: + break; + case RESPONSE_SCENE: + { + DevMsg("Precaching scene %s...\n", response); + + // fixup $gender references + char file[_MAX_PATH]; + Q_strncpy( file, response, sizeof(file) ); + char *gender = strstr( file, "$gender" ); + if ( gender ) + { + // replace with male & female + const char *postGender = gender + strlen("$gender"); + *gender = 0; + char genderFile[_MAX_PATH]; + + Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender); + PrecacheInstancedScene( genderFile ); + + Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender); + PrecacheInstancedScene( genderFile ); + } + else + { + PrecacheInstancedScene( file ); + } + } + break; + case RESPONSE_SPEAK: + { + DevMsg("Precaching sound %s...\n", response); + CBaseEntity::PrecacheScriptSound( response ); + } + break; + } + } + + bool IsRootCommand() + { + if (!Q_stricmp( token, "#include" ) || !Q_stricmp( token, "response" ) + || !Q_stricmp( token, "enumeration" ) || !Q_stricmp( token, "criteria" ) + || !Q_stricmp( token, "criterion" ) || !Q_stricmp( token, "rule" )) + return true; + return false; + } + + void ParseResponse( void ) + { + // Must go to response group name + ParseToken(); + + while ( 1 ) + { + ParseToken(); + + if ( !Q_stricmp( token, "{" ) ) + { + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + byte type = ComputeResponseType( token ); + if (type == RESPONSE_NONE) + continue; + + ParseToken(); + char *value = CopyString( token ); + + PrecacheResponse(value, type); + } + break; + } + + byte type = ComputeResponseType( token ); + if (type == RESPONSE_NONE) + break; + + ParseToken(); + char *value = CopyString( token ); + + PrecacheResponse(value, type); + + break; + } + } + + bool LoadFromBuffer(const char *scriptfile, unsigned char *buffer, CStringPool &includedFiles) + { + includedFiles.Allocate( scriptfile ); + + CResponseSystem::ScriptEntry e; + e.name = filesystem->FindOrAddFileName( scriptfile ); + e.buffer = buffer; + e.currenttoken = (char *)e.buffer; + e.tokencount = 0; + m_ScriptStack.AddToHead( e ); + + while ( 1 ) + { + ParseToken(); + if ( !token[0] ) + { + break; + } + + if ( !Q_stricmp( token, "response" ) ) + { + ParseResponse(); + } + else if ( !Q_stricmp( token, "#include" ) || !Q_stricmp( token, "#base" ) ) + { + // Compacted version of ParseInclude(), including new changes. + // Look at that if you want to read. + char includefile[ 256 ]; + ParseToken(); + if (scriptfile) { size_t len = strlen(scriptfile)-1; + for (size_t i = 0; i < len; i++) + { if (scriptfile[i] == CORRECT_PATH_SEPARATOR || scriptfile[i] == INCORRECT_PATH_SEPARATOR) + { len = i; } + } Q_strncpy(includefile, scriptfile, len+1); + if (len+1 != strlen(scriptfile)) + { Q_snprintf(includefile, sizeof(includefile), "%s/%s", includefile, token); } + else includefile[0] = '\0'; + } if (!includefile[0]) Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); + + if ( includedFiles.Find( includefile ) == NULL ) + { + MEM_ALLOC_CREDIT(); + + // Try and load it + CUtlBuffer buf; + if ( filesystem->ReadFile( includefile, "GAME", buf ) ) + { + LoadFromBuffer( includefile, (unsigned char *)buf.PeekGet(), includedFiles ); + } + } + } + } + + if ( m_ScriptStack.Count() > 0 ) + m_ScriptStack.Remove( 0 ); + + return true; + } +}; + +// Loads a file directly to the main response system +bool LoadResponseSystemFile(const char *scriptfile) +{ + CUtlBuffer buf; + if ( !filesystem->ReadFile( scriptfile, "GAME", buf ) ) + { + return false; + } + + // This is a really messy and specialized system that precaches the responses and only the responses of a talker file. + CStringPool includedFiles; + CResponseFilePrecacher *rs = new CResponseFilePrecacher(); + if (!rs || !rs->LoadFromBuffer(scriptfile, (unsigned char *)buf.PeekGet(), includedFiles)) + { + Warning( "Failed to load response system data from %s", scriptfile ); + delete rs; + return false; + } + delete rs; + + CStringPool includedFiles2; + defaultresponsesytem.LoadFromBuffer(scriptfile, (const char *)buf.PeekGet(), includedFiles2); + + return true; +} + +// Called from Mapbase manifests to flush +void ReloadResponseSystem() +{ + defaultresponsesytem.ReloadAllResponseSystems(); +} +#endif + static short RESPONSESYSTEM_SAVE_RESTORE_VERSION = 1; // note: this won't save/restore settings from instanced response systems. Could add that with a CDefSaveRestoreOps implementation if needed @@ -3103,6 +3595,34 @@ public: pSave->EndBlock(); } + +#ifdef MAPBASE + // Enhanced Response System save/restore + int count2 = 0; + if (rr_enhanced_saverestore.GetBool()) + { + // Rule state save/load + count2 = rs.m_Rules.Count(); + pSave->WriteInt( &count2 ); + for ( int i = 0; i < count2; ++i ) + { + pSave->StartBlock( "Rule" ); + + pSave->WriteString( rs.m_Rules.GetElementName( i ) ); + const Rule *rule = &rs.m_Rules[ i ]; + + bool bEnabled = rule->m_bEnabled; + pSave->WriteBool( &bEnabled ); + + pSave->EndBlock(); + } + } + else + { + // Indicate this isn't using enhanced save/restore + pSave->WriteInt( &count2 ); + } +#endif } void Restore( IRestore *pRestore, bool createPlayers ) @@ -3166,6 +3686,34 @@ public: pRestore->EndBlock(); } + +#ifdef MAPBASE + // Enhanced Response System save/restore + count = pRestore->ReadInt(); + for ( int i = 0; i < count; ++i ) + { + char szRuleBlockName[SIZE_BLOCK_NAME_BUF]; + pRestore->StartBlock( szRuleBlockName ); + if ( !Q_stricmp( szRuleBlockName, "Rule" ) ) + { + char groupname[ 256 ]; + pRestore->ReadString( groupname, sizeof( groupname ), 0 ); + + // Try and find it + int idx = rs.m_Rules.Find( groupname ); + if ( idx != rs.m_Rules.InvalidIndex() ) + { + Rule *rule = &rs.m_Rules[ idx ]; + + bool bEnabled; + pRestore->ReadBool( &bEnabled ); + rule->m_bEnabled = bEnabled; + } + } + + pRestore->EndBlock(); + } +#endif } private: diff --git a/mp/src/game/server/BasePropDoor.h b/mp/src/game/server/BasePropDoor.h index 1bfdc406..d96c75c5 100644 --- a/mp/src/game/server/BasePropDoor.h +++ b/mp/src/game/server/BasePropDoor.h @@ -75,6 +75,10 @@ public: virtual float GetOpenInterval(void) = 0; // } +#ifdef MAPBASE + virtual bool PassesDoorFilter(CBaseEntity *pEntity) { return true; } +#endif + protected: enum DoorState_t @@ -163,6 +167,10 @@ private: void InputOpenAwayFrom(inputdata_t &inputdata); void InputToggle(inputdata_t &inputdata); void InputUnlock(inputdata_t &inputdata); +#ifdef MAPBASE + void InputAllowPlayerUse(inputdata_t &inputdata); + void InputDisallowPlayerUse(inputdata_t &inputdata); +#endif void SetDoorBlocker( CBaseEntity *pBlocker ); diff --git a/mp/src/game/server/CRagdollMagnet.cpp b/mp/src/game/server/CRagdollMagnet.cpp index 454f7804..fc17cc9a 100644 --- a/mp/src/game/server/CRagdollMagnet.cpp +++ b/mp/src/game/server/CRagdollMagnet.cpp @@ -20,10 +20,17 @@ BEGIN_DATADESC( CRagdollMagnet ) DEFINE_KEYFIELD( m_force, FIELD_FLOAT, "force" ), DEFINE_KEYFIELD( m_axis, FIELD_VECTOR, "axis" ), DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_BoneTarget, FIELD_STRING, "BoneTarget" ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnUsed, "OnUsed" ), +#endif + END_DATADESC() //----------------------------------------------------------------------------- @@ -111,20 +118,54 @@ CRagdollMagnet *CRagdollMagnet::FindBestMagnet( CBaseEntity *pNPC ) // // NOTE: This function assumes pNPC is within this magnet's radius. //----------------------------------------------------------------------------- +#ifdef MAPBASE +Vector CRagdollMagnet::GetForceVector( CBaseEntity *pNPC, int *pBone ) +#else Vector CRagdollMagnet::GetForceVector( CBaseEntity *pNPC ) +#endif { Vector vecForceToApply; +#ifdef MAPBASE + Vector vecNPCPos = pNPC->WorldSpaceCenter(); + + if (pBone) + { + CBaseAnimating *pAnimating = pNPC->GetBaseAnimating(); + Assert( pAnimating != NULL ); + + const char *szBoneTarget = BoneTarget(); + Assert( szBoneTarget != NULL ); + + int iBone = pAnimating->LookupBone( szBoneTarget ); + + if (iBone != -1) + { + matrix3x4_t bonetoworld; + pAnimating->GetBoneTransform( iBone, bonetoworld ); + MatrixPosition( bonetoworld, vecNPCPos ); + *pBone = iBone; + } + } +#endif + if( IsBarMagnet() ) { CPlane axis; Vector vecForceDir; Vector vecClosest; +#ifdef MAPBASE + CalcClosestPointOnLineSegment( vecNPCPos, GetAbsOrigin(), m_axis, vecClosest, NULL ); + + vecForceDir = (vecClosest - vecNPCPos ); + VectorNormalize( vecForceDir ); +#else CalcClosestPointOnLineSegment( pNPC->WorldSpaceCenter(), GetAbsOrigin(), m_axis, vecClosest, NULL ); vecForceDir = (vecClosest - pNPC->WorldSpaceCenter() ); VectorNormalize( vecForceDir ); +#endif vecForceToApply = vecForceDir * m_force; } @@ -132,7 +173,11 @@ Vector CRagdollMagnet::GetForceVector( CBaseEntity *pNPC ) { Vector vecForce; +#ifdef MAPBASE + vecForce = GetAbsOrigin() - vecNPCPos; +#else vecForce = GetAbsOrigin() - pNPC->WorldSpaceCenter(); +#endif VectorNormalize( vecForce ); vecForceToApply = vecForce * m_force; diff --git a/mp/src/game/server/CRagdollMagnet.h b/mp/src/game/server/CRagdollMagnet.h index b2211724..52d3f57d 100644 --- a/mp/src/game/server/CRagdollMagnet.h +++ b/mp/src/game/server/CRagdollMagnet.h @@ -18,7 +18,11 @@ public: DECLARE_CLASS( CRagdollMagnet, CPointEntity ); DECLARE_DATADESC(); +#ifdef MAPBASE + Vector GetForceVector( CBaseEntity *pNPC, int *pBone = NULL ); +#else Vector GetForceVector( CBaseEntity *pNPC ); +#endif float GetRadius( void ) { return m_radius; } Vector GetAxisVector( void ) { return m_axis - GetAbsOrigin(); } float DistToPoint( const Vector &vecPoint ); @@ -35,11 +39,20 @@ public: void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + const char *BoneTarget() { return STRING(m_BoneTarget); } + + COutputVector m_OnUsed; +#endif + private: bool m_bDisabled; float m_radius; float m_force; Vector m_axis; +#ifdef MAPBASE + string_t m_BoneTarget; +#endif }; #endif //CRAGDOLLMAGNET_H \ No newline at end of file diff --git a/mp/src/game/server/CommentarySystem.cpp b/mp/src/game/server/CommentarySystem.cpp index 677b4d84..37fe0c86 100644 --- a/mp/src/game/server/CommentarySystem.cpp +++ b/mp/src/game/server/CommentarySystem.cpp @@ -22,6 +22,9 @@ #include "gamestats.h" #include "ai_basenpc.h" #include "Sprite.h" +#ifdef MAPBASE +#include "mapbase/SystemConvarMod.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -42,6 +45,7 @@ enum teleport_stages_t TELEPORT_FADEIN, }; +#ifndef MAPBASE // This has been moved to mapbase/SystemConvarMod.h // Convar restoration save/restore #define MAX_MODIFIED_CONVAR_STRING 128 struct modifiedconvars_t @@ -52,6 +56,7 @@ struct modifiedconvars_t char pszCurrentValue[MAX_MODIFIED_CONVAR_STRING]; char pszOrgValue[MAX_MODIFIED_CONVAR_STRING]; }; +#endif bool g_bInCommentaryMode = false; bool IsInCommentaryMode( void ) @@ -663,6 +668,10 @@ public: Msg( "Commentary: Could not find commentary data file '%s'. \n", szFullName ); } +#ifdef MAPBASE // VDC Memory Leak Fixes + pkvFile->deleteThis(); +#endif + engine->LockNetworkStringTables( oldLock ); } @@ -821,11 +830,13 @@ BEGIN_DATADESC_NO_BASE( CCommentarySystem ) DEFINE_FIELD( m_iCommentaryNodeCount, FIELD_INTEGER ), END_DATADESC() +#ifndef MAPBASE // This has been moved to mapbase/SystemConvarMod.h BEGIN_SIMPLE_DATADESC( modifiedconvars_t ) DEFINE_ARRAY( pszConvar, FIELD_CHARACTER, MAX_MODIFIED_CONVAR_STRING ), DEFINE_ARRAY( pszCurrentValue, FIELD_CHARACTER, MAX_MODIFIED_CONVAR_STRING ), DEFINE_ARRAY( pszOrgValue, FIELD_CHARACTER, MAX_MODIFIED_CONVAR_STRING ), END_DATADESC() +#endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/EnvBeam.cpp b/mp/src/game/server/EnvBeam.cpp index b4308811..c74df00d 100644 --- a/mp/src/game/server/EnvBeam.cpp +++ b/mp/src/game/server/EnvBeam.cpp @@ -48,6 +48,11 @@ public: void InputTurnOff( inputdata_t &inputdata ); void InputToggle( inputdata_t &inputdata ); void InputStrikeOnce( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputAmplitude( inputdata_t &inputdata ); + void InputSetStartEntity( inputdata_t &inputdata ) { m_iszStartEntity = inputdata.value.StringID(); BeamUpdateVars(); } + void InputSetEndEntity( inputdata_t &inputdata ) { m_iszEndEntity = inputdata.value.StringID(); BeamUpdateVars(); } +#endif void TurnOn( void ); void TurnOff( void ); @@ -123,6 +128,11 @@ BEGIN_DATADESC( CEnvBeam ) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), DEFINE_INPUTFUNC( FIELD_VOID, "StrikeOnce", InputStrikeOnce ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "Amplitude", InputAmplitude ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetStartEntity", InputSetStartEntity ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetEndEntity", InputSetEndEntity ), +#endif DEFINE_OUTPUT( m_OnTouchedByEntity, "OnTouchedByEntity" ), @@ -287,6 +297,17 @@ void CEnvBeam::InputStrikeOnce( inputdata_t &inputdata ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for amplitude +//----------------------------------------------------------------------------- +void CEnvBeam::InputAmplitude( inputdata_t &inputdata ) +{ + m_noiseAmplitude = inputdata.value.Float(); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Turns the lightning on. If it is set for interval refiring, it will // begin doing so. If it is set to be continually on, it will do so. @@ -383,11 +404,53 @@ void CEnvBeam::Strike( void ) m_speed = clamp( (int) m_speed, 0, (int) MAX_BEAM_SCROLLSPEED ); +#ifdef MAPBASE + bool pointStart = IsStaticPointEntity( pStart ); + bool pointEnd = IsStaticPointEntity( pEnd ); +#else int pointStart = IsStaticPointEntity( pStart ); int pointEnd = IsStaticPointEntity( pEnd ); +#endif if ( pointStart || pointEnd ) { +#ifdef MAPBASE + if ( m_spawnflags & SF_BEAM_RING ) + { + te->BeamRing( filter, 0.0, + pStart->entindex(), + pEnd->entindex(), + m_spriteTexture, + 0, // No halo + m_frameStart, + (int)m_flFrameRate, + m_life, + m_boltWidth, + 0, // No spread + m_noiseAmplitude, + m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a, + m_speed ); + } + else + { + te->BeamEntPoint( filter, 0.0, + pStart->entindex(), + &pStart->GetAbsOrigin(), + pEnd->entindex(), + &pEnd->GetAbsOrigin(), + m_spriteTexture, + 0, // No halo + m_frameStart, + (int)m_flFrameRate, + m_life, + m_boltWidth, + m_boltWidth, // End width + 0, // No fade + m_noiseAmplitude, + m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a, + m_speed ); + } +#else if ( m_spawnflags & SF_BEAM_RING ) { // don't work @@ -410,6 +473,7 @@ void CEnvBeam::Strike( void ) m_noiseAmplitude, m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a, m_speed ); +#endif } else { diff --git a/mp/src/game/server/EnvFade.cpp b/mp/src/game/server/EnvFade.cpp index fa0185d5..8b6c58f2 100644 --- a/mp/src/game/server/EnvFade.cpp +++ b/mp/src/game/server/EnvFade.cpp @@ -58,6 +58,9 @@ END_DATADESC() #define SF_FADE_MODULATE 0x0002 // Modulate, don't blend #define SF_FADE_ONLYONE 0x0004 #define SF_FADE_STAYOUT 0x0008 +#ifdef MAPBASE +#define SF_FADE_DONT_PURGE 0x0016 +#endif //----------------------------------------------------------------------------- // Purpose: @@ -93,6 +96,13 @@ void CEnvFade::InputFade( inputdata_t &inputdata ) fadeFlags |= FFADE_STAYOUT; } +#ifdef MAPBASE + if ( !HasSpawnFlags(SF_FADE_DONT_PURGE) ) + { + fadeFlags |= FFADE_PURGE; + } +#endif + if ( m_spawnflags & SF_FADE_ONLYONE ) { if ( inputdata.pActivator && inputdata.pActivator->IsNetClient() ) @@ -102,7 +112,11 @@ void CEnvFade::InputFade( inputdata_t &inputdata ) } else { +#ifdef MAPBASE + UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), fadeFlags ); +#else UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), fadeFlags|FFADE_PURGE ); +#endif } m_OnBeginFade.FireOutput( inputdata.pActivator, this ); diff --git a/mp/src/game/server/EnvHudHint.cpp b/mp/src/game/server/EnvHudHint.cpp index 858fc7b9..2cb38eab 100644 --- a/mp/src/game/server/EnvHudHint.cpp +++ b/mp/src/game/server/EnvHudHint.cpp @@ -32,6 +32,9 @@ private: void InputShowHudHint( inputdata_t &inputdata ); void InputHideHudHint( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetHudHint( inputdata_t &inputdata ); +#endif string_t m_iszMessage; DECLARE_DATADESC(); }; @@ -43,6 +46,9 @@ BEGIN_DATADESC( CEnvHudHint ) DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ), DEFINE_INPUTFUNC( FIELD_VOID, "ShowHudHint", InputShowHudHint ), DEFINE_INPUTFUNC( FIELD_VOID, "HideHudHint", InputHideHudHint ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetHudHint", InputSetHudHint ), +#endif END_DATADESC() @@ -140,3 +146,12 @@ void CEnvHudHint::InputHideHudHint( inputdata_t &inputdata ) MessageEnd(); } } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvHudHint::InputSetHudHint( inputdata_t &inputdata ) +{ + m_iszMessage = inputdata.value.StringID(); +} +#endif diff --git a/mp/src/game/server/EnvLaser.cpp b/mp/src/game/server/EnvLaser.cpp index 0e603db6..3db94e7c 100644 --- a/mp/src/game/server/EnvLaser.cpp +++ b/mp/src/game/server/EnvLaser.cpp @@ -31,6 +31,10 @@ BEGIN_DATADESC( CEnvLaser ) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnTouchedByEntity, "OnTouchedByEntity" ), +#endif + END_DATADESC() @@ -221,6 +225,12 @@ void CEnvLaser::FireAtPoint( trace_t &tr ) // Apply damage and do sparks every 1/10th of a second. if ( gpGlobals->curtime >= m_flFireTime + 0.1 ) { +#ifdef MAPBASE + if ( tr.fraction != 1.0 && tr.m_pEnt && !tr.m_pEnt->IsWorld() ) + { + m_OnTouchedByEntity.FireOutput( tr.m_pEnt, this ); + } +#endif BeamDamage( &tr ); DoSparks( GetAbsStartPos(), tr.endpos ); } diff --git a/mp/src/game/server/EnvLaser.h b/mp/src/game/server/EnvLaser.h index 79165042..fc740442 100644 --- a/mp/src/game/server/EnvLaser.h +++ b/mp/src/game/server/EnvLaser.h @@ -37,6 +37,9 @@ public: void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); void InputToggle( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetTarget( inputdata_t &inputdata ) { m_iszLaserTarget = inputdata.value.StringID(); } +#endif DECLARE_DATADESC(); @@ -45,6 +48,10 @@ public: string_t m_iszSpriteName; Vector m_firePosition; +#ifdef MAPBASE + COutputEvent m_OnTouchedByEntity; +#endif + float m_flStartFrame; }; diff --git a/mp/src/game/server/EnvMessage.cpp b/mp/src/game/server/EnvMessage.cpp index a22dfc11..3f47c2ea 100644 --- a/mp/src/game/server/EnvMessage.cpp +++ b/mp/src/game/server/EnvMessage.cpp @@ -168,6 +168,11 @@ private: bool m_bRolledOutroCredits; float m_flLogoLength; + +#ifdef MAPBASE + // Custom credits.txt, defaults to that + string_t m_iszCreditsFile; +#endif }; LINK_ENTITY_TO_CLASS( env_credits, CCredits ); @@ -179,6 +184,10 @@ BEGIN_DATADESC( CCredits ) DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLogoLength", InputSetLogoLength ), DEFINE_OUTPUT( m_OnCreditsDone, "OnCreditsDone"), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszCreditsFile, FIELD_STRING, "CreditsFile" ), +#endif + DEFINE_FIELD( m_bRolledOutroCredits, FIELD_BOOLEAN ), DEFINE_FIELD( m_flLogoLength, FIELD_FLOAT ) END_DATADESC() @@ -226,6 +235,9 @@ void CCredits::RollOutroCredits() UserMessageBegin( user, "CreditsMsg" ); WRITE_BYTE( 3 ); +#ifdef MAPBASE + WRITE_STRING( STRING(m_iszCreditsFile) ); +#endif MessageEnd(); } @@ -250,12 +262,18 @@ void CCredits::InputShowLogo( inputdata_t &inputdata ) { UserMessageBegin( user, "LogoTimeMsg" ); WRITE_FLOAT( m_flLogoLength ); +#ifdef MAPBASE + WRITE_STRING( STRING(m_iszCreditsFile) ); +#endif MessageEnd(); } else { UserMessageBegin( user, "CreditsMsg" ); WRITE_BYTE( 1 ); +#ifdef MAPBASE + WRITE_STRING( STRING(m_iszCreditsFile) ); +#endif MessageEnd(); } } @@ -274,5 +292,8 @@ void CCredits::InputRollCredits( inputdata_t &inputdata ) UserMessageBegin( user, "CreditsMsg" ); WRITE_BYTE( 2 ); +#ifdef MAPBASE + WRITE_STRING( STRING(m_iszCreditsFile) ); +#endif MessageEnd(); } diff --git a/mp/src/game/server/RagdollBoogie.cpp b/mp/src/game/server/RagdollBoogie.cpp index 6344b74a..0d54418d 100644 --- a/mp/src/game/server/RagdollBoogie.cpp +++ b/mp/src/game/server/RagdollBoogie.cpp @@ -15,6 +15,10 @@ #include "effect_dispatch_data.h" #include "te_effect_dispatch.h" #include "IEffects.h" +#ifdef MAPBASE +#include "saverestore_utlvector.h" +#include "interval.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -37,6 +41,10 @@ BEGIN_DATADESC( CRagdollBoogie ) // Think this should be handled by StartTouch/etc. // DEFINE_FIELD( m_nSuppressionCount, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_FIELD( m_vecColor, FIELD_VECTOR ), +#endif + DEFINE_FUNCTION( BoogieThink ), DEFINE_FUNCTION( ZapThink ), @@ -50,7 +58,11 @@ LINK_ENTITY_TO_CLASS( env_ragdoll_boogie, CRagdollBoogie ); // Input : pTarget - //----------------------------------------------------------------------------- CRagdollBoogie *CRagdollBoogie::Create( CBaseEntity *pTarget, float flMagnitude, +#ifdef MAPBASE + float flStartTime, float flLengthTime, int nSpawnFlags, const Vector *vecColor ) +#else float flStartTime, float flLengthTime, int nSpawnFlags ) +#endif { CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pTarget ); if ( !pRagdoll ) @@ -64,6 +76,10 @@ CRagdollBoogie *CRagdollBoogie::Create( CBaseEntity *pTarget, float flMagnitude, pBoogie->AttachToEntity( pTarget ); pBoogie->SetBoogieTime( flStartTime, flLengthTime ); pBoogie->SetMagnitude( flMagnitude ); +#ifdef MAPBASE + if (vecColor != NULL) + pBoogie->SetColor( *vecColor ); +#endif pBoogie->Spawn(); return pBoogie; } @@ -115,6 +131,13 @@ void CRagdollBoogie::ZapThink() data.m_nEntIndex = GetMoveParent()->entindex(); data.m_flMagnitude = 4; data.m_flScale = HasSpawnFlags(SF_RAGDOLL_BOOGIE_ELECTRICAL_NARROW_BEAM) ? 1.0f : 2.0f; +#ifdef MAPBASE + if (!m_vecColor.IsZero()) + { + data.m_bCustomColors = true; + data.m_CustomColors.m_vecColor1 = m_vecColor; + } +#endif DispatchEffect( "TeslaHitboxes", data ); } @@ -266,3 +289,190 @@ void CRagdollBoogie::BoogieThink( void ) SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.2f ) ); } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Allows mappers to control ragdoll dancing +//----------------------------------------------------------------------------- +class CPointRagdollBoogie : public CBaseEntity +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CPointRagdollBoogie, CBaseEntity ); + +public: + bool ApplyBoogie(CBaseEntity *pTarget, CBaseEntity *pActivator); + + void InputActivate( inputdata_t &inputdata ); + void InputDeactivate( inputdata_t &inputdata ); + void InputBoogieTarget( inputdata_t &inputdata ); + void InputSetZapColor( inputdata_t &inputdata ); + + bool KeyValue( const char *szKeyName, const char *szValue ); + +private: + float m_flStartTime; + interval_t m_BoogieLength; + float m_flMagnitude; + + Vector m_vecZapColor; + + // This allows us to change or remove active boogies later. + CUtlVector> m_Boogies; +}; + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- +BEGIN_DATADESC( CPointRagdollBoogie ) + + DEFINE_KEYFIELD( m_flStartTime, FIELD_FLOAT, "StartTime" ), + DEFINE_KEYFIELD( m_BoogieLength, FIELD_INTERVAL, "BoogieLength" ), + DEFINE_KEYFIELD( m_flMagnitude, FIELD_FLOAT, "Magnitude" ), + + DEFINE_KEYFIELD( m_vecZapColor, FIELD_VECTOR, "ZapColor" ), + + // Think this should be handled by StartTouch/etc. +// DEFINE_FIELD( m_nSuppressionCount, FIELD_INTEGER ), + + DEFINE_UTLVECTOR( m_Boogies, FIELD_EHANDLE ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), + DEFINE_INPUTFUNC( FIELD_STRING, "BoogieTarget", InputBoogieTarget ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetZapColor", InputSetZapColor ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( point_ragdollboogie, CPointRagdollBoogie ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +bool CPointRagdollBoogie::ApplyBoogie( CBaseEntity *pTarget, CBaseEntity *pActivator ) +{ + if (dynamic_cast(pTarget)) + { + m_Boogies.AddToTail(CRagdollBoogie::Create(pTarget, m_flMagnitude, gpGlobals->curtime + m_flStartTime, RandomInterval(m_BoogieLength), GetSpawnFlags(), &m_vecZapColor)); + } + else if (pTarget->MyCombatCharacterPointer()) + { + // Basically CBaseCombatCharacter::BecomeRagdollBoogie(), but adjusted to our needs + CTakeDamageInfo info(this, pActivator, 1.0f, DMG_GENERIC); + + CBaseEntity *pRagdoll = CreateServerRagdoll(pTarget->MyCombatCharacterPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true); + + pRagdoll->SetCollisionBounds(CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs()); + + m_Boogies.AddToTail(CRagdollBoogie::Create(pRagdoll, m_flMagnitude, gpGlobals->curtime + m_flStartTime, RandomInterval(m_BoogieLength), GetSpawnFlags(), &m_vecZapColor)); + + CTakeDamageInfo ragdollInfo(this, pActivator, 10000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL); + ragdollInfo.SetDamagePosition(WorldSpaceCenter()); + ragdollInfo.SetDamageForce(Vector(0, 0, 1)); + ragdollInfo.SetForceFriendlyFire(true); + pTarget->TakeDamage(ragdollInfo); + } + else + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPointRagdollBoogie::InputActivate( inputdata_t &inputdata ) +{ + CBaseEntity *pEnt = gEntList.FindEntityByName(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + while (pEnt) + { + ApplyBoogie(pEnt, inputdata.pActivator); + + pEnt = gEntList.FindEntityByName(pEnt, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPointRagdollBoogie::InputDeactivate( inputdata_t &inputdata ) +{ + if (m_Boogies.Count() == 0) + return; + + for (int i = 0; i < m_Boogies.Count(); i++) + { + UTIL_Remove(m_Boogies[i]); + } + + m_Boogies.Purge(); + + //m_Boogies.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPointRagdollBoogie::InputBoogieTarget( inputdata_t &inputdata ) +{ + CBaseEntity *pEnt = gEntList.FindEntityByName(NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller); + while (pEnt) + { + if (!ApplyBoogie(pEnt, inputdata.pActivator)) + { + Warning("%s was unable to apply ragdoll boogie to %s, classname %s.\n", GetDebugName(), pEnt->GetDebugName(), pEnt->GetClassname()); + } + + pEnt = gEntList.FindEntityByName(pEnt, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPointRagdollBoogie::InputSetZapColor( inputdata_t &inputdata ) +{ + inputdata.value.Vector3D( m_vecZapColor ); + if (!m_vecZapColor.IsZero()) + { + // Turn into ratios of 255 + m_vecZapColor /= 255.0f; + } + + // Apply to existing boogies + for (int i = 0; i < m_Boogies.Count(); i++) + { + if (m_Boogies[i]) + { + m_Boogies[i]->SetColor( m_vecZapColor ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CPointRagdollBoogie::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "ZapColor" ) ) + { + UTIL_StringToVector(m_vecZapColor.Base(), szValue); + if (!m_vecZapColor.IsZero()) + { + // Turn into ratios of 255 + m_vecZapColor /= 255.0f; + } + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} +#endif diff --git a/mp/src/game/server/RagdollBoogie.h b/mp/src/game/server/RagdollBoogie.h index cb52c955..8ca99e81 100644 --- a/mp/src/game/server/RagdollBoogie.h +++ b/mp/src/game/server/RagdollBoogie.h @@ -28,10 +28,18 @@ class CRagdollBoogie : public CBaseEntity DECLARE_CLASS( CRagdollBoogie, CBaseEntity ); public: +#ifdef MAPBASE + static CRagdollBoogie *Create( CBaseEntity *pTarget, float flMagnitude, float flStartTime, float flLengthTime = 0.0f, int nSpawnFlags = 0, const Vector *vecColor = NULL ); +#else static CRagdollBoogie *Create( CBaseEntity *pTarget, float flMagnitude, float flStartTime, float flLengthTime = 0.0f, int nSpawnFlags = 0 ); +#endif static void IncrementSuppressionCount( CBaseEntity *pTarget ); static void DecrementSuppressionCount( CBaseEntity *pTarget ); +#ifdef MAPBASE + void SetColor( const Vector &vecColor ) { m_vecColor = vecColor; } +#endif + void Spawn(); private: @@ -45,6 +53,10 @@ private: float m_flBoogieLength; float m_flMagnitude; int m_nSuppressionCount; + +#ifdef MAPBASE + Vector m_vecColor = Vector(1, 1, 1); +#endif }; #endif // RAGDOLLBOOGIE_H diff --git a/mp/src/game/server/SkyCamera.cpp b/mp/src/game/server/SkyCamera.cpp index 294c9c25..be9716d4 100644 --- a/mp/src/game/server/SkyCamera.cpp +++ b/mp/src/game/server/SkyCamera.cpp @@ -15,13 +15,24 @@ // automatically hooks in the system's callbacks CEntityClassList g_SkyList; template <> CSkyCamera *CEntityClassList::m_pClassList = NULL; +#ifdef MAPBASE +CHandle g_hActiveSkybox = NULL; +#endif //----------------------------------------------------------------------------- // Retrives the current skycamera //----------------------------------------------------------------------------- CSkyCamera* GetCurrentSkyCamera() { +#ifdef MAPBASE + if ( g_hActiveSkybox.Get() == NULL ) + { + g_hActiveSkybox = GetSkyCameraList(); + } + return g_hActiveSkybox.Get(); +#else return g_SkyList.m_pClassList; +#endif } CSkyCamera* GetSkyCameraList() @@ -38,6 +49,12 @@ BEGIN_DATADESC( CSkyCamera ) DEFINE_KEYFIELD( m_skyboxData.scale, FIELD_INTEGER, "scale" ), DEFINE_FIELD( m_skyboxData.origin, FIELD_VECTOR ), DEFINE_FIELD( m_skyboxData.area, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_FIELD( m_skyboxData.angles, FIELD_VECTOR ), + DEFINE_FIELD( m_skyboxData.skycamera, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_skyboxData.skycolor, FIELD_COLOR32, "skycolor" ), + DEFINE_KEYFIELD( m_bUseAnglesForSky, FIELD_BOOLEAN, "use_angles_for_sky" ), +#endif // Quiet down classcheck // DEFINE_FIELD( m_skyboxData, sky3dparams_t ), @@ -55,6 +72,36 @@ BEGIN_DATADESC( CSkyCamera ) DEFINE_KEYFIELD( m_skyboxData.fog.start, FIELD_FLOAT, "fogstart" ), DEFINE_KEYFIELD( m_skyboxData.fog.end, FIELD_FLOAT, "fogend" ), DEFINE_KEYFIELD( m_skyboxData.fog.maxdensity, FIELD_FLOAT, "fogmaxdensity" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_skyboxData.fog.farz, FIELD_FLOAT, "farz" ), +#endif + +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "ForceUpdate", InputForceUpdate ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartUpdating", InputStartUpdating ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopUpdating", InputStopUpdating ), + + DEFINE_INPUTFUNC( FIELD_VOID, "ActivateSkybox", InputActivateSkybox ), + DEFINE_INPUTFUNC( FIELD_VOID, "DeactivateSkybox", InputDeactivateSkybox ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFogStartDist", InputSetFogStartDist ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFogEndDist", InputSetFogEndDist ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFogMaxDensity", InputSetFogMaxDensity ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOnFog", InputTurnOnFog ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOffFog", InputTurnOffFog ), + DEFINE_INPUTFUNC( FIELD_COLOR32, "SetFogColor", InputSetFogColor ), + DEFINE_INPUTFUNC( FIELD_COLOR32, "SetFogColorSecondary", InputSetFogColorSecondary ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "CopyFogController", InputCopyFogController ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "CopyFogControllerWithScale", InputCopyFogControllerWithScale ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFarZ", InputSetFarZ ), + + DEFINE_INPUTFUNC( FIELD_COLOR32, "SetSkyColor", InputSetSkyColor ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetScale", InputSetScale ), + + DEFINE_THINKFUNC( UpdateThink ), +#endif END_DATADESC() @@ -93,6 +140,9 @@ CSkyCamera::CSkyCamera() { g_SkyList.Insert( this ); m_skyboxData.fog.maxdensity = 1.0f; +#ifdef MAPBASE + m_skyboxData.skycolor.Init(0, 0, 0, 0); +#endif } CSkyCamera::~CSkyCamera() @@ -102,7 +152,24 @@ CSkyCamera::~CSkyCamera() void CSkyCamera::Spawn( void ) { +#ifdef MAPBASE + if (HasSpawnFlags(SF_SKY_MASTER)) + g_hActiveSkybox = this; + + if (HasSpawnFlags(SF_SKY_START_UPDATING)) + { + SetCameraEntityMode(); + + SetThink( &CSkyCamera::UpdateThink ); + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); + } + else + { + SetCameraPositionMode(); + } +#else m_skyboxData.origin = GetLocalOrigin(); +#endif m_skyboxData.area = engine->GetArea( m_skyboxData.origin ); Precache(); @@ -145,3 +212,251 @@ void CSkyCamera::Activate( ) } #endif } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CSkyCamera::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ) +{ + if (!BaseClass::AcceptInput( szInputName, pActivator, pCaller, Value, outputID )) + return false; + + if (g_hActiveSkybox == this) + { + // Most inputs require an update + DoUpdate( true ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSkyCamera::SetCameraEntityMode() +{ + m_skyboxData.skycamera = this; + + // Ensure the viewrender knows whether this should be using angles + if (m_bUseAnglesForSky) + m_skyboxData.angles.SetX( 1 ); + else + m_skyboxData.angles.SetX( 0 ); +} + +void CSkyCamera::SetCameraPositionMode() +{ + // Must be absolute now that the sky_camera can be parented + m_skyboxData.skycamera = NULL; + m_skyboxData.origin = GetAbsOrigin(); + if (m_bUseAnglesForSky) + m_skyboxData.angles = GetAbsAngles(); +} + +//----------------------------------------------------------------------------- +// Purpose: Update sky position mid-game +//----------------------------------------------------------------------------- +bool CSkyCamera::DoUpdate( bool bUpdateData ) +{ + // Now that sky camera updating uses an entity handle directly transmitted to the client, + // this thinking is only used to update area and other parameters + + // Getting into another area is unlikely, but if it's not expensive, I guess it's okay. + int area = engine->GetArea( GetAbsOrigin() ); + if (m_skyboxData.area != area) + { + m_skyboxData.area = area; + bUpdateData = true; + } + + if ( m_bUseAngles ) + { + Vector fogForward; + AngleVectors( GetAbsAngles(), &fogForward ); + fogForward *= -1.0f; + + if ( m_skyboxData.fog.dirPrimary.Get() != fogForward ) + { + m_skyboxData.fog.dirPrimary = fogForward; + bUpdateData = true; + } + } + + if (bUpdateData) + { + // Updates client data, this completely ignores m_pOldSkyCamera + CBasePlayer *pPlayer = NULL; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + pPlayer = UTIL_PlayerByIndex(i); + if (pPlayer) + pPlayer->m_Local.m_skybox3d.CopyFrom(m_skyboxData); + } + } + + // Needed for entity interpolation + SetSimulationTime( gpGlobals->curtime ); + + return bUpdateData; +} + +void CSkyCamera::UpdateThink() +{ + if (DoUpdate()) + { + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); + } + else + { + SetNextThink( gpGlobals->curtime + 0.2f ); + } +} + +void CSkyCamera::InputForceUpdate( inputdata_t &inputdata ) +{ + if (m_skyboxData.skycamera == NULL) + { + m_skyboxData.origin = GetAbsOrigin(); + if (m_bUseAnglesForSky) + m_skyboxData.angles = GetAbsAngles(); + } + + DoUpdate( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSkyCamera::InputStartUpdating( inputdata_t &inputdata ) +{ + if (GetCurrentSkyCamera() == this) + { + SetCameraEntityMode(); + DoUpdate( true ); + + SetThink( &CSkyCamera::UpdateThink ); + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); + } + + // If we become the current sky camera later, remember that we want to update + AddSpawnFlags( SF_SKY_START_UPDATING ); + + // Must update transmit state so we show up on the client + DispatchUpdateTransmitState(); +} + +void CSkyCamera::InputStopUpdating( inputdata_t &inputdata ) +{ + SetThink( NULL ); + SetNextThink( TICK_NEVER_THINK ); + RemoveSpawnFlags( SF_SKY_START_UPDATING ); + DispatchUpdateTransmitState(); + + SetCameraPositionMode(); + DoUpdate( true ); +} + +//----------------------------------------------------------------------------- +// Activate! +//----------------------------------------------------------------------------- +void CSkyCamera::InputActivateSkybox( inputdata_t &inputdata ) +{ + CSkyCamera *pActiveSky = GetCurrentSkyCamera(); + if (pActiveSky && pActiveSky->GetNextThink() != TICK_NEVER_THINK && pActiveSky != this) + { + // Deactivate that skybox + pActiveSky->SetThink( NULL ); + pActiveSky->SetNextThink( TICK_NEVER_THINK ); + } + + g_hActiveSkybox = this; + + if (HasSpawnFlags( SF_SKY_START_UPDATING )) + InputStartUpdating( inputdata ); +} + +//----------------------------------------------------------------------------- +// Deactivate! +//----------------------------------------------------------------------------- +void CSkyCamera::InputDeactivateSkybox( inputdata_t &inputdata ) +{ + if (GetCurrentSkyCamera() == this) + { + g_hActiveSkybox = NULL; + + // ClientData doesn't catch this immediately + CBasePlayer *pPlayer = NULL; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + pPlayer = UTIL_PlayerByIndex( i ); + if (pPlayer) + pPlayer->m_Local.m_skybox3d.area = 255; + } + } + + SetThink( NULL ); + SetNextThink( TICK_NEVER_THINK ); +} + +//------------------------------------------------------------------------------ +// Purpose: Input handlers for setting fog stuff. +//------------------------------------------------------------------------------ +void CSkyCamera::InputSetFogStartDist( inputdata_t &inputdata ) { m_skyboxData.fog.start = inputdata.value.Float(); } +void CSkyCamera::InputSetFogEndDist( inputdata_t &inputdata ) { m_skyboxData.fog.end = inputdata.value.Float(); } +void CSkyCamera::InputSetFogMaxDensity( inputdata_t &inputdata ) { m_skyboxData.fog.maxdensity = inputdata.value.Float(); } +void CSkyCamera::InputTurnOnFog( inputdata_t &inputdata ) { m_skyboxData.fog.enable = true; } +void CSkyCamera::InputTurnOffFog( inputdata_t &inputdata ) { m_skyboxData.fog.enable = false; } +void CSkyCamera::InputSetFogColor( inputdata_t &inputdata ) { m_skyboxData.fog.colorPrimary = inputdata.value.Color32(); } +void CSkyCamera::InputSetFogColorSecondary( inputdata_t &inputdata ) { m_skyboxData.fog.colorSecondary = inputdata.value.Color32(); } + +void CSkyCamera::InputSetFarZ( inputdata_t &inputdata ) { m_skyboxData.fog.farz = inputdata.value.Int(); } + +void CSkyCamera::InputCopyFogController( inputdata_t &inputdata ) +{ + CFogController *pFogController = dynamic_cast(inputdata.value.Entity().Get()); + if (!pFogController) + return; + + m_skyboxData.fog.dirPrimary = pFogController->m_fog.dirPrimary; + m_skyboxData.fog.colorPrimary = pFogController->m_fog.colorPrimary; + m_skyboxData.fog.colorSecondary = pFogController->m_fog.colorSecondary; + //m_skyboxData.fog.colorPrimaryLerpTo = pFogController->m_fog.colorPrimaryLerpTo; + //m_skyboxData.fog.colorSecondaryLerpTo = pFogController->m_fog.colorSecondaryLerpTo; + m_skyboxData.fog.start = pFogController->m_fog.start; + m_skyboxData.fog.end = pFogController->m_fog.end; + m_skyboxData.fog.farz = pFogController->m_fog.farz; + m_skyboxData.fog.maxdensity = pFogController->m_fog.maxdensity; + + //m_skyboxData.fog.startLerpTo = pFogController->m_fog.startLerpTo; + //m_skyboxData.fog.endLerpTo = pFogController->m_fog.endLerpTo; + //m_skyboxData.fog.lerptime = pFogController->m_fog.lerptime; + //m_skyboxData.fog.duration = pFogController->m_fog.duration; + //m_skyboxData.fog.enable = pFogController->m_fog.enable; + m_skyboxData.fog.blend = pFogController->m_fog.blend; +} + +void CSkyCamera::InputCopyFogControllerWithScale( inputdata_t &inputdata ) +{ + CFogController *pFogController = dynamic_cast(inputdata.value.Entity().Get()); + if (!pFogController) + return; + + m_skyboxData.fog.dirPrimary = pFogController->m_fog.dirPrimary; + m_skyboxData.fog.colorPrimary = pFogController->m_fog.colorPrimary; + m_skyboxData.fog.colorSecondary = pFogController->m_fog.colorSecondary; + //m_skyboxData.fog.colorPrimaryLerpTo = pFogController->m_fog.colorPrimaryLerpTo; + //m_skyboxData.fog.colorSecondaryLerpTo = pFogController->m_fog.colorSecondaryLerpTo; + m_skyboxData.fog.start = pFogController->m_fog.start * m_skyboxData.scale; + m_skyboxData.fog.end = pFogController->m_fog.end * m_skyboxData.scale; + m_skyboxData.fog.farz = pFogController->m_fog.farz != -1 ? (pFogController->m_fog.farz * m_skyboxData.scale) : pFogController->m_fog.farz; + m_skyboxData.fog.maxdensity = pFogController->m_fog.maxdensity; + + //m_skyboxData.fog.startLerpTo = pFogController->m_fog.startLerpTo; + //m_skyboxData.fog.endLerpTo = pFogController->m_fog.endLerpTo; + //m_skyboxData.fog.lerptime = pFogController->m_fog.lerptime; + //m_skyboxData.fog.duration = pFogController->m_fog.duration; + //m_skyboxData.fog.enable = pFogController->m_fog.enable; + m_skyboxData.fog.blend = pFogController->m_fog.blend; +} +#endif diff --git a/mp/src/game/server/SkyCamera.h b/mp/src/game/server/SkyCamera.h index 8032d8fd..a1936568 100644 --- a/mp/src/game/server/SkyCamera.h +++ b/mp/src/game/server/SkyCamera.h @@ -14,13 +14,30 @@ class CSkyCamera; +#ifdef MAPBASE +#define SF_SKY_MASTER (1 << 0) +#define SF_SKY_START_UPDATING (1 << 1) + +//============================================================================= +// +// Sky Camera Class +// Now derived directly from CBaseEntity for parenting and angles! (please don't break anything) +// +//============================================================================= +class CSkyCamera : public CBaseEntity +#else //============================================================================= // // Sky Camera Class // class CSkyCamera : public CLogicalEntity +#endif { +#ifdef MAPBASE + DECLARE_CLASS( CSkyCamera, CBaseEntity ); +#else DECLARE_CLASS( CSkyCamera, CLogicalEntity ); +#endif public: @@ -30,9 +47,48 @@ public: virtual void Spawn( void ); virtual void Activate(); +#ifdef MAPBASE + bool AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ); + + int UpdateTransmitState() { return HasSpawnFlags( SF_SKY_START_UPDATING ) ? SetTransmitState( FL_EDICT_ALWAYS ) : BaseClass::UpdateTransmitState(); } + + void SetCameraEntityMode(); + void SetCameraPositionMode(); + + bool DoUpdate( bool bUpdateData = false ); + void UpdateThink(); + + void InputForceUpdate( inputdata_t &inputdata ); + void InputStartUpdating( inputdata_t &inputdata ); + void InputStopUpdating( inputdata_t &inputdata ); + + void InputActivateSkybox( inputdata_t &inputdata ); + void InputDeactivateSkybox( inputdata_t &inputdata ); + + void InputSetFogStartDist( inputdata_t &data ); + void InputSetFogEndDist( inputdata_t &data ); + void InputTurnOnFog( inputdata_t &data ); + void InputTurnOffFog( inputdata_t &data ); + void InputSetFogColor( inputdata_t &data ); + void InputSetFogColorSecondary( inputdata_t &data ); + void InputSetFogMaxDensity( inputdata_t &inputdata ); + void InputCopyFogController( inputdata_t &inputdata ); + void InputCopyFogControllerWithScale( inputdata_t &inputdata ); + + void InputSetFarZ( inputdata_t &data ); + + void InputSetSkyColor( inputdata_t &inputdata ) { m_skyboxData.skycolor = inputdata.value.Color32(); } + + void InputSetScale( inputdata_t &inputdata ) { m_skyboxData.scale = inputdata.value.Int(); } +#endif + public: sky3dparams_t m_skyboxData; bool m_bUseAngles; +#ifdef MAPBASE + // Uses angles for actual skybox + bool m_bUseAnglesForSky; +#endif CSkyCamera *m_pNext; }; diff --git a/mp/src/game/server/TemplateEntities.cpp b/mp/src/game/server/TemplateEntities.cpp index a5092904..b2a54fd1 100644 --- a/mp/src/game/server/TemplateEntities.cpp +++ b/mp/src/game/server/TemplateEntities.cpp @@ -159,6 +159,70 @@ string_t Templates_FindByTargetName(const char *pszName) return NULL_STRING; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: A new version of name fixup which targets all instances of a name +// in a keyvalue, including output parameters. +//----------------------------------------------------------------------------- +void Templates_NewNameFixup( CUtlVector< grouptemplate_t > &GroupTemplates, int i, int iCount, CEntityMapData *mapData, CUtlDict< int, int > &KeyInstanceCount, char *keyName, char *value ) +{ + do + { + // Ignore targetnames + if ( !stricmp( keyName, "targetname" ) ) + continue; + + // Add to the count for this + int idx = KeyInstanceCount.Find( keyName ); + if ( idx == KeyInstanceCount.InvalidIndex() ) + { + idx = KeyInstanceCount.Insert( keyName, 0 ); + } + KeyInstanceCount[idx]++; + + // Loop through our group templates + for ( int iTName = 0; iTName < iCount; iTName++ ) + { + char *pName = GroupTemplates[iTName].pszName; + if (strstr( value, pName ) == NULL) + continue; + + if ( template_debug.GetInt() ) + { + Msg("Template Connection Found: Key %s (\"%s\") in entity named \"%s\"(%d) matches entity %d's targetname\n", keyName, value, GroupTemplates[i].pszName, i, iTName ); + } + + char newvalue[MAPKEY_MAXLENGTH]; + char fixedup[MAPKEY_MAXLENGTH]; + Q_strncpy( fixedup, pName, MAPKEY_MAXLENGTH ); + Q_strncat( fixedup, ENTITYIO_FIXUP_STRING, sizeof( fixedup ), COPY_ALL_CHARACTERS ); + + // Get the current key instance. (-1 because it's this one we're changing) + int nKeyInstance = KeyInstanceCount[idx] - 1; + + // Add our IO value to the targetname + V_StrSubst( value, pName, fixedup, newvalue, MAPKEY_MAXLENGTH ); + + if ( template_debug.GetInt() ) + { + Msg(" Fixed up value: Key %s with \"%s\" in entity named \"%s\"(%d) has become \"%s\"\n", keyName, value, GroupTemplates[i].pszName, i, newvalue ); + } + + mapData->SetValue( keyName, newvalue, nKeyInstance ); + Q_strncpy( value, newvalue, MAPKEY_MAXLENGTH ); + + // Remember we changed this targetname + GroupTemplates[iTName].bChangeTargetname = true; + + // Set both entity's flags telling them their template needs fixup when it's spawned + g_Templates[ GroupTemplates[i].iIndex ]->bNeedsEntityIOFixup = true; + g_Templates[ GroupTemplates[iTName].iIndex ]->bNeedsEntityIOFixup = true; + } + } + while ( mapData->GetNextKey(keyName, value) ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: A CPointTemplate has asked us to reconnect all the entity I/O links // inside it's templates. Go through the keys and add look for values @@ -208,6 +272,14 @@ void Templates_ReconnectIOForGroup( CPointTemplate *pGroup ) if ( !mapData->GetFirstKey(keyName, value) ) continue; +#ifdef MAPBASE + if ( pGroup->NameFixupExpanded() ) + { + Templates_NewNameFixup( GroupTemplates, i, iCount, mapData, KeyInstanceCount, keyName, value ); + continue; + } +#endif + do { // Ignore targetnames @@ -326,7 +398,9 @@ void Templates_StartUniqueInstance( void ) //----------------------------------------------------------------------------- char *Templates_GetEntityIOFixedMapData( int iIndex ) { +#ifndef MAPBASE // This code also runs when the point_template's script scope is active Assert( Templates_IndexRequiresEntityIOFixup( iIndex ) ); +#endif // First time through? if ( !g_Templates[iIndex]->pszFixedMapData ) diff --git a/mp/src/game/server/ai_activity.cpp b/mp/src/game/server/ai_activity.cpp index 3f5b5b49..74c312f7 100644 --- a/mp/src/game/server/ai_activity.cpp +++ b/mp/src/game/server/ai_activity.cpp @@ -2169,51 +2169,6 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_SPELL_VM_ARM ); ADD_ACTIVITY_TO_SR( ACT_SPELL_VM_FIRE ); - ADD_ACTIVITY_TO_SR( ACT_BREADSAPPER_VM_DRAW ); - ADD_ACTIVITY_TO_SR( ACT_BREADSAPPER_VM_IDLE ); - - ADD_ACTIVITY_TO_SR( ACT_BREADGLOVES_VM_HITLEFT ); - ADD_ACTIVITY_TO_SR( ACT_BREADGLOVES_VM_HITRIGHT ); - ADD_ACTIVITY_TO_SR( ACT_BREADGLOVES_VM_SWINGHARD ); - ADD_ACTIVITY_TO_SR( ACT_BREADGLOVES_VM_IDLE ); - ADD_ACTIVITY_TO_SR( ACT_BREADGLOVES_VM_DRAW ); - - ADD_ACTIVITY_TO_SR( ACT_BREADMONSTER_GLOVES_IDLE ); - ADD_ACTIVITY_TO_SR( ACT_BREADMONSTER_GLOVES_HITRIGHT ); - ADD_ACTIVITY_TO_SR( ACT_BREADMONSTER_GLOVES_HITUP ); - - ADD_ACTIVITY_TO_SR( ACT_BREADMONSTER_VM_DRAW ); - ADD_ACTIVITY_TO_SR( ACT_BREADMONSTER_VM_IDLE ); - ADD_ACTIVITY_TO_SR( ACT_BREADMONSTER_VM_PRIMARYATTACK ); - - ADD_ACTIVITY_TO_SR( ACT_PARACHUTE_DEPLOY ); - ADD_ACTIVITY_TO_SR( ACT_PARACHUTE_DEPLOY_IDLE ); - ADD_ACTIVITY_TO_SR( ACT_PARACHUTE_RETRACT ); - ADD_ACTIVITY_TO_SR( ACT_PARACHUTE_RETRACT_IDLE ); - - ADD_ACTIVITY_TO_SR( ACT_BOT_SPAWN ); - ADD_ACTIVITY_TO_SR( ACT_BOT_PANIC ); - ADD_ACTIVITY_TO_SR( ACT_BOT_PRIMARY_MOVEMENT ); - ADD_ACTIVITY_TO_SR( ACT_BOT_GESTURE_FLINCH ); - ADD_ACTIVITY_TO_SR( ACT_BOT_PANIC_START ); - ADD_ACTIVITY_TO_SR( ACT_BOT_PANIC_END ); - - ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_DRAW ); - ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_IDLE ); - ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_PRIMARYATTACK ); - ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_RELOAD ); - - ADD_ACTIVITY_TO_SR( ACT_KART_IDLE ); - ADD_ACTIVITY_TO_SR( ACT_KART_ACTION_SHOOT ); - ADD_ACTIVITY_TO_SR( ACT_KART_ACTION_DASH ); - ADD_ACTIVITY_TO_SR( ACT_KART_JUMP_START ); - ADD_ACTIVITY_TO_SR( ACT_KART_JUMP_FLOAT ); - ADD_ACTIVITY_TO_SR( ACT_KART_JUMP_LAND ); - ADD_ACTIVITY_TO_SR( ACT_KART_IMPACT ); - ADD_ACTIVITY_TO_SR( ACT_KART_IMPACT_BIG ); - ADD_ACTIVITY_TO_SR( ACT_KART_GESTURE_POSITIVE ); - ADD_ACTIVITY_TO_SR( ACT_KART_GESTURE_NEGATIVE ); - ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_DRAW ); ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_IDLE ); ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_FIRE_START ); @@ -2233,4 +2188,49 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_MELEE_VM_INSPECT_START ); ADD_ACTIVITY_TO_SR( ACT_MELEE_VM_INSPECT_IDLE ); ADD_ACTIVITY_TO_SR( ACT_MELEE_VM_INSPECT_END ); + +#if AR2_ACTIVITY_FIX == 1 + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_AR2 ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR2_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR2_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_AR2_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR2_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR2_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_AR2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_AR2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR2_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR2 ); + + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2 ); + //ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW ); + + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR2 ); +#endif + +#ifdef SHARED_COMBINE_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_COMBINE_THROW_GRENADE ); + ADD_ACTIVITY_TO_SR( ACT_COMBINE_AR2_ALTFIRE ); + + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_ADVANCE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_FORWARD ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_GROUP ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_HALT ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_LEFT ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_RIGHT ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_TAKECOVER ); +#endif + +#ifdef COMPANION_HOLSTER_WORKAROUND + ADD_ACTIVITY_TO_SR( ACT_ARM_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_RIFLE ); +#endif } diff --git a/mp/src/game/server/ai_baseactor.cpp b/mp/src/game/server/ai_baseactor.cpp index e9814967..2ddc60f7 100644 --- a/mp/src/game/server/ai_baseactor.cpp +++ b/mp/src/game/server/ai_baseactor.cpp @@ -98,6 +98,15 @@ BEGIN_DATADESC( CAI_BaseActor ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_BaseActor, CAI_BaseNPC, "The base class for NPCs which act in complex choreo scenes." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTarget, "AddLookTarget", "Add a potential look target for this actor." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTargetPos, "AddLookTargetPos", "Add a potential look target position for this actor." ) + +END_SCRIPTDESC(); +#endif + BEGIN_SIMPLE_DATADESC( CAI_InterestTarget_t ) DEFINE_FIELD( m_eType, FIELD_INTEGER ), @@ -230,7 +239,11 @@ void CAI_BaseActor::SetModel( const char *szModelName ) // Purpose: //----------------------------------------------------------------------------- +#ifdef MAPBASE +bool CAI_BaseActor::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget, CSceneEntity *pSceneEnt ) +#else bool CAI_BaseActor::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ) +#endif { Assert( info ); Assert( info->m_pScene ); @@ -314,6 +327,109 @@ bool CAI_BaseActor::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, { info->m_nType = SCENE_AI_DISABLEAI; } +#ifdef MAPBASE + else if (stricmp(event->GetParameters(), "AI_ADDCONTEXT") == 0) + { + // Adds a response context to the caller in place of the target field. + // This is supposed to be used with the talker system. + if (event->GetParameters2()) + { + info->m_nType = SCENE_AI_ADDCONTEXT; + AddContext(event->GetParameters2()); + return true; + } + } + else if (stricmp(event->GetParameters(), "AI_INPUT") == 0) + { + // Fires an input on an entity in place of the target field. + // This is supposed to be used with the talker system. + if (event->GetParameters2()) + { + info->m_nType = SCENE_AI_INPUT; + + const char *raw = event->GetParameters2(); + char sTarget[128]; + char sInput[128]; + char sParameter[128]; + char *colon1 = Q_strstr( raw, ":" ); + if (!colon1) + { + Warning("%s (%s) AI_INPUT missing colon separator!\n", GetClassname(), GetDebugName()); + return false; + } + + int len = colon1 - raw; + Q_strncpy( sTarget, raw, MIN( len + 1, sizeof(sTarget) ) ); + sTarget[MIN(len, sizeof(sTarget) - 1)] = 0; + + bool bParameter = true; + char *colon2 = Q_strstr(colon1 + 1, ":"); + if (!colon2) + { + DevMsg("Assuming no parameter\n"); + colon2 = colon1 + 1; + bParameter = false; + } + + if (bParameter) + { + len = MIN(colon2 - (colon1 + 1), sizeof(sInput) - 1); + Q_strncpy(sInput, colon1 + 1, MIN(len + 1, sizeof(sInput))); + sInput[MIN(len, sizeof(sInput) - 1)] = 0; + + Q_strncpy(sParameter, colon2 + 1, sizeof(sInput)); + } + else + { + len = colon2 - raw; + Q_strncpy(sInput, colon2, sizeof(sInput)); + } + + CBaseEntity *pEnt = gEntList.FindEntityByName(NULL, sTarget, this); + if (!pEnt) + { + DevMsg("%s not found with normal search, slamming to scene ent\n", sTarget); + pEnt = UTIL_FindNamedSceneEntity(sTarget, this, pSceneEnt); + if (!pEnt) + { + DevWarning("%s slammed to self!\n", sTarget); + pEnt = this; + } + } + + if (pEnt && sInput) + { + variant_t variant; + if (bParameter && sParameter) + { + const char *strParam = sParameter; + if (strParam[0] == '!') + { + CBaseEntity *pParamEnt = UTIL_FindNamedSceneEntity(strParam, this, pSceneEnt); + if (pParamEnt && pParamEnt->GetEntityName() != NULL_STRING && !gEntList.FindEntityProcedural(strParam)) + { + // We make sure it's a scene entity that can't be found with entlist procedural so we can translate !target# without messing with !activators, etc. + //const char *newname = pParamEnt->GetEntityName().ToCStr(); + strParam = pParamEnt->GetEntityName().ToCStr(); + } + } + + if (strParam) + { + variant.SetString(MAKE_STRING(strParam)); + } + } + + pEnt->AcceptInput(sInput, this, this, variant, 0); + return true; + } + else + { + Warning("%s (%s) AI_INPUT cannot find entity %s!\n", GetClassname(), GetDebugName(), sTarget); + } + } + } +#endif else { return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget ); @@ -508,6 +624,11 @@ bool CAI_BaseActor::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scen Vector vecAimTargetLoc = info->m_hTarget->EyePosition(); Vector vecAimDir = vecAimTargetLoc - EyePosition(); +#ifdef MAPBASE + // Mind the ramp + vecAimDir *= event->GetIntensity(scene->GetTime()); +#endif + VectorNormalize( vecAimDir ); SetAim( vecAimDir); } @@ -548,6 +669,16 @@ bool CAI_BaseActor::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scen EnterSceneSequence( scene, event ); } return true; +#ifdef MAPBASE + case SCENE_AI_ADDCONTEXT: + { + } + return true; + case SCENE_AI_INPUT: + { + } + return true; +#endif default: return false; } @@ -1802,7 +1933,7 @@ void CAI_BaseActor::OnStateChange( NPC_STATE OldState, NPC_STATE NewState ) { PlayExpressionForState( NewState ); -#ifdef HL2_EPISODIC +#if defined(HL2_EPISODIC) || defined(MAPBASE) // If we've just switched states, ensure we stop any scenes that asked to be stopped if ( OldState == NPC_STATE_IDLE ) { diff --git a/mp/src/game/server/ai_baseactor.h b/mp/src/game/server/ai_baseactor.h index 49caea53..f0d9f8ae 100644 --- a/mp/src/game/server/ai_baseactor.h +++ b/mp/src/game/server/ai_baseactor.h @@ -101,7 +101,11 @@ public: virtual void SetModel( const char *szModelName ); +#ifdef MAPBASE + virtual bool StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget, CSceneEntity *pSceneEnt = NULL ); +#else virtual bool StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ); +#endif virtual bool ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ); virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); virtual bool CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event ); @@ -166,6 +170,15 @@ public: void ClearExpression(); const char * GetExpression(); +#ifdef MAPBASE_VSCRIPT + //--------------------------------- + + void ScriptAddLookTarget( HSCRIPT pTarget, float flImportance, float flDuration, float flRamp = 0.0 ) { AddLookTarget(ToEnt(pTarget), flImportance, flDuration, flRamp); } + void ScriptAddLookTargetPos( const Vector &vecPosition, float flImportance, float flDuration, float flRamp = 0.0 ) { AddLookTarget(vecPosition, flImportance, flDuration, flRamp); } + + //--------------------------------- +#endif + enum { SCENE_AI_BLINK = 1, @@ -177,10 +190,19 @@ public: SCENE_AI_RANDOMHEADFLEX, SCENE_AI_IGNORECOLLISION, SCENE_AI_DISABLEAI +#ifdef MAPBASE + , + SCENE_AI_ADDCONTEXT, + SCENE_AI_INPUT, + SCENE_AI_GAMETEXT, // This is handled in CBaseFlex +#endif }; DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif private: enum { diff --git a/mp/src/game/server/ai_basenpc.cpp b/mp/src/game/server/ai_basenpc.cpp index b4442260..1e831080 100644 --- a/mp/src/game/server/ai_basenpc.cpp +++ b/mp/src/game/server/ai_basenpc.cpp @@ -95,6 +95,10 @@ #include "prop_portal_shared.h" #endif +#ifdef MAPBASE +#include "mapbase/matchers.h" +#endif + #include "env_debughistory.h" #include "collisionutils.h" @@ -156,6 +160,12 @@ ConVar ai_test_moveprobe_ignoresmall( "ai_test_moveprobe_ignoresmall", "0" ); extern ConVar ai_vehicle_avoidance; #endif // HL2_EPISODIC +#ifdef MAPBASE +extern ISoundEmitterSystemBase *soundemitterbase; + +ConVar ai_dynint_always_enabled( "ai_dynint_always_enabled", "0", FCVAR_NONE, "Makes the \"Don't Care\" setting equivalent to \"Yes\"." ); +#endif + #ifndef _RETAIL #define ShouldUseEfficiency() ( ai_use_think_optimizations.GetBool() && ai_use_efficiency.GetBool() ) #define ShouldUseFrameThinkLimits() ( ai_use_think_optimizations.GetBool() && ai_use_frame_think_limits.GetBool() ) @@ -291,6 +301,14 @@ int CAI_BaseNPC::gm_nSpawnedThisFrame; CSimpleSimTimer CAI_BaseNPC::m_AnyUpdateEnemyPosTimer; +#ifdef MAPBASE_VSCRIPT +// TODO: Better placement? +ScriptHook_t g_Hook_QueryHearSound; +ScriptHook_t g_Hook_QuerySeeEntity; +ScriptHook_t g_Hook_TranslateActivity; +ScriptHook_t g_Hook_TranslateSchedule; +#endif + // // Deferred Navigation calls go here // @@ -544,7 +562,7 @@ void CAI_BaseNPC::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) RemoveActorFromScriptedScenes( this, false /*all scenes*/ ); } else - DevMsg( "Unexpected double-death-cleanup\n" ); + CGMsg( 1, CON_GROUP_NPC_AI, "Unexpected double-death-cleanup\n" ); } void CAI_BaseNPC::SelectDeathPose( const CTakeDamageInfo &info ) @@ -660,11 +678,209 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo ConVar ai_block_damage( "ai_block_damage","0" ); +#ifdef MAPBASE +bool CAI_BaseNPC::FriendlyFireEnabled() +{ + if (m_FriendlyFireOverride != TRS_NONE) + return m_FriendlyFireOverride == TRS_TRUE; + + if (HL2GameRules()->GlobalFriendlyFire() != TRS_NONE) + return HL2GameRules()->GlobalFriendlyFire() == TRS_TRUE; + + return !(CapabilitiesGet() & bits_CAP_FRIENDLY_DMG_IMMUNE); +} + +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputSetFriendlyFire( inputdata_t &inputdata ) +{ + m_FriendlyFireOverride = TO_THREESTATE(inputdata.value.Int()); +} +#endif + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptGetEnemy() +{ + return ToHScript( GetEnemy() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::VScriptSetEnemy( HSCRIPT pEnemy ) +{ + SetEnemy( ToEnt( pEnemy ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::VScriptGetEnemyLKP() +{ + return GetEnemyLKP(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT pEnemy ) +{ + HSCRIPT hScript = NULL; + AI_EnemyInfo_t *info = GetEnemies()->Find( ToEnt(pEnemy) ); + if (info) + { + hScript = g_pScriptVM->RegisterInstance( info ); + } + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CAI_BaseNPC::VScriptGetState() +{ + return (int)GetState(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptGetHintNode() +{ + return ToHScript( GetHintNode() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CAI_BaseNPC::VScriptGetSchedule() +{ + const char *pName = NULL; + if (GetCurSchedule()) + pName = GetCurSchedule()->GetName(); + + if (!pName) + pName = "Unknown"; + + return pName; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CAI_BaseNPC::VScriptGetScheduleID() +{ + if (!GetCurSchedule()) + return -1; + + int iSched = GetCurSchedule()->GetId(); + + // Local IDs are needed to correspond with user-friendly enums + if ( AI_IdIsGlobal( iSched ) ) + { + iSched = GetClassScheduleIdSpace()->ScheduleGlobalToLocal(iSched); + } + + return iSched; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::VScriptSetSchedule( const char *szSchedule ) +{ + SetSchedule( GetScheduleID( szSchedule ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CAI_BaseNPC::VScriptGetTask() +{ + const Task_t *pTask = GetTask(); + const char *pName = NULL; + if (pTask) + pName = TaskName( pTask->iTask ); + else + pName = "None"; + + return pName; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CAI_BaseNPC::VScriptGetTaskID() +{ + const Task_t *pTask = GetTask(); + int iID = -1; + if (pTask) + iID = GetTaskID( TaskName( pTask->iTask ) ); + + return iID; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptGetExpresser() +{ + HSCRIPT hScript = NULL; + CAI_Expresser *pExpresser = GetExpresser(); + if (pExpresser) + { + hScript = g_pScriptVM->RegisterInstance( pExpresser ); + } + + return hScript; +} + +HSCRIPT CAI_BaseNPC::VScriptGetCine() +{ + return ToHScript(m_hCine.Get()); +} + +HSCRIPT CAI_BaseNPC::VScriptGetSquad() +{ + HSCRIPT hScript = NULL; + CAI_Squad *pSquad = GetSquad(); + if (pSquad) + { + hScript = g_pScriptVM->RegisterInstance( pSquad ); + } + + return hScript; +} +#endif + bool CAI_BaseNPC::PassesDamageFilter( const CTakeDamageInfo &info ) { if ( ai_block_damage.GetBool() ) return false; // FIXME: hook a friendly damage filter to the npc instead? +#ifdef MAPBASE + if ( !FriendlyFireEnabled() && info.GetAttacker() && info.GetAttacker() != this && !info.IsForceFriendlyFire() ) + { + // check attackers relationship with me + CBaseCombatCharacter *npcEnemy = info.GetAttacker()->MyCombatCharacterPointer(); + + if ( npcEnemy && npcEnemy->IRelationType( this ) == D_LI ) + { + m_fNoDamageDecal = true; + + if ( npcEnemy->IsPlayer() ) + { + m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this ); + // This also counts as being harmed by player's squad. + m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); + } + + return false; + } + + if ( IServerVehicle *pVehicle = info.GetAttacker()->GetServerVehicle() ) + { + m_fNoDamageDecal = true; + if (pVehicle->GetPassenger() && pVehicle->GetPassenger()->IRelationType(this) == D_LI) + { + // Players could bail from their cars to kill NPCs with this! + // Is there a "last passenger" variable we could use? + return false; + } + } + } +#else if ( (CapabilitiesGet() & bits_CAP_FRIENDLY_DMG_IMMUNE) && info.GetAttacker() && info.GetAttacker() != this ) { // check attackers relationship with me @@ -692,6 +908,7 @@ bool CAI_BaseNPC::PassesDamageFilter( const CTakeDamageInfo &info ) return false; } } +#endif if ( !BaseClass::PassesDamageFilter( info ) ) { @@ -714,7 +931,11 @@ int CAI_BaseNPC::OnTakeDamage_Alive( const CTakeDamageInfo &info ) return 0; if ( GetSleepState() == AISS_WAITING_FOR_THREAT ) +#ifdef MAPBASE + Wake( info.GetAttacker() ); +#else Wake(); +#endif // NOTE: This must happen after the base class is called; we need to reduce // health before the pain sound, since some NPCs use the final health @@ -776,7 +997,12 @@ int CAI_BaseNPC::OnTakeDamage_Alive( const CTakeDamageInfo &info ) else { // See if the person that injured me is an NPC. +#ifdef MAPBASE + // Sometimes I find these things and I can't just sit idly by. + CAI_BaseNPC *pAttacker = info.GetAttacker()->MyNPCPointer(); +#else CAI_BaseNPC *pAttacker = dynamic_cast( info.GetAttacker() ); +#endif CBasePlayer *pPlayer = AI_GetSinglePlayer(); if( pAttacker && pAttacker->IsAlive() && pPlayer ) @@ -995,7 +1221,8 @@ void CAI_BaseNPC::NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity ) { if ( (originNpc.AsVector2D() - origin.AsVector2D()).LengthSqr() < NEAR_XY_SQ ) { - if ( pNpc->GetSquad() == GetSquad() || IRelationType( pNpc ) == D_LI ) + //Tony; add a check to make sure this doesn't get called if the npc isn't in a squad + if ( ( pNpc->GetSquad() == GetSquad() && !( pNpc->GetSquad() == NULL || GetSquad() == NULL ) ) || IRelationType( pNpc ) == D_LI ) pNpc->OnFriendDamaged( this, pAttacker ); } } @@ -1013,7 +1240,11 @@ void CAI_BaseNPC::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity float distSqToThreat = ( GetAbsOrigin() - pAttacker->GetAbsOrigin() ).LengthSqr(); if ( GetSleepState() != AISS_AWAKE && distSqToThreat < Square( 20 * 12 ) ) +#ifdef MAPBASE + Wake( pAttacker ); +#else Wake(); +#endif if ( distSqToThreat < Square( 50 * 12 ) ) ForceGatherConditions(); @@ -1182,7 +1413,12 @@ void CAI_BaseNPC::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir break; } +#ifdef MAPBASE + bool bBloodAllowed = DamageFilterAllowsBlood( info ); + if ( subInfo.GetDamage() >= 1.0 && !(subInfo.GetDamageType() & DMG_SHOCK ) && bBloodAllowed ) +#else if ( subInfo.GetDamage() >= 1.0 && !(subInfo.GetDamageType() & DMG_SHOCK ) ) +#endif { if( !IsPlayer() || ( IsPlayer() && g_pGameRules->IsMultiplayer() ) ) { @@ -1197,6 +1433,12 @@ void CAI_BaseNPC::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir m_fNoDamageDecal = true; } } +#ifdef MAPBASE + else if (!bBloodAllowed) + { + m_fNoDamageDecal = true; + } +#endif // Airboat gun will impart major force if it's about to kill him.... if ( info.GetDamageType() & DMG_AIRBOAT ) @@ -1286,7 +1528,12 @@ bool CAI_BaseNPC::PlayerInSpread( const Vector &sourcePos, const Vector &targetP { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); +#ifdef MAPBASE + // "> D_FR" means it isn't D_HT, D_FR, or D_ER (error disposition) + if ( pPlayer && ( !ignoreHatedPlayers || IRelationType( pPlayer ) > D_FR ) && !(pPlayer->GetFlags() & FL_NOTARGET) ) +#else if ( pPlayer && ( !ignoreHatedPlayers || IRelationType( pPlayer ) != D_HT ) ) +#endif { if ( PointInSpread( pPlayer, sourcePos, targetPos, pPlayer->WorldSpaceCenter(), flSpread, maxDistOffCenter ) ) return true; @@ -1738,6 +1985,118 @@ void CAI_BaseNPC::ClearIgnoreConditions( int *pConditions, int nConditions ) } } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: Adds a condition to this NPC, integral or not +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputSetCondition( inputdata_t &inputdata ) +{ + const char *pszCondition = inputdata.value.String(); + if (!pszCondition || !pszCondition[0]) + return; + + int iCondition = atoi(pszCondition); + if (iCondition == 0) + { + // Convert from string + iCondition = GetConditionID(pszCondition); + } + + SetCondition(iCondition); +} + +//------------------------------------------------------------------------------ +// Purpose: Removes a condition from this NPC, integral or not +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputClearCondition( inputdata_t &inputdata ) +{ + const char *pszCondition = inputdata.value.String(); + if (!pszCondition || !pszCondition[0]) + return; + + int iCondition = atoi(pszCondition); + if (iCondition == 0) + { + // Convert from string + iCondition = GetConditionID(pszCondition); + } + + ClearCondition(iCondition); +} + +//------------------------------------------------------------------------------ +// Purpose: Sets our think to CallNPCThink() in case SetThinkNull was fired before +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputSetThinkNPC( inputdata_t &inputdata ) +{ + SetThink ( &CAI_BaseNPC::CallNPCThink ); + SetNextThink(gpGlobals->curtime + inputdata.value.Float()); +} + +//------------------------------------------------------------------------------ +// Purpose: Sets our look distance +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputSetDistLook( inputdata_t &inputdata ) +{ + if ( inputdata.value.Float() != 0.0f ) + { + SetDistLook( inputdata.value.Float() ); + } + else + { + SetDistLook( 2048.0 ); + + if ( HasSpawnFlags( SF_NPC_LONG_RANGE ) ) + { + SetDistLook( 6000.0 ); + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: Sets our distance too far +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputSetDistTooFar( inputdata_t &inputdata ) +{ + if ( inputdata.value.Float() != 0.0f ) + { + m_flDistTooFar = inputdata.value.Float(); + } + else + { + m_flDistTooFar = 1024.0; + + if ( HasSpawnFlags( SF_NPC_LONG_RANGE ) ) + { + m_flDistTooFar = 1e9f; + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputSetTarget( inputdata_t &inputdata ) +{ + m_target = inputdata.value.StringID(); + + if ( m_target != NULL_STRING )// this npc has a target + { + // Find the npc's initial target entity, stash it + SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) ); + + if ( !GetGoalEnt() ) + { + Warning( "ReadyNPC()--%s couldn't find target %s\n", GetClassname(), STRING(m_target)); + } + else + { + StartTargetHandling( GetGoalEnt() ); + } + } +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- bool CAI_BaseNPC::HasInterruptCondition( int iCondition ) @@ -1935,6 +2294,15 @@ bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) return false; } +#ifdef MAPBASE + if ( pSound->SoundContext() & SOUND_CONTEXT_OWNER_ALLIES ) + { + CBaseCombatCharacter *pOwner = ToBaseCombatCharacter(pSound->m_hOwner); + if (!pOwner || pOwner->IRelationType(this) != D_LI) + return false; + } +#endif + if ( pSound->IsSoundType( SOUND_PLAYER ) && GetState() == NPC_STATE_IDLE && !FVisible( pSound->GetSoundReactOrigin() ) ) { // NPC's that are IDLE should disregard player movement sounds if they can't see them. @@ -1955,6 +2323,22 @@ bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) if( ShouldIgnoreSound( pSound ) ) return false; +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_QueryHearSound.CanRunInScope(m_ScriptScope)) + { + HSCRIPT hSound = g_pScriptVM->RegisterInstance( pSound ); + + ScriptVariant_t functionReturn = true; + ScriptVariant_t args[] = { hSound }; + g_Hook_QueryHearSound.Call( m_ScriptScope, &functionReturn, args ); + + g_pScriptVM->RemoveInstance( hSound ); + + if (functionReturn.m_bool == false) + return false; + } +#endif + return true; } @@ -1962,12 +2346,31 @@ bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) bool CAI_BaseNPC::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC ) { + bool bValid = true; + if ( bOnlyHateOrFearIfNPC && pEntity->IsNPC() ) { Disposition_t disposition = IRelationType( pEntity ); - return ( disposition == D_HT || disposition == D_FR ); + bValid = ( disposition == D_HT || disposition == D_FR ); } - return true; + +#ifdef MAPBASE_VSCRIPT + if (bValid) + { + if (m_ScriptScope.IsInitialized() && g_Hook_QuerySeeEntity.CanRunInScope(m_ScriptScope)) + { + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript(pEntity) }; + if (g_Hook_QuerySeeEntity.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_bool == false) + bValid = false; + } + } + } +#endif + + return bValid; } //----------------------------------------------------------------------------- @@ -2129,7 +2532,7 @@ void CAI_BaseNPC::OnListened() case SOUND_PLAYER_VEHICLE: condition = COND_HEAR_PLAYER; break; default: - DevMsg( "**ERROR: NPC %s hearing sound of unknown type %d!\n", GetClassname(), pCurrentSound->SoundType() ); + CGMsg( 1, CON_GROUP_NPC_AI, "**ERROR: NPC %s hearing sound of unknown type %d!\n", GetClassname(), pCurrentSound->SoundType() ); break; } } @@ -2317,7 +2720,7 @@ CSound* CAI_BaseNPC::GetBestSound( int validTypes ) return m_pLockedBestSound; CSound *pResult = GetSenses()->GetClosestSound( false, validTypes ); if ( pResult == NULL) - DevMsg( "Warning: NULL Return from GetBestSound\n" ); // condition previously set now no longer true. Have seen this when play too many sounds... + CGMsg( 1, CON_GROUP_NPC_AI, "Warning: NULL Return from GetBestSound\n" ); // condition previously set now no longer true. Have seen this when play too many sounds... return pResult; } @@ -2329,7 +2732,7 @@ CSound* CAI_BaseNPC::GetBestScent( void ) { CSound *pResult = GetSenses()->GetClosestSound( true ); if ( pResult == NULL) - DevMsg("Warning: NULL Return from GetBestScent\n" ); + CGMsg( 1, CON_GROUP_NPC_AI, "Warning: NULL Return from GetBestScent\n" ); return pResult; } @@ -3354,6 +3757,9 @@ void CAI_BaseNPC::UpdateSleepState( bool bInPVS ) { if ( GetSleepState() > AISS_AWAKE ) { +#ifdef MAPBASE +#define Wake() Wake(pLocalPlayer) +#endif CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); if ( !pLocalPlayer ) { @@ -3414,6 +3820,9 @@ void CAI_BaseNPC::UpdateSleepState( bool bInPVS ) } } } +#ifdef MAPBASE +#undef Wake +#endif } else { @@ -4176,6 +4585,24 @@ int CAI_BaseNPC::CapabilitiesGet( void ) const return capability; } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: Adds capabilities to this NPC +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputAddCapabilities( inputdata_t &inputdata ) +{ + CapabilitiesAdd(inputdata.value.Int()); +} + +//------------------------------------------------------------------------------ +// Purpose: Removes capabilities from this NPC +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputRemoveCapabilities( inputdata_t &inputdata ) +{ + CapabilitiesRemove(inputdata.value.Int()); +} +#endif + // Set capability mask int CAI_BaseNPC::CapabilitiesAdd( int capability ) { @@ -4379,7 +4806,11 @@ void CAI_BaseNPC::SetState( NPC_STATE State ) if ( GetEnemy() != NULL ) { SetEnemy( NULL ); // not allowed to have an enemy anymore. +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_NPC_AI, "Stripped enemy pointer from NPC going back to idle\n" ); +#else DevMsg( 2, "Stripped\n" ); +#endif } break; } @@ -4398,6 +4829,20 @@ void CAI_BaseNPC::SetState( NPC_STATE State ) // Notify the character that its state has changed. if( fNotifyChange ) { +#ifdef MAPBASE + // Doing OnStateChange here instead of in OnStateChange() to prevent override shenanigans. + + // Assume our enemy is the activator. + // States that don't have an enemy have a NULL activator, which is fine. + CBaseEntity *pActivator = GetEnemy(); + + // If we entered a script, use the scripted_sequence as the activator + if (m_NPCState == NPC_STATE_SCRIPT) + pActivator = m_hCine; + + m_OnStateChange.Set(m_NPCState, pActivator, this); +#endif + OnStateChange( OldState, m_NPCState ); } } @@ -4414,10 +4859,39 @@ void CAI_BaseNPC::Wake( bool bFireOutput ) if ( GetSleepState() != AISS_AWAKE ) { m_nWakeTick = gpGlobals->tickcount; +#ifndef MAPBASE SetSleepState( AISS_AWAKE ); +#endif RemoveEffects( EF_NODRAW ); if ( bFireOutput ) +#ifdef MAPBASE + { + // Activator is based on sleep state + CBaseEntity *pActivator = this; + + switch (GetSleepState()) + { + case AISS_WAITING_FOR_THREAT: + pActivator = GetEnemy(); + break; + + case AISS_WAITING_FOR_PVS: + case AISS_AUTO_PVS: + case AISS_AUTO_PVS_AFTER_PVS: + pActivator = UTIL_GetLocalPlayer(); + break; + + case AISS_WAITING_FOR_INPUT: + // I can't really do this here, but InputWake() uses the new function with pActivator, so it's fine + break; + } + + m_OnWake.FireOutput( pActivator, this ); + } + SetSleepState( AISS_AWAKE ); +#else m_OnWake.FireOutput( this, this ); +#endif if ( m_bWakeSquad && GetSquad() ) { @@ -4435,6 +4909,37 @@ void CAI_BaseNPC::Wake( bool bFireOutput ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::Wake( CBaseEntity *pActivator ) +{ + if ( GetSleepState() != AISS_AWAKE ) + { + m_nWakeTick = gpGlobals->tickcount; + RemoveEffects( EF_NODRAW ); + + m_OnWake.FireOutput( pActivator, this ); + + SetSleepState( AISS_AWAKE ); + + if ( m_bWakeSquad && GetSquad() ) + { + AISquadIter_t iter; + for ( CAI_BaseNPC *pSquadMember = GetSquad()->GetFirstMember( &iter ); pSquadMember; pSquadMember = GetSquad()->GetNextMember( &iter ) ) + { + if ( pSquadMember->IsAlive() && pSquadMember != this ) + { + pSquadMember->m_bWakeSquad = false; + pSquadMember->Wake(pActivator); + } + } + + } + } +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_BaseNPC::Sleep() @@ -4698,7 +5203,7 @@ void CAI_BaseNPC::GatherConditions( void ) // @Note (toml 05-05-04): There seems to be a case where an NPC can not respond // to COND_NEW_ENEMY. Only evidence right now is save // games after the fact, so for now, just patching it up - DevMsg( 2, "Had to force COND_NEW_ENEMY\n" ); + CGMsg( 2, CON_GROUP_NPC_AI, "Had to force COND_NEW_ENEMY\n" ); SetCondition(COND_NEW_ENEMY); } } @@ -4750,8 +5255,30 @@ void CAI_BaseNPC::PrescheduleThink( void ) CheckForScriptedNPCInteractions(); #endif +#ifdef MAPBASE + // Please excuse the readability here. + if (CapabilitiesGet() & bits_CAP_USE_WEAPONS) + { + if ( CanUnholsterWeapon() ) + { + // If we should have our gun out, fetch it + if ( ShouldUnholsterWeapon() && m_iDesiredWeaponState == DESIREDWEAPONSTATE_IGNORE ) + { + SetDesiredWeaponState( DESIREDWEAPONSTATE_UNHOLSTERED ); + } + } + else if (m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED) + { + // If we cannot have our gun out, refuse to fetch it + SetDesiredWeaponState( DESIREDWEAPONSTATE_IGNORE ); + } + + // If our desired weapon state is not the current, fix it + if( (m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) ) +#else // If we use weapons, and our desired weapon state is not the current, fix it if( (CapabilitiesGet() & bits_CAP_USE_WEAPONS) && (m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) ) +#endif { if ( IsAlive() && !IsInAScript() ) { @@ -4774,6 +5301,9 @@ void CAI_BaseNPC::PrescheduleThink( void ) m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; } } +#ifdef MAPBASE + } +#endif } //----------------------------------------------------------------------------- @@ -5130,6 +5660,62 @@ NPC_STATE CAI_BaseNPC::SelectIdealState( void ) return m_IdealNPCState; } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: Creates a new weapon and makes us equip it. +//------------------------------------------------------------------------------ +CBaseCombatWeapon *CAI_BaseNPC::GiveWeapon( string_t iszWeaponName, bool bDiscardCurrent ) +{ + CBaseCombatWeapon *pWeapon = Weapon_Create( STRING(iszWeaponName) ); + if ( !pWeapon ) + { + Warning( "Couldn't create weapon %s to give NPC %s.\n", STRING(iszWeaponName), GetDebugName() ); + return NULL; + } + + // If I have a weapon already, drop it + if ( bDiscardCurrent && GetActiveWeapon() ) + { + Weapon_Drop( GetActiveWeapon() ); + } + + // If I have a name, make my weapon match it with "_weapon" appended + if ( GetEntityName() != NULL_STRING ) + { + pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", STRING(GetEntityName()) )) ); + } + + Weapon_Equip( pWeapon ); + + // Handle this case + OnGivenWeapon( pWeapon ); + + return pWeapon; +} + +//------------------------------------------------------------------------------ +// Purpose: Creates a new weapon and puts it in our inventory. +//------------------------------------------------------------------------------ +CBaseCombatWeapon *CAI_BaseNPC::GiveWeaponHolstered( string_t iszWeaponName ) +{ + CBaseCombatWeapon *pWeapon = Weapon_Create( STRING(iszWeaponName) ); + if ( !pWeapon ) + { + Warning( "Couldn't create weapon %s to give NPC %s.\n", STRING(iszWeaponName), GetDebugName() ); + return NULL; + } + + // If I have a name, make my weapon match it with "_weapon" appended + if ( GetEntityName() != NULL_STRING ) + { + pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", STRING(GetEntityName()) )) ); + } + + Weapon_EquipHolstered( pWeapon ); + + return pWeapon; +} +#else //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void CAI_BaseNPC::GiveWeapon( string_t iszWeaponName ) @@ -5158,6 +5744,7 @@ void CAI_BaseNPC::GiveWeapon( string_t iszWeaponName ) // Handle this case OnGivenWeapon( pWeapon ); } +#endif //----------------------------------------------------------------------------- // Rather specific function that tells us if an NPC is in the process of @@ -5599,6 +6186,11 @@ void CAI_BaseNPC::GatherEnemyConditions( CBaseEntity *pEnemy ) EHANDLE hEnemy; hEnemy.Set( GetEnemy() ); +#ifdef MAPBASE + if (hEnemy->IsPlayer()) + m_OnFoundPlayer.Set(hEnemy, hEnemy, this); + m_OnFoundEnemy.Set(hEnemy, hEnemy, this); +#else if (GetEnemy()->IsPlayer()) { m_OnFoundPlayer.Set(hEnemy, this, this); @@ -5608,6 +6200,7 @@ void CAI_BaseNPC::GatherEnemyConditions( CBaseEntity *pEnemy ) { m_OnFoundEnemy.Set(hEnemy, this, this); } +#endif } Remember( bits_MEMORY_HAD_LOS ); } @@ -5904,6 +6497,107 @@ CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float dur #endif// HL2_DLL } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: This is used by TranslateActivity() before anything else. +// Input : eNewActivity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity ) +{ + if (CapabilitiesGet() & bits_CAP_DUCK) + { + // ======================================================================== + // The main issue with cover hint nodes is that crouch activities are not translated at the right time + // in the activity translation process. Weapons aren't given a chance to translate from them + // and therefore the crouch activities on many NPCs are inaccessible. + // + // Regular, non-hint-node crouching seen on Combine soldiers and Episodic Alyx + // seemingly don't work when put here, so don't bother. + // ======================================================================== + Activity nCoverActivity = eNewActivity; + if (eNewActivity == ACT_RELOAD) + { + nCoverActivity = GetReloadActivity(GetHintNode()); + } + // INCOVER is synonymous with crouching at crouch cover nodes. + // Any time we need to crouch at cover is when INCOVER is valid. + else if (HasMemory(bits_MEMORY_INCOVER)) + { + if (eNewActivity == ACT_IDLE || eNewActivity == ACT_COVER) + { + // They stick to cover sometimes, but that might just be when they can't path to the enemy. + if (GetState() == NPC_STATE_COMBAT /*&& !IsCurSchedule(SCHED_COMBAT_STAND, false)*/) + nCoverActivity = GetCoverActivity(GetHintNode()); + } + else if (eNewActivity == ACT_RANGE_ATTACK1) + { + // Soldiers are the only ones I've seen attack while INCOVER, + // so I don't think we have to give it its own function. + CAI_Hint *pHint = GetHintNode(); + if (pHint) + { + if (pHint->HintType() == HINT_TACTICAL_COVER_LOW || pHint->HintType() == HINT_TACTICAL_COVER_MED) + { + nCoverActivity = ACT_RANGE_ATTACK1_LOW; + } + } + } + } + + if (nCoverActivity != ACT_IDLE) + eNewActivity = nCoverActivity; + + /* + // --------------------------------------------------------------- + // Some NPCs don't have a cover activity defined so just use idle + // --------------------------------------------------------------- + if (nCoverActivity != eNewActivity) + { + if (SelectWeightedSequence(nCoverActivity) != ACTIVITY_NOT_AVAILABLE) + { + eNewActivity = nCoverActivity; + } + else if (eNewActivity == ACT_COVER) + { + // Untranslated ACT_COVER should revert to ACT_IDLE + eNewActivity = ACT_IDLE; + } + } + */ + } + + return eNewActivity; +} + +//----------------------------------------------------------------------------- +// Purpose: Backup activity (NPC version of Weapon_BackupActivity) +// This only gets called if the NPC absolutely does not have an animation. +// This means if we don't return another activity, the NPC will most likely T-pose. +// Input : eNewActivity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity ) +{ + //if (eNewActivity == ACT_DROP_WEAPON) + // return TranslateActivity(ACT_IDLE); + + // Accounts for certain act busy activities that aren't on all NPCs. + if (eNewActivity == ACT_BUSY_QUEUE || eNewActivity == ACT_BUSY_STAND) + return TranslateActivity(ACT_IDLE); + + if (eNewActivity == ACT_WALK_ANGRY) + return TranslateActivity(ACT_WALK); + + // GetCoverActivity() should have this covered. + // --------------------------------------------- + //if (eNewActivity == ACT_COVER) + // return TranslateActivity(ACT_IDLE); + + return eNewActivity; +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : eNewActivity - @@ -5911,6 +6605,51 @@ CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float dur //----------------------------------------------------------------------------- Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) { +#ifdef MAPBASE + Assert( eNewActivity != ACT_INVALID ); + + if (IsCrouching()) + { + switch (eNewActivity) + { + case ACT_RANGE_ATTACK1: eNewActivity = ACT_RANGE_ATTACK1_LOW; break; + case ACT_RELOAD: eNewActivity = ACT_RELOAD_LOW; break; + case ACT_IDLE: eNewActivity = ACT_CROUCHIDLE; break; + + // ==== + // HACK : LEIPZIG 06 - The underlying problem is that the AR2 and SMG1 cannot map IDLE_ANGRY to a crouched equivalent automatically + // which causes the character to pop up and down in their idle state of firing while crouched. -- jdw + case ACT_IDLE_ANGRY_SMG1: + case ACT_IDLE_ANGRY_AR2: + eNewActivity = ACT_RANGE_AIM_LOW; + break; + } + } + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_TranslateActivity.CanRunInScope(m_ScriptScope)) + { + // activity, activity_id + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { GetActivityName(eNewActivity), (int)eNewActivity }; + if (g_Hook_TranslateActivity.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_INTEGER) + { + Activity activity = (Activity)functionReturn.m_int; + if (activity != ACT_INVALID) + eNewActivity = (Activity)functionReturn.m_int; + } + else + { + Activity activity = (Activity)GetActivityID( functionReturn.m_pszString ); + if (activity != ACT_INVALID) + eNewActivity = activity; + } + } + } +#endif +#else Assert( eNewActivity != ACT_INVALID ); if (eNewActivity == ACT_RANGE_ATTACK1) @@ -5967,6 +6706,7 @@ Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) return nCoverActivity; } } +#endif return eNewActivity; } @@ -5986,6 +6726,13 @@ Activity CAI_BaseNPC::TranslateActivity( Activity idealActivity, Activity *pIdea Activity last; Activity current; +#ifdef MAPBASE + // Crouch activities are translated before everything else. + idealActivity = TranslateCrouchActivity( idealActivity ); + //if ( idealWeaponActivity != idealActivity /*&& HaveSequenceForActivity(idealWeaponActivity)*/ ) + // idealActivity = idealWeaponActivity; +#endif + idealWeaponActivity = Weapon_TranslateActivity( idealActivity, &bIdealWeaponRequired ); if ( pIdealWeaponActivity ) *pIdealWeaponActivity = idealWeaponActivity; @@ -6013,6 +6760,12 @@ Activity CAI_BaseNPC::TranslateActivity( Activity idealActivity, Activity *pIdea if ( HaveSequenceForActivity( weaponTranslation ) ) return weaponTranslation; +#ifdef MAPBASE + // This is used so NPCs can use any weapon, restored AR2 activities on one NPC don't T-pose another, etc. + Activity backupActivity = Weapon_BackupActivity(baseTranslation, bWeaponRequired); + if (baseTranslation != backupActivity && HaveSequenceForActivity(backupActivity)) + return backupActivity; +#endif if ( bWeaponRequired ) { @@ -6038,6 +6791,22 @@ Activity CAI_BaseNPC::TranslateActivity( Activity idealActivity, Activity *pIdea if ( idealActivity != idealWeaponActivity && HaveSequenceForActivity( idealActivity ) ) return idealActivity; +#ifdef MAPBASE + // We absolutely do not have an activity for this. + // Do the same as above, but with any backup activity we may have available. + backupActivity = NPC_BackupActivity(baseTranslation); + if (backupActivity != baseTranslation && HaveSequenceForActivity(backupActivity)) + return backupActivity; + + backupActivity = NPC_BackupActivity(idealWeaponActivity); + if (backupActivity != idealWeaponActivity && HaveSequenceForActivity(backupActivity)) + return backupActivity; + + backupActivity = NPC_BackupActivity(idealActivity); + if (backupActivity != idealActivity && HaveSequenceForActivity(backupActivity)) + return backupActivity; +#endif + Assert( !HaveSequenceForActivity( idealActivity ) ); if ( idealActivity == ACT_RUN ) { @@ -6983,7 +7752,11 @@ void CAI_BaseNPC::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseComb //----------------------------------------------------------------------------- bool CAI_BaseNPC::CanHolsterWeapon( void ) { +#ifdef MAPBASE + int seq = SelectWeightedSequence( TranslateActivity(ACT_DISARM) ); +#else int seq = SelectWeightedSequence( ACT_DISARM ); +#endif return (seq >= 0); } @@ -6995,12 +7768,22 @@ int CAI_BaseNPC::HolsterWeapon( void ) if ( IsWeaponHolstered() ) return -1; +#ifdef COMPANION_HOLSTER_WORKAROUND + Activity activity = TranslateActivity( ACT_DISARM ); + int iHolsterGesture = FindGestureLayer( activity ); + if ( iHolsterGesture != -1 ) + return iHolsterGesture; + + int iLayer = AddGesture( activity, true ); + //iLayer = AddGesture( ACT_GESTURE_DISARM, true ); +#else int iHolsterGesture = FindGestureLayer( ACT_DISARM ); if ( iHolsterGesture != -1 ) return iHolsterGesture; int iLayer = AddGesture( ACT_DISARM, true ); //iLayer = AddGesture( ACT_GESTURE_DISARM, true ); +#endif if (iLayer != -1) { @@ -7022,6 +7805,13 @@ int CAI_BaseNPC::HolsterWeapon( void ) ClearCondition(COND_NO_PRIMARY_AMMO); ClearCondition(COND_NO_SECONDARY_AMMO); } +#ifdef MAPBASE + else + { + // We don't have the animation, so just make our weapon holster instantaneously. + DoHolster(); + } +#endif return iLayer; } @@ -7034,19 +7824,42 @@ int CAI_BaseNPC::UnholsterWeapon( void ) if ( !IsWeaponHolstered() ) return -1; +#ifdef COMPANION_HOLSTER_WORKAROUND + Activity activity = TranslateActivity( ACT_ARM ); + int iHolsterGesture = FindGestureLayer( activity ); +#else int iHolsterGesture = FindGestureLayer( ACT_ARM ); +#endif if ( iHolsterGesture != -1 ) return iHolsterGesture; +#ifdef MAPBASE + int i = m_iLastHolsteredWeapon >= 0 && GetWeapon(m_iLastHolsteredWeapon) ? m_iLastHolsteredWeapon : -1; + if (i == -1) + { + // Set i to the first weapon you can find + for (i = 0; i < WeaponCount(); i++) + { + if (GetWeapon(i)) + break; + } + } +#else // Deploy the first weapon you can find for (int i = 0; i < WeaponCount(); i++) +#endif { if ( GetWeapon( i )) { SetActiveWeapon( GetWeapon(i) ); +#ifdef COMPANION_HOLSTER_WORKAROUND + int iLayer = AddGesture( activity, true ); + //iLayer = AddGesture( ACT_GESTURE_ARM, true ); +#else int iLayer = AddGesture( ACT_ARM, true ); //iLayer = AddGesture( ACT_GESTURE_ARM, true ); +#endif if (iLayer != -1) { @@ -7056,6 +7869,13 @@ int CAI_BaseNPC::UnholsterWeapon( void ) m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING; } +#ifdef MAPBASE + else + { + // We don't have the animation, so just make our weapon unholster instantaneously. + DoUnholster(); + } +#endif // Refill the clip if ( GetActiveWeapon()->UsesClipsForAmmo1() ) @@ -7096,6 +7916,26 @@ void CAI_BaseNPC::InputHolsterAndDestroyWeapon( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CAI_BaseNPC::InputUnholsterWeapon( inputdata_t &inputdata ) { +#ifdef MAPBASE + // Support for unholstering a specific weapon + if (inputdata.value.String()) + { + if (IsWeaponHolstered()) + { + for (int i=0;im_iClassname == inputdata.value.StringID() ) + { + //Weapon_Switch(m_hMyWeapons[i]); + //DoHolster(); + m_iLastHolsteredWeapon = i; + } + } + } + } +#endif + m_iDesiredWeaponState = DESIREDWEAPONSTATE_UNHOLSTERED; } @@ -7121,6 +7961,226 @@ bool CAI_BaseNPC::IsWeaponStateChanging( void ) return ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING || m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING_DESTROY ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Allows NPC to holster from more than just the animation event +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::DoHolster( void ) +{ + // Cache off the weapon. + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + + if (pWeapon) + { + // Mark this as our last weapon + for (int i = 0; i < MAX_WEAPONS; i++) + { + if (m_hMyWeapons[i].Get() == pWeapon) + { + // Set to this weapon if we don't have a "target" weapon to unholster + if (m_iLastHolsteredWeapon == -1) + m_iLastHolsteredWeapon = i; + + break; + } + } + + GetActiveWeapon()->Holster(); + SetActiveWeapon( NULL ); + + //Force the NPC to recalculate it's arrival activity since it'll most likely be wrong now that we don't have a weapon out. + GetNavigator()->SetArrivalSequence( ACT_INVALID ); + + if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING_DESTROY ) + { + // Get rid of it! + UTIL_Remove( pWeapon ); + } + + if ( m_iDesiredWeaponState != DESIREDWEAPONSTATE_IGNORE ) + { + m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; + m_Activity = ACT_RESET; + } + + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Allows NPC to unholster from more than just the animation event +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::DoUnholster( void ) +{ + if (GetActiveWeapon()) + { + GetActiveWeapon()->Deploy(); + + //Force the NPC to recalculate it's arrival activity since it'll most likely be wrong now. + GetNavigator()->SetArrivalSequence( ACT_INVALID ); + + if ( m_iDesiredWeaponState != DESIREDWEAPONSTATE_IGNORE ) + { + m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; + m_Activity = ACT_RESET; + } + + // Clear last holstered weapon + m_iLastHolsteredWeapon = -1; + + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ +// Purpose: Give the NPC in question the weapon specified +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputGiveWeaponHolstered( inputdata_t &inputdata ) +{ + string_t iszWeaponName = inputdata.value.StringID(); + if ( iszWeaponName != NULL_STRING ) + { + GiveWeaponHolstered( iszWeaponName ); + } +} + +//------------------------------------------------------------------------------ +// Purpose: Makes the NPC change to the specified weapon via successive holster/unholster animations, creates it if it doesn't exist +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputChangeWeapon( inputdata_t &inputdata ) +{ + CBaseCombatWeapon *pSwitchTo = NULL; // The weapon itself + int iSwitchTo; // Index in m_hMyWeapons + for (int i=0;im_iClassname == inputdata.value.StringID() ) + { + pSwitchTo = m_hMyWeapons[i]; + iSwitchTo = i; + break; + } + } + + if (!pSwitchTo) + { + // We must not have it + pSwitchTo = GiveWeaponHolstered(inputdata.value.StringID()); + for (int i = 0; im_iClassname); + g_EventQueue.AddEvent(this, "UnholsterWeapon", variant, GetLayerDuration(iHolsterLayer) /*+ 0.65*/, this, this); + } +} + +//------------------------------------------------------------------------------ +// Purpose: Makes the NPC pick up the specified weapon if it can +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputPickupWeapon( inputdata_t &inputdata ) +{ + string_t iszWeaponName = inputdata.value.StringID(); + if (iszWeaponName == NULL_STRING) + return; + + // Doing a custom search implementation to prevent us from trying to pick up other people's weapons + // Also works generically so you could do things like "PickupWeapon > weapon_smg1" to pick up the nearest SMG + float flCurDist = MAX_TRACE_LENGTH; + CBaseCombatWeapon *pWeapon = NULL; + + CBaseEntity *pSearch = NULL; + while ((pSearch = gEntList.FindEntityGeneric(pSearch, STRING(iszWeaponName), this, inputdata.pActivator, inputdata.pCaller)) != NULL) + { + if (!pSearch->edict()) + continue; + + // Don't pick up non-weapons or weapons we can't use + CBaseCombatWeapon *pSearchWeapon = pSearch->MyCombatWeaponPointer(); + if (!pSearchWeapon || pSearchWeapon->GetOwner() || pSearchWeapon->IsLocked(this)) + continue; + + // If the weapon is marked to deny NPC pickup, only reject it if we're not searching for this weapon specifically. + // It might only have that spawnflag to prevent other NPCs from picking it up automatically. + // If we're searching for any weapon in general though (classnames or wildcards), don't use it. + if (pSearchWeapon->HasSpawnFlags( SF_WEAPON_NO_NPC_PICKUP ) && iszWeaponName != pSearchWeapon->GetEntityName()) + continue; + + float flDist = (pSearch->GetAbsOrigin() - GetAbsOrigin()).LengthSqr(); + if (flDist < flCurDist) + { + pWeapon = pSearchWeapon; + flCurDist = flDist; + } + } + + if (!pWeapon) + { + Msg("%s couldn't find %s to pick up\n", GetDebugName(), STRING(iszWeaponName)); + return; + } + + m_flNextWeaponSearchTime = gpGlobals->curtime + 10.0; + // Now lock the weapon for several seconds while we go to pick it up. + pWeapon->Lock( 10.0, this ); + SetTarget( pWeapon ); + SetSchedule(SCHED_NEW_WEAPON); +} + +//------------------------------------------------------------------------------ +// Purpose: Makes the NPC pick up the specified item if it can +//------------------------------------------------------------------------------ +void CAI_BaseNPC::InputPickupItem( inputdata_t &inputdata ) +{ + string_t iszItemName = inputdata.value.StringID(); + if (iszItemName == NULL_STRING) + return; + + // Searching generically allows for things like "PickupItem > item_health*" to pick up the nearest healthkit + CBaseEntity *pEntity = gEntList.FindEntityGenericNearest(STRING(iszItemName), GetLocalOrigin(), 0, this, inputdata.pActivator, inputdata.pCaller); + if (!pEntity) + return; + + SetTarget( pEntity ); + + // Can apply to anything, not just healthkits + SetSchedule(SCHED_GET_HEALTHKIT); +} +#endif + //----------------------------------------------------------------------------- // Set up the shot regulator based on the equipped weapon //----------------------------------------------------------------------------- @@ -7152,6 +8212,7 @@ void CAI_BaseNPC::InitRelationshipTable(void) } +#ifndef MAPBASE // Moved to CBaseCombatCharacter //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -7236,6 +8297,28 @@ void CAI_BaseNPC::AddRelationship( const char *pszRelationship, CBaseEntity *pAc } else { +#ifdef MAPBASE + if (!Q_strnicmp(entityString, "CLASS_", 5)) + { + // Go through all of the classes and find which one this is + Class_T resultClass = CLASS_NONE; + for (int i = 0; i < NUM_AI_CLASSES; i++) + { + if (FStrEq(g_pGameRules->AIClassText(i), entityString)) + { + resultClass = (Class_T)i; + } + } + + if (resultClass != CLASS_NONE) + { + AddClassRelationship( resultClass, disposition, priority ); + bFoundEntity = true; + } + } + + if (!bFoundEntity) +#endif DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString ); } } @@ -7244,6 +8327,7 @@ void CAI_BaseNPC::AddRelationship( const char *pszRelationship, CBaseEntity *pAc entityString = strtok(NULL," "); } } +#endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -7448,7 +8532,7 @@ bool CAI_BaseNPC::InitSquad( void ) { if ( !m_SquadName ) { - DevMsg(2, "Found %s that isn't in a squad\n",GetClassname()); + CGMsg( 2, CON_GROUP_NPC_AI, "Found %s that isn't in a squad\n", GetClassname() ); } else { @@ -7976,6 +9060,24 @@ Activity CAI_BaseNPC::GetReloadActivity( CAI_Hint* pHint ) case HINT_TACTICAL_COVER_LOW: case HINT_TACTICAL_COVER_MED: { +#ifdef MAPBASE + if (HasMemory(bits_MEMORY_INCOVER)) + { + // Don't do that trace thing. INCOVER means we're already crouching. + nReloadActivity = ACT_RELOAD_LOW; + } + else + { + Vector vEyePos = GetAbsOrigin() + GetCrouchEyeOffset(); + // Check if this location will block the threat's line of sight to me + trace_t tr; + AI_TraceLOS(vEyePos, GetEnemy()->EyePosition(), this, &tr); + if (tr.fraction != 1.0) + { + nReloadActivity = ACT_RELOAD_LOW; + } + } +#else if (SelectWeightedSequence( ACT_RELOAD_LOW ) != ACTIVITY_NOT_AVAILABLE) { Vector vEyePos = GetAbsOrigin() + EyeOffset(ACT_RELOAD_LOW); @@ -7987,6 +9089,7 @@ Activity CAI_BaseNPC::GetReloadActivity( CAI_Hint* pHint ) nReloadActivity = ACT_RELOAD_LOW; } } +#endif break; } } @@ -8011,12 +9114,24 @@ Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint ) switch (pHint->HintType()) { case HINT_TACTICAL_COVER_MED: +#ifndef MAPBASE // I know what you're thinking, but ACT_COVER_MED is pretty much deprecated at this point anyway. { nCoverActivity = ACT_COVER_MED; +#ifdef MAPBASE + // Some NPCs lack ACT_COVER_MED, but could easily use ACT_COVER_LOW. + if (SelectWeightedSequence(nCoverActivity) == ACTIVITY_NOT_AVAILABLE) + nCoverActivity = ACT_COVER_LOW; +#endif break; } +#endif case HINT_TACTICAL_COVER_LOW: { +#ifdef MAPBASE + if (pHint->HintActivityName() != NULL_STRING) + nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) ); + else +#endif nCoverActivity = ACT_COVER_LOW; break; } @@ -8024,7 +9139,11 @@ Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint ) } if ( nCoverActivity == ACT_INVALID ) +#ifdef MAPBASE + nCoverActivity = ACT_IDLE; +#else nCoverActivity = ACT_COVER; +#endif return nCoverActivity; } @@ -8078,7 +9197,7 @@ void CAI_BaseNPC::SetDefaultEyeOffset ( void ) { if ( Classify() != CLASS_NONE ) { - DevMsg( "WARNING: %s(%s) has no eye offset in .qc!\n", GetClassname(), STRING(GetModelName()) ); + CGMsg( 1, CON_GROUP_NPC_AI, "WARNING: %s(%s) has no eye offset in .qc!\n", GetClassname(), STRING( GetModelName() ) ); } VectorAdd( WorldAlignMins(), WorldAlignMaxs(), m_vDefaultEyeOffset ); m_vDefaultEyeOffset *= 0.75; @@ -8394,8 +9513,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) { if ( GetActiveWeapon() ) { +#ifdef MAPBASE + GetActiveWeapon()->Reload_NPC(); +#else GetActiveWeapon()->WeaponSound( RELOAD_NPC ); GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); +#endif ClearCondition(COND_LOW_PRIMARY_AMMO); ClearCondition(COND_NO_PRIMARY_AMMO); ClearCondition(COND_NO_SECONDARY_AMMO); @@ -8445,6 +9568,11 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) { if (pEvent->event == AE_NPC_HOLSTER) { +#ifdef MAPBASE + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if (DoHolster()) + m_OnHolsterWeapon.Set(pWeapon, pWeapon, this); +#else // Cache off the weapon. CBaseCombatWeapon *pWeapon = GetActiveWeapon(); @@ -8467,11 +9595,16 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; m_Activity = ACT_RESET; } +#endif return; } else if (pEvent->event == AE_NPC_DRAW) { +#ifdef MAPBASE + if (DoUnholster()) + m_OnUnholsterWeapon.Set(GetActiveWeapon(), GetActiveWeapon(), this); +#else if (GetActiveWeapon()) { GetActiveWeapon()->Deploy(); @@ -8485,6 +9618,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) m_Activity = ACT_RESET; } } +#endif return; } else if ( pEvent->event == AE_NPC_BODYDROP_HEAVY ) @@ -9899,6 +11033,7 @@ int CAI_BaseNPC::PlayScriptedSentence( const char *pszSentence, float delay, flo return PlaySentence( pszSentence, delay, volume, soundlevel, pListener ); } +#ifndef MAPBASE // Moved to CBaseCombatCharacter //----------------------------------------------------------------------------- // Purpose: // Input : @@ -9973,6 +11108,7 @@ CBaseEntity *CAI_BaseNPC::FindNamedEntity( const char *name, IEntityFindFilter * return NULL; } +#endif void CAI_BaseNPC::CorpseFallThink( void ) @@ -10272,7 +11408,7 @@ bool CAI_BaseNPC::ChooseEnemy( void ) } else if ( !fHaveCondNewEnemy && !fHaveCondLostEnemy && GetCurSchedule() ) { - DevMsg( 2, "WARNING: AI enemy went NULL but schedule (%s) is not interested\n", GetCurSchedule()->GetName() ); + CGMsg( 2, CON_GROUP_NPC_AI, "WARNING: AI enemy went NULL but schedule (%s) is not interested\n", GetCurSchedule()->GetName() ); } m_bSkippedChooseEnemy = false; @@ -10357,6 +11493,51 @@ void CAI_BaseNPC::PickupWeapon( CBaseCombatWeapon *pWeapon ) m_iszPendingWeapon = NULL_STRING; } +#ifdef MAPBASE +extern ConVar sk_healthvial; + +//------------------------------------------------------------------------------ +// Purpose: Moved from CNPC_Citizen to CAI_BaseNPC for new pickup input +//------------------------------------------------------------------------------ +void CAI_BaseNPC::PickupItem( CBaseEntity *pItem ) +{ + // Must cache number of elements in case any fire only once (therefore being removed after being fired) + int iNumElements = m_OnItemPickup.NumberOfElements(); + if (iNumElements > 0) + m_OnItemPickup.Set( pItem, pItem, this ); + + Assert( pItem != NULL ); + if( FClassnameIs( pItem, "item_healthkit" ) ) + { + if ( TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) ) + { + RemoveAllDecals(); + UTIL_Remove( pItem ); + } + } + else if( FClassnameIs( pItem, "item_healthvial" ) ) + { + if ( TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) ) + { + RemoveAllDecals(); + UTIL_Remove( pItem ); + } + } + else if ( FClassnameIs(pItem, "item_ammo_ar2_altfire") || FClassnameIs(pItem, "item_ammo_smg1_grenade") || + FClassnameIs(pItem, "weapon_frag") ) + { + AddGrenades( 1 ); + UTIL_Remove( pItem ); + } + + // Only warn if we didn't have any elements of OnItemPickup + else if (iNumElements <= 0) + { + DevMsg("%s doesn't know how to pick up %s!\n", GetClassname(), pItem->GetClassname() ); + } +} +#endif + //========================================================= // DropItem - dead npc drops named item //========================================================= @@ -10631,6 +11812,9 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_FIELD( m_iInteractionPlaying, FIELD_INTEGER ), DEFINE_UTLVECTOR(m_ScriptedInteractions,FIELD_EMBEDDED), DEFINE_FIELD( m_flInteractionYaw, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_INPUT( m_iDynamicInteractionsAllowed, FIELD_INTEGER, "SetDynamicInteractions" ), +#endif DEFINE_EMBEDDED( m_CheckOnGroundTimer ), DEFINE_FIELD( m_vDefaultEyeOffset, FIELD_VECTOR ), DEFINE_FIELD( m_flNextEyeLookTime, FIELD_TIME ), @@ -10641,7 +11825,11 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_FIELD( m_flHeadYaw, FIELD_FLOAT ), DEFINE_FIELD( m_flHeadPitch, FIELD_FLOAT ), DEFINE_FIELD( m_flOriginalYaw, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bInAScript, FIELD_BOOLEAN, "SpawnWithStartScripting" ), +#else DEFINE_FIELD( m_bInAScript, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_scriptState, FIELD_INTEGER ), DEFINE_FIELD( m_hCine, FIELD_EHANDLE ), DEFINE_CUSTOM_FIELD( m_ScriptArrivalActivity, ActivityDataOps() ), @@ -10662,6 +11850,9 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_KEYFIELD( m_bIgnoreUnseenEnemies, FIELD_BOOLEAN , "ignoreunseenenemies"), DEFINE_EMBEDDED( m_ShotRegulator ), DEFINE_FIELD( m_iDesiredWeaponState, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_FIELD( m_iLastHolsteredWeapon, FIELD_INTEGER ), +#endif // m_pSquad Saved specially in ai_saverestore.cpp DEFINE_KEYFIELD(m_SquadName, FIELD_STRING, "squadname" ), DEFINE_FIELD( m_iMySquadSlot, FIELD_INTEGER ), @@ -10704,6 +11895,12 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_FIELD( m_bImportanRagdoll, FIELD_BOOLEAN ), DEFINE_FIELD( m_bPlayerAvoidState, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_FriendlyFireOverride, FIELD_INTEGER, "FriendlyFireOverride" ), + + DEFINE_KEYFIELD( m_flSpeedModifier, FIELD_FLOAT, "BaseSpeedModifier" ), +#endif + // Satisfy classcheck // DEFINE_FIELD( m_ScheduleHistory, CUtlVector < AIScheduleChoice_t > ), @@ -10745,11 +11942,20 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_OUTPUT( m_OnForcedInteractionStarted, "OnForcedInteractionStarted" ), DEFINE_OUTPUT( m_OnForcedInteractionAborted, "OnForcedInteractionAborted" ), DEFINE_OUTPUT( m_OnForcedInteractionFinished, "OnForcedInteractionFinished" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnItemPickup, "OnItemPickup" ), +#endif // Inputs +#ifndef MAPBASE DEFINE_INPUTFUNC( FIELD_STRING, "SetRelationship", InputSetRelationship ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetEnemyFilter", InputSetEnemyFilter ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthFraction", InputSetHealthFraction ), +#else DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "BeginRappel", InputBeginRappel ), DEFINE_INPUTFUNC( FIELD_STRING, "SetSquad", InputSetSquad ), DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), @@ -10766,12 +11972,42 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpeedModifier", InputDisableSpeedModifier ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeedModRadius", InputSetSpeedModifierRadius ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeedModSpeed", InputSetSpeedModifierSpeed ), +#ifndef MAPBASE // Moved to CBaseCombatCharacter DEFINE_INPUTFUNC( FIELD_VOID, "HolsterWeapon", InputHolsterWeapon ), DEFINE_INPUTFUNC( FIELD_VOID, "HolsterAndDestroyWeapon", InputHolsterAndDestroyWeapon ), DEFINE_INPUTFUNC( FIELD_VOID, "UnholsterWeapon", InputUnholsterWeapon ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "ForceInteractionWithNPC", InputForceInteractionWithNPC ), DEFINE_INPUTFUNC( FIELD_STRING, "UpdateEnemyMemory", InputUpdateEnemyMemory ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFriendlyFire", InputSetFriendlyFire ), + + DEFINE_INPUTFUNC( FIELD_STRING, "GiveWeaponHolstered", InputGiveWeaponHolstered ), + DEFINE_INPUTFUNC( FIELD_STRING, "ChangeWeapon", InputChangeWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "PickupWeapon", InputPickupWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "PickupItem", InputPickupItem ), + DEFINE_OUTPUT( m_OnHolsterWeapon, "OnHolsterWeapon" ), + DEFINE_OUTPUT( m_OnUnholsterWeapon, "OnUnholsterWeapon" ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddCapabilities", InputAddCapabilities ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveCapabilities", InputRemoveCapabilities ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetCondition", InputSetCondition ), + DEFINE_INPUTFUNC( FIELD_STRING, "ClearCondition", InputClearCondition ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetHintGroup", InputSetHintGroup ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetThinkNPC", InputSetThinkNPC ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDistLook", InputSetDistLook ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDistTooFar", InputSetDistTooFar ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeedModifier", InputSetSpeedModifier ), + + DEFINE_OUTPUT( m_OnStateChange, "OnStateChange" ), +#endif + // Function pointers DEFINE_USEFUNC( NPCUse ), DEFINE_THINKFUNC( CallNPCThink ), @@ -10780,6 +12016,95 @@ BEGIN_DATADESC( CAI_BaseNPC ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPCs derive from." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetEnemy, "GetEnemy", "Get the NPC's current enemy." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetEnemy, "SetEnemy", "Set the NPC's current enemy." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetEnemyLKP, "GetEnemyLKP", "Get the last known position of the NPC's current enemy." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptFindEnemyMemory, "FindEnemyMemory", "Get information about the NPC's current enemy." ) + + DEFINE_SCRIPTFUNC( GetLastAttackTime, "Get the last time the NPC has used an attack (e.g. fired a bullet from a gun)." ) + DEFINE_SCRIPTFUNC( GetLastDamageTime, "Get the last time the NPC has been damaged." ) + DEFINE_SCRIPTFUNC( GetLastPlayerDamageTime, "Get the last time the NPC has been damaged by a player." ) + DEFINE_SCRIPTFUNC( GetLastEnemyTime, "Get the last time the NPC has seen an enemy." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetState, "GetNPCState", "Get the NPC's current state." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptWake, "Wake", "Awakens the NPC if it is currently asleep." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSleep, "Sleep", "Puts the NPC into a sleeping state." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSleepState, "GetSleepState", "Get the NPC's sleep state. (see AISS_ set of constants)" ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetSleepState, "SetSleepState", "Set the NPC's sleep state. (see AISS_ set of constants)" ) + DEFINE_SCRIPTFUNC( AddSleepFlags, "Add to the NPC's sleep flags. (see AI_SLEEP_ set of constants)" ) + DEFINE_SCRIPTFUNC( RemoveSleepFlags, "Remove from NPC's sleep flags. (see AI_SLEEP_ set of constants)" ) + DEFINE_SCRIPTFUNC( HasSleepFlags, "Return true if the NPC has the specified sleep flags. (see AI_SLEEP_ set of constants)" ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetHintGroup, "GetHintGroup", "Get the name of the NPC's hint group." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetHintNode, "GetHintNode", "Get the NPC's current AI hint." ) + + DEFINE_SCRIPTFUNC( CapabilitiesGet, "Get the capabilities the NPC currently possesses." ) + DEFINE_SCRIPTFUNC( CapabilitiesAdd, "Add capabilities to the NPC." ) + DEFINE_SCRIPTFUNC( CapabilitiesRemove, "Remove capabilities from the NPC." ) + DEFINE_SCRIPTFUNC( CapabilitiesClear, "Clear capabilities for the NPC." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActivity, "GetActivity", "Get the NPC's current activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActivityID, "GetActivityID", "Get the NPC's current activity ID." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivity, "SetActivity", "Set the NPC's current activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivityID, "SetActivityID", "Set the NPC's current activity ID." ) + DEFINE_SCRIPTFUNC( ResetActivity, "Reset the NPC's current activity." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSchedule, "GetSchedule", "Get the NPC's current schedule." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetScheduleID, "GetScheduleID", "Get the NPC's current schedule ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetSchedule, "SetSchedule", "Set the NPC's current schedule." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetScheduleID, "SetScheduleID", "Set the NPC's current schedule ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetTask, "GetTask", "Get the NPC's current task." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetTaskID, "GetTaskID", "Get the NPC's current task ID." ) + DEFINE_SCRIPTFUNC( ClearSchedule, "Clear the NPC's current schedule for the specified reason." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptHasCondition, "HasCondition", "Get whether the NPC has a condition." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptHasConditionID, "HasConditionID", "Get whether the NPC has a condition ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetCondition, "SetCondition", "Set a condition on the NPC." ) + DEFINE_SCRIPTFUNC_NAMED( SetCondition, "SetConditionID", "Set a condition on the NPC by ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptClearCondition, "ClearCondition", "Clear a condition on the NPC." ) + DEFINE_SCRIPTFUNC_NAMED( ClearCondition, "ClearConditionID", "Clear a condition on the NPC by ID." ) + + DEFINE_SCRIPTFUNC( IsMoving, "Check if the NPC is moving." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetExpresser, "GetExpresser", "Get a handle for this NPC's expresser." ) + + DEFINE_SCRIPTFUNC( IsCommandable, "Check if the NPC is commandable." ) + DEFINE_SCRIPTFUNC( IsInPlayerSquad, "Check if the NPC is in the player's squad." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetCine, "GetCine", "Get the NPC's currently running scripted sequence if it has one." ) + DEFINE_SCRIPTFUNC( GetScriptState, "Get the NPC's current scripted sequence state." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSquad, "GetSquad", "Get the NPC's squad if it has one." ) + DEFINE_SCRIPTFUNC( IsInSquad, "Returns true if the NPC is in a squad." ) + DEFINE_SCRIPTFUNC( NumWeaponsInSquad, "Get the number of weapons in a squad." ) + + // + // Hooks + // + BEGIN_SCRIPTHOOK( g_Hook_QueryHearSound, "QueryHearSound", FIELD_BOOLEAN, "Called when the NPC is deciding whether to hear a CSound or not." ) + DEFINE_SCRIPTHOOK_PARAM( "sound", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( g_Hook_QuerySeeEntity, "QuerySeeEntity", FIELD_BOOLEAN, "Called when the NPC is deciding whether to see an entity or not." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( g_Hook_TranslateActivity, "NPC_TranslateActivity", FIELD_VARIANT, "Called when the NPC is translating their current activity. The activity is provided in both string and ID form. Should return either an activity string or an activity ID. Return -1 to not translate." ) + DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING ) + DEFINE_SCRIPTHOOK_PARAM( "activity_id", FIELD_INTEGER ) + END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( g_Hook_TranslateSchedule, "NPC_TranslateSchedule", FIELD_VARIANT, "Called when the NPC is translating their current schedule. The schedule is provided in both string and ID form. Should return either a schedule string or a schedule ID. Return -1 to not translate." ) + DEFINE_SCRIPTHOOK_PARAM( "schedule", FIELD_CSTRING ) + DEFINE_SCRIPTHOOK_PARAM( "schedule_id", FIELD_INTEGER ) + END_SCRIPTHOOK() + +END_SCRIPTDESC(); +#endif + BEGIN_SIMPLE_DATADESC( AIScheduleState_t ) DEFINE_FIELD( iCurTask, FIELD_INTEGER ), DEFINE_FIELD( fTaskStatus, FIELD_INTEGER ), @@ -10834,6 +12159,9 @@ BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t ) DEFINE_FIELD( vecRelativeOrigin, FIELD_VECTOR ), DEFINE_FIELD( angRelativeAngles, FIELD_VECTOR ), DEFINE_FIELD( vecRelativeVelocity, FIELD_VECTOR ), +#ifdef MAPBASE + DEFINE_FIELD( vecRelativeEndPos, FIELD_VECTOR ), +#endif DEFINE_FIELD( flDelay, FIELD_FLOAT ), DEFINE_FIELD( flDistSqr, FIELD_FLOAT ), DEFINE_FIELD( iszMyWeapon, FIELD_STRING ), @@ -10842,6 +12170,9 @@ BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t ) DEFINE_FIELD( matDesiredLocalToWorld, FIELD_VMATRIX ), DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ), DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_FIELD( MiscCriteria, FIELD_STRING ),//DEFINE_UTLVECTOR( MiscCriteria, FIELD_EMBEDDED ), +#endif END_DATADESC() //------------------------------------- @@ -11069,7 +12400,7 @@ void CAI_BaseNPC::DiscardScheduleState() // for now, just go back to idle and let the AI figure out what to do. SetState( NPC_STATE_IDLE ); SetIdealState( NPC_STATE_IDLE ); - DevMsg(1, "Scripted Sequence stripped on level transition for %s\n", GetDebugName() ); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "Scripted Sequence stripped on level transition for %s\n", GetDebugName() ); } } @@ -11254,6 +12585,36 @@ bool CAI_BaseNPC::KeyValue( const char *szKeyName, const char *szValue ) return bResult; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + // Why was GetKeyValue("squadname") pointless again? + /* + if (FStrEq(szKeyName, "squadname")) + { + // "squadname" does change initially and in InputSetSquad(), but not in SetSquad() itself. + // Probably not in any other function that changes the squad either. This is needed so we get + // an accurate reading of the squad name. + GetSquad() ? + Q_strncpy(szValue, GetSquad()->GetName(), iMaxLen) : + Q_strncpy(szValue, "", iMaxLen); + } + else*/ if (FStrEq(szKeyName, "additionalequipment")) + { + if (GetActiveWeapon()) + Q_strncpy(szValue, GetActiveWeapon()->GetClassname(), iMaxLen); + else + return false; + } + else + return BaseClass::GetKeyValue(szKeyName, szValue, iMaxLen); + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Debug function to make this NPC freeze in place (or unfreeze). //----------------------------------------------------------------------------- @@ -11386,6 +12747,11 @@ CAI_BaseNPC::CAI_BaseNPC(void) m_bInChoreo = true; // assume so until call to UpdateEfficiency() SetCollisionGroup( COLLISION_GROUP_NPC ); + +#ifdef MAPBASE + m_iDynamicInteractionsAllowed = TRS_NONE; + m_flSpeedModifier = 1.0f; +#endif } //----------------------------------------------------------------------------- @@ -11418,7 +12784,7 @@ void CAI_BaseNPC::UpdateOnRemove(void) if ( !m_bDidDeathCleanup ) { if ( m_NPCState == NPC_STATE_DEAD ) - DevMsg( "May not have cleaned up on NPC death\n"); + CGMsg( 1, CON_GROUP_NPC_AI, "May not have cleaned up on NPC death\n" ); CleanupOnDeath( NULL, false ); } @@ -11533,6 +12899,7 @@ CAI_Pathfinder *CAI_BaseNPC::CreatePathfinder() return new CAI_Pathfinder( this ); } +#ifndef MAPBASE //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -11540,6 +12907,7 @@ void CAI_BaseNPC::InputSetRelationship( inputdata_t &inputdata ) { AddRelationship( inputdata.value.String(), inputdata.pActivator ); } +#endif //----------------------------------------------------------------------------- @@ -11552,6 +12920,39 @@ void CAI_BaseNPC::InputSetEnemyFilter( inputdata_t &inputdata ) m_hEnemyFilter = dynamic_cast(pFilter); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputSetHealthFraction( inputdata_t &inputdata ) +{ + // npc_helicopter uses SetHealth() instead of the regular NPC practice of TakeHealth() and TakeDamage(). + // It also also uses 50, 75, etc. and scales it by 0.01 for some reason. + // We're using the same model as InputSetHealth() and just letting npc_helicopter override it. No big deal. + // We're also adding support for its "whole number * 0.01" thing too. + float flFactor = inputdata.value.Float(); + if ( flFactor > 1.0f ) + { + flFactor *= 0.01f; + } + + // Excuse the complication... + float flNewHealth = (GetMaxHealth() * flFactor); + int iNewHealth = (int)flNewHealth; + if (flNewHealth < (GetMaxHealth() / 2)) + iNewHealth = (int)ceil(flNewHealth); + + int iDelta = abs(GetHealth() - iNewHealth); + if ( iNewHealth > GetHealth() ) + { + TakeHealth( iDelta, DMG_GENERIC ); + } + else if ( iNewHealth < GetHealth() ) + { + TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); + } +} +#else //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -11568,6 +12969,7 @@ void CAI_BaseNPC::InputSetHealth( inputdata_t &inputdata ) TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); } } +#endif //----------------------------------------------------------------------------- // Purpose: @@ -11610,7 +13012,11 @@ void CAI_BaseNPC::InputSetSquad( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CAI_BaseNPC::InputWake( inputdata_t &inputdata ) { +#ifdef MAPBASE + Wake( inputdata.pActivator ); +#else Wake(); +#endif // Check if we have a path to follow. This is normally done in StartNPC, // but putting the NPC to sleep will cancel it, so we have to do it again. @@ -11637,10 +13043,16 @@ void CAI_BaseNPC::InputForgetEntity( inputdata_t &inputdata ) { const char *pszEntityToForget = inputdata.value.String(); +#ifndef MAPBASE // I would assume this was here for a reason, but wildcards have nothing to do with ClearMemory(), so I really have no idea. if ( g_pDeveloper->GetInt() && pszEntityToForget[strlen( pszEntityToForget ) - 1] == '*' ) DevMsg( "InputForgetEntity does not support wildcards\n" ); +#endif +#ifdef MAPBASE + CBaseEntity *pEntity = gEntList.FindEntityGeneric( NULL, pszEntityToForget, this, inputdata.pActivator, inputdata.pCaller ); +#else CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, pszEntityToForget ); +#endif if ( pEntity ) { if ( GetEnemy() == pEntity ) @@ -11672,7 +13084,11 @@ void CAI_BaseNPC::InputIgnoreDangerSounds( inputdata_t &inputdata ) void CAI_BaseNPC::InputUpdateEnemyMemory( inputdata_t &inputdata ) { const char *pszEnemy = inputdata.value.String(); +#ifdef MAPBASE + CBaseEntity *pEnemy = gEntList.FindEntityByName( NULL, pszEnemy, this, inputdata.pActivator, inputdata.pCaller ); +#else CBaseEntity *pEnemy = gEntList.FindEntityByName( NULL, pszEnemy ); +#endif if( pEnemy ) { @@ -11812,7 +13228,11 @@ bool CAI_BaseNPC::CineCleanup() if ( m_hForcedInteractionPartner ) { // We've finished a forced interaction. Let the mapmaker know. +#ifdef MAPBASE + m_OnForcedInteractionFinished.FireOutput( m_hInteractionPartner, this ); +#else m_OnForcedInteractionFinished.FireOutput( this, this ); +#endif } // Clear interaction partner, because we're not running a scripted sequence anymore @@ -12141,7 +13561,19 @@ bool CAI_BaseNPC::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, { if ( pMoveGoal->directTrace.pObstruction ) { +#ifdef MAPBASE + CBaseEntity *pObstruction = pMoveGoal->directTrace.pObstruction; + CBasePropDoor *pPropDoor = dynamic_cast( pObstruction ); + + // It could be another entity (e.g. a func_brush) parented to a blank door + if (!pPropDoor && pObstruction->GetParent()) + { + pObstruction = pObstruction->GetParent(); + pPropDoor = dynamic_cast( pObstruction ); + } +#else CBasePropDoor *pPropDoor = dynamic_cast( pMoveGoal->directTrace.pObstruction ); +#endif if ( pPropDoor && OnUpcomingPropDoor( pMoveGoal, pPropDoor, distClear, pResult ) ) { return true; @@ -12233,7 +13665,11 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, m_hOpeningDoor = NULL; } +#ifdef MAPBASE + if ((CapabilitiesGet() & bits_CAP_DOORS_GROUP) && !pDoor->IsDoorLocked() && (pDoor->IsDoorClosed() || pDoor->IsDoorClosing()) && pDoor->PassesDoorFilter(this)) +#else if ((CapabilitiesGet() & bits_CAP_DOORS_GROUP) && !pDoor->IsDoorLocked() && (pDoor->IsDoorClosed() || pDoor->IsDoorClosing())) +#endif { AI_Waypoint_t *pOpenDoorRoute = NULL; @@ -12483,7 +13919,7 @@ static void AIMsgGuts( CAI_BaseNPC *pAI, unsigned flags, const char *pszMsg ) else pszFmt2 = "%s (%s: %d/%s) [%d]"; - DevMsg( pszFmt2, + CGMsg( 1, CON_GROUP_NPC_AI, pszFmt2, pszMsg, pAI->GetClassname(), pAI->entindex(), @@ -12680,7 +14116,11 @@ void CAI_BaseNPC::TestPlayerPushing( CBaseEntity *pEntity ) // Heuristic for determining if the player is pushing me away CBasePlayer *pPlayer = ToBasePlayer( pEntity ); +#ifdef MAPBASE + if ( pPlayer && !( pPlayer->GetFlags() & FL_NOTARGET ) && IRelationType( pPlayer ) > D_FR ) +#else if ( pPlayer && !( pPlayer->GetFlags() & FL_NOTARGET ) ) +#endif { if ( (pPlayer->m_nButtons & (IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT)) || pPlayer->GetAbsVelocity().AsVector2D().LengthSqr() > 50*50 ) @@ -12922,6 +14362,34 @@ void CAI_BaseNPC::InputSetSpeedModifierSpeed( inputdata_t &inputdata ) m_iSpeedModSpeed = inputdata.value.Int(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Get movement speed, multipled by modifier +//----------------------------------------------------------------------------- +float CAI_BaseNPC::GetSequenceGroundSpeed( CStudioHdr *pStudioHdr, int iSequence ) +{ + float t = SequenceDuration( pStudioHdr, iSequence ); + + if (t > 0) + { + return (GetSequenceMoveDist( pStudioHdr, iSequence ) * m_flSpeedModifier / t); + } + else + { + return 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hammer input to change the speed of the NPC (based on 1upD's npc_shadow_walker code) +// Not to be confused with the inputs above +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputSetSpeedModifier( inputdata_t &inputdata ) +{ + this->m_flSpeedModifier = inputdata.value.Float(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -12962,6 +14430,171 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions( void ) ScriptedNPCInteraction_t sInteraction; sInteraction.iszInteractionName = AllocPooledString( pkvNode->GetName() ); +#ifdef MAPBASE + // The method for parsing dynamic interactions has been reworked. + // Unknown values are now stored as response contexts to test against response criteria. + + bool bValidInteraction = true; + + // Default values + UTIL_StringToVector( sInteraction.vecRelativeOrigin.Base(), "0 0 0" ); + sInteraction.flDelay = 10.0; + sInteraction.flDistSqr = (DSS_MAX_DIST * DSS_MAX_DIST); + + // Misc. response criteria + const char *szCriteria = ""; + + KeyValues *pCurNode = pkvNode->GetFirstSubKey(); + const char *szName = NULL; + const char *szValue = NULL; + while (pCurNode) + { + szName = pCurNode->GetName(); + szValue = pCurNode->GetString(); + + if (!szName || !szValue) + { + DevWarning("ERROR: Invalid dynamic interaction string\n"); + pCurNode = pCurNode->GetNextKey(); + } + + if (!Q_strncmp(szName, "classname", 9)) + { + bool pass = false; + if (Q_strstr(szValue, "!=")) + { + szValue += 2; + pass = true; + } + + if (!FStrEq(GetClassname(), szValue)) + pass = !pass; + } + else if (!Q_strncmp(szName, "mapbase", 7)) + { + sInteraction.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; + } + else if (!Q_strncmp(szName, "trigger", 7)) + { + if (!Q_strncmp(szValue, "auto_in_combat", 14)) + sInteraction.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; + } + else if (!Q_strncmp(szValue, "loop_break_trigger", 18)) + { + char szTrigger[256]; + Q_strncpy( szTrigger, szValue, sizeof(szTrigger) ); + char *pszParam = strtok( szTrigger, " " ); + while (pszParam) + { + if ( !Q_strncmp( pszParam, "on_damage", 9) ) + { + sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_DAMAGE; + } + else if ( !Q_strncmp( pszParam, "on_flashlight_illum", 19) ) + { + sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM; + } + + pszParam = strtok(NULL," "); + } + } + else if (!Q_strncmp(szName, "origin_relative", 15)) + UTIL_StringToVector( sInteraction.vecRelativeOrigin.Base(), szValue ); + else if (!Q_strncmp(szName, "angles_relative", 15)) + { + sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + UTIL_StringToVector( sInteraction.angRelativeAngles.Base(), szValue ); + } + else if (!Q_strncmp(szName, "velocity_relative", 17)) + { + sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_VELOCITY; + UTIL_StringToVector( sInteraction.vecRelativeVelocity.Base(), szValue ); + } +#ifdef MAPBASE + else if (!Q_strncmp(szName, "end_position", 12)) + { + sInteraction.iFlags |= SCNPC_FLAG_TEST_END_POSITION; + UTIL_StringToVector( sInteraction.vecRelativeEndPos.Base(), szValue ); + } +#endif + + else if (!Q_strncmp(szName, "entry_sequence", 14)) + sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString( szValue ); + else if (!Q_strncmp(szName, "entry_activity", 14)) + sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID( szValue ); + + else if (!Q_strncmp(szName, "sequence", 8)) + sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( szValue ); + else if (!Q_strncmp(szName, "activity", 8)) + sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID( szValue ); + + else if (!Q_strncmp(szName, "exit_sequence", 13)) + sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString( szValue ); + else if (!Q_strncmp(szName, "exit_activity", 13)) + sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID(szValue); + + else if (!Q_strncmp(szName, "delay", 5)) + sInteraction.flDelay = atof(szValue); + else if (!Q_strncmp(szName, "origin_max_delta", 16)) + sInteraction.flDistSqr = atof(szValue); + + else if (!Q_strncmp(szName, "loop_in_action", 14) && !FStrEq(szValue, "0")) + sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION; + + else if (!Q_strncmp(szName, "dont_teleport_at_end", 20)) + { + if ( !Q_stricmp( szValue, "me" ) || !Q_stricmp( szValue, "both" ) ) + sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME; + else if ( !Q_stricmp( szValue, "them" ) || !Q_stricmp( szValue, "both" ) ) + sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM; + } + + else if (!Q_strncmp(szName, "needs_weapon", 12)) + { + if ( !Q_strncmp( szValue, "ME", 2 ) ) + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; + else if ( !Q_strncmp( szValue, "THEM", 4 ) ) + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; + else if ( !Q_strncmp( szValue, "BOTH", 4 ) ) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; + } + } + + else if (!Q_strncmp(szName, "weapon_mine", 11)) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; + sInteraction.iszMyWeapon = AllocPooledString( szValue ); + } + else if (!Q_strncmp(szName, "weapon_theirs", 13)) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; + sInteraction.iszTheirWeapon = AllocPooledString( szValue ); + } + + // Add anything else to our miscellaneous criteria + else + { + szCriteria = UTIL_VarArgs("%s,%s:%s", szCriteria, szName, szValue); + } + + pCurNode = pCurNode->GetNextKey(); + } + + if (!bValidInteraction) + { + DevMsg("Scripted interaction %s rejected by %s\n", pkvNode->GetName(), GetClassname()); + pkvNode = pkvNode->GetNextKey(); + continue; + } + + if (szCriteria[0] == ',') + { + szCriteria += 1; + sInteraction.MiscCriteria = AllocPooledString(szCriteria); + } +#else // Trigger method const char *pszTrigger = pkvNode->GetString( "trigger", NULL ); if ( pszTrigger ) @@ -13111,6 +14744,7 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions( void ) sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; sInteraction.iszTheirWeapon = AllocPooledString( pszWeaponName ); } +#endif // Add it to the list AddScriptedNPCInteraction( &sInteraction ); @@ -13348,6 +14982,11 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN //----------------------------------------------------------------------------- bool CAI_BaseNPC::CanRunAScriptedNPCInteraction( bool bForced ) { +#ifdef MAPBASE + if ( m_iDynamicInteractionsAllowed == TRS_FALSE && !bForced ) + return false; +#endif + if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_COMBAT ) return false; @@ -13431,6 +15070,11 @@ void CAI_BaseNPC::CheckForScriptedNPCInteractions( void ) if ( pInteraction->flNextAttemptTime > gpGlobals->curtime ) continue; +#ifdef MAPBASE + if ( !InteractionIsAllowed(pNPC, pInteraction) ) + continue; +#endif + Vector vecOrigin; QAngle angAngles; if ( InteractionCouldStart( pNPC, pInteraction, vecOrigin, angAngles ) ) @@ -13473,7 +15117,12 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) // If we have a damage filter that prevents us hurting the enemy, // don't interact with him, since most interactions kill the enemy. // Create a fake damage info to test it with. +#ifdef MAPBASE + // DMG_PREVENT_PHYSICS_FORCE can be used to identify dynamic interaction tests + CTakeDamageInfo tempinfo( this, this, vec3_origin, vec3_origin, 1.0, DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE ); +#else CTakeDamageInfo tempinfo( this, this, vec3_origin, vec3_origin, 1.0, DMG_BULLET ); +#endif if ( !pNPC->PassesDamageFilter( tempinfo ) ) continue; @@ -13484,23 +15133,82 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) continue; // Check the specific weapon type +#ifdef MAPBASE + if ( pInteraction->iszMyWeapon != NULL_STRING ) + { + const char *myweapon = STRING(pInteraction->iszMyWeapon); + bool pass = false; + if (Q_strstr(myweapon, "!=")) + { + myweapon += 2; + pass = true; + } + + if (Q_strstr(myweapon, "WEPCLASS")) + pass = (GetActiveWeapon()->WeaponClassFromString(myweapon) == GetActiveWeapon()->WeaponClassify()) ? !pass : pass; + else + pass = (GetActiveWeapon()->m_iClassname == pInteraction->iszMyWeapon) ? !pass : pass; + + if (!pass) + continue; + } +#else if ( pInteraction->iszMyWeapon != NULL_STRING && GetActiveWeapon()->m_iClassname != pInteraction->iszMyWeapon ) continue; +#endif } if ( pInteraction->iFlags & SCNPC_FLAG_NEEDS_WEAPON_THEM ) { if ( !pNPC->GetActiveWeapon() ) continue; +#ifdef MAPBASE + if ( pInteraction->iszTheirWeapon != NULL_STRING ) + { + const char *theirweapon = STRING(pInteraction->iszTheirWeapon); + bool pass = false; + if (Q_strstr(theirweapon, "!=")) + { + theirweapon += 2; + pass = true; + } + + if (Q_strstr(theirweapon, "WEPCLASS")) + pass = (pNPC->GetActiveWeapon()->WeaponClassFromString(theirweapon) == pNPC->GetActiveWeapon()->WeaponClassify()) ? !pass : pass; + else + pass = (pNPC->GetActiveWeapon()->m_iClassname == pInteraction->iszTheirWeapon) ? !pass : pass; + + if (!pass) + continue; + } +#else // Check the specific weapon type if ( pInteraction->iszTheirWeapon != NULL_STRING && pNPC->GetActiveWeapon()->m_iClassname != pInteraction->iszTheirWeapon ) continue; +#endif } // Script needs the other NPC, so make sure they're not dead if ( !pNPC->IsAlive() ) continue; +#ifdef MAPBASE + // If they have an interaction with the same name, it means we're not supposed to engage with them + bool bSame = false; + for ( int i2 = 0; i2 < pNPC->m_ScriptedInteractions.Count(); i2++ ) + { + // These strings are pooled, so this works + if (m_ScriptedInteractions[i].iszInteractionName == pNPC->m_ScriptedInteractions[i2].iszInteractionName) + { + bSame = true; + break; + } + } + + if (bSame) + continue; +#endif + // Use sequence? or activity? if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID ) { @@ -13517,6 +15225,50 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) continue; } +#ifdef MAPBASE + if (pInteraction->MiscCriteria != NULL_STRING) + { + // Test against response system criteria + AI_CriteriaSet set; + ModifyOrAppendCriteria( set ); + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if( pPlayer ) + pPlayer->ModifyOrAppendPlayerCriteria( set ); + ReAppendContextCriteria( set ); + + DevMsg("Testing %s misc criteria\n", STRING(pInteraction->MiscCriteria)); + + int index; + const char *criteriavalue; + char key[128]; + char value[128]; + const char *p = STRING(pInteraction->MiscCriteria); + while ( p ) + { + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL ); + + index = set.FindCriterionIndex(key); + if (index != -1) + { + criteriavalue = set.GetValue(index); + if (!Matcher_Match(value, criteriavalue)) + { + continue; + } + } + else + { + // Test with empty string in case our criteria is != or something + criteriavalue = ""; + if (!Matcher_Match(value, criteriavalue)) + { + continue; + } + } + } + } +#endif + pInteraction->bValidOnCurrentEnemy = true; bFound = true; @@ -13584,7 +15336,11 @@ void CAI_BaseNPC::CheckForcedNPCInteractions( void ) if ( m_hForcedInteractionPartner ) { // We've aborted a forced interaction. Let the mapmaker know. +#ifdef MAPBASE + m_OnForcedInteractionAborted.FireOutput( pNPC, this ); +#else m_OnForcedInteractionAborted.FireOutput( this, this ); +#endif } CleanupForcedInteraction(); @@ -13594,7 +15350,11 @@ void CAI_BaseNPC::CheckForcedNPCInteractions( void ) } StartScriptedNPCInteraction( pNPC, pInteraction, vecOrigin, angAngles ); +#ifdef MAPBASE + m_OnForcedInteractionStarted.FireOutput( pNPC, this ); +#else m_OnForcedInteractionStarted.FireOutput( this, this ); +#endif } @@ -13660,6 +15420,25 @@ bool CanNPCsTradePlaces( CAI_BaseNPC *pNPC1, CAI_BaseNPC *pNPC2, bool bDebug ) return true; } +#ifdef MAPBASE +bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction ) +{ + // Now that female citizens have hunter interactions, Alyx is vulnerable to being murdered by hunters *dynamically*! + // Citizens also have antlion interaction kill animations, so antlions could potentially murder her as well. + // + // Forced interactions should still work, but this prevents regular interactions from targeting vital allies. + // Hopefully there aren't any maps that already have hunters murder Barneys. + if (pOtherNPC->Classify() == CLASS_PLAYER_ALLY_VITAL) + return false; + + // This convar allows all NPCs to perform Mapbase interactions for both testing and player fun. + if (ai_dynint_always_enabled.GetBool() && m_iDynamicInteractionsAllowed != TRS_FALSE) + return true; + + // m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction(). + return !(pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE); +} +#endif //----------------------------------------------------------------------------- // Purpose: @@ -13801,6 +15580,34 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte return false; } +#ifdef MAPBASE + // Make sure we could fit at the sequence end position too. + if (pInteraction->iFlags & SCNPC_FLAG_TEST_END_POSITION) + { + VMatrix matTestToWorld; + matTestToWorld.SetupMatrixOrgAngles(pInteraction->vecRelativeEndPos, angMyCurrent); + MatrixMultiply( matMeToWorld, matTestToWorld, matLocalToWorld ); + Vector vecPos = (matLocalToWorld.GetTranslation()); + + // Start from the NPC's position. + AI_TraceHull( pOtherNPC->GetAbsOrigin(), vecPos, GetHullMins(), GetHullMaxs(), MASK_SOLID, &traceFilter, &tr ); + if ( tr.fraction != 1.0 ) + { + if ( bDebug ) + { + NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 ); + } + return false; + } + else if ( bDebug ) + { + //NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 0,255,0, 100, 1.0 ); + + NDebugOverlay::Axis( vecPos, angAngles, 20, true, 10.0 ); + } + } +#endif + // If the NPCs are swapping places during this interaction, make sure they can fit at each // others' origins before allowing the interaction. if ( !CanNPCsTradePlaces( this, pOtherNPC, bDebug ) ) @@ -13997,6 +15804,9 @@ void CAI_BaseNPC::ModifyOrAppendCriteria( AI_CriteriaSet& set ) set.AppendCriteria( "timesinceseenplayer", "-1" ); } +#ifdef MAPBASE + ModifyOrAppendEnemyCriteria(set, GetEnemy()); +#else // Append distance to my enemy if ( GetEnemy() ) { @@ -14006,8 +15816,96 @@ void CAI_BaseNPC::ModifyOrAppendCriteria( AI_CriteriaSet& set ) { set.AppendCriteria( "distancetoenemy", "-1" ); } +#endif + +#ifdef MAPBASE + // Append our gender + // I know scenes can use $gender01, but some rules now use more than scenes. + const char *modelname = STRING(GetModelName()); + if (modelname) + { + set.AppendCriteria( "gender", UTIL_VarArgs("%i", soundemitterbase->GetActorGender(modelname)) ); + } + + if (IsInSquad()) + { + set.AppendCriteria( "insquad", "1" ); + set.AppendCriteria( "squadmates", UTIL_VarArgs( "%i", GetSquad()->NumMembers() ) ); + set.AppendCriteria( "isleader", GetSquad()->IsLeader(this) ? "0" : "1" ); + } + else + { + set.AppendCriteria( "insquad", "0" ); + } +#endif } +#ifdef MAPBASE +// Went for a different structure that directly takes AI_CriteriaSet for modifiers +/* +#define GetModifiersFromCriteria( function, output, maxlen ) AI_CriteriaSet set; \ + function; \ + for (int i = 0; i < set.GetCount(); i++) { Q_snprintf(output, maxlen, "%s,%s:%s", output, set.GetName(i), set.GetValue(i)); } \ + memmove(output, output + 1, strlen(output + 1) + 1); \ + */ + +//----------------------------------------------------------------------------- +// Purpose: Appends enemy criteria so some classes could re-define it for, say, killing something +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ModifyOrAppendEnemyCriteria( AI_CriteriaSet& set, CBaseEntity *pEnemy ) +{ + if ( pEnemy ) + { + set.AppendCriteria( "enemy", pEnemy->GetClassname() ); + set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify()) + set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) ); + set.AppendCriteria( "timesincecombat", "-1" ); + } + else + { + if ( GetLastEnemyTime() == 0.0 ) + set.AppendCriteria( "timesincecombat", "999999.0" ); + else + set.AppendCriteria( "timesincecombat", UTIL_VarArgs( "%f", gpGlobals->curtime - GetLastEnemyTime() ) ); + + set.AppendCriteria( "distancetoenemy", "-1" ); + } +} + +/* +//----------------------------------------------------------------------------- +// Purpose: Gets enemy criteria in context form +//----------------------------------------------------------------------------- +void CAI_BaseNPC::GetEnemyCriteriaModifiers( CBaseEntity *pEnemy, char *szCriteria, int iMaxLen ) +{ + GetModifiersFromCriteria(ModifyOrAppendEnemyCriteria(set, pEnemy), szCriteria, iMaxLen); +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: Appends damage criteria for pain sounds, death sounds, etc. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ModifyOrAppendDamageCriteria( AI_CriteriaSet& set, const CTakeDamageInfo &info ) +{ + if ( info.GetAttacker() && info.GetAttacker() != this ) + set.AppendCriteria("attacker", info.GetAttacker()->GetClassname()); + else + set.AppendCriteria("attacker", ""); + + set.AppendCriteria("damagetype", UTIL_VarArgs("%i", info.GetDamageType())); +} + +/* +//----------------------------------------------------------------------------- +// Purpose: Gets damage criteria in context form +//----------------------------------------------------------------------------- +void CAI_BaseNPC::GetDamageCriteriaModifiers( const CTakeDamageInfo &info, char *szCriteria, int iMaxLen ) +{ + GetModifiersFromCriteria(ModifyOrAppendDamageCriteria(set, info), szCriteria, iMaxLen); +} +*/ +#endif + //----------------------------------------------------------------------------- // If I were crouching at my current location, could I shoot this target? //----------------------------------------------------------------------------- @@ -14050,6 +15948,11 @@ bool CAI_BaseNPC::IsCrouchedActivity( Activity activity ) case ACT_COVER_PISTOL_LOW: case ACT_COVER_SMG1_LOW: case ACT_RELOAD_SMG1_LOW: +#ifdef MAPBASE + //case ACT_RELOAD_AR2_LOW: + case ACT_RELOAD_PISTOL_LOW: + case ACT_RELOAD_SHOTGUN_LOW: +#endif return true; } diff --git a/mp/src/game/server/ai_basenpc.h b/mp/src/game/server/ai_basenpc.h index 7c65c0cc..88b43d91 100644 --- a/mp/src/game/server/ai_basenpc.h +++ b/mp/src/game/server/ai_basenpc.h @@ -95,6 +95,36 @@ extern bool AIStrongOpt( void ); // Max's of the box used to search for a weapon to pick up. 45x45x~8 ft. #define WEAPON_SEARCH_DELTA Vector( 540, 540, 100 ) +#ifdef MAPBASE +// Defines Mapbase's extended NPC response system usage. +#define EXPANDED_RESPONSE_SYSTEM_USAGE +#endif + +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE + +// This macro implements the response system on any NPC, particularly non-actors that can't use CAI_ExpresserHost. +// NOTE: Because of the lack of CAI_ExpresserHost, some Response System settings like odds, delays, etc. cannot be used. +// It's recommended to just use CAI_ExpresserHost if possible. +#define DeclareResponseSystem IResponseSystem *GetResponseSystem() { extern IResponseSystem *g_pResponseSystem; return g_pResponseSystem; } + +// Default CAI_ExpresserHost implementation for NPCs using CAI_ExpresserHost. +#define DeclareDefaultExpresser() virtual CAI_Expresser *CreateExpresser( void ) { m_pExpresser = new CAI_Expresser(this); if (!m_pExpresser) return NULL; m_pExpresser->Connect(this); return m_pExpresser; } \\ + virtual CAI_Expresser *GetExpresser() { return m_pExpresser; } \\ + virtual void PostConstructor(const char *szClassname) { BaseClass::PostConstructor(szClassname); CreateExpresser(); } \\ + private: \\ + CAI_Expresser *m_pExpresser; \\ + public: + +// Variant of DeclareDefaultExpresser() that doesn't implement its own PostConstructor. +// CreateExpresser() should still be called from there. +#define DeclareDefaultExpresser_ExistingPC() virtual CAI_Expresser *CreateExpresser( void ) { m_pExpresser = new CAI_Expresser(this); if (!m_pExpresser) return NULL; m_pExpresser->Connect(this); return m_pExpresser; } \\ + virtual CAI_Expresser *GetExpresser() { return m_pExpresser; } \\ + private: \\ + CAI_Expresser *m_pExpresser; \\ + public: + +#endif + enum Interruptability_t { GENERAL_INTERRUPTABILITY, @@ -316,6 +346,10 @@ struct UnreachableEnt_t #define SCNPC_FLAG_NEEDS_WEAPON_THEM ( 1 << 5 ) #define SCNPC_FLAG_DONT_TELEPORT_AT_END_ME ( 1 << 6 ) #define SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM ( 1 << 7 ) +#ifdef MAPBASE +#define SCNPC_FLAG_MAPBASE_ADDITION ( 1 << 8 ) +#define SCNPC_FLAG_TEST_END_POSITION ( 1 << 9 ) +#endif // ----------------------------------------- // Scripted NPC interaction trigger methods @@ -387,6 +421,10 @@ struct ScriptedNPCInteraction_t flNextAttemptTime = 0; iszMyWeapon = NULL_STRING; iszTheirWeapon = NULL_STRING; +#ifdef MAPBASE + vecRelativeEndPos = vec3_origin; + MiscCriteria = NULL_STRING; +#endif for ( int i = 0; i < SNPCINT_NUM_PHASES; i++) { @@ -403,6 +441,9 @@ struct ScriptedNPCInteraction_t Vector vecRelativeOrigin; // (forward, right, up) QAngle angRelativeAngles; Vector vecRelativeVelocity; // Desired relative velocity of the other NPC +#ifdef MAPBASE + Vector vecRelativeEndPos; // Relative position that the NPC must fit in +#endif float flDelay; // Delay before interaction can be used again float flDistSqr; // Max distance sqr from the relative origin the NPC is allowed to be to trigger string_t iszMyWeapon; // Classname of the weapon I'm holding, if any @@ -415,6 +456,13 @@ struct ScriptedNPCInteraction_t float flNextAttemptTime; +#ifdef MAPBASE + // Unrecognized keyvalues are tested against response criteria later. + // This was originally a CUtlVector that carries response contexts, but I couldn't get it working due to some CUtlVector-struct shenanigans. + // It works when we use a single string_t that's split and read each time the code runs, but feel free to improve on this. + string_t MiscCriteria; // CUtlVector +#endif + DECLARE_SIMPLE_DATADESC(); }; @@ -499,6 +547,9 @@ public: DECLARE_DATADESC(); DECLARE_SERVERCLASS(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif virtual int Save( ISave &save ); virtual int Restore( IRestore &restore ); @@ -510,6 +561,9 @@ public: virtual unsigned int PhysicsSolidMaskForEntity( void ) const; virtual bool KeyValue( const char *szKeyName, const char *szValue ); +#ifdef MAPBASE + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); +#endif //--------------------------------- @@ -565,6 +619,10 @@ public: // Thinking, including core thinking, movement, animation virtual void NPCThink( void ); +#ifdef MAPBASE + void InputSetThinkNPC( inputdata_t &inputdata ); +#endif + // Core thinking (schedules & tasks) virtual void RunAI( void );// core ai function! @@ -853,6 +911,11 @@ public: bool DidChooseEnemy() const { return !m_bSkippedChooseEnemy; } +#ifdef MAPBASE + void InputSetCondition( inputdata_t &inputdata ); + void InputClearCondition( inputdata_t &inputdata ); +#endif + private: CAI_ScheduleBits m_Conditions; CAI_ScheduleBits m_CustomInterruptConditions; //Bit string assembled by the schedule running, then @@ -897,6 +960,10 @@ public: void UpdateSleepState( bool bInPVS ); virtual void Wake( bool bFireOutput = true ); +#ifdef MAPBASE + // A version of Wake() that takes an activator + virtual void Wake( CBaseEntity *pActivator ); +#endif void Sleep(); bool WokeThisTick() const; @@ -926,6 +993,10 @@ public: Activity TranslateActivity( Activity idealActivity, Activity *pIdealWeaponActivity = NULL ); Activity NPC_TranslateActivity( Activity eNewActivity ); +#ifdef MAPBASE + Activity TranslateCrouchActivity( Activity baseAct ); + virtual Activity NPC_BackupActivity( Activity eNewActivity ); +#endif Activity GetActivity( void ) { return m_Activity; } virtual void SetActivity( Activity NewActivity ); Activity GetIdealActivity( void ) { return m_IdealActivity; } @@ -973,6 +1044,10 @@ public: const CAI_Senses * GetSenses() const { return m_pSenses; } void SetDistLook( float flDistLook ); +#ifdef MAPBASE + void InputSetDistLook( inputdata_t &inputdata ); + void InputSetDistTooFar( inputdata_t &inputdata ); +#endif virtual bool QueryHearSound( CSound *pSound ); virtual bool QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false ); @@ -1042,6 +1117,9 @@ public: CBaseEntity *GetEnemyOccluder(void); virtual void StartTargetHandling( CBaseEntity *pTargetEnt ); +#ifdef MAPBASE + void InputSetTarget( inputdata_t &inputdata ); +#endif //--------------------------------- @@ -1122,6 +1200,64 @@ private: public: CAI_MoveMonitor m_CommandMoveMonitor; +#ifdef MAPBASE + ThreeState_t m_FriendlyFireOverride = TRS_NONE; + virtual bool FriendlyFireEnabled(); + void InputSetFriendlyFire( inputdata_t &inputdata ); + + // Grenade-related functions from Combine soldiers ported to ai_basenpc so they could be shared. + // + // This is necessary because other NPCs can use them now and many instances where they were used relied on dynamic_casts. + virtual Vector GetAltFireTarget() { return GetEnemy() ? GetEnemy()->BodyTarget(Weapon_ShootPosition()) : vec3_origin; } + virtual void DelayGrenadeCheck(float delay) { ; } + virtual void AddGrenades( int inc, CBaseEntity *pLastGrenade = NULL ) { ; } +#endif + +#ifdef MAPBASE_VSCRIPT + // VScript stuff uses "VScript" instead of just "Script" to avoid + // confusion with NPC_STATE_SCRIPT or StartScripting + HSCRIPT VScriptGetEnemy(); + void VScriptSetEnemy( HSCRIPT pEnemy ); + Vector VScriptGetEnemyLKP(); + + HSCRIPT VScriptFindEnemyMemory( HSCRIPT pEnemy ); + + int VScriptGetState(); + + void VScriptWake( HSCRIPT hActivator ) { Wake( ToEnt(hActivator) ); } + void VScriptSleep() { Sleep(); } + + int VScriptGetSleepState() { return (int)GetSleepState(); } + void VScriptSetSleepState( int sleepState ) { SetSleepState( (AI_SleepState_t)sleepState ); } + + const char* VScriptGetHintGroup() { return STRING( GetHintGroup() ); } + HSCRIPT VScriptGetHintNode(); + + const char* ScriptGetActivity() { return GetActivityName( GetActivity() ); } + int ScriptGetActivityID() { return GetActivity(); } + void ScriptSetActivity( const char *szActivity ) { SetActivity( (Activity)GetActivityID( szActivity ) ); } + void ScriptSetActivityID( int iActivity ) { SetActivity((Activity)iActivity); } + + const char* VScriptGetSchedule(); + int VScriptGetScheduleID(); + void VScriptSetSchedule( const char *szSchedule ); + void VScriptSetScheduleID( int iSched ) { SetSchedule( iSched ); } + const char* VScriptGetTask(); + int VScriptGetTaskID(); + + bool VScriptHasCondition( const char *szCondition ) { return HasCondition( GetConditionID( szCondition ) ); } + bool VScriptHasConditionID( int iCondition ) { return HasCondition( iCondition ); } + void VScriptSetCondition( const char *szCondition ) { SetCondition( GetConditionID( szCondition ) ); } + void VScriptClearCondition( const char *szCondition ) { ClearCondition( GetConditionID( szCondition ) ); } + + HSCRIPT VScriptGetExpresser(); + + HSCRIPT VScriptGetCine(); + int GetScriptState() { return m_scriptState; } + + HSCRIPT VScriptGetSquad(); +#endif + //----------------------------------------------------- // Dynamic scripted NPC interactions //----------------------------------------------------- @@ -1137,6 +1273,11 @@ protected: void CheckForScriptedNPCInteractions( void ); void CalculateValidEnemyInteractions( void ); void CheckForcedNPCInteractions( void ); +#ifdef MAPBASE + // This is checked during automatic dynamic interactions, but not during forced interactions. + // This is so we can control interaction permissions while still letting forced interactions play when needed. + virtual bool InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction ); +#endif bool InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles ); virtual bool CanRunAScriptedNPCInteraction( bool bForced = false ); bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); } @@ -1162,10 +1303,20 @@ private: bool m_bCannotDieDuringInteraction; int m_iInteractionState; int m_iInteractionPlaying; +#ifdef MAPBASE +public: +#endif CUtlVector m_ScriptedInteractions; float m_flInteractionYaw; +#ifdef MAPBASE + // Allows mappers to control dynamic interactions. + // DI added by Mapbase requires this to be on TRS_TRUE (1). Others, like Alyx's interactions, only require TRS_NONE (2). + // TRS_FALSE (0) disables all dynamic interactions, including existing ones. + ThreeState_t m_iDynamicInteractionsAllowed; +#endif + public: //----------------------------------------------------- @@ -1209,6 +1360,10 @@ public: virtual void PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot ); virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set ); +#ifdef MAPBASE + virtual void ModifyOrAppendEnemyCriteria( AI_CriteriaSet& set, CBaseEntity *pEnemy ); + virtual void ModifyOrAppendDamageCriteria( AI_CriteriaSet& set, const CTakeDamageInfo &info ); +#endif protected: float SoundWaitTime() const { return m_flSoundWaitTime; } @@ -1226,6 +1381,11 @@ public: int CapabilitiesRemove( int capabilities ); void CapabilitiesClear( void ); +#ifdef MAPBASE + void InputAddCapabilities( inputdata_t &inputdata ); + void InputRemoveCapabilities( inputdata_t &inputdata ); +#endif + private: int m_afCapability; // tells us what a npc can/can't do. @@ -1562,8 +1722,25 @@ public: bool IsWeaponStateChanging( void ); void SetDesiredWeaponState( DesiredWeaponState_t iState ) { m_iDesiredWeaponState = iState; } +#ifdef MAPBASE + virtual bool DoHolster(void); + virtual bool DoUnholster(void); + + virtual bool ShouldUnholsterWeapon() { return GetState() == NPC_STATE_COMBAT; } + virtual bool CanUnholsterWeapon() { return IsWeaponHolstered(); } + + void InputGiveWeaponHolstered( inputdata_t &inputdata ); + void InputChangeWeapon( inputdata_t &inputdata ); + void InputPickupWeapon( inputdata_t &inputdata ); + void InputPickupItem( inputdata_t &inputdata ); +#endif + // NOTE: The Shot Regulator is used to manage the RangeAttack1 weapon. inline CAI_ShotRegulator* GetShotRegulator() { return &m_ShotRegulator; } +#ifdef MAPBASE + // A special function for ai_weaponmodifier. + inline void SetShotRegulator(CAI_ShotRegulator NewRegulator) { m_ShotRegulator = NewRegulator; } +#endif virtual void OnRangeAttack1(); protected: @@ -1582,6 +1759,10 @@ protected: float m_flLastAttackTime; // Last time that I attacked my current enemy float m_flLastEnemyTime; float m_flNextWeaponSearchTime; // next time to search for a better weapon +#ifdef MAPBASE +public: + int m_iLastHolsteredWeapon; +#endif string_t m_iszPendingWeapon; // THe NPC should create and equip this weapon. bool m_bIgnoreUnseenEnemies; @@ -1624,6 +1805,10 @@ public: void SetHintGroup( string_t name, bool bHintGroupNavLimiting = false ); bool IsLimitingHintGroups( void ) { return m_bHintGroupNavLimiting; } +#ifdef MAPBASE + void InputSetHintGroup( inputdata_t &inputdata ) { SetHintGroup(inputdata.value.StringID()); } +#endif + //--------------------------------- CAI_TacticalServices *GetTacticalServices() { return m_pTacticalServices; } @@ -1663,7 +1848,9 @@ public: //----------------------------------------------------- void InitRelationshipTable( void ); +#ifndef MAPBASE void AddRelationship( const char *pszRelationship, CBaseEntity *pActivator ); +#endif virtual void AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ); virtual void AddClassRelationship( Class_T nClass, Disposition_t nDisposition, int nPriority ); @@ -1695,7 +1882,9 @@ public: //--------------------------------- +#ifndef MAPBASE // Moved to CBaseCombatCharacter virtual CBaseEntity *FindNamedEntity( const char *pszName, IEntityFindFilter *pFilter = NULL ); +#endif //--------------------------------- // States @@ -1706,7 +1895,12 @@ public: virtual bool ShouldLookForBetterWeapon(); bool Weapon_IsBetterAvailable ( void ) ; virtual Vector Weapon_ShootPosition( void ); +#ifdef MAPBASE + virtual CBaseCombatWeapon* GiveWeapon( string_t iszWeaponName, bool bDiscardCurrent = true ); + virtual CBaseCombatWeapon* GiveWeaponHolstered( string_t iszWeaponName ); +#else virtual void GiveWeapon( string_t iszWeaponName ); +#endif virtual void OnGivenWeapon( CBaseCombatWeapon *pNewWeapon ) { } bool IsMovingToPickupWeapon(); virtual bool WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions); @@ -1803,16 +1997,27 @@ public: //--------------------------------- virtual void PickupWeapon( CBaseCombatWeapon *pWeapon ); +#ifdef MAPBASE + virtual void PickupItem( CBaseEntity *pItem ); +#else virtual void PickupItem( CBaseEntity *pItem ) { }; +#endif CBaseEntity* DropItem( const char *pszItemName, Vector vecPos, QAngle vecAng );// drop an item. //--------------------------------- // Inputs //--------------------------------- +#ifndef MAPBASE // Moved to CBaseCombatCharacter void InputSetRelationship( inputdata_t &inputdata ); +#endif void InputSetEnemyFilter( inputdata_t &inputdata ); +#ifdef MAPBASE + // This is virtual so npc_helicopter can override it + virtual void InputSetHealthFraction( inputdata_t &inputdata ); +#else void InputSetHealth( inputdata_t &inputdata ); +#endif void InputBeginRappel( inputdata_t &inputdata ); void InputSetSquad( inputdata_t &inputdata ); void InputWake( inputdata_t &inputdata ); @@ -1903,6 +2108,15 @@ public: COutputEvent m_OnForcedInteractionAborted; COutputEvent m_OnForcedInteractionFinished; +#ifdef MAPBASE + COutputEHANDLE m_OnHolsterWeapon; + COutputEHANDLE m_OnUnholsterWeapon; + + COutputEHANDLE m_OnItemPickup; + + COutputInt m_OnStateChange; +#endif + public: // use this to shrink the bbox temporarily void SetHullSizeNormal( bool force = false ); @@ -2116,6 +2330,15 @@ public: void InputSetSpeedModifierRadius( inputdata_t &inputdata ); void InputSetSpeedModifierSpeed( inputdata_t &inputdata ); +#ifdef MAPBASE + // Hammer input to change the speed of the NPC (based on 1upD's npc_shadow_walker code) + // Not to be confused with the inputs above + virtual float GetSequenceGroundSpeed( CStudioHdr *pStudioHdr, int iSequence ); + inline float GetSequenceGroundSpeed( int iSequence ) { return GetSequenceGroundSpeed( GetModelPtr(), iSequence ); } + void InputSetSpeedModifier( inputdata_t &inputdata ); + float m_flSpeedModifier; +#endif + virtual bool ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity ); bool m_bPlayerAvoidState; @@ -2675,7 +2898,11 @@ public: derivedClass::AccessClassScheduleIdSpaceDirect().Init( #derivedClass, BaseClass::GetSchedulingSymbols(), &BaseClass::AccessClassScheduleIdSpaceDirect() ); \ derivedClass::gm_SquadSlotIdSpace.Init( &CAI_BaseNPC::gm_SquadSlotNamespace, &BaseClass::gm_SquadSlotIdSpace); +#ifdef MAPBASE +#define ADD_CUSTOM_INTERACTION( interaction ) { CBaseCombatCharacter::AddInteractionWithString( interaction, #interaction ); } +#else #define ADD_CUSTOM_INTERACTION( interaction ) { interaction = CBaseCombatCharacter::GetInteractionID(); } +#endif #define ADD_CUSTOM_SQUADSLOT_NAMED(derivedClass,squadSlotName,squadSlotEN)\ if ( !derivedClass::gm_SquadSlotIdSpace.AddSymbol( squadSlotName, squadSlotEN, "squadslot", derivedClass::gm_pszErrorClassName ) ) return; @@ -3058,10 +3285,19 @@ public: // NOTE: YOU MUST DEFINE THE OUTPUTS IN YOUR CLASS'S DATADESC! // THE DO SO, INSERT THE FOLLOWING MACRO INTO YOUR CLASS'S DATADESC. // +#ifdef MAPBASE +#define DEFINE_BASENPCINTERACTABLE_DATADESC() \ + DEFINE_OUTPUT( m_OnAlyxStartedInteraction, "OnAlyxStartedInteraction" ), \ + DEFINE_OUTPUT( m_OnAlyxFinishedInteraction, "OnAlyxFinishedInteraction" ), \ + DEFINE_OUTPUT( m_OnHacked, "OnHacked" ), \ + DEFINE_INPUTFUNC( FIELD_VOID, "InteractivePowerDown", InputPowerdown ), \ + DEFINE_INPUTFUNC( FIELD_VOID, "Hack", InputDoInteraction ) +#else #define DEFINE_BASENPCINTERACTABLE_DATADESC() \ DEFINE_OUTPUT( m_OnAlyxStartedInteraction, "OnAlyxStartedInteraction" ), \ DEFINE_OUTPUT( m_OnAlyxFinishedInteraction, "OnAlyxFinishedInteraction" ), \ DEFINE_INPUTFUNC( FIELD_VOID, "InteractivePowerDown", InputPowerdown ) +#endif template class CNPCBaseInteractive : public NPC_CLASS, public INPCInteractive @@ -3077,6 +3313,13 @@ public: } +#ifdef MAPBASE + virtual void InputDoInteraction( inputdata_t &inputdata ) + { + NotifyInteraction(inputdata.pActivator ? inputdata.pActivator->MyNPCPointer() : NULL); + } +#endif + // Alyx specific interactions virtual void AlyxStartedInteraction( void ) { @@ -3092,6 +3335,9 @@ public: // Alyx specific interactions COutputEvent m_OnAlyxStartedInteraction; COutputEvent m_OnAlyxFinishedInteraction; +#ifdef MAPBASE + COutputEvent m_OnHacked; +#endif }; // diff --git a/mp/src/game/server/ai_basenpc_schedule.cpp b/mp/src/game/server/ai_basenpc_schedule.cpp index 5b7cb1f1..10bf02e5 100644 --- a/mp/src/game/server/ai_basenpc_schedule.cpp +++ b/mp/src/game/server/ai_basenpc_schedule.cpp @@ -470,7 +470,7 @@ CAI_Schedule *CAI_BaseNPC::GetNewSchedule( void ) // You may not be in combat state with no enemy!!! (sjb) 11/4/03 if( m_NPCState == NPC_STATE_COMBAT && !GetEnemy() ) { - DevMsg("**ERROR: Combat State with no enemy! slamming to ALERT\n"); + CGMsg( 1, CON_GROUP_NPC_AI, "**ERROR: Combat State with no enemy! slamming to ALERT\n" ); SetState( NPC_STATE_ALERT ); } @@ -693,7 +693,7 @@ void CAI_BaseNPC::MaintainSchedule ( void ) if ( !GetCurSchedule() || GetCurSchedule()->NumTasks() == 0 ) { - DevMsg("ERROR: Missing or invalid schedule!\n"); + CGMsg( 1, CON_GROUP_NPC_AI, "ERROR: Missing or invalid schedule!\n" ); SetActivity ( ACT_IDLE ); return; } @@ -981,6 +981,9 @@ bool CAI_BaseNPC::FindCoverFromEnemy( bool bNodesOnly, float flMinDistance, floa if (GetHintNode()) { GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) ); +#ifdef MAPBASE + if (GetHintNode()->GetIgnoreFacing() != HIF_NO) +#endif GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() ); } @@ -1004,7 +1007,7 @@ bool CAI_BaseNPC::FindCoverFromBestSound( Vector *pCoverPos ) } else { - DevMsg( 2, "Attempting to find cover from best sound, but best sound not founc.\n" ); + CGMsg( 2, CON_GROUP_NPC_AI, "Attempting to find cover from best sound, but best sound not founc.\n" ); } return false; @@ -1585,6 +1588,39 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) SetWait( pTask->flTaskData ); break; +#ifdef MAPBASE + case TASK_FACE_INTERACTION_ANGLES: + { + if ( !m_hForcedInteractionPartner ) + { + TaskFail( FAIL_NO_TARGET ); + return; + } + + // Get our running interaction from our partner, + // as this should only run with the NPC "receiving" the interaction + ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction(); + + // Get our target's origin + Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin(); + + // Face the angles the interaction actually wants us at, opposite to the partner + float angInteractionAngle = pInteraction->angRelativeAngles.y; + angInteractionAngle += 180.0f; + + GetMotor()->SetIdealYaw( CalcIdealYaw( vecTarget ) + angInteractionAngle ); + + if (FacingIdeal()) + TaskComplete(); + else + { + GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); + SetTurnActivity(); + } + } + break; +#endif + case TASK_FACE_ENEMY: { Vector vecEnemyLKP = GetEnemyLKP(); @@ -2707,7 +2743,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) case TASK_SOUND_ANGRY: { // sounds are complete as soon as we get here, cause we've already played them. - DevMsg( 2, "SOUND\n" ); + CGMsg( 2, CON_GROUP_NPC_AI, "SOUND\n" ); TaskComplete(); break; } @@ -2749,7 +2785,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) { if ( !m_hCine ) { - DevMsg( "Scripted sequence destroyed while in use\n" ); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "Scripted sequence destroyed while in use\n" ); TaskFail( FAIL_SCHEDULE_NOT_FOUND ); break; } @@ -2760,13 +2796,20 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) { if ( !m_hCine ) { - DevMsg( "Scripted sequence destroyed while in use\n" ); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "Scripted sequence destroyed while in use\n" ); TaskFail( FAIL_SCHEDULE_NOT_FOUND ); break; } string_t iszArrivalText; +#ifdef MAPBASE + if ( m_hCine->m_iszPreIdle != NULL_STRING ) + { + iszArrivalText = m_hCine->m_iszPreIdle; + } + else +#endif if ( m_hCine->m_iszEntry != NULL_STRING ) { iszArrivalText = m_hCine->m_iszEntry; @@ -2857,6 +2900,9 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) // if ( m_hCine->m_iszPreIdle != NULL_STRING ) { +#ifdef MAPBASE + m_hCine->OnPreIdleSequence( this ); +#endif m_hCine->StartSequence( ( CAI_BaseNPC * )this, m_hCine->m_iszPreIdle, false ); if ( FStrEq( STRING( m_hCine->m_iszPreIdle ), STRING( m_hCine->m_iszPlay ) ) ) { @@ -3097,7 +3143,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) default: { - DevMsg( "No StartTask entry for %s\n", TaskName( task ) ); + CGMsg( 1, CON_GROUP_NPC_AI, "No StartTask entry for %s\n", TaskName( task ) ); } break; } @@ -3244,7 +3290,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // Put a debugging check in here if (GetHintNode()->User() != this) { - DevMsg("Hint node (%s) being used by non-owner!\n",GetHintNode()->GetDebugName()); + CGMsg( 1, CON_GROUP_NPC_AI, "Hint node (%s) being used by non-owner!\n", GetHintNode()->GetDebugName() ); } if ( IsActivityFinished() ) @@ -3380,6 +3426,36 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) } break; +#ifdef MAPBASE + case TASK_FACE_INTERACTION_ANGLES: + { + if ( !m_hForcedInteractionPartner ) + { + TaskFail( FAIL_NO_TARGET ); + return; + } + + // Get our running interaction from our partner, + // as this should only run with the NPC "receiving" the interaction + ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction(); + + // Get our target's origin + Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin(); + + // Face the angles the interaction actually wants us at, opposite to the partner + float angInteractionAngle = pInteraction->angRelativeAngles.y; + angInteractionAngle += 180.0f; + + GetMotor()->SetIdealYawAndUpdate( CalcIdealYaw( vecTarget ) + angInteractionAngle, AI_KEEP_YAW_SPEED ); + + if (IsWaitFinished()) + { + TaskComplete(); + } + } + break; +#endif + case TASK_FIND_COVER_FROM_BEST_SOUND: { switch( GetTaskInterrupt() ) @@ -3668,9 +3744,22 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) { if( GetNavigator()->SetGoal(vecGoal) ) { +#ifdef MAPBASE + // Pushaway destinations could be an entire floor above. + // That would get frustrating. Only go to hints within a path distance of 300 units, + // only slightly above our initial search conditions. + if (GetNavigator()->BuildAndGetPathDistToGoal() < 300.0f) + { + pHint->NPCHandleStartNav(this, false); + pHint->DisableForSeconds( 0.1f ); // Force others to find their own. + TaskComplete(); + break; + } +#else pHint->DisableForSeconds( 0.1f ); // Force others to find their own. TaskComplete(); break; +#endif } } } @@ -3881,7 +3970,11 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) if ( m_hCine && m_hCine->IsTimeToStart() ) { TaskComplete(); +#ifdef MAPBASE + m_hCine->OnBeginSequence(this); +#else m_hCine->OnBeginSequence(); +#endif // If we have an entry, we have to play it first if ( m_hCine->m_iszEntry != NULL_STRING ) @@ -3904,7 +3997,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) } else if (!m_hCine) { - DevMsg( "Cine died!\n"); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "Cine died!\n" ); TaskComplete(); } else if ( IsRunningDynamicInteraction() ) @@ -3960,7 +4053,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) { if ( !m_hCine ) { - DevMsg( "Scripted sequence destroyed while in use\n" ); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "Scripted sequence destroyed while in use\n" ); TaskFail( FAIL_SCHEDULE_NOT_FOUND ); break; } @@ -3979,7 +4072,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) { if ( !m_hCine ) { - DevMsg( "Scripted sequence destroyed while in use\n" ); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "Scripted sequence destroyed while in use\n" ); TaskFail( FAIL_SCHEDULE_NOT_FOUND ); break; } @@ -4136,7 +4229,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) default: { - DevMsg( "No RunTask entry for %s\n", TaskName( pTask->iTask ) ); + CGMsg( 1, CON_GROUP_NPC_AI, "No RunTask entry for %s\n", TaskName( pTask->iTask ) ); TaskComplete(); } break; @@ -4308,7 +4401,7 @@ int CAI_BaseNPC::GetScriptCustomMoveSequence( void ) iSequence = LookupSequence( STRING( m_hCine->m_iszCustomMove ) ); if ( iSequence == ACTIVITY_NOT_AVAILABLE ) { - DevMsg( "SCRIPT_CUSTOM_MOVE: %s has no sequence:%s\n", GetClassname(), STRING(m_hCine->m_iszCustomMove) ); + CGMsg( 1, CON_GROUP_NPC_SCRIPTS, "SCRIPT_CUSTOM_MOVE: %s has no sequence:%s\n", GetClassname(), STRING(m_hCine->m_iszCustomMove) ); } } else if ( m_iszSceneCustomMoveSeq != NULL_STRING ) diff --git a/mp/src/game/server/ai_behavior.cpp b/mp/src/game/server/ai_behavior.cpp index 7071bd50..8822f750 100644 --- a/mp/src/game/server/ai_behavior.cpp +++ b/mp/src/game/server/ai_behavior.cpp @@ -411,6 +411,17 @@ void CAI_BehaviorBase::HandleAnimEvent( animevent_t *pEvent ) m_pBackBridge->BackBridge_HandleAnimEvent( pEvent ); } +#ifdef MAPBASE +//------------------------------------- + +bool CAI_BehaviorBase::CanUnholsterWeapon( void ) +{ + Assert( m_pBackBridge != NULL ); + + return m_pBackBridge->BackBridge_CanUnholsterWeapon(); +} +#endif + //------------------------------------- bool CAI_BehaviorBase::NotifyChangeBehaviorStatus( bool fCanFinishSchedule ) diff --git a/mp/src/game/server/ai_behavior.h b/mp/src/game/server/ai_behavior.h index f88a0c45..ce26ca61 100644 --- a/mp/src/game/server/ai_behavior.h +++ b/mp/src/game/server/ai_behavior.h @@ -130,6 +130,9 @@ public: void BridgeModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ); void BridgeTeleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ); void BridgeHandleAnimEvent( animevent_t *pEvent ); +#ifdef MAPBASE + bool BridgeCanUnholsterWeapon( void ); +#endif virtual void GatherConditions(); virtual void GatherConditionsNotActive() { return; } // Override this and your behavior will call this in place of GatherConditions() when your behavior is NOT the active one. @@ -215,6 +218,9 @@ protected: virtual void ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ); virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ); virtual void HandleAnimEvent( animevent_t *pEvent ); +#ifdef MAPBASE + virtual bool CanUnholsterWeapon( void ); +#endif virtual bool ShouldAlwaysThink(); @@ -361,6 +367,11 @@ public: virtual void BackBridge_HandleAnimEvent( animevent_t *pEvent ) = 0; +#ifdef MAPBASE + // For func_tank behavior + virtual bool BackBridge_CanUnholsterWeapon( void ) = 0; +#endif + //------------------------------------- }; @@ -457,6 +468,9 @@ public: Activity GetFlinchActivity( bool bHeavyDamage, bool bGesture ); bool OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); void HandleAnimEvent( animevent_t *pEvent ); +#ifdef MAPBASE + bool CanUnholsterWeapon( void ); +#endif bool ShouldAlwaysThink(); @@ -517,6 +531,11 @@ private: void BackBridge_HandleAnimEvent( animevent_t *pEvent ); +#ifdef MAPBASE + // For func_tank behavior + bool BackBridge_CanUnholsterWeapon( void ); +#endif + CAI_BehaviorBase **AccessBehaviors(); int NumBehaviors(); @@ -887,6 +906,15 @@ inline void CAI_BehaviorBase::BridgeHandleAnimEvent( animevent_t *pEvent ) HandleAnimEvent( pEvent ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- + +inline bool CAI_BehaviorBase::BridgeCanUnholsterWeapon( void ) +{ + return CanUnholsterWeapon(); +} +#endif + //----------------------------------------------------------------------------- template @@ -1462,6 +1490,16 @@ inline void CAI_BehaviorHost::BackBridge_HandleAnimEvent( animevent_t BaseClass::HandleAnimEvent( pEvent ); } +#ifdef MAPBASE +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_CanUnholsterWeapon( void ) +{ + return BaseClass::CanUnholsterWeapon(); +} +#endif + //------------------------------------- template @@ -1865,6 +1903,19 @@ inline void CAI_BehaviorHost::HandleAnimEvent( animevent_t *pEvent ) return BaseClass::HandleAnimEvent( pEvent ); } +#ifdef MAPBASE +//------------------------------------- + +template +inline bool CAI_BehaviorHost::CanUnholsterWeapon( void ) +{ + if ( m_pCurBehavior ) + return m_pCurBehavior->BridgeCanUnholsterWeapon(); + + return BaseClass::CanUnholsterWeapon(); +} +#endif + //------------------------------------- template diff --git a/mp/src/game/server/ai_behavior_assault.cpp b/mp/src/game/server/ai_behavior_assault.cpp index e02627cc..bacb4d6a 100644 --- a/mp/src/game/server/ai_behavior_assault.cpp +++ b/mp/src/game/server/ai_behavior_assault.cpp @@ -251,7 +251,12 @@ CAssaultPoint *CAI_AssaultBehavior::FindAssaultPoint( string_t iszAssaultPointNa CUtlVectorpAssaultPoints; CUtlVectorpClearAssaultPoints; +#ifdef MAPBASE + // Prevents non-assault points (e.g. rally points) from crashing the game + CAssaultPoint *pAssaultEnt = dynamic_cast(gEntList.FindEntityByName( NULL, iszAssaultPointName )); +#else CAssaultPoint *pAssaultEnt = (CAssaultPoint *)gEntList.FindEntityByName( NULL, iszAssaultPointName ); +#endif while( pAssaultEnt != NULL ) { diff --git a/mp/src/game/server/ai_behavior_fear.cpp b/mp/src/game/server/ai_behavior_fear.cpp index ea908ba6..d153b7e3 100644 --- a/mp/src/game/server/ai_behavior_fear.cpp +++ b/mp/src/game/server/ai_behavior_fear.cpp @@ -20,6 +20,9 @@ BEGIN_DATADESC( CAI_FearBehavior ) DEFINE_FIELD( m_hMovingToHint, FIELD_EHANDLE ), DEFINE_EMBEDDED( m_SafePlaceMoveMonitor ), DEFINE_FIELD( m_flDeferUntil, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_FIELD( m_hFearGoal, FIELD_EHANDLE ), +#endif END_DATADESC(); #define BEHAVIOR_FEAR_SAFETY_TIME 5 @@ -61,6 +64,11 @@ void CAI_FearBehavior::StartTask( const Task_t *pTask ) m_hSafePlaceHint = m_hMovingToHint; m_hSafePlaceHint->Lock( GetOuter() ); m_SafePlaceMoveMonitor.SetMark( GetOuter(), FEAR_SAFE_PLACE_TOLERANCE ); +#ifdef MAPBASE + m_hSafePlaceHint->NPCStartedUsing( GetOuter() ); + if (m_hFearGoal) + m_hFearGoal->m_OnArriveAtFearNode.FireOutput(m_hSafePlaceHint, GetOuter()); +#endif TaskComplete(); break; @@ -149,7 +157,11 @@ void CAI_FearBehavior::RunTask( const Task_t *pTask ) } else { +#ifdef MAPBASE + m_hMovingToHint->NPCHandleStartNav( GetOuter(), true ); +#else GetNavigator()->SetArrivalDirection( m_hMovingToHint->GetAbsAngles() ); +#endif } } break; @@ -231,6 +243,10 @@ void CAI_FearBehavior::ReleaseAllHints() // If I have a safe place, unlock it for others. m_hSafePlaceHint->Unlock(); +#ifdef MAPBASE + m_hSafePlaceHint->NPCStoppedUsing(GetOuter()); +#endif + // Don't make it available right away. I probably left for a good reason. // We also don't want to oscillate m_hSafePlaceHint->DisableForSeconds( 4.0f ); @@ -274,6 +290,22 @@ bool CAI_FearBehavior::CanSelectSchedule() if( GetOuter()->IRelationType(pEnemy) != D_FR ) return false; +#ifdef MAPBASE + // Don't run fear behavior if we've been ordered somewhere + if (GetOuter()->GetCommandGoal() != vec3_invalid) + return false; + + // Don't run fear behavior if we're running any non-follow behaviors + if (GetOuter()->GetRunningBehavior() && GetOuter()->GetRunningBehavior() != this && !FStrEq(GetOuter()->GetRunningBehavior()->GetName(), "Follow")) + return false; + + if (m_hFearGoal && m_iszFearTarget != NULL_STRING) + { + if (pEnemy->NameMatches(m_iszFearTarget) || pEnemy->ClassMatches(m_iszFearTarget)) + return true; + } +#endif + if( !pEnemy->ClassMatches("npc_hunter") ) return false; @@ -457,6 +489,9 @@ CAI_Hint *CAI_FearBehavior::FindFearWithdrawalDest() hintCriteria.AddHintType( HINT_PLAYER_ALLY_FEAR_DEST ); hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE_TO_PLAYER | bits_HINT_NOT_CLOSE_TO_ENEMY /*| bits_HINT_NODE_IN_VIEWCONE | bits_HINT_NPC_IN_NODE_FOV*/ ); +#ifdef MAPBASE + hintCriteria.SetFlag(bits_HINT_NODE_USE_GROUP); +#endif hintCriteria.AddIncludePosition( AI_GetSinglePlayer()->GetAbsOrigin(), ( ai_fear_player_dist.GetFloat() ) ); pHint = CAI_HintManager::FindHint( pOuter, hintCriteria ); @@ -478,6 +513,108 @@ CAI_Hint *CAI_FearBehavior::FindFearWithdrawalDest() return pHint; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +Activity CAI_FearBehavior::NPC_TranslateActivity( Activity activity ) +{ + if ( activity == ACT_IDLE && m_hSafePlaceHint && m_hSafePlaceHint->HintActivityName() != NULL_STRING ) + { + return GetOuter()->GetHintActivity(m_hSafePlaceHint->HintType(), (Activity)CAI_BaseNPC::GetActivityID( STRING(m_hSafePlaceHint->HintActivityName()) ) ); + } + return BaseClass::NPC_TranslateActivity( activity ); +} + +//----------------------------------------------------------------------------- +// Updates the fear goal's target. +//----------------------------------------------------------------------------- +void CAI_FearBehavior::OnRestore() +{ + BaseClass::OnRestore(); + + if (m_hFearGoal.Get() != NULL) + { + m_iszFearTarget = m_hFearGoal->m_target; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_FearBehavior::SetParameters( CAI_FearGoal *pGoal, string_t target ) +{ + m_hFearGoal = pGoal; + m_iszFearTarget = target; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//============================================================================= +//============================================================================= +// >AI_GOAL_FEAR +//============================================================================= +//============================================================================= +LINK_ENTITY_TO_CLASS( ai_goal_fear, CAI_FearGoal ); + +BEGIN_DATADESC( CAI_FearGoal ) + //DEFINE_KEYFIELD( m_iSomething, FIELD_INTEGER, "something" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + + // Outputs + //DEFINE_OUTPUT( m_OnSeeFearEntity, "OnSeeFearEntity" ), + DEFINE_OUTPUT( m_OnArriveAtFearNode, "OnArrivedAtNode" ), +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_FearGoal::EnableGoal( CAI_BaseNPC *pAI ) +{ + CAI_FearBehavior *pBehavior; + + if ( !pAI->GetBehavior( &pBehavior ) ) + { + return; + } + + pBehavior->SetParameters(this, m_target); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_FearGoal::DisableGoal( CAI_BaseNPC *pAI ) +{ + CAI_FearBehavior *pBehavior; + + if ( !pAI->GetBehavior( &pBehavior ) ) + { + return; + } + + pBehavior->SetParameters(NULL, NULL_STRING); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_FearGoal::InputActivate( inputdata_t &inputdata ) +{ + BaseClass::InputActivate( inputdata ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_FearGoal::InputDeactivate( inputdata_t &inputdata ) +{ + BaseClass::InputDeactivate( inputdata ); +} +#endif + AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FearBehavior ) DECLARE_TASK( TASK_FEAR_GET_PATH_TO_SAFETY_HINT ) @@ -531,6 +668,25 @@ AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FearBehavior ) //=============================================== //=============================================== +#ifdef MAPBASE + DEFINE_SCHEDULE + ( + SCHED_FEAR_STAY_IN_SAFE_PLACE, + + " Tasks" + " TASK_FEAR_WAIT_FOR_SAFETY 0" + "" + " Interrupts" + "" + " COND_NEW_ENEMY" + " COND_HEAR_DANGER" + " COND_FEAR_ENEMY_CLOSE" + " COND_FEAR_ENEMY_TOO_CLOSE" + " COND_CAN_RANGE_ATTACK1" + " COND_FEAR_SEPARATED_FROM_PLAYER" + " COND_ENEMY_DEAD" // Allows the fearful to follow the player when enemy dies + ); +#else DEFINE_SCHEDULE ( SCHED_FEAR_STAY_IN_SAFE_PLACE, @@ -547,6 +703,7 @@ AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FearBehavior ) " COND_CAN_RANGE_ATTACK1" " COND_FEAR_SEPARATED_FROM_PLAYER" ); +#endif AI_END_CUSTOM_SCHEDULE_PROVIDER() diff --git a/mp/src/game/server/ai_behavior_fear.h b/mp/src/game/server/ai_behavior_fear.h index b13848cb..e92ae689 100644 --- a/mp/src/game/server/ai_behavior_fear.h +++ b/mp/src/game/server/ai_behavior_fear.h @@ -20,6 +20,37 @@ #include "ai_behavior.h" +#ifdef MAPBASE +#include "ai_goalentity.h" + +//========================================================= +//========================================================= +class CAI_FearGoal : public CAI_GoalEntity +{ + DECLARE_CLASS( CAI_FearGoal, CAI_GoalEntity ); +public: + CAI_FearGoal() + { + } + + void EnableGoal( CAI_BaseNPC *pAI ); + void DisableGoal( CAI_BaseNPC *pAI ); + + // Inputs + virtual void InputActivate( inputdata_t &inputdata ); + virtual void InputDeactivate( inputdata_t &inputdata ); + + // Note that the outer is the caller in these outputs + //COutputEvent m_OnSeeFearEntity; + COutputEvent m_OnArriveAtFearNode; + + DECLARE_DATADESC(); + +protected: + // Put something here +}; +#endif + class CAI_FearBehavior : public CAI_SimpleBehavior { DECLARE_CLASS( CAI_FearBehavior, CAI_SimpleBehavior ); @@ -56,6 +87,17 @@ public: void BuildScheduleTestBits(); int TranslateSchedule( int scheduleType ); +#ifdef MAPBASE + virtual Activity NPC_TranslateActivity( Activity activity ); + + virtual void OnRestore(); + virtual void SetParameters( CAI_FearGoal *pGoal, string_t target ); + CHandle m_hFearGoal; + + // Points to goal's fear target + string_t m_iszFearTarget; +#endif + enum { diff --git a/mp/src/game/server/ai_behavior_follow.cpp b/mp/src/game/server/ai_behavior_follow.cpp index 4c77972c..bcd254a5 100644 --- a/mp/src/game/server/ai_behavior_follow.cpp +++ b/mp/src/game/server/ai_behavior_follow.cpp @@ -20,6 +20,9 @@ #ifdef HL2_EPISODIC #include "info_darknessmode_lightsource.h" +#ifdef MAPBASE + #include "globalstate.h" +#endif #endif // memdbgon must be the last include file in a .cpp file!!! @@ -403,7 +406,11 @@ bool CAI_FollowBehavior::SetFollowGoal( CAI_FollowGoal *pGoal, bool fFinishCurSc } SetFollowTarget( pGoal->GetGoalEntity() ); +#ifdef MAPBASE + Assert( pGoal->m_iFormation < AIF_NUM_FORMATIONS ); +#else Assert( pGoal->m_iFormation == AIF_SIMPLE || pGoal->m_iFormation == AIF_WIDE || pGoal->m_iFormation == AIF_MEDIUM || pGoal->m_iFormation == AIF_SIDEKICK || pGoal->m_iFormation == AIF_VORTIGAUNT ); +#endif SetParameters( AI_FollowParams_t( (AI_Formations_t)pGoal->m_iFormation ) ); m_hFollowGoalEnt = pGoal; m_flTimeUpdatedFollowPosition = 0; @@ -764,7 +771,12 @@ void CAI_FollowBehavior::GatherConditions( void ) #ifdef HL2_EPISODIC // Let followers know if the player is lit in the darkness +#ifdef MAPBASE + // If the darkness mode counter is 1, follow behavior is not affected by darkness. + if ( GetFollowTarget()->IsPlayer() && HL2GameRules()->IsAlyxInDarknessMode() && GlobalEntity_GetCounter("ep_alyx_darknessmode") != 1 ) +#else if ( GetFollowTarget()->IsPlayer() && HL2GameRules()->IsAlyxInDarknessMode() ) +#endif { if ( LookerCouldSeeTargetInDarkness( GetOuter(), GetFollowTarget() ) ) { @@ -907,6 +919,11 @@ void CAI_FollowBehavior::ClearFollowPoint() { if ( GetHintNode() && GetHintNode()->HintType() == HINT_FOLLOW_WAIT_POINT ) { +#ifdef MAPBASE + // If we were in range, we were probably already using it + if (GetFollowTarget() && (GetHintNode()->GetAbsOrigin() - GetFollowTarget()->GetAbsOrigin()).LengthSqr() < Square(MAX(m_FollowNavGoal.followPointTolerance, GetGoalRange()))) + GetHintNode()->NPCStoppedUsing(GetOuter()); +#endif GetHintNode()->Unlock(); SetHintNode( NULL ); } @@ -931,7 +948,14 @@ CAI_Hint *CAI_FollowBehavior::FindFollowPoint() CHintCriteria hintCriteria; hintCriteria.SetHintType( HINT_FOLLOW_WAIT_POINT ); +#ifdef MAPBASE + // NOTE: Does this make them stop following? + hintCriteria.SetGroup( GetOuter()->GetHintGroup() ); + hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE | bits_HINT_NODE_NEAREST | bits_HINT_NODE_USE_GROUP ); + //hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE | bits_HINT_NODE_NEAREST ); +#else hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE | bits_HINT_NODE_NEAREST ); +#endif // Add the search position hintCriteria.AddIncludePosition( GetGoalPosition(), MAX( m_FollowNavGoal.followPointTolerance, GetGoalRange() ) ); @@ -1033,6 +1057,9 @@ int CAI_FollowBehavior::SelectScheduleFollowPoints() { if ( bNewHint || distSqToPoint > WAIT_HINT_MIN_DIST ) return SCHED_FOLLOWER_GO_TO_WAIT_POINT; +#ifdef MAPBASE + GetHintNode()->NPCStartedUsing(GetOuter()); +#endif if ( !ShouldIgnoreFollowPointFacing() ) return SCHED_FOLLOWER_STAND_AT_WAIT_POINT; } diff --git a/mp/src/game/server/ai_behavior_follow.h b/mp/src/game/server/ai_behavior_follow.h index a0e43837..84cb0109 100644 --- a/mp/src/game/server/ai_behavior_follow.h +++ b/mp/src/game/server/ai_behavior_follow.h @@ -36,6 +36,9 @@ enum AI_Formations_t AIF_SIDEKICK, AIF_HUNTER, AIF_VORTIGAUNT, +#ifdef MAPBASE + AIF_NUM_FORMATIONS, +#endif }; enum AI_FollowFormationFlags_t diff --git a/mp/src/game/server/ai_behavior_lead.cpp b/mp/src/game/server/ai_behavior_lead.cpp index 9cee1951..a6d7c0ea 100644 --- a/mp/src/game/server/ai_behavior_lead.cpp +++ b/mp/src/game/server/ai_behavior_lead.cpp @@ -543,8 +543,12 @@ int CAI_LeadBehavior::SelectSchedule() if ( !m_flWeaponSafetyTimeOut || (m_flWeaponSafetyTimeOut > gpGlobals->curtime) ) return SCHED_LEAD_PLAYERNEEDSWEAPON; +#ifdef MAPBASE + pFollower->GiveNamedItem( STRING(m_weaponname) ); +#else string_t iszItem = AllocPooledString( "weapon_bugbait" ); pFollower->GiveNamedItem( STRING(iszItem) ); +#endif } } @@ -1649,6 +1653,9 @@ public: private: string_t m_iszWeaponName; +#ifdef MAPBASE + float m_flTimeoutTime = 60; +#endif string_t m_iszMissingWeaponConceptModifier; DECLARE_DATADESC(); @@ -1664,6 +1671,9 @@ LINK_ENTITY_TO_CLASS( ai_goal_lead_weapon, CAI_LeadGoal_Weapon ); BEGIN_DATADESC( CAI_LeadGoal_Weapon ) DEFINE_KEYFIELD( m_iszWeaponName, FIELD_STRING, "WeaponName"), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flTimeoutTime, FIELD_FLOAT, "TimeoutTime" ), +#endif DEFINE_KEYFIELD( m_iszMissingWeaponConceptModifier, FIELD_STRING, "MissingWeaponConceptModifier"), END_DATADESC() @@ -1688,6 +1698,10 @@ void CAI_LeadGoal_Weapon::InputActivate( inputdata_t &inputdata ) CAI_LeadBehavior *pBehavior = GetLeadBehavior(); if ( pBehavior ) { +#ifdef MAPBASE + pBehavior->SetWaitForWeapon( m_iszWeaponName, m_flTimeoutTime ); +#else pBehavior->SetWaitForWeapon( m_iszWeaponName ); +#endif } } diff --git a/mp/src/game/server/ai_behavior_lead.h b/mp/src/game/server/ai_behavior_lead.h index d59a87d3..2104b1f2 100644 --- a/mp/src/game/server/ai_behavior_lead.h +++ b/mp/src/game/server/ai_behavior_lead.h @@ -125,7 +125,11 @@ public: bool Connect( CAI_LeadBehaviorHandler *); bool Disconnect( CAI_LeadBehaviorHandler *); +#ifdef MAPBASE + void SetWaitForWeapon( string_t iszWeaponName, float flTimeout = 60 ) { m_weaponname = iszWeaponName; m_flWeaponSafetyTimeOut = gpGlobals->curtime + flTimeout; } +#else void SetWaitForWeapon( string_t iszWeaponName ) { m_weaponname = iszWeaponName; m_flWeaponSafetyTimeOut = gpGlobals->curtime + 60; } +#endif enum { diff --git a/mp/src/game/server/ai_behavior_rappel.cpp b/mp/src/game/server/ai_behavior_rappel.cpp index 7dfe4e14..a9a75e91 100644 --- a/mp/src/game/server/ai_behavior_rappel.cpp +++ b/mp/src/game/server/ai_behavior_rappel.cpp @@ -125,6 +125,9 @@ bool CAI_RappelBehavior::KeyValue( const char *szKeyName, const char *szValue ) void CAI_RappelBehavior::Precache() { +#ifdef MAPBASE + CBaseEntity::PrecacheModel( "cable/cable_rappel.vmt" ); +#endif CBaseEntity::PrecacheModel( "cable/cable.vmt" ); } @@ -300,7 +303,14 @@ void CAI_RappelBehavior::GatherConditions() if( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) { // Shoot at the enemy so long as I'm six feet or more above them. +#ifdef MAPBASE + // There seems to be an underlying issue here. COND_CAN_RANGE_ATTACK1 should not be valid without an enemy, + // but crashes have been reported from GetEnemy() returning null in this code. + Assert( GetEnemy() ); + if( GetEnemy() && (GetAbsOrigin().z - GetEnemy()->GetAbsOrigin().z >= 36.0f) && GetOuter()->GetShotRegulator()->ShouldShoot() ) +#else if( (GetAbsOrigin().z - GetEnemy()->GetAbsOrigin().z >= 36.0f) && GetOuter()->GetShotRegulator()->ShouldShoot() ) +#endif { Activity activity = GetOuter()->TranslateActivity( ACT_GESTURE_RANGE_ATTACK1 ); Assert( activity != ACT_INVALID ); @@ -386,7 +396,11 @@ void CAI_RappelBehavior::CreateZipline() if( attachment > 0 ) { CBeam *pBeam; +#ifdef MAPBASE + pBeam = CBeam::BeamCreate( "cable/cable_rappel.vmt", 1 ); +#else pBeam = CBeam::BeamCreate( "cable/cable.vmt", 1 ); +#endif pBeam->SetColor( 150, 150, 150 ); pBeam->SetWidth( 0.3 ); pBeam->SetEndWidth( 0.3 ); diff --git a/mp/src/game/server/ai_behavior_standoff.cpp b/mp/src/game/server/ai_behavior_standoff.cpp index 4f5f7469..0792df23 100644 --- a/mp/src/game/server/ai_behavior_standoff.cpp +++ b/mp/src/game/server/ai_behavior_standoff.cpp @@ -516,7 +516,11 @@ int CAI_StandoffBehavior::SelectScheduleCheckCover( void ) if ( GetOuter()->GetShotRegulator()->IsInRestInterval() ) { StandoffMsg( "Regulated to not shoot\n" ); +#ifdef MAPBASE + if ( GetHintType() == HINT_TACTICAL_COVER_LOW || GetHintType() == HINT_TACTICAL_COVER_MED ) +#else if ( GetHintType() == HINT_TACTICAL_COVER_LOW ) +#endif SetPosture( AIP_CROUCHING ); else SetPosture( AIP_STANDING ); @@ -1067,10 +1071,18 @@ void CAI_StandoffBehavior::UnlockHintNode() Activity CAI_StandoffBehavior::GetCoverActivity() { +#ifdef MAPBASE + // This does two things: + // A. Allows medium cover nodes to be used, kind of. + // B. GetCoverActivity() already checks everything we checked here. + Activity coveract = GetOuter()->GetCoverActivity( GetHintNode() ); + return coveract == ACT_IDLE ? ACT_INVALID : coveract; +#else CAI_Hint *pHintNode = GetHintNode(); if ( pHintNode && pHintNode->HintType() == HINT_TACTICAL_COVER_LOW ) return GetOuter()->GetCoverActivity( pHintNode ); return ACT_INVALID; +#endif } @@ -1114,7 +1126,12 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() { if ( !mappings[i].pszWeapon || stricmp( mappings[i].pszWeapon, pszWeaponClass ) == 0 ) { +#ifdef MAPBASE + // Check backup activity + if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) || HaveSequenceForActivity( GetOuter()->Weapon_BackupActivity( mappings[i].translation ) ) ) +#else if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) ) +#endif { Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() ); m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].translation ); diff --git a/mp/src/game/server/ai_concommands.cpp b/mp/src/game/server/ai_concommands.cpp index 8c647f55..e004dca1 100644 --- a/mp/src/game/server/ai_concommands.cpp +++ b/mp/src/game/server/ai_concommands.cpp @@ -390,6 +390,152 @@ void CC_NPC_Focus( const CCommand &args ) static ConCommand npc_focus("npc_focus", CC_NPC_Focus, "Displays red line to NPC's enemy (if has one) and blue line to NPC's target entity (if has one)\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at", FCVAR_CHEAT); ConVar npc_create_equipment("npc_create_equipment", ""); + +#ifdef MAPBASE +extern int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 ); +extern bool UtlStringLessFunc( const CUtlString &lhs, const CUtlString &rhs ); + +//------------------------------------------------------------------------------ +// Purpose: Create an NPC of the given type +//------------------------------------------------------------------------------ +class CNPCCreateAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback +{ +public: + virtual bool CreateAimed() { return false; } + + virtual void CommandCallback( const CCommand &args ) + { + MDLCACHE_CRITICAL_SECTION(); + + bool allowPrecache = CBaseEntity::IsPrecacheAllowed(); + CBaseEntity::SetAllowPrecache( true ); + + // Try to create entity + CAI_BaseNPC *baseNPC = dynamic_cast< CAI_BaseNPC * >( CreateEntityByName(args[1]) ); + if (baseNPC) + { + baseNPC->KeyValue( "additionalequipment", npc_create_equipment.GetString() ); + + if ( args.ArgC() == 3 ) + { + baseNPC->SetName( AllocPooledString( args[2] ) ); + } + else if ( args.ArgC() > 3 ) + { + baseNPC->SetName( AllocPooledString( args[2] ) ); + + // Pass in any additional parameters. + for ( int i = 3; i + 1 < args.ArgC(); i += 2 ) + { + const char *pKeyName = args[i]; + const char *pValue = args[i+1]; + baseNPC->KeyValue( pKeyName, pValue ); + } + } + + DispatchSpawn(baseNPC); + + // Now attempt to drop into the world + CBasePlayer* pPlayer = UTIL_GetCommandClient(); + trace_t tr; + Vector forward; + QAngle angles; + pPlayer->EyeVectors( &forward ); + + bool bCreateAimed = CreateAimed(); + if (bCreateAimed) + { + VectorAngles( forward, angles ); + angles.x = 0; + angles.z = 0; + } + + // Pass through the player's vehicle + CTraceFilterSkipTwoEntities filter( pPlayer, pPlayer->GetVehicleEntity(), COLLISION_GROUP_NONE ); + AI_TraceLine(pPlayer->EyePosition(), + pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID, + &filter, &tr ); + + if ( tr.fraction != 1.0) + { + if (baseNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY) + { + Vector pos = tr.endpos - forward * 36; + baseNPC->Teleport( &pos, bCreateAimed ? &angles : NULL, NULL ); + } + else + { + // Raise the end position a little up off the floor, place the npc and drop him down + tr.endpos.z += 12; + baseNPC->Teleport( &tr.endpos, bCreateAimed ? &angles : NULL, NULL ); + UTIL_DropToFloor( baseNPC, MASK_NPCSOLID ); + } + + // Now check that this is a valid location for the new npc to be + Vector vUpBit = baseNPC->GetAbsOrigin(); + vUpBit.z += 1; + + AI_TraceHull( baseNPC->GetAbsOrigin(), vUpBit, baseNPC->GetHullMins(), baseNPC->GetHullMaxs(), + MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr ); + if ( tr.startsolid || (tr.fraction < 1.0) ) + { + baseNPC->SUB_Remove(); + DevMsg("Can't create %s. Bad Position!\n",args[1]); + NDebugOverlay::Box(baseNPC->GetAbsOrigin(), baseNPC->GetHullMins(), baseNPC->GetHullMaxs(), 255, 0, 0, 0, 0); + } + } + else if (bCreateAimed) + { + baseNPC->Teleport( NULL, &angles, NULL ); + } + + baseNPC->Activate(); + } + CBaseEntity::SetAllowPrecache( allowPrecache ); + } + + virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + if ( !g_pGameRules ) + { + return 0; + } + + const char *cmdname = CreateAimed() ? "npc_create_aimed" : "npc_create"; + + char *substring = (char *)partial; + if ( Q_strstr( partial, cmdname ) ) + { + substring = (char *)partial + strlen( cmdname ) + 1; + } + + int checklen = Q_strlen( substring ); + + if (checklen <= 0) + { + // Only show classnames prefixed with "npc" unless the user starts typing other characters + substring = "npc"; + checklen = 3; + } + + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + return EntityFactory_AutoComplete( cmdname, commands, symbols, substring, checklen ); + } +}; + +static CNPCCreateAutoCompletionFunctor g_NPCCreateAutoComplete; +static ConCommand npc_create("npc_create", &g_NPCCreateAutoComplete, "Creates an NPC of the given type where the player is looking (if the given NPC can actually stand at that location).\n\tArguments: {npc_class_name}", FCVAR_CHEAT, &g_NPCCreateAutoComplete); + +class CNPCCreateAimedAutoCompletionFunctor : public CNPCCreateAutoCompletionFunctor +{ +public: + virtual bool CreateAimed() { return true; } +}; + +static CNPCCreateAimedAutoCompletionFunctor g_NPCCreateAimedAutoComplete; + +static ConCommand npc_create_aimed("npc_create_aimed", &g_NPCCreateAimedAutoComplete, "Creates an NPC aimed away from the player of the given type where the player is looking (if the given NPC can actually stand at that location).\n\tArguments: {npc_class_name}", FCVAR_CHEAT, &g_NPCCreateAimedAutoComplete); +#else //------------------------------------------------------------------------------ // Purpose: Create an NPC of the given type //------------------------------------------------------------------------------ @@ -411,6 +557,20 @@ void CC_NPC_Create( const CCommand &args ) { baseNPC->SetName( AllocPooledString( args[2] ) ); } +#ifdef MAPBASE + else if ( args.ArgC() > 3 ) + { + baseNPC->SetName( AllocPooledString( args[2] ) ); + + // Pass in any additional parameters. + for ( int i = 3; i + 1 < args.ArgC(); i += 2 ) + { + const char *pKeyName = args[i]; + const char *pValue = args[i+1]; + baseNPC->KeyValue( pKeyName, pValue ); + } + } +#endif DispatchSpawn(baseNPC); // Now attempt to drop into the world @@ -526,6 +686,7 @@ void CC_NPC_Create_Aimed( const CCommand &args ) CBaseEntity::SetAllowPrecache( allowPrecache ); } static ConCommand npc_create_aimed("npc_create_aimed", CC_NPC_Create_Aimed, "Creates an NPC aimed away from the player of the given type where the player is looking (if the given NPC can actually stand at that location). Note that this only works for npc classes that are already in the world. You can not create an entity that doesn't have an instance in the level.\n\tArguments: {npc_class_name}", FCVAR_CHEAT); +#endif //------------------------------------------------------------------------------ // Purpose: Destroy unselected NPCs @@ -697,6 +858,134 @@ void CC_NPC_Reset( void ) } static ConCommand npc_reset("npc_reset", CC_NPC_Reset, "Reloads schedules for all NPC's from their script files\n\tArguments: -none-", FCVAR_CHEAT); +#ifdef MAPBASE +extern bool UtlStringLessFunc( const CUtlString &lhs, const CUtlString &rhs ); + +//------------------------------------------------------------------------------ +// Purpose : Auto-completes with entities in the entity list, but only uses NPC-derived entities. +// Input : cmdname - The name of the command. +// &commands - Where the complete autocompletes should be sent to. +// substring - The current search query. (only pool entities that start with this) +// checklen - The number of characters to check. +// Output : A pointer to a cUtlRBTRee containing all of the entities. +//------------------------------------------------------------------------------ +static int AutoCompleteNPCs(const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0) +{ + CBaseEntity *pos = NULL; + while ((pos = gEntList.NextEnt(pos)) != NULL) + { + if (!pos->IsNPC()) + continue; + + const char *name = pos->GetClassname(); + if (pos->GetEntityName() == NULL_STRING || Q_strnicmp(STRING(pos->GetEntityName()), substring, checklen)) + { + if (Q_strnicmp(pos->GetClassname(), substring, checklen)) + continue; + } + else + name = STRING(pos->GetEntityName()); + + CUtlString sym = name; + int idx = symbols.Find(sym); + if (idx == symbols.InvalidIndex()) + { + symbols.Insert(sym); + } + + // Too many + if (symbols.Count() >= COMMAND_COMPLETION_MAXITEMS) + break; + } + + // Now fill in the results + for (int i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder(i)) + { + const char *name = symbols[i].String(); + + char buf[512]; + Q_strncpy(buf, name, sizeof(buf)); + Q_strlower(buf); + + CUtlString command; + command = CFmtStr("%s %s", cmdname, buf); + commands.AddToTail(command); + } + + return symbols.Count(); +} + +//------------------------------------------------------------------------------ +// There's a big set of NPC debug commands that do similar operations and +// can fall under this base class for auto-completion, etc. +//------------------------------------------------------------------------------ +class CNPCDebugAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback +{ +public: + virtual const char *CommandName() { return NULL; } + virtual void CommandCallback( const CCommand &args ) + { + SetDebugBits( UTIL_GetCommandClient(), args[1], OVERLAY_NPC_NEAREST_BIT ); + } + + virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + if ( !g_pGameRules ) + { + return 0; + } + + const char *cmdname = CommandName(); + + char *substring = (char *)partial; + if ( Q_strstr( partial, cmdname ) ) + { + substring = (char *)partial + strlen( cmdname ) + 1; + } + + int checklen = Q_strlen( substring ); + + if (checklen == 0 || atoi(substring) != 0) + { + // Must be the picker or an entity index + return 0; + } + + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + return AutoCompleteNPCs(cmdname, commands, symbols, substring, checklen); + } +}; + +#define NPCDebugCommand(name, functor, bit, help) class CNPC##functor##AutoCompletionFunctor : public CNPCDebugAutoCompletionFunctor \ +{ \ +public: \ + virtual const char *CommandName() { return #name; } \ + virtual void CommandCallback( const CCommand &args ) \ + { \ + SetDebugBits( UTIL_GetCommandClient(), args[1], bit ); \ + } \ +}; \ +static CNPC##functor##AutoCompletionFunctor g_NPC##functor##AutoCompletionFunctor; \ +static ConCommand name(#name, &g_NPC##functor##AutoCompletionFunctor, help, FCVAR_CHEAT, &g_NPC##functor##AutoCompletionFunctor); + +NPCDebugCommand( npc_nearest, Nearest, OVERLAY_NPC_NEAREST_BIT, "Draw's a while box around the NPC(s) nearest node\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at " ); +NPCDebugCommand( npc_route, Route, OVERLAY_NPC_ROUTE_BIT, "Displays the current route of the given NPC as a line on the screen. Waypoints along the route are drawn as small cyan rectangles. Line is color coded in the following manner:\n\tBlue - path to a node\n\tCyan - detour around an object (triangulation)\n\tRed - jump\n\tMaroon - path to final target position\n\tArguments: {npc_name} / {npc_class_name} / no argument picks what player is looking at " ); +NPCDebugCommand( npc_select, Select, OVERLAY_NPC_SELECTED_BIT, "Select or deselects the given NPC(s) for later manipulation. Selected NPC's are shown surrounded by a red translucent box\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at " ); +NPCDebugCommand( npc_combat, Combat, OVERLAY_NPC_SQUAD_BIT, "Displays text debugging information about the squad and enemy of the selected NPC (See Overlay Text)\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at" ); +NPCDebugCommand( npc_tasks, Tasks, OVERLAY_NPC_TASK_BIT, "Displays detailed text debugging information about the all the tasks of the selected NPC current schedule (See Overlay Text)\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at " ); +NPCDebugCommand( npc_task_text, TaskText, OVERLAY_TASK_TEXT_BIT, "Outputs text debugging information to the console about the all the tasks + break conditions of the selected NPC current schedule\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at " ); +NPCDebugCommand( npc_conditions, Conditions, OVERLAY_NPC_CONDITIONS_BIT, "Displays all the current AI conditions that an NPC has in the overlay text.\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at" ); +NPCDebugCommand( npc_viewcone, Viewcone, OVERLAY_NPC_VIEWCONE_BIT, "Displays the viewcone of the NPC (where they are currently looking and what the extents of there vision is)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at" ); +NPCDebugCommand( npc_relationships, Relationships, OVERLAY_NPC_RELATION_BIT, "Displays the relationships between this NPC and all others.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at" ); +NPCDebugCommand( npc_steering, Steering, OVERLAY_NPC_STEERING_REGULATIONS, "Displays the steering obstructions of the NPC( used to perform local avoidance )\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at" ); + +// For backwards compatibility +void CC_NPC_Squads( const CCommand &args ) +{ + SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_SQUAD_BIT); +} +static ConCommand npc_squads("npc_squads", CC_NPC_Squads, "Obsolete. Replaced by npc_combat", FCVAR_CHEAT); +#else //------------------------------------------------------------------------------ // Purpose: Show the selected NPC's nearest node //------------------------------------------------------------------------------ @@ -791,6 +1080,7 @@ void CC_NPC_ViewSteeringRegulations( const CCommand &args ) SetDebugBits( UTIL_GetCommandClient(), args[1], OVERLAY_NPC_STEERING_REGULATIONS); } static ConCommand npc_steering("npc_steering", CC_NPC_ViewSteeringRegulations, "Displays the steering obstructions of the NPC (used to perform local avoidance)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at", FCVAR_CHEAT); +#endif void CC_NPC_ViewSteeringRegulationsAll( void ) { diff --git a/mp/src/game/server/ai_default.cpp b/mp/src/game/server/ai_default.cpp index ffa85e08..8f660ec3 100644 --- a/mp/src/game/server/ai_default.cpp +++ b/mp/src/game/server/ai_default.cpp @@ -374,6 +374,10 @@ int CAI_BaseNPC::TranslateSchedule( int scheduleType ) return scheduleType; } +#ifdef MAPBASE +extern ScriptHook_t g_Hook_TranslateSchedule; +#endif + //========================================================= // GetScheduleOfType - returns a pointer to one of the // NPC's available schedules of the indicated type. @@ -386,6 +390,35 @@ CAI_Schedule *CAI_BaseNPC::GetScheduleOfType( int scheduleType ) scheduleType = TranslateSchedule( scheduleType ); AI_PROFILE_SCOPE_END(); +#ifdef MAPBASE_VSCRIPT + if ( m_ScriptScope.IsInitialized() && g_Hook_TranslateSchedule.CanRunInScope(m_ScriptScope) ) + { + int newSchedule = scheduleType; + if ( AI_IdIsLocal( newSchedule ) ) + { + newSchedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(newSchedule); + } + + // schedule, schedule_id (local ID) + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { GetSchedulingSymbols()->ScheduleIdToSymbol( newSchedule ), scheduleType }; + if (g_Hook_TranslateSchedule.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_INTEGER) + { + newSchedule = functionReturn.m_int; + } + else + { + newSchedule = GetScheduleID( functionReturn.m_pszString ); + } + + if (newSchedule != scheduleType && newSchedule > -1) + scheduleType = newSchedule; + } + } +#endif + // Get a pointer to that schedule CAI_Schedule *schedule = GetSchedule(scheduleType); @@ -1774,6 +1807,24 @@ AI_DEFINE_SCHEDULE // Run to cover, but don't turn to face enemy and upon // fail run around randomly //========================================================= +#ifdef MAPBASE +AI_DEFINE_SCHEDULE +( + SCHED_RUN_FROM_ENEMY, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RUN_FROM_ENEMY_FALLBACK" + " TASK_STOP_MOVING 0" + " TASK_FIND_COVER_FROM_ENEMY 0" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + " TASK_REMEMBER MEMORY:INCOVER" // Now that crouch nodes are fixed, this is necessary in case cover leads to a crouch node + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" +); +#else AI_DEFINE_SCHEDULE ( SCHED_RUN_FROM_ENEMY, @@ -1789,6 +1840,7 @@ AI_DEFINE_SCHEDULE " COND_NEW_ENEMY" " COND_ENEMY_DEAD" ); +#endif AI_DEFINE_SCHEDULE ( @@ -2351,6 +2403,19 @@ AI_DEFINE_SCHEDULE //========================================================= // > SCHED_INTERACTION_WAIT_FOR_PARTNER //========================================================= +#ifdef MAPBASE +AI_DEFINE_SCHEDULE +( + SCHED_INTERACTION_WAIT_FOR_PARTNER, + + " Tasks" + " TASK_FACE_INTERACTION_ANGLES 0" // New task to fix forced interaction anomalies + " TASK_WAIT 1" + "" + " Interrupts" + " COND_NO_CUSTOM_INTERRUPTS" +); +#else AI_DEFINE_SCHEDULE ( SCHED_INTERACTION_WAIT_FOR_PARTNER, @@ -2362,6 +2427,7 @@ AI_DEFINE_SCHEDULE " Interrupts" " COND_NO_CUSTOM_INTERRUPTS" ); +#endif //========================================================= // > SCHED_SLEEP diff --git a/mp/src/game/server/ai_dynamiclink.cpp b/mp/src/game/server/ai_dynamiclink.cpp index 5f5fba9c..a08402e8 100644 --- a/mp/src/game/server/ai_dynamiclink.cpp +++ b/mp/src/game/server/ai_dynamiclink.cpp @@ -15,6 +15,14 @@ #include "ai_link.h" #include "ai_network.h" #include "ai_networkmanager.h" +#ifdef MAPBASE +#include "ai_hint.h" +#include "ai_basenpc.h" +#include "filters.h" +#include "point_template.h" +#include "TemplateEntities.h" +#include "mapentities.h" +#endif #include "saverestore_utlvector.h" #include "editor_sendcommand.h" #include "bitstring.h" @@ -169,6 +177,156 @@ void CAI_DynamicLinkController::InputSetInvert( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//============================================================================= +// >> CAI_CustomLinkController +// Uses the specified link class +//============================================================================= +class CAI_CustomLinkController : public CAI_DynamicLinkController +{ + DECLARE_CLASS( CAI_CustomLinkController, CAI_DynamicLinkController ); +public: + CAI_CustomLinkController(); + + void GenerateLinksFromVolume(); + int GetReferenceLinkIndex(); + + string_t m_iszReferenceLinkTemplate; + int m_iReferenceLink; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(info_template_link_controller, CAI_CustomLinkController); + +BEGIN_DATADESC( CAI_CustomLinkController ) + + DEFINE_KEYFIELD( m_iszReferenceLinkTemplate, FIELD_STRING, "ReferenceTemplate" ), + //DEFINE_FIELD( m_iReferenceLink, FIELD_INTEGER ), // I don't know if this should be saved. It's only a cached variable, so not saving it shouldn't hurt anything. + +END_DATADESC() + +CAI_CustomLinkController::CAI_CustomLinkController() +{ + m_iReferenceLink = -1; +} + +int CAI_CustomLinkController::GetReferenceLinkIndex() +{ + if (m_iReferenceLink != -1) + return m_iReferenceLink; + + CBaseEntity *pEnt = gEntList.FindEntityByName(NULL, STRING(m_iszReferenceLinkTemplate), this); + if (CPointTemplate *pTemplate = dynamic_cast(pEnt)) + { + Assert(pTemplate->GetTemplateEntity(0)); + + m_iReferenceLink = pTemplate->GetTemplateIndexForTemplate(0); + return m_iReferenceLink; + } + + return -1; +} + +void CAI_CustomLinkController::GenerateLinksFromVolume() +{ + Assert( m_ControlledLinks.Count() == 0 ); + + int nNodes = g_pBigAINet->NumNodes(); + CAI_Node **ppNodes = g_pBigAINet->AccessNodes(); + + float MinDistCareSq = 0; + if (m_bUseAirLinkRadius) + { + MinDistCareSq = Square(MAX_AIR_NODE_LINK_DIST + 0.1); + } + else + { + MinDistCareSq = Square(MAX_NODE_LINK_DIST + 0.1); + } + + const Vector &origin = WorldSpaceCenter(); + Vector vAbsMins, vAbsMaxs; + CollisionProp()->WorldSpaceAABB( &vAbsMins, &vAbsMaxs ); + vAbsMins -= Vector( 1, 1, 1 ); + vAbsMaxs += Vector( 1, 1, 1 ); + + int iReference = GetReferenceLinkIndex(); + if (iReference == -1) + { + Warning("WARNING! %s reference link is invalid!\n", GetDebugName()); + return; + } + + // Get the map data before the loop + char *pMapData = (char*)STRING( Templates_FindByIndex( iReference ) ); + + // Make sure the entity is a dynamic link before doing anything + CBaseEntity *pEntity = NULL; + MapEntity_ParseEntity( pEntity, pMapData, NULL ); + if ( !dynamic_cast(pEntity) ) + { + Warning("WARNING! %s reference link is not a node link!\n", GetDebugName()); + UTIL_RemoveImmediate(pEntity); + return; + } + + UTIL_RemoveImmediate(pEntity); + + for ( int i = 0; i < nNodes; i++ ) + { + CAI_Node *pNode = ppNodes[i]; + const Vector &nodeOrigin = pNode->GetOrigin(); + if ( origin.DistToSqr(nodeOrigin) < MinDistCareSq ) + { + int nLinks = pNode->NumLinks(); + for ( int j = 0; j < nLinks; j++ ) + { + CAI_Link *pLink = pNode->GetLinkByIndex( j ); + int iLinkDest = pLink->DestNodeID( i ); + if ( iLinkDest > i ) + { + const Vector &originOther = ppNodes[iLinkDest]->GetOrigin(); + if ( origin.DistToSqr(originOther) < MinDistCareSq ) + { + if ( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, nodeOrigin, originOther - nodeOrigin ) ) + { + Assert( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, originOther, nodeOrigin - originOther ) ); + + CBaseEntity *pEntity = NULL; + + // Create the entity from the mapdata + MapEntity_ParseEntity( pEntity, pMapData, NULL ); + if ( pEntity == NULL ) + { + Msg("%s failed to initialize templated link with mapdata: %s\n", GetDebugName(), pMapData ); + return; + } + + // We already made sure it was an info_node_link template earlier. + CAI_DynamicLink *pLink = static_cast(pEntity); + + pLink->m_nSrcID = i; + pLink->m_nDestID = iLinkDest; + pLink->m_nSrcEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nSrcID ); + pLink->m_nDestEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nDestID ); + pLink->m_nLinkState = m_nLinkState; + pLink->m_strAllowUse = m_strAllowUse; + pLink->m_bInvertAllow = m_bInvertAllow; + pLink->m_bFixedUpIds = true; + pLink->m_bNotSaved = true; + + pLink->Spawn(); + m_ControlledLinks.AddToTail( pLink ); + } + } + } + } + } + } +} +#endif + //----------------------------------------------------------------------------- LINK_ENTITY_TO_CLASS(info_node_link, CAI_DynamicLink); @@ -579,6 +737,242 @@ CAI_DynamicLink::~CAI_DynamicLink(void) { } } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose : Determines if usage is allowed by a NPC, whether the link is disabled or not. +// This was created for info_node_link derivatives. +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CAI_DynamicLink::UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd) +{ + if (!(FindLink()->m_LinkInfo & bits_LINK_OFF)) + return true; + + if ( m_strAllowUse == NULL_STRING ) + return false; + + const char *pszAllowUse = STRING( m_strAllowUse ); + if ( m_bInvertAllow ) + { + // Exlude only the specified entity name or classname + if ( !pNPC->NameMatches(pszAllowUse) && !pNPC->ClassMatches( pszAllowUse ) ) + return true; + } + else + { + // Exclude everything but the allowed entity name or classname + if ( pNPC->NameMatches( pszAllowUse) || pNPC->ClassMatches( pszAllowUse ) ) + return true; + } + + return false; +} + +//============================================================================= +// >> CAI_DynanicLinkOneWay +//============================================================================= +class CAI_DynamicLinkOneWay : public CAI_DynamicLink +{ + DECLARE_CLASS( CAI_DynamicLinkOneWay, CAI_DynamicLink ); +public: + virtual bool UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd); + //virtual void SetLinkState( void ); + + bool m_bNormalWhenEnabled; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(info_node_link_oneway, CAI_DynamicLinkOneWay); + +BEGIN_DATADESC( CAI_DynamicLinkOneWay ) + + DEFINE_KEYFIELD( m_bNormalWhenEnabled, FIELD_BOOLEAN, "Usage" ), + +END_DATADESC() + +//------------------------------------------------------------------------------ +// Purpose : Determines if usage is allowed by a NPC. +// This was created for info_node_link derivatives. +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CAI_DynamicLinkOneWay::UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd) +{ + if (m_bNormalWhenEnabled) + return (m_nLinkState == LINK_OFF && bFromEnd) ? BaseClass::UseAllowed(pNPC, bFromEnd) : true; + + if (bFromEnd || m_nLinkState == LINK_OFF) + return BaseClass::UseAllowed(pNPC, bFromEnd); + + return true; +} + +#if 0 +//------------------------------------------------------------------------------ +// Purpose : Updates network link state if dynamic link state has changed +// Input : +// Output : +//------------------------------------------------------------------------------ +void CAI_DynamicLinkOneWay::SetLinkState(void) +{ + if (m_bNormalWhenEnabled) + return BaseClass::SetLinkState(); + + if ( !gm_bInitialized ) + { + // Safe to quietly return. Consistency will be enforced when InitDynamicLinks() is called + return; + } + + if (m_nSrcID == NO_NODE || m_nDestID == NO_NODE) + { + Vector pos = GetAbsOrigin(); + DevWarning("ERROR: Dynamic link at %f %f %f pointing to invalid node ID!!\n", pos.x, pos.y, pos.z); + return; + } + + CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false); + if ( pSrcNode ) + { + CAI_Link* pLink = FindLink(); + if ( pLink ) + { + // One-way always registers as off so it always calls UseAllowed() + pLink->m_pDynamicLink = this; + pLink->m_LinkInfo |= bits_LINK_OFF; + } + else + { + DevMsg("Dynamic Link Error: (%s) unable to form between nodes %d and %d\n", GetDebugName(), m_nSrcID, m_nDestID ); + } + } +} +#endif + +//============================================================================= +// >> CAI_DynamicLinkFilter +//============================================================================= +class CAI_DynamicLinkFilter : public CAI_DynamicLink +{ + DECLARE_CLASS( CAI_DynamicLinkFilter, CAI_DynamicLink ); +public: + virtual bool UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd); + //virtual void SetLinkState( void ); + + bool m_bNormalWhenEnabled; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(info_node_link_filtered, CAI_DynamicLinkFilter); + +BEGIN_DATADESC( CAI_DynamicLinkFilter ) + + DEFINE_KEYFIELD( m_bNormalWhenEnabled, FIELD_BOOLEAN, "Usage" ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetLinkFilter", InputSetDamageFilter ), + +END_DATADESC() + +//------------------------------------------------------------------------------ +// Purpose : Determines if usage is allowed by a NPC. +// This was created for info_node_link derivatives. +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CAI_DynamicLinkFilter::UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd) +{ + if ( !m_hDamageFilter ) + { + m_hDamageFilter = gEntList.FindEntityByName( NULL, m_iszDamageFilterName ); + if (!m_hDamageFilter) + { + Warning("%s (%s) couldn't find filter \"%s\"!\n", GetClassname(), GetDebugName(), STRING(m_iszDamageFilterName)); + return BaseClass::UseAllowed(pNPC, bFromEnd); + } + } + + CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get()); + + if (m_bNormalWhenEnabled) + return (m_nLinkState == LINK_OFF) ? (pFilter->PassesFilter(this, pNPC) || BaseClass::UseAllowed(pNPC, bFromEnd)) : true; + + if (m_nLinkState == LINK_OFF) + return BaseClass::UseAllowed(pNPC, bFromEnd); + + return pFilter->PassesFilter(this, pNPC); +} + +//============================================================================= +// >> CAI_DynamicLinkLogic +//============================================================================= +class CAI_DynamicLinkLogic : public CAI_DynamicLink +{ + DECLARE_CLASS( CAI_DynamicLinkLogic, CAI_DynamicLink ); +public: + virtual bool UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd); + virtual bool FinalUseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd); + + COutputEvent m_OnUsageAccepted; + COutputEvent m_OnUsageAcceptedWhileDisabled; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(info_node_link_logic, CAI_DynamicLinkLogic); + +BEGIN_DATADESC( CAI_DynamicLinkLogic ) + + DEFINE_OUTPUT( m_OnUsageAccepted, "OnUsageAccepted" ), + DEFINE_OUTPUT( m_OnUsageAcceptedWhileDisabled, "OnUsageAcceptedWhileDisabled" ), + +END_DATADESC() + +//------------------------------------------------------------------------------ +// Purpose : Determines if usage is allowed by a NPC. +// This was created for info_node_link derivatives. +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CAI_DynamicLinkLogic::UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd) +{ + // + // If the link is off, we want to fire "OnUsageAcceptedWhileDisabled", but we have to make sure + // the rest of the pathfinding calculations work. Yes, they might do all of this just to find a disabled link, + // but we have to fire the output somehow. + // + // Links already enabled go through regular usage rules. + // + if (m_nLinkState == LINK_OFF) + return true; + else + return BaseClass::UseAllowed( pNPC, bFromEnd ); +} + +//------------------------------------------------------------------------------ +// Purpose : After nothing else is left, finally determines if usage is allowed by a NPC. +// This was created for info_node_link derivatives. +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CAI_DynamicLinkLogic::FinalUseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd) +{ + if (m_nLinkState == LINK_ON) + { + m_OnUsageAccepted.FireOutput(pNPC, this); + return true; + } + else + { + m_OnUsageAcceptedWhileDisabled.FireOutput(pNPC, this); + + // We skipped the usage rules before. Do them now. + return BaseClass::UseAllowed(pNPC, bFromEnd); + } +} +#endif + LINK_ENTITY_TO_CLASS(info_radial_link_controller, CAI_RadialLinkController); BEGIN_DATADESC( CAI_RadialLinkController ) diff --git a/mp/src/game/server/ai_dynamiclink.h b/mp/src/game/server/ai_dynamiclink.h index cefec66c..971df96a 100644 --- a/mp/src/game/server/ai_dynamiclink.h +++ b/mp/src/game/server/ai_dynamiclink.h @@ -65,6 +65,13 @@ public: int ObjectCaps(); +#ifdef MAPBASE + virtual bool UseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd); + + // Called after we know the NPC meets all of the node's criteria + virtual bool FinalUseAllowed(CAI_BaseNPC *pNPC, bool bFromEnd) { return true; } +#endif + // ---------------- // Inputs // ---------------- @@ -83,6 +90,9 @@ class CAI_DynamicLinkController : public CServerOnlyEntity { DECLARE_CLASS( CAI_DynamicLinkController, CServerOnlyEntity ); public: +#ifdef MAPBASE + virtual +#endif void GenerateLinksFromVolume(); // ---------------- diff --git a/mp/src/game/server/ai_goalentity.cpp b/mp/src/game/server/ai_goalentity.cpp index 31981097..916d9210 100644 --- a/mp/src/game/server/ai_goalentity.cpp +++ b/mp/src/game/server/ai_goalentity.cpp @@ -38,6 +38,15 @@ BEGIN_DATADESC( CAI_GoalEntity ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_GoalEntity, CBaseEntity, "The base class for goal entities used to control NPC behavior." ) + + DEFINE_SCRIPTFUNC( IsActive, "Check if the goal entity is active." ) + DEFINE_SCRIPTFUNC( NumActors, "Get the number of actors using this goal entity." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------- diff --git a/mp/src/game/server/ai_goalentity.h b/mp/src/game/server/ai_goalentity.h index 2a6805f4..56252d25 100644 --- a/mp/src/game/server/ai_goalentity.h +++ b/mp/src/game/server/ai_goalentity.h @@ -27,6 +27,9 @@ class CAI_GoalEntity : public CBaseEntity, public IEntityListener { DECLARE_CLASS( CAI_GoalEntity, CBaseEntity ); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: CAI_GoalEntity() : m_iszActor(NULL_STRING), diff --git a/mp/src/game/server/ai_hint.cpp b/mp/src/game/server/ai_hint.cpp index c0014220..1f8ec4d1 100644 --- a/mp/src/game/server/ai_hint.cpp +++ b/mp/src/game/server/ai_hint.cpp @@ -890,6 +890,9 @@ BEGIN_DATADESC( CAI_Hint ) // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "EnableHint", InputEnableHint ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableHint", InputDisableHint ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetHintGroup", InputSetHintGroup ), +#endif // Outputs DEFINE_OUTPUT( m_OnNPCStartedUsing, "OnNPCStartedUsing" ), @@ -897,6 +900,23 @@ BEGIN_DATADESC( CAI_Hint ) END_DATADESC( ); +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_Hint, CBaseEntity, "An entity which gives contextual pointers for NPCs." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHintType, "GetHintType", "Get the hint's type ID." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUser, "GetUser", "Get the hint's current user." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHintGroup, "GetHintGroup", "Get the name of the hint's group." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHintActivity, "GetHintActivity", "Get the name of the hint activity." ) + + DEFINE_SCRIPTFUNC( IsDisabled, "Check if the hint is disabled." ) + DEFINE_SCRIPTFUNC( IsLocked, "Check if the hint is locked." ) + DEFINE_SCRIPTFUNC( GetNodeId, "Get the hint's node ID." ) + DEFINE_SCRIPTFUNC( Yaw, "Get the hint's yaw." ) + DEFINE_SCRIPTFUNC( GetDirection, "Get the hint's direction." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------------------------------------------------ // Purpose : //------------------------------------------------------------------------------ @@ -913,6 +933,18 @@ void CAI_Hint::InputDisableHint( inputdata_t &inputdata ) m_NodeData.iDisabled = true; } +#ifdef MAPBASE +void CAI_Hint::SetGroup( string_t iszNewGroup ) +{ + m_NodeData.strGroup = iszNewGroup; +} + +void CAI_Hint::InputSetHintGroup( inputdata_t &inputdata ) +{ + SetGroup(inputdata.value.StringID()); +} +#endif + //------------------------------------------------------------------------------ // Purpose : @@ -1075,6 +1107,40 @@ bool CAI_Hint::IsInNodeFOV( CBaseEntity *pOther ) return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// An easy way of engaging certain hint parameters on certain hint types that didn't use it before. +//----------------------------------------------------------------------------- +void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ) +{ + Assert( pNPC != NULL ); + + HintIgnoreFacing_t facing = GetIgnoreFacing(); + if (facing == HIF_DEFAULT) + facing = bDefaultFacing ? HIF_YES : HIF_NO; + + if (facing == HIF_YES) + pNPC->GetNavigator()->SetArrivalDirection(GetDirection()); + + if (HintActivityName() != NULL_STRING) + { + Activity hintActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(HintActivityName()) ); + if ( hintActivity != ACT_INVALID ) + { + pNPC->GetNavigator()->SetArrivalActivity( pNPC->GetHintActivity(HintType(), hintActivity) ); + } + else + { + int iSequence = pNPC->LookupSequence(STRING(HintActivityName())); + if ( iSequence != ACT_INVALID ) + { + pNPC->GetNavigator()->SetArrivalSequence( iSequence ); + } + } + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Locks the node for use by an AI for hints // Output : Returns true if the node was available for locking, false on failure. @@ -1426,6 +1492,15 @@ int CAI_Hint::DrawDebugTextOverlays(void) EntityText(text_offset,tempstr,0); text_offset++; +#ifdef MAPBASE + if (m_NodeData.strGroup != NULL_STRING) + { + Q_snprintf(tempstr,sizeof(tempstr),"hintgroup %s", STRING(m_NodeData.strGroup) ) ; + EntityText(text_offset,tempstr,0); + text_offset++; + } +#endif + if ( m_NodeData.iDisabled ) { Q_snprintf(tempstr,sizeof(tempstr),"DISABLED" ); diff --git a/mp/src/game/server/ai_hint.h b/mp/src/game/server/ai_hint.h index 89daef3d..25fd4fdb 100644 --- a/mp/src/game/server/ai_hint.h +++ b/mp/src/game/server/ai_hint.h @@ -281,6 +281,9 @@ public: float Yaw( void ); CAI_Node *GetNode( void ); string_t GetGroup( void ) const { return m_NodeData.strGroup; } +#ifdef MAPBASE + void SetGroup( string_t iszNewGroup ); +#endif CBaseEntity *User( void ) const { return m_hHintOwner; }; Hint_e HintType( void ) const { return (Hint_e)m_NodeData.nHintType; }; void SetHintType( int hintType, bool force = false ); @@ -305,6 +308,17 @@ public: bool HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock = false, bool bIgnoreHintType = false ); bool IsInNodeFOV( CBaseEntity *pOther ); +#ifdef MAPBASE + void NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ); +#endif + +#ifdef MAPBASE_VSCRIPT + int ScriptGetHintType() { return (int)HintType(); } + HSCRIPT ScriptGetUser() { return ToHScript( User() ); } + const char* ScriptGetHintGroup() { return STRING( GetGroup() ); } + const char* ScriptGetHintActivity() { return STRING( HintActivityName() ); } +#endif + private: void Spawn( void ); virtual void Activate(); @@ -317,6 +331,9 @@ private: // Input handlers void InputEnableHint( inputdata_t &inputdata ); void InputDisableHint( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetHintGroup( inputdata_t &inputdata ); +#endif private: @@ -333,6 +350,9 @@ private: friend class CAI_HintManager; DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; #define SF_ALLOW_JUMP_UP 65536 diff --git a/mp/src/game/server/ai_memory.cpp b/mp/src/game/server/ai_memory.cpp index 7ac69311..0af73a89 100644 --- a/mp/src/game/server/ai_memory.cpp +++ b/mp/src/game/server/ai_memory.cpp @@ -146,6 +146,29 @@ BEGIN_SIMPLE_DATADESC( AI_EnemyInfo_t ) // NOT SAVED nextEMemory END_DATADESC() +#ifdef MAPBASE_VSCRIPT +#define DEFINE_ENEMY_INFO_SCRIPTFUNCS(name, desc) \ + DEFINE_SCRIPTFUNC_NAMED( Get##name, #name, "Get " desc ) \ + DEFINE_SCRIPTFUNC( Set##name, "Set " desc ) + +BEGIN_SCRIPTDESC_ROOT( AI_EnemyInfo_t, "Accessor for information about an enemy." ) + DEFINE_SCRIPTFUNC( Enemy, "Get the enemy." ) + DEFINE_SCRIPTFUNC( SetEnemy, "Set the enemy." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastKnownLocation, "the enemy's last known location." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastSeenLocation, "the enemy's last seen location." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastSeen, "the last time the enemy was seen." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeFirstSeen, "the first time the enemy was seen." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReacquired, "the last time the enemy was reaquired." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeValidEnemy, "the time at which the enemy can be selected (reaction delay)." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReceivedDamageFrom, "the last time damage was received from this enemy." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeAtFirstHand, "the time at which the enemy was seen firsthand." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( DangerMemory, "the memory of danger position w/o enemy pointer." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( EludedMe, "whether the enemy is not at the last known location." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( Unforgettable, "whether the enemy is unforgettable." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( MobbedMe, "whether the enemy was part of a mob at some point." ) +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- CAI_Enemies::CAI_Enemies(void) diff --git a/mp/src/game/server/ai_memory.h b/mp/src/game/server/ai_memory.h index d348c53e..faa482a2 100644 --- a/mp/src/game/server/ai_memory.h +++ b/mp/src/game/server/ai_memory.h @@ -45,6 +45,29 @@ struct AI_EnemyInfo_t bool bUnforgettable; bool bMobbedMe; // True if enemy was part of a mob at some point +#ifdef MAPBASE_VSCRIPT + // Script functions. + #define ENEMY_INFO_SCRIPT_FUNCS(type, name, var) \ + type Get##name() { return var; } \ + void Set##name( type v ) { var = v; } + + HSCRIPT Enemy() { return ToHScript(hEnemy); } + void SetEnemy( HSCRIPT ent ) { hEnemy = ToEnt(ent); } + + ENEMY_INFO_SCRIPT_FUNCS( Vector, LastKnownLocation, vLastKnownLocation ); + ENEMY_INFO_SCRIPT_FUNCS( Vector, LastSeenLocation, vLastSeenLocation ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastSeen, timeLastSeen ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeFirstSeen, timeFirstSeen ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastReacquired, timeLastReacquired ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeValidEnemy, timeValidEnemy ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastReceivedDamageFrom, timeLastReceivedDamageFrom ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeAtFirstHand, timeAtFirstHand ); + ENEMY_INFO_SCRIPT_FUNCS( bool, DangerMemory, bDangerMemory ); + ENEMY_INFO_SCRIPT_FUNCS( bool, EludedMe, bEludedMe ); + ENEMY_INFO_SCRIPT_FUNCS( bool, Unforgettable, bUnforgettable ); + ENEMY_INFO_SCRIPT_FUNCS( bool, MobbedMe, bMobbedMe ); +#endif + DECLARE_SIMPLE_DATADESC(); }; diff --git a/mp/src/game/server/ai_moveprobe.cpp b/mp/src/game/server/ai_moveprobe.cpp index b2a86d83..bdf8796b 100644 --- a/mp/src/game/server/ai_moveprobe.cpp +++ b/mp/src/game/server/ai_moveprobe.cpp @@ -94,10 +94,16 @@ bool CAI_MoveProbe::ShouldBrushBeIgnored( CBaseEntity *pEntity ) CFuncBrush *pFuncBrush = assert_cast(pEntity); // this is true if my class or entity name matches the exclusion name on the func brush +#ifdef MAPBASE + // The only way it could conflict is if a map has a NPC using a classname as its targetname, like a single npc_fastzombie entity referred to as "npc_zombie". + // I doubt anyone's doing that unless they don't know what they're doing, and even then this still shouldn't be barred from plain-HL2 mappers. + bool nameMatches = GetOuter()->ClassMatches(pFuncBrush->m_iszExcludedClass) || GetOuter()->NameMatches(pFuncBrush->m_iszExcludedClass); +#else #if HL2_EPISODIC bool nameMatches = ( pFuncBrush->m_iszExcludedClass == GetOuter()->m_iClassname ) || GetOuter()->NameMatches(pFuncBrush->m_iszExcludedClass); #else // do not match against entity name in base HL2 (just in case there is some case somewhere that might be broken by this) bool nameMatches = ( pFuncBrush->m_iszExcludedClass == GetOuter()->m_iClassname ); +#endif #endif // return true (ignore brush) if the name matches, or, if exclusion is inverted, if the name does not match diff --git a/mp/src/game/server/ai_network.cpp b/mp/src/game/server/ai_network.cpp index 78b7a53f..16d8a7fa 100644 --- a/mp/src/game/server/ai_network.cpp +++ b/mp/src/game/server/ai_network.cpp @@ -16,6 +16,9 @@ #include "ai_navigator.h" #include "world.h" #include "ai_moveprobe.h" +#ifdef MAPBASE_VSCRIPT +#include "ai_hint.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -31,6 +34,53 @@ extern float MOVE_HEIGHT_EPSILON; // later point we will probabaly have multiple AINetworkds per level CAI_Network* g_pBigAINet; +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAI_Network, SCRIPT_SINGLETON "The global list of AI nodes." ) + DEFINE_SCRIPTFUNC( NumNodes, "Number of nodes in the level" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodePosition, "GetNodePosition", "Get position of node using a generic human hull" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodePositionWithHull, "GetNodePositionWithHull", "Get position of node using the specified hull" ) + DEFINE_SCRIPTFUNC( GetNodeYaw, "Get yaw of node" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptNearestNodeToPoint, "NearestNodeToPoint", "Get ID of nearest node" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNearestNodeToPointWithNPC, "NearestNodeToPointForNPC", "Get ID of nearest node using the specified NPC's properties" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodeType, "GetNodeType", "Get a node's type" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodeHint, "GetNodeHint", "Get a node's hint" ) +END_SCRIPTDESC(); + +HSCRIPT CAI_Network::ScriptGetNodeHint( int nodeID ) +{ + CAI_Node *pNode = GetNode( nodeID ); + if (!pNode) + return NULL; + + return ToHScript( pNode->GetHint() ); +} + +int CAI_Network::ScriptGetNodeType( int nodeID ) +{ + CAI_Node *pNode = GetNode( nodeID ); + if (!pNode) + return NULL; + + return (int)pNode->GetType(); +} + +int CAI_Network::ScriptNearestNodeToPointWithNPC( HSCRIPT hNPC, const Vector &vecPosition, bool bCheckVisibility ) +{ + CBaseEntity *pEnt = ToEnt( hNPC ); + if (!pEnt || !pEnt->MyNPCPointer()) + { + Warning("vscript: NearestNodeToPointWithNPC - Invalid NPC\n"); + return NO_NODE; + } + + return NearestNodeToPoint( pEnt->MyNPCPointer(), vecPosition, bCheckVisibility ); +} +#endif + //----------------------------------------------------------------------------- abstract_class INodeListFilter diff --git a/mp/src/game/server/ai_network.h b/mp/src/game/server/ai_network.h index 8cebd16a..a86d89b8 100644 --- a/mp/src/game/server/ai_network.h +++ b/mp/src/game/server/ai_network.h @@ -127,6 +127,17 @@ public: } CAI_Node** AccessNodes() const { return m_pAInode; } + +#ifdef MAPBASE_VSCRIPT + Vector ScriptGetNodePosition( int nodeID ) { return GetNodePosition( HULL_HUMAN, nodeID ); } + Vector ScriptGetNodePositionWithHull( int nodeID, int hull ) { return GetNodePosition( (Hull_t)hull, nodeID ); } + + int ScriptNearestNodeToPoint( const Vector &vecPosition, bool bCheckVisibility = true ) { return NearestNodeToPoint( NULL, vecPosition, bCheckVisibility ); } + int ScriptNearestNodeToPointWithNPC( HSCRIPT hNPC, const Vector &vecPosition, bool bCheckVisibility = true ); + + HSCRIPT ScriptGetNodeHint( int nodeID ); + int ScriptGetNodeType( int nodeID ); +#endif private: friend class CAI_NetworkManager; diff --git a/mp/src/game/server/ai_networkmanager.cpp b/mp/src/game/server/ai_networkmanager.cpp index 21a6f9de..8f470a47 100644 --- a/mp/src/game/server/ai_networkmanager.cpp +++ b/mp/src/game/server/ai_networkmanager.cpp @@ -69,6 +69,9 @@ CON_COMMAND( ai_debug_node_connect, "Debug the attempted connection between two // line to properly override the node graph building. ConVar g_ai_norebuildgraph( "ai_norebuildgraph", "0" ); +#ifdef MAPBASE +ConVar g_ai_norebuildgraphmessage( "ai_norebuildgraphmessage", "0", FCVAR_ARCHIVE, "Stops the \"Node graph out of date\" message from appearing when rebuilding node graph" ); +#endif //----------------------------------------------------------------------------- @@ -945,6 +948,13 @@ void CAI_NetworkManager::InitializeAINetworks() } } +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM) + { + g_pScriptVM->RegisterInstance( g_pBigAINet, "AINetwork" ); + } +#endif + // Reset node counter used during load CNodeEnt::m_nNodeCount = 0; @@ -1110,9 +1120,18 @@ void CAI_NetworkManager::DelayedInit( void ) #endif DevMsg( "Node Graph out of Date. Rebuilding... (%d, %d, %d)\n", (int)m_bDontSaveGraph, (int)!CAI_NetworkManager::NetworksLoaded(), (int) engine->IsInEditMode() ); +#ifdef MAPBASE + if (!g_ai_norebuildgraphmessage.GetBool()) + UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding...\n" ); + + // Do it much sooner after map load + g_pAINetworkManager->SetNextThink( gpGlobals->curtime + 0.5 ); + m_bNeedGraphRebuild = true; +#else UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding...\n" ); m_bNeedGraphRebuild = true; g_pAINetworkManager->SetNextThink( gpGlobals->curtime + 1 ); +#endif return; } diff --git a/mp/src/game/server/ai_pathfinder.cpp b/mp/src/game/server/ai_pathfinder.cpp index 5099925e..61c641a5 100644 --- a/mp/src/game/server/ai_pathfinder.cpp +++ b/mp/src/game/server/ai_pathfinder.cpp @@ -597,6 +597,17 @@ bool CAI_Pathfinder::IsLinkUsable(CAI_Link *pLink, int startID) // -------------------------------------------------------------------------- // Skip if link turned off // -------------------------------------------------------------------------- +#ifdef MAPBASE + if (pLink->m_pDynamicLink) + { + if (!pLink->m_pDynamicLink->UseAllowed(GetOuter(), startID == pLink->m_pDynamicLink->m_nDestID)) + return false; + } + else if (pLink->m_LinkInfo & bits_LINK_OFF) + { + return false; + } +#else if (pLink->m_LinkInfo & bits_LINK_OFF) { CAI_DynamicLink *pDynamicLink = pLink->m_pDynamicLink; @@ -618,6 +629,7 @@ bool CAI_Pathfinder::IsLinkUsable(CAI_Link *pLink, int startID) return false; } } +#endif // -------------------------------------------------------------------------- // Get the destination nodeID @@ -691,6 +703,12 @@ bool CAI_Pathfinder::IsLinkUsable(CAI_Link *pLink, int startID) return false; } } +#ifdef MAPBASE + if (pLink->m_pDynamicLink) + { + return pLink->m_pDynamicLink->FinalUseAllowed(GetOuter(), startID == pLink->m_pDynamicLink->m_nDestID); + } +#endif return true; } diff --git a/mp/src/game/server/ai_playerally.cpp b/mp/src/game/server/ai_playerally.cpp index 1ce4da31..fabbbee1 100644 --- a/mp/src/game/server/ai_playerally.cpp +++ b/mp/src/game/server/ai_playerally.cpp @@ -12,6 +12,9 @@ #include "eventqueue.h" #include "ai_behavior_lead.h" #include "gameinterface.h" +#ifdef MAPBASE +#include "mapbase/matchers.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -338,6 +341,9 @@ BEGIN_DATADESC( CAI_PlayerAlly ) DEFINE_INPUTFUNC( FIELD_STRING, "SpeakResponseConcept", InputSpeakResponseConcept ), DEFINE_INPUTFUNC( FIELD_VOID, "MakeGameEndAlly", InputMakeGameEndAlly ), DEFINE_INPUTFUNC( FIELD_VOID, "MakeRegularAlly", InputMakeRegularAlly ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "AskQuestion", InputAskQuestion ), +#endif DEFINE_INPUTFUNC( FIELD_INTEGER, "AnswerQuestion", InputAnswerQuestion ), DEFINE_INPUTFUNC( FIELD_INTEGER, "AnswerQuestionHello", InputAnswerQuestionHello ), DEFINE_INPUTFUNC( FIELD_VOID, "EnableSpeakWhileScripting", InputEnableSpeakWhileScripting ), @@ -727,8 +733,13 @@ bool CAI_PlayerAlly::SelectQuestionAndAnswerSpeech( AISpeechSelection_t *pSelect return false; // if there is a friend nearby to speak to, play sentence, set friend's response time, return +#ifdef MAPBASE + CAI_PlayerAlly *pFriend = dynamic_cast(FindSpeechTarget( AIST_NPCS | AIST_NOT_GAGGED )); + if ( pFriend && !pFriend->IsMoving() ) +#else CAI_PlayerAlly *pFriend = dynamic_cast(FindSpeechTarget( AIST_NPCS )); if ( pFriend && !pFriend->IsMoving() && !pFriend->HasSpawnFlags(SF_NPC_GAG) ) +#endif return SelectQuestionFriend( pFriend, pSelection ); return false; @@ -818,7 +829,17 @@ bool CAI_PlayerAlly::SelectQuestionFriend( CBaseEntity *pFriend, AISpeechSelecti // If we haven't said hello, say hello first. // Only ever say hello to NPCs other than my type. +#ifdef MAPBASE + // Why only say hello to NPCs other than my type? + // Are citizens a hivemind? Do they not greet each other? + // They don't have any responses for it anyway, so SelectSpeechResponse() will fail + // and TLK_HELLO_NPC will be marked as spoken. + // + // Responses could be added so modders/mappers can take advantage of this. + if ( !GetExpresser()->SpokeConcept( TLK_HELLO_NPC ) ) +#else if ( !GetExpresser()->SpokeConcept( TLK_HELLO_NPC ) && !FClassnameIs( this, pFriend->GetClassname()) ) +#endif { if ( SelectSpeechResponse( TLK_HELLO_NPC, NULL, pFriend, pSelection ) ) return true; @@ -846,6 +867,71 @@ bool CAI_PlayerAlly::SelectAnswerFriend( CBaseEntity *pFriend, AISpeechSelection return SelectSpeechResponse( TLK_ANSWER, NULL, pFriend, pSelection ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Asks a question now. +//----------------------------------------------------------------------------- +bool CAI_PlayerAlly::AskQuestionNow( CBaseEntity *pSpeechTarget, int iQARandomNumber, const char *concept ) +{ + m_hPotentialSpeechTarget = pSpeechTarget; + m_iQARandomNumber = iQARandomNumber; + + if (!m_hPotentialSpeechTarget) + m_hPotentialSpeechTarget = /*dynamic_cast*/(FindSpeechTarget( AIST_NPCS | AIST_NOT_GAGGED )); + + if (m_iQARandomNumber == -1) + m_iQARandomNumber = RandomInt(0, 100); + + AISpeechSelection_t selection; + SelectSpeechResponse( concept, NULL, m_hPotentialSpeechTarget.Get(), &selection ); + + SetSpeechTarget( selection.hSpeechTarget ); + ClearPendingSpeech(); + + // Speak immediately + return SpeakDispatchResponse( selection.concept.c_str(), selection.Response ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PlayerAlly::InputAskQuestion( inputdata_t &inputdata ) +{ + CBaseEntity *pSpeechTarget = NULL; + int iQARandomNumber = 0; + const char *concept = TLK_QUESTION; + + // I didn't feel like using strtok today. + CUtlStringList vecStrings; + V_SplitString(inputdata.value.String(), " ", vecStrings); + FOR_EACH_VEC( vecStrings, i ) + { + // 0 : QA Number (-1 for N/A) + // 1 : Speech Target + // 2 : Concept + switch (i) + { + case 0: iQARandomNumber = atoi(vecStrings[i]); break; + case 1: pSpeechTarget = gEntList.FindEntityByName(NULL, vecStrings[i], this, inputdata.pActivator, inputdata.pCaller); break; + case 2: concept = vecStrings[i]; break; + } + } + + if (pSpeechTarget == NULL) + { + CAI_PlayerAlly *pFriend = dynamic_cast(FindSpeechTarget( AIST_NPCS | AIST_NOT_GAGGED )); + if ( pFriend ) + pSpeechTarget = pFriend; + } + else if (pSpeechTarget == this) + { + pSpeechTarget = NULL; + } + + AskQuestionNow(pSpeechTarget, iQARandomNumber, concept); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1078,6 +1164,24 @@ void CAI_PlayerAlly::Touch( CBaseEntity *pOther ) } } +#ifdef MAPBASE +ConVar mapbase_ally_flinching("mapbase_ally_flinching", "1", FCVAR_ARCHIVE, "Enables/disables the new flinching animations."); +//----------------------------------------------------------------------------- +// Purpose: This is to adjust for the new citizen flinching animations, +// as they would exist on all NPCs that use citizen animations. +// +// Vortigaunts and Alyx in the Episodes are the only ones who can flinch normally, +// and that's been rectified with their own functions. (they currently skip CAI_PlayerAlly's implementation) +//----------------------------------------------------------------------------- +bool CAI_PlayerAlly::CanFlinch( void ) +{ + if (mapbase_ally_flinching.GetBool() != true) + return false; + + return BaseClass::CanFlinch(); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled ) @@ -1088,6 +1192,10 @@ void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled ) ( pKilled->MyNPCPointer()->GetLastPlayerDamageTime() == 0 || gpGlobals->curtime - pKilled->MyNPCPointer()->GetLastPlayerDamageTime() > 5 ) ) { +#ifdef MAPBASE + m_hPotentialSpeechTarget = pKilled; + SetSpeechTarget(pKilled); +#endif SpeakIfAllowed( TLK_ENEMY_DEAD ); } } @@ -1175,8 +1283,14 @@ void CAI_PlayerAlly::Event_Killed( const CTakeDamageInfo &info ) CBasePlayer *player = AI_GetSinglePlayer(); if ( player ) { +#ifdef MAPBASE + variant_t variant; + variant.SetEntity(this); + player->AcceptInput( "OnSquadMemberKilled", info.GetAttacker(), this, variant, 0 ); +#else variant_t emptyVariant; player->AcceptInput( "OnSquadMemberKilled", this, this, emptyVariant, 0 ); +#endif } } @@ -1186,6 +1300,10 @@ void CAI_PlayerAlly::Event_Killed( const CTakeDamageInfo &info ) CAI_PlayerAlly *pMourner = dynamic_cast(FindSpeechTarget( AIST_NPCS )); if ( pMourner ) { +#ifdef MAPBASE + pMourner->m_hPotentialSpeechTarget = this; + pMourner->SetSpeechTarget(this); +#endif pMourner->SpeakIfAllowed( TLK_ALLY_KILLED ); } @@ -1287,6 +1405,11 @@ bool CAI_PlayerAlly::IsValidSpeechTarget( int flags, CBaseEntity *pEntity ) // Don't bother people who don't want to be bothered if ( !pNPC->CanBeUsedAsAFriend() ) return false; + +#ifdef MAPBASE + if (flags & AIST_NOT_GAGGED && pNPC->HasSpawnFlags(SF_NPC_GAG)) + return false; +#endif } if ( flags & AIST_FACING_TARGET ) @@ -1592,17 +1715,41 @@ bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize ) +{ + if ( IsAllowedToSpeak( concept, bRespondingToPlayer ) ) + { + return Speak( concept, modifiers, pszOutResponseChosen, bufsize ); + } + return false; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_PlayerAlly::ModifyOrAppendCriteria( AI_CriteriaSet& set ) { BaseClass::ModifyOrAppendCriteria( set ); +#ifdef MAPBASE + // For the below speechtarget criteria + if (GetSpeechTarget() && !m_hPotentialSpeechTarget) + m_hPotentialSpeechTarget = GetSpeechTarget(); +#endif + if ( m_hPotentialSpeechTarget ) { set.AppendCriteria( "speechtarget", m_hPotentialSpeechTarget->GetClassname() ); set.AppendCriteria( "speechtargetname", STRING(m_hPotentialSpeechTarget->GetEntityName()) ); set.AppendCriteria( "randomnum", UTIL_VarArgs("%d", m_iQARandomNumber) ); + +#ifdef MAPBASE + // Speech target contexts. + m_hPotentialSpeechTarget->AppendContextToCriteria(set, "speechtarget_"); +#endif } // Do we have a speech filter? If so, append it's criteria too @@ -1619,11 +1766,13 @@ void CAI_PlayerAlly::OnSpokeConcept( AIConcept_t concept, AI_Response *response CAI_AllySpeechManager *pSpeechManager = GetAllySpeechManager(); pSpeechManager->OnSpokeConcept( this, concept, response ); +#ifndef MAPBASE // This has been moved directly to CAI_Expresser if( response != NULL && (response->GetParams()->flags & AI_ResponseParams::RG_WEAPONDELAY) ) { // Stop shooting, as instructed, so that my speech can be heard. GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + response->GetWeaponDelay() ); } +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/ai_playerally.h b/mp/src/game/server/ai_playerally.h index bd1e5ab4..e78135e6 100644 --- a/mp/src/game/server/ai_playerally.h +++ b/mp/src/game/server/ai_playerally.h @@ -244,6 +244,10 @@ enum AISpeechTargetSearchFlags_t AIST_IGNORE_RELATIONSHIP = (1<<2), AIST_ANY_QUALIFIED = (1<<3), AIST_FACING_TARGET = (1<<4), +#ifdef MAPBASE + // I needed this for something + AIST_NOT_GAGGED = (1<<5), +#endif }; struct AISpeechSelection_t @@ -284,6 +288,10 @@ public: void ClearTransientConditions(); void Touch( CBaseEntity *pOther ); +#ifdef MAPBASE + virtual bool CanFlinch( void ); +#endif + //--------------------------------- // Combat //--------------------------------- @@ -313,6 +321,12 @@ public: CBaseEntity *GetSpeechTarget() { return m_hTalkTarget.Get(); } void SetSpeechTarget( CBaseEntity *pSpeechTarget ) { m_hTalkTarget = pSpeechTarget; } + +#ifdef MAPBASE + // Needed for additional speech target responses + CBaseEntity *GetPotentialSpeechTarget() { return m_hPotentialSpeechTarget.Get(); } + void SetPotentialSpeechTarget( CBaseEntity *pSpeechTarget ) { m_hPotentialSpeechTarget = pSpeechTarget; } +#endif void SetSpeechFilter( CAI_SpeechFilter *pFilter ) { m_hSpeechFilter = pFilter; } CAI_SpeechFilter *GetSpeechFilter( void ) { return m_hSpeechFilter; } @@ -361,6 +375,9 @@ public: bool ShouldSpeakRandom( AIConcept_t concept, int iChance ); bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false ); virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 ); +#ifdef MAPBASE + virtual bool SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 ); +#endif void ModifyOrAppendCriteria( AI_CriteriaSet& set ); //--------------------------------- @@ -386,6 +403,10 @@ public: virtual const char *GetDeathMessageText( void ) { return "GAMEOVER_ALLY"; } void InputMakeGameEndAlly( inputdata_t &inputdata ); void InputMakeRegularAlly( inputdata_t &inputdata ); +#ifdef MAPBASE + bool AskQuestionNow( CBaseEntity *pSpeechTarget = NULL, int iQARandomNumber = -1, const char *concept = TLK_QUESTION ); + void InputAskQuestion( inputdata_t &inputdata ); +#endif void InputAnswerQuestion( inputdata_t &inputdata ); void InputAnswerQuestionHello( inputdata_t &inputdata ); void InputEnableSpeakWhileScripting( inputdata_t &inputdata ); diff --git a/mp/src/game/server/ai_relationship.cpp b/mp/src/game/server/ai_relationship.cpp index eec28d99..a700552a 100644 --- a/mp/src/game/server/ai_relationship.cpp +++ b/mp/src/game/server/ai_relationship.cpp @@ -7,6 +7,9 @@ #include "cbase.h" #include "ndebugoverlay.h" #include "ai_basenpc.h" +#ifdef MAPBASE +#include "mapbase/matchers.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -34,6 +37,9 @@ public: void Activate(); void SetActive( bool bActive ); +#ifdef MAPBASE + virtual +#endif void ChangeRelationships( int disposition, int iReverting, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); void ApplyRelationship( CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); @@ -45,11 +51,20 @@ public: bool IsASubject( CBaseEntity *pEntity ); bool IsATarget( CBaseEntity *pEntity ); +#ifdef MAPBASE + // Assume no insane person already has "CLASS_" at the beginning of an entity's (class)name. + inline bool IsSubjectClassify() { return Q_strncmp(STRING(m_target), "CLASS_", 6) == 0; } + inline bool IsTargetClassify() { return Q_strncmp(STRING(m_target), "CLASS_", 6) == 0; } +#endif void OnEntitySpawned( CBaseEntity *pEntity ); void OnEntityDeleted( CBaseEntity *pEntity ); +#ifdef MAPBASE +protected: +#else private: +#endif void ApplyRelationshipThink( void ); CBaseEntity *FindEntityForProceduralName( string_t iszName, CBaseEntity *pActivator, CBaseEntity *pCaller ); @@ -259,6 +274,12 @@ bool CAI_Relationship::IsASubject( CBaseEntity *pEntity ) if( pEntity->ClassMatches( m_iszSubject ) ) return true; +#ifdef MAPBASE + // Hopefully doesn't impact performance. + if (Matcher_NamesMatch(STRING(m_iszSubject), g_pGameRules->AIClassText((pEntity->Classify())))) + return true; +#endif + return false; } @@ -272,6 +293,12 @@ bool CAI_Relationship::IsATarget( CBaseEntity *pEntity ) if( pEntity->ClassMatches( m_target ) ) return true; +#ifdef MAPBASE + // Hopefully doesn't impact performance. + if (Matcher_NamesMatch(STRING(m_target), g_pGameRules->AIClassText((pEntity->Classify())))) + return true; +#endif + return false; } @@ -494,3 +521,133 @@ void CAI_Relationship::ChangeRelationships( int disposition, int iReverting, CBa } } +#ifdef MAPBASE +//========================================================= +//========================================================= +class CAI_ClassRelationship : public CAI_Relationship +{ + DECLARE_CLASS( CAI_ClassRelationship, CAI_Relationship ); + +public: + + // Must override CAI_Relationship + void Spawn() { m_bIsActive = false; } + + bool KeyValue( const char *szKeyName, const char *szValue ); + + void ChangeRelationships( int disposition, int iReverting, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); + + inline bool SubjectUsingClassify() { return m_iSubjectClass != NUM_AI_CLASSES; } + inline bool TargetUsingClassify() { return m_iTargetClass != NUM_AI_CLASSES; } + +protected: + + Class_T m_iSubjectClass; + Class_T m_iTargetClass; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( ai_relationship_classify, CAI_ClassRelationship ); + +BEGIN_DATADESC( CAI_ClassRelationship ) + + DEFINE_FIELD( m_iSubjectClass, FIELD_INTEGER ), + DEFINE_FIELD( m_iTargetClass, FIELD_INTEGER ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Caches entity key values until spawn is called. +// Input : szKeyName - +// szValue - +// Output : +//----------------------------------------------------------------------------- +bool CAI_ClassRelationship::KeyValue( const char *szKeyName, const char *szValue ) +{ + // Override regular subject and target from ai_relationship + if (FStrEq(szKeyName, "subject")) + { + m_iSubjectClass = (Class_T)atoi(szValue); + + // Direct string maybe + if (m_iSubjectClass == CLASS_NONE) + { + for (int i = 0; i < NUM_AI_CLASSES; i++) + { + if (FStrEq(szValue, g_pGameRules->AIClassText(i))) + { + m_iSubjectClass = (Class_T)i; + } + } + } + } + else if (FStrEq(szKeyName, "target")) + { + m_iTargetClass = (Class_T)atoi(szValue); + + // Direct string maybe + if (m_iTargetClass == CLASS_NONE) + { + for (int i = 0; i < NUM_AI_CLASSES; i++) + { + if (FStrEq(szValue, g_pGameRules->AIClassText(i))) + { + m_iTargetClass = (Class_T)i; + } + } + } + } + else + return BaseClass::KeyValue(szKeyName, szValue); + + return true; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CAI_ClassRelationship::ChangeRelationships( int disposition, int iReverting, CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + if( iReverting != NOT_REVERTING && m_iPreviousDisposition == -1 ) + { + // Trying to revert without having ever set the relationships! + DevMsg( 2, "ai_relationship cannot revert changes before they are applied!\n"); + return; + } + + if ( m_iPreviousDisposition == -1 && iReverting == NOT_REVERTING ) + { + // Set previous disposition. + m_iPreviousDisposition = CBaseCombatCharacter::GetDefaultRelationshipDisposition( m_iSubjectClass, m_iTargetClass ); + m_iPreviousRank = CBaseCombatCharacter::GetDefaultRelationshipPriority( m_iSubjectClass, m_iTargetClass ); + } + + // We can't actually reset to "default" without resetting all class relationships period, so we just use the previous disposition. + // There probably wouldn't be much overlap with this entity anyway. + if ( iReverting == REVERTING_TO_PREV || iReverting == REVERTING_TO_DEFAULT ) + { + CBaseCombatCharacter::SetDefaultRelationship(m_iSubjectClass, m_iTargetClass, (Disposition_t)m_iPreviousDisposition, m_iPreviousRank ); + + if( m_bReciprocal ) + { + CBaseCombatCharacter::SetDefaultRelationship(m_iTargetClass, m_iSubjectClass, (Disposition_t)m_iPreviousDisposition, m_iPreviousRank ); + } + } + else if( CBaseCombatCharacter::GetDefaultRelationshipDisposition( m_iSubjectClass, m_iTargetClass ) != disposition || + CBaseCombatCharacter::GetDefaultRelationshipPriority( m_iSubjectClass, m_iTargetClass ) != m_iRank ) + { + // Apply the relationship to the subject + CBaseCombatCharacter::SetDefaultRelationship(m_iSubjectClass, m_iTargetClass, (Disposition_t)disposition, m_iRank ); + + // Notification flags can't be used here. Sorry. + + // This relationship is applied to target and subject alike + if ( m_bReciprocal ) + { + // Apply the relationship to the target + CBaseCombatCharacter::SetDefaultRelationship(m_iTargetClass, m_iSubjectClass, (Disposition_t)disposition, m_iRank ); + } + } +} +#endif + diff --git a/mp/src/game/server/ai_scriptconditions.cpp b/mp/src/game/server/ai_scriptconditions.cpp index 9f10c0c3..205bcfb9 100644 --- a/mp/src/game/server/ai_scriptconditions.cpp +++ b/mp/src/game/server/ai_scriptconditions.cpp @@ -52,6 +52,9 @@ BEGIN_DATADESC( CAI_ScriptConditions ) DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SatisfyConditions", InputSatisfyConditions ), +#endif //--------------------------------- @@ -195,12 +198,20 @@ bool CAI_ScriptConditions::EvalState( const EvalArgs_t &args ) -1, // NPC_STATE_PLAYDEAD -1, // NPC_STATE_PRONE -1, // NPC_STATE_DEAD +#ifdef MAPBASE + 3, // A "Don't care" for the maximum value +#endif }; int valState = stateVals[pNpc->m_NPCState]; if ( valState < 0 ) { +#ifdef MAPBASE + if (m_fMinState == 0) + return true; +#endif + if ( pNpc->m_NPCState == NPC_STATE_SCRIPT && m_fScriptStatus >= TRS_TRUE ) return true; @@ -676,6 +687,25 @@ void CAI_ScriptConditions::InputDisable( inputdata_t &inputdata ) Disable(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- + +void CAI_ScriptConditions::InputSatisfyConditions( inputdata_t &inputdata ) +{ + // This satisfies things. + CBaseEntity *pActivator = HasSpawnFlags(SF_ACTOR_AS_ACTIVATOR) ? inputdata.value.Entity() : this; + m_OnConditionsSatisfied.FireOutput(pActivator, this); + + + //All done! + if ( m_ElementList.Count() == 1 ) + { + Disable(); + m_ElementList.Purge(); + } +} +#endif + //----------------------------------------------------------------------------- bool CAI_ScriptConditions::IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone ) @@ -829,6 +859,11 @@ void CAI_ScriptConditions::OnEntitySpawned( CBaseEntity *pEntity ) if ( pEntity->MyNPCPointer() == NULL ) return; +#ifdef MAPBASE + if ( m_Actor == NULL_STRING ) + return; +#endif + if ( pEntity->NameMatches( m_Actor ) ) { if ( ActorInList( pEntity ) == false ) diff --git a/mp/src/game/server/ai_scriptconditions.h b/mp/src/game/server/ai_scriptconditions.h index 8162c100..f63096ce 100644 --- a/mp/src/game/server/ai_scriptconditions.h +++ b/mp/src/game/server/ai_scriptconditions.h @@ -159,6 +159,9 @@ private: // Input handlers void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSatisfyConditions( inputdata_t &inputdata ); +#endif // Output handlers COutputEvent m_OnConditionsSatisfied; diff --git a/mp/src/game/server/ai_speech.cpp b/mp/src/game/server/ai_speech.cpp index da85fdd9..370f6f17 100644 --- a/mp/src/game/server/ai_speech.cpp +++ b/mp/src/game/server/ai_speech.cpp @@ -16,6 +16,9 @@ #include "AI_Criteria.h" #include "isaverestore.h" #include "sceneentity.h" +#ifdef MAPBASE +#include "ai_squad.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include @@ -180,6 +183,25 @@ BEGIN_SIMPLE_DATADESC( CAI_Expresser ) DEFINE_FIELD( m_flLastTimeAcceptedSpeak, FIELD_TIME ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAI_Expresser, "Expresser class for complex speech." ) + + DEFINE_SCRIPTFUNC( IsSpeaking, "Check if the actor is speaking." ) + DEFINE_SCRIPTFUNC( CanSpeak, "Check if the actor can speak." ) + DEFINE_SCRIPTFUNC( BlockSpeechUntil, "Block speech for a certain amount of time. This is stored in curtime." ) + DEFINE_SCRIPTFUNC( ForceNotSpeaking, "If the actor is speaking, force the system to recognize them as not speaking." ) + + DEFINE_SCRIPTFUNC( GetVoicePitch, "Get the actor's voice pitch. Used in sentences." ) + DEFINE_SCRIPTFUNC( SetVoicePitch, "Set the actor's voice pitch. Used in sentences." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSpeakRawScene, "SpeakRawScene", "Speak a raw, instanced VCD scene as though it were played through the Response System. Return whether the scene successfully plays." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSpeakAutoGeneratedScene, "SpeakAutoGeneratedScene", "Speak an automatically generated, instanced VCD scene for this sound as though it were played through the Response System. Return whether the scene successfully plays." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSpeakRawSentence, "SpeakRawSentence", "Speak a raw sentence as though it were played through the Response System. Return the sentence's index; -1 if not successfully played." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSpeak, "Speak", "Speak a response concept with the specified modifiers." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------- bool CAI_Expresser::SemaphoreIsAvailable( CBaseEntity *pTalker ) @@ -274,6 +296,17 @@ static const int LEN_SPECIFIC_SCENE_MODIFIER = strlen( AI_SPECIFIC_SCENE_MODIFIE //----------------------------------------------------------------------------- bool CAI_Expresser::SpeakFindResponse( AI_Response &outResponse, AIConcept_t concept, const char *modifiers /*= NULL*/ ) { +#ifdef MAPBASE + AI_CriteriaSet set; + + if (modifiers) + { + MergeModifiers(set, modifiers); + } + + // Now return the code in the new function. + return SpeakFindResponse( outResponse, concept, set ); +#else IResponseSystem *rs = GetOuter()->GetResponseSystem(); if ( !rs ) { @@ -318,6 +351,108 @@ bool CAI_Expresser::SpeakFindResponse( AI_Response &outResponse, AIConcept_t con pPlayer->ModifyOrAppendPlayerCriteria( set ); } +#ifdef MAPBASE + GetOuter()->ReAppendContextCriteria( set ); +#endif + + // Now that we have a criteria set, ask for a suitable response + bool found = rs->FindBestResponse( set, outResponse, this ); + + if ( rr_debugresponses.GetInt() == 3 ) + { + if ( ( GetOuter()->MyNPCPointer() && GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) || GetOuter()->IsPlayer() ) + { + const char *pszName = GetOuter()->IsPlayer() ? + ((CBasePlayer*)GetOuter())->GetPlayerName() : GetOuter()->GetDebugName(); + + if ( found ) + { + const char *szReponse = outResponse.GetResponsePtr(); + Warning( "RESPONSERULES: %s spoke '%s'. Found response '%s'.\n", pszName, concept, szReponse ); + } + else + { + Warning( "RESPONSERULES: %s spoke '%s'. Found no matching response.\n", pszName, concept ); + } + } + } + + if ( !found ) + return false; + + const char *szReponse = outResponse.GetResponsePtr(); + if ( !szReponse[0] ) + return false; + + if ( ( outResponse.GetOdds() < 100 ) && ( random->RandomInt( 1, 100 ) <= outResponse.GetOdds() ) ) + return false; + + return true; +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Merges modifiers with set. +//----------------------------------------------------------------------------- +void CAI_Expresser::MergeModifiers( AI_CriteriaSet& set, const char *modifiers ) +{ + char copy_modifiers[ 255 ]; + const char *pCopy; + char key[ 128 ] = { 0 }; + char value[ 128 ] = { 0 }; + + Q_strncpy( copy_modifiers, modifiers, sizeof( copy_modifiers ) ); + pCopy = copy_modifiers; + + while( pCopy ) + { + pCopy = SplitContext( pCopy, key, sizeof( key ), value, sizeof( value ), NULL ); + + if( *key && *value ) + { + set.AppendCriteria( key, value, CONCEPT_WEIGHT ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Searches for a possible response, takes an AI_CriteriaSet instead. +// Input : concept - +// NULL - +// Output : AI_Response +//----------------------------------------------------------------------------- +bool CAI_Expresser::SpeakFindResponse( AI_Response &outResponse, AIConcept_t concept, const AI_CriteriaSet &modifiers ) +{ + IResponseSystem *rs = GetOuter()->GetResponseSystem(); + if ( !rs ) + { + Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" ); + return NULL; + } + + AI_CriteriaSet set; + // Always include the concept name + set.AppendCriteria( "concept", concept, CONCEPT_WEIGHT ); + + // Tier 1: Criteria + // Let our outer fill in most match criteria + GetOuter()->ModifyOrAppendCriteria( set ); + + // Append local player criteria to set, but not if this is a player doing the talking + if ( !GetOuter()->IsPlayer() ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); + if( pPlayer ) + pPlayer->ModifyOrAppendPlayerCriteria( set ); + } + + // Tier 2: Modifiers + set.MergeSet(modifiers); + + // Tier 3: Contexts + GetOuter()->ReAppendContextCriteria( set ); + // Now that we have a criteria set, ask for a suitable response bool found = rs->FindBestResponse( set, outResponse, this ); @@ -352,21 +487,70 @@ bool CAI_Expresser::SpeakFindResponse( AI_Response &outResponse, AIConcept_t con return true; } +#endif //----------------------------------------------------------------------------- // Purpose: Dispatches the result // Input : *response - //----------------------------------------------------------------------------- +#ifdef MAPBASE +bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response& response, IRecipientFilter *filter, const AI_CriteriaSet *modifiers ) +#else bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response& response, IRecipientFilter *filter /* = NULL */ ) +#endif { bool spoke = false; float delay = response.GetDelay(); const char *szResponse = response.GetResponsePtr(); soundlevel_t soundlevel = response.GetSoundLevel(); +#ifdef MAPBASE + if (szResponse[0] == '$') + { + const char *context = szResponse + 1; + const char *replace = GetOuter()->GetContextValue(context); + + // If we can't find the context, check modifiers + if (!replace && modifiers) + { + for (int i = 0; i < modifiers->GetCount(); i++) + { + if (FStrEq(context, modifiers->GetName(i))) + { + replace = modifiers->GetValue(i); + break; + } + } + } + + if (replace) + { + CGMsg( 1, CON_GROUP_CHOREO, "Replacing %s with %s...\n", response, replace ); + szResponse = replace; + + // Precache it now because it may not have been precached before + switch ( response.GetType() ) + { + case RESPONSE_SPEAK: + { + GetOuter()->PrecacheScriptSound( szResponse ); + } + break; + + case RESPONSE_SCENE: + { + // TODO: Gender handling? + PrecacheInstancedScene( szResponse ); + } + break; + } + } + } +#endif + if ( IsSpeaking() && concept[0] != 0 ) { - DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) already speaking, forcing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept ); + CGMsg( 1, CON_GROUP_CHOREO, "SpeakDispatchResponse: Entity ( %i/%s ) already speaking, forcing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept ); // Tracker 15911: Can break the game if we stop an imported map placed lcs here, so only // cancel actor out of instanced scripted scenes. ywb @@ -375,7 +559,7 @@ bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response& res if ( IsRunningScriptedScene( GetOuter() ) ) { - DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) refusing to speak due to scene entity, tossing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept ); + CGMsg( 1, CON_GROUP_CHOREO, "SpeakDispatchResponse: Entity ( %i/%s ) refusing to speak due to scene entity, tossing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept ); return false; } } @@ -390,14 +574,18 @@ bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response& res if ( !response.ShouldntUseScene() ) { // This generates a fake CChoreoScene wrapping the sound.txt name +#ifdef MAPBASE + spoke = SpeakAutoGeneratedScene( szResponse, delay, &response, filter ); +#else spoke = SpeakAutoGeneratedScene( szResponse, delay ); +#endif } else { float speakTime = GetResponseDuration( response ); GetOuter()->EmitSound( szResponse ); - DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) playing sound '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), szResponse ); + CGMsg( 1, CON_GROUP_CHOREO, "SpeakDispatchResponse: Entity ( %i/%s ) playing sound '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), szResponse ); NoteSpeaking( speakTime, delay ); spoke = true; } @@ -436,6 +624,57 @@ bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response& res NDebugOverlay::Text( vPrintPos, CFmtStr( "%s: %s", concept, szResponse ), true, 1.5 ); } +#ifdef MAPBASE + if (response.GetContext()) + { + const char *pszContext = response.GetContext(); + + // Check for operators + const char *pOperator = Q_strstr(pszContext, ":")+1; + if (pOperator && (pOperator[0] == '+' || pOperator[0] == '-' || + pOperator[0] == '*' || pOperator[0] == '/')) + { + pszContext = ParseApplyContext(pszContext); + } + + int iContextFlags = response.GetContextFlags(); + if ( iContextFlags & APPLYCONTEXT_SQUAD ) + { + CAI_BaseNPC *pNPC = GetOuter()->MyNPCPointer(); + if (pNPC && pNPC->GetSquad()) + { + AISquadIter_t iter; + CAI_BaseNPC *pSquadmate = pNPC->GetSquad()->GetFirstMember( &iter ); + while ( pSquadmate ) + { + pSquadmate->AddContext( pszContext ); + + pSquadmate = pNPC->GetSquad()->GetNextMember( &iter ); + } + } + } + if ( iContextFlags & APPLYCONTEXT_ENEMY ) + { + CBaseEntity *pEnemy = GetOuter()->GetEnemy(); + if ( pEnemy ) + { + pEnemy->AddContext( pszContext ); + } + } + if ( iContextFlags & APPLYCONTEXT_WORLD ) + { + CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) ); + if ( pEntity ) + { + pEntity->AddContext( pszContext ); + } + } + if ( iContextFlags == 0 || iContextFlags & APPLYCONTEXT_SELF ) + { + GetOuter()->AddContext( pszContext ); + } + } +#else if ( response.IsApplyContextToWorld() ) { CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) ); @@ -448,7 +687,7 @@ bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response& res { GetOuter()->AddContext( response.GetContext() ); } - +#endif SetSpokeConcept( concept, &response ); } @@ -516,6 +755,32 @@ bool CAI_Expresser::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/ return spoke; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Uses an AI_CriteriaSet directly instead of using context-format modifier text. +// Input : concept - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_Expresser::Speak( AIConcept_t concept, const AI_CriteriaSet& modifiers, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + AI_Response response; + bool result = SpeakFindResponse( response, concept, modifiers ); + if ( !result ) + return false; + + SpeechMsg( GetOuter(), "%s (%p) spoke %s (%f)\n", STRING(GetOuter()->GetEntityName()), GetOuter(), concept, gpGlobals->curtime ); + + bool spoke = SpeakDispatchResponse( concept, response, filter, &modifiers ); + if ( pszOutResponseChosen ) + { + const char *szResponse = response.GetResponsePtr(); + Q_strncpy( pszOutResponseChosen, szResponse, bufsize ); + } + + return spoke; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -544,9 +809,17 @@ bool CAI_Expresser::SpeakRawScene( const char *pszScene, float delay, AI_Respons } // This will create a fake .vcd/CChoreoScene to wrap the sound to be played +#ifdef MAPBASE +bool CAI_Expresser::SpeakAutoGeneratedScene( char const *soundname, float delay, AI_Response *response, IRecipientFilter *filter ) +#else bool CAI_Expresser::SpeakAutoGeneratedScene( char const *soundname, float delay ) +#endif { +#ifdef MAPBASE + float speakTime = GetOuter()->PlayAutoGeneratedSoundScene( soundname, delay, response, filter ); +#else float speakTime = GetOuter()->PlayAutoGeneratedSoundScene( soundname ); +#endif if ( speakTime > 0 ) { SpeechMsg( GetOuter(), "SpeakAutoGeneratedScene( %s, %f) %f\n", soundname, delay, speakTime ); @@ -763,8 +1036,30 @@ void CAI_Expresser::SetSpokeConcept( AIConcept_t concept, AI_Response *response, slot->response = new AI_Response( *response ); } +#ifdef MAPBASE + // This "weapondelay" was just in player allies before. + // Putting it here eliminates the need to implement OnSpokeConcept on all NPCs for weapondelay. + if ( bCallback ) + { + if( response != NULL && (response->GetParams()->flags & AI_ResponseParams::RG_WEAPONDELAY) ) + { + if ( GetOuter()->IsNPC() ) + { + // Stop shooting, as instructed, so that my speech can be heard. + GetOuter()->MyNPCPointer()->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + response->GetWeaponDelay() ); + } + else + { + Warning("%s response %s wants to use weapondelay, but %s is not a NPC!\n", GetOuter()->GetDebugName(), response->GetResponsePtr(), GetOuter()->GetDebugName()); + } + } + + GetSink()->OnSpokeConcept( concept, response ); + } +#else if ( bCallback ) GetSink()->OnSpokeConcept( concept, response ); +#endif } //------------------------------------- @@ -831,11 +1126,71 @@ void CAI_Expresser::SpeechMsg( CBaseEntity *pFlex, const char *pszFormat, ... ) } else { - DevMsg( "%s", string ); + CGMsg( 1, CON_GROUP_CHOREO "%s", string ); } UTIL_LogPrintf( "%s", string ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CAI_Expresser::ParseApplyContext( const char *szContext ) +{ + char szKey[128]; + char szValue[128]; + float flDuration = 0.0; + + SplitContext(szContext, szKey, sizeof(szKey), szValue, sizeof(szValue), &flDuration); + + // This is the value without the operator + const char *pszValue = szValue + 1; + + // This is the operator itself + char cOperator = szValue[0]; + + // This is the value to operate with (e.g. the "7" in "+7") + float flNewValue = atof( pszValue ); + + if (flNewValue == 0.0f) + { + // If it's really 0, then this is a waste of time + Warning("\"%s\" was detected by applyContext operators as an operable number, but it's not.\n", szValue); + return strdup(szContext); + } + + // This is the existing value; will be operated upon and become the final assignment + float flValue = 0.0f; + const char *szExistingValue = GetOuter()->GetContextValue(szKey); + if (szExistingValue) + flValue = atof(szExistingValue); + + // Do the operation based on what the character was + switch (cOperator) + { + case '+': flValue += flNewValue; break; + case '-': flValue -= flNewValue; break; + case '*': flValue *= flNewValue; break; + case '/': flValue /= flNewValue; break; + } + + Q_snprintf(szValue, sizeof(szValue), "%f", flValue); + + // Remove all trailing 0's from the float to maintain whole integers + int i; + for (i = strlen(szValue)-1; szValue[i] == '0'; i--) + { + szValue[i] = '\0'; + } + + // If there were only zeroes past the period, this is a whole number. Remove the period + if (szValue[i] == '.') + szValue[i] = '\0'; + + return UTIL_VarArgs("%s:%s:%f", szKey, szValue, flDuration); +} +#endif + //----------------------------------------------------------------------------- @@ -854,6 +1209,7 @@ void CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_C set.AppendCriteria( "npcstate", UTIL_VarArgs( "[NPCState::%s]", pStateNames[pSpeaker->m_NPCState] ) ); } +#ifndef MAPBASE if ( pSpeaker->GetEnemy() ) { set.AppendCriteria( "enemy", pSpeaker->GetEnemy()->GetClassname() ); @@ -866,6 +1222,7 @@ void CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_C else set.AppendCriteria( "timesincecombat", UTIL_VarArgs( "%f", gpGlobals->curtime - pSpeaker->GetLastEnemyTime() ) ); } +#endif set.AppendCriteria( "speed", UTIL_VarArgs( "%.3f", pSpeaker->GetSmoothedVelocity().Length() ) ); @@ -984,4 +1341,4 @@ void CMultiplayer_Expresser::AllowMultipleScenes() void CMultiplayer_Expresser::DisallowMultipleScenes() { m_bAllowMultipleScenes = false; -} \ No newline at end of file +} diff --git a/mp/src/game/server/ai_speech.h b/mp/src/game/server/ai_speech.h index fa173c1e..87b74e07 100644 --- a/mp/src/game/server/ai_speech.h +++ b/mp/src/game/server/ai_speech.h @@ -157,10 +157,19 @@ public: // -------------------------------- bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); +#ifdef MAPBASE + bool Speak( AIConcept_t concept, const AI_CriteriaSet& modifiers, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); + bool SpeakFindResponse( AI_Response &response, AIConcept_t concept, const AI_CriteriaSet& modifiers ); + void MergeModifiers( AI_CriteriaSet& set, const char *modifiers ); +#endif // These two methods allow looking up a response and dispatching it to be two different steps bool SpeakFindResponse( AI_Response &response, AIConcept_t concept, const char *modifiers = NULL ); +#ifdef MAPBASE + bool SpeakDispatchResponse( AIConcept_t concept, AI_Response &response, IRecipientFilter *filter = NULL, const AI_CriteriaSet *modifiers = NULL ); +#else bool SpeakDispatchResponse( AIConcept_t concept, AI_Response &response, IRecipientFilter *filter = NULL ); +#endif float GetResponseDuration( AI_Response &response ); virtual int SpeakRawSentence( const char *pszSentence, float delay, float volume = VOL_NORM, soundlevel_t soundlevel = SNDLVL_TALKING, CBaseEntity *pListener = NULL ); @@ -194,17 +203,33 @@ public: // Force the NPC to release the semaphore & clear next speech time void ForceNotSpeaking( void ); +#ifdef MAPBASE_VSCRIPT + bool ScriptSpeakRawScene( char const *soundname, float delay ) { return SpeakRawScene( soundname, delay, NULL ); } + bool ScriptSpeakAutoGeneratedScene( char const *soundname, float delay ) { return SpeakAutoGeneratedScene( soundname, delay ); } + int ScriptSpeakRawSentence( char const *pszSentence, float delay ) { return SpeakRawSentence( pszSentence, delay ); } + bool ScriptSpeak( char const *concept, const char *modifiers ) { return Speak( concept, modifiers[0] != '\0' ? modifiers : NULL ); } +#endif + protected: CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc ); bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL ); // This will create a fake .vcd/CChoreoScene to wrap the sound to be played +#ifdef MAPBASE + bool SpeakAutoGeneratedScene( char const *soundname, float delay, AI_Response *response = NULL, IRecipientFilter *filter = NULL ); +#else bool SpeakAutoGeneratedScene( char const *soundname, float delay ); +#endif void DumpHistories(); void SpeechMsg( CBaseEntity *pFlex, PRINTF_FORMAT_STRING const char *pszFormat, ... ); +#ifdef MAPBASE + // Handles context operators + const char *ParseApplyContext( const char *szContext ); +#endif + // -------------------------------- CAI_ExpresserSink *GetSink() { return m_pSink; } @@ -281,9 +306,15 @@ public: virtual void NoteSpeaking( float duration, float delay ); virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); +#ifdef MAPBASE + virtual bool Speak( AIConcept_t concept, const AI_CriteriaSet& modifiers, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); +#endif // These two methods allow looking up a response and dispatching it to be two different steps bool SpeakFindResponse( AI_Response& response, AIConcept_t concept, const char *modifiers = NULL ); +#ifdef MAPBASE + bool SpeakFindResponse( AI_Response& response, AIConcept_t concept, const AI_CriteriaSet& modifiers ); +#endif bool SpeakDispatchResponse( AIConcept_t concept, AI_Response& response ); virtual void PostSpeakDispatchResponse( AIConcept_t concept, AI_Response& response ) { return; } float GetResponseDuration( AI_Response& response ); @@ -324,6 +355,18 @@ inline bool CAI_ExpresserHost::Speak( AIConcept_t concept, const char return this->GetExpresser()->Speak( concept, modifiers, pszOutResponseChosen, bufsize, filter ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Version of Speak() that takes a direct AI_CriteriaSet for modifiers. +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::Speak( AIConcept_t concept, const AI_CriteriaSet& modifiers, char *pszOutResponseChosen /*=NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + AssertOnce( this->GetExpresser()->GetOuter() == this ); + return this->GetExpresser()->Speak( concept, modifiers, pszOutResponseChosen, bufsize, filter ); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template @@ -365,6 +408,16 @@ inline bool CAI_ExpresserHost::SpeakFindResponse( AI_Response& respons return this->GetExpresser()->SpeakFindResponse( response, concept, modifiers ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::SpeakFindResponse( AI_Response& response, AIConcept_t concept, const AI_CriteriaSet& modifiers ) +{ + return this->GetExpresser()->SpeakFindResponse( response, concept, modifiers ); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template diff --git a/mp/src/game/server/ai_squad.cpp b/mp/src/game/server/ai_squad.cpp index 17e0c18f..15e7fb68 100644 --- a/mp/src/game/server/ai_squad.cpp +++ b/mp/src/game/server/ai_squad.cpp @@ -114,6 +114,48 @@ void CAI_SquadManager::DeleteAllSquads(void) CAI_SquadManager::m_pSquads = NULL; } +#ifdef MAPBASE_VSCRIPT +//------------------------------------- +// Purpose: +//------------------------------------- +HSCRIPT CAI_SquadManager::ScriptGetFirstSquad() +{ + return m_pSquads ? g_pScriptVM->RegisterInstance( m_pSquads ) : NULL; +} + +HSCRIPT CAI_SquadManager::ScriptGetNextSquad( HSCRIPT hStart ) +{ + CAI_Squad *pSquad = HScriptToClass( hStart ); + return (pSquad && pSquad->m_pNextSquad) ? g_pScriptVM->RegisterInstance( pSquad->m_pNextSquad ) : NULL; +} + +//------------------------------------- +// Purpose: +//------------------------------------- +HSCRIPT CAI_SquadManager::ScriptFindSquad( const char *squadName ) +{ + CAI_Squad *pSquad = FindSquad( MAKE_STRING(squadName) ); + return pSquad ? g_pScriptVM->RegisterInstance( pSquad ) : NULL; +} + +HSCRIPT CAI_SquadManager::ScriptFindCreateSquad( const char *squadName ) +{ + CAI_Squad *pSquad = FindCreateSquad( MAKE_STRING( squadName ) ); + return pSquad ? g_pScriptVM->RegisterInstance( pSquad ) : NULL; +} + +BEGIN_SCRIPTDESC_ROOT( CAI_SquadManager, SCRIPT_SINGLETON "Manager for NPC squads." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstSquad, "GetFirstSquad", "Get the first squad in the squad list." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNextSquad, "GetNextSquad", "Get the next squad in the squad list starting from the specified squad." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFindSquad, "FindSquad", "Find the specified squad in the squad list. Returns null if none found." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFindCreateSquad, "FindCreateSquad", "Find the specified squad in the squad list or create it if it doesn't exist." ) + + DEFINE_SCRIPTFUNC( NumSquads, "Get the number of squads in the list." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // CAI_Squad // @@ -151,6 +193,38 @@ BEGIN_SIMPLE_DATADESC( CAI_Squad ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAI_Squad, "NPC squads used for schedule coordination, sharing information about enemies, etc." ) + + DEFINE_SCRIPTFUNC( GetName, "Get the squad's name." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstMember, "GetFirstMember", "Get the squad's first member. The parameter is for whether to ignore silent members (see CAI_Squad::IsSilentMember() for more info)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMember, "GetMember", "Get one of the squad's members by their index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAnyMember, "GetAnyMember", "Randomly get any one of the squad's members." ) + DEFINE_SCRIPTFUNC( NumMembers, "Get the squad's number of members. The parameter is for whether to ignore silent members (see CAI_Squad::IsSilentMember() for more info)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSquadIndex, "GetSquadIndex", "Get the index of the specified NPC in the squad." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptUpdateEnemyMemory, "UpdateEnemyMemory", "Updates the squad's memory of an enemy. The first parameter is the updater, the second parameter is the enemy, and the third parameter is the position." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSquadMemberInRange, "SquadMemberInRange", "Get the first squad member found around the specified position in the specified range." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNearestSquadMember, "NearestSquadMember", "Get the squad member nearest to the specified member." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetVisibleSquadMembers, "GetVisibleSquadMembers", "Get the number of squad members visible to the specified member." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSquadMemberNearestTo, "GetSquadMemberNearestTo", "Get the squad member nearest to a point." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsMember, "IsMember", "Returns true if the specified NPC is a member of the squad." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsLeader, "IsLeader", "Returns true if the specified NPC is the squad's leader." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeader, "GetLeader", "Get the squad's leader." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddToSquad, "AddToSquad", "Adds a NPC to the squad." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveFromSquad, "RemoveFromSquad", "Removes a NPC from the squad." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptIsSilentMember, "IsSilentMember", "Returns true if the specified NPC is a \"silent squad member\", which means it's only in squads for enemy information purposes and does not actually participate in any tactics. For example, this is used for npc_enemyfinder and vital allies (e.g. Alyx) in the player's squad. Please note that this does not check if the NPC is in the squad first." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetSquadData, "SetSquadData", "Set the squad data in the specified slot." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSquadData, "GetSquadData", "Get the squad data in the specified slot." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------- CAI_Squad::CAI_Squad(string_t newName) @@ -220,7 +294,7 @@ void CAI_Squad::RemoveFromSquad( CAI_BaseNPC *pNPC, bool bDeath ) int myIndex = m_SquadMembers.Find(pNPC); if (myIndex == -1) { - DevMsg("ERROR: Attempting to remove non-existing squad membmer!\n"); + CGMsg( 1, CON_GROUP_NPC_AI, "ERROR: Attempting to remove non-existing squad member!\n" ); return; } m_SquadMembers.Remove(myIndex); @@ -263,7 +337,7 @@ void CAI_Squad::AddToSquad(CAI_BaseNPC *pNPC) if (m_SquadMembers.Count() == MAX_SQUAD_MEMBERS) { - DevMsg("Error!! Squad %s is too big!!! Replacing last member\n", STRING( this->m_Name )); + CGMsg( 1, CON_GROUP_NPC_AI, "Error!! Squad %s is too big!!! Replacing last member\n", STRING( this->m_Name ) ); m_SquadMembers.Remove(m_SquadMembers.Count()-1); } m_SquadMembers.AddToTail(pNPC); @@ -485,7 +559,7 @@ void CAI_Squad::SquadNewEnemy( CBaseEntity *pEnemy ) { if ( !pEnemy ) { - DevMsg( "ERROR: SquadNewEnemy() - pEnemy is NULL!\n" ); + CGMsg( 1, CON_GROUP_NPC_AI, "ERROR: SquadNewEnemy() - pEnemy is NULL!\n" ); return; } @@ -604,7 +678,7 @@ bool CAI_Squad::OccupyStrategySlotRange( CBaseEntity *pEnemy, int slotIDStart, i // As a debug measure check to see if slot was filled if (!IsSlotOccupied(pEnemy, *pSlot)) { - DevMsg( "ERROR! Vacating an empty slot!\n"); + CGMsg( 1, CON_GROUP_NPC_AI, "ERROR! Vacating an empty slot!\n" ); } // Free the slot @@ -643,7 +717,7 @@ void CAI_Squad::VacateStrategySlot( CBaseEntity *pEnemy, int slot) // As a debug measure check to see if slot was filled if (!IsSlotOccupied(pEnemy, slot)) { - DevMsg( "ERROR! Vacating an empty slot!\n"); + CGMsg( 1, CON_GROUP_NPC_AI, "ERROR! Vacating an empty slot!\n" ); } // Free the slot @@ -783,5 +857,44 @@ bool CAI_Squad::IsSquadInflictor( CBaseEntity *pInflictor ) return (m_hSquadInflictor.Get() == pInflictor); } +#ifdef MAPBASE_VSCRIPT +//------------------------------------------------------------------------------ + +// Functions tailored specifically for VScript. + +HSCRIPT CAI_Squad::ScriptGetFirstMember( bool bIgnoreSilentMembers ) { return ToHScript( GetFirstMember( NULL, bIgnoreSilentMembers ) ); } +HSCRIPT CAI_Squad::ScriptGetMember( int iIndex ) { return iIndex < m_SquadMembers.Count() ? ToHScript( m_SquadMembers[iIndex] ) : NULL; } +HSCRIPT CAI_Squad::ScriptGetAnyMember() { return ToHScript( GetAnyMember() ); } +//int CAI_Squad::ScriptNumMembers( bool bIgnoreSilentMembers ) { return NumMembers( bIgnoreSilentMembers ); } +int CAI_Squad::ScriptGetSquadIndex( HSCRIPT hNPC ) { return GetSquadIndex( HScriptToClass( hNPC ) ); } + +void CAI_Squad::ScriptUpdateEnemyMemory( HSCRIPT hUpdater, HSCRIPT hEnemy, const Vector &position ) { UpdateEnemyMemory( HScriptToClass( hUpdater ), ToEnt( hEnemy ), position ); } + +HSCRIPT CAI_Squad::ScriptSquadMemberInRange( const Vector &vecLocation, float flDist ) { return ToHScript( SquadMemberInRange( vecLocation, flDist ) ); } +HSCRIPT CAI_Squad::ScriptNearestSquadMember( HSCRIPT hMember ) { return ToHScript( NearestSquadMember( HScriptToClass( hMember ) ) ); } +int CAI_Squad::ScriptGetVisibleSquadMembers( HSCRIPT hMember ) { return GetVisibleSquadMembers( HScriptToClass( hMember ) ); } +HSCRIPT CAI_Squad::ScriptGetSquadMemberNearestTo( const Vector &vecLocation ) { return ToHScript( GetSquadMemberNearestTo( vecLocation ) ); } +bool CAI_Squad::ScriptIsMember( HSCRIPT hMember ) { return SquadIsMember( HScriptToClass( hMember ) ); } +bool CAI_Squad::ScriptIsLeader( HSCRIPT hLeader ) { return IsLeader( HScriptToClass( hLeader ) ); } +HSCRIPT CAI_Squad::ScriptGetLeader( void ) { return ToHScript( GetLeader() ); } + +void CAI_Squad::ScriptAddToSquad( HSCRIPT hNPC ) { AddToSquad( HScriptToClass( hNPC ) ); } +void CAI_Squad::ScriptRemoveFromSquad( HSCRIPT hNPC ) { RemoveFromSquad( HScriptToClass( hNPC ), false ); } + +bool CAI_Squad::ScriptIsSilentMember( HSCRIPT hNPC ) { return IsSilentMember( HScriptToClass( hNPC ) ); } + +void CAI_Squad::ScriptSetSquadData( int iSlot, const char *data ) +{ + SetSquadData( iSlot, data ); +} + +const char *CAI_Squad::ScriptGetSquadData( int iSlot ) +{ + const char *data; + GetSquadData( iSlot, &data ); + return data; +} +#endif + //============================================================================= diff --git a/mp/src/game/server/ai_squad.h b/mp/src/game/server/ai_squad.h index 846e9a11..9604d1f0 100644 --- a/mp/src/game/server/ai_squad.h +++ b/mp/src/game/server/ai_squad.h @@ -52,6 +52,14 @@ public: void DeleteSquad( CAI_Squad *pSquad ); void DeleteAllSquads(void); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetFirstSquad(); + HSCRIPT ScriptGetNextSquad( HSCRIPT hStart ); + + HSCRIPT ScriptFindSquad( const char *squadName ); + HSCRIPT ScriptFindCreateSquad( const char *squadName ); +#endif + private: CAI_Squad * m_pSquads; // A linked list of all squads @@ -152,6 +160,36 @@ private: void VacateSlot( CBaseEntity *pEnemy, int i ); bool IsSlotOccupied( CBaseEntity *pEnemy, int i ) const; +#ifdef MAPBASE_VSCRIPT + // Functions tailored specifically for VScript. + ALLOW_SCRIPT_ACCESS(); +private: + + HSCRIPT ScriptGetFirstMember( bool bIgnoreSilentMembers ); + HSCRIPT ScriptGetMember( int iIndex ); + HSCRIPT ScriptGetAnyMember(); + //int ScriptNumMembers( bool bIgnoreSilentMembers ); + int ScriptGetSquadIndex( HSCRIPT hNPC ); + + void ScriptUpdateEnemyMemory( HSCRIPT hUpdater, HSCRIPT hEnemy, const Vector &position ); + + HSCRIPT ScriptSquadMemberInRange( const Vector &vecLocation, float flDist ); + HSCRIPT ScriptNearestSquadMember( HSCRIPT hMember ); + int ScriptGetVisibleSquadMembers( HSCRIPT hMember ); + HSCRIPT ScriptGetSquadMemberNearestTo( const Vector &vecLocation ); + bool ScriptIsMember( HSCRIPT hMember ); + bool ScriptIsLeader( HSCRIPT hLeader ); + HSCRIPT ScriptGetLeader( void ); + + void ScriptAddToSquad( HSCRIPT hNPC ); + void ScriptRemoveFromSquad( HSCRIPT hNPC ); + + bool ScriptIsSilentMember( HSCRIPT hNPC ); + + void ScriptSetSquadData( int iSlot, const char *data ); + const char *ScriptGetSquadData( int iSlot ); +#endif + private: friend class CAI_SaveRestoreBlockHandler; friend class CAI_SquadManager; diff --git a/mp/src/game/server/ai_task.cpp b/mp/src/game/server/ai_task.cpp index a2cf6be6..6e471351 100644 --- a/mp/src/game/server/ai_task.cpp +++ b/mp/src/game/server/ai_task.cpp @@ -222,6 +222,9 @@ void CAI_BaseNPC::InitDefaultTaskSR(void) ADD_DEF_TASK( TASK_ADD_HEALTH ); ADD_DEF_TASK( TASK_GET_PATH_TO_INTERACTION_PARTNER ); ADD_DEF_TASK( TASK_PRE_SCRIPT ); +#ifdef MAPBASE + ADD_DEF_TASK( TASK_FACE_INTERACTION_ANGLES ); +#endif } diff --git a/mp/src/game/server/ai_task.h b/mp/src/game/server/ai_task.h index 43170a39..dc167752 100644 --- a/mp/src/game/server/ai_task.h +++ b/mp/src/game/server/ai_task.h @@ -494,6 +494,11 @@ enum sharedtasks_e // First task of all schedules for playing back scripted sequences TASK_PRE_SCRIPT, +#ifdef MAPBASE + // Faces the actual interaction angles instead of just facing the enemy + TASK_FACE_INTERACTION_ANGLES, +#endif + // ====================================== // IMPORTANT: This must be the last enum // ====================================== diff --git a/mp/src/game/server/baseanimating.cpp b/mp/src/game/server/baseanimating.cpp index 9999c496..446af96d 100644 --- a/mp/src/game/server/baseanimating.cpp +++ b/mp/src/game/server/baseanimating.cpp @@ -27,6 +27,11 @@ #include "datacache/idatacache.h" #include "smoke_trail.h" #include "props.h" +#ifdef MAPBASE +#include "ai_speech.h" +#include "gib.h" +#include "CRagdollMagnet.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -202,9 +207,17 @@ BEGIN_DATADESC( CBaseAnimating ) DEFINE_INPUTFUNC( FIELD_INTEGER, "IgniteNumHitboxFires", InputIgniteNumHitboxFires ), DEFINE_INPUTFUNC( FIELD_FLOAT, "IgniteHitboxFireScale", InputIgniteHitboxFireScale ), DEFINE_INPUTFUNC( FIELD_VOID, "BecomeRagdoll", InputBecomeRagdoll ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "CreateSeparateRagdoll", InputCreateSeparateRagdoll ), + DEFINE_INPUTFUNC( FIELD_VOID, "CreateSeparateRagdollClient", InputCreateSeparateRagdollClient ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetPoseParameter", InputSetPoseParameter ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetLightingOriginHack", InputSetLightingOriginRelative ), DEFINE_INPUTFUNC( FIELD_STRING, "SetLightingOrigin", InputSetLightingOrigin ), DEFINE_OUTPUT( m_OnIgnite, "OnIgnite" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnServerRagdoll, "OnServerRagdoll" ), +#endif DEFINE_INPUT( m_fadeMinDist, FIELD_FLOAT, "fademindist" ), DEFINE_INPUT( m_fadeMaxDist, FIELD_FLOAT, "fademaxdist" ), @@ -212,6 +225,12 @@ BEGIN_DATADESC( CBaseAnimating ) DEFINE_KEYFIELD( m_flModelScale, FIELD_FLOAT, "modelscale" ), DEFINE_INPUTFUNC( FIELD_VECTOR, "SetModelScale", InputSetModelScale ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetModel", InputSetModel ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetCycle", InputSetCycle ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPlaybackRate", InputSetPlaybackRate ), +#endif DEFINE_FIELD( m_fBoneCacheFlags, FIELD_SHORT ), @@ -263,6 +282,59 @@ IMPLEMENT_SERVERCLASS_ST(CBaseAnimating, DT_BaseAnimating) END_SEND_TABLE() +BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) + + DEFINE_SCRIPTFUNC( LookupAttachment, "Get the named attachement id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentOrigin, "GetAttachmentOrigin", "Get the attachement id's origin vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentAngles, "GetAttachmentAngles", "Get the attachement id's angles as a p,y,r vector" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentMatrix, "GetAttachmentMatrix", "Get the attachement id's matrix transform" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPoseParameter, "GetPoseParameter", "Get the specified pose parameter's value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetPoseParameter, "SetPoseParameter", "Set the specified pose parameter to the specified value" ) + DEFINE_SCRIPTFUNC( LookupBone, "Get the named bone id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneTransform, "GetBoneTransform", "Get the transform for the specified bone" ) + DEFINE_SCRIPTFUNC( GetPhysicsBone, "Get physics bone from bone index" ) + DEFINE_SCRIPTFUNC( GetNumBones, "Get the number of bones" ) + DEFINE_SCRIPTFUNC( GetSequence, "Gets the current sequence" ) + DEFINE_SCRIPTFUNC( SetSequence, "Sets the current sequence" ) + DEFINE_SCRIPTFUNC( SequenceLoops, "Loops the current sequence" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSequenceDuration, "SequenceDuration", "Get the specified sequence duration" ) + DEFINE_SCRIPTFUNC( LookupSequence, "Gets the index of the specified sequence name" ) + DEFINE_SCRIPTFUNC( LookupActivity, "Gets the ID of the specified activity name" ) + DEFINE_SCRIPTFUNC_NAMED( HasMovement, "SequenceHasMovement", "Checks if the specified sequence has movement" ) + DEFINE_SCRIPTFUNC( GetSequenceMoveYaw, "Gets the move yaw of the specified sequence" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceMoveDist, "GetSequenceMoveDist", "Gets the move distance of the specified sequence" ) + DEFINE_SCRIPTFUNC( GetSequenceName, "Gets the name of the specified sequence index" ) + DEFINE_SCRIPTFUNC( GetSequenceActivityName, "Gets the activity name of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceActivity, "GetSequenceActivity", "Gets the activity ID of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSelectWeightedSequence, "SelectWeightedSequence", "Selects a sequence for the specified activity ID" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSelectHeaviestSequence, "SelectHeaviestSequence", "Selects the sequence with the heaviest weight for the specified activity ID" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceKeyValues, "GetSequenceKeyValues", "Get a KeyValue class instance on the specified sequence. WARNING: This uses the same KeyValue pointer as GetModelKeyValues!" ) + DEFINE_SCRIPTFUNC( GetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( SetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( GetCycle, "" ) + DEFINE_SCRIPTFUNC( SetCycle, "" ) + DEFINE_SCRIPTFUNC( GetSkin, "Gets the model's skin" ) + DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) +#endif + DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( GetBodygroup, "Gets a bodygroup" ) + DEFINE_SCRIPTFUNC( GetBodygroupName, "Gets a bodygroup name" ) + DEFINE_SCRIPTFUNC( FindBodygroupByName, "Finds a bodygroup by name" ) + DEFINE_SCRIPTFUNC( GetBodygroupCount, "Gets the number of models in a bodygroup" ) + DEFINE_SCRIPTFUNC( GetNumBodyGroups, "Gets the number of bodygroups" ) + + DEFINE_SCRIPTFUNC( Dissolve, "Use 'sprites/blueglow1.vmt' for the default material, Time() for the default start time, false for npcOnly if you don't want it to check if the entity is a NPC first, 0 for the default dissolve type, Vector(0,0,0) for the default dissolver origin, and 0 for the default magnitude." ) + DEFINE_SCRIPTFUNC( Ignite, "'NPCOnly' only lets this fall through if the entity is a NPC and 'CalledByLevelDesigner' determines whether to treat this like the Ignite input or just an internal ignition call." ) + DEFINE_SCRIPTFUNC( Scorch, "Makes the entity darker from scorching" ) + + DEFINE_SCRIPTFUNC( BecomeRagdollOnClient, "" ) + DEFINE_SCRIPTFUNC( IsRagdoll, "" ) + DEFINE_SCRIPTFUNC( CanBecomeRagdoll, "" ) +#endif +END_SCRIPTDESC(); CBaseAnimating::CBaseAnimating() { @@ -525,7 +597,11 @@ void CBaseAnimating::StudioFrameAdvance() //----------------------------------------------------------------------------- // Set the relative lighting origin //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CBaseAnimating::SetLightingOriginRelative( string_t strLightingOriginRelative, inputdata_t *inputdata ) +#else void CBaseAnimating::SetLightingOriginRelative( string_t strLightingOriginRelative ) +#endif { if ( strLightingOriginRelative == NULL_STRING ) { @@ -533,7 +609,11 @@ void CBaseAnimating::SetLightingOriginRelative( string_t strLightingOriginRelati } else { +#ifdef MAPBASE + CBaseEntity *pLightingOrigin = gEntList.FindEntityByName( NULL, strLightingOriginRelative, this, inputdata ? inputdata->pActivator : NULL, inputdata ? inputdata->pCaller : NULL ); +#else CBaseEntity *pLightingOrigin = gEntList.FindEntityByName( NULL, strLightingOriginRelative ); +#endif if ( !pLightingOrigin ) { DevWarning( "%s: Could not find info_lighting_relative '%s'!\n", GetClassname(), STRING( strLightingOriginRelative ) ); @@ -563,7 +643,11 @@ void CBaseAnimating::SetLightingOriginRelative( string_t strLightingOriginRelati //----------------------------------------------------------------------------- // Set the lighting origin //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CBaseAnimating::SetLightingOrigin( string_t strLightingOrigin, inputdata_t *inputdata ) +#else void CBaseAnimating::SetLightingOrigin( string_t strLightingOrigin ) +#endif { if ( strLightingOrigin == NULL_STRING ) { @@ -571,7 +655,11 @@ void CBaseAnimating::SetLightingOrigin( string_t strLightingOrigin ) } else { +#ifdef MAPBASE + CBaseEntity *pLightingOrigin = gEntList.FindEntityByName( NULL, strLightingOrigin, this, inputdata ? inputdata->pActivator : NULL, inputdata ? inputdata->pCaller : NULL ); +#else CBaseEntity *pLightingOrigin = gEntList.FindEntityByName( NULL, strLightingOrigin ); +#endif if ( !pLightingOrigin ) { DevWarning( "%s: Could not find lighting origin entity named '%s'!\n", GetClassname(), STRING( strLightingOrigin ) ); @@ -594,9 +682,15 @@ void CBaseAnimating::SetLightingOrigin( string_t strLightingOrigin ) //----------------------------------------------------------------------------- void CBaseAnimating::InputSetLightingOriginRelative( inputdata_t &inputdata ) { +#ifdef MAPBASE + // Pass our input data now. + // (and stop doing that MAKE_STRING nonsense) + SetLightingOriginRelative( inputdata.value.StringID(), &inputdata ); +#else // Find our specified target string_t strLightingOriginRelative = MAKE_STRING( inputdata.value.String() ); SetLightingOriginRelative( strLightingOriginRelative ); +#endif } //----------------------------------------------------------------------------- @@ -605,9 +699,15 @@ void CBaseAnimating::InputSetLightingOriginRelative( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CBaseAnimating::InputSetLightingOrigin( inputdata_t &inputdata ) { +#ifdef MAPBASE + // Pass our input data now. + // (and stop doing that MAKE_STRING nonsense) + SetLightingOrigin( inputdata.value.StringID(), &inputdata ); +#else // Find our specified target string_t strLightingOrigin = MAKE_STRING( inputdata.value.String() ); SetLightingOrigin( strLightingOrigin ); +#endif } //----------------------------------------------------------------------------- @@ -621,6 +721,37 @@ void CBaseAnimating::InputSetModelScale( inputdata_t &inputdata ) SetModelScale( vecScale.x, vecScale.y ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets our current model +//----------------------------------------------------------------------------- +void CBaseAnimating::InputSetModel( inputdata_t &inputdata ) +{ + const char *szModel = inputdata.value.String(); + if (PrecacheModel(szModel, false) != -1) + { + SetModelName(AllocPooledString(szModel)); + SetModel(szModel); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our current cycle +//----------------------------------------------------------------------------- +void CBaseAnimating::InputSetCycle( inputdata_t &inputdata ) +{ + SetCycle( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our current cycle +//----------------------------------------------------------------------------- +void CBaseAnimating::InputSetPlaybackRate( inputdata_t &inputdata ) +{ + SetPlaybackRate( inputdata.value.Float() ); +} +#endif + //========================================================= // SelectWeightedSequence @@ -1149,6 +1280,21 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent ) EmitSound( pEvent->options ); return; } +#ifdef MAPBASE + else if ( pEvent->event == AE_NPC_RESPONSE ) + { + if (!MyNPCPointer()->GetExpresser()->IsSpeaking()) + { + DispatchResponse( pEvent->options ); + } + return; + } + else if ( pEvent->event == AE_NPC_RESPONSE_FORCED ) + { + DispatchResponse( pEvent->options ); + return; + } +#endif else if ( pEvent->event == AE_RAGDOLL ) { // Convert to ragdoll immediately @@ -2028,6 +2174,95 @@ bool CBaseAnimating::GetAttachment( int iAttachment, Vector &absOrigin, Vector * return bRet; } +//----------------------------------------------------------------------------- +// Purpose: Returns the world location and world angles of an attachment to vscript caller +// Input : attachment name +// Output : location and angles +//----------------------------------------------------------------------------- +const Vector& CBaseAnimating::ScriptGetAttachmentOrigin( int iAttachment ) +{ + + static Vector absOrigin; + static QAngle qa; + + CBaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + + return absOrigin; +} + +const Vector& CBaseAnimating::ScriptGetAttachmentAngles( int iAttachment ) +{ + + static Vector absOrigin; + static Vector absAngles; + static QAngle qa; + + CBaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + absAngles.x = qa.x; + absAngles.y = qa.y; + absAngles.z = qa.z; + return absAngles; +} + +#ifdef MAPBASE_VSCRIPT +HSCRIPT CBaseAnimating::ScriptGetAttachmentMatrix( int iAttachment ) +{ + static matrix3x4_t matrix; + + CBaseAnimating::GetAttachment( iAttachment, matrix ); + return g_pScriptVM->RegisterInstance( &matrix ); +} + +float CBaseAnimating::ScriptGetPoseParameter( const char* szName ) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return 0.0f; + + int iPoseParam = LookupPoseParameter( pHdr, szName ); + return GetPoseParameter( iPoseParam ); +} + +void CBaseAnimating::ScriptSetPoseParameter( const char* szName, float fValue ) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return; + + int iPoseParam = LookupPoseParameter( pHdr, szName ); + SetPoseParameter( pHdr, iPoseParam, fValue ); +} + +void CBaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ) +{ + if (hTransform == NULL) + return; + + GetBoneTransform( iBone, *HScriptToClass( hTransform ) ); +} + +//----------------------------------------------------------------------------- +// VScript access to sequence's key values +// for iteration and value access, use: +// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString, +// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey +// NOTE: This is recycled from ScriptGetModelKeyValues() and uses its pointer!!! +//----------------------------------------------------------------------------- +HSCRIPT CBaseAnimating::ScriptGetSequenceKeyValues( int iSequence ) +{ + KeyValues *pSeqKeyValues = GetSequenceKeyValues( iSequence ); + HSCRIPT hScript = NULL; + if ( pSeqKeyValues ) + { + // UNDONE: how does destructor get called on this + m_pScriptModelKeyValues = hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pSeqKeyValues, true ); + + // UNDONE: who calls ReleaseInstance on this??? Does name need to be unique??? + } + + return hScript; +} +#endif //----------------------------------------------------------------------------- // Returns the attachment in local space @@ -3215,6 +3450,18 @@ void CBaseAnimating::CopyAnimationDataFrom( CBaseAnimating *pSource ) this->m_flAnimTime = pSource->m_flAnimTime; this->m_nBody = pSource->m_nBody; this->m_nSkin = pSource->m_nSkin; +#ifdef MAPBASE + this->m_clrRender = pSource->m_clrRender; + this->m_nRenderMode = pSource->m_nRenderMode; + this->m_nRenderFX = pSource->m_nRenderFX; + this->m_iViewHideFlags = pSource->m_iViewHideFlags; + this->m_fadeMinDist = pSource->m_fadeMinDist; + this->m_fadeMaxDist = pSource->m_fadeMaxDist; + this->m_flFadeScale = pSource->m_flFadeScale; + + if (this->GetModelScale() != pSource->GetModelScale()) + this->SetModelScale( pSource->GetModelScale() ); +#endif this->LockStudioHdr(); } @@ -3549,6 +3796,72 @@ void CBaseAnimating::InputBecomeRagdoll( inputdata_t &inputdata ) BecomeRagdollOnClient( vec3_origin ); } +#ifdef MAPBASE +void CBaseAnimating::InputCreateSeparateRagdoll( inputdata_t &inputdata ) +{ + CTakeDamageInfo info( this, inputdata.pActivator, 0.0f, DMG_GENERIC ); + + // See if there's a ragdoll magnet that should influence our force. + CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this ); + if( pMagnet ) + { + info.SetDamageForce(pMagnet->GetForceVector( this )); + pMagnet->m_OnUsed.Set(info.GetDamageForce(), this, pMagnet); + } + + CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); +} + +void CBaseAnimating::InputCreateSeparateRagdollClient( inputdata_t &inputdata ) +{ + // I remember there being a reason why this must be initialized to all 0's... + Vector forceVector = Vector(0.0f, 0.0f, 0.0f); + + // See if there's a ragdoll magnet that should influence our force. + CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this ); + if( pMagnet ) + { + forceVector += pMagnet->GetForceVector( this ); + pMagnet->m_OnUsed.Set(forceVector, this, pMagnet); + } + + CBaseEntity *pRagdoll = CreateRagGib( STRING( GetModelName() ), GetAbsOrigin(), GetAbsAngles(), forceVector, 25.0f ); + + if (pRagdoll->GetBaseAnimating()) + { + pRagdoll->GetBaseAnimating()->CopyAnimationDataFrom( this ); + } +} + +void CBaseAnimating::InputSetPoseParameter( inputdata_t &inputdata ) +{ + char token[64]; + Q_strncpy( token, inputdata.value.String(), sizeof(token) ); + char *sChar = strchr( token, ' ' ); + if ( sChar ) + { + *sChar = '\0'; + + // Name + const int index = LookupPoseParameter( token ); + if (index == -1) + { + Warning("SetPoseParameter: Could not find pose parameter \"%s\" on %s\n", token, GetDebugName()); + return; + } + + // Value + const float value = atof( sChar+1 ); + + SetPoseParameter( index, value ); + } + else + { + Warning("SetPoseParameter: \"%s\" is invalid; format is \" \"\n", inputdata.value.String()); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/baseanimating.h b/mp/src/game/server/baseanimating.h index 0550683e..a5d0dc0d 100644 --- a/mp/src/game/server/baseanimating.h +++ b/mp/src/game/server/baseanimating.h @@ -44,6 +44,7 @@ public: DECLARE_DATADESC(); DECLARE_SERVERCLASS(); + DECLARE_ENT_SCRIPTDESC(); virtual void SetModel( const char *szModelName ); virtual void Activate(); @@ -99,6 +100,9 @@ public: inline float SequenceDuration( void ) { return SequenceDuration( m_nSequence ); } float SequenceDuration( CStudioHdr *pStudioHdr, int iSequence ); inline float SequenceDuration( int iSequence ) { return SequenceDuration(GetModelPtr(), iSequence); } +#ifdef MAPBASE_VSCRIPT + inline float ScriptSequenceDuration( int iSequence ) { return SequenceDuration(GetModelPtr(), iSequence); } +#endif float GetSequenceCycleRate( CStudioHdr *pStudioHdr, int iSequence ); inline float GetSequenceCycleRate( int iSequence ) { return GetSequenceCycleRate(GetModelPtr(),iSequence); } float GetLastVisibleCycle( CStudioHdr *pStudioHdr, int iSequence ); @@ -187,6 +191,26 @@ public: bool GetAttachment( int iAttachment, Vector &absOrigin, QAngle &absAngles ); int GetAttachmentBone( int iAttachment ); virtual bool GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld ); + const Vector& ScriptGetAttachmentOrigin(int iAttachment); + const Vector& ScriptGetAttachmentAngles(int iAttachment); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetAttachmentMatrix(int iAttachment); + float ScriptGetPoseParameter(const char* szName); + void ScriptSetPoseParameter(const char* szName, float fValue); + + void ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ); + + int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); } + float ScriptGetSequenceMoveDist( int iSequence ) { return GetSequenceMoveDist( GetModelPtr(), iSequence ); } + int ScriptSelectHeaviestSequence( int activity ) { return SelectHeaviestSequence( (Activity)activity ); } + int ScriptSelectWeightedSequence( int activity, int curSequence ) { return SelectWeightedSequence( (Activity)activity, curSequence ); } + + HSCRIPT ScriptGetSequenceKeyValues( int iSequence ); + + // For VScript + int GetSkin() { return m_nSkin; } + void SetSkin( int iSkin ) { m_nSkin = iSkin; } +#endif // These return the attachment in the space of the entity bool GetAttachmentLocal( const char *szName, Vector &origin, QAngle &angles ); @@ -298,6 +322,11 @@ public: void InputIgniteNumHitboxFires( inputdata_t &inputdata ); void InputIgniteHitboxFireScale( inputdata_t &inputdata ); void InputBecomeRagdoll( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputCreateSeparateRagdoll( inputdata_t &inputdata ); + void InputCreateSeparateRagdollClient( inputdata_t &inputdata ); + void InputSetPoseParameter( inputdata_t &inputdata ); +#endif // Dissolve, returns true if the ragdoll has been created bool Dissolve( const char *pMaterialName, float flStartTime, bool bNPCOnly = true, int nDissolveType = 0, Vector vDissolverOrigin = vec3_origin, int iMagnitude = 0 ); @@ -309,11 +338,19 @@ public: float m_flLastEventCheck; // cycle index of when events were last checked virtual void SetLightingOriginRelative( CBaseEntity *pLightingOriginRelative ); +#ifdef MAPBASE + void SetLightingOriginRelative( string_t strLightingOriginRelative, inputdata_t *inputdata = NULL ); +#else void SetLightingOriginRelative( string_t strLightingOriginRelative ); +#endif CBaseEntity *GetLightingOriginRelative(); virtual void SetLightingOrigin( CBaseEntity *pLightingOrigin ); +#ifdef MAPBASE + void SetLightingOrigin( string_t strLightingOrigin, inputdata_t *inputdata = NULL ); +#else void SetLightingOrigin( string_t strLightingOrigin ); +#endif CBaseEntity *GetLightingOrigin(); const float* GetPoseParameterArray() { return m_flPoseParameter.Base(); } @@ -340,6 +377,12 @@ private: void InputSetLightingOriginRelative( inputdata_t &inputdata ); void InputSetLightingOrigin( inputdata_t &inputdata ); void InputSetModelScale( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetModel( inputdata_t &inputdata ); + + void InputSetCycle( inputdata_t &inputdata ); + void InputSetPlaybackRate( inputdata_t &inputdata ); +#endif bool CanSkipAnimation( void ); @@ -416,6 +459,9 @@ protected: public: COutputEvent m_OnIgnite; +#ifdef MAPBASE + COutputEHANDLE m_OnServerRagdoll; +#endif private: CStudioHdr *m_pStudioHdr; diff --git a/mp/src/game/server/basebludgeonweapon.cpp b/mp/src/game/server/basebludgeonweapon.cpp index ff8df099..cb9c1fc8 100644 --- a/mp/src/game/server/basebludgeonweapon.cpp +++ b/mp/src/game/server/basebludgeonweapon.cpp @@ -93,6 +93,14 @@ void CBaseHLBludgeonWeapon::ItemPostFrame( void ) if ( pOwner == NULL ) return; +#ifdef MAPBASE + if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) + { + WeaponIdle(); + return; + } +#endif + if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) ) { PrimaryAttack(); @@ -363,12 +371,27 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary ) // We want to test the first swing again Vector testEnd = swingStart + forward * GetRange(); + +#ifdef MAPBASE + // Sound has been moved here since we're using the other melee sounds now + WeaponSound( SINGLE ); +#endif // See if we happened to hit water ImpactWater( swingStart, testEnd ); } else { +#ifdef MAPBASE + // Other melee sounds + if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld()) + WeaponSound(MELEE_HIT_WORLD); + else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo)) + WeaponSound(MELEE_MISS); + else + WeaponSound(MELEE_HIT); +#endif + Hit( traceHit, nHitActivity, bIsSecondary ? true : false ); } @@ -379,6 +402,8 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary ) m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate(); m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); +#ifndef MAPBASE //Play swing sound WeaponSound( SINGLE ); +#endif } diff --git a/mp/src/game/server/basecombatcharacter.cpp b/mp/src/game/server/basecombatcharacter.cpp index 71d6c844..3a82ce65 100644 --- a/mp/src/game/server/basecombatcharacter.cpp +++ b/mp/src/game/server/basecombatcharacter.cpp @@ -61,6 +61,11 @@ extern int g_interactionBarnacleVictimReleased; #endif //HL2_DLL +#ifdef MAPBASE +extern acttable_t *GetSMG1Acttable(); +extern int GetSMG1ActtableCount(); +#endif + extern ConVar weapon_showproficiency; ConVar ai_show_hull_attacks( "ai_show_hull_attacks", "0" ); @@ -68,12 +73,17 @@ ConVar ai_force_serverside_ragdoll( "ai_force_serverside_ragdoll", "0" ); ConVar nb_last_area_update_tolerance( "nb_last_area_update_tolerance", "4.0", FCVAR_CHEAT, "Distance a character needs to travel in order to invalidate cached area" ); // 4.0 tested as sweet spot (for wanderers, at least). More resulted in little benefit, less quickly diminished benefit [7/31/2008 tom] +#ifdef MAPBASE +// ShouldUseVisibilityCache() is used as an actual function now +ConVar ai_use_visibility_cache( "ai_use_visibility_cache", "1" ); +#else #ifndef _RETAIL ConVar ai_use_visibility_cache( "ai_use_visibility_cache", "1" ); #define ShouldUseVisibilityCache() ai_use_visibility_cache.GetBool() #else #define ShouldUseVisibilityCache() true #endif +#endif BEGIN_DATADESC( CBaseCombatCharacter ) @@ -97,19 +107,94 @@ BEGIN_DATADESC( CBaseCombatCharacter ) DEFINE_FIELD( m_flDamageAccumulator, FIELD_FLOAT ), DEFINE_INPUT( m_impactEnergyScale, FIELD_FLOAT, "physdamagescale" ), DEFINE_FIELD( m_CurrentWeaponProficiency, FIELD_INTEGER), +#ifdef MAPBASE + DEFINE_INPUT( m_ProficiencyOverride, FIELD_INTEGER, "SetProficiencyOverride"), +#endif DEFINE_UTLVECTOR( m_Relationship, FIELD_EMBEDDED), DEFINE_AUTO_ARRAY( m_iAmmo, FIELD_INTEGER ), DEFINE_AUTO_ARRAY( m_hMyWeapons, FIELD_EHANDLE ), DEFINE_FIELD( m_hActiveWeapon, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_INPUT( m_bForceServerRagdoll, FIELD_BOOLEAN, "SetForceServerRagdoll" ), +#else DEFINE_FIELD( m_bForceServerRagdoll, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_bPreventWeaponPickup, FIELD_BOOLEAN ), +#ifndef MAPBASE // See CBaseEntity::InputKilledNPC() DEFINE_INPUTFUNC( FIELD_VOID, "KilledNPC", InputKilledNPC ), +#endif + +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetBloodColor", InputSetBloodColor ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetRelationship", InputSetRelationship ), + + DEFINE_INPUTFUNC( FIELD_VOID, "HolsterWeapon", InputHolsterWeapon ), + DEFINE_INPUTFUNC( FIELD_VOID, "HolsterAndDestroyWeapon", InputHolsterAndDestroyWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "UnholsterWeapon", InputUnholsterWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "SwitchToWeapon", InputSwitchToWeapon ), + + DEFINE_INPUTFUNC( FIELD_STRING, "GiveWeapon", InputGiveWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "DropWeapon", InputDropWeapon ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "PickupWeaponInstant", InputPickupWeaponInstant ), + DEFINE_OUTPUT( m_OnWeaponEquip, "OnWeaponEquip" ), + DEFINE_OUTPUT( m_OnWeaponDrop, "OnWeaponDrop" ), + + DEFINE_OUTPUT( m_OnKilledEnemy, "OnKilledEnemy" ), + DEFINE_OUTPUT( m_OnKilledPlayer, "OnKilledPlayer" ), + DEFINE_OUTPUT( m_OnHealthChanged, "OnHealthChanged" ), +#endif END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by players and NPCs." ) + + DEFINE_SCRIPTFUNC_NAMED( GetScriptActiveWeapon, "GetActiveWeapon", "Get the character's active weapon entity." ) + DEFINE_SCRIPTFUNC( WeaponCount, "Get the number of weapons a character possesses." ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptWeaponIndex, "GetWeapon", "Get a specific weapon in the character's inventory." ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptWeaponByType, "FindWeapon", "Find a specific weapon in the character's inventory by its classname." ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptAllWeapons, "GetAllWeapons", "Get the character's weapon inventory." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetCurrentWeaponProficiency, "GetCurrentWeaponProficiency", "Get the character's current proficiency (accuracy) with their current weapon." ) + + DEFINE_SCRIPTFUNC_NAMED( Weapon_ShootPosition, "ShootPosition", "Get the character's shoot position." ) + DEFINE_SCRIPTFUNC_NAMED( Weapon_DropAll, "DropAllWeapons", "Make the character drop all of its weapons." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEquipWeapon, "EquipWeapon", "Make the character equip the specified weapon entity. If they don't already own the weapon, they will acquire it instantly." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptDropWeapon, "DropWeapon", "Make the character drop the specified weapon entity if they own it." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAmmoCount, "GetAmmoCount", "Get the ammo count of the specified ammo type." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAmmoCount, "SetAmmoCount", "Set the ammo count of the specified ammo type." ) + + DEFINE_SCRIPTFUNC( DoMuzzleFlash, "Does a muzzle flash." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttackSpread, "GetAttackSpread", "Get the attack spread." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSpreadBias, "GetSpreadBias", "Get the spread bias." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptRelationType, "GetRelationship", "Get a character's relationship to a specific entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRelationPriority, "GetRelationPriority", "Get a character's relationship priority for a specific entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetRelationship, "SetRelationship", "Set a character's relationship with a specific entity." ) + + DEFINE_SCRIPTFUNC_NAMED( GetScriptVehicleEntity, "GetVehicleEntity", "Get the entity for a character's current vehicle if they're in one." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptInViewCone, "InViewCone", "Check if the specified position is in the character's viewcone." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEntInViewCone, "EntInViewCone", "Check if the specified entity is in the character's viewcone." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptInAimCone, "InAimCone", "Check if the specified position is in the character's aim cone." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEntInViewCone, "EntInAimCone", "Check if the specified entity is in the character's aim cone." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptBodyAngles, "BodyAngles", "Get the body's angles." ) + DEFINE_SCRIPTFUNC( BodyDirection2D, "Get the body's 2D direction." ) + DEFINE_SCRIPTFUNC( BodyDirection3D, "Get the body's 3D direction." ) + DEFINE_SCRIPTFUNC( HeadDirection2D, "Get the head's 2D direction." ) + DEFINE_SCRIPTFUNC( HeadDirection3D, "Get the head's 3D direction." ) + DEFINE_SCRIPTFUNC( EyeDirection2D, "Get the eyes' 2D direction." ) + DEFINE_SCRIPTFUNC( EyeDirection3D, "Get the eyes' 3D direction." ) + +END_SCRIPTDESC(); +#endif + BEGIN_SIMPLE_DATADESC( Relationship_t ) DEFINE_FIELD( entity, FIELD_EHANDLE ), @@ -225,6 +310,21 @@ int CBaseCombatCharacter::GetInteractionID(void) return (m_lastInteraction); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: New method of adding interactions which allows their name to be available (currently used for VScript) +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::AddInteractionWithString( int &interaction, const char *szName ) +{ + interaction = GetInteractionID(); + + if (g_pScriptVM) + { + ScriptRegisterConstantNamed( g_pScriptVM, interaction, szName, "An interaction which could be used with HandleInteraction or DispatchInteraction. NOTE: These are usually only initialized by certain types of NPCs when an instance of one spawns in the level for the first time!!! (the fact you're seeing this one means there was an NPC in the level which initialized it)" ); + } +} +#endif + // ============================================================================ bool CBaseCombatCharacter::HasHumanGibs( void ) { @@ -331,11 +431,16 @@ bool CBaseCombatCharacter::FVisible( CBaseEntity *pEntity, int traceMask, CBaseE { VPROF( "CBaseCombatCharacter::FVisible" ); +#ifdef MAPBASE + if ( traceMask != MASK_BLOCKLOS || !ShouldUseVisibilityCache( pEntity ) || pEntity == this || !ai_use_visibility_cache.GetBool() + ) +#else if ( traceMask != MASK_BLOCKLOS || !ShouldUseVisibilityCache() || pEntity == this #if defined(HL2_DLL) || Classify() == CLASS_BULLSEYE || pEntity->Classify() == CLASS_BULLSEYE #endif ) +#endif { return BaseClass::FVisible( pEntity, traceMask, ppBlocker ); } @@ -441,6 +546,17 @@ void CBaseCombatCharacter::ResetVisibilityCache( CBaseCombatCharacter *pBCC ) } } +#ifdef MAPBASE +bool CBaseCombatCharacter::ShouldUseVisibilityCache( CBaseEntity *pEntity ) +{ +#ifdef HL2_DLL + return Classify() != CLASS_BULLSEYE && pEntity->Classify() != CLASS_BULLSEYE; +#else + return true; +#endif +} +#endif + #ifdef PORTAL bool CBaseCombatCharacter::FVisibleThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) { @@ -1162,7 +1278,11 @@ bool CTraceFilterMelee::ShouldHitEntity( IHandleEntity *pHandleEntity, int conte if ( pBCC && pVictimBCC ) { // Can only damage other NPCs that we hate +#ifdef MAPBASE + if ( m_bDamageAnyNPC || pBCC->IRelationType( pEntity ) <= D_FR ) +#else if ( m_bDamageAnyNPC || pBCC->IRelationType( pEntity ) == D_HT ) +#endif { if ( info.GetDamage() ) { @@ -1485,6 +1605,30 @@ bool CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, const Vect return true; } +#ifdef MAPBASE +CBaseEntity *CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags, const Vector *vecColor ) +{ + Assert( CanBecomeRagdoll() ); + + CTakeDamageInfo info( pKiller, pKiller, 1.0f, DMG_GENERIC ); + + info.SetDamageForce( forceVector ); + + CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + + pRagdoll->SetCollisionBounds( CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs() ); + + CBaseEntity *pBoogie = CRagdollBoogie::Create( pRagdoll, 200, gpGlobals->curtime, duration, flags, vecColor ); + + CTakeDamageInfo ragdollInfo( pKiller, pKiller, 10000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL ); + ragdollInfo.SetDamagePosition( WorldSpaceCenter() ); + ragdollInfo.SetDamageForce( Vector( 0, 0, 1 ) ); + TakeDamage( ragdollInfo ); + + return pBoogie; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1581,6 +1725,25 @@ Killed */ void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_OnDeath.CanRunInScope( m_ScriptScope )) + { + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + // info + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( hInfo ) }; + if ( g_Hook_OnDeath.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_BOOLEAN && functionReturn.m_bool == false) ) + { + // Make this entity cheat death + g_pScriptVM->RemoveInstance( hInfo ); + return; + } + + g_pScriptVM->RemoveInstance( hInfo ); + } +#endif + extern ConVar npc_vphysics; // Advance life state to dying @@ -1593,7 +1756,23 @@ void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info ) CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this ); if( pMagnet ) { +#ifdef MAPBASE + if (pMagnet->BoneTarget() && pMagnet->BoneTarget()[0] != '\0') + { + int iBone = -1; + forceVector += pMagnet->GetForceVector( this, &iBone ); + if (iBone != -1) + m_nForceBone = GetPhysicsBone(iBone); + } + else + { + forceVector += pMagnet->GetForceVector( this ); + } + + pMagnet->m_OnUsed.Set(forceVector, this, pMagnet); +#else forceVector += pMagnet->GetForceVector( this ); +#endif } CBaseCombatWeapon *pDroppedWeapon = m_hActiveWeapon.Get(); @@ -1646,7 +1825,11 @@ void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info ) } } #ifdef HL2_DLL +#ifdef MAPBASE + else if ( PlayerHasMegaPhysCannon() && GlobalEntity_GetCounter("super_phys_gun") != 1 ) +#else else if ( PlayerHasMegaPhysCannon() ) +#endif { if ( pDroppedWeapon ) { @@ -1906,7 +2089,11 @@ void CBaseCombatCharacter::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector return; // If I'm an NPC, fill the weapon with ammo before I drop it. +#ifdef MAPBASE + if ( GetFlags() & FL_NPC && !pWeapon->HasSpawnFlags(SF_WEAPON_PRESERVE_AMMO) ) +#else if ( GetFlags() & FL_NPC ) +#endif { if ( pWeapon->UsesClipsForAmmo1() ) { @@ -2042,6 +2229,10 @@ void CBaseCombatCharacter::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector pWeapon->Drop( vecThrow ); Weapon_Detach( pWeapon ); +#ifdef MAPBASE + m_OnWeaponDrop.FireOutput(pWeapon, this); +#endif + if ( HasSpawnFlags( SF_NPC_NO_WEAPON_DROP ) ) { // Don't drop weapons when the super physgun is happening. @@ -2064,6 +2255,227 @@ void CBaseCombatCharacter::SetLightingOriginRelative( CBaseEntity *pLightingOrig } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Gives character new weapon and equips it +// Input : New weapon +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::Weapon_Equip( CBaseCombatWeapon *pWeapon ) +{ + Weapon_HandleEquip(pWeapon); + + // Players don't automatically holster their current weapon + if ( IsPlayer() == false ) + { + if ( m_hActiveWeapon ) + { + m_hActiveWeapon->Holster(); + // FIXME: isn't this handeled by the weapon? + m_hActiveWeapon->AddEffects( EF_NODRAW ); + } + SetActiveWeapon( pWeapon ); + m_hActiveWeapon->RemoveEffects( EF_NODRAW ); + + } + + WeaponProficiency_t proficiency; + proficiency = CalcWeaponProficiency( pWeapon ); + + if( weapon_showproficiency.GetBool() != 0 ) + { + Msg("%s equipped with %s, proficiency is %s\n", GetClassname(), pWeapon->GetClassname(), GetWeaponProficiencyName( proficiency ) ); + } + + SetCurrentWeaponProficiency( proficiency ); +} + +//----------------------------------------------------------------------------- +// Purpose: Puts a new weapon in the inventory +// Input : New weapon +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::Weapon_EquipHolstered( CBaseCombatWeapon *pWeapon ) +{ + Weapon_HandleEquip(pWeapon); + pWeapon->AddEffects( EF_NODRAW ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds new weapon to the character +// Input : New weapon +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ) +{ + // Add the weapon to my weapon inventory + if (IsPlayer()) + { + // This code drops existing weapons that are in the same bucket and bucket position. + // This doesn't really harm anything since that situation would've broken the HUD anyway. + // + // It goes through every single index in case there's a NULL pointer in between weapons. + int iFirstNullIndex = -1; + for (int i=0;iGetSlot() == m_hMyWeapons[i]->GetSlot() && + pWeapon->GetPosition() == m_hMyWeapons[i]->GetPosition()) + { + // Replace our existing weapon in this slot + Weapon_Drop(m_hMyWeapons[i]); + { + // We found a slot, we don't care about the first null index anymore + iFirstNullIndex = -1; + + m_hMyWeapons.Set( i, pWeapon ); + break; + } + } + } + } + + if (iFirstNullIndex != -1) + m_hMyWeapons.Set( iFirstNullIndex, pWeapon ); + } + else + { + for (int i=0;iChangeTeam( GetTeamNumber() ); + + bool bPreserveAmmo = pWeapon->HasSpawnFlags(SF_WEAPON_PRESERVE_AMMO); + if (!bPreserveAmmo) + { + // ---------------------- + // Give Primary Ammo + // ---------------------- + // If gun doesn't use clips, just give ammo + if (pWeapon->GetMaxClip1() == -1) + { +#ifdef HL2_DLL + if( FStrEq(STRING(gpGlobals->mapname), "d3_c17_09") && FClassnameIs(pWeapon, "weapon_rpg") && pWeapon->NameMatches("player_spawn_items") ) + { + // !!!HACK - Don't give any ammo with the spawn equipment RPG in d3_c17_09. This is a chapter + // start and the map is way to easy if you start with 3 RPG rounds. It's fine if a player conserves + // them and uses them here, but it's not OK to start with enough ammo to bypass the snipers completely. + GiveAmmo( 0, pWeapon->m_iPrimaryAmmoType); + } + else +#endif // HL2_DLL + GiveAmmo(pWeapon->GetDefaultClip1(), pWeapon->m_iPrimaryAmmoType); + } + // If default ammo given is greater than clip + // size, fill clips and give extra ammo + else if ( pWeapon->GetDefaultClip1() > pWeapon->GetMaxClip1() ) + { + pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); + GiveAmmo( (pWeapon->GetDefaultClip1() - pWeapon->GetMaxClip1()), pWeapon->m_iPrimaryAmmoType); + } + + // ---------------------- + // Give Secondary Ammo + // ---------------------- + // If gun doesn't use clips, just give ammo + if (pWeapon->GetMaxClip2() == -1) + { + GiveAmmo(pWeapon->GetDefaultClip2(), pWeapon->m_iSecondaryAmmoType); + } + // If default ammo given is greater than clip + // size, fill clips and give extra ammo + else if ( pWeapon->GetDefaultClip2() > pWeapon->GetMaxClip2() ) + { + pWeapon->m_iClip2 = pWeapon->GetMaxClip2(); + GiveAmmo( (pWeapon->GetDefaultClip2() - pWeapon->GetMaxClip2()), pWeapon->m_iSecondaryAmmoType); + } + } + else //if (IsPlayer()) + { + if (pWeapon->UsesClipsForAmmo1()) + { + if (pWeapon->m_iClip1 > pWeapon->GetMaxClip1()) + { + // Handle excess ammo + GiveAmmo( pWeapon->m_iClip1 - pWeapon->GetMaxClip1(), pWeapon->m_iPrimaryAmmoType ); + pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); + } + } + else if (pWeapon->m_iClip1 > 0) + { + // Just because the weapon can't use clips doesn't mean + // the mapper can't override their clip value for ammo. + GiveAmmo(pWeapon->m_iClip1, pWeapon->m_iPrimaryAmmoType); + pWeapon->m_iClip1 = WEAPON_NOCLIP; + } + + if (pWeapon->UsesClipsForAmmo2()) + { + if (pWeapon->m_iClip2 > pWeapon->GetMaxClip2()) + { + // Handle excess ammo + GiveAmmo(pWeapon->m_iClip2 - pWeapon->GetMaxClip2(), pWeapon->m_iSecondaryAmmoType); + pWeapon->m_iClip2 = pWeapon->GetMaxClip2(); + } + } + else if (pWeapon->m_iClip2 > 0) + { + // Just because the weapon can't use clips doesn't mean + // the mapper can't override their clip value for ammo. + GiveAmmo(pWeapon->m_iClip2, pWeapon->m_iSecondaryAmmoType); + pWeapon->m_iClip2 = WEAPON_NOCLIP; + } + } + + pWeapon->Equip( this ); + + // Gotta do this *after* Equip because it may whack maxRange + if ( IsPlayer() == false ) + { + // If SF_NPC_LONG_RANGE spawn flags is set let weapon work from any distance + if ( HasSpawnFlags(SF_NPC_LONG_RANGE) ) + { + pWeapon->m_fMaxRange1 = 999999999; + pWeapon->m_fMaxRange2 = 999999999; + } + } + else if (bPreserveAmmo) + { + // The clip doesn't update on the client unless we do this. + // This is the only way I've figured out how to update without doing something worse. + // TODO: Remove this hack, we've finally fixed it + /* + variant_t clip1; + clip1.SetInt(pWeapon->m_iClip1); + variant_t clip2; + clip2.SetInt(pWeapon->m_iClip2); + + pWeapon->m_iClip1 = pWeapon->m_iClip1 - 1; + pWeapon->m_iClip2 = pWeapon->m_iClip2 - 1; + + g_EventQueue.AddEvent(pWeapon, "SetAmmo1", clip1, 0.0001f, this, this, 0); + g_EventQueue.AddEvent(pWeapon, "SetAmmo2", clip2, 0.0001f, this, this, 0); + */ + } + + // Pass the lighting origin over to the weapon if we have one + pWeapon->SetLightingOriginRelative( GetLightingOriginRelative() ); + + //if (m_aliveTimer.IsLessThen(0.01f)) + m_OnWeaponEquip.FireOutput(pWeapon, this); +} +#else //----------------------------------------------------------------------------- // Purpose: Add new weapon to the character // Input : New weapon @@ -2165,6 +2577,7 @@ void CBaseCombatCharacter::Weapon_Equip( CBaseCombatWeapon *pWeapon ) // Pass the lighting origin over to the weapon if we have one pWeapon->SetLightingOriginRelative( GetLightingOriginRelative() ); } +#endif //----------------------------------------------------------------------------- // Purpose: Leaves weapon, giving only ammo to the character @@ -2298,6 +2711,12 @@ bool CBaseCombatCharacter::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) if ( SelectWeightedSequence(translatedActivity) == ACTIVITY_NOT_AVAILABLE ) { +#ifdef MAPBASE + // Do we have a backup? + translatedActivity = Weapon_BackupActivity((Activity)(pTable->baseAct), true, pWeapon); + if (SelectWeightedSequence(translatedActivity) != ACTIVITY_NOT_AVAILABLE) + return true; +#endif return false; } } @@ -2306,6 +2725,48 @@ bool CBaseCombatCharacter::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) return true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Uses an activity from a different weapon when the activity we were originally looking for does not exist on this character. +// Created to give NPCs the ability to use weapons they are not otherwise allowed to use. +// Right now, everyone falls back to the SMG act table. +//----------------------------------------------------------------------------- +Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon ) +{ + CBaseCombatWeapon *pWeapon = pSpecificWeapon ? pSpecificWeapon : GetActiveWeapon(); + if (!pWeapon) + return activity; + + // Make sure the weapon allows this activity to have a backup. + if (!pWeapon->SupportsBackupActivity(activity)) + return activity; + + // Sometimes, the NPC is supposed to use the default activity. Return that if the weapon translation was "not required" and we have an original activity. + if (!weaponTranslationWasRequired && GetModelPtr()->HaveSequenceForActivity(activity)) + { + return activity; + } + + acttable_t *pTable = GetSMG1Acttable(); + int actCount = GetSMG1ActtableCount(); + for ( int i = 0; i < actCount; i++, pTable++ ) + { + if ( activity == pTable->baseAct ) + { + // Don't pick SMG animations we don't actually have an animation for. + if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false) + { + return activity; + } + + return (Activity)pTable->weaponAct; + } + } + + return activity; +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : @@ -2352,6 +2813,11 @@ int CBaseCombatCharacter::TakeHealth (float flHealth, int bitsDamageType) { if (!m_takedamage) return 0; + +#ifdef MAPBASE + float flRatio = clamp( (float)m_iHealth / (float)m_iMaxHealth, 0.f, 1.f ); + m_OnHealthChanged.Set(flRatio, NULL, this); +#endif return BaseClass::TakeHealth(flHealth, bitsDamageType); } @@ -2418,6 +2884,13 @@ int CBaseCombatCharacter::OnTakeDamage( const CTakeDamageInfo &info ) { case LIFE_ALIVE: retVal = OnTakeDamage_Alive( info ); +#ifdef MAPBASE + if (retVal) + { + float flRatio = clamp( (float)m_iHealth / (float)m_iMaxHealth, 0.f, 1.f ); + m_OnHealthChanged.Set(flRatio, NULL, this); + } +#endif if ( m_iHealth <= 0 ) { IPhysicsObject *pPhysics = VPhysicsGetObject(); @@ -2445,11 +2918,28 @@ int CBaseCombatCharacter::OnTakeDamage( const CTakeDamageInfo &info ) break; case LIFE_DYING: +#ifdef MAPBASE + retVal = OnTakeDamage_Dying( info ); + if (retVal) + { + float flRatio = clamp( (float)m_iHealth / (float)m_iMaxHealth, 0.f, 1.f ); + m_OnHealthChanged.Set(flRatio, NULL, this); + } + return retVal; +#else return OnTakeDamage_Dying( info ); +#endif default: case LIFE_DEAD: retVal = OnTakeDamage_Dead( info ); +#ifdef MAPBASE + if (retVal) + { + float flRatio = clamp( (float)m_iHealth / (float)m_iMaxHealth, 0.f, 1.f ); + m_OnHealthChanged.Set(flRatio, NULL, this); + } +#endif if ( m_iHealth <= 0 && g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && ShouldGib( info ) ) { Event_Gibbed( info ); @@ -2609,6 +3099,29 @@ void CBaseCombatCharacter::AddClassRelationship ( Class_T class_type, Dispositio m_Relationship[index].priority = ( priority != DEF_RELATIONSHIP_PRIORITY ) ? priority : 0; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Removes a class relationship from our list +// Input : *class_type - Class with whom the relationship should be ended +// Output : True is relation was removed, false if it was not found +//----------------------------------------------------------------------------- +bool CBaseCombatCharacter::RemoveClassRelationship( Class_T class_type ) +{ + // Find the relationship in our list, if it exists + for ( int i = m_Relationship.Count()-1; i >= 0; i-- ) + { + if ( m_Relationship[i].classType == class_type ) + { + // Done, remove it + m_Relationship.Remove( i ); + return true; + } + } + + return false; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Add or Change a entity relationship for this entity // Input : @@ -2690,6 +3203,44 @@ void CBaseCombatCharacter::SetDefaultRelationship(Class_T nClass, Class_T nClass } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Fetch the default (ignore ai_relationship changes) relationship +// Input : +// Output : +//----------------------------------------------------------------------------- +Disposition_t CBaseCombatCharacter::GetDefaultRelationshipDisposition( Class_T nClassSource, Class_T nClassTarget ) +{ + Assert( m_DefaultRelationship != NULL ); + + return m_DefaultRelationship[nClassSource][nClassTarget].disposition; +} + +//----------------------------------------------------------------------------- +// Purpose: Fetch the default (ignore ai_relationship changes) priority +// Input : +// Output : +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::GetDefaultRelationshipPriority( Class_T nClassSource, Class_T nClassTarget ) +{ + Assert( m_DefaultRelationship != NULL ); + + return m_DefaultRelationship[nClassSource][nClassTarget].priority; +} + +//----------------------------------------------------------------------------- +// Purpose: Fetch the default (ignore ai_relationship changes) priority +// Input : +// Output : +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::GetDefaultRelationshipPriority( Class_T nClassTarget ) +{ + Assert( m_DefaultRelationship != NULL ); + + return m_DefaultRelationship[Classify()][nClassTarget].priority; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Fetch the default (ignore ai_relationship changes) relationship // Input : @@ -2761,6 +3312,131 @@ int CBaseCombatCharacter::IRelationPriority( CBaseEntity *pTarget ) return 0; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Ported from CAI_BaseNPC so players can use it +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::AddRelationship( const char *pszRelationship, CBaseEntity *pActivator ) +{ + // Parse the keyvalue data + char parseString[1000]; + Q_strncpy(parseString, pszRelationship, sizeof(parseString)); + + // Look for an entity string + char *entityString = strtok(parseString," "); + while (entityString) + { + // Get the disposition + char *dispositionString = strtok(NULL," "); + Disposition_t disposition = D_NU; + if ( dispositionString ) + { + if (!stricmp(dispositionString,"D_HT")) + { + disposition = D_HT; + } + else if (!stricmp(dispositionString,"D_FR")) + { + disposition = D_FR; + } + else if (!stricmp(dispositionString,"D_LI")) + { + disposition = D_LI; + } + else if (!stricmp(dispositionString,"D_NU")) + { + disposition = D_NU; + } + else + { + disposition = D_NU; + Warning( "***ERROR***\nBad relationship type (%s) to unknown entity (%s)!\n", dispositionString,entityString ); + Assert( 0 ); + return; + } + } + else + { + Warning("Can't parse relationship info (%s) - Expecting 'name [D_HT, D_FR, D_LI, D_NU] [1-99]'\n", pszRelationship ); + Assert(0); + return; + } + + // Get the priority + char *priorityString = strtok(NULL," "); + int priority = ( priorityString ) ? atoi(priorityString) : DEF_RELATIONSHIP_PRIORITY; + + bool bFoundEntity = false; + + // Try to get pointer to an entity of this name + CBaseEntity *entity = gEntList.FindEntityByName( NULL, entityString ); + while( entity ) + { + // make sure you catch all entities of this name. + bFoundEntity = true; + AddEntityRelationship(entity, disposition, priority ); + entity = gEntList.FindEntityByName( entity, entityString ); + } + + if( !bFoundEntity ) + { + // Need special condition for player as we can only have one + if (!stricmp("player", entityString) || !stricmp("!player", entityString)) + { + AddClassRelationship( CLASS_PLAYER, disposition, priority ); + } + // Otherwise try to create one too see if a valid classname and get class type + else + { + // HACKHACK: + CBaseEntity *pEntity = CanCreateEntityClass( entityString ) ? CreateEntityByName( entityString ) : NULL; + if (pEntity) + { + AddClassRelationship( pEntity->Classify(), disposition, priority ); + UTIL_RemoveImmediate(pEntity); + } + else + { +#ifdef MAPBASE // I know the extra #ifdef is pointless, but it's there so you know this is new + if (!Q_strnicmp(entityString, "CLASS_", 5)) + { + // Go through all of the classes and find which one this is + Class_T resultClass = CLASS_NONE; + for (int i = 0; i < NUM_AI_CLASSES; i++) + { + if (FStrEq(g_pGameRules->AIClassText(i), entityString)) + { + resultClass = (Class_T)i; + } + } + + if (resultClass != CLASS_NONE) + { + AddClassRelationship( resultClass, disposition, priority ); + bFoundEntity = true; + } + } + + if (!bFoundEntity) +#endif + DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString ); + } + } + } + // Check for another entity in the list + entityString = strtok(NULL," "); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputSetRelationship( inputdata_t &inputdata ) +{ + AddRelationship( inputdata.value.String(), inputdata.pActivator ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Get shoot position of BCC at current position/orientation // Input : @@ -2793,6 +3469,11 @@ CBaseEntity *CBaseCombatCharacter::FindHealthItem( const Vector &vecPosition, co if( pItem ) { +#ifdef MAPBASE + if (pItem->HasSpawnFlags(SF_ITEM_NO_NPC_PICKUP)) + continue; +#endif + // Healthkits and healthvials if( pItem->ClassMatches( "item_health*" ) && FVisible( pItem ) ) { @@ -2837,6 +3518,18 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range ) { bool bConservative = false; +#ifdef MAPBASE + if (HasContext("weapon_conservative:1")) + bConservative = true; +#ifdef HL2_DLL + else if (hl2_episodic.GetBool() && !GetActiveWeapon()) + { + // Unarmed citizens are conservative in their weapon finding...in Episode One + if (Classify() != CLASS_PLAYER_ALLY_VITAL && Q_strncmp(STRING(gpGlobals->mapname), "ep1_", 4)) + bConservative = true; + } +#endif +#else #ifdef HL2_DLL if( hl2_episodic.GetBool() && !GetActiveWeapon() ) { @@ -2846,6 +3539,7 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range ) bConservative = true; } } +#endif #endif CBaseCombatWeapon *weaponList[64]; @@ -2869,26 +3563,56 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range ) if ( pWeapon->CanBePickedUpByNPCs() == false ) continue; +#ifdef MAPBASE + if ( pWeapon->HasSpawnFlags(SF_WEAPON_NO_NPC_PICKUP) ) + continue; +#endif + if ( velocity.LengthSqr() > 1 || !Weapon_CanUse(pWeapon) ) continue; if ( pWeapon->IsLocked(this) ) continue; +#ifdef MAPBASE + // Skip weapons we already own + if ( Weapon_OwnsThisType(pWeapon->GetClassname()) ) + continue; +#endif + if ( GetActiveWeapon() ) { // Already armed. Would picking up this weapon improve my situation? +#ifndef MAPBASE if( GetActiveWeapon()->m_iClassname == pWeapon->m_iClassname ) { // No, I'm already using this type of weapon. continue; } +#endif +#ifdef MAPBASE + if ( pWeapon->IsMeleeWeapon() && !GetActiveWeapon()->IsMeleeWeapon() ) + { + // This weapon is a melee weapon and the weapon I have now is not. + // Picking up this weapon might not improve my situation. + continue; + } + + if ( pWeapon->GetWeight() != 0 && GetActiveWeapon()->GetWeight() > pWeapon->GetWeight() ) + { + // Discard if our target weapon supports weight but our current weapon has more of it. + // + // (RIP going from AR2 to shotgun) + continue; + } +#else if( FClassnameIs( pWeapon, "weapon_pistol" ) ) { // No, it's a pistol. continue; } +#endif } float fCurDist = (pWeapon->GetLocalOrigin() - GetLocalOrigin()).Length(); @@ -2901,6 +3625,23 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range ) if ( pBestWeapon ) { +#ifdef MAPBASE + // NPCs now use weight to determine which weapon is best. + // All HL2 weapons are weighted and are usually good enough. + // + // This probably won't cause problems... + if (pWeapon->GetWeight() > 1) + { +#if 0 + float flRatio = MIN( (2.5f / (pWeapon->GetWeight() - (GetActiveWeapon() ? GetActiveWeapon()->GetWeight() : 0))), 1.0 ); + if (flRatio < 0) + flRatio *= -1; flRatio += 1.0f; + fCurDist *= flRatio; +#else + fCurDist *= MIN( (2.5f / pWeapon->GetWeight()), 1.0 ); +#endif + } +#else // UNDONE: Better heuristic needed here // Need to pick by power of weapons // Don't want to pick a weapon right next to a NPC! @@ -2910,6 +3651,7 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range ) { fCurDist *= 0.5; } +#endif // choose the last range attack weapon you find or the first available other weapon if ( ! (pWeapon->CapabilitiesGet() & bits_CAP_RANGE_ATTACK_GROUP) ) @@ -3324,6 +4066,308 @@ void CBaseCombatCharacter::InputKilledNPC( inputdata_t &inputdata ) OnKilledNPC( inputdata.pActivator ? inputdata.pActivator->MyCombatCharacterPointer() : NULL ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Handle enemy kills. This actually measures players too. +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::OnKilledNPC( CBaseCombatCharacter *pKilled ) +{ + // I know this can sometimes pass as NULL, but that can work here...right? + m_OnKilledEnemy.Set(pKilled, pKilled, this); + + // Fire an additional output if this was the player + if (pKilled && pKilled->IsPlayer()) + m_OnKilledPlayer.Set(pKilled, pKilled, this); +} + +//------------------------------------------------------------------------------ +// Purpose: Give the NPC in question the weapon specified +//------------------------------------------------------------------------------ +void CBaseCombatCharacter::InputGiveWeapon( inputdata_t &inputdata ) +{ + // Give the NPC the specified weapon + string_t iszWeaponName = inputdata.value.StringID(); + if ( iszWeaponName != NULL_STRING ) + { + if (IsNPC()) + { + if( Classify() == CLASS_PLAYER_ALLY_VITAL ) + { + MyNPCPointer()->m_iszPendingWeapon = iszWeaponName; + } + else + { + MyNPCPointer()->GiveWeapon( iszWeaponName ); + } + } + else + { + CBaseCombatWeapon *pWeapon = Weapon_Create(STRING(iszWeaponName)); + if (pWeapon) + { + Weapon_Equip(pWeapon); + } + else + { + Warning( "Couldn't create weapon %s to give %s.\n", STRING(iszWeaponName), GetDebugName() ); + return; + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputDropWeapon( inputdata_t &inputdata ) +{ + CBaseCombatWeapon *pWeapon = FStrEq(inputdata.value.String(), "") ? GetActiveWeapon() : Weapon_OwnsThisType(inputdata.value.String()); + if (pWeapon) + { + Weapon_Drop(pWeapon); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputPickupWeaponInstant( inputdata_t &inputdata ) +{ + if (inputdata.value.Entity() && inputdata.value.Entity()->IsBaseCombatWeapon()) + { + CBaseCombatWeapon *pWeapon = inputdata.value.Entity()->MyCombatWeaponPointer(); + if (pWeapon->GetOwner()) + { + Msg("Ignoring PickupWeaponInstant on %s because %s already has an owner\n", GetDebugName(), pWeapon->GetDebugName()); + return; + } + + if (CBaseCombatWeapon *pExistingWeapon = Weapon_OwnsThisType(pWeapon->GetClassname())) + { + // Drop our existing weapon then! + Weapon_Drop(pExistingWeapon); + } + + if (IsNPC()) + { + Weapon_Equip(pWeapon); + MyNPCPointer()->OnGivenWeapon(pWeapon); + } + else + { + Weapon_Equip(pWeapon); + } + + pWeapon->OnPickedUp( this ); + } + else + { + Warning("%s received PickupWeaponInstant with invalid entity %s\n", GetDebugName(), inputdata.value.Entity() ? "null" : inputdata.value.Entity()->GetDebugName()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputHolsterWeapon( inputdata_t &inputdata ) +{ + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if (pWeapon) + { + pWeapon->Holster(); + //SetActiveWeapon( NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputHolsterAndDestroyWeapon( inputdata_t &inputdata ) +{ + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if (pWeapon) + { + pWeapon->Holster(); + SetActiveWeapon( NULL ); + + if (pWeapon->GetActivity() == ACT_VM_HOLSTER) + { + // Remove when holster is finished + pWeapon->ThinkSet( &CBaseEntity::SUB_Remove, gpGlobals->curtime + pWeapon->GetViewModelSequenceDuration() ); + } + else + { + // Remove now + UTIL_Remove( pWeapon ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputUnholsterWeapon( inputdata_t &inputdata ) +{ + // NPCs can handle strings, but players fall back to SwitchToWeapon + if (inputdata.value.StringID() != NULL_STRING) + InputSwitchToWeapon( inputdata ); + + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if (pWeapon && pWeapon->IsEffectActive(EF_NODRAW)) + { + pWeapon->Deploy(); + } +} + +//------------------------------------------------------------------------------ +// Purpose: Makes the NPC instantly switch to the specified weapon, creates it if it doesn't exist +//------------------------------------------------------------------------------ +void CBaseCombatCharacter::InputSwitchToWeapon( inputdata_t &inputdata ) +{ + for (int i = 0; im_iClassname == inputdata.value.StringID()) + { + Weapon_Switch( m_hMyWeapons[i] ); + return; + } + } + + // We must not have it + if (IsNPC()) + MyNPCPointer()->GiveWeapon( inputdata.value.StringID(), false ); + else + { + CBaseCombatWeapon *pWeapon = Weapon_Create( inputdata.value.String() ); + if (pWeapon) + { + Weapon_Equip( pWeapon ); + } + else + { + Warning( "Couldn't create weapon %s to give %s.\n", inputdata.value.String(), GetDebugName() ); + } + } +} + +#define FINDNAMEDENTITY_MAX_ENTITIES 32 +//----------------------------------------------------------------------------- +// Purpose: FindNamedEntity has been moved from CAI_BaseNPC to CBaseCombatCharacter so players can use it. +// Coincidentally, everything that it did on NPCs could be done on BaseCombatCharacters with no consequences. +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *CBaseCombatCharacter::FindNamedEntity( const char *szName, IEntityFindFilter *pFilter ) +{ + const char *name = szName; + if (name[0] == '!') + name++; + + if ( !stricmp( name, "player" )) + { + return AI_GetSinglePlayer(); + } + else if ( !stricmp( name, "enemy" ) ) + { + return GetEnemy(); + } + else if ( !stricmp( name, "self" ) || !stricmp( name, "target1" ) ) + { + return this; + } + else if ( !stricmp( name, "nearestfriend" ) || !strnicmp( name, "friend", 6 ) ) + { + // Just look for the nearest friendly NPC within 500 units + // (most of this was stolen from CAI_PlayerAlly::FindSpeechTarget()) + const Vector & vAbsOrigin = GetAbsOrigin(); + float closestDistSq = Square(500.0); + CBaseEntity * pNearest = NULL; + float distSq; + int i; + for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + CAI_BaseNPC *pNPC = (g_AI_Manager.AccessAIs())[i]; + + if ( pNPC == this ) + continue; + + distSq = ( vAbsOrigin - pNPC->GetAbsOrigin() ).LengthSqr(); + + if ( distSq > closestDistSq ) + continue; + + if ( IRelationType( pNPC ) == D_LI ) + { + closestDistSq = distSq; + pNearest = pNPC; + } + } + + if (stricmp(name, "friend_npc") != 0) + { + // Okay, find the nearest friendly client. + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer ) + { + // Don't get players with notarget + if (pPlayer->GetFlags() & FL_NOTARGET) + continue; + + distSq = ( vAbsOrigin - pPlayer->GetAbsOrigin() ).LengthSqr(); + + if ( distSq > closestDistSq ) + continue; + + if ( IRelationType( pPlayer ) == D_LI ) + { + closestDistSq = distSq; + pNearest = pPlayer; + } + } + } + } + + return pNearest; + } + else if (!stricmp( name, "weapon" )) + { + return GetActiveWeapon(); + } + + // FindEntityProcedural can go through this now, so running this code would likely cause an infinite loop or something. + // As a result, FindEntityProcedural identifies itself with this weird new entity filter. + // Hey, if you've got a better idea, go ahead. + else if (!pFilter || !dynamic_cast(pFilter)) + { + // search for up to 32 entities with the same name and choose one randomly + CBaseEntity *entityList[ FINDNAMEDENTITY_MAX_ENTITIES ]; + CBaseEntity *entity; + int iCount; + + entity = NULL; + for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ ) + { + entity = gEntList.FindEntityByName( entity, szName, this, NULL, NULL, pFilter ); + if ( !entity ) + { + break; + } + entityList[ iCount ] = entity; + } + + if ( iCount > 0 ) + { + int index = RandomInt( 0, iCount - 1 ); + entity = entityList[ index ]; + return entity; + } + } + + return NULL; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Overload our muzzle flash and send it to any actively held weapon //----------------------------------------------------------------------------- @@ -3342,6 +4386,175 @@ void CBaseCombatCharacter::DoMuzzleFlash() } } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptActiveWeapon() +{ + return ToHScript( GetActiveWeapon() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptWeaponIndex( int i ) +{ + return ToHScript( GetWeapon( i ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptWeaponByType( const char *pszWeapon, int iSubType ) +{ + return ToHScript( Weapon_OwnsThisType( pszWeapon, iSubType ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::GetScriptAllWeapons( HSCRIPT hTable ) +{ + for (int i=0;iSetValue( hTable, m_hMyWeapons[i]->GetClassname(), ToHScript( m_hMyWeapons[i] ) ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptDropWeapon( HSCRIPT hWeapon ) +{ + CBaseCombatWeapon *pWeapon = HScriptToClass( hWeapon ); + if (!pWeapon) + return; + + if (pWeapon->GetOwner() == this) + { + // Drop the weapon + Weapon_Drop( pWeapon ); + } + else + { + CGMsg( 1, CON_GROUP_VSCRIPT, "ScriptDropWeapon: %s is not owned by %s", pWeapon->GetDebugName(), GetDebugName() ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptEquipWeapon( HSCRIPT hWeapon ) +{ + CBaseCombatWeapon *pWeapon = HScriptToClass( hWeapon ); + if (!pWeapon) + return; + + if (pWeapon->GetOwner() == this) + { + // Switch to this weapon + Weapon_Switch( pWeapon ); + } + else + { + if (CBaseCombatWeapon *pExistingWeapon = Weapon_OwnsThisType( pWeapon->GetClassname() )) + { + // Drop our existing weapon then! + Weapon_Drop( pExistingWeapon ); + } + + if (IsNPC()) + { + Weapon_Equip( pWeapon ); + MyNPCPointer()->OnGivenWeapon( pWeapon ); + } + else + { + Weapon_Equip( pWeapon ); + } + + pWeapon->OnPickedUp( this ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::ScriptGetAmmoCount( int iType ) const +{ + return GetAmmoCount( iType ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptSetAmmoCount( int iType, int iCount ) +{ + if (iType == -1) + { + Warning("%i is not a valid ammo type\n", iType); + return; + } + + return SetAmmoCount( iCount, iType ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const Vector& CBaseCombatCharacter::ScriptGetAttackSpread( HSCRIPT hWeapon, HSCRIPT hTarget ) +{ + CBaseEntity *pWeapon = ToEnt( hWeapon ); + if (!pWeapon || !pWeapon->IsBaseCombatWeapon()) + { + Warning( "GetAttackSpread: %s is not a valid weapon\n", pWeapon ? pWeapon->GetDebugName() : "Null entity" ); + return vec3_origin; + } + + // TODO: Make this a simple non-reference Vector? + static Vector vec; + vec = GetAttackSpread( pWeapon->MyCombatWeaponPointer(), ToEnt( hTarget ) ); + return vec; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CBaseCombatCharacter::ScriptGetSpreadBias( HSCRIPT hWeapon, HSCRIPT hTarget ) +{ + CBaseEntity *pWeapon = ToEnt( hWeapon ); + if (!pWeapon || !pWeapon->IsBaseCombatWeapon()) + { + Warning( "GetSpreadBias: %s is not a valid weapon\n", pWeapon ? pWeapon->GetDebugName() : "Null entity" ); + return 1.0f; + } + + return GetSpreadBias( pWeapon->MyCombatWeaponPointer(), ToEnt( hTarget ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::ScriptRelationType( HSCRIPT pTarget ) +{ + return (int)IRelationType( ToEnt( pTarget ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::ScriptRelationPriority( HSCRIPT pTarget ) +{ + return IRelationPriority( ToEnt( pTarget ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority ) +{ + AddEntityRelationship( ToEnt( pTarget ), (Disposition_t)disposition, priority ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptVehicleEntity() +{ + return ToHScript( GetVehicleEntity() ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: return true if given target cant be seen because of fog //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/basecombatcharacter.h b/mp/src/game/server/basecombatcharacter.h index 3e92332b..6d971ca3 100644 --- a/mp/src/game/server/basecombatcharacter.h +++ b/mp/src/game/server/basecombatcharacter.h @@ -119,6 +119,9 @@ public: DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: @@ -136,6 +139,10 @@ public: virtual bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ) { return BaseClass::FVisible( vecTarget, traceMask, ppBlocker ); } static void ResetVisibilityCache( CBaseCombatCharacter *pBCC = NULL ); +#ifdef MAPBASE + virtual bool ShouldUseVisibilityCache( CBaseEntity *pEntity ); +#endif + #ifdef PORTAL virtual bool FVisibleThroughPortal( const CProp_Portal *pPortal, CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); #endif @@ -154,8 +161,10 @@ public: virtual bool ShouldShootMissTarget( CBaseCombatCharacter *pAttacker ); virtual CBaseEntity *FindMissTarget( void ); +#ifndef MAPBASE // This function now exists in CBaseEntity // Do not call HandleInteraction directly, use DispatchInteraction bool DispatchInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ) { return ( interactionType > 0 ) ? HandleInteraction( interactionType, data, sourceEnt ) : false; } +#endif virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ); virtual QAngle BodyAngles(); @@ -225,7 +234,14 @@ public: virtual void Weapon_HandleAnimEvent( animevent_t *pEvent ); CBaseCombatWeapon* Weapon_OwnsThisType( const char *pszWeapon, int iSubType = 0 ) const; // True if already owns a weapon of this class virtual bool Weapon_CanUse( CBaseCombatWeapon *pWeapon ); // True is allowed to use this class of weapon +#ifdef MAPBASE + virtual Activity Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired = false, CBaseCombatWeapon *pSpecificWeapon = NULL ); virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player + virtual void Weapon_EquipHolstered( CBaseCombatWeapon *pWeapon ); // Pretty much only useful for NPCs + virtual void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); +#else + virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player +#endif virtual bool Weapon_EquipAmmoOnly( CBaseCombatWeapon *pWeapon ); // Adds weapon ammo to player, leaves weapon bool Weapon_Detach( CBaseCombatWeapon *pWeapon ); // Clear any pointers to the weapon. virtual void Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL ); @@ -288,7 +304,29 @@ public: // Killed a character void InputKilledNPC( inputdata_t &inputdata ); +#ifdef MAPBASE + + void InputGiveWeapon( inputdata_t &inputdata ); + void InputDropWeapon( inputdata_t &inputdata ); + void InputPickupWeaponInstant( inputdata_t &inputdata ); + COutputEvent m_OnWeaponEquip; + COutputEvent m_OnWeaponDrop; + + virtual void InputHolsterWeapon( inputdata_t &inputdata ); + virtual void InputHolsterAndDestroyWeapon( inputdata_t &inputdata ); + virtual void InputUnholsterWeapon( inputdata_t &inputdata ); + void InputSwitchToWeapon( inputdata_t &inputdata ); + + COutputEHANDLE m_OnKilledEnemy; + COutputEHANDLE m_OnKilledPlayer; + virtual void OnKilledNPC( CBaseCombatCharacter *pKilled ); + + virtual CBaseEntity *FindNamedEntity( const char *pszName, IEntityFindFilter *pFilter = NULL ); + + COutputFloat m_OnHealthChanged; +#else virtual void OnKilledNPC( CBaseCombatCharacter *pKilled ) {}; +#endif // Exactly one of these happens immediately after killed (gibbed may happen later when the corpse gibs) // Character gibbed or faded out (violence controls) (only fired once) @@ -304,6 +342,12 @@ public: virtual bool BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags ); +#ifdef MAPBASE + // A version of BecomeRagdollBoogie() that allows the color to change and returns the entity itself instead. + // In order to avoid breaking anything, it doesn't change the original function. + virtual CBaseEntity *BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags, const Vector *vecColor ); +#endif + CBaseEntity *FindHealthItem( const Vector &vecPosition, const Vector &range ); @@ -327,6 +371,11 @@ public: virtual Disposition_t IRelationType( CBaseEntity *pTarget ); virtual int IRelationPriority( CBaseEntity *pTarget ); +#ifdef MAPBASE + void AddRelationship( const char *pszRelationship, CBaseEntity *pActivator ); + void InputSetRelationship( inputdata_t &inputdata ); +#endif + virtual void SetLightingOriginRelative( CBaseEntity *pLightingOrigin ); protected: @@ -342,6 +391,9 @@ public: // Blood color (see BLOOD_COLOR_* macros in baseentity.h) void SetBloodColor( int nBloodColor ); +#ifdef MAPBASE + void InputSetBloodColor( inputdata_t &inputdata ); +#endif // Weapons.. CBaseCombatWeapon* GetActiveWeapon() const; @@ -349,23 +401,70 @@ public: CBaseCombatWeapon* GetWeapon( int i ) const; bool RemoveWeapon( CBaseCombatWeapon *pWeapon ); virtual void RemoveAllWeapons(); - WeaponProficiency_t GetCurrentWeaponProficiency() { return m_CurrentWeaponProficiency; } + WeaponProficiency_t GetCurrentWeaponProficiency() + { +#ifdef MAPBASE + // Mapbase adds proficiency override + return (m_ProficiencyOverride > WEAPON_PROFICIENCY_INVALID) ? m_ProficiencyOverride : m_CurrentWeaponProficiency; +#else + return m_CurrentWeaponProficiency; +#endif + } void SetCurrentWeaponProficiency( WeaponProficiency_t iProficiency ) { m_CurrentWeaponProficiency = iProficiency; } virtual WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ); virtual Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL ); virtual float GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ); virtual void DoMuzzleFlash(); +#ifdef MAPBASE_VSCRIPT + HSCRIPT GetScriptActiveWeapon(); + HSCRIPT GetScriptWeaponIndex( int i ); + HSCRIPT GetScriptWeaponByType( const char *pszWeapon, int iSubType = 0 ); + void GetScriptAllWeapons( HSCRIPT hTable ); + int ScriptGetCurrentWeaponProficiency() { return GetCurrentWeaponProficiency(); } + + void ScriptDropWeapon( HSCRIPT hWeapon ); + void ScriptEquipWeapon( HSCRIPT hWeapon ); + + int ScriptGetAmmoCount( int iType ) const; + void ScriptSetAmmoCount( int iType, int iCount ); + + const Vector& ScriptGetAttackSpread( HSCRIPT hWeapon, HSCRIPT hTarget ); + float ScriptGetSpreadBias( HSCRIPT hWeapon, HSCRIPT hTarget ); + + int ScriptRelationType( HSCRIPT pTarget ); + int ScriptRelationPriority( HSCRIPT pTarget ); + void ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority ); + + HSCRIPT GetScriptVehicleEntity(); + + bool ScriptInViewCone( const Vector &vecSpot ) { return FInViewCone( vecSpot ); } + bool ScriptEntInViewCone( HSCRIPT pEntity ) { return FInViewCone( ToEnt( pEntity ) ); } + + bool ScriptInAimCone( const Vector &vecSpot ) { return FInAimCone( vecSpot ); } + bool ScriptEntInAimCone( HSCRIPT pEntity ) { return FInAimCone( ToEnt( pEntity ) ); } + + const Vector& ScriptBodyAngles( void ) { static Vector vec; QAngle qa = BodyAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } +#endif + // Interactions static void InitInteractionSystem(); // Relationships static void AllocateDefaultRelationships( ); static void SetDefaultRelationship( Class_T nClass, Class_T nClassTarget, Disposition_t nDisposition, int nPriority ); +#ifdef MAPBASE + static Disposition_t GetDefaultRelationshipDisposition( Class_T nClassSource, Class_T nClassTarget ); + static int GetDefaultRelationshipPriority( Class_T nClassSource, Class_T nClassTarget ); + int GetDefaultRelationshipPriority( Class_T nClassTarget ); +#endif Disposition_t GetDefaultRelationshipDisposition( Class_T nClassTarget ); virtual void AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ); virtual bool RemoveEntityRelationship( CBaseEntity *pEntity ); virtual void AddClassRelationship( Class_T nClass, Disposition_t nDisposition, int nPriority ); +#ifdef MAPBASE + virtual bool RemoveClassRelationship( Class_T nClass ); +#endif virtual void ChangeTeam( int iTeamNum ); @@ -450,7 +549,9 @@ public: public: // returns the last body region that took damage int LastHitGroup() const { return m_LastHitGroup; } +#ifndef MAPBASE // For filter_damage_transfer protected: +#endif void SetLastHitGroup( int nHitGroup ) { m_LastHitGroup = nHitGroup; } public: @@ -481,6 +582,11 @@ protected: public: static int GetInteractionID(); // Returns the next interaction # +#ifdef MAPBASE + // Mapbase's new method for adding interactions which allows them to be handled with their names, currently for VScript + static void AddInteractionWithString( int &interaction, const char *szName ); +#endif + protected: // Visibility-related stuff bool ComputeLOS( const Vector &vecEyePosition, const Vector &vecTarget ) const; @@ -503,6 +609,11 @@ private: // cached off as the CurrentWeaponProficiency. WeaponProficiency_t m_CurrentWeaponProficiency; +#ifdef MAPBASE + // Weapon proficiency can be overridden with this. + WeaponProficiency_t m_ProficiencyOverride = WEAPON_PROFICIENCY_INVALID; +#endif + // --------------- // Relationships // --------------- diff --git a/mp/src/game/server/basecombatweapon.cpp b/mp/src/game/server/basecombatweapon.cpp index c05cb33d..1135aa82 100644 --- a/mp/src/game/server/basecombatweapon.cpp +++ b/mp/src/game/server/basecombatweapon.cpp @@ -342,7 +342,11 @@ bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector if ( pBCC ) { +#ifdef MAPBASE + if ( npcOwner->IRelationType( pBCC ) <= D_FR ) +#else if ( npcOwner->IRelationType( pBCC ) == D_HT ) +#endif return true; if ( bSetConditions ) @@ -716,6 +720,11 @@ void CBaseCombatWeapon::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ { m_OnPlayerUse.FireOutput( pActivator, pCaller ); +#ifdef MAPBASE + // Mark that we're being +USE'd, not bumped + AddSpawnFlags(SF_WEAPON_USED); +#endif + // // Bump the weapon to try equipping it before picking it up physically. This is // important in a few spots in the game where the player could potentially +use pickup @@ -729,6 +738,10 @@ void CBaseCombatWeapon::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ { pPlayer->PickupObject( this ); } + +#ifdef MAPBASE + RemoveSpawnFlags(SF_WEAPON_USED); +#endif } } diff --git a/mp/src/game/server/baseentity.cpp b/mp/src/game/server/baseentity.cpp index 9bd525a4..27900eca 100644 --- a/mp/src/game/server/baseentity.cpp +++ b/mp/src/game/server/baseentity.cpp @@ -62,6 +62,10 @@ #include "env_debughistory.h" #include "tier1/utlstring.h" #include "utlhashtable.h" +#ifdef MAPBASE +#include "mapbase/matchers.h" +#include "mapbase/datadesc_mod.h" +#endif #if defined( TF_DLL ) #include "tf_gamerules.h" @@ -98,6 +102,9 @@ bool CBaseEntity::s_bAbsQueriesValid = true; ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Test networking visibility distance" ); +ConVar sv_script_think_interval("sv_script_think_interval", "0.1"); + + // This table encodes edict data. void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) { @@ -275,6 +282,10 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) SendPropInt (SENDINFO(m_nRenderMode), 8, SPROP_UNSIGNED ), SendPropInt (SENDINFO(m_fEffects), EF_MAX_BITS, SPROP_UNSIGNED), SendPropInt (SENDINFO(m_clrRender), 32, SPROP_UNSIGNED), +#ifdef MAPBASE + // Keep consistent with VIEW_ID_COUNT in viewrender.h + SendPropInt (SENDINFO(m_iViewHideFlags), 9, SPROP_UNSIGNED ), +#endif SendPropInt (SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0), SendPropInt (SENDINFO(m_CollisionGroup), 5, SPROP_UNSIGNED), SendPropFloat (SENDINFO(m_flElasticity), 0, SPROP_COORD), @@ -284,6 +295,8 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)), SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED), + SendPropStringT( SENDINFO( m_iName ) ), + SendPropInt (SENDINFO_NAME( m_MoveType, movetype ), MOVETYPE_MAX_BITS, SPROP_UNSIGNED ), SendPropInt (SENDINFO_NAME( m_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ), #if PREDICTION_ERROR_CHECK_LEVEL > 1 @@ -613,6 +626,18 @@ CBaseEntity *CBaseEntity::GetFollowedEntity() return GetMoveParent(); } +#ifdef MAPBASE_VSCRIPT +void CBaseEntity::ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ) +{ + FollowEntity( ToEnt( hBaseEntity ), bBoneMerge ); +} + +HSCRIPT CBaseEntity::ScriptGetFollowedEntity() +{ + return ToHScript( GetFollowedEntity() ); +} +#endif + void CBaseEntity::SetClassname( const char *className ) { m_iClassname = AllocPooledString( className ); @@ -1032,6 +1057,20 @@ int CBaseEntity::DrawDebugTextOverlays(void) EntityText( offset,tempstr,0 ); offset++; } + +#ifdef MAPBASE + if (m_ResponseContexts.Count() > 0) + { + const char *contexts = UTIL_VarArgs("%s:%s", STRING(m_ResponseContexts[0].m_iszName), STRING(m_ResponseContexts[0].m_iszValue)); + for (int i = 1; i < GetContextCount(); i++) + { + contexts = UTIL_VarArgs("%s,%s:%s", contexts, STRING(m_ResponseContexts[i].m_iszName), STRING(m_ResponseContexts[i].m_iszValue)); + } + Q_snprintf(tempstr, sizeof(tempstr), "Response Contexts:%s", contexts); + EntityText(offset, tempstr, 0); + offset++; + } +#endif } if (m_debugOverlays & OVERLAY_VIEWOFFSET) @@ -1046,7 +1085,11 @@ int CBaseEntity::DrawDebugTextOverlays(void) void CBaseEntity::SetParent( string_t newParent, CBaseEntity *pActivator, int iAttachment ) { // find and notify the new parent +#ifdef MAPBASE + CBaseEntity *pParent = gEntList.FindEntityByName( NULL, newParent, this, pActivator ); +#else CBaseEntity *pParent = gEntList.FindEntityByName( NULL, newParent, NULL, pActivator ); +#endif // debug check if ( newParent != NULL_STRING && pParent == NULL ) @@ -1056,7 +1099,11 @@ void CBaseEntity::SetParent( string_t newParent, CBaseEntity *pActivator, int iA else { // make sure there isn't any ambiguity +#ifdef MAPBASE + if ( gEntList.FindEntityByName( pParent, newParent, this, pActivator ) ) +#else if ( gEntList.FindEntityByName( pParent, newParent, NULL, pActivator ) ) +#endif { Msg( "Entity %s(%s) has ambigious parent %s\n", STRING(m_iClassname), GetDebugName(), STRING(newParent) ); } @@ -1286,6 +1333,44 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa if ( pszOutput == NULL ) return; + CBaseEntityOutput *pOutput = FindNamedOutput( pszOutput ); + if ( pOutput ) + { + pOutput->FireOutput( variant, pActivator, pCaller, flDelay ); + return; + } +} + +#ifdef MAPBASE_VSCRIPT +void CBaseEntity::ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay ) +{ + variant_t value; + value.SetString( MAKE_STRING(szValue) ); + + FireNamedOutput( pszOutput, value, ToEnt(hActivator), ToEnt(hCaller), flDelay ); +} + +float CBaseEntity::GetMaxOutputDelay( const char *pszOutput ) +{ + CBaseEntityOutput *pOutput = FindNamedOutput( pszOutput ); + if ( pOutput ) + { + return pOutput->GetMaxDelay(); + } + return 0; +} + +void CBaseEntity::CancelEventsByInput( const char *szInput ) +{ + g_EventQueue.CancelEventsByInput( this, szInput ); +} +#endif // MAPBASE_VSCRIPT + +CBaseEntityOutput *CBaseEntity::FindNamedOutput( const char *pszOutput ) +{ + if ( pszOutput == NULL ) + return NULL; + datamap_t *dmap = GetDataDescMap(); while ( dmap ) { @@ -1298,14 +1383,13 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa CBaseEntityOutput *pOutput = ( CBaseEntityOutput * )( ( int )this + ( int )dataDesc->fieldOffset[0] ); if ( !Q_stricmp( dataDesc->externalName, pszOutput ) ) { - pOutput->FireOutput( variant, pActivator, pCaller, flDelay ); - return; + return pOutput; } } } - dmap = dmap->baseMap; } + return NULL; } void CBaseEntity::Activate( void ) @@ -1473,17 +1557,27 @@ int CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo ) } } +#ifndef MAPBASE // Moved below the gamerules AllowDamage() check // Make sure our damage filter allows the damage. if ( !PassesDamageFilter( inputInfo )) { return 0; } +#endif if( !g_pGameRules->AllowDamage(this, inputInfo) ) { return 0; } +#ifdef MAPBASE + // Make sure our damage filter allows the damage. + if ( !PassesFinalDamageFilter( inputInfo )) + { + return 0; + } +#endif + if ( PhysIsInCallback() ) { PhysCallbackDamage( this, inputInfo ); @@ -1503,6 +1597,11 @@ int CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo ) //Msg("%s took %.2f Damage, at %.2f\n", GetClassname(), info.GetDamage(), gpGlobals->curtime ); +#ifdef MAPBASE + // Modify damage if we have a filter that does that + DamageFilterDamageMod(info); +#endif + return OnTakeDamage( info ); } return 0; @@ -1577,7 +1676,27 @@ int CBaseEntity::VPhysicsTakeDamage( const CTakeDamageInfo &info ) if ( gameFlags & FVPHYSICS_PLAYER_HELD ) { // if the player is holding the object, use it's real mass (player holding reduced the mass) - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + CBasePlayer *pPlayer = NULL; + + if ( AI_IsSinglePlayer() ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + else + { + // See which MP player is holding the physics object and then use that player to get the real mass of the object. + // This is ugly but better than having linkage between an object and its "holding" player. + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *tempPlayer = UTIL_PlayerByIndex( i ); + if ( tempPlayer && (tempPlayer->GetHeldObject() == this ) ) + { + pPlayer = tempPlayer; + break; + } + } + } + if ( pPlayer ) { float mass = pPlayer->GetHeldObjectMass( VPhysicsGetObject() ); @@ -1611,6 +1730,25 @@ int CBaseEntity::VPhysicsTakeDamage( const CTakeDamageInfo &info ) // Character killed (only fired once) void CBaseEntity::Event_Killed( const CTakeDamageInfo &info ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_OnDeath.CanRunInScope( m_ScriptScope )) + { + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + // info + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( hInfo ) }; + if ( g_Hook_OnDeath.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_BOOLEAN && functionReturn.m_bool == false) ) + { + // Make this entity cheat death + g_pScriptVM->RemoveInstance( hInfo ); + return; + } + + g_pScriptVM->RemoveInstance( hInfo ); + } +#endif + if( info.GetAttacker() ) { info.GetAttacker()->Event_KilledOther(this, info); @@ -1781,10 +1919,19 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FIELD( m_flSimulationTime, FIELD_TIME ), DEFINE_FIELD( m_nLastThinkTick, FIELD_TICK ), + DEFINE_FIELD(m_iszScriptId, FIELD_STRING), + // m_ScriptScope; + // m_hScriptInstance; + + DEFINE_KEYFIELD(m_iszVScripts, FIELD_STRING, "vscripts"), + DEFINE_KEYFIELD(m_iszScriptThinkFunction, FIELD_STRING, "thinkfunction"), DEFINE_KEYFIELD( m_nNextThinkTick, FIELD_TICK, "nextthink" ), DEFINE_KEYFIELD( m_fEffects, FIELD_INTEGER, "effects" ), DEFINE_KEYFIELD( m_clrRender, FIELD_COLOR32, "rendercolor" ), DEFINE_GLOBAL_KEYFIELD( m_nModelIndex, FIELD_SHORT, "modelindex" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iViewHideFlags, FIELD_INTEGER, "viewhideflags" ), +#endif #if !defined( NO_ENTITY_PREDICTION ) // DEFINE_FIELD( m_PredictableID, CPredictableId ), #endif @@ -1827,7 +1974,11 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FIELD( m_MoveType, FIELD_CHARACTER ), DEFINE_FIELD( m_MoveCollide, FIELD_CHARACTER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_hOwnerEntity, FIELD_EHANDLE, "OwnerEntity" ), +#else DEFINE_FIELD( m_hOwnerEntity, FIELD_EHANDLE ), +#endif DEFINE_FIELD( m_CollisionGroup, FIELD_INTEGER ), DEFINE_PHYSPTR( m_pPhysicsObject), DEFINE_FIELD( m_flElasticity, FIELD_FLOAT ), @@ -1878,7 +2029,12 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_KEYFIELD( m_vecViewOffset, FIELD_VECTOR, "view_ofs" ), +#ifdef MAPBASE + // You know, m_fFlags access + DEFINE_KEYFIELD( m_fFlags, FIELD_INTEGER, "m_fFlags" ), +#else DEFINE_FIELD( m_fFlags, FIELD_INTEGER ), +#endif #if !defined( NO_ENTITY_PREDICTION ) // DEFINE_FIELD( m_bIsPlayerSimulated, FIELD_INTEGER ), // DEFINE_FIELD( m_hPlayerSimulationOwner, FIELD_EHANDLE ), @@ -1927,17 +2083,106 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_INPUTFUNC( FIELD_VOID, "EnableShadow", InputEnableShadow ), DEFINE_INPUTFUNC( FIELD_STRING, "AddOutput", InputAddOutput ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "ChangeVariable", InputChangeVariable ), +#endif +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INPUT, "PassUser1", InputPassUser1 ), + DEFINE_INPUTFUNC( FIELD_INPUT, "PassUser2", InputPassUser2 ), + DEFINE_INPUTFUNC( FIELD_INPUT, "PassUser3", InputPassUser3 ), + DEFINE_INPUTFUNC( FIELD_INPUT, "PassUser4", InputPassUser4 ), + + DEFINE_INPUTFUNC( FIELD_VOID, "FireUser1", InputFireUser1 ), + DEFINE_INPUTFUNC( FIELD_VOID, "FireUser2", InputFireUser2 ), + DEFINE_INPUTFUNC( FIELD_VOID, "FireUser3", InputFireUser3 ), + DEFINE_INPUTFUNC( FIELD_VOID, "FireUser4", InputFireUser4 ), + + DEFINE_INPUTFUNC( FIELD_VOID, "FireRandomUser", InputFireRandomUser ), + DEFINE_INPUTFUNC( FIELD_INPUT, "PassRandomUser", InputPassRandomUser ), +#else DEFINE_INPUTFUNC( FIELD_STRING, "FireUser1", InputFireUser1 ), DEFINE_INPUTFUNC( FIELD_STRING, "FireUser2", InputFireUser2 ), DEFINE_INPUTFUNC( FIELD_STRING, "FireUser3", InputFireUser3 ), DEFINE_INPUTFUNC( FIELD_STRING, "FireUser4", InputFireUser4 ), +#endif + + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptFile", InputRunScriptFile), + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptCode", InputRunScript), + DEFINE_INPUTFUNC(FIELD_STRING, "CallScriptFunction", InputCallScriptFunction), +#ifdef MAPBASE_VSCRIPT + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptCodeQuotable", InputRunScriptQuotable), + DEFINE_INPUTFUNC(FIELD_VOID, "ClearScriptScope", InputClearScriptScope), +#endif + +#ifdef MAPBASE + DEFINE_OUTPUT( m_OutUser1, "OutUser1" ), + DEFINE_OUTPUT( m_OutUser2, "OutUser2" ), + DEFINE_OUTPUT( m_OutUser3, "OutUser3" ), + DEFINE_OUTPUT( m_OutUser4, "OutUser4" ), +#endif DEFINE_OUTPUT( m_OnUser1, "OnUser1" ), DEFINE_OUTPUT( m_OnUser2, "OnUser2" ), DEFINE_OUTPUT( m_OnUser3, "OnUser3" ), DEFINE_OUTPUT( m_OnUser4, "OnUser4" ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetEntityName", InputSetEntityName ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetOwnerEntity", InputSetOwnerEntity ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddHealth", InputAddHealth ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveHealth", InputRemoveHealth ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxHealth", InputSetMaxHealth ), + + DEFINE_INPUTFUNC( FIELD_STRING, "FireOutput", InputFireOutput ), + DEFINE_INPUTFUNC( FIELD_STRING, "RemoveOutput", InputRemoveOutput ), + //DEFINE_INPUTFUNC( FIELD_STRING, "CancelOutput", InputCancelOutput ), // Find a way to implement this + DEFINE_INPUTFUNC( FIELD_STRING, "ReplaceOutput", InputReplaceOutput ), + DEFINE_INPUTFUNC( FIELD_STRING, "AcceptInput", InputAcceptInput ), + DEFINE_INPUTFUNC( FIELD_VOID, "CancelPending", InputCancelPending ), + + DEFINE_INPUTFUNC( FIELD_VOID, "FreeChildren", InputFreeChildren ), + + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetLocalOrigin", InputSetLocalOrigin ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetLocalAngles", InputSetLocalAngles ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetAbsOrigin", InputSetAbsOrigin ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetAbsAngles", InputSetAbsAngles ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetLocalVelocity", InputSetLocalVelocity ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetLocalAngularVelocity", InputSetLocalAngularVelocity ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddSpawnFlags", InputAddSpawnFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveSpawnFlags", InputRemoveSpawnFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetRenderMode", InputSetRenderMode ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetRenderFX", InputSetRenderFX ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetViewHideFlags", InputSetViewHideFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddEffects", InputAddEffects ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveEffects", InputRemoveEffects ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableDraw", InputDrawEntity ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableDraw", InputUndrawEntity ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddEFlags", InputAddEFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveEFlags", InputRemoveEFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddSolidFlags", InputAddSolidFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveSolidFlags", InputRemoveSolidFlags ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMoveType", InputSetMoveType ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCollisionGroup", InputSetCollisionGroup ), + + DEFINE_INPUTFUNC( FIELD_EHANDLE, "Touch", InputTouch ), + + DEFINE_INPUTFUNC( FIELD_INPUT, "KilledNPC", InputKilledNPC ), + + DEFINE_INPUTFUNC( FIELD_VOID, "KillIfNotVisible", InputKillIfNotVisible ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "KillWhenNotVisible", InputKillWhenNotVisible ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetThinkNull", InputSetThinkNull ), + + DEFINE_OUTPUT( m_OnKilled, "OnKilled" ), +#endif + // Function Pointers DEFINE_FUNCTION( SUB_Remove ), DEFINE_FUNCTION( SUB_DoNothing ), @@ -1947,6 +2192,14 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FUNCTION( SUB_Vanish ), DEFINE_FUNCTION( SUB_CallUseToggle ), DEFINE_THINKFUNC( ShadowCastDistThink ), + DEFINE_THINKFUNC( ScriptThink ), +#ifdef MAPBASE_VSCRIPT + DEFINE_THINKFUNC( ScriptThinkH ), +#endif + +#ifdef MAPBASE + DEFINE_FUNCTION( SUB_RemoveWhenNotVisible ), +#endif DEFINE_FIELD( m_hEffectEntity, FIELD_EHANDLE ), @@ -1959,6 +2212,275 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) END_DATADESC() + +#ifdef MAPBASE_VSCRIPT +ScriptHook_t CBaseEntity::g_Hook_UpdateOnRemove; +ScriptHook_t CBaseEntity::g_Hook_VPhysicsCollision; +ScriptHook_t CBaseEntity::g_Hook_FireBullets; +ScriptHook_t CBaseEntity::g_Hook_OnDeath; +ScriptHook_t CBaseEntity::g_Hook_HandleInteraction; +#endif + +BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( ConnectOutputToScript, "ConnectOutput", "Adds an I/O connection that will call the named function when the specified output fires" ) + DEFINE_SCRIPTFUNC_NAMED( DisconnectOutputFromScript, "DisconnectOutput", "Removes a connected script function from an I/O event." ) + + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( SetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + DEFINE_SCRIPTFUNC( SetMaxHealth, "" ) + + DEFINE_SCRIPTFUNC( SetModel, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEmitSound, "EmitSound", "Plays a sound from this entity." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptPrecacheScriptSound, "PrecacheSoundScript", "Precache a sound for later playing." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSoundDuration, "GetSoundDuration", "Returns float duration of the sound. Takes soundname and optional actormodelname.") + + + DEFINE_SCRIPTFUNC( GetClassname, "" ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityNameAsCStr, "GetName", "" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( GetDebugName, "If name exists returns name, otherwise returns classname" ) + DEFINE_SCRIPTFUNC_NAMED( SetNameAsCStr, "SetName", "" ) +#endif + DEFINE_SCRIPTFUNC( GetPreTemplateName, "Get the entity name stripped of template unique decoration" ) + + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC( SetAbsOrigin, "SetAbsOrigin" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( SetAbsAngles, "SetAbsAngles" ) + + DEFINE_SCRIPTFUNC( GetLocalOrigin, "GetLocalOrigin" ) + DEFINE_SCRIPTFUNC( SetLocalOrigin, "SetLocalOrigin" ) + DEFINE_SCRIPTFUNC( GetLocalAngles, "GetLocalAngles" ) + DEFINE_SCRIPTFUNC( SetLocalAngles, "SetLocalAngles" ) +#endif + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOrigin, "SetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRight, "GetRightVector", "Get the right vector of the entity" ) +#endif + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeft, "GetLeftVector", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOriginAngles, "SetOriginAngles", "Set both the origin and the angles" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOriginAnglesVelocity, "SetOriginAnglesVelocity", "Set the origin, the angles, and the velocity" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + + DEFINE_SCRIPTFUNC( ApplyAbsVelocityImpulse, "" ) + DEFINE_SCRIPTFUNC( ApplyLocalAngularVelocityImpulse, "" ) +#endif + + DEFINE_SCRIPTFUNC_NAMED( GetAbsVelocity, "GetVelocity", "" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsVelocity, "SetVelocity", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetLocalAngularVelocity, "SetAngularVelocity", "Set the local angular velocity - takes float pitch,yaw,roll velocities" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLocalAngularVelocity, "GetAngularVelocity", "Get the local angular velocity - returns a vector of pitch,yaw,roll" ) + + DEFINE_SCRIPTFUNC_NAMED( WorldSpaceCenter, "GetCenter", "Get vector to center of object - absolute coords") + DEFINE_SCRIPTFUNC_NAMED( ScriptEyePosition, "EyePosition", "Get vector to eye position - absolute coords") +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptEyeAngles, "EyeAngles", "Get eye pitch, yaw, roll as a vector" ) +#endif + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAngles, "SetAngles", "Set entity pitch, yaw, roll") + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector") + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetSize, "SetSize", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMins, "GetBoundingMins", "Get a vector containing min bounds, centered on object") + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMaxs, "GetBoundingMaxs", "Get a vector containing max bounds, centered on object") + + DEFINE_SCRIPTFUNC_NAMED( ScriptUtilRemove, "Destroy", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOwner, "SetOwner", "" ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "" ) + DEFINE_SCRIPTFUNC_NAMED( ChangeTeam, "SetTeam", "" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptSetParent, "SetParent", "" ) +#endif + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNextMovePeer, "NextMovePeer", "" ) + + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromString, "__KeyValueFromString", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromFloat, "__KeyValueFromFloat", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromInt, "__KeyValueFromInt", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromVector, "__KeyValueFromVector", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelKeyValues, "GetModelKeyValues", "Get a KeyValue class instance on this entity's model") + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptIsVisible, "IsVisible", "Check if the specified position can be visible to this entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsEntVisible, "IsEntVisible", "Check if the specified entity can be visible to this entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsVisibleWithMask, "IsVisibleWithMask", "Check if the specified position can be visible to this entity with a specific trace mask." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptTakeDamage, "TakeDamage", "Apply damage to this entity with a given info handle" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFireBullets, "FireBullets", "Fire bullets from entity with a given info handle" ) + + DEFINE_SCRIPTFUNC( TakeHealth, "Give this entity health" ) + DEFINE_SCRIPTFUNC( IsAlive, "Return true if this entity is alive" ) + + DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetContext, "GetContext", "Get a response context value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddContext, "AddContext", "Add a response context value" ) + DEFINE_SCRIPTFUNC( GetContextExpireTime, "Get a response context's expiration time" ) + DEFINE_SCRIPTFUNC( GetContextCount, "Get the number of response contexts" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetContextIndex, "GetContextIndex", "Get a response context at a specific index in the form of a table" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptFollowEntity, "FollowEntity", "Begin following the specified entity. This makes this entity non-solid, parents it to the target entity, and teleports it to the specified entity's origin. The second parameter is whether or not to use bonemerging while following." ) + DEFINE_SCRIPTFUNC( StopFollowingEntity, "Stops following an entity if we're following one." ) + DEFINE_SCRIPTFUNC( IsFollowingEntity, "Returns true if this entity is following another entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFollowedEntity, "GetFollowedEntity", "Get the entity we're following." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptClassify, "Classify", "Get Class_T class ID (corresponds to the CLASS_ set of constants)" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAcceptInput, "AcceptInput", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFireOutput, "FireOutput", "Fire an entity output" ) + DEFINE_SCRIPTFUNC( GetMaxOutputDelay, "Get the longest delay for all events attached to an output" ) + DEFINE_SCRIPTFUNC( CancelEventsByInput, "Cancel all I/O events for this entity, match input" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddOutput, "AddOutput", "Add an output" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValue, "GetKeyValue", "Get a keyvalue" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetRenderColorVector", "Get the render color as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetRenderColorR", "Get the render color's R value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetRenderColorG", "Get the render color's G value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorB, "GetRenderColorB", "Get the render color's B value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAlpha, "GetRenderAlpha", "Get the render color's alpha value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorVector, "SetRenderColorVector", "Set the render color as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColor, "SetRenderColor", "Set the render color" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorR, "SetRenderColorR", "Set the render color's R value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorG, "SetRenderColorG", "Set the render color's G value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetRenderColorB", "Set the render color's B value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetRenderAlpha", "Set the render color's alpha value" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetColorVector", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetColorR", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetColorG", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorB, "GetColorB", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAlpha, "GetAlpha", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorVector, "SetColorVector", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorR, "SetColorR", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorG, "SetColorG", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetColorB", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetAlpha", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRenderMode, "GetRenderMode", "Get render mode" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetRenderMode, "SetRenderMode", "Set render mode" ) + + DEFINE_SCRIPTFUNC( GetSpawnFlags, "Get spawnflags" ) + DEFINE_SCRIPTFUNC( AddSpawnFlags, "Add spawnflag(s)" ) + DEFINE_SCRIPTFUNC( RemoveSpawnFlags, "Remove spawnflag(s)" ) + DEFINE_SCRIPTFUNC( ClearSpawnFlags, "Clear spawnflag(s)" ) + DEFINE_SCRIPTFUNC( HasSpawnFlags, "Check if the entity has specific spawnflag(s) ticked" ) + + DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( AddEffects, "Add effect(s)" ) + DEFINE_SCRIPTFUNC( RemoveEffects, "Remove effect(s)" ) + DEFINE_SCRIPTFUNC( ClearEffects, "Clear effect(s)" ) + DEFINE_SCRIPTFUNC( SetEffects, "Set effect(s)" ) + DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + + DEFINE_SCRIPTFUNC( GetFlags, "Get flags" ) + DEFINE_SCRIPTFUNC( AddFlag, "Add flag" ) + DEFINE_SCRIPTFUNC( RemoveFlag, "Remove flag" ) + + DEFINE_SCRIPTFUNC( GetEFlags, "Get Eflags" ) + DEFINE_SCRIPTFUNC( AddEFlags, "Add Eflags" ) + DEFINE_SCRIPTFUNC( RemoveEFlags, "Remove Eflags" ) + + DEFINE_SCRIPTFUNC( GetTransmitState, "" ) + DEFINE_SCRIPTFUNC( SetTransmitState, "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveType, "GetMoveType", "Get the move type" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetMoveType, "SetMoveType", "Set the move type" ) + + DEFINE_SCRIPTFUNC( GetCollisionGroup, "Get the collision group" ) + DEFINE_SCRIPTFUNC( SetCollisionGroup, "Set the collision group" ) + + DEFINE_SCRIPTFUNC( GetGravity, "" ) + DEFINE_SCRIPTFUNC( SetGravity, "" ) + DEFINE_SCRIPTFUNC( GetFriction, "" ) + DEFINE_SCRIPTFUNC( SetFriction, "" ) + DEFINE_SCRIPTFUNC( GetMass, "" ) + DEFINE_SCRIPTFUNC( SetMass, "" ) + + DEFINE_SCRIPTFUNC( GetSolidFlags, "Get solid flags" ) + DEFINE_SCRIPTFUNC( AddSolidFlags, "Add solid flags" ) + DEFINE_SCRIPTFUNC( RemoveSolidFlags, "Remove solid flags" ) + + DEFINE_SCRIPTFUNC( IsPlayer, "Returns true if this entity is a player." ) + DEFINE_SCRIPTFUNC( IsNPC, "Returns true if this entity is a NPC." ) + DEFINE_SCRIPTFUNC( IsCombatCharacter, "Returns true if this entity is a combat character (player or NPC)." ) + DEFINE_SCRIPTFUNC_NAMED( IsBaseCombatWeapon, "IsWeapon", "Returns true if this entity is a weapon." ) + DEFINE_SCRIPTFUNC( IsWorld, "Returns true if this entity is the world." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptDispatchInteraction, "DispatchInteraction", "Dispatches an interaction on this entity. See the g_interaction set of constants for more information." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetTakeDamage, "GetTakeDamage", "Gets this entity's m_takedamage value. (DAMAGE_YES, DAMAGE_NO, etc.)" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetTakeDamage, "SetTakeDamage", "Sets this entity's m_takedamage value. (DAMAGE_YES, DAMAGE_NO, etc.)" ) + + // DEFINE_SCRIPTFUNC( IsMarkedForDeletion, "Returns true if the entity is valid and marked for deletion." ) +#endif + + DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( GetOrCreatePrivateScriptScope, "Create and retrieve the script-side data associated with an entity" ) +#endif + DEFINE_SCRIPTFUNC( GetScriptId, "Retrieve the unique identifier used to refer to the entity within the scripting system" ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + DEFINE_SCRIPTFUNC( entindex, "" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptSetThinkFunction, "SetThinkFunction", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopThinkFunction, "StopThinkFunction", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetThink, "SetThink", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopThink, "StopThink", "" ) + + // + // Hooks + // + DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." ) + + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_VPhysicsCollision, "VPhysicsCollision", FIELD_VOID, "Called for every single VPhysics-related collision experienced by this entity." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "speed", FIELD_FLOAT ) + DEFINE_SCRIPTHOOK_PARAM( "point", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "normal", FIELD_VECTOR ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_FireBullets, "FireBullets", FIELD_VOID, "Called for every single VPhysics-related collision experienced by this entity." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "speed", FIELD_FLOAT ) + DEFINE_SCRIPTHOOK_PARAM( "point", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "normal", FIELD_VECTOR ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_OnDeath, "OnDeath", FIELD_BOOLEAN, "Called when the entity dies (Event_Killed). Returning false makes the entity cancel death, although this could have unforeseen consequences. For hooking any damage instead of just death, see filter_script and PassesFinalDamageFilter." ) + DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_HandleInteraction, "HandleInteraction", FIELD_BOOLEAN, "Called for internal game interactions. See the g_interaction set of constants for more information. Returning true or false will return that value without falling to any internal handling. Returning nothing will allow the interaction to fall to any internal handling." ) + DEFINE_SCRIPTHOOK_PARAM( "interaction", FIELD_INTEGER ) + //DEFINE_SCRIPTHOOK_PARAM( "data", FIELD_VARIANT ) + DEFINE_SCRIPTHOOK_PARAM( "sourceEnt", FIELD_HSCRIPT ) + END_SCRIPTHOOK() +#endif +END_SCRIPTDESC(); + + // For code error checking extern bool g_bReceivedChainedUpdateOnRemove; @@ -2058,6 +2580,27 @@ void CBaseEntity::UpdateOnRemove( void ) modelinfo->ReleaseDynamicModel( m_nModelIndex ); // no-op if not dynamic m_nModelIndex = -1; } + + if ( m_hScriptInstance ) + { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized()) + { + g_Hook_UpdateOnRemove.Call( m_ScriptScope, NULL, NULL ); + } +#endif // MAPBASE_VSCRIPT + + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + +#ifdef MAPBASE_VSCRIPT + if ( m_hfnThink ) + { + g_pScriptVM->ReleaseScript( m_hfnThink ); + m_hfnThink = NULL; + } +#endif // MAPBASE_VSCRIPT + } } //----------------------------------------------------------------------------- @@ -2646,6 +3189,21 @@ void CBaseEntity::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) int otherIndex = !index; CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex]; +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_VPhysicsCollision.CanRunInScope(m_ScriptScope)) + { + Vector vecContactPoint; + pEvent->pInternalData->GetContactPoint( vecContactPoint ); + + Vector vecSurfaceNormal; + pEvent->pInternalData->GetSurfaceNormal( vecSurfaceNormal ); + + // entity, speed, point, normal + ScriptVariant_t args[] = { ScriptVariant_t( pHitEntity->GetScriptInstance() ), pEvent->collisionSpeed, vecContactPoint, vecSurfaceNormal }; + g_Hook_VPhysicsCollision.Call( m_ScriptScope, NULL, args ); + } +#endif + // Don't make sounds / effects if neither entity is MOVETYPE_VPHYSICS. The game // physics should have done so. if ( GetMoveType() != MOVETYPE_VPHYSICS && pHitEntity->GetMoveType() != MOVETYPE_VPHYSICS ) @@ -2967,14 +3525,80 @@ bool CBaseEntity::PassesDamageFilter( const CTakeDamageInfo &info ) if (m_hDamageFilter) { CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get()); +#ifdef MAPBASE + return pFilter->PassesDamageFilter(this, info); +#else return pFilter->PassesDamageFilter(info); +#endif } return true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: A damage filter pass for when this is most certainly the part where we might actually take damage. +// Made for the "damage" family of filters, including filter_damage_transfer. +//----------------------------------------------------------------------------- +bool CBaseEntity::PassesFinalDamageFilter( const CTakeDamageInfo &info ) +{ + if (!PassesDamageFilter(info)) + return false; + + if (m_hDamageFilter) + { + CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get()); + if (!pFilter->PassesFinalDamageFilter(this, info)) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: A hack for damage transfers. +//----------------------------------------------------------------------------- +bool CBaseEntity::DamageFilterAllowsBlood( const CTakeDamageInfo &info ) +{ + if (m_hDamageFilter) + { + CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get()); + if (!pFilter->BloodAllowed(this, info)) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Modifies damage taken. Returns true if damage was successfully modded. +//----------------------------------------------------------------------------- +bool CBaseEntity::DamageFilterDamageMod( CTakeDamageInfo &info ) +{ + if (m_hDamageFilter) + { + CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get()); + if (pFilter->DamageMod(this, info)) + { + return true; + } + } + + return false; +} +#endif + FORCEINLINE bool NamesMatch( const char *pszQuery, string_t nameToMatch ) { +#ifdef MAPBASE + // NamesMatch has been turned into Matcher_NamesMatch in matchers.h + // for a wider range of accessibility and flexibility. + return Matcher_NamesMatch(pszQuery, STRING(nameToMatch)); +#else if ( nameToMatch == NULL_STRING ) return (!pszQuery || *pszQuery == 0 || *pszQuery == '*'); @@ -3009,6 +3633,7 @@ FORCEINLINE bool NamesMatch( const char *pszQuery, string_t nameToMatch ) return true; return false; +#endif } bool CBaseEntity::NameMatchesComplex( const char *pszNameOrWildcard ) @@ -3248,6 +3873,7 @@ void CBaseEntity::OnSave( IEntitySaveUtils *pUtils ) //----------------------------------------------------------------------------- void CBaseEntity::OnRestore() { +#ifndef MAPBASE // It's your fault if you're trying to load old, broken saves from a possibly closed 2013 beta in Mapbase. #if defined( PORTAL ) || defined( HL2_EPISODIC ) || defined ( HL2_DLL ) || defined( HL2_LOSTCOAST ) // We had a short period during the 2013 beta where the FL_* flags had a bogus value near the top, so detect // these bad saves and just give up. Only saves from the short beta period should have been effected. @@ -3259,6 +3885,7 @@ void CBaseEntity::OnRestore() engine->ServerCommand("wait;wait;disconnect;showconsole\n"); } +#endif #endif SimThink_EntityChanged( this ); @@ -3718,9 +4345,9 @@ const char *CBaseEntity::GetDebugName(void) if ( this == NULL ) return "<>"; - if ( m_iName != NULL_STRING ) + if ( m_iName.Get() != NULL_STRING ) { - return STRING(m_iName); + return STRING(m_iName.Get()); } else { @@ -3897,17 +4524,12 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, { // found a match - char szBuffer[256]; // mapper debug message - if (pCaller != NULL) - { - Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, STRING(pCaller->m_iName), GetDebugName(), szInputName, Value.String() ); - } - else - { - Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input : %s.%s(%s)\n", gpGlobals->curtime, GetDebugName(), szInputName, Value.String() ); - } - DevMsg( 2, "%s", szBuffer ); +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_IO_SYSTEM, "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, pCaller ? STRING(pCaller->m_iName.Get()) : "", GetDebugName(), szInputName, Value.String() ); +#else + DevMsg( 2, "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, pCaller ? STRING(pCaller->m_iName.Get()) : "", GetDebugName(), szInputName, Value.String() ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); if (m_debugOverlays & OVERLAY_MESSAGE_BIT) @@ -3920,15 +4542,51 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, { if ( !(Value.FieldType() == FIELD_VOID && dmap->dataDesc[i].fieldType == FIELD_STRING) ) // allow empty strings { +#ifdef MAPBASE + // Activator, etc. support for EHANDLE convert + if ( !Value.Convert( (fieldtype_t)dmap->dataDesc[i].fieldType, this, pActivator, pCaller ) ) + { + bool bBadConversion = true; + + // Attempt to convert to string and back. + // Almost all field types support being converted to a string, and many support being parsed from a string too. + fieldtype_t originalfield = Value.FieldType(); + if (Value.Convert(FIELD_STRING)) + { + bBadConversion = !(Value.Convert((fieldtype_t)dmap->dataDesc[i].fieldType, this, pActivator, pCaller)); + if (!bBadConversion) + { + // Actual support should be added for each field, but if it works, it works. + // Warning against it only matters if you're a programmer and want to add support for each field. + // Only send a warning in dev mode. + DevWarning("!! Had to convert to string and back\n" + "!! Source Field Type: %i, Target Field Type: %i\n", + originalfield, dmap->dataDesc[i].fieldType); + } + } + + if (bBadConversion) + { + Warning( "!! ERROR: bad input/output link:\n!! Unable to convert value \"%s\" from %s (%s) to field type %i\n!! Target Entity: %s (%s), Input: %s\n", + Value.GetDebug(), + ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "", + ( pCaller != NULL ) ? STRING(pCaller->m_iName.Get()) : "", + dmap->dataDesc[i].fieldType, + STRING(m_iClassname), GetDebugName(), szInputName ); + return false; + } + } +#else if ( !Value.Convert( (fieldtype_t)dmap->dataDesc[i].fieldType ) ) { // bad conversion Warning( "!! ERROR: bad input/output link:\n!! %s(%s,%s) doesn't match type from %s(%s)\n", STRING(m_iClassname), GetDebugName(), szInputName, ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "", - ( pCaller != NULL ) ? STRING(pCaller->m_iName) : "" ); + ( pCaller != NULL ) ? STRING(pCaller->m_iName.Get()) : "" ); return false; } +#endif } } @@ -3944,7 +4602,24 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, data.value = Value; data.nOutputID = outputID; - (this->*pfnInput)( data ); + + // Now, see if there's a function named Input in this entity's script file. + // If so, execute it and let it decide whether to allow the default behavior to also execute. + bool bCallInputFunc = true; // Always assume default behavior (do call the input function) + + if ( m_ScriptScope.IsInitialized() ) + { + ScriptVariant_t functionReturn; + if ( ScriptInputHook( szInputName, pActivator, pCaller, Value, functionReturn ) ) + { + bCallInputFunc = functionReturn.m_bool; + } + } + + if( bCallInputFunc ) + { + (this->*pfnInput)( data ); + } } else if ( dmap->dataDesc[i].flags & FTYPEDESC_KEY ) { @@ -3967,10 +4642,70 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, } } - DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName)*/ ); +#ifdef MAPBASE_VSCRIPT + // Allow VScript to handle unhandled inputs. + if (m_ScriptScope.IsInitialized()) + { + ScriptVariant_t functionReturn; + + if ( ScriptInputHook( szInputName, pActivator, pCaller, Value, functionReturn ) ) + { + if (functionReturn.m_bool) + return true; + } + } +#endif + +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_IO_SYSTEM, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName() ); +#else + DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName.Get())*/ ); +#endif return false; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseEntity::ScriptInputHook( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, ScriptVariant_t &functionReturn ) +{ + char szScriptFunctionName[255]; + Q_strcpy( szScriptFunctionName, "Input" ); + Q_strcat( szScriptFunctionName, szInputName, 255 ); + + g_pScriptVM->SetValue( "activator", ( pActivator ) ? ScriptVariant_t( pActivator->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + g_pScriptVM->SetValue( "caller", ( pCaller ) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); +#ifdef MAPBASE_VSCRIPT + Value.SetScriptVariant( functionReturn ); + g_pScriptVM->SetValue( "parameter", functionReturn ); +#endif + + bool bHandled = false; + if( CallScriptFunction( szScriptFunctionName, &functionReturn ) ) + { + bHandled = true; + } + + g_pScriptVM->ClearValue( "activator" ); + g_pScriptVM->ClearValue( "caller" ); +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->ClearValue( "parameter" ); +#endif + + return bHandled; +} + +#ifdef MAPBASE_VSCRIPT +bool CBaseEntity::ScriptAcceptInput( const char *szInputName, const char *szValue, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + variant_t value; + value.SetString( MAKE_STRING(szValue) ); + + return AcceptInput( szInputName, ToEnt(hActivator), ToEnt(hCaller), value, 0 ); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Input handler for the entity alpha. // Input : nAlpha - Alpha value (0 - 255). @@ -4010,6 +4745,17 @@ void CBaseEntity::InputColor( inputdata_t &inputdata ) void CBaseEntity::InputUse( inputdata_t &inputdata ) { Use( inputdata.pActivator, inputdata.pCaller, (USE_TYPE)inputdata.nOutputID, 0 ); + +#ifdef MAPBASE + IGameEvent *event = gameeventmanager->CreateEvent( "player_use" ); + if ( event ) + { + event->SetInt( "userid", inputdata.pActivator && inputdata.pActivator->IsPlayer() ? + ((CBasePlayer*)inputdata.pActivator)->GetUserID() : 0 ); + event->SetInt( "entity", entindex() ); + gameeventmanager->FireEvent( event ); + } +#endif // MAPBASE } @@ -4121,7 +4867,23 @@ void CBaseEntity::InputKill( inputdata_t &inputdata ) SetOwnerEntity( NULL ); } +#ifdef MAPBASE + m_OnKilled.FireOutput( inputdata.pActivator, this ); +#endif + +#ifdef MAPBASE + // Kick players + if ( IsPlayer() ) + { + engine->ServerCommand( UTIL_VarArgs( "kickid %d CBaseEntity::InputKill()\n", engine->GetPlayerUserId(edict()) ) ); + } + else + { + UTIL_Remove( this ); + } +#else UTIL_Remove( this ); +#endif } void CBaseEntity::InputKillHierarchy( inputdata_t &inputdata ) @@ -4141,6 +4903,13 @@ void CBaseEntity::InputKillHierarchy( inputdata_t &inputdata ) SetOwnerEntity( NULL ); } +#ifdef MAPBASE + m_OnKilled.FireOutput( inputdata.pActivator, this ); + + // Kicking players in InputKillHierarchy does not exist in future Valve games + // if ( IsPlayer() ) +#endif + UTIL_Remove( this ); } @@ -4399,6 +5168,37 @@ void CBaseEntity::NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseEntity::DispatchInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ) +{ + if (interactionType <= 0) + return false; + + if (m_ScriptScope.IsInitialized() && g_Hook_HandleInteraction.CanRunInScope( m_ScriptScope )) + { + //HSCRIPT hData = g_pScriptVM->RegisterInstance( data ); + + // interaction, data, sourceEnt + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { interactionType/*, ScriptVariant_t( hData )*/, ScriptVariant_t( ToHScript( sourceEnt ) ) }; + if ( g_Hook_HandleInteraction.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_BOOLEAN) ) + { + // Return the interaction here + //g_pScriptVM->RemoveInstance( hData ); + return functionReturn.m_bool; + } + + //g_pScriptVM->RemoveInstance( hData ); + } + + return HandleInteraction( interactionType, data, sourceEnt ); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Holds an entity's previous abs origin and angles at the time of // teleportation. Used for child & constrained entity fixup to prevent @@ -4825,7 +5625,7 @@ void CBaseEntity::PrecacheModelComponents( int nModelIndex ) { char token[256]; const char *pOptions = pEvent->pszOptions(); - nexttoken( token, pOptions, ' ' ); + nexttoken( token, pOptions, ' ', sizeof( token ) ); if ( token[0] ) { PrecacheParticleSystem( token ); @@ -4981,6 +5781,59 @@ void ConsoleFireTargets( CBasePlayer *pPlayer, const char *name) FireTargets( name, pPlayer, pPlayer, USE_TOGGLE, 0 ); } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose : More concommands needed access to entities, so this has been moved to its own function. +// Input : cmdname - The name of the command. +// &commands - Where the complete autocompletes should be sent to. +// substring - The current search query. (only pool entities that start with this) +// checklen - The number of characters to check. +// Output : A pointer to a cUtlRBTRee containing all of the entities. +//------------------------------------------------------------------------------ +static int AutoCompleteEntities(const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0) +{ + CBaseEntity *pos = NULL; + while ((pos = gEntList.NextEnt(pos)) != NULL) + { + const char *name = pos->GetClassname(); + if (pos->GetEntityName() == NULL_STRING || Q_strnicmp(STRING(pos->GetEntityName()), substring, checklen)) + { + if (Q_strnicmp(pos->GetClassname(), substring, checklen)) + continue; + } + else + name = STRING(pos->GetEntityName()); + + CUtlString sym = name; + int idx = symbols.Find(sym); + if (idx == symbols.InvalidIndex()) + { + symbols.Insert(sym); + } + + // Too many + if (symbols.Count() >= COMMAND_COMPLETION_MAXITEMS) + break; + } + + // Now fill in the results + for (int i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder(i)) + { + const char *name = symbols[i].String(); + + char buf[512]; + Q_strncpy(buf, name, sizeof(buf)); + Q_strlower(buf); + + CUtlString command; + command = CFmtStr("%s %s", cmdname, buf); + commands.AddToTail(command); + } + + return symbols.Count(); +} +#endif + //------------------------------------------------------------------------------ // Purpose : // Input : @@ -4992,12 +5845,77 @@ void CC_Ent_Name( const CCommand& args ) } static ConCommand ent_name("ent_name", CC_Ent_Name, 0, FCVAR_CHEAT); + //------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DumpScriptScope(CBasePlayer* pPlayer, const char* name) +{ + CBaseEntity* pEntity = NULL; + while ((pEntity = GetNextCommandEntity(pPlayer, name, pEntity)) != NULL) + { + if (pEntity->m_ScriptScope.IsInitialized()) + { + Msg("----Script Dump for entity %s\n", pEntity->GetDebugName()); + HSCRIPT hDumpScopeFunc = g_pScriptVM->LookupFunction("__DumpScope"); + g_pScriptVM->Call(hDumpScopeFunc, NULL, true, NULL, 1, (HSCRIPT)pEntity->m_ScriptScope); + Msg("----End Script Dump\n"); + } + else + { + DevWarning("ent_script_dump: Entity %s has no script scope!\n", pEntity->GetDebugName()); + } + } +} + +//------------------------------------------------------------------------------ +void CC_Ent_Script_Dump( const CCommand& args ) +{ + DumpScriptScope(UTIL_GetCommandClient(),args[1]); +} +static ConCommand ent_script_dump("ent_script_dump", CC_Ent_Script_Dump, "Dumps the names and values of this entity's script scope to the console\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT); + + +//------------------------------------------------------------------------------ +#ifdef MAPBASE +class CEntTextAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback +{ +public: + virtual void CommandCallback( const CCommand &command ) + { + SetDebugBits(UTIL_GetCommandClient(), command.Arg(1), OVERLAY_TEXT_BIT); + } + + virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + if ( !g_pGameRules ) + { + return 0; + } + + const char *cmdname = "ent_text"; + + char *substring = (char *)partial; + if ( Q_strstr( partial, cmdname ) ) + { + substring = (char *)partial + strlen( cmdname ) + 1; + } + + int checklen = Q_strlen( substring ); + + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + return AutoCompleteEntities(cmdname, commands, symbols, substring, checklen); + } +}; + +static CEntTextAutoCompletionFunctor g_EntTextAutoComplete; +static ConCommand ent_text("ent_text", &g_EntTextAutoComplete, "Displays text debugging information about the given entity(ies) on top of the entity (See Overlay Text)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT, &g_EntTextAutoComplete); +#else void CC_Ent_Text( const CCommand& args ) { SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_TEXT_BIT); } static ConCommand ent_text("ent_text", CC_Ent_Text, "Displays text debugging information about the given entity(ies) on top of the entity (See Overlay Text)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT); +#endif //------------------------------------------------------------------------------ void CC_Ent_BBox( const CCommand& args ) @@ -5321,6 +6239,8 @@ void CC_Ent_FireTarget( const CCommand& args ) } static ConCommand firetarget("firetarget", CC_Ent_FireTarget, 0, FCVAR_CHEAT); +#ifndef MAPBASE +#endif class CEntFireAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback { public: @@ -5341,7 +6261,11 @@ public: { const char *target = "", *action = "Use"; variant_t value; +#ifdef MAPBASE + float delay = 0; +#else int delay = 0; +#endif target = STRING( AllocPooledString(command.Arg( 1 ) ) ); @@ -5379,7 +6303,11 @@ public: } if ( command.ArgC() >= 5 ) { +#ifdef MAPBASE + delay = atof( command.Arg( 4 ) ); +#else delay = atoi( command.Arg( 4 ) ); +#endif } g_EventQueue.AddEvent( target, action, value, delay, pPlayer, pPlayer ); @@ -5412,6 +6340,10 @@ public: checklen = Q_strlen( substring ); } +#ifdef MAPBASE + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + return AutoCompleteEntities(cmdname, commands, symbols, substring, checklen); +#else CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); CBaseEntity *pos = NULL; @@ -5451,6 +6383,7 @@ public: } return symbols.Count(); +#endif } private: int EntFire_AutoCompleteInput( const char *partial, CUtlVector< CUtlString > &commands ) @@ -5479,7 +6412,12 @@ private: Q_strncat( targetEntity, substring, sizeof( targetEntity ), nEntityNameLength ); // Find the target entity by name +#ifdef MAPBASE + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + CBaseEntity *target = gEntList.FindEntityGeneric( NULL, targetEntity, pPlayer, pPlayer, pPlayer ); +#else CBaseEntity *target = gEntList.FindEntityByName( NULL, targetEntity ); +#endif if ( target == NULL ) return 0; @@ -5506,10 +6444,14 @@ private: if ( !( field->flags & FTYPEDESC_INPUT ) ) continue; +#ifndef MAPBASE // What did input variables ever do to you? + // Only want input functions if ( field->flags & FTYPEDESC_SAVE ) continue; +#endif + // See if we've got a partial string for the input name already if ( inputPartial != NULL ) { @@ -5624,6 +6566,153 @@ void CC_Ent_Info( const CCommand& args ) } static ConCommand ent_info("ent_info", CC_Ent_Info, "Usage:\n ent_info \n", FCVAR_CHEAT); +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +void CC_Ent_Info_Datatable( const CCommand& args ) +{ + CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); + if (!pPlayer) + { + return; + } + + if ( args.ArgC() < 2 ) + { + ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage:\n ent_info_datatable \n" ); + } + else + { + // Each element corresponds to a specific field type. + // Hey, if you've got a better idea, be my guest. + static const char *g_FieldStrings[FIELD_TYPECOUNT] = + { + "VOID", + "FLOAT", + "STRING", + "VECTOR", + "QUATERNION", + "INTEGER", + "BOOLEAN", + "SHORT", + "CHARACTER", + "COLOR32", + "EMBEDDED", + "CUSTOM", + + "CLASSPTR", + "EHANDLE", + "EDICT", + + "POSITION_VECTOR", + "TIME", + "TICK", + "MODELNAME", + "SOUNDNAME", + + "INPUT", + "FUNCTION", + "VMATRIX", + "VMATRIX_WORLDSPACE", + "MATRIX3X4_WORLDSPACE", + "INTERVAL", + "MODELINDEX", + "MATERIALINDEX", + + "VECTOR2D", + }; + + // iterate through all the ents printing out their details + CBaseEntity *ent = CreateEntityByName( args[1] ); + + if ( ent ) + { +#define ENT_INFO_BY_HIERARCHY 1 +#ifdef ENT_INFO_BY_HIERARCHY + CUtlVector dmap_namelist; + + CUtlVector< CUtlVector > dmap_fieldlist; + CUtlVector< CUtlVector > dmap_fieldtypelist; + + datamap_t *dmap; + int dmapnum = 0; + for ( dmap = ent->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + dmap_fieldlist.AddToTail(); + dmap_fieldtypelist.AddToTail(); + + // search through all the actions in the data description, printing out details + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + dmap_fieldlist[dmapnum].AddToTail(dmap->dataDesc[i].fieldName); + dmap_fieldtypelist[dmapnum].AddToTail(dmap->dataDesc[i].fieldType); + } + + dmapnum++; + dmap_namelist.AddToTail(dmap->dataClassName); + } + + char offset[64] = { 0 }; // Needed so garbage isn't spewed at the beginning + for ( int i = 0; i < dmapnum; i++ ) + { + Q_strncat(offset, " ", sizeof(offset)); + + // Header for each class + ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs("%s=========| %s |=========\n", offset, dmap_namelist[i]) ); + + Q_strncat(offset, " ", sizeof(offset)); + + int iFieldCount = dmap_fieldlist[i].Count(); + for ( int index = 0; index < iFieldCount; index++ ) + { + int iType = dmap_fieldtypelist[i][index]; + ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs("%s%s (%i): %s\n", offset, g_FieldStrings[iType], iType, dmap_fieldlist[i][index]) ); + } + + // Clean up after ourselves + dmap_fieldlist[i].RemoveAll(); + dmap_fieldtypelist[i].RemoveAll(); + } +#else // This sorts by field type instead + CUtlVector fieldlist[FIELD_TYPECOUNT]; + + datamap_t *dmap; + for ( dmap = ent->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the actions in the data description, printing out details + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + fieldlist[dmap->dataDesc[i].fieldType].AddToTail(dmap->dataDesc[i].fieldName); + } + } + + for ( int i = 0; i < FIELD_TYPECOUNT; i++ ) + { + const char *typestring = g_FieldStrings[i]; + for ( int index = 0; index < fieldlist[i].Count(); index++ ) + { + ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs(" %s (%i): %s\n", typestring, i, fieldlist[i][index]) ); + } + + // Clean up after ourselves + fieldlist[i].RemoveAll(); + } +#endif + + delete ent; + } + else + { + ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs("no such entity %s\n", args[1]) ); + } + } +} +static ConCommand ent_info_datatable("ent_info_datatable", CC_Ent_Info_Datatable, "Usage:\n ent_info_datatable \n", FCVAR_CHEAT); +#endif + //------------------------------------------------------------------------------ // Purpose : @@ -6324,8 +7413,10 @@ void CBaseEntity::ModifyOrAppendCriteria( AI_CriteriaSet& set ) set.AppendCriteria( szGlobalName, UTIL_VarArgs( "%i", iGlobalState ) ); } +#ifndef MAPBASE // We do this later now so contexts can override criteria. I originally didn't want to remove it here in case there would be problems, but I think I have all of the bases covered. // Append anything from I/O or keyvalues pairs AppendContextToCriteria( set ); +#endif if( hl2_episodic.GetBool() ) { @@ -6333,11 +7424,21 @@ void CBaseEntity::ModifyOrAppendCriteria( AI_CriteriaSet& set ) } // Append anything from world I/O/keyvalues with "world" as prefix +#ifdef MAPBASE + CWorld *world = GetWorldEntity(); +#else CWorld *world = dynamic_cast< CWorld * >( CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) ) ); +#endif if ( world ) { world->AppendContextToCriteria( set, "world" ); } + +#ifdef MAPBASE + // Append base stuff + set.AppendCriteria("spawnflags", UTIL_VarArgs("%i", GetSpawnFlags())); + set.AppendCriteria("flags", UTIL_VarArgs("%i", GetFlags())); +#endif } //----------------------------------------------------------------------------- @@ -6364,6 +7465,29 @@ void CBaseEntity::AppendContextToCriteria( AI_CriteriaSet& set, const char *pref } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// "" - +//----------------------------------------------------------------------------- +void CBaseEntity::ReAppendContextCriteria( AI_CriteriaSet& set ) +{ + // Append contexts again. This allows it to override standard criteria, including that of derived classes. + CWorld *world = GetWorldEntity(); + if ( world ) + { + // I didn't know this until recently, but world contexts are actually prefixed by "world". + // I'm changing this here to reduce confusion and allow greater potential. + // (e.g. disabling combine soldier episodic on/off in Episodic binaries with world context "episodic:0", even though that's not happening anymore) + // The old prefixed ones still exist. They're just not appended again. + world->AppendContextToCriteria( set ); + } + + AppendContextToCriteria( set ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Removes expired concepts from list // Output : @@ -6424,6 +7548,7 @@ const char *CBaseEntity::GetContextValue( int index ) const } return m_ResponseContexts[ index ].m_iszValue.ToCStr(); + } //----------------------------------------------------------------------------- @@ -6464,6 +7589,131 @@ int CBaseEntity::FindContextByName( const char *name ) const return -1; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Searches entity for named context string and/or value. +// Intended to be called by entities rather than the conventional response system. +// Input : *name - Context name. +// *value - Context value. (optional) +// Output : bool +//----------------------------------------------------------------------------- +bool CBaseEntity::HasContext( const char *name, const char *value ) const +{ + int c = m_ResponseContexts.Count(); + for ( int i = 0; i < c; i++ ) + { + if ( Matcher_NamesMatch( name, STRING(m_ResponseContexts[i].m_iszName) ) ) + { + if (value == NULL) + return true; + else + return Matcher_Match(STRING(m_ResponseContexts[i].m_iszValue), value); + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Searches entity for named context string and/or value. +// Intended to be called by entities rather than the conventional response system. +// Input : *name - Context name. +// *value - Context value. (optional) +// Output : bool +//----------------------------------------------------------------------------- +bool CBaseEntity::HasContext( string_t name, string_t value ) const +{ + int c = m_ResponseContexts.Count(); + for ( int i = 0; i < c; i++ ) + { + if ( name == m_ResponseContexts[i].m_iszName ) + { + if (value == NULL_STRING) + return true; + else + return value == m_ResponseContexts[i].m_iszValue; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Searches entity for named context string and/or value. +// Intended to be called by entities rather than the conventional response system. +// Input : *nameandvalue - Context name and value. +// Output : bool +//----------------------------------------------------------------------------- +bool CBaseEntity::HasContext( const char *nameandvalue ) const +{ + char key[ 128 ]; + char value[ 128 ]; + + const char *p = nameandvalue; + while ( p ) + { + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL ); + + return HasContext( key, value ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : const char +//----------------------------------------------------------------------------- +const char *CBaseEntity::GetContextValue( const char *contextName ) const +{ + int idx = FindContextByName( contextName ); + if ( idx == -1 ) + return ""; + + return m_ResponseContexts[ idx ].m_iszValue.ToCStr(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CBaseEntity::GetContextExpireTime( const char *name ) +{ + int idx = FindContextByName( name ); + if ( idx == -1 ) + return 0.0f; + + return m_ResponseContexts[ idx ].m_fExpirationTime; +} + +//----------------------------------------------------------------------------- +// Purpose: Internal method or removing contexts and can remove multiple contexts in one call +// Input : *contextName - +//----------------------------------------------------------------------------- +void CBaseEntity::RemoveContext( const char *contextName ) +{ + char key[ 128 ]; + char value[ 128 ]; + float duration; + + const char *p = contextName; + while ( p ) + { + duration = 0.0f; + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration ); + if ( duration ) + { + duration += gpGlobals->curtime; + } + + int iIndex = FindContextByName( key ); + if ( iIndex != -1 ) + { + m_ResponseContexts.Remove( iIndex ); + } + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : inputdata - @@ -6510,6 +7760,1092 @@ void CBaseEntity::InputFireUser4( inputdata_t& inputdata ) } +#ifdef MAPBASE +void CBaseEntity::InputPassUser1( inputdata_t& inputdata ) +{ + m_OutUser1.Set( inputdata.value, inputdata.pActivator, this ); +} + +void CBaseEntity::InputPassUser2( inputdata_t& inputdata ) +{ + m_OutUser2.Set( inputdata.value, inputdata.pActivator, this ); +} + +void CBaseEntity::InputPassUser3( inputdata_t& inputdata ) +{ + m_OutUser3.Set( inputdata.value, inputdata.pActivator, this ); +} + +void CBaseEntity::InputPassUser4( inputdata_t& inputdata ) +{ + m_OutUser4.Set( inputdata.value, inputdata.pActivator, this ); +} + + +void CBaseEntity::InputFireRandomUser( inputdata_t& inputdata ) +{ + switch (RandomInt(1, 4)) + { + case 1: m_OnUser1.FireOutput( inputdata.pActivator, this ); break; + case 2: m_OnUser2.FireOutput( inputdata.pActivator, this ); break; + case 3: m_OnUser3.FireOutput( inputdata.pActivator, this ); break; + case 4: m_OnUser4.FireOutput( inputdata.pActivator, this ); break; + } +} + +void CBaseEntity::InputPassRandomUser( inputdata_t& inputdata ) +{ + switch (RandomInt(1, 4)) + { + case 1: m_OutUser1.Set( inputdata.value, inputdata.pActivator, this ); break; + case 2: m_OutUser2.Set( inputdata.value, inputdata.pActivator, this ); break; + case 3: m_OutUser3.Set( inputdata.value, inputdata.pActivator, this ); break; + case 4: m_OutUser4.Set( inputdata.value, inputdata.pActivator, this ); break; + } +} +#endif + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets the entity's targetname. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetEntityName( inputdata_t& inputdata ) +{ + SetName( inputdata.value.StringID() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the generic target field. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetTarget( inputdata_t& inputdata ) +{ + m_target = inputdata.value.StringID(); + Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our owner entity. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetOwnerEntity( inputdata_t& inputdata ) +{ + SetOwnerEntity(inputdata.value.Entity()); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for adding to the entity's health. +// Input : Integer health points to add. +//----------------------------------------------------------------------------- +void CBaseEntity::InputAddHealth( inputdata_t &inputdata ) +{ + TakeHealth( abs(inputdata.value.Int()), DMG_GENERIC ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for removing health from the entity. +// Input : Integer health points to remove. +//----------------------------------------------------------------------------- +void CBaseEntity::InputRemoveHealth( inputdata_t &inputdata ) +{ + TakeDamage( CTakeDamageInfo( this, this, abs(inputdata.value.Int()), DMG_GENERIC ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetHealth( inputdata_t &inputdata ) +{ + int iNewHealth = inputdata.value.Int(); + int iDelta = abs(GetHealth() - iNewHealth); + if ( iNewHealth > GetHealth() ) + { + TakeHealth( iDelta, DMG_GENERIC ); + } + else if ( iNewHealth < GetHealth() ) + { + TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetMaxHealth( inputdata_t &inputdata ) +{ + int iNewMaxHealth = inputdata.value.Int(); + SetMaxHealth(iNewMaxHealth); + + if (GetHealth() > iNewMaxHealth) + { + SetHealth(iNewMaxHealth); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Forces the named output to fire. +// In addition to the output itself, parameter may include !activator, !caller, and what to pass. +//----------------------------------------------------------------------------- +void CBaseEntity::InputFireOutput( inputdata_t& inputdata ) +{ + char sParameter[MAX_PATH]; + Q_strncpy( sParameter, inputdata.value.String(), sizeof(sParameter) ); + if ( sParameter ) + { + int iter = 0; + char *data[5] = {sParameter}; + char *sToken = strtok( sParameter, ":" ); + while ( sToken && iter < 5 ) + { + data[iter] = sToken; + iter++; + sToken = strtok( NULL, ":" ); + } + + //DevMsg("data[0]: %s\ndata[1]: %s\ndata[2]: %s\ndata[3]: %s\ndata[4]: %s\n", data[0], data[1], data[2], data[3], data[4]); + + // Format: :::: + // + // data[0] = Output Name + // data[1] = Activator + // data[2] = Caller + // data[3] = Parameter + // data[4] = Delay + // + CBaseEntity *pActivator = inputdata.pActivator; + if (data[1]) + pActivator = gEntList.FindEntityByName(NULL, data[1], this, inputdata.pActivator, inputdata.pCaller); + + CBaseEntity *pCaller = this; + if (data[2]) + pCaller = gEntList.FindEntityByName(NULL, data[2], this, inputdata.pActivator, inputdata.pCaller); + + variant_t parameter; + if (data[3]) + { + parameter.SetString(MAKE_STRING(data[3])); + } + + float flDelay = 0.0f; + if (data[4]) + flDelay = atof(data[4]); + + FireNamedOutput(data[0], parameter, pActivator, pCaller, flDelay); + //Msg("Output Name: %s, Activator: %s, Caller: %s, Data: %s, Delay: %f\n", data[0], pActivator->GetDebugName(), pCaller->GetDebugName(), parameter.String(), flDelay); + } + else + { + Warning("FireOutput input fired with bad parameter. Format: ::::\n"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Removes all outputs of the specified name. +//----------------------------------------------------------------------------- +void CBaseEntity::InputRemoveOutput( inputdata_t& inputdata ) +{ + const char *szOutput = inputdata.value.String(); + datamap_t *dmap = GetDataDescMap(); + while ( dmap ) + { + int fields = dmap->dataNumFields; + for ( int i = 0; i < fields; i++ ) + { + typedescription_t *dataDesc = &dmap->dataDesc[i]; + if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) ) + { + // If our names match, remove + if (Matcher_NamesMatch(szOutput, dataDesc->externalName)) + { + CBaseEntityOutput *pOutput = (CBaseEntityOutput *)((int)this + (int)dataDesc->fieldOffset[0]); + pOutput->DeleteAllElements(); + } + } + } + + dmap = dmap->baseMap; + } +} + +// Find a way to implement this +/* +//------------------------------------------------------------------------------ +// Purpose: Cancels all I/O events of a specific output. +//------------------------------------------------------------------------------ +void CBaseEntity::InputCancelOutput( inputdata_t &inputdata ) +{ + +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: Replaces all outputs of the specified name. +//----------------------------------------------------------------------------- +void CBaseEntity::InputReplaceOutput( inputdata_t& inputdata ) +{ + char sParameter[128]; + Q_strncpy( sParameter, inputdata.value.String(), sizeof(sParameter) ); + if (!sParameter) + return; + + int iter = 0; + char *data[2]; + char *sToken = strtok( sParameter, ": " ); + while ( sToken && iter < 2 ) + { + data[iter] = sToken; + iter++; + sToken = strtok( NULL, ": " ); + } + + const char *szOutput = data[0]; + const char *szNewOutput = data[1]; + if (!szOutput || !szNewOutput) + { + Warning("ReplaceOutput input fired with bad parameter. Format: :\n"); + return; + } + + int iOutputsReplaced = 0; + datamap_t *dmap = GetDataDescMap(); + while ( dmap ) + { + int fields = dmap->dataNumFields; + for ( int i = 0; i < fields; i++ ) + { + typedescription_t *dataDesc = &dmap->dataDesc[i]; + if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) ) + { + // If our names match, replace + if (Matcher_NamesMatch(szOutput, dataDesc->externalName)) + { + CBaseEntityOutput *pOutput = (CBaseEntityOutput *)((int)this + (int)dataDesc->fieldOffset[0]); + const char *szTarget; + const char *szInputName; + const char *szParam; + float flDelay; + int iNumTimes; + char szData[256]; + for ( CEventAction *ev = pOutput->GetActionList(); ev != NULL; ev = ev->m_pNext ) + { + // This is the only way I think we could do this. Accomplishes the job more or less anyway + szTarget = STRING(ev->m_iTarget); + szInputName = STRING(ev->m_iTargetInput); + szParam = ev->m_iParameter == NULL_STRING ? "" : STRING(ev->m_iParameter); + flDelay = ev->m_flDelay; + iNumTimes = ev->m_nTimesToFire; + Q_snprintf(szData, sizeof(szData), "%s,%s,%s,%f,%i", szTarget, szInputName, szParam, flDelay, iNumTimes); + + KeyValue(szNewOutput, szData); + + DevMsg("ReplaceOutput: %s %s\n", szNewOutput, szData); + + iOutputsReplaced++; + } + pOutput->DeleteAllElements(); + } + } + } + + dmap = dmap->baseMap; + } + + if (iOutputsReplaced == 0) + { + Warning("ReplaceOutput unable to find %s on %s\n", szOutput, GetDebugName()); + } + else + { + DevMsg("Replaced %i instances of %s with %s on %s\n", iOutputsReplaced, szOutput, szNewOutput, GetDebugName()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Forces the named input to fire...what? +// Inputception...or is it just "Inception"? Whatever. +// True inception would be using this input to fire AcceptInput. +// (it would probably crash, I haven't tested it) +// +// In addition to the input itself, parameter may include !activator, !caller, and what to pass. +//----------------------------------------------------------------------------- +void CBaseEntity::InputAcceptInput( inputdata_t& inputdata ) +{ + char sParameter[MAX_PATH]; + Q_strncpy( sParameter, inputdata.value.String(), sizeof(sParameter) ); + if ( sParameter ) + { + int iter = 0; + char *data[5] = {sParameter}; + char *sToken = strtok( sParameter, ":" ); + while ( sToken && iter < 5 ) + { + data[iter] = sToken; + iter++; + sToken = strtok( NULL, ":" ); + } + + //DevMsg("data[0]: %s\ndata[1]: %s\ndata[2]: %s\ndata[3]: %s\ndata[4]: %s\n", data[0], data[1], data[2], data[3], data[4]); + + // Format: :::: + // + // data[0] = Input Name + // data[1] = Parameter + // data[2] = Activator + // data[3] = Caller + // data[4] = Output ID + // + variant_t parameter; + if (data[1]) + { + parameter.SetString(MAKE_STRING(data[1])); + } + + CBaseEntity *pActivator = inputdata.pActivator; + if (data[2]) + pActivator = gEntList.FindEntityByName(NULL, data[2], this, inputdata.pActivator, inputdata.pCaller); + + CBaseEntity *pCaller = this; + if (data[3]) + pCaller = gEntList.FindEntityByName(NULL, data[3], this, inputdata.pActivator, inputdata.pCaller); + + int iOutputID = -1; + if (data[4]) + iOutputID = atoi(data[4]); + + AcceptInput(data[0], pActivator, pCaller, parameter, iOutputID); + Msg("Input Name: %s, Activator: %s, Caller: %s, Data: %s, Output ID: %i\n", data[0], pActivator ? pActivator->GetDebugName() : "None", pCaller ? pCaller->GetDebugName() : "None", parameter.String(), iOutputID); + } + else + { + Warning("AcceptInput input fired with bad parameter. Format: ::::\n"); + } +} + +//------------------------------------------------------------------------------ +// Purpose: Cancels any I/O events in the queue that were fired by this entity. +//------------------------------------------------------------------------------ +void CBaseEntity::InputCancelPending( inputdata_t &inputdata ) +{ + g_EventQueue.CancelEvents( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Frees all of our children, entities parented to this entity. +//----------------------------------------------------------------------------- +void CBaseEntity::InputFreeChildren( inputdata_t& inputdata ) +{ + UnlinkAllChildren( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our origin. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetLocalOrigin( inputdata_t& inputdata ) +{ + Vector vec; + inputdata.value.Vector3D(vec); + SetLocalOrigin(vec); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our angles. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetLocalAngles( inputdata_t& inputdata ) +{ + QAngle ang; + inputdata.value.Angle3D(ang); + SetLocalAngles(ang); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our origin. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetAbsOrigin( inputdata_t& inputdata ) +{ + Vector vec; + inputdata.value.Vector3D(vec); + SetAbsOrigin(vec); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our angles. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetAbsAngles( inputdata_t& inputdata ) +{ + QAngle ang; + inputdata.value.Angle3D(ang); + SetAbsAngles(ang); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our velocity. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetLocalVelocity( inputdata_t& inputdata ) +{ + Vector vec; + inputdata.value.Vector3D(vec); + SetLocalVelocity(vec); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our angular velocity. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetLocalAngularVelocity( inputdata_t& inputdata ) +{ + Vector vec; + inputdata.value.Vector3D(vec); + SetLocalAngularVelocity(QAngle(vec.x, vec.y, vec.z)); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds spawn flags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputAddSpawnFlags( inputdata_t& inputdata ) +{ + AddSpawnFlags(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes spawn flags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputRemoveSpawnFlags( inputdata_t& inputdata ) +{ + RemoveSpawnFlags(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our render mode. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetRenderMode( inputdata_t& inputdata ) +{ + SetRenderMode((RenderMode_t)inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our render FX. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetRenderFX( inputdata_t& inputdata ) +{ + m_nRenderFX = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our view hide flags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetViewHideFlags( inputdata_t& inputdata ) +{ + m_iViewHideFlags = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds effects. +//----------------------------------------------------------------------------- +void CBaseEntity::InputAddEffects( inputdata_t& inputdata ) +{ + AddEffects(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes effects. +//----------------------------------------------------------------------------- +void CBaseEntity::InputRemoveEffects( inputdata_t& inputdata ) +{ + RemoveEffects(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut for removing nodraw. +//----------------------------------------------------------------------------- +void CBaseEntity::InputDrawEntity( inputdata_t& inputdata ) +{ + RemoveEffects(EF_NODRAW); +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut to adding nodraw. +//----------------------------------------------------------------------------- +void CBaseEntity::InputUndrawEntity( inputdata_t& inputdata ) +{ + AddEffects(EF_NODRAW); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds eflags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputAddEFlags( inputdata_t& inputdata ) +{ + AddEFlags(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes eflags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputRemoveEFlags( inputdata_t& inputdata ) +{ + RemoveEFlags(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds solid flags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputAddSolidFlags( inputdata_t& inputdata ) +{ + AddSolidFlags(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes solid flags. +//----------------------------------------------------------------------------- +void CBaseEntity::InputRemoveSolidFlags( inputdata_t& inputdata ) +{ + RemoveSolidFlags(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the movetype. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetMoveType( inputdata_t& inputdata ) +{ + SetMoveType((MoveType_t)inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the collision group. +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetCollisionGroup( inputdata_t& inputdata ) +{ + SetCollisionGroup(inputdata.value.Int()); +} + +//----------------------------------------------------------------------------- +// Purpose: Touch touch :) +//----------------------------------------------------------------------------- +void CBaseEntity::InputTouch( inputdata_t& inputdata ) +{ + if (inputdata.value.Entity()) + Touch( inputdata.value.Entity() ); + else + Warning( "%s InputTouch: Can't touch null entity", GetDebugName() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Passes KilledNPC to our possibly more capable parents. +//----------------------------------------------------------------------------- +void CBaseEntity::InputKilledNPC( inputdata_t &inputdata ) +{ + // Don't get stuck in an endless loop + if (inputdata.value.Int() > 16) + return; + else + inputdata.value.SetInt(inputdata.value.Int() + 1); + + if (GetOwnerEntity()) + { + GetOwnerEntity()->AcceptInput("KilledNPC", inputdata.pActivator, inputdata.pCaller, inputdata.value, inputdata.nOutputID); + } + else if (HasPhysicsAttacker(4.0f)) + { + HasPhysicsAttacker(4.0f)->AcceptInput("KilledNPC", inputdata.pActivator, inputdata.pCaller, inputdata.value, inputdata.nOutputID); + } + else if (GetMoveParent()) + { + GetMoveParent()->AcceptInput("KilledNPC", inputdata.pActivator, inputdata.pCaller, inputdata.value, inputdata.nOutputID); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Remove if not visible by any players +//----------------------------------------------------------------------------- +void CBaseEntity::InputKillIfNotVisible( inputdata_t& inputdata ) +{ +#ifdef MAPBASE_MP + // Go through each client and check if we're in their viewcone. + // If we're in someone's viewcone, return immediately. + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer && pPlayer->FInViewCone( this ) ) + return; + } +#else + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( !pPlayer || !pPlayer->FInViewCone( this ) ) +#endif + { + m_OnKilled.FireOutput(inputdata.pActivator, this); + UTIL_Remove(this); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Remove when not visible by any players +//----------------------------------------------------------------------------- +void CBaseEntity::InputKillWhenNotVisible( inputdata_t& inputdata ) +{ + SetContextThink( &CBaseEntity::SUB_RemoveWhenNotVisible, gpGlobals->curtime + inputdata.value.Float(), "SUB_RemoveWhenNotVisible" ); + //SetRenderColorA( 255 ); + //m_nRenderMode = kRenderNormal; +} + +//----------------------------------------------------------------------------- +// Purpose: Stop thinking +//----------------------------------------------------------------------------- +void CBaseEntity::InputSetThinkNull( inputdata_t& inputdata ) +{ + const char *szContext = inputdata.value.String(); + if (szContext && szContext[0] != '\0') + { + SetContextThink( NULL, TICK_NEVER_THINK, szContext ); + } + else + { + SetThink( NULL ); + SetNextThink( TICK_NEVER_THINK ); + } +} +#endif + + +//--------------------------------------------------------- +// Use the string as the filename of a script file +// that should be loaded from disk, compiled, and run. +//--------------------------------------------------------- +void CBaseEntity::InputRunScriptFile(inputdata_t& inputdata) +{ + RunScriptFile(inputdata.value.String()); +} + +//--------------------------------------------------------- +// Send the string to the VM as source code and execute it +//--------------------------------------------------------- +void CBaseEntity::InputRunScript(inputdata_t& inputdata) +{ + RunScript(inputdata.value.String(), "InputRunScript"); +} + +//--------------------------------------------------------- +// Make an explicit function call. +//--------------------------------------------------------- +void CBaseEntity::InputCallScriptFunction(inputdata_t& inputdata) +{ + CallScriptFunction(inputdata.value.String(), NULL); +} + +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +// Send the string to the VM as source code and execute it +//--------------------------------------------------------- +void CBaseEntity::InputRunScriptQuotable(inputdata_t& inputdata) +{ + char szQuotableCode[1024]; + if (V_StrSubst( inputdata.value.String(), "''", "\"", szQuotableCode, sizeof( szQuotableCode ), false )) + { + RunScript( szQuotableCode, "InputRunScriptQuotable" ); + } + else + { + RunScript( inputdata.value.String(), "InputRunScriptQuotable" ); + } +} + +//--------------------------------------------------------- +// Clear this entity's script scope +//--------------------------------------------------------- +void CBaseEntity::InputClearScriptScope(inputdata_t& inputdata) +{ + m_ScriptScope.Term(); +} +#endif + +// #define VMPROFILE // define to profile vscript calls + +#ifdef VMPROFILE +float g_debugCumulativeTime = 0.0; +float g_debugCounter = 0; + +#define START_VMPROFILE float debugStartTime = Plat_FloatTime(); +#define UPDATE_VMPROFILE \ + g_debugCumulativeTime += Plat_FloatTime() - debugStartTime; \ + g_debugCounter++; \ + if ( g_debugCounter >= 500 ) \ + { \ + DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", "500 vscript function calls", "", g_debugCumulativeTime*1000.0 ); \ + g_debugCounter = 0; \ + g_debugCumulativeTime = 0.0; \ + } \ + +#else + +#define START_VMPROFILE +#define UPDATE_VMPROFILE + +#endif // VMPROFILE + +//----------------------------------------------------------------------------- +// Returns true if the function was located and called. false otherwise. +// NOTE: Assumes the function takes no parameters at the moment. +//----------------------------------------------------------------------------- +bool CBaseEntity::CallScriptFunction(const char* pFunctionName, ScriptVariant_t* pFunctionReturn) +{ + START_VMPROFILE + + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + + HSCRIPT hFunc = m_ScriptScope.LookupFunction(pFunctionName); + + if (hFunc) + { + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + UPDATE_VMPROFILE + + return true; + } + + return false; +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Gets a function handle +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::LookupScriptFunction(const char* pFunctionName) +{ + START_VMPROFILE + + if (!m_ScriptScope.IsInitialized()) + { + return NULL; + } + + return m_ScriptScope.LookupFunction(pFunctionName); +} + +//----------------------------------------------------------------------------- +// Calls and releases a function handle (ASSUMES SCRIPT SCOPE AND FUNCTION ARE VALID!) +//----------------------------------------------------------------------------- +bool CBaseEntity::CallScriptFunctionHandle(HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn) +{ + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + UPDATE_VMPROFILE + + return true; +} +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ConnectOutputToScript(const char* pszOutput, const char* pszScriptFunc) +{ + CBaseEntityOutput* pOutput = FindNamedOutput(pszOutput); + if (!pOutput) + { + DevMsg(2, "Script failed to find output \"%s\"\n", pszOutput); + return; + } + + string_t iszSelf = AllocPooledString("!self"); // @TODO: cache this [4/25/2008 tom] + CEventAction* pAction = pOutput->GetActionList(); + while (pAction) + { + if (pAction->m_iTarget == iszSelf && + pAction->m_flDelay == 0 && + pAction->m_nTimesToFire == EVENT_FIRE_ALWAYS && + V_strcmp(STRING(pAction->m_iTargetInput), "CallScriptFunction") == 0 && + V_strcmp(STRING(pAction->m_iParameter), pszScriptFunc) == 0) + { + return; + } + pAction = pAction->m_pNext; + } + + pAction = new CEventAction(NULL); + pAction->m_iTarget = iszSelf; + pAction->m_iTargetInput = AllocPooledString("CallScriptFunction"); + pAction->m_iParameter = AllocPooledString(pszScriptFunc); + pOutput->AddEventAction(pAction); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::DisconnectOutputFromScript(const char* pszOutput, const char* pszScriptFunc) +{ + CBaseEntityOutput* pOutput = FindNamedOutput(pszOutput); + if (!pOutput) + { + DevMsg(2, "Script failed to find output \"%s\"\n", pszOutput); + return; + } + + string_t iszSelf = AllocPooledString("!self"); // @TODO: cache this [4/25/2008 tom] + CEventAction* pAction = pOutput->GetActionList(); + while (pAction) + { + if (pAction->m_iTarget == iszSelf && + pAction->m_flDelay == 0 && + pAction->m_nTimesToFire == EVENT_FIRE_ALWAYS && + V_strcmp(STRING(pAction->m_iTargetInput), "CallScriptFunction") == 0 && + V_strcmp(STRING(pAction->m_iParameter), pszScriptFunc) == 0) + { + pOutput->RemoveEventAction(pAction); + delete pAction; + return; + } + pAction = pAction->m_pNext; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptThink(void) +{ + ScriptVariant_t varThinkRetVal; + if (CallScriptFunction(m_iszScriptThinkFunction.ToCStr(), &varThinkRetVal)) + { + float flThinkFrequency = 0.0f; + if (!varThinkRetVal.AssignTo(&flThinkFrequency)) + { + // use default think interval if script think function doesn't provide one + flThinkFrequency = sv_script_think_interval.GetFloat(); + } + + SetNextThink( gpGlobals->curtime + flThinkFrequency, "ScriptThink" ); + } + else + { + DevWarning("%s FAILED to call script think function %s!\n", GetDebugName(), STRING(m_iszScriptThinkFunction)); + } +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetThinkFunction( const char *szFunc, float flTime ) +{ + // Empty string stops thinking + if (!szFunc || szFunc[0] == '\0') + { + ScriptStopThinkFunction(); + } + else + { + m_iszScriptThinkFunction = AllocPooledString(szFunc); + flTime = max( 0, flTime ); + SetContextThink( &CBaseEntity::ScriptThink, gpGlobals->curtime + flTime, "ScriptThink" ); + } +} + +void CBaseEntity::ScriptStopThinkFunction() +{ + m_iszScriptThinkFunction = NULL_STRING; + SetContextThink( NULL, TICK_NEVER_THINK, "ScriptThink" ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptThinkH() +{ + ScriptVariant_t varThinkRetVal; + if ( g_pScriptVM->ExecuteFunction(m_hfnThink, NULL, 0, &varThinkRetVal, NULL, true) == SCRIPT_ERROR ) + { + DevWarning("%s FAILED to call script think function (invalid closure)!\n", GetDebugName()); + ScriptStopThink(); + return; + } + + float flThinkFrequency = 0.f; + if ( !varThinkRetVal.AssignTo(&flThinkFrequency) ) + { + // no return value stops thinking + ScriptStopThink(); + return; + } + + SetNextThink( gpGlobals->curtime + flThinkFrequency, "ScriptThinkH" ); +} + +void CBaseEntity::ScriptSetThink( HSCRIPT hFunc, float flTime ) +{ + if ( hFunc ) + { + if ( m_hfnThink ) + { + // release old func + ScriptStopThink(); + } + + // no type check here, print error on call instead + m_hfnThink = hFunc; + + flTime = max( 0, flTime ); + SetContextThink( &CBaseEntity::ScriptThinkH, gpGlobals->curtime + flTime, "ScriptThinkH" ); + } + else + { + ScriptStopThink(); + } +} + +void CBaseEntity::ScriptStopThink() +{ + if (m_hfnThink) + { + g_pScriptVM->ReleaseScript(m_hfnThink); + m_hfnThink = NULL; + } + SetContextThink( NULL, TICK_NEVER_THINK, "ScriptThinkH" ); +} +#endif // MAPBASE_VSCRIPT + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char* CBaseEntity::GetScriptId() +{ +#ifdef MAPBASE_VSCRIPT + return STRING(m_iszScriptId); +#else + return STRING(m_iszScriptThinkFunction); +#endif +} + +//----------------------------------------------------------------------------- +// Recreate the old behaviour of GetScriptId under a new function +//----------------------------------------------------------------------------- +// const char* CBaseEntity::GetScriptThinkFunction() +// { +// return STRING(m_iszScriptThinkFunction); +// } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::GetScriptScope() +{ + return m_ScriptScope; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#ifdef MAPBASE_VSCRIPT +HSCRIPT CBaseEntity::GetOrCreatePrivateScriptScope() +{ + ValidateScriptScope(); + return m_ScriptScope; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetParent(HSCRIPT hParent, const char *szAttachment) +{ + CBaseEntity *pParent = ToEnt(hParent); + if ( !pParent ) + { + SetParent(NULL); + return; + } + + // if an attachment is specified, the parent needs to be CBaseAnimating + if ( szAttachment && szAttachment[0] != '\0' ) + { + CBaseAnimating *pAnimating = pParent->GetBaseAnimating(); + if ( !pAnimating ) + { + Warning("ERROR: Tried to set parent for entity %s (%s), but its parent has no model.\n", GetClassname(), GetDebugName()); + return; + } + + int iAttachment = pAnimating->LookupAttachment(szAttachment); + if ( iAttachment <= 0 ) + { + Warning("ERROR: Tried to set parent for entity %s (%s), but it has no attachment named %s.\n", GetClassname(), GetDebugName(), szAttachment); + return; + } + + SetParent(pParent, iAttachment); + SetMoveType(MOVETYPE_NONE); + return; + } + + SetParent(pParent); +} +#endif +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetMoveParent(void) +{ + return ToHScript(GetMoveParent()); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript(GetRootMoveParent()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptFirstMoveChild(void) +{ + return ToHScript(FirstMoveChild()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptNextMovePeer(void) +{ + return ToHScript(NextMovePeer()); +} + +//----------------------------------------------------------------------------- +// Purpose: Load, compile, and run a script file from disk. +// Input : *pScriptFile - The filename of the script file. +// bUseRootScope - If true, runs this script in the root scope, not +// in this entity's private scope. +//----------------------------------------------------------------------------- +bool CBaseEntity::RunScriptFile(const char* pScriptFile, bool bUseRootScope) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (bUseRootScope) + { + return VScriptRunScript(pScriptFile); + } + else + { + return VScriptRunScript(pScriptFile, m_ScriptScope, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Compile and execute a discrete string of script source code +// Input : *pScriptText - A string containing script code to compile and run +//----------------------------------------------------------------------------- +bool CBaseEntity::RunScript(const char* pScriptText, const char* pDebugFilename) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (m_ScriptScope.Run(pScriptText, pDebugFilename) == SCRIPT_ERROR) + { + DevWarning(" Entity %s encountered an error in RunScript()\n", GetDebugName()); + } + + return true; +} + //----------------------------------------------------------------------------- // Purpose: // Input : *contextName - @@ -6548,6 +8884,27 @@ void CBaseEntity::AddContext( const char *contextName ) } } +#ifdef MAPBASE +void CBaseEntity::AddContext( const char *name, const char *value, float duration ) +{ + int iIndex = FindContextByName( name ); + if ( iIndex != -1 ) + { + // Set the existing context to the new value + m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value ); + m_ResponseContexts[iIndex].m_fExpirationTime = duration; + return; + } + + ResponseContext_t newContext; + newContext.m_iszName = AllocPooledString( name ); + newContext.m_iszValue = AllocPooledString( value ); + newContext.m_fExpirationTime = duration; + + m_ResponseContexts.AddToTail( newContext ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : inputdata - @@ -6631,6 +8988,67 @@ void CBaseEntity::InputAddOutput( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CBaseEntity::InputChangeVariable( inputdata_t &inputdata ) +{ + const char *szKeyName = NULL; + const char *szValue = NULL; + + char sOutputName[MAX_PATH]; + Q_strncpy( sOutputName, inputdata.value.String(), sizeof(sOutputName) ); + char *sChar = strchr( sOutputName, ' ' ); + if ( sChar ) + { + *sChar = '\0'; + // Now replace all the :'s in the string with ,'s. + // Has to be done this way because Hammer doesn't allow ,'s inside parameters. + char *sColon = strchr( sChar+1, ':' ); + while ( sColon ) + { + *sColon = ','; + sColon = strchr( sChar+1, ':' ); + } + + szKeyName = sOutputName; + szValue = sChar + 1; + } + else + { + Warning("ChangeVariable input fired with bad string. Format: ,,,,\n"); + return; + } + + if (szKeyName == NULL) + return; + + for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_SAVE | FTYPEDESC_KEY) ) + { + if ( Matcher_NamesMatch(szKeyName, dmap->dataDesc[i].fieldName) ) + { + // Copied from ::ParseKeyvalue...or technically logic_datadesc_accessor... + typedescription_t *pField = &dmap->dataDesc[i]; + char *data = Datadesc_SetFieldString( szValue, this, pField, NULL ); + + if (!data) + { + Warning( "%s cannot set field of type %i.\n", GetDebugName(), dmap->dataDesc[i].fieldType ); + } + } + } + } + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *conceptName - @@ -6652,6 +9070,10 @@ void CBaseEntity::DispatchResponse( const char *conceptName ) if( pPlayer ) pPlayer->ModifyOrAppendPlayerCriteria( set ); +#ifdef MAPBASE + ReAppendContextCriteria( set ); +#endif + // Now that we have a criteria set, ask for a suitable response AI_Response result; bool found = rs->FindBestResponse( set, result ); @@ -6660,6 +9082,36 @@ void CBaseEntity::DispatchResponse( const char *conceptName ) // Handle the response here... const char *szResponse = result.GetResponsePtr(); +#ifdef MAPBASE + if (szResponse[0] == '$') + { + const char *context = szResponse + 1; + const char *replace = GetContextValue(context); + + if (replace) + { + DevMsg("Replacing %s with %s...\n", szResponse, replace); + szResponse = replace; + + // Precache it now because it may not have been precached before + switch ( result.GetType() ) + { + case RESPONSE_SPEAK: + { + PrecacheScriptSound( szResponse ); + } + break; + + case RESPONSE_SCENE: + { + // TODO: Gender handling? + PrecacheInstancedScene( szResponse ); + } + break; + } + } + } +#endif switch ( result.GetType() ) { case RESPONSE_SPEAK: @@ -6668,6 +9120,13 @@ void CBaseEntity::DispatchResponse( const char *conceptName ) case RESPONSE_SENTENCE: { +#ifdef MAPBASE + if (szResponse[0] != '!') + { + SENTENCEG_PlayRndSz( edict(), szResponse, 1, result.GetSoundLevel(), 0, PITCH_NORM ); + break; + } +#endif int sentenceIndex = SENTENCEG_Lookup( szResponse ); if( sentenceIndex == -1 ) { @@ -6682,8 +9141,29 @@ void CBaseEntity::DispatchResponse( const char *conceptName ) break; case RESPONSE_SCENE: +#ifdef MAPBASE + // Most flexing actors that use scenes override DispatchResponse via CAI_Expresser in ai_speech. + // So, in order for non-actors to use scenes by themselves, they actually don't really use them at all. + // Most scenes that would be used as responses only have one sound, so we take the first sound in a scene and emit it manually. + // + // Of course, env_speaker uses scenes without using itself as an actor, but that overrides DispatchResponse in Mapbase + // with the original code intact. Hopefully no other entity uses this like that... + + //if (!ClassMatches("env_speaker")) + { + // Expand gender string + GenderExpandString( szResponse, const_cast(szResponse), AI_Response::MAX_RESPONSE_NAME ); + + // Trust that it's been precached + const char *pszSound = GetFirstSoundInScene(szResponse); + EmitSound(pszSound); + } + //else + // InstancedScriptedScene(NULL, response); +#else // Try to fire scene w/o an actor InstancedScriptedScene( NULL, szResponse ); +#endif break; case RESPONSE_PRINT: @@ -6713,6 +9193,10 @@ void CBaseEntity::DumpResponseCriteria( void ) pPlayer->ModifyOrAppendPlayerCriteria( set ); } +#ifdef MAPBASE + ReAppendContextCriteria( set ); +#endif + // Now dump it all to console set.Describe(); } @@ -7217,6 +9701,11 @@ void CBaseEntity::SUB_FadeOut( void ) if ( m_clrRender->a == 0 ) { +#ifdef MAPBASE + // This was meant for KillWhenNotVisible before it used its own function, + // but there's not really any harm for keeping this here. + m_OnKilled.FireOutput(this, this); +#endif UTIL_Remove(this); } else @@ -7225,6 +9714,33 @@ void CBaseEntity::SUB_FadeOut( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: For KillWhenNotVisible, based off of SUB_FadeOut +//----------------------------------------------------------------------------- +void CBaseEntity::SUB_RemoveWhenNotVisible( void ) +{ + if ( SUB_AllowedToFade() == false ) + { + SetNextThink( gpGlobals->curtime + 1, "SUB_RemoveWhenNotVisible" ); + SetRenderColorA( 255 ); + return; + } + + SetRenderColorA( m_clrRender->a - 1 ); + + if ( m_clrRender->a == 0 ) + { + m_OnKilled.FireOutput(this, this); + UTIL_Remove(this); + } + else + { + SetNextThink( gpGlobals->curtime, "SUB_RemoveWhenNotVisible" ); + } +} +#endif + inline bool AnyPlayersInHierarchy_R( CBaseEntity *pEnt ) { @@ -7299,6 +9815,561 @@ void CBaseEntity::SetCollisionBoundsFromModel() } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName.Get() != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + +//----------------------------------------------------------------------------- +// Using my edict, cook up a unique VScript scope that's private to me, and +// persistent. +//----------------------------------------------------------------------------- +bool CBaseEntity::ValidateScriptScope() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + // Force instance creation + GetScriptInstance(); + + EHANDLE hThis; + hThis.Set(this); + + bool bResult = m_ScriptScope.Init(STRING(m_iszScriptId)); + + if (!bResult) + { + DevMsg("%s couldn't create ScriptScope!\n", GetDebugName()); + return false; + } + g_pScriptVM->SetValue(m_ScriptScope, "self", GetScriptInstance()); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Run all of the vscript files that are set in this entity's VSCRIPTS +// field in Hammer. The list is space-delimited. +//----------------------------------------------------------------------------- +void CBaseEntity::RunVScripts() +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM == NULL) + { + return; + } +#endif + + ValidateScriptScope(); + + // All functions we want to have call chained instead of overwritten + // by other scripts in this entities list. + static const char* sCallChainFunctions[] = + { + "OnPostSpawn", + "Precache" + }; + + ScriptLanguage_t language = g_pScriptVM->GetLanguage(); + + // Make a call chainer for each in this entities scope + for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j) + { + + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter + HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript(CFmtStr("%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j])); + g_pScriptVM->Run(hCreateChainScript, (HSCRIPT)m_ScriptScope); + } + } + + char szScriptsList[255]; + Q_strcpy(szScriptsList, STRING(m_iszVScripts)); + CUtlStringList szScripts; + + V_SplitString(szScriptsList, " ", szScripts); + + for (int i = 0; i < szScripts.Count(); i++) + { +#ifdef MAPBASE + CGMsg( 0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i] ); +#else + Log( "%s executing script: %s\n", GetDebugName(), szScripts[i]); +#endif + + RunScriptFile(szScripts[i], IsWorld()); + + for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j) + { + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter. + HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript(CFmtStr("%sCallChain.PostScriptExecute()", sCallChainFunctions[j])); + g_pScriptVM->Run(hRunPostScriptExecute, (HSCRIPT)m_ScriptScope); + } + } + } + + if (m_iszScriptThinkFunction != NULL_STRING) + { + SetContextThink(&CBaseEntity::ScriptThink, gpGlobals->curtime + sv_script_think_interval.GetFloat(), "ScriptThink"); + } +} + + +//-------------------------------------------------------------------------------------------------- +// This is called during entity spawning and after restore to allow scripts to precache any +// resources they need. +//-------------------------------------------------------------------------------------------------- +void CBaseEntity::RunPrecacheScripts(void) +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM == NULL) + { + return; + } +#endif + + HSCRIPT hScriptPrecache = m_ScriptScope.LookupFunction("DispatchPrecache"); + if (hScriptPrecache) + { + g_pScriptVM->Call(hScriptPrecache, m_ScriptScope); + m_ScriptScope.ReleaseFunction(hScriptPrecache); + } +} + +void CBaseEntity::RunOnPostSpawnScripts(void) +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM == NULL) + { + return; + } +#endif + + HSCRIPT hFuncConnect = g_pScriptVM->LookupFunction("ConnectOutputs"); + if (hFuncConnect) + { + g_pScriptVM->Call(hFuncConnect, NULL, true, NULL, (HSCRIPT)m_ScriptScope); + g_pScriptVM->ReleaseFunction(hFuncConnect); + } + + HSCRIPT hFuncDisp = m_ScriptScope.LookupFunction("DispatchOnPostSpawn"); + if (hFuncDisp) + { + variant_t variant; + variant.SetString(MAKE_STRING("DispatchOnPostSpawn")); + g_EventQueue.AddEvent(this, "CallScriptFunction", variant, 0, this, this); + m_ScriptScope.ReleaseFunction(hFuncDisp); + + } +} + +HSCRIPT CBaseEntity::GetScriptOwnerEntity() +{ + return ToHScript(GetOwnerEntity()); +} + +void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) +{ + SetOwnerEntity(ToEnt(pOwner)); +} + +//----------------------------------------------------------------------------- +// VScript access to model's key values +// for iteration and value access, use: +// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString, +// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void ) +{ + KeyValues *pModelKeyValues = new KeyValues(""); + HSCRIPT hScript = NULL; + const char *pszModelName = modelinfo->GetModelName( GetModel() ); + const char *pBuffer = modelinfo->GetModelKeyValueText( GetModel() ) ; + + if ( pModelKeyValues->LoadFromBuffer( pszModelName, pBuffer ) ) + { + // UNDONE: how does destructor get called on this +#ifdef MAPBASE_VSCRIPT + m_pScriptModelKeyValues = hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pModelKeyValues, true ); // Allow VScript to delete this when the instance is removed. +#else + m_pScriptModelKeyValues = new CScriptKeyValues( pModelKeyValues ); +#endif + + // UNDONE: who calls ReleaseInstance on this??? Does name need to be unique??? + +#ifndef MAPBASE_VSCRIPT + hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues ); +#endif + + /* + KeyValues *pParticleEffects = pModelKeyValues->FindKey("Particles"); + if ( pParticleEffects ) + { + // Start grabbing the sounds and slotting them in + for ( KeyValues *pSingleEffect = pParticleEffects->GetFirstSubKey(); pSingleEffect; pSingleEffect = pSingleEffect->GetNextKey() ) + { + const char *pParticleEffectName = pSingleEffect->GetString( "name", "" ); + PrecacheParticleSystem( pParticleEffectName ); + } + } + */ + } + + return hScript; +} + +void CBaseEntity::ScriptSetLocalAngularVelocity(float pitchVel, float yawVel, float rollVel) +{ + QAngle qa; + qa.Init(pitchVel, yawVel, rollVel); + SetLocalAngularVelocity(qa); +} + +const Vector& CBaseEntity::ScriptGetLocalAngularVelocity(void) +{ + QAngle qa = GetLocalAngularVelocity(); + static Vector v; + v.x = qa.x; + v.y = qa.y; + v.z = qa.z; + return v; +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the min collision bounds, centered on object +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetBoundingMins(void) +{ + return m_Collision.OBBMins(); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the max collision bounds, centered on object +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetBoundingMaxs(void) +{ + return m_Collision.OBBMaxs(); +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseEntity::ScriptTakeDamage( HSCRIPT pInfo ) +{ + if (pInfo) + { + CTakeDamageInfo *info = HScriptToClass( pInfo ); //ToDamageInfo( pInfo ); + if (info) + { + return OnTakeDamage( *info ); + } + } + + return 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptFireBullets( HSCRIPT pInfo ) +{ + if (pInfo) + { + extern FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ); + FireBulletsInfo_t *info = GetFireBulletsInfoFromInfo( pInfo ); + if (info) + { + FireBullets( *info ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptAddContext( const char *name, const char *value, float duration ) +{ + AddContext( name, value, duration ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CBaseEntity::ScriptGetContext( const char *name ) +{ + return GetContextValue( name ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetContextIndex( int index ) +{ + if (index >= m_ResponseContexts.Count()) + return NULL; + + ScriptVariant_t varTable; + g_pScriptVM->CreateTable( varTable ); + + g_pScriptVM->SetValue( varTable, "name", STRING( m_ResponseContexts[index].m_iszName ) ); + g_pScriptVM->SetValue( varTable, "value", STRING( m_ResponseContexts[index].m_iszValue ) ); + g_pScriptVM->SetValue( varTable, "expiration_time", m_ResponseContexts[index].m_fExpirationTime ); + + return varTable.m_hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseEntity::ScriptClassify( void ) +{ + return (int)Classify(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CBaseEntity::ScriptAddOutput( const char *pszOutputName, const char *pszTarget, const char *pszAction, const char *pszParameter, float flDelay, int iMaxTimes ) +{ + const char *pszValue = UTIL_VarArgs("%s,%s,%s,%f,%i", pszTarget, pszAction, pszParameter, flDelay, iMaxTimes); + return KeyValue( pszOutputName, pszValue ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName ) +{ + static char szValue[128]; + GetKeyValue( pszKeyName, szValue, sizeof(szValue) ); + return szValue; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetColorVector() +{ + static Vector vecColor; + vecColor.Init( m_clrRender.GetR(), m_clrRender.GetG(), m_clrRender.GetB() ); + return vecColor; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetColorVector( const Vector& vecColor ) +{ + SetRenderColor( vecColor.x, vecColor.y, vecColor.z ); +} + +void CBaseEntity::ScriptSetColor( int r, int g, int b ) +{ + SetRenderColor( r, g, b ); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the entity matrix transform +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptEntityToWorldTransform( void ) +{ + return g_pScriptVM->RegisterInstance( &EntityToWorldTransform() ); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the entity's physics object if it has one +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetPhysicsObject( void ) +{ + if (VPhysicsGetObject()) + return g_pScriptVM->RegisterInstance( VPhysicsGetObject() ); + else + return NULL; +} + +//----------------------------------------------------------------------------- +// Vscript: Dispatch an interaction to the entity +//----------------------------------------------------------------------------- +bool CBaseEntity::ScriptDispatchInteraction( int interactionType, HSCRIPT data, HSCRIPT sourceEnt ) +{ + return DispatchInteraction( interactionType, data, ToEnt( sourceEnt ) ? ToEnt( sourceEnt )->MyCombatCharacterPointer() : NULL ); +} +#endif + + +#ifdef MAPBASE +extern int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 ); + +//------------------------------------------------------------------------------ +// Purpose: Create an entity of the given type +//------------------------------------------------------------------------------ +class CEntCreateAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback +{ +public: + virtual bool CreateAimed() { return false; } + + virtual void CommandCallback( const CCommand &args ) + { + MDLCACHE_CRITICAL_SECTION(); + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if (!pPlayer) + { + return; + } + + // Don't allow regular users to create point_servercommand entities for the same reason as blocking ent_fire + if ( !Q_stricmp( args[1], "point_servercommand" ) ) + { + if ( engine->IsDedicatedServer() ) + { + // We allow people with disabled autokick to do it, because they already have rcon. + if ( pPlayer->IsAutoKickDisabled() == false ) + return; + } + else if ( gpGlobals->maxClients > 1 ) + { + // On listen servers with more than 1 player, only allow the host to create point_servercommand. + CBasePlayer *pHostPlayer = UTIL_GetListenServerHost(); + if ( pPlayer != pHostPlayer ) + return; + } + } + + bool allowPrecache = CBaseEntity::IsPrecacheAllowed(); + CBaseEntity::SetAllowPrecache( true ); + + // Try to create entity + CBaseEntity *entity = dynamic_cast< CBaseEntity * >( CreateEntityByName(args[1]) ); + if (entity) + { + // Pass in any additional parameters. + for ( int i = 2; i + 1 < args.ArgC(); i += 2 ) + { + const char *pKeyName = args[i]; + const char *pValue = args[i+1]; + entity->KeyValue( pKeyName, pValue ); + } + + DispatchSpawn(entity); + + // Now attempt to drop into the world + trace_t tr; + Vector forward; + pPlayer->EyeVectors( &forward ); + + // Pass through the player's vehicle + CTraceFilterSkipTwoEntities filter( pPlayer, pPlayer->GetVehicleEntity(), COLLISION_GROUP_NONE ); + UTIL_TraceLine(pPlayer->EyePosition(), + pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID, + &filter, &tr ); + + if ( tr.fraction != 1.0 ) + { + // Raise the end position a little up off the floor, place the npc and drop him down + tr.endpos.z += 12; + + if (CreateAimed()) + { + QAngle angles; + VectorAngles( forward, angles ); + angles.x = 0; + angles.z = 0; + entity->Teleport( &tr.endpos, &angles, NULL ); + } + else + { + entity->Teleport( &tr.endpos, NULL, NULL ); + } + + UTIL_DropToFloor( entity, MASK_SOLID ); + } + + entity->Activate(); + } + CBaseEntity::SetAllowPrecache( allowPrecache ); + } + + virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + if ( !g_pGameRules ) + { + return 0; + } + + const char *cmdname = CreateAimed() ? "ent_create_aimed" : "ent_create"; + + char *substring = (char *)partial; + if ( Q_strstr( partial, cmdname ) ) + { + substring = (char *)partial + strlen( cmdname ) + 1; + } + + int checklen = Q_strlen( substring ); + + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + return EntityFactory_AutoComplete( cmdname, commands, symbols, substring, checklen ); + } +}; + +static CEntCreateAutoCompletionFunctor g_EntCreateAutoComplete; +static ConCommand ent_create("ent_create", &g_EntCreateAutoComplete, "Creates an entity of the given type where the player is looking. Additional parameters can be passed in in the form: ent_create ... ", FCVAR_GAMEDLL | FCVAR_CHEAT, &g_EntCreateAutoComplete); + +class CEntCreateAimedAutoCompletionFunctor : public CEntCreateAutoCompletionFunctor +{ +public: + virtual bool CreateAimed() { return true; } +}; + +static CEntCreateAimedAutoCompletionFunctor g_EntCreateAimedAutoComplete; + +static ConCommand ent_create_aimed("ent_create_aimed", &g_EntCreateAimedAutoComplete, "Creates an entity of the given type where the player is looking. Additional parameters can be passed in in the form: ent_create_aimed ... ", FCVAR_CHEAT, &g_EntCreateAimedAutoComplete); +#else //------------------------------------------------------------------------------ // Purpose: Create an NPC of the given type //------------------------------------------------------------------------------ @@ -7369,6 +10440,7 @@ void CC_Ent_Create( const CCommand& args ) CBaseEntity::SetAllowPrecache( allowPrecache ); } static ConCommand ent_create("ent_create", CC_Ent_Create, "Creates an entity of the given type where the player is looking. Additional parameters can be passed in in the form: ent_create ... ", FCVAR_GAMEDLL | FCVAR_CHEAT); +#endif //------------------------------------------------------------------------------ // Purpose: Teleport a specified entity to where the player is looking @@ -7425,6 +10497,51 @@ bool CC_GetCommandEnt( const CCommand& args, CBaseEntity **ent, Vector *vecTarge return true; } +#ifdef MAPBASE +class CEntTeleportAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback +{ +public: + virtual void CommandCallback( const CCommand &command ) + { + if ( command.ArgC() < 2 ) + { + Msg( "Format: ent_teleport \n" ); + return; + } + + CBaseEntity *pEnt; + Vector vecTargetPoint; + if ( CC_GetCommandEnt( command, &pEnt, &vecTargetPoint, NULL ) ) + { + pEnt->Teleport( &vecTargetPoint, NULL, NULL ); + } + } + + virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + if ( !g_pGameRules ) + { + return 0; + } + + const char *cmdname = "ent_teleport"; + + char *substring = (char *)partial; + if ( Q_strstr( partial, cmdname ) ) + { + substring = (char *)partial + strlen( cmdname ) + 1; + } + + int checklen = Q_strlen( substring ); + + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + return AutoCompleteEntities(cmdname, commands, symbols, substring, checklen); + } +}; + +static CEntTeleportAutoCompletionFunctor g_EntTeleportAutoComplete; +static ConCommand ent_teleport("ent_teleport", &g_EntTeleportAutoComplete, "Teleport the specified entity to where the player is looking.\n\tFormat: ent_teleport ", FCVAR_CHEAT, &g_EntTeleportAutoComplete); +#else //------------------------------------------------------------------------------ // Purpose: Teleport a specified entity to where the player is looking //------------------------------------------------------------------------------ @@ -7445,6 +10562,7 @@ void CC_Ent_Teleport( const CCommand& args ) } static ConCommand ent_teleport("ent_teleport", CC_Ent_Teleport, "Teleport the specified entity to where the player is looking.\n\tFormat: ent_teleport ", FCVAR_CHEAT); +#endif //------------------------------------------------------------------------------ // Purpose: Orient a specified entity to match the player's angles diff --git a/mp/src/game/server/baseentity.h b/mp/src/game/server/baseentity.h index 42c0cdf2..1e1c1002 100644 --- a/mp/src/game/server/baseentity.h +++ b/mp/src/game/server/baseentity.h @@ -21,6 +21,9 @@ #include "shareddefs.h" #include "engine/ivmodelinfo.h" +#include "vscript/ivscript.h" +#include "vscript_server.h" + class CDamageModifier; class CDmgAccumulator; @@ -82,6 +85,7 @@ typedef struct KeyValueData_s KeyValueData; class CUserCmd; class CSkyCamera; class CEntityMapData; +class CWorld; class INextBot; class IHasAttributes; @@ -311,11 +315,13 @@ a list of all CBaseEntitys is kept in gEntList CBaseEntity *CreateEntityByName( const char *className, int iForceEdictIndex = -1 ); CBaseNetworkable *CreateNetworkableByName( const char *className ); +CBaseEntity* ToEnt(HSCRIPT hScript); + // creates an entity and calls all the necessary spawn functions extern void SpawnEntityByName( const char *className, CEntityMapData *mapData = NULL ); // calls the spawn functions for an entity -extern int DispatchSpawn( CBaseEntity *pEntity ); +extern int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts = true); inline CBaseEntity *GetContainingEntity( edict_t *pent ); @@ -379,6 +385,8 @@ public: DECLARE_SERVERCLASS(); // data description DECLARE_DATADESC(); + // script description + DECLARE_ENT_SCRIPTDESC(); // memory handling void *operator new( size_t stAllocateBlock ); @@ -493,6 +501,8 @@ public: virtual void SetOwnerEntity( CBaseEntity* pOwner ); void SetEffectEntity( CBaseEntity *pEffectEnt ); CBaseEntity *GetEffectEntity() const; + HSCRIPT GetScriptOwnerEntity(); + virtual void SetScriptOwnerEntity(HSCRIPT pOwner); // Only CBaseEntity implements these. CheckTransmit calls the virtual ShouldTransmit to see if the // entity wants to be sent. If so, it calls SetTransmit, which will mark any dependents for transmission too. @@ -544,6 +554,11 @@ public: bool IsFollowingEntity(); CBaseEntity *GetFollowedEntity(); +#ifdef MAPBASE_VSCRIPT + void ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ); + HSCRIPT ScriptGetFollowedEntity(); +#endif + // initialization virtual void Spawn( void ); virtual void Precache( void ) {} @@ -563,9 +578,20 @@ public: virtual bool KeyValue( const char *szKeyName, float flValue ); virtual bool KeyValue( const char *szKeyName, const Vector &vecValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + bool KeyValueFromString( const char *szKeyName, const char *szValue ) { return KeyValue( szKeyName, szValue ); } + bool KeyValueFromFloat( const char *szKeyName, float flValue ) { return KeyValue( szKeyName, flValue ); } + bool KeyValueFromInt( const char *szKeyName, int nValue ) { return KeyValue( szKeyName, nValue ); } + bool KeyValueFromVector( const char *szKeyName, const Vector &vecValue ) { return KeyValue( szKeyName, vecValue ); } void ValidateEntityConnections(); void FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay = 0.0f ); + CBaseEntityOutput *FindNamedOutput( const char *pszOutput ); +#ifdef MAPBASE_VSCRIPT + void ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay ); + float GetMaxOutputDelay( const char *pszOutput ); + void CancelEventsByInput( const char *szInput ); +#endif + // Activate - called for each entity after each load game and level load virtual void Activate( void ); @@ -577,6 +603,9 @@ public: CBaseEntity *NextMovePeer( void ); void SetName( string_t newTarget ); +#ifdef MAPBASE_VSCRIPT + void SetNameAsCStr( const char *newTarget ); +#endif void SetParent( string_t newParent, CBaseEntity *pActivator, int iAttachment = -1 ); // Set the movement parent. Your local origin and angles will become relative to this parent. @@ -588,6 +617,8 @@ public: int GetParentAttachment(); string_t GetEntityName(); + const char* GetEntityNameAsCStr(); // This method is temporary for VSCRIPT functionality until we figure out what to do with string_t (sjb) + const char* GetPreTemplateName(); // Not threadsafe. Get the name stripped of template unique decoration bool NameMatches( const char *pszNameOrWildcard ); bool ClassMatches( const char *pszClassOrWildcard ); @@ -636,6 +667,11 @@ public: // handles an input (usually caused by outputs) // returns true if the the value in the pass in should be set, false if the input is to be ignored virtual bool AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ); +#ifdef MAPBASE_VSCRIPT + bool ScriptAcceptInput(const char *szInputName, const char *szValue, HSCRIPT hActivator, HSCRIPT hCaller); +#endif + + bool ScriptInputHook( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, ScriptVariant_t &functionReturn ); // // Input handlers. @@ -663,11 +699,90 @@ public: void InputDisableShadow( inputdata_t &inputdata ); void InputEnableShadow( inputdata_t &inputdata ); void InputAddOutput( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputChangeVariable( inputdata_t &inputdata ); +#endif void InputFireUser1( inputdata_t &inputdata ); void InputFireUser2( inputdata_t &inputdata ); void InputFireUser3( inputdata_t &inputdata ); void InputFireUser4( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputPassUser1( inputdata_t &inputdata ); + void InputPassUser2( inputdata_t &inputdata ); + void InputPassUser3( inputdata_t &inputdata ); + void InputPassUser4( inputdata_t &inputdata ); + + void InputFireRandomUser( inputdata_t &inputdata ); + void InputPassRandomUser( inputdata_t &inputdata ); + + void InputSetEntityName( inputdata_t &inputdata ); + + virtual void InputSetTarget( inputdata_t &inputdata ); + virtual void InputSetOwnerEntity( inputdata_t &inputdata ); + + virtual void InputAddHealth( inputdata_t &inputdata ); + virtual void InputRemoveHealth( inputdata_t &inputdata ); + virtual void InputSetHealth( inputdata_t &inputdata ); + + virtual void InputSetMaxHealth( inputdata_t &inputdata ); + + void InputFireOutput( inputdata_t &inputdata ); + void InputRemoveOutput( inputdata_t &inputdata ); + //virtual void InputCancelOutput( inputdata_t &inputdata ); // Find a way to implement this + void InputReplaceOutput( inputdata_t &inputdata ); + void InputAcceptInput( inputdata_t &inputdata ); + virtual void InputCancelPending( inputdata_t &inputdata ); + + void InputFreeChildren( inputdata_t &inputdata ); + + void InputSetLocalOrigin( inputdata_t &inputdata ); + void InputSetLocalAngles( inputdata_t &inputdata ); + void InputSetAbsOrigin( inputdata_t &inputdata ); + void InputSetAbsAngles( inputdata_t &inputdata ); + void InputSetLocalVelocity( inputdata_t &inputdata ); + void InputSetLocalAngularVelocity( inputdata_t &inputdata ); + + void InputAddSpawnFlags( inputdata_t &inputdata ); + void InputRemoveSpawnFlags( inputdata_t &inputdata ); + void InputSetRenderMode( inputdata_t &inputdata ); + void InputSetRenderFX( inputdata_t &inputdata ); + void InputSetViewHideFlags( inputdata_t &inputdata ); + void InputAddEffects( inputdata_t &inputdata ); + void InputRemoveEffects( inputdata_t &inputdata ); + void InputDrawEntity( inputdata_t &inputdata ); + void InputUndrawEntity( inputdata_t &inputdata ); + void InputAddEFlags( inputdata_t &inputdata ); + void InputRemoveEFlags( inputdata_t &inputdata ); + void InputAddSolidFlags( inputdata_t &inputdata ); + void InputRemoveSolidFlags( inputdata_t &inputdata ); + void InputSetMoveType( inputdata_t &inputdata ); + void InputSetCollisionGroup( inputdata_t &inputdata ); + + void InputTouch( inputdata_t &inputdata ); + + virtual void InputKilledNPC( inputdata_t &inputdata ); + + void InputKillIfNotVisible( inputdata_t &inputdata ); + void InputKillWhenNotVisible( inputdata_t &inputdata ); + + void InputSetThinkNull( inputdata_t &inputdata ); + + COutputEvent m_OnKilled; +#endif + + void InputRunScript(inputdata_t& inputdata); + void InputRunScriptFile(inputdata_t& inputdata); + void InputCallScriptFunction(inputdata_t& inputdata); +#ifdef MAPBASE_VSCRIPT + void InputRunScriptQuotable(inputdata_t& inputdata); + void InputClearScriptScope(inputdata_t& inputdata); +#endif + + bool RunScriptFile(const char* pScriptFile, bool bUseRootScope = false); + bool RunScript(const char* pScriptText, const char* pDebugFilename = "CBaseEntity::RunScript"); + + // Returns the origin at which to play an inputted dispatcheffect virtual void GetInputDispatchEffectPosition( const char *sInputString, Vector &pOrigin, QAngle &pAngles ); @@ -795,6 +910,16 @@ public: CNetworkArray( int, m_nModelIndexOverrides, MAX_VISION_MODES ); // used to override the base model index on the client if necessary #endif +#ifdef MAPBASE + // Prevents this entity from drawing under certain view IDs. Each flag is (1 << the view ID to hide from). + // For example, hiding an entity from VIEW_MONITOR prevents it from showing up on RT camera monitors + // and hiding an entity from VIEW_MAIN just prevents it from showing up through the player's own "eyes". + // Doing this via flags allows for the entity to be hidden from multiple view IDs at the same time. + // + // This was partly inspired by Underhell's keyvalue that allows entities to only render in mirrors and cameras. + CNetworkVar( int, m_iViewHideFlags ); +#endif + // was pev->rendercolor CNetworkColor32( m_clrRender ); const color32 GetRenderColor() const; @@ -841,12 +966,29 @@ protected: #endif void RemoveExpiredConcepts( void ); +#ifdef MAPBASE + // Some new code needs to access these functions from outside of the class. +public: +#endif int GetContextCount() const; // Call RemoveExpiredConcepts to clean out expired concepts const char *GetContextName( int index ) const; // note: context may be expired const char *GetContextValue( int index ) const; // note: context may be expired bool ContextExpired( int index ) const; int FindContextByName( const char *name ) const; +#ifndef MAPBASE public: +#endif + +#ifdef MAPBASE + bool HasContext( const char *name, const char *value ) const; + bool HasContext( string_t name, string_t value ) const; // NOTE: string_t version only compares pointers! + bool HasContext( const char *nameandvalue ) const; + const char *GetContextValue( const char *contextName ) const; + float GetContextExpireTime( const char *name ); + void RemoveContext( const char *nameandvalue ); + void AddContext( const char *name, const char *value, float duration = 0.0f ); +#endif + void AddContext( const char *nameandvalue ); protected: @@ -891,6 +1033,12 @@ public: // Call this to do a TraceAttack on an entity, performs filtering. Don't call TraceAttack() directly except when chaining up to base class void DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator = NULL ); virtual bool PassesDamageFilter( const CTakeDamageInfo &info ); +#ifdef MAPBASE + // Special filter functions made for the "damage" family of filters, including filter_damage_transfer. + bool PassesFinalDamageFilter( const CTakeDamageInfo &info ); + bool DamageFilterAllowsBlood( const CTakeDamageInfo &info ); + bool DamageFilterDamageMod( CTakeDamageInfo &info ); +#endif protected: @@ -929,7 +1077,7 @@ public: virtual INextBot *MyNextBotPointer( void ) { return NULL; } virtual float GetDelay( void ) { return 0; } virtual bool IsMoving( void ); - bool IsWorld() { return entindex() == 0; } + bool IsWorld() const { extern CWorld *g_WorldEntity; return (void *)this == (void *)g_WorldEntity; } // Ported from the Alien Swarm SDK to fix false IsWorld() positives on server-only entities virtual char const *DamageDecal( int bitsDamageType, int gameMaterial ); virtual void DecalTrace( trace_t *pTrace, char const *decalName ); virtual void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName = NULL ); @@ -1049,6 +1197,10 @@ public: void SUB_CallUseToggle( void ) { this->Use( this, this, USE_TOGGLE, 0 ); } void SUB_PerformFadeOut( void ); virtual bool SUB_AllowedToFade( void ); +#ifdef MAPBASE + // For KillWhenNotVisible + void SUB_RemoveWhenNotVisible( void ); +#endif // change position, velocity, orientation instantly // passing NULL means no change @@ -1125,6 +1277,9 @@ public: virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set ); void AppendContextToCriteria( AI_CriteriaSet& set, const char *prefix = "" ); +#ifdef MAPBASE + void ReAppendContextCriteria( AI_CriteriaSet& set ); +#endif void DumpResponseCriteria( void ); // Return the IHasAttributes interface for this base entity. Removes the need for: @@ -1175,6 +1330,20 @@ public: virtual float GetDamage() { return 0; } virtual void SetDamage(float flDamage) {} +#ifdef MAPBASE + // Some entities want to use interactions regardless of whether they're a CBaseCombatCharacter. + // Valve ran into this issue with frag grenades when they started deriving from CBaseAnimating instead of CBaseCombatCharacter, + // preventing them from using the barnacle interactions for rigged grenade timing so it's guaranteed to blow up in the barnacle's face. + // We're used to unaltered behavior now, so we're not restoring that as default, but making this a "base entity" thing is supposed to help in situtions like those. + // + // Also, keep in mind pretty much all existing DispatchInteraction() calls are only performed on CBaseCombatCharacters. + // You'll need to change their code manually if you want other, non-character entities to use the interaction. + bool DispatchInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ); + + // Do not call HandleInteraction directly, use DispatchInteraction + virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ) { return false; } +#endif + virtual Vector EyePosition( void ); // position of eyes virtual const QAngle &EyeAngles( void ); // Direction of eyes in world space virtual const QAngle &LocalEyeAngles( void ); // Direction of eyes @@ -1225,6 +1394,10 @@ public: void SetGravity( float gravity ); float GetFriction( void ) const; void SetFriction( float flFriction ); +#ifdef MAPBASE_VSCRIPT + void SetMass(float mass); + float GetMass(); +#endif virtual bool FVisible ( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); virtual bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); @@ -1456,6 +1629,11 @@ public: // Callbacks for the physgun/cannon picking up an entity virtual CBasePlayer *HasPhysicsAttacker( float dt ) { return NULL; } +#ifdef MAPBASE + // This function needed to be extended to phys_magnet and I didn't like the dynamic_casts. + virtual bool CanBePickedUpByPhyscannon() { return false; } +#endif + // UNDONE: Make this data? virtual unsigned int PhysicsSolidMaskForEntity( void ) const; @@ -1600,7 +1778,7 @@ private: // was pev->flags CNetworkVarForDerived( int, m_fFlags ); - string_t m_iName; // name used to identify this entity + CNetworkVar( string_t, m_iName ); // name used to identify this entity // Damage modifiers friend class CDamageModifier; @@ -1694,6 +1872,12 @@ private: CNetworkVar( bool, m_bAlternateSorting ); // User outputs. Fired when the "FireInputX" input is triggered. +#ifdef MAPBASE + COutputVariant m_OutUser1; + COutputVariant m_OutUser2; + COutputVariant m_OutUser3; + COutputVariant m_OutUser4; +#endif COutputEvent m_OnUser1; COutputEvent m_OnUser2; COutputEvent m_OnUser3; @@ -1802,6 +1986,148 @@ public: } virtual bool ShouldBlockNav() const { return true; } + + // VSCRIPT + HSCRIPT GetScriptInstance(); + bool ValidateScriptScope(); + virtual void RunVScripts(); + bool CallScriptFunction(const char* pFunctionName, ScriptVariant_t* pFunctionReturn); + void ConnectOutputToScript(const char* pszOutput, const char* pszScriptFunc); + void DisconnectOutputFromScript(const char* pszOutput, const char* pszScriptFunc); + void ScriptThink(); +#ifdef MAPBASE_VSCRIPT + void ScriptSetThinkFunction(const char *szFunc, float time); + void ScriptStopThinkFunction(); + void ScriptSetThink(HSCRIPT hFunc, float time); + void ScriptStopThink(); + void ScriptThinkH(); +private: + HSCRIPT m_hfnThink; +public: +#endif + const char* GetScriptId(); + HSCRIPT GetScriptScope(); +#ifdef MAPBASE_VSCRIPT + HSCRIPT GetOrCreatePrivateScriptScope(); +#endif + void RunPrecacheScripts(void); + void RunOnPostSpawnScripts(void); + +#ifdef MAPBASE_VSCRIPT + HSCRIPT LookupScriptFunction(const char* pFunctionName); + bool CallScriptFunctionHandle(HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn); +#endif + + HSCRIPT ScriptGetMoveParent(void); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild(void); + HSCRIPT ScriptNextMovePeer(void); + + const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } +#ifdef MAPBASE_VSCRIPT + const QAngle& ScriptEyeAngles(void) { static QAngle ang; ang = EyeAngles(); return ang; } + void ScriptSetAngles(const QAngle angles) { Teleport(NULL, &angles, NULL); } + const QAngle& ScriptGetAngles(void) { return GetAbsAngles(); } +#else + void ScriptSetAngles(float fPitch, float fYaw, float fRoll) { QAngle angles(fPitch, fYaw, fRoll); Teleport(NULL, &angles, NULL); } + const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } +#endif + + void ScriptSetSize(const Vector& mins, const Vector& maxs) { UTIL_SetSize(this, mins, maxs); } + void ScriptUtilRemove(void) { UTIL_Remove(this); } + void ScriptSetOwner(HSCRIPT hEntity) { SetOwnerEntity(ToEnt(hEntity)); } + void ScriptSetOrigin(const Vector& v) { Teleport(&v, NULL, NULL); } + void ScriptSetForward(const Vector& v) { QAngle angles; VectorAngles(v, angles); Teleport(NULL, &angles, NULL); } + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } +#ifdef MAPBASE_VSCRIPT + const Vector& ScriptGetRight(void) { static Vector vecRight; GetVectors(NULL, &vecRight, NULL); return vecRight; } +#endif + const Vector& ScriptGetLeft(void) { static Vector vecRight; GetVectors(NULL, &vecRight, NULL); return vecRight; } + + const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } + +#ifdef MAPBASE_VSCRIPT + void ScriptSetOriginAngles(const Vector &vecOrigin, const QAngle &angAngles) { Teleport(&vecOrigin, &angAngles, NULL); } + void ScriptSetOriginAnglesVelocity(const Vector &vecOrigin, const QAngle &angAngles, const Vector &vecVelocity) { Teleport(&vecOrigin, &angAngles, &vecVelocity); } + + HSCRIPT ScriptEntityToWorldTransform( void ); + + HSCRIPT ScriptGetPhysicsObject( void ); + + void ScriptSetParent(HSCRIPT hParent, const char *szAttachment); +#endif + + const char* ScriptGetModelName(void) const; + HSCRIPT ScriptGetModelKeyValues(void); + + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + void VScriptPrecacheScriptSound(const char* soundname); + + const Vector& ScriptGetLocalAngularVelocity( void ); + void ScriptSetLocalAngularVelocity( float pitchVel, float yawVel, float rollVel ); + + const Vector& ScriptGetBoundingMins(void); + const Vector& ScriptGetBoundingMaxs(void); + +#ifdef MAPBASE_VSCRIPT + bool ScriptIsVisible( const Vector &vecSpot ) { return FVisible( vecSpot ); } + bool ScriptIsEntVisible( HSCRIPT pEntity ) { return FVisible( ToEnt( pEntity ) ); } + bool ScriptIsVisibleWithMask( const Vector &vecSpot, int traceMask ) { return FVisible( vecSpot, traceMask ); } + + int ScriptTakeDamage( HSCRIPT pInfo ); + void ScriptFireBullets( HSCRIPT pInfo ); + + void ScriptAddContext( const char *name, const char *value, float duration = 0.0f ); + const char *ScriptGetContext( const char *name ); + HSCRIPT ScriptGetContextIndex( int index ); + + int ScriptClassify(void); + + bool ScriptAddOutput( const char *pszOutputName, const char *pszTarget, const char *pszAction, const char *pszParameter, float flDelay, int iMaxTimes ); + const char *ScriptGetKeyValue( const char *pszKeyName ); + + const Vector& ScriptGetColorVector(); + int ScriptGetColorR() { return m_clrRender.GetR(); } + int ScriptGetColorG() { return m_clrRender.GetG(); } + int ScriptGetColorB() { return m_clrRender.GetB(); } + int ScriptGetAlpha() { return m_clrRender.GetA(); } + void ScriptSetColorVector( const Vector& vecColor ); + void ScriptSetColor( int r, int g, int b ); + void ScriptSetColorR( int iVal ) { SetRenderColorR( iVal ); } + void ScriptSetColorG( int iVal ) { SetRenderColorG( iVal ); } + void ScriptSetColorB( int iVal ) { SetRenderColorB( iVal ); } + void ScriptSetAlpha( int iVal ) { SetRenderColorA( iVal ); } + + int ScriptGetRenderMode() { return GetRenderMode(); } + void ScriptSetRenderMode( int nRenderMode ) { SetRenderMode( (RenderMode_t)nRenderMode ); } + + int ScriptGetMoveType() { return GetMoveType(); } + void ScriptSetMoveType( int iMoveType ) { SetMoveType( (MoveType_t)iMoveType ); } + + bool ScriptDispatchInteraction( int interactionType, HSCRIPT data, HSCRIPT sourceEnt ); + + int ScriptGetTakeDamage() { return m_takedamage; } + void ScriptSetTakeDamage( int val ) { m_takedamage = val; } + + static ScriptHook_t g_Hook_UpdateOnRemove; + static ScriptHook_t g_Hook_VPhysicsCollision; + static ScriptHook_t g_Hook_FireBullets; + static ScriptHook_t g_Hook_OnDeath; + static ScriptHook_t g_Hook_HandleInteraction; +#endif + + string_t m_iszVScripts; + string_t m_iszScriptThinkFunction; + CScriptScope m_ScriptScope; + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; +#ifdef MAPBASE_VSCRIPT + HSCRIPT m_pScriptModelKeyValues; +#else + CScriptKeyValues* m_pScriptModelKeyValues; +#endif }; // Send tables exposed in this module. @@ -1930,11 +2256,32 @@ inline string_t CBaseEntity::GetEntityName() return m_iName; } +inline const char *CBaseEntity::GetEntityNameAsCStr() +{ + return STRING(m_iName.Get()); +} + +inline const char *CBaseEntity::GetPreTemplateName() +{ + const char *pszDelimiter = V_strrchr( STRING(m_iName.Get()), '&' ); + if ( !pszDelimiter ) + return STRING( m_iName.Get() ); + static char szStrippedName[128]; + V_strncpy( szStrippedName, STRING( m_iName.Get() ), MIN( ARRAYSIZE(szStrippedName), pszDelimiter - STRING( m_iName.Get() ) + 1 ) ); + return szStrippedName; +} + inline void CBaseEntity::SetName( string_t newName ) { m_iName = newName; } +#ifdef MAPBASE_VSCRIPT +inline void CBaseEntity::SetNameAsCStr( const char *newName ) +{ + m_iName = AllocPooledString(newName); +} +#endif inline bool CBaseEntity::NameMatches( const char *pszNameOrWildcard ) { @@ -2112,6 +2459,36 @@ inline const QAngle& CBaseEntity::GetAbsAngles( void ) const return m_angAbsRotation; } +#ifdef MAPBASE_VSCRIPT +inline float CBaseEntity::GetMass() +{ + IPhysicsObject *vPhys = VPhysicsGetObject(); + if (vPhys) + { + return vPhys->GetMass(); + } + else + { + Warning("Tried to call GetMass() on %s but it has no physics.\n", GetDebugName()); + return 0; + } +} + +inline void CBaseEntity::SetMass(float mass) +{ + mass = clamp(mass, VPHYSICS_MIN_MASS, VPHYSICS_MAX_MASS); + + IPhysicsObject *vPhys = VPhysicsGetObject(); + if (vPhys) + { + vPhys->SetMass(mass); + } + else + { + Warning("Tried to call SetMass() on %s but it has no physics.\n", GetDebugName()); + } +} +#endif //----------------------------------------------------------------------------- @@ -2608,6 +2985,14 @@ inline void CBaseEntity::FireBullets( int cShots, const Vector &vecSrc, FireBullets( info ); } +//----------------------------------------------------------------------------- +// VScript +//----------------------------------------------------------------------------- +inline const char* CBaseEntity::ScriptGetModelName(void) const +{ + return STRING(m_ModelName); +} + // Ugly technique to override base member functions // Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a // member function of a base class. static_cast is a sleezy way around that problem. diff --git a/mp/src/game/server/baseflex.cpp b/mp/src/game/server/baseflex.cpp index b760ed54..156d6d7e 100644 --- a/mp/src/game/server/baseflex.cpp +++ b/mp/src/game/server/baseflex.cpp @@ -95,6 +95,10 @@ BEGIN_DATADESC( CBaseFlex ) END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimating, "Animated characters who have vertex flex capability." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOldestScene, "GetCurrentScene", "Returns the instance of the oldest active scene entity (if any)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSceneByIndex, "GetSceneByIndex", "Returns the instance of the scene entity at the specified index." ) +END_SCRIPTDESC(); LINK_ENTITY_TO_CLASS( funCBaseFlex, CBaseFlex ); // meaningless independant class!! @@ -394,7 +398,11 @@ bool CBaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canc // expression - // duration - //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, CSceneEntity *pSceneEnt ) +#else void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget ) +#endif { if ( !scene || !event ) { @@ -417,9 +425,14 @@ void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEn info.m_pEvent = event; info.m_pScene = scene; info.m_hTarget = pTarget; - info.m_bStarted = false; + info.m_bStarted = false; + info.m_hSceneEntity = pSceneEnt; +#ifdef MAPBASE + if (StartSceneEvent( &info, scene, event, actor, pTarget, pSceneEnt )) +#else if (StartSceneEvent( &info, scene, event, actor, pTarget )) +#endif { m_SceneEvents.AddToTail( info ); } @@ -735,7 +748,11 @@ bool CBaseFlex::StartMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scen //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +#ifdef MAPBASE +bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget, CSceneEntity *pSceneEnt ) +#else bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ) +#endif { switch ( event->GetType() ) { @@ -765,6 +782,58 @@ bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CCh case CChoreoEvent::EXPRESSION: // These are handled client-side return true; + +#ifdef MAPBASE + case CChoreoEvent::GENERIC: + { + // This is handled in CBaseFlex so that any flex entity--including players--could use this text. + if (stricmp(event->GetParameters(), "AI_GAMETEXT") == 0) + { + // game_text-based lines, for placeholders + if ( event->GetParameters2() ) + { + info->m_nType = 12; // SCENE_AI_GAMETEXT + + hudtextparms_t textParams; + textParams.holdTime = event->GetDuration(); + textParams.fadeinTime = 0.5f; + textParams.fadeoutTime = 0.5f; + + if ( GetGameTextSpeechParams( textParams ) ) + { + CRecipientFilter filter; + filter.AddAllPlayers(); + filter.MakeReliable(); + + UserMessageBegin( filter, "HudMsg" ); + WRITE_BYTE ( textParams.channel & 0xFF ); + WRITE_FLOAT( textParams.x ); + WRITE_FLOAT( textParams.y ); + WRITE_BYTE ( textParams.r1 ); + WRITE_BYTE ( textParams.g1 ); + WRITE_BYTE ( textParams.b1 ); + WRITE_BYTE ( textParams.a1 ); + WRITE_BYTE ( textParams.r2 ); + WRITE_BYTE ( textParams.g2 ); + WRITE_BYTE ( textParams.b2 ); + WRITE_BYTE ( textParams.a2 ); + WRITE_BYTE ( textParams.effect ); + WRITE_FLOAT( textParams.fadeinTime ); + WRITE_FLOAT( textParams.fadeoutTime ); + WRITE_FLOAT( textParams.holdTime ); + WRITE_FLOAT( textParams.fxTime ); + WRITE_STRING( event->GetParameters2() ); + WRITE_STRING( "" ); // No custom font + WRITE_BYTE ( Q_strlen( event->GetParameters2() ) ); + MessageEnd(); + } + return true; + } + } + + return false; + } +#endif } return false; @@ -2004,12 +2073,114 @@ float CBaseFlex::PlayScene( const char *pszScene, float flDelay, AI_Response *re // Input : *soundname - // Output : float //----------------------------------------------------------------------------- +#ifdef MAPBASE +float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname, float flDelay, AI_Response *response, IRecipientFilter *filter ) +{ + return InstancedAutoGeneratedSoundScene( this, soundname, NULL, flDelay, false, response, false, filter ); +} +#else float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname ) { return InstancedAutoGeneratedSoundScene( this, soundname ); } +#endif + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Parameters for scene event AI_GameText +//----------------------------------------------------------------------------- +bool CBaseFlex::GetGameTextSpeechParams( hudtextparms_t ¶ms ) +{ + params.channel = 3; + params.x = -1; + params.y = 0.6; + params.effect = 0; + + params.r1 = 255; + params.g1 = 255; + params.b1 = 255; + + ScriptVariant_t varTable; + if (g_pScriptVM->GetValue(m_ScriptScope, "m_GameTextSpeechParams", &varTable) && varTable.m_type == FIELD_HSCRIPT) + { + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( varTable.m_hScript, nIterator, &varKey, &varValue )) != -1) + { + if (FStrEq( varKey.m_pszString, "color" )) + { + params.r1 = varValue.m_pVector->x; + params.g1 = varValue.m_pVector->y; + params.b1 = varValue.m_pVector->z; + } + else if (FStrEq( varKey.m_pszString, "color2" )) + { + params.r2 = varValue.m_pVector->x; + params.g2 = varValue.m_pVector->y; + params.b2 = varValue.m_pVector->z; + } + else if (FStrEq( varKey.m_pszString, "channel" )) + { + params.channel = varValue.m_int; + } + else if (FStrEq( varKey.m_pszString, "x" )) + { + params.x = varValue.m_float; + } + else if (FStrEq( varKey.m_pszString, "y" )) + { + params.y = varValue.m_float; + } + else if (FStrEq( varKey.m_pszString, "effect" )) + { + params.effect = varValue.m_int; + } + else if (FStrEq( varKey.m_pszString, "fxtime" )) + { + params.fxTime = varValue.m_float; + } + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + } + + return true; +} +#endif +//-------------------------------------------------------------------------------------------------- +// Returns the script instance of the scene entity associated with our oldest ("top level") scene event +//-------------------------------------------------------------------------------------------------- +HSCRIPT CBaseFlex::ScriptGetOldestScene( void ) +{ + if ( m_SceneEvents.Count() > 0 ) + { + CSceneEventInfo curScene = m_SceneEvents.Head(); + return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) ); + } + else + { + return NULL; + } +} + +//-------------------------------------------------------------------------------------------------- +// Returns the script instance of the scene at the specified index, or null if index >= count +//-------------------------------------------------------------------------------------------------- +HSCRIPT CBaseFlex::ScriptGetSceneByIndex( int index ) +{ + if ( m_SceneEvents.IsValidIndex( index ) ) + { + CSceneEventInfo curScene = m_SceneEvents.Element( index ); + return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) ); + } + else + { + return NULL; + } +} // FIXME: move to CBaseActor diff --git a/mp/src/game/server/baseflex.h b/mp/src/game/server/baseflex.h index 1f0009ec..215b13f5 100644 --- a/mp/src/game/server/baseflex.h +++ b/mp/src/game/server/baseflex.h @@ -46,6 +46,8 @@ public: DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); // Construction CBaseFlex( void ); @@ -72,7 +74,11 @@ public: void RemoveChoreoScene( CChoreoScene *scene, bool canceled = false ); // Start the specifics of an scene event +#ifdef MAPBASE + virtual bool StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget, CSceneEntity *pSceneEnt = NULL ); +#else virtual bool StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ); +#endif // Manipulation of events for the object // Should be called by think function to process all scene events @@ -91,7 +97,11 @@ public: virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); // Add the event to the queue for this actor +#ifdef MAPBASE + void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget = NULL, CSceneEntity *pSceneEnt = NULL ); +#else void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget = NULL ); +#endif // Remove the event from the queue for this actor void RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill ); @@ -116,10 +126,22 @@ public: void SentenceStop( void ) { EmitSound( "AI_BaseNPC.SentenceStop" ); } virtual float PlayScene( const char *pszScene, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL ); +#ifdef MAPBASE + virtual float PlayAutoGeneratedSoundScene( const char *soundname, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL ); +#else virtual float PlayAutoGeneratedSoundScene( const char *soundname ); +#endif + + // Returns the script instance of the scene entity associated with our oldest ("top level") scene event + virtual HSCRIPT ScriptGetOldestScene( void ); + virtual HSCRIPT ScriptGetSceneByIndex( int index ); virtual int GetSpecialDSP( void ) { return 0; } +#ifdef MAPBASE + virtual bool GetGameTextSpeechParams( hudtextparms_t ¶ms ); +#endif + protected: // For handling .vfe files // Search list, or add if not in list diff --git a/mp/src/game/server/bmodels.cpp b/mp/src/game/server/bmodels.cpp index 71be7d39..8c5ca480 100644 --- a/mp/src/game/server/bmodels.cpp +++ b/mp/src/game/server/bmodels.cpp @@ -16,9 +16,7 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#define SF_BRUSH_ACCDCC 16// brush should accelerate and decelerate when toggled -#define SF_BRUSH_HURT 32// rotating brush that inflicts pain based on rotation speed -#define SF_ROTATING_NOT_SOLID 64 // some special rotating objects are not solid. +//Tony; moved the spawnflags to util.h to prevent more mistakes in the future. // =================== FUNC_WALL ============================================== class CFuncWall : public CBaseEntity @@ -962,6 +960,18 @@ void CFuncRotating::UpdateSpeed( float flNewSpeed ) RampPitchVol(); } +#ifdef MAPBASE + QAngle angNormalizedAngles = GetLocalAngles(); + if (m_vecMoveAng.x) + angNormalizedAngles.x = AngleNormalize( angNormalizedAngles.x ); + if (m_vecMoveAng.y) + angNormalizedAngles.y = AngleNormalize( angNormalizedAngles.y ); + if (m_vecMoveAng.z) + angNormalizedAngles.z = AngleNormalize( angNormalizedAngles.z ); + + SetLocalAngles(angNormalizedAngles); +#endif + SetLocalAngularVelocity( m_vecMoveAng * m_flSpeed ); } @@ -1380,6 +1390,9 @@ public: void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetFilter( inputdata_t &inputdata ); +#endif private: @@ -1394,10 +1407,17 @@ BEGIN_DATADESC( CFuncVPhysicsClip ) // Keyfields DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ), DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), +#else DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetFilter", InputSetFilter ), +#endif END_DATADESC() @@ -1440,6 +1460,12 @@ bool CFuncVPhysicsClip::EntityPassesFilter( CBaseEntity *pOther ) if ( pFilter ) return pFilter->PassesFilter( this, pOther ); +#ifdef MAPBASE + // I couldn't figure out what else made this crash. The entity shouldn't be NULL. + if ( !pOther->VPhysicsGetObject() ) + return false; +#endif + if ( pOther->GetMoveType() == MOVETYPE_VPHYSICS && pOther->VPhysicsGetObject()->IsMoveable() ) return true; @@ -1463,3 +1489,19 @@ void CFuncVPhysicsClip::InputDisable( inputdata_t &inputdata ) VPhysicsGetObject()->EnableCollisions(false); m_bDisabled = true; } + +#ifdef MAPBASE +void CFuncVPhysicsClip::InputSetFilter( inputdata_t &inputdata ) +{ + if (inputdata.value.Entity()) + { + m_iFilterName = inputdata.value.Entity()->GetEntityName(); + m_hFilter = dynamic_cast(inputdata.value.Entity().Get()); + } + else + { + m_iFilterName = NULL_STRING; + m_hFilter = NULL; + } +} +#endif diff --git a/mp/src/game/server/buttons.cpp b/mp/src/game/server/buttons.cpp index af46d0c4..47472d43 100644 --- a/mp/src/game/server/buttons.cpp +++ b/mp/src/game/server/buttons.cpp @@ -898,6 +898,13 @@ void CRotButton::Spawn( void ) SetUse(&CRotButton::ButtonUse); +#ifdef MAPBASE + if (HasSpawnFlags(SF_BUTTON_LOCKED)) + { + m_bLocked = true; + } +#endif + // // If touching activates the button, set its touch function. // diff --git a/mp/src/game/server/cbase.cpp b/mp/src/game/server/cbase.cpp index d6627b4b..ec125a8f 100644 --- a/mp/src/game/server/cbase.cpp +++ b/mp/src/game/server/cbase.cpp @@ -84,6 +84,10 @@ OUTPUTS: #include "tier1/strtools.h" #include "datacache/imdlcache.h" #include "env_debughistory.h" +#ifdef MAPBASE +#include "mapbase/variant_tools.h" +#include "mapbase/matchers.h" +#endif #include "tier0/vprof.h" @@ -132,7 +136,7 @@ CEventAction::CEventAction( const char *ActionData ) // // Parse the target name. // - const char *psz = nexttoken(szToken, ActionData, ','); + const char *psz = nexttoken(szToken, ActionData, ',', sizeof(szToken)); if (szToken[0] != '\0') { m_iTarget = AllocPooledString(szToken); @@ -141,7 +145,7 @@ CEventAction::CEventAction( const char *ActionData ) // // Parse the input name. // - psz = nexttoken(szToken, psz, ','); + psz = nexttoken(szToken, psz, ',', sizeof(szToken)); if (szToken[0] != '\0') { m_iTargetInput = AllocPooledString(szToken); @@ -154,7 +158,7 @@ CEventAction::CEventAction( const char *ActionData ) // // Parse the parameter override. // - psz = nexttoken(szToken, psz, ','); + psz = nexttoken(szToken, psz, ',', sizeof(szToken)); if (szToken[0] != '\0') { m_iParameter = AllocPooledString(szToken); @@ -163,7 +167,7 @@ CEventAction::CEventAction( const char *ActionData ) // // Parse the delay. // - psz = nexttoken(szToken, psz, ','); + psz = nexttoken(szToken, psz, ',', sizeof(szToken)); if (szToken[0] != '\0') { m_flDelay = atof(szToken); @@ -172,7 +176,7 @@ CEventAction::CEventAction( const char *ActionData ) // // Parse the number of times to fire. // - nexttoken(szToken, psz, ','); + nexttoken(szToken, psz, ',', sizeof(szToken)); if (szToken[0] != '\0') { m_nTimesToFire = atoi(szToken); @@ -272,7 +276,12 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa // variant_t ValueOverride; ValueOverride.SetString( ev->m_iParameter ); +#ifdef MAPBASE + // I found this while making point_advanced_finder. FireOutput()'s own delay parameter doesn't work with...uh...parameters. + g_EventQueue.AddEvent( STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), ValueOverride, ev->m_flDelay + fDelay, pActivator, pCaller, ev->m_iIDStamp ); +#else g_EventQueue.AddEvent( STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), ValueOverride, ev->m_flDelay, pActivator, pCaller, ev->m_iIDStamp ); +#endif } if ( ev->m_flDelay ) @@ -293,7 +302,11 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa ev->m_flDelay, STRING(ev->m_iParameter) ); +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_IO_SYSTEM, "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); } else @@ -312,7 +325,11 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa STRING(ev->m_iTargetInput), STRING(ev->m_iParameter) ); +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_IO_SYSTEM, "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); } @@ -333,7 +350,12 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa { char szBuffer[256]; Q_snprintf( szBuffer, sizeof(szBuffer), "Removing from action list: (%s,%s) -> (%s,%s)\n", pCaller ? STRING(pCaller->m_iClassname) : "NULL", pCaller ? STRING(pCaller->GetEntityName()) : "NULL", STRING(ev->m_iTarget), STRING(ev->m_iTargetInput)); + +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_IO_SYSTEM, "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); bRemove = true; } @@ -387,6 +409,28 @@ void CBaseEntityOutput::AddEventAction( CEventAction *pEventAction ) m_ActionList = pEventAction; } +void CBaseEntityOutput::RemoveEventAction( CEventAction *pEventAction ) +{ + CEventAction *pAction = GetActionList(); + CEventAction *pPrevAction = NULL; + while ( pAction ) + { + if ( pAction == pEventAction ) + { + if ( !pPrevAction ) + { + m_ActionList = NULL; + } + else + { + pPrevAction->m_pNext = pAction->m_pNext; + } + return; + } + pAction = pAction->m_pNext; + } +} + // save data description for the event queue BEGIN_SIMPLE_DATADESC( CBaseEntityOutput ) @@ -805,7 +849,12 @@ void CEventQueue::Dump( void ) //----------------------------------------------------------------------------- // Purpose: adds the action into the correct spot in the priority queue, targeting entity via string name //----------------------------------------------------------------------------- -void CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) +#ifdef MAPBASE_VSCRIPT +intptr_t +#else +void +#endif +CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) { // build the new event EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t; @@ -823,12 +872,21 @@ void CEventQueue::AddEvent( const char *target, const char *targetInput, variant newEvent->m_iOutputID = outputID; AddEvent( newEvent ); + +#ifdef MAPBASE_VSCRIPT + return reinterpret_cast(newEvent); // POINTER_TO_INT +#endif } //----------------------------------------------------------------------------- // Purpose: adds the action into the correct spot in the priority queue, targeting entity via pointer //----------------------------------------------------------------------------- -void CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) +#ifdef MAPBASE_VSCRIPT +intptr_t +#else +void +#endif +CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) { // build the new event EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t; @@ -846,6 +904,10 @@ void CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, varian newEvent->m_iOutputID = outputID; AddEvent( newEvent ); + +#ifdef MAPBASE_VSCRIPT + return reinterpret_cast(newEvent); +#endif } void CEventQueue::AddEvent( CBaseEntity *target, const char *action, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) @@ -922,6 +984,72 @@ void CEventQueue::ServiceEvents( void ) { // In the context the event, the searching entity is also the caller CBaseEntity *pSearchingEntity = pe->m_pCaller; +#ifdef MAPBASE + //=============================================================== + // + // This is the code that the I/O system uses to look for its targets. + // + // I wanted a good way to access a COutputEHANDLE's handle parameter. + // Sure, you could do it through logic_register_activator, but what if that's not good enough? + // + // Basic gEntList searches, which this originally used, would require extra implementation for another entity pointer to be passed. + // Without changing the way entity searching works, I just created a custom version of it here. + // + // Yes, all of this for mere "!output" support. + // + // I don't think there's much harm in this anyway. It's functionally identical and might even run a few nanoseconds faster + // since we don't need to check for filters or call the same loop over and over again. + // + //=============================================================== + const char *szName = STRING(pe->m_iTarget); + if ( szName[0] == '!' ) + { + CBaseEntity *target = gEntList.FindEntityProcedural( szName, pSearchingEntity, pe->m_pActivator, pe->m_pCaller ); + + if (!target) + { + // Here's the !output I was talking about. + // It only checks for it if we're looking for a procedural entity ('!' confirmed) + // and we didn't find one from FindEntityProcedural. + if (FStrEq(szName, "!output")) + { + pe->m_VariantValue.Convert( FIELD_EHANDLE ); + target = pe->m_VariantValue.Entity(); + } + } + + if (target) + { + // pump the action into the target + target->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID ); + targetFound = true; + } + } + else + { + const CEntInfo *pInfo = gEntList.FirstEntInfo(); + + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; + if ( !ent ) + { + DevWarning( "NULL entity in global entity list!\n" ); + continue; + } + + if ( !ent->GetEntityName() ) + continue; + + if ( ent->NameMatches( szName ) ) + { + // pump the action into the target + ent->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID ); + targetFound = true; + } + } + } +#else CBaseEntity *target = NULL; while ( 1 ) { @@ -933,6 +1061,7 @@ void CEventQueue::ServiceEvents( void ) target->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID ); targetFound = true; } +#endif } // direct pointer @@ -974,7 +1103,11 @@ void CEventQueue::ServiceEvents( void ) char szBuffer[256]; Q_snprintf( szBuffer, sizeof(szBuffer), "unhandled input: (%s) -> (%s), from (%s,%s); target entity not found\n", STRING(pe->m_iTargetInput), STRING(pe->m_iTarget), pClass, pName ); +#ifdef MAPBASE + CGMsg( 2, CON_GROUP_IO_SYSTEM, "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); } @@ -1122,6 +1255,80 @@ void ServiceEventQueue( void ) } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Remove events on entity by input. +//----------------------------------------------------------------------------- +void CEventQueue::CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ) +{ + if ( !pTarget ) + return; + + string_t iszDebugName = MAKE_STRING( pTarget->GetDebugName() ); + EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext; + + while ( pCur ) + { + bool bRemove = false; + + if ( pTarget == pCur->m_pEntTarget || pCur->m_iTarget == iszDebugName ) + { + if ( !V_strncmp( STRING(pCur->m_iTargetInput), szInput, strlen(szInput) ) ) + { + bRemove = true; + } + } + + EventQueuePrioritizedEvent_t *pPrev = pCur; + pCur = pCur->m_pNext; + + if ( bRemove ) + { + RemoveEvent(pPrev); + delete pPrev; + } + } +} + +bool CEventQueue::RemoveEvent( intptr_t event ) +{ + if ( !event ) + return false; + + EventQueuePrioritizedEvent_t *pe = reinterpret_cast(event); // INT_TO_POINTER + + for ( EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext; pCur; pCur = pCur->m_pNext ) + { + if ( pCur == pe ) + { + RemoveEvent(pCur); + delete pCur; + return true; + } + } + + return false; +} + +float CEventQueue::GetTimeLeft( intptr_t event ) +{ + if ( !event ) + return 0.f; + + EventQueuePrioritizedEvent_t *pe = reinterpret_cast(event); // INT_TO_POINTER + + for ( EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext; pCur; pCur = pCur->m_pNext ) + { + if ( pCur == pe ) + { + return (pCur->m_flFireTime - gpGlobals->curtime); + } + } + + return 0.f; +} +#endif // MAPBASE_VSCRIPT + // save data description for the event queue BEGIN_SIMPLE_DATADESC( CEventQueue ) @@ -1252,6 +1459,23 @@ void variant_t::Set( fieldtype_t ftype, void *data ) break; } +#ifdef MAPBASE + // There's this output class called COutputVariant which could output any data type, like a FIELD_INPUT input function. + // Well...nobody added support for it. It was there, but it wasn't functional. + // Mapbase adds support for it so you could variant your outputs as you please. + case FIELD_INPUT: + { + variant_t *variant = (variant_t*)data; + + // Pretty much just copying over its stored value. + fieldType = variant->FieldType(); + variant->SetOther(data); + + Set(fieldType, data); + break; + } +#endif + case FIELD_EHANDLE: eVal = *((EHANDLE *)data); break; case FIELD_CLASSPTR: eVal = *((CBaseEntity **)data); break; case FIELD_VOID: @@ -1292,6 +1516,10 @@ void variant_t::SetOther( void *data ) } } +#ifdef MAPBASE +// This way we don't have to use string comparisons when reading failed conversions +static const char *g_szNoConversion = "No conversion to string"; +#endif //----------------------------------------------------------------------------- // Purpose: Converts the variant to a new type. This function defines which I/O @@ -1324,6 +1552,24 @@ bool variant_t::Convert( fieldtype_t newType ) return true; } +#ifdef MAPBASE + if (newType == FIELD_STRING) + { + // I got a conversion error when I tried to convert int to string. I'm actually quite baffled. + // Was that case really not handled before? Did I do something that overrode something that already did this? + const char *szString = ToString(); + + // g_szNoConversion is returned in ToString() when we can't convert to a string, + // so this is safe and it lets us get away with a pointer comparison. + if (szString != g_szNoConversion) + { + SetString(AllocPooledString(szString)); + return true; + } + return false; + } +#endif + switch ( fieldType ) { case FIELD_INTEGER: @@ -1441,8 +1687,14 @@ bool variant_t::Convert( fieldtype_t newType ) CBaseEntity *ent = NULL; if ( iszVal != NULL_STRING ) { +#ifdef MAPBASE + // We search by both entity name and class name now. + // We also have an entirely new version of Convert specifically for !activators on FIELD_EHANDLE. + ent = gEntList.FindEntityGeneric( NULL, STRING(iszVal) ); +#else // FIXME: do we need to pass an activator in here? ent = gEntList.FindEntityByName( NULL, iszVal ); +#endif } SetEntity( ent ); return true; @@ -1452,6 +1704,7 @@ bool variant_t::Convert( fieldtype_t newType ) break; } +#ifndef MAPBASE // ToString() above handles this case FIELD_EHANDLE: { switch ( newType ) @@ -1469,12 +1722,64 @@ bool variant_t::Convert( fieldtype_t newType ) } break; } +#endif + +#ifdef MAPBASE + case FIELD_VOID: + { + // Many fields already turn into some equivalent of "NULL" when given a null string_t. + // This takes advantage of that and allows FIELD_VOID to be converted to more than just empty strings. + SetString(NULL_STRING); + return Convert(newType); + } +#endif } // invalid conversion return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Only for when something like !activator needs to become a FIELD_EHANDLE, or when that's a possibility. +//----------------------------------------------------------------------------- +bool variant_t::Convert( fieldtype_t newType, CBaseEntity *pSelf, CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + // Support for turning !activator, !caller, and !self into a FIELD_EHANDLE. + // Extremely necessary. + if (newType == FIELD_EHANDLE) + { + if (newType == fieldType) + return true; + + CBaseEntity *ent = NULL; + if (iszVal != NULL_STRING) + { + ent = gEntList.FindEntityGeneric(NULL, STRING(iszVal), pSelf, pActivator, pCaller); + } + SetEntity(ent); + return true; + } + +#if 0 // This was scrapped almost immediately. See the Trello card for details. + // Serves as a way of converting the name of the !activator, !caller, or !self into a string + // without passing the text "!activator" and stuff. + else if (fieldType == FIELD_STRING && STRING(iszVal)[0] == '&') + { + const char *val = STRING(iszVal) + 1; + + #define GetRealName(string, ent) if (FStrEq(val, string)) { if (ent) {SetString(ent->GetEntityName());} return true; } + + GetRealName("!activator", pActivator) + else GetRealName("!caller", pCaller) + else GetRealName("!self", pSelf) + } +#endif + + return Convert(newType); +} +#endif + //----------------------------------------------------------------------------- // Purpose: All types must be able to display as strings for debugging purposes. @@ -1542,13 +1847,22 @@ const char *variant_t::ToString( void ) const case FIELD_EHANDLE: { +#ifdef MAPBASE + // This is a really bad idea. + const char *pszName = (Entity()) ? Entity()->GetDebugName() : "<>"; +#else const char *pszName = (Entity()) ? STRING(Entity()->GetEntityName()) : "<>"; +#endif Q_strncpy( szBuf, pszName, 512 ); return (szBuf); } } +#ifdef MAPBASE + return g_szNoConversion; +#else return("No conversion to string"); +#endif } #define classNameTypedef variant_t // to satisfy DEFINE... macros @@ -1756,6 +2070,21 @@ class CVariantSaveDataOps : public CDefSaveRestoreOps // Don't no how to. This is okay, since objects of this type // are always born clean before restore, and not reused } + +#ifdef MAPBASE + // Parses a keyvalue string into a variant_t. + // We could just turn it into a string since variant_t can convert it later, but this keyvalue is probably a variant_t for a reason, + // meaning it might use strings and numbers completely differently without converting them. + // As a result, we try to read it to figure out what type it is. + virtual bool Parse( const SaveRestoreFieldInfo_t &fieldInfo, char const* szValue ) + { + variant_t *var = (variant_t*)fieldInfo.pField; + + *var = Variant_Parse(szValue); + + return true; + } +#endif }; CVariantSaveDataOps g_VariantSaveDataOps; diff --git a/mp/src/game/server/cbase.h b/mp/src/game/server/cbase.h index 0d56ec3b..290e3b25 100644 --- a/mp/src/game/server/cbase.h +++ b/mp/src/game/server/cbase.h @@ -80,6 +80,9 @@ #include "baseentity_shared.h" #include "basetoggle.h" #include "igameevents.h" +#ifdef MAPBASE +#include "tier1/mapbase_con_groups.h" +#endif // saverestore.h declarations class ISave; diff --git a/mp/src/game/server/client.cpp b/mp/src/game/server/client.cpp index eced1df1..b555f92b 100644 --- a/mp/src/game/server/client.cpp +++ b/mp/src/game/server/client.cpp @@ -45,6 +45,10 @@ #include "weapon_physcannon.h" #endif +#ifdef MAPBASE +#include "fmtstr.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -388,6 +392,16 @@ void ClientPrecache( void ) CBaseEntity::PrecacheScriptSound( "Bounce.Shell" ); CBaseEntity::PrecacheScriptSound( "Bounce.Concrete" ); +#ifdef MAPBASE + // Game Instructor sounds + CBaseEntity::PrecacheScriptSound( "Instructor.LessonStart" ); + CBaseEntity::PrecacheScriptSound( "Instructor.ImportantLessonStart" ); + + // TODO: Does sv_pure cover this? This is from the ASW SDK to prevent people from making simple scripted wall hacks + //engine->ForceExactFile( "scripts/instructor_lessons.txt" ); + //engine->ForceExactFile( "scripts/mod_lessons.txt" ); +#endif + ClientGamePrecache(); } @@ -1591,6 +1605,32 @@ void ClientCommand( CBasePlayer *pPlayer, const CCommand &args ) { if ( !g_pGameRules->ClientCommand( pPlayer, args ) ) { +#ifdef MAPBASE_VSCRIPT + // Console command hook for VScript + if ( pPlayer->m_ScriptScope.IsInitialized() ) + { + ScriptVariant_t functionReturn; + g_pScriptVM->SetValue( "command", ScriptVariant_t( pCmd ) ); + + ScriptVariant_t varTable; + g_pScriptVM->CreateTable( varTable ); + HSCRIPT hTable = varTable.m_hScript; + for ( int i = 0; i < args.ArgC(); i++ ) + { + g_pScriptVM->SetValue( hTable, CNumStr( i ), ScriptVariant_t( args[i] ) ); + } + g_pScriptVM->SetValue( "args", varTable ); + + pPlayer->CallScriptFunction( "ClientCommand", &functionReturn ); + + g_pScriptVM->ClearValue( "command" ); + g_pScriptVM->ClearValue( "args" ); + g_pScriptVM->ReleaseValue( varTable ); + + if (functionReturn.m_bool) + return; + } +#endif if ( Q_strlen( pCmd ) > 128 ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Console command too long.\n" ); diff --git a/mp/src/game/server/effects.cpp b/mp/src/game/server/effects.cpp index f9d457c6..7b07e157 100644 --- a/mp/src/game/server/effects.cpp +++ b/mp/src/game/server/effects.cpp @@ -29,11 +29,19 @@ #include "Sprite.h" #include "precipitation_shared.h" #include "shot_manipulator.h" +#ifdef MAPBASE +#include "point_template.h" +#include "TemplateEntities.h" +#include "mapentities_shared.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them. +#ifdef MAPBASE +#define SF_FUNNEL_DONT_REMOVE 2 +#endif #define SF_GIBSHOOTER_REPEATABLE (1<<0) // allows a gibshooter to be refired #define SF_SHOOTER_FLAMING (1<<1) // gib is on fire @@ -536,7 +544,11 @@ CBaseEntity *CGibShooter::SpawnGib( const Vector &vecShootDir, float flSpeed ) { // UNDONE: Assume a mass of 200 for now Vector force = vecShootDir * flSpeed * 200; +#ifdef MAPBASE + return CreateRagGib( STRING( GetModelName() ), GetAbsOrigin(), GetAbsAngles(), force, m_flGibLife, HasSpawnFlags(SF_SHOOTER_FLAMING) ); +#else return CreateRagGib( STRING( GetModelName() ), GetAbsOrigin(), GetAbsAngles(), force, m_flGibLife ); +#endif } case GIB_SIMULATE_PHYSICS: @@ -1246,6 +1258,10 @@ public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); int m_iSprite; // Don't save, precache +#ifdef MAPBASE + // This unfortunately doesn't work with the way the effect is set up + //string_t m_iszSprite; +#endif }; LINK_ENTITY_TO_CLASS( env_funnel, CEnvFunnel ); @@ -1255,6 +1271,10 @@ LINK_ENTITY_TO_CLASS( env_funnel, CEnvFunnel ); //--------------------------------------------------------- BEGIN_DATADESC( CEnvFunnel ) +#ifdef MAPBASE + //DEFINE_KEYFIELD( m_iszSprite, FIELD_STRING, "Sprite" ), + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputUse ), +#endif // DEFINE_FIELD( m_iSprite, FIELD_INTEGER ), END_DATADESC() @@ -1263,7 +1283,15 @@ END_DATADESC() void CEnvFunnel::Precache ( void ) { +#ifdef MAPBASE + //if (m_iszSprite == NULL_STRING) + // m_iszSprite = AllocPooledString("sprites/flare6.vmt"); + + //m_iSprite = PrecacheModel(STRING(m_iszSprite)); m_iSprite = PrecacheModel ( "sprites/flare6.vmt" ); +#else + m_iSprite = PrecacheModel ( "sprites/flare6.vmt" ); +#endif } void CEnvFunnel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) @@ -1272,6 +1300,10 @@ void CEnvFunnel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE us te->LargeFunnel( filter, 0.0, &GetAbsOrigin(), m_iSprite, HasSpawnFlags( SF_FUNNEL_REVERSE ) ? 1 : 0 ); +#ifdef MAPBASE + if (HasSpawnFlags(SF_FUNNEL_DONT_REMOVE)) + return; +#endif SetThink( &CEnvFunnel::SUB_Remove ); SetNextThink( gpGlobals->curtime ); } @@ -1480,6 +1512,7 @@ public: DECLARE_SERVERCLASS(); CPrecipitation(); + int UpdateTransmitState(); void Spawn( void ); CNetworkVar( PrecipitationType_t, m_nPrecipType ); @@ -1493,7 +1526,10 @@ END_DATADESC() // Just send the normal entity crap IMPLEMENT_SERVERCLASS_ST( CPrecipitation, DT_Precipitation) - SendPropInt( SENDINFO( m_nPrecipType ), Q_log2( NUM_PRECIPITATION_TYPES ) + 1, SPROP_UNSIGNED ) + SendPropInt( SENDINFO( m_nPrecipType ), Q_log2( NUM_PRECIPITATION_TYPES ) + 1, SPROP_UNSIGNED ), +#ifdef MAPBASE + SendPropInt( SENDINFO( m_spawnflags ), 2, SPROP_UNSIGNED ), +#endif END_SEND_TABLE() @@ -1502,17 +1538,35 @@ CPrecipitation::CPrecipitation() m_nPrecipType = PRECIPITATION_TYPE_RAIN; // default to rain. } +int CPrecipitation::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + void CPrecipitation::Spawn( void ) { + //SetTransmitState( FL_EDICT_ALWAYS ); + SetTransmitState( FL_EDICT_PVSCHECK ); + PrecacheMaterial( "effects/fleck_ash1" ); PrecacheMaterial( "effects/fleck_ash2" ); PrecacheMaterial( "effects/fleck_ash3" ); PrecacheMaterial( "effects/ember_swirling001" ); Precache(); - SetSolid( SOLID_NONE ); // Remove model & collisions SetMoveType( MOVETYPE_NONE ); SetModel( STRING( GetModelName() ) ); // Set size + if ( IsParticleRainType( m_nPrecipType ) ) + { + SetSolid( SOLID_VPHYSICS ); + AddSolidFlags( FSOLID_NOT_SOLID ); + AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED ); + VPhysicsInitStatic(); + } + else + { + SetSolid( SOLID_NONE ); // Remove model & collisions + } // Default to rain. if ( m_nPrecipType < 0 || m_nPrecipType > NUM_PRECIPITATION_TYPES ) @@ -1559,6 +1613,11 @@ BEGIN_DATADESC( CEnvWind ) DEFINE_KEYFIELD( m_EnvWindShared.m_iGustDirChange, FIELD_INTEGER, "gustdirchange" ), DEFINE_KEYFIELD( m_EnvWindShared.m_flGustDuration, FIELD_FLOAT, "gustduration" ), // DEFINE_KEYFIELD( m_EnvWindShared.m_iszGustSound, FIELD_STRING, "gustsound" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_EnvWindShared.m_windRadius, FIELD_FLOAT, "windradius" ), + DEFINE_KEYFIELD( m_EnvWindShared.m_windRadiusInner, FIELD_FLOAT, "windradiusinner" ), + DEFINE_KEYFIELD( m_EnvWindShared.m_flTreeSwayScale, FIELD_FLOAT, "treeswayscale" ), +#endif // Just here to quiet down classcheck // DEFINE_FIELD( m_EnvWindShared, CEnvWindShared ), @@ -1566,6 +1625,20 @@ BEGIN_DATADESC( CEnvWind ) DEFINE_FIELD( m_EnvWindShared.m_iWindDir, FIELD_INTEGER ), DEFINE_FIELD( m_EnvWindShared.m_flWindSpeed, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_INPUT( m_EnvWindShared.m_iMinWind, FIELD_INTEGER, "SetMinWind" ), + DEFINE_INPUT( m_EnvWindShared.m_iMaxWind, FIELD_INTEGER, "SetMaxWind" ), + DEFINE_INPUT( m_EnvWindShared.m_iMinGust, FIELD_INTEGER, "SetMinGust" ), + DEFINE_INPUT( m_EnvWindShared.m_iMaxGust, FIELD_INTEGER, "SetMaxGust" ), + DEFINE_INPUT( m_EnvWindShared.m_flMinGustDelay, FIELD_FLOAT, "SetMinGustDelay" ), + DEFINE_INPUT( m_EnvWindShared.m_flMaxGustDelay, FIELD_FLOAT, "SetMaxGustDelay" ), + DEFINE_INPUT( m_EnvWindShared.m_iGustDirChange, FIELD_INTEGER, "SetGustDirChange" ), + DEFINE_INPUT( m_EnvWindShared.m_flGustDuration, FIELD_FLOAT, "SetGustDuration" ), + DEFINE_INPUT( m_EnvWindShared.m_windRadius, FIELD_FLOAT, "SetWindRadius" ), + DEFINE_INPUT( m_EnvWindShared.m_windRadiusInner, FIELD_FLOAT, "SetWindRadiusInner" ), + DEFINE_INPUT( m_EnvWindShared.m_flTreeSwayScale, FIELD_FLOAT, "SetTreeSwayScale" ), +#endif + DEFINE_OUTPUT( m_EnvWindShared.m_OnGustStart, "OnGustStart" ), DEFINE_OUTPUT( m_EnvWindShared.m_OnGustEnd, "OnGustEnd" ), @@ -1594,6 +1667,12 @@ BEGIN_SEND_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared) SendPropFloat (SENDINFO(m_flGustDuration), 0, SPROP_NOSCALE), // Sound related // SendPropInt (SENDINFO(m_iszGustSound), 10, SPROP_UNSIGNED ), +#ifdef MAPBASE + SendPropFloat (SENDINFO(m_windRadius), 0, SPROP_NOSCALE), + SendPropFloat (SENDINFO(m_windRadiusInner), 0, SPROP_NOSCALE), + SendPropVector (SENDINFO(m_location), -1, SPROP_COORD), + SendPropFloat (SENDINFO(m_flTreeSwayScale), 0, SPROP_NOSCALE), +#endif END_SEND_TABLE() // This table encodes the CBaseEntity data. @@ -1615,7 +1694,13 @@ void CEnvWind::Spawn( void ) SetSolid( SOLID_NONE ); AddEffects( EF_NODRAW ); +#ifdef MAPBASE + m_EnvWindShared.m_iInitialWindDir = (int)(anglemod( m_EnvWindShared.m_iInitialWindDir )); + m_EnvWindShared.Init( entindex(), 0, gpGlobals->curtime, GetLocalAngles().y, 0 ); + m_EnvWindShared.m_location = GetAbsOrigin(); +#else m_EnvWindShared.Init( entindex(), 0, gpGlobals->frametime, GetLocalAngles().y, 0 ); +#endif SetThink( &CEnvWind::WindThink ); SetNextThink( gpGlobals->curtime ); @@ -2037,6 +2122,10 @@ public: void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputFireBurst( inputdata_t &inputdata ); +#endif + int m_iMinBurstSize; int m_iMaxBurstSize; @@ -2062,6 +2151,10 @@ public: EHANDLE m_hTarget; +#ifdef MAPBASE + COutputEvent m_OnFire; +#endif + DECLARE_DATADESC(); }; @@ -2090,6 +2183,11 @@ BEGIN_DATADESC( CEnvGunfire ) DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "FireBurst", InputFireBurst ), + + DEFINE_OUTPUT( m_OnFire, "OnFire" ), +#endif END_DATADESC() LINK_ENTITY_TO_CLASS( env_gunfire, CEnvGunfire ); @@ -2234,10 +2332,26 @@ void CEnvGunfire::ShootThink() m_iShotsRemaining--; +#ifdef MAPBASE + m_OnFire.FireOutput(m_hTarget, this); +#endif + if( m_iShotsRemaining == 0 ) { +#ifdef MAPBASE + if (m_bDisabled) + { + StopShooting(); + } + else + { + StartShooting(); + SetNextThink( gpGlobals->curtime + random->RandomFloat( m_flMinBurstDelay, m_flMaxBurstDelay ) ); + } +#else StartShooting(); SetNextThink( gpGlobals->curtime + random->RandomFloat( m_flMinBurstDelay, m_flMaxBurstDelay ) ); +#endif } } @@ -2257,6 +2371,18 @@ void CEnvGunfire::InputDisable( inputdata_t &inputdata ) SetThink( NULL ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvGunfire::InputFireBurst( inputdata_t &inputdata ) +{ + m_iShotsRemaining = inputdata.value.Int(); + + SetThink( &CEnvGunfire::ShootThink ); + SetNextThink( gpGlobals->curtime ); +} +#endif + //----------------------------------------------------------------------------- // Quadratic spline beam effect //----------------------------------------------------------------------------- @@ -2377,3 +2503,126 @@ void CEnvViewPunch::InputViewPunch( inputdata_t &inputdata ) { DoViewPunch(); } + +#ifdef MAPBASE +class CBreakableGibShooter : public CBaseEntity +{ + DECLARE_CLASS( CBreakableGibShooter, CBaseEntity ); + DECLARE_DATADESC(); +public: + + const char *GetRandomTemplateModel( CPointTemplate *pTemplate ); + + void Precache( void ); + + void Shoot( void ); + + // ---------------- + // Inputs + // ---------------- + void InputShoot( inputdata_t &inputdata ); + +public: + + int m_iModelType; + enum + { + MODELTYPE_BREAKABLECHUNKS, + MODELTYPE_MODEL, + MODELTYPE_TEMPLATE, + }; + + int m_iCount; + float m_flDelay; + Vector m_vecGibSize; + float m_flGibSpeed; + int m_iRandomization; + float m_flLifetime; + int m_iGibFlags; +}; + +BEGIN_DATADESC( CBreakableGibShooter ) + + DEFINE_KEYFIELD( m_iModelType, FIELD_INTEGER, "modeltype" ), + DEFINE_INPUT( m_iCount, FIELD_INTEGER, "SetCount" ), + DEFINE_INPUT( m_flDelay, FIELD_FLOAT, "SetDelay" ), + DEFINE_INPUT( m_vecGibSize, FIELD_VECTOR, "SetGibSize" ), + DEFINE_INPUT( m_flGibSpeed, FIELD_FLOAT, "SetGibSpeed" ), + DEFINE_INPUT( m_iRandomization, FIELD_INTEGER, "SetRandomization" ), + DEFINE_INPUT( m_flLifetime, FIELD_FLOAT, "SetLifetime" ), + DEFINE_INPUT( m_iGibFlags, FIELD_INTEGER, "SetGibFlags" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Shoot", InputShoot ), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( env_break_shooter, CBreakableGibShooter ); + + +const char *CBreakableGibShooter::GetRandomTemplateModel( CPointTemplate *pTemplate ) +{ + int iIndex = RandomInt( 0, pTemplate->GetNumTemplates() ); + char *iszTemplate = (char*)(STRING(Templates_FindByIndex(pTemplate->GetTemplateIndexForTemplate(iIndex)))); + + CEntityMapData entData( iszTemplate ); + + // This might seem a little messy, but I think it's cheaper than creating the entity. + char szModel[MAPKEY_MAXLENGTH]; + if (!entData.ExtractValue("model", szModel)) + return NULL; + + return strdup(szModel); +} + +void CBreakableGibShooter::Precache( void ) +{ + if (m_iModelType == MODELTYPE_MODEL) + PrecacheModel( STRING(GetModelName()) ); +} + +void CBreakableGibShooter::Shoot( void ) +{ + int iModelIndex = 0; + if (m_iModelType == MODELTYPE_MODEL) + iModelIndex = modelinfo->GetModelIndex( STRING(GetModelName()) ); + + CPointTemplate *pTemplate = NULL; + if (m_iModelType == MODELTYPE_TEMPLATE) + { + pTemplate = dynamic_cast(gEntList.FindEntityByName(NULL, STRING(GetModelName()), this)); + if (!pTemplate) + { + Warning("%s cannot find point_template %s!\n", GetDebugName(), STRING(GetModelName())); + return; + } + } + + CPVSFilter filter( GetAbsOrigin() ); + for ( int i = 0; i < m_iCount; i++ ) + { + if (m_iModelType == MODELTYPE_BREAKABLECHUNKS) + iModelIndex = modelinfo->GetModelIndex( g_PropDataSystem.GetRandomChunkModel( STRING( GetModelName() ) ) ); + else if (m_iModelType == MODELTYPE_TEMPLATE) + iModelIndex = modelinfo->GetModelIndex( GetRandomTemplateModel(pTemplate) ); + + // All objects except the first one in this run are marked as slaves... + int slaveFlag = 0; + if ( i != 0 ) + { + slaveFlag = BREAK_SLAVE; + } + + Vector vecShootDir; + AngleVectors( GetAbsAngles(), &vecShootDir ); + VectorNormalize( vecShootDir ); + + te->BreakModel( filter, m_flDelay, GetAbsOrigin(), GetAbsAngles(), m_vecGibSize, vecShootDir * m_flGibSpeed, iModelIndex, m_iRandomization, 1, m_flLifetime, m_iGibFlags | slaveFlag ); + } +} + +void CBreakableGibShooter::InputShoot( inputdata_t &inputdata ) +{ + Shoot(); +} +#endif diff --git a/mp/src/game/server/enginecallback.h b/mp/src/game/server/enginecallback.h index cce07866..45993be9 100644 --- a/mp/src/game/server/enginecallback.h +++ b/mp/src/game/server/enginecallback.h @@ -26,6 +26,7 @@ class IDataCache; class IMDLCache; class IServerEngineTools; class IXboxSystem; +class IScriptManager; class CSteamAPIContext; class CSteamGameServerAPIContext; @@ -43,6 +44,7 @@ extern IDataCache *datacache; extern IMDLCache *mdlcache; extern IServerEngineTools *serverenginetools; extern IXboxSystem *xboxsystem; // 360 only +extern IScriptManager *scriptmanager; extern CSteamAPIContext *steamapicontext; // available on game clients extern CSteamGameServerAPIContext *steamgameserverapicontext; //available on game servers diff --git a/mp/src/game/server/entitylist.cpp b/mp/src/game/server/entitylist.cpp index d1afa218..00b83b66 100644 --- a/mp/src/game/server/entitylist.cpp +++ b/mp/src/game/server/entitylist.cpp @@ -20,6 +20,9 @@ #ifdef HL2_DLL #include "npc_playercompanion.h" +#ifdef MAPBASE +#include "hl2_player.h" +#endif #endif // HL2_DLL // memdbgon must be the last include file in a .cpp file!!! @@ -479,7 +482,11 @@ bool CGlobalEntityList::IsEntityPtr( void *pTest ) // Input : pStartEntity - Last entity found, NULL to start a new iteration. // szName - Classname to search for. //----------------------------------------------------------------------------- +#ifdef MAPBASE +CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName, IEntityFindFilter *pFilter ) +#else CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName ) +#endif { const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo(); @@ -492,8 +499,18 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity continue; } +#ifdef MAPBASE + if ( pEntity->ClassMatches(szName) ) + { + if ( pFilter && !pFilter->ShouldFindEntity(pEntity) ) + continue; + + return pEntity; + } +#else if ( pEntity->ClassMatches(szName) ) return pEntity; +#endif } return NULL; @@ -551,12 +568,60 @@ CBaseEntity *CGlobalEntityList::FindEntityProcedural( const char *szName, CBaseE } else if ( FStrEq( pName, "picker" ) ) { +#ifdef MAPBASE_MP + // TODO: Player could be activator instead + CBasePlayer *pPlayer = ToBasePlayer(pSearchingEntity); + return FindPickerEntity( pPlayer ? pPlayer : UTIL_PlayerByIndex(1) ); +#else return FindPickerEntity( UTIL_PlayerByIndex(1) ); +#endif } else if ( FStrEq( pName, "self" ) ) { return pSearchingEntity; } +#ifdef MAPBASE + else if ( FStrEq( pName, "parent" ) ) + { + return pSearchingEntity ? pSearchingEntity->GetParent() : NULL; + } + else if ( FStrEq( pName, "owner" ) ) + { + return pSearchingEntity ? pSearchingEntity->GetOwnerEntity() : NULL; + } + else if ( FStrEq( pName, "plrsquadrep" ) ) + { +#ifdef HL2_DLL + CHL2_Player *pPlayer = static_cast(UTIL_PlayerByIndex(1)); + if (pPlayer) + { + return pPlayer->GetSquadCommandRepresentative(); + } +#endif + } + else if (strchr(pName, ':')) + { + char name[128]; + Q_strncpy(name, pName, strchr(pName, ':')-pName+1); + + CBaseEntity *pEntity = FindEntityProcedural(UTIL_VarArgs("!%s", name), pSearchingEntity, pActivator, pCaller); + if (pEntity && pEntity->IsNPC()) + { + const char *target = (Q_strstr(pName, ":") + 1); + if (target[0] != '!') + target = UTIL_VarArgs("!%s", target); + + return pEntity->MyNPCPointer()->FindNamedEntity(target); + } + } + else if (pSearchingEntity && pSearchingEntity->IsCombatCharacter()) + { + // Perhaps the entity itself has the answer? + // This opens up new possibilities. The weird filter is there so it doesn't go through this twice. + CNullEntityFilter pFilter; + return pSearchingEntity->MyCombatCharacterPointer()->FindNamedEntity(szName, &pFilter); + } +#endif else { Warning( "Invalid entity search name %s\n", szName ); @@ -602,7 +667,7 @@ CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, con continue; } - if ( !ent->m_iName ) + if ( !ent->m_iName.Get() ) continue; if ( ent->NameMatches( szName ) ) @@ -917,6 +982,20 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassnameWithin( CBaseEntity *pStart // or Use handler, NULL otherwise. // Output : Returns a pointer to the found entity, NULL if none. //----------------------------------------------------------------------------- +#ifdef MAPBASE +CBaseEntity *CGlobalEntityList::FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller, IEntityFindFilter *pFilter ) +{ + CBaseEntity *pEntity = NULL; + + pEntity = gEntList.FindEntityByName( pStartEntity, szName, pSearchingEntity, pActivator, pCaller, pFilter ); + if (!pEntity) + { + pEntity = gEntList.FindEntityByClassname( pStartEntity, szName, pFilter ); + } + + return pEntity; +} +#else CBaseEntity *CGlobalEntityList::FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller ) { CBaseEntity *pEntity = NULL; @@ -929,6 +1008,7 @@ CBaseEntity *CGlobalEntityList::FindEntityGeneric( CBaseEntity *pStartEntity, co return pEntity; } +#endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/entitylist.h b/mp/src/game/server/entitylist.h index 540e2d50..27074b3f 100644 --- a/mp/src/game/server/entitylist.h +++ b/mp/src/game/server/entitylist.h @@ -66,6 +66,16 @@ public: virtual CBaseEntity *GetFilterResult( void ) = 0; }; +#ifdef MAPBASE +// Returns false every time. Created for some sick hack involving FindEntityProcedural looking at FindNamedEntity. +class CNullEntityFilter : public IEntityFindFilter +{ +public: + virtual bool ShouldFindEntity( CBaseEntity *pEntity ) { return false; } + virtual CBaseEntity *GetFilterResult( void ) { return NULL; } +}; +#endif + //----------------------------------------------------------------------------- // Purpose: a global list of all the entities in the game. All iteration through // entities is done through this object. @@ -133,7 +143,11 @@ public: // search functions bool IsEntityPtr( void *pTest ); +#ifdef MAPBASE + CBaseEntity *FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName, IEntityFindFilter *pFilter = NULL ); +#else CBaseEntity *FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName ); +#endif CBaseEntity *FindEntityByName( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL, IEntityFindFilter *pFilter = NULL ); CBaseEntity *FindEntityByName( CBaseEntity *pStartEntity, string_t iszName, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL, IEntityFindFilter *pFilter = NULL ) { @@ -149,7 +163,11 @@ public: CBaseEntity *FindEntityByClassnameWithin( CBaseEntity *pStartEntity , const char *szName, const Vector &vecSrc, float flRadius ); CBaseEntity *FindEntityByClassnameWithin( CBaseEntity *pStartEntity , const char *szName, const Vector &vecMins, const Vector &vecMaxs ); +#ifdef MAPBASE + CBaseEntity *FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL, IEntityFindFilter *pFilter = NULL ); +#else CBaseEntity *FindEntityGeneric( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); +#endif CBaseEntity *FindEntityGenericWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); CBaseEntity *FindEntityGenericNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); diff --git a/mp/src/game/server/entityoutput.h b/mp/src/game/server/entityoutput.h index 6aeb7380..424ae761 100644 --- a/mp/src/game/server/entityoutput.h +++ b/mp/src/game/server/entityoutput.h @@ -63,6 +63,7 @@ public: void ParseEventAction( const char *EventData ); void AddEventAction( CEventAction *pEventAction ); + void RemoveEventAction( CEventAction *pEventAction ); int Save( ISave &save ); int Restore( IRestore &restore, int elementCount ); @@ -78,6 +79,12 @@ public: /// Delete every single action in the action list. void DeleteAllElements( void ) ; +#ifdef MAPBASE + // Needed for ReplaceOutput, hopefully not bad + CEventAction *GetActionList() { return m_ActionList; } + void SetActionList(CEventAction *newlist) { m_ActionList = newlist; } +#endif + protected: variant_t m_Value; CEventAction *m_ActionList; @@ -146,6 +153,29 @@ public: { m_Value.Vector3D(vec); } + +#ifdef MAPBASE + // Shortcut to using QAngles in Vector outputs, makes it look cleaner and allows easy modification + void Init( const QAngle &value ) + { + // reinterpret_cast(value) + m_Value.SetAngle3D( value ); + } + + // Shortcut to using QAngles in Vector outputs, makes it look cleaner and allows easy modification + void Set( const QAngle &value, CBaseEntity *pActivator, CBaseEntity *pCaller ) + { + // reinterpret_cast(value) + m_Value.SetAngle3D( value ); + FireOutput( m_Value, pActivator, pCaller ); + } + + // Shortcut to using QAngles in Vector outputs, makes it look cleaner and allows easy modification + void Get( QAngle &ang ) + { + m_Value.Angle3D(ang); + } +#endif }; diff --git a/mp/src/game/server/env_dof_controller.cpp b/mp/src/game/server/env_dof_controller.cpp new file mode 100644 index 00000000..d061fc73 --- /dev/null +++ b/mp/src/game/server/env_dof_controller.cpp @@ -0,0 +1,186 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: Depth of field controller entity +// +//============================================================================= + +#include "cbase.h" +#include "baseentity.h" +#include "entityoutput.h" +#include "env_dof_controller.h" +#include "ai_utils.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( env_dof_controller, CEnvDOFController ); + +BEGIN_DATADESC( CEnvDOFController ) + + DEFINE_KEYFIELD( m_bDOFEnabled, FIELD_BOOLEAN, "enabled" ), + DEFINE_KEYFIELD( m_flNearBlurDepth, FIELD_FLOAT, "near_blur" ), + DEFINE_KEYFIELD( m_flNearFocusDepth, FIELD_FLOAT, "near_focus" ), + DEFINE_KEYFIELD( m_flFarFocusDepth, FIELD_FLOAT, "far_focus" ), + DEFINE_KEYFIELD( m_flFarBlurDepth, FIELD_FLOAT, "far_blur" ), + DEFINE_KEYFIELD( m_flNearBlurRadius, FIELD_FLOAT, "near_radius" ), + DEFINE_KEYFIELD( m_flFarBlurRadius, FIELD_FLOAT, "far_radius" ), + DEFINE_KEYFIELD( m_strFocusTargetName, FIELD_STRING, "focus_target" ), + DEFINE_KEYFIELD( m_flFocusTargetRange, FIELD_FLOAT, "focus_range" ), + + DEFINE_FIELD( m_hFocusTarget, FIELD_EHANDLE ), + + DEFINE_THINKFUNC( UpdateParamBlend ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNearBlurDepth", InputSetNearBlurDepth ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNearFocusDepth", InputSetNearFocusDepth ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFarFocusDepth", InputSetFarFocusDepth ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFarBlurDepth", InputSetFarBlurDepth ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNearBlurRadius", InputSetNearBlurRadius ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFarBlurRadius", InputSetFarBlurRadius ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFocusTarget", InputSetFocusTarget ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFocusTargetRange", InputSetFocusTargetRange ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CEnvDOFController, DT_EnvDOFController ) + SendPropInt( SENDINFO(m_bDOFEnabled), 1, SPROP_UNSIGNED ), + SendPropFloat( SENDINFO(m_flNearBlurDepth), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO(m_flNearFocusDepth), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO(m_flFarFocusDepth), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO(m_flFarBlurDepth), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO(m_flNearBlurRadius), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO(m_flFarBlurRadius), 0, SPROP_NOSCALE), +END_SEND_TABLE() + +void CEnvDOFController::Spawn() +{ + SetSolid( SOLID_NONE ); + SetMoveType( MOVETYPE_NONE ); + +#ifdef MAPBASE + // Find our target entity and hold on to it + m_hFocusTarget = gEntList.FindEntityByName( NULL, m_strFocusTargetName, this ); + + // Update if we have a focal target + if ( m_hFocusTarget ) + { + SetThink( &CEnvDOFController::UpdateParamBlend ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } +#endif +} + +void CEnvDOFController::Activate() +{ + BaseClass::Activate(); + +#ifndef MAPBASE // Mapbase moves this to Spawn() to avoid issues with save/restore and entities set via the SetFocusTarget input + // Find our target entity and hold on to it + m_hFocusTarget = gEntList.FindEntityByName( NULL, m_strFocusTargetName ); + + // Update if we have a focal target + if ( m_hFocusTarget ) + { + SetThink( &CEnvDOFController::UpdateParamBlend ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } +#endif +} + +int CEnvDOFController::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +void CEnvDOFController::InputSetNearBlurDepth( inputdata_t &inputdata ) +{ + m_flNearBlurDepth = inputdata.value.Float(); +} + +void CEnvDOFController::InputSetNearFocusDepth( inputdata_t &inputdata ) +{ + m_flNearFocusDepth = inputdata.value.Float(); +} + +void CEnvDOFController::InputSetFarFocusDepth( inputdata_t &inputdata ) +{ + m_flFarFocusDepth = inputdata.value.Float(); +} + +void CEnvDOFController::InputSetFarBlurDepth( inputdata_t &inputdata ) +{ + m_flFarBlurDepth = inputdata.value.Float(); +} + +void CEnvDOFController::InputSetNearBlurRadius( inputdata_t &inputdata ) +{ + m_flNearBlurRadius = inputdata.value.Float(); + m_bDOFEnabled = ( m_flNearBlurRadius > 0.0f ) || ( m_flFarBlurRadius > 0.0f ); +} + +void CEnvDOFController::InputSetFarBlurRadius( inputdata_t &inputdata ) +{ + m_flFarBlurRadius = inputdata.value.Float(); + m_bDOFEnabled = ( m_flNearBlurRadius > 0.0f ) || ( m_flFarBlurRadius > 0.0f ); +} + +void CEnvDOFController::SetControllerState( DOFControlSettings_t setting ) +{ + m_flNearBlurDepth = setting.flNearBlurDepth; + m_flNearBlurRadius = setting.flNearBlurRadius; + m_flNearFocusDepth = setting.flNearFocusDistance; + + m_flFarBlurDepth = setting.flFarBlurDepth; + m_flFarBlurRadius = setting.flFarBlurRadius; + m_flFarFocusDepth = setting.flFarFocusDistance; + + m_bDOFEnabled = ( m_flNearBlurRadius > 0.0f ) || ( m_flFarBlurRadius > 0.0f ); +} + +#define BLUR_DEPTH 500.0f + +//----------------------------------------------------------------------------- +// Purpose: Blend the parameters to the specified value +//----------------------------------------------------------------------------- +void CEnvDOFController::UpdateParamBlend() +{ + // Update our focal target if we have one + if ( m_hFocusTarget ) + { + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + float flDistToFocus = ( m_hFocusTarget->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).Length(); + m_flFarFocusDepth.GetForModify() = flDistToFocus + m_flFocusTargetRange; + m_flFarBlurDepth.GetForModify() = m_flFarFocusDepth + BLUR_DEPTH; + + SetThink( &CEnvDOFController::UpdateParamBlend ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the "focus" target entity +//----------------------------------------------------------------------------- +void CEnvDOFController::InputSetFocusTarget( inputdata_t &inputdata ) +{ +#ifdef MAPBASE + m_hFocusTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); +#else + m_hFocusTarget = gEntList.FindEntityByName( NULL, inputdata.value.String() ); +#endif + + // Update if we have a focal target + if ( m_hFocusTarget ) + { + SetThink( &CEnvDOFController::UpdateParamBlend ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the range behind the focus entity that we'll blur (in units) +//----------------------------------------------------------------------------- +void CEnvDOFController::InputSetFocusTargetRange( inputdata_t &inputdata ) +{ + m_flFocusTargetRange = inputdata.value.Float(); +} diff --git a/mp/src/game/server/env_dof_controller.h b/mp/src/game/server/env_dof_controller.h new file mode 100644 index 00000000..e323ef52 --- /dev/null +++ b/mp/src/game/server/env_dof_controller.h @@ -0,0 +1,56 @@ +#pragma once + +struct DOFControlSettings_t +{ + // Near plane + float flNearBlurDepth; + float flNearBlurRadius; + float flNearFocusDistance; + // Far plane + float flFarBlurDepth; + float flFarBlurRadius; + float flFarFocusDistance; +}; + +//----------------------------------------------------------------------------- +// Purpose: Entity that controls depth of field postprocessing +//----------------------------------------------------------------------------- +class CEnvDOFController : public CPointEntity +{ + DECLARE_CLASS( CEnvDOFController, CPointEntity ); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + virtual void Spawn(); + virtual void Activate(); + virtual int UpdateTransmitState(); + void SetControllerState( DOFControlSettings_t setting ); + + void UpdateParamBlend(); + + // Inputs + void InputSetNearBlurDepth( inputdata_t &inputdata ); + void InputSetNearFocusDepth( inputdata_t &inputdata ); + void InputSetFarFocusDepth( inputdata_t &inputdata ); + void InputSetFarBlurDepth( inputdata_t &inputdata ); + void InputSetNearBlurRadius( inputdata_t &inputdata ); + void InputSetFarBlurRadius( inputdata_t &inputdata ); + + void InputSetFocusTarget( inputdata_t &inputdata ); + void InputSetFocusTargetRange( inputdata_t &inputdata ); + +private: + float m_flFocusTargetRange; + + string_t m_strFocusTargetName; // Name of the entity to focus on + EHANDLE m_hFocusTarget; + + CNetworkVar( bool, m_bDOFEnabled ); + CNetworkVar( float, m_flNearBlurDepth ); + CNetworkVar( float, m_flNearFocusDepth ); + CNetworkVar( float, m_flFarFocusDepth ); + CNetworkVar( float, m_flFarBlurDepth ); + CNetworkVar( float, m_flNearBlurRadius ); + CNetworkVar( float, m_flFarBlurRadius ); +}; diff --git a/mp/src/game/server/env_entity_maker.cpp b/mp/src/game/server/env_entity_maker.cpp index d8ad528e..19d8ddf6 100644 --- a/mp/src/game/server/env_entity_maker.cpp +++ b/mp/src/game/server/env_entity_maker.cpp @@ -30,6 +30,7 @@ class CEnvEntityMaker : public CPointEntity DECLARE_CLASS( CEnvEntityMaker, CPointEntity ); public: DECLARE_DATADESC(); + DECLARE_ENT_SCRIPTDESC(); virtual void Spawn( void ); virtual void Activate( void ); @@ -38,7 +39,15 @@ public: void CheckSpawnThink( void ); void InputForceSpawn( inputdata_t &inputdata ); void InputForceSpawnAtEntityOrigin( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputForceSpawnAtEntityCenter( inputdata_t &inputdata ); + void InputForceSpawnAtPosition( inputdata_t &inputdata ); +#endif + void SpawnEntityFromScript(); + void SpawnEntityAtEntityOriginFromScript(HSCRIPT hEntity); + void SpawnEntityAtNamedEntityOriginFromScript(const char* pszName); + void SpawnEntityAtLocationFromScript(const Vector& vecAlternateOrigin, const Vector& vecAlternateAngles); private: CPointTemplate *FindTemplate(); @@ -62,6 +71,9 @@ private: COutputEvent m_pOutputOnSpawned; COutputEvent m_pOutputOnFailedSpawn; +#ifdef MAPBASE + COutputEHANDLE m_pOutputOutEntity; +#endif }; BEGIN_DATADESC( CEnvEntityMaker ) @@ -79,15 +91,29 @@ BEGIN_DATADESC( CEnvEntityMaker ) // Outputs DEFINE_OUTPUT( m_pOutputOnSpawned, "OnEntitySpawned" ), DEFINE_OUTPUT( m_pOutputOnFailedSpawn, "OnEntityFailedSpawn" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_pOutputOutEntity, "OutSpawnedEntity" ), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "ForceSpawn", InputForceSpawn ), DEFINE_INPUTFUNC( FIELD_STRING, "ForceSpawnAtEntityOrigin", InputForceSpawnAtEntityOrigin ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "ForceSpawnAtEntityCenter", InputForceSpawnAtEntityCenter ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "ForceSpawnAtPosition", InputForceSpawnAtPosition ), +#endif // Functions DEFINE_THINKFUNC( CheckSpawnThink ), END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CEnvEntityMaker, CBaseEntity, "env_entity_maker" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityFromScript, "SpawnEntity", "Create an entity at the location of the maker" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtEntityOriginFromScript, "SpawnEntityAtEntityOrigin", "Create an entity at the location of a specified entity instance" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtNamedEntityOriginFromScript, "SpawnEntityAtNamedEntityOrigin", "Create an entity at the location of a named entity" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtLocationFromScript, "SpawnEntityAtLocation", "Create an entity at a specified location and orientaton, orientation is Euler angle in degrees (pitch, yaw, roll)" ) +END_SCRIPTDESC() + LINK_ENTITY_TO_CLASS( env_entity_maker, CEnvEntityMaker ); @@ -201,6 +227,11 @@ void CEnvEntityMaker::SpawnEntity( Vector vecAlternateOrigin, QAngle vecAlternat for ( int i = 0; i < hNewEntities.Count(); i++ ) { CBaseEntity *pEntity = hNewEntities[i]; + +#ifdef MAPBASE + m_pOutputOutEntity.Set(pEntity, pEntity, this); +#endif + if ( pEntity->GetMoveType() == MOVETYPE_NONE ) continue; @@ -238,8 +269,59 @@ void CEnvEntityMaker::SpawnEntity( Vector vecAlternateOrigin, QAngle vecAlternat } } } +#ifdef MAPBASE + else + { + for ( int i = 0; i < hNewEntities.Count(); i++ ) + { + m_pOutputOutEntity.Set(hNewEntities[i], hNewEntities[i], this); + } + } +#endif + + pTemplate->CreationComplete( hNewEntities ); } +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityFromScript() +{ + SpawnEntity(); +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtEntityOriginFromScript( HSCRIPT hEntity ) +{ + CBaseEntity *pTargetEntity = ToEnt( hEntity ); + if ( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtNamedEntityOriginFromScript( const char *pszName ) +{ + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, pszName, this, NULL, NULL ); + + if( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtLocationFromScript( const Vector &vecAlternateOrigin, const Vector &vecAlternateAngles ) +{ + SpawnEntity( vecAlternateOrigin, *((QAngle *)&vecAlternateAngles) ); +} //----------------------------------------------------------------------------- // Purpose: Returns whether or not the template entities can fit if spawned. @@ -364,3 +446,30 @@ void CEnvEntityMaker::InputForceSpawnAtEntityOrigin( inputdata_t &inputdata ) SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); } } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvEntityMaker::InputForceSpawnAtEntityCenter( inputdata_t &inputdata ) +{ + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); + + if( pTargetEntity ) + { + SpawnEntity( pTargetEntity->WorldSpaceCenter(), pTargetEntity->GetAbsAngles() ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvEntityMaker::InputForceSpawnAtPosition(inputdata_t &inputdata) +{ + Vector vecPos; + inputdata.value.Vector3D(vecPos); + if (vecPos != vec3_origin && vecPos.IsValid()) + { + SpawnEntity(vecPos, GetLocalAngles()); + } +} +#endif // MAPBASE + diff --git a/mp/src/game/server/env_global_light.cpp b/mp/src/game/server/env_global_light.cpp new file mode 100644 index 00000000..6c1549b2 --- /dev/null +++ b/mp/src/game/server/env_global_light.cpp @@ -0,0 +1,336 @@ +//========= Copyright 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: global dynamic light. Ported from Insolence's port of Alien Swarm's env_global_light. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//------------------------------------------------------------------------------ +// FIXME: This really should inherit from something more lightweight +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Purpose : Sunlight shadow control entity +//------------------------------------------------------------------------------ +class CGlobalLight : public CBaseEntity +{ +public: + DECLARE_CLASS( CGlobalLight, CBaseEntity ); + + CGlobalLight(); + + void Spawn( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + int UpdateTransmitState(); + + // Inputs + void InputSetAngles( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputSetTexture( inputdata_t &inputdata ); + void InputSetEnableShadows( inputdata_t &inputdata ); + void InputSetLightColor( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetBrightness( inputdata_t &inputdata ); + void InputSetColorTransitionTime( inputdata_t &inputdata ); + void InputSetXOffset( inputdata_t &inputdata ) { m_flEastOffset = inputdata.value.Float(); } + void InputSetYOffset( inputdata_t &inputdata ) { m_flForwardOffset = inputdata.value.Float(); } + void InputSetOrthoSize( inputdata_t &inputdata ) { m_flOrthoSize = inputdata.value.Float(); } + void InputSetDistance( inputdata_t &inputdata ) { m_flSunDistance = inputdata.value.Float(); } + void InputSetFOV( inputdata_t &inputdata ) { m_flFOV = inputdata.value.Float(); } + void InputSetNearZDistance( inputdata_t &inputdata ) { m_flNearZ = inputdata.value.Float(); } + void InputSetNorthOffset( inputdata_t &inputdata ) { m_flNorthOffset = inputdata.value.Float(); } +#endif + + virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + +private: + CNetworkVector( m_shadowDirection ); + + CNetworkVar( bool, m_bEnabled ); + + CNetworkString( m_TextureName, MAX_PATH ); +#ifdef MAPBASE + CNetworkVar( int, m_nSpotlightTextureFrame ); +#endif + CNetworkColor32( m_LightColor ); +#ifdef MAPBASE + CNetworkVar( float, m_flBrightnessScale ); +#endif + CNetworkVar( float, m_flColorTransitionTime ); + CNetworkVar( float, m_flSunDistance ); + CNetworkVar( float, m_flFOV ); + CNetworkVar( float, m_flNearZ ); + CNetworkVar( float, m_flNorthOffset ); +#ifdef MAPBASE + CNetworkVar( float, m_flEastOffset ); // xoffset + CNetworkVar( float, m_flForwardOffset ); // yoffset + CNetworkVar( float, m_flOrthoSize ); +#endif + CNetworkVar( bool, m_bEnableShadows ); +}; + +LINK_ENTITY_TO_CLASS(env_global_light, CGlobalLight); + +BEGIN_DATADESC( CGlobalLight ) + + DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ), + DEFINE_AUTO_ARRAY_KEYFIELD( m_TextureName, FIELD_CHARACTER, "texturename" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_nSpotlightTextureFrame, FIELD_INTEGER, "textureframe" ), +#endif + DEFINE_KEYFIELD( m_flSunDistance, FIELD_FLOAT, "distance" ), + DEFINE_KEYFIELD( m_flFOV, FIELD_FLOAT, "fov" ), + DEFINE_KEYFIELD( m_flNearZ, FIELD_FLOAT, "nearz" ), + DEFINE_KEYFIELD( m_flNorthOffset, FIELD_FLOAT, "northoffset" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flEastOffset, FIELD_FLOAT, "eastoffset" ), + DEFINE_KEYFIELD( m_flForwardOffset, FIELD_FLOAT, "forwardoffset" ), + DEFINE_KEYFIELD( m_flOrthoSize, FIELD_FLOAT, "orthosize" ), +#endif + DEFINE_KEYFIELD( m_bEnableShadows, FIELD_BOOLEAN, "enableshadows" ), + DEFINE_FIELD( m_LightColor, FIELD_COLOR32 ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flBrightnessScale, FIELD_FLOAT, "brightnessscale" ), +#endif + DEFINE_KEYFIELD( m_flColorTransitionTime, FIELD_FLOAT, "colortransitiontime" ), + + DEFINE_FIELD( m_shadowDirection, FIELD_VECTOR ), + + // Inputs +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetXOffset", InputSetXOffset ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetYOffset", InputSetYOffset ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetOrthoSize", InputSetOrthoSize ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDistance", InputSetDistance ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOV", InputSetFOV ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNearZDistance", InputSetNearZDistance ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNorthOffset", InputSetNorthOffset ), +#else + DEFINE_INPUT( m_flSunDistance, FIELD_FLOAT, "SetDistance" ), + DEFINE_INPUT( m_flFOV, FIELD_FLOAT, "SetFOV" ), + DEFINE_INPUT( m_flNearZ, FIELD_FLOAT, "SetNearZDistance" ), + DEFINE_INPUT( m_flNorthOffset, FIELD_FLOAT, "SetNorthOffset" ), +#endif + + DEFINE_INPUTFUNC( FIELD_COLOR32, "LightColor", InputSetLightColor ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetAngles", InputSetAngles ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTexture", InputSetTexture ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "EnableShadows", InputSetEnableShadows ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetBrightness", InputSetBrightness ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetColorTransitionTime", InputSetColorTransitionTime ), +#endif + +END_DATADESC() + + +IMPLEMENT_SERVERCLASS_ST_NOBASE(CGlobalLight, DT_GlobalLight) + SendPropVector(SENDINFO(m_shadowDirection), -1, SPROP_NOSCALE ), + SendPropBool(SENDINFO(m_bEnabled) ), + SendPropString(SENDINFO(m_TextureName)), +#ifdef MAPBASE + SendPropInt(SENDINFO(m_nSpotlightTextureFrame)), +#endif + /*SendPropInt(SENDINFO (m_LightColor ), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt32 ),*/ + SendPropInt(SENDINFO (m_LightColor ), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt ), +#ifdef MAPBASE + SendPropFloat( SENDINFO( m_flBrightnessScale ) ), +#endif + SendPropFloat( SENDINFO( m_flColorTransitionTime ) ), + SendPropFloat(SENDINFO(m_flSunDistance), 0, SPROP_NOSCALE ), + SendPropFloat(SENDINFO(m_flFOV), 0, SPROP_NOSCALE ), + SendPropFloat(SENDINFO(m_flNearZ), 0, SPROP_NOSCALE ), + SendPropFloat(SENDINFO(m_flNorthOffset), 0, SPROP_NOSCALE ), +#ifdef MAPBASE + SendPropFloat(SENDINFO(m_flEastOffset), 0, SPROP_NOSCALE ), + SendPropFloat(SENDINFO(m_flForwardOffset), 0, SPROP_NOSCALE ), + SendPropFloat(SENDINFO(m_flOrthoSize), 0, SPROP_NOSCALE ), +#endif + SendPropBool( SENDINFO( m_bEnableShadows ) ), +END_SEND_TABLE() + + +CGlobalLight::CGlobalLight() +{ +#if defined( _X360 ) + Q_strcpy( m_TextureName.GetForModify(), "effects/flashlight_border" ); +#else + Q_strcpy( m_TextureName.GetForModify(), "effects/flashlight001" ); +#endif +#ifdef MAPBASE + m_LightColor.Init( 255, 255, 255, 255 ); +#else + m_LightColor.Init( 255, 255, 255, 1 ); +#endif + m_flColorTransitionTime = 0.5f; + m_flSunDistance = 10000.0f; + m_flFOV = 5.0f; + m_bEnableShadows = false; +#ifdef MAPBASE + m_nSpotlightTextureFrame = 0; + m_flBrightnessScale = 1.0f; + m_flOrthoSize = 1000.0f; +#endif + m_bEnabled = true; +} + + +//------------------------------------------------------------------------------ +// Purpose : Send even though we don't have a model +//------------------------------------------------------------------------------ +int CGlobalLight::UpdateTransmitState() +{ + // ALWAYS transmit to all clients. + return SetTransmitState( FL_EDICT_ALWAYS ); +} + + +bool CGlobalLight::KeyValue( const char *szKeyName, const char *szValue ) +{ +#ifdef MAPBASE + if ( FStrEq( szKeyName, "lightcolor" ) || FStrEq( szKeyName, "color" ) ) +#else + if ( FStrEq( szKeyName, "color" ) ) +#endif + { + float tmp[4]; + UTIL_StringToFloatArray( tmp, 4, szValue ); + + m_LightColor.SetR( tmp[0] ); + m_LightColor.SetG( tmp[1] ); + m_LightColor.SetB( tmp[2] ); + m_LightColor.SetA( tmp[3] ); + } + else if ( FStrEq( szKeyName, "angles" ) ) + { + QAngle angles; + UTIL_StringToVector( angles.Base(), szValue ); + if (angles == vec3_angle) + { + angles.Init( 80, 30, 0 ); + } + Vector vForward; + AngleVectors( angles, &vForward ); + m_shadowDirection = vForward; + return true; + } + else if ( FStrEq( szKeyName, "texturename" ) ) + { +#if defined( _X360 ) + if ( Q_strcmp( szValue, "effects/flashlight001" ) == 0 ) + { + // Use this as the default for Xbox + Q_strcpy( m_TextureName.GetForModify(), "effects/flashlight_border" ); + } + else + { + Q_strcpy( m_TextureName.GetForModify(), szValue ); + } +#else + Q_strcpy( m_TextureName.GetForModify(), szValue ); +#endif + } + else if ( FStrEq( szKeyName, "StartDisabled" ) ) + { + m_bEnabled.Set( atoi( szValue ) <= 0 ); + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +bool CGlobalLight::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if ( FStrEq( szKeyName, "color" ) ) + { + Q_snprintf( szValue, iMaxLen, "%d %d %d %d", m_LightColor.GetR(), m_LightColor.GetG(), m_LightColor.GetB(), m_LightColor.GetA() ); + return true; + } + else if ( FStrEq( szKeyName, "texturename" ) ) + { + Q_snprintf( szValue, iMaxLen, "%s", m_TextureName.Get() ); + return true; + } + else if ( FStrEq( szKeyName, "StartDisabled" ) ) + { + Q_snprintf( szValue, iMaxLen, "%d", !m_bEnabled.Get() ); + return true; + } + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); +} + +//------------------------------------------------------------------------------ +// Purpose : +//------------------------------------------------------------------------------ +void CGlobalLight::Spawn( void ) +{ + Precache(); + SetSolid( SOLID_NONE ); +} + +//------------------------------------------------------------------------------ +// Input values +//------------------------------------------------------------------------------ +void CGlobalLight::InputSetAngles( inputdata_t &inputdata ) +{ + const char *pAngles = inputdata.value.String(); + + QAngle angles; + UTIL_StringToVector( angles.Base(), pAngles ); + + Vector vTemp; + AngleVectors( angles, &vTemp ); + m_shadowDirection = vTemp; +} + +//------------------------------------------------------------------------------ +// Purpose : Input handlers +//------------------------------------------------------------------------------ +void CGlobalLight::InputEnable( inputdata_t &inputdata ) +{ + m_bEnabled = true; +} + +void CGlobalLight::InputDisable( inputdata_t &inputdata ) +{ + m_bEnabled = false; +} + +void CGlobalLight::InputSetTexture( inputdata_t &inputdata ) +{ + Q_strcpy( m_TextureName.GetForModify(), inputdata.value.String() ); +} + +void CGlobalLight::InputSetEnableShadows( inputdata_t &inputdata ) +{ + m_bEnableShadows = inputdata.value.Bool(); +} + +void CGlobalLight::InputSetLightColor( inputdata_t &inputdata ) +{ + m_LightColor = inputdata.value.Color32(); +} + +#ifdef MAPBASE +void CGlobalLight::InputSetBrightness( inputdata_t &inputdata ) +{ + m_flBrightnessScale = inputdata.value.Float(); +} + +void CGlobalLight::InputSetColorTransitionTime( inputdata_t &inputdata ) +{ + m_flColorTransitionTime = inputdata.value.Float(); +} +#endif diff --git a/mp/src/game/server/env_instructor_hint.cpp b/mp/src/game/server/env_instructor_hint.cpp new file mode 100644 index 00000000..32b75041 --- /dev/null +++ b/mp/src/game/server/env_instructor_hint.cpp @@ -0,0 +1,229 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: An entity for creating instructor hints entirely with map logic +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "baseentity.h" +#include "world.h" + +#ifdef INFESTED_DLL + #include "asw_marine.h" + #include "asw_player.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CEnvInstructorHint : public CPointEntity +{ +public: + DECLARE_CLASS( CEnvInstructorHint, CPointEntity ); + DECLARE_DATADESC(); + +private: + void InputShowHint( inputdata_t &inputdata ); + void InputEndHint( inputdata_t &inputdata ); + +#ifdef MAPBASE + void InputSetCaption( inputdata_t &inputdata ) { m_iszCaption = inputdata.value.StringID(); } +#endif + + string_t m_iszReplace_Key; + string_t m_iszHintTargetEntity; + int m_iTimeout; + string_t m_iszIcon_Onscreen; + string_t m_iszIcon_Offscreen; + string_t m_iszCaption; + string_t m_iszActivatorCaption; + color32 m_Color; + float m_fIconOffset; + float m_fRange; + uint8 m_iPulseOption; + uint8 m_iAlphaOption; + uint8 m_iShakeOption; + bool m_bStatic; + bool m_bNoOffscreen; + bool m_bForceCaption; + string_t m_iszBinding; + bool m_bAllowNoDrawTarget; + bool m_bLocalPlayerOnly; +#ifdef MAPBASE + string_t m_iszStartSound; + int m_iHintTargetPos; +#endif +}; + +LINK_ENTITY_TO_CLASS( env_instructor_hint, CEnvInstructorHint ); + +BEGIN_DATADESC( CEnvInstructorHint ) + + DEFINE_KEYFIELD( m_iszReplace_Key, FIELD_STRING, "hint_replace_key" ), + DEFINE_KEYFIELD( m_iszHintTargetEntity, FIELD_STRING, "hint_target" ), + DEFINE_KEYFIELD( m_iTimeout, FIELD_INTEGER, "hint_timeout" ), + DEFINE_KEYFIELD( m_iszIcon_Onscreen, FIELD_STRING, "hint_icon_onscreen" ), + DEFINE_KEYFIELD( m_iszIcon_Offscreen, FIELD_STRING, "hint_icon_offscreen" ), + DEFINE_KEYFIELD( m_iszCaption, FIELD_STRING, "hint_caption" ), + DEFINE_KEYFIELD( m_iszActivatorCaption, FIELD_STRING, "hint_activator_caption" ), + DEFINE_KEYFIELD( m_Color, FIELD_COLOR32, "hint_color" ), + DEFINE_KEYFIELD( m_fIconOffset, FIELD_FLOAT, "hint_icon_offset" ), + DEFINE_KEYFIELD( m_fRange, FIELD_FLOAT, "hint_range" ), + DEFINE_KEYFIELD( m_iPulseOption, FIELD_CHARACTER, "hint_pulseoption" ), + DEFINE_KEYFIELD( m_iAlphaOption, FIELD_CHARACTER, "hint_alphaoption" ), + DEFINE_KEYFIELD( m_iShakeOption, FIELD_CHARACTER, "hint_shakeoption" ), + DEFINE_KEYFIELD( m_bStatic, FIELD_BOOLEAN, "hint_static" ), + DEFINE_KEYFIELD( m_bNoOffscreen, FIELD_BOOLEAN, "hint_nooffscreen" ), + DEFINE_KEYFIELD( m_bForceCaption, FIELD_BOOLEAN, "hint_forcecaption" ), + DEFINE_KEYFIELD( m_iszBinding, FIELD_STRING, "hint_binding" ), + DEFINE_KEYFIELD( m_bAllowNoDrawTarget, FIELD_BOOLEAN, "hint_allow_nodraw_target" ), + DEFINE_KEYFIELD( m_bLocalPlayerOnly, FIELD_BOOLEAN, "hint_local_player_only" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszStartSound, FIELD_STRING, "hint_start_sound" ), + DEFINE_KEYFIELD( m_iHintTargetPos, FIELD_INTEGER, "hint_target_pos" ), +#endif + + DEFINE_INPUTFUNC( FIELD_STRING, "ShowHint", InputShowHint ), + DEFINE_INPUTFUNC( FIELD_VOID, "EndHint", InputEndHint ), + +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetCaption", InputSetCaption ), +#endif + +END_DATADESC() + + +#define LOCATOR_ICON_FX_PULSE_SLOW 0x00000001 +#define LOCATOR_ICON_FX_ALPHA_SLOW 0x00000008 +#define LOCATOR_ICON_FX_SHAKE_NARROW 0x00000040 +#define LOCATOR_ICON_FX_STATIC 0x00000100 // This icon draws at a fixed location on the HUD. + +//----------------------------------------------------------------------------- +// Purpose: Input handler for showing the message and/or playing the sound. +//----------------------------------------------------------------------------- +void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) +{ + IGameEvent * event = gameeventmanager->CreateEvent( "instructor_server_hint_create", false ); + if ( event ) + { + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, m_iszHintTargetEntity ); + if( pTargetEntity == NULL && !m_bStatic ) + pTargetEntity = inputdata.pActivator; + + if( pTargetEntity == NULL ) + pTargetEntity = GetWorldEntity(); + + char szColorString[128]; + Q_snprintf( szColorString, sizeof( szColorString ), "%.3d,%.3d,%.3d", m_Color.r, m_Color.g, m_Color.b ); + + int iFlags = 0; + + iFlags |= (m_iPulseOption == 0) ? 0 : (LOCATOR_ICON_FX_PULSE_SLOW << (m_iPulseOption - 1)); + iFlags |= (m_iAlphaOption == 0) ? 0 : (LOCATOR_ICON_FX_ALPHA_SLOW << (m_iAlphaOption - 1)); + iFlags |= (m_iShakeOption == 0) ? 0 : (LOCATOR_ICON_FX_SHAKE_NARROW << (m_iShakeOption - 1)); + iFlags |= m_bStatic ? LOCATOR_ICON_FX_STATIC : 0; + + CBasePlayer *pActivator = NULL; + bool bFilterByActivator = m_bLocalPlayerOnly; + +#ifdef INFESTED_DLL + CASW_Marine *pMarine = dynamic_cast( inputdata.pActivator ); + if ( pMarine ) + { + pActivator = pMarine->GetCommander(); + } +#else + if ( inputdata.value.StringID() != NULL_STRING ) + { + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String() ); + pActivator = dynamic_cast( pTarget ); + if ( pActivator ) + { + bFilterByActivator = true; + } + } + else + { + if ( GameRules()->IsMultiplayer() == false ) + { + pActivator = UTIL_GetLocalPlayer(); + } + else + { + Warning( "Failed to play server side instructor hint: no player specified for hint\n" ); + Assert( 0 ); + } + } +#endif + + const char *pActivatorCaption = m_iszActivatorCaption.ToCStr(); + if ( !pActivatorCaption || pActivatorCaption[ 0 ] == '\0' ) + { + pActivatorCaption = m_iszCaption.ToCStr(); + } + + event->SetString( "hint_name", GetEntityName().ToCStr() ); + event->SetString( "hint_replace_key", m_iszReplace_Key.ToCStr() ); + event->SetInt( "hint_target", pTargetEntity->entindex() ); + event->SetInt( "hint_activator_userid", ( pActivator ? pActivator->GetUserID() : 0 ) ); + event->SetInt( "hint_timeout", m_iTimeout ); + event->SetString( "hint_icon_onscreen", m_iszIcon_Onscreen.ToCStr() ); + event->SetString( "hint_icon_offscreen", m_iszIcon_Offscreen.ToCStr() ); + event->SetString( "hint_caption", m_iszCaption.ToCStr() ); + event->SetString( "hint_activator_caption", pActivatorCaption ); + event->SetString( "hint_color", szColorString ); + event->SetFloat( "hint_icon_offset", m_fIconOffset ); + event->SetFloat( "hint_range", m_fRange ); + event->SetInt( "hint_flags", iFlags ); + event->SetString( "hint_binding", m_iszBinding.ToCStr() ); + event->SetBool( "hint_allow_nodraw_target", m_bAllowNoDrawTarget ); + event->SetBool( "hint_nooffscreen", m_bNoOffscreen ); + event->SetBool( "hint_forcecaption", m_bForceCaption ); + event->SetBool( "hint_local_player_only", bFilterByActivator ); +#ifdef MAPBASE + event->SetString( "hint_start_sound", m_iszStartSound.ToCStr() ); + event->SetInt( "hint_target_pos", m_iHintTargetPos ); +#endif + + gameeventmanager->FireEvent( event ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvInstructorHint::InputEndHint( inputdata_t &inputdata ) +{ + IGameEvent * event = gameeventmanager->CreateEvent( "instructor_server_hint_stop", false ); + if ( event ) + { + event->SetString( "hint_name", GetEntityName().ToCStr() ); + + gameeventmanager->FireEvent( event ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: A generic target entity that gets replicated to the client for instructor hint targetting +//----------------------------------------------------------------------------- +class CInfoInstructorHintTarget : public CPointEntity +{ +public: + DECLARE_CLASS( CInfoInstructorHintTarget, CPointEntity ); + + virtual int UpdateTransmitState( void ) // set transmit filter to transmit always + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( info_target_instructor_hint, CInfoInstructorHintTarget ); + +BEGIN_DATADESC( CInfoInstructorHintTarget ) + +END_DATADESC() diff --git a/mp/src/game/server/env_projectedtexture.cpp b/mp/src/game/server/env_projectedtexture.cpp index 3cdacb40..0299fbd0 100644 --- a/mp/src/game/server/env_projectedtexture.cpp +++ b/mp/src/game/server/env_projectedtexture.cpp @@ -6,11 +6,462 @@ #include "cbase.h" #include "shareddefs.h" +#ifdef ASW_PROJECTED_TEXTURES +#include "env_projectedtexture.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef ASW_PROJECTED_TEXTURES + +LINK_ENTITY_TO_CLASS( env_projectedtexture, CEnvProjectedTexture ); + +BEGIN_DATADESC( CEnvProjectedTexture ) + DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bDontFollowTarget, FIELD_BOOLEAN, "dontfollowtarget" ), + DEFINE_FIELD( m_bAlwaysUpdate, FIELD_BOOLEAN ), +#endif + DEFINE_FIELD( m_bState, FIELD_BOOLEAN ), + DEFINE_KEYFIELD( m_flLightFOV, FIELD_FLOAT, "lightfov" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flLightHorFOV, FIELD_FLOAT, "lighthorfov" ), +#endif + DEFINE_KEYFIELD( m_bEnableShadows, FIELD_BOOLEAN, "enableshadows" ), + DEFINE_KEYFIELD( m_bLightOnlyTarget, FIELD_BOOLEAN, "lightonlytarget" ), + DEFINE_KEYFIELD( m_bLightWorld, FIELD_BOOLEAN, "lightworld" ), + DEFINE_KEYFIELD( m_bCameraSpace, FIELD_BOOLEAN, "cameraspace" ), + DEFINE_KEYFIELD( m_flAmbient, FIELD_FLOAT, "ambient" ), + DEFINE_AUTO_ARRAY( m_SpotlightTextureName, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_nSpotlightTextureFrame, FIELD_INTEGER, "textureframe" ), + DEFINE_KEYFIELD( m_flNearZ, FIELD_FLOAT, "nearz" ), + DEFINE_KEYFIELD( m_flFarZ, FIELD_FLOAT, "farz" ), + DEFINE_KEYFIELD( m_nShadowQuality, FIELD_INTEGER, "shadowquality" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bAlwaysDraw, FIELD_BOOLEAN, "alwaysdraw" ), + DEFINE_KEYFIELD( m_bProjectedTextureVersion, FIELD_BOOLEAN, "ProjectedTextureVersion" ), +#endif + DEFINE_KEYFIELD( m_flBrightnessScale, FIELD_FLOAT, "brightnessscale" ), + DEFINE_FIELD( m_LightColor, FIELD_COLOR32 ), + DEFINE_KEYFIELD( m_flColorTransitionTime, FIELD_FLOAT, "colortransitiontime" ), +#ifdef MAPBASE + DEFINE_FIELD( m_flConstantAtten, FIELD_FLOAT ), + DEFINE_FIELD( m_flLinearAtten, FIELD_FLOAT ), + DEFINE_FIELD( m_flQuadraticAtten, FIELD_FLOAT ), + DEFINE_KEYFIELD( m_flShadowAtten, FIELD_FLOAT, "shadowatten" ), +#endif + + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), + DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysUpdateOn", InputAlwaysUpdateOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysUpdateOff", InputAlwaysUpdateOff ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "FOV", InputSetFOV ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "VerFOV", InputSetVerFOV ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "HorFOV", InputSetHorFOV ), +#endif + DEFINE_INPUTFUNC( FIELD_EHANDLE, "Target", InputSetTarget ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "CameraSpace", InputSetCameraSpace ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "LightOnlyTarget", InputSetLightOnlyTarget ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "LightWorld", InputSetLightWorld ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "EnableShadows", InputSetEnableShadows ), + DEFINE_INPUTFUNC( FIELD_COLOR32, "LightColor", InputSetLightColor ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "Ambient", InputSetAmbient ), + DEFINE_INPUTFUNC( FIELD_STRING, "SpotlightTexture", InputSetSpotlightTexture ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpotlightFrame", InputSetSpotlightFrame ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetBrightness", InputSetBrightness ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetColorTransitionTime", InputSetColorTransitionTime ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetQuadratic", InputSetQuadratic ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLinear", InputSetLinear ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetConstant", InputSetConstant ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetShadowAtten", InputSetShadowAtten ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNearZ", InputSetNearZ ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFarZ", InputSetFarZ ), + DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysDrawOn", InputAlwaysDrawOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysDrawOff", InputAlwaysDrawOff ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopFollowingTarget", InputStopFollowingTarget ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartFollowingTarget", InputStartFollowingTarget ), +#endif + DEFINE_THINKFUNC( InitialThink ), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CEnvProjectedTexture, DT_EnvProjectedTexture ) + SendPropEHandle( SENDINFO( m_hTargetEntity ) ), +#ifdef MAPBASE + SendPropBool( SENDINFO( m_bDontFollowTarget ) ), +#endif + SendPropBool( SENDINFO( m_bState ) ), + SendPropBool( SENDINFO( m_bAlwaysUpdate ) ), + SendPropFloat( SENDINFO( m_flLightFOV ) ), +#ifdef MAPBASE + SendPropFloat( SENDINFO( m_flLightHorFOV ) ), +#endif + SendPropBool( SENDINFO( m_bEnableShadows ) ), + SendPropBool( SENDINFO( m_bLightOnlyTarget ) ), + SendPropBool( SENDINFO( m_bLightWorld ) ), + SendPropBool( SENDINFO( m_bCameraSpace ) ), + SendPropFloat( SENDINFO( m_flBrightnessScale ) ), + SendPropInt( SENDINFO ( m_LightColor ), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt ), + SendPropFloat( SENDINFO( m_flColorTransitionTime ) ), + SendPropFloat( SENDINFO( m_flAmbient ) ), + SendPropString( SENDINFO( m_SpotlightTextureName ) ), + SendPropInt( SENDINFO( m_nSpotlightTextureFrame ) ), + SendPropFloat( SENDINFO( m_flNearZ ), 16, SPROP_ROUNDDOWN, 0.0f, 500.0f ), + SendPropFloat( SENDINFO( m_flFarZ ), 18, SPROP_ROUNDDOWN, 0.0f, 1500.0f ), + SendPropInt( SENDINFO( m_nShadowQuality ), 1, SPROP_UNSIGNED ), // Just one bit for now +#ifdef MAPBASE + SendPropFloat( SENDINFO( m_flConstantAtten ) ), + SendPropFloat( SENDINFO( m_flLinearAtten ) ), + SendPropFloat( SENDINFO( m_flQuadraticAtten ) ), + SendPropFloat( SENDINFO( m_flShadowAtten ) ), + SendPropBool( SENDINFO( m_bAlwaysDraw ) ), + + // Not needed on the client right now, change when it actually is needed + //SendPropBool( SENDINFO( m_bProjectedTextureVersion ) ), +#endif +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CEnvProjectedTexture::CEnvProjectedTexture( void ) +{ + m_bState = true; + m_bAlwaysUpdate = false; + m_flLightFOV = 45.0f; + m_bEnableShadows = false; + m_bLightOnlyTarget = false; + m_bLightWorld = true; + m_bCameraSpace = false; + +#ifndef MAPBASE + Q_strcpy( m_SpotlightTextureName.GetForModify(), "effects/flashlight_border" ); +#endif + Q_strcpy( m_SpotlightTextureName.GetForModify(), "effects/flashlight001" ); + + m_nSpotlightTextureFrame = 0; + m_flBrightnessScale = 1.0f; + m_LightColor.Init( 255, 255, 255, 255 ); +#ifdef MAPBASE + m_flColorTransitionTime = 0.0f; +#else + m_flColorTransitionTime = 0.5f; +#endif + m_flAmbient = 0.0f; + m_flNearZ = 4.0f; + m_flFarZ = 750.0f; + m_nShadowQuality = 0; +#ifdef MAPBASE + m_flQuadraticAtten = 0.0f; + m_flLinearAtten = 100.0f; + m_flConstantAtten = 0.0f; + m_flShadowAtten = 0.0f; +#endif +} + +void UTIL_ColorStringToLinearFloatColor( Vector &color, const char *pString ) +{ + float tmp[4]; + UTIL_StringToFloatArray( tmp, 4, pString ); + if( tmp[3] <= 0.0f ) + { + tmp[3] = 255.0f; + } + tmp[3] *= ( 1.0f / 255.0f ); + color.x = tmp[0] * ( 1.0f / 255.0f ) * tmp[3]; + color.y = tmp[1] * ( 1.0f / 255.0f ) * tmp[3]; + color.z = tmp[2] * ( 1.0f / 255.0f ) * tmp[3]; +} + +bool CEnvProjectedTexture::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "lightcolor" ) ) + { +#ifdef MAPBASE + // + // Most existing projected textures don't have intensity implemented. + // This can give them 0% intensity, which makes them invisible. + // If intensity is not detected, assume 255. + // + // Also, get rid of the floats, for god's sake. Have you ever seen a color32 with decimals? + // + int tmp[4]; + tmp[3] = 255; + UTIL_StringToIntArray_PreserveArray( tmp, 4, szValue ); + + m_LightColor.SetR( tmp[0] ); + m_LightColor.SetG( tmp[1] ); + m_LightColor.SetB( tmp[2] ); + m_LightColor.SetA( tmp[3] ); +#else + float tmp[4]; + UTIL_StringToFloatArray( tmp, 4, szValue ); + + m_LightColor.SetR( tmp[0] ); + m_LightColor.SetG( tmp[1] ); + m_LightColor.SetB( tmp[2] ); + m_LightColor.SetA( tmp[3] ); +#endif + } + else if ( FStrEq( szKeyName, "texturename" ) ) + { +#if defined( _X360 ) + if ( Q_strcmp( szValue, "effects/flashlight001" ) == 0 ) + { + // Use this as the default for Xbox + Q_strcpy( m_SpotlightTextureName.GetForModify(), "effects/flashlight_border" ); + } + else + { + Q_strcpy( m_SpotlightTextureName.GetForModify(), szValue ); + } +#else + Q_strcpy( m_SpotlightTextureName.GetForModify(), szValue ); +#endif + } +#ifdef MAPBASE + else if ( FStrEq( szKeyName, "constant_attn" ) ) + { + m_flConstantAtten = CorrectConstantAtten( atof( szValue ) ); + } + else if ( FStrEq( szKeyName, "linear_attn" ) ) + { + m_flLinearAtten = CorrectLinearAtten( atof( szValue ) ); + } + else if ( FStrEq( szKeyName, "quadratic_attn" ) ) + { + m_flQuadraticAtten = CorrectQuadraticAtten( atof( szValue ) ); + } +#endif + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} + +bool CEnvProjectedTexture::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if ( FStrEq( szKeyName, "lightcolor" ) ) + { + Q_snprintf( szValue, iMaxLen, "%d %d %d %d", m_LightColor.GetR(), m_LightColor.GetG(), m_LightColor.GetB(), m_LightColor.GetA() ); + return true; + } + else if ( FStrEq( szKeyName, "texturename" ) ) + { + Q_snprintf( szValue, iMaxLen, "%s", m_SpotlightTextureName.Get() ); + return true; + } +#ifdef MAPBASE + else if ( FStrEq( szKeyName, "constant_attn" ) ) + { + // Undo correction + Q_snprintf( szValue, iMaxLen, "%f", m_flConstantAtten *= 2.0f ); + return true; + } + else if ( FStrEq( szKeyName, "linear_attn" ) ) + { + // Undo correction + Q_snprintf( szValue, iMaxLen, "%f", m_flLinearAtten *= 0.01f ); + return true; + } + else if ( FStrEq( szKeyName, "quadratic_attn" ) ) + { + // Undo correction + Q_snprintf( szValue, iMaxLen, "%f", m_flQuadraticAtten *= 0.0001f ); + return true; + } +#endif + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); +} + +void CEnvProjectedTexture::InputTurnOn( inputdata_t &inputdata ) +{ + m_bState = true; +} + +void CEnvProjectedTexture::InputTurnOff( inputdata_t &inputdata ) +{ + m_bState = false; +} + +void CEnvProjectedTexture::InputAlwaysUpdateOn( inputdata_t &inputdata ) +{ + m_bAlwaysUpdate = true; +} + +void CEnvProjectedTexture::InputAlwaysUpdateOff( inputdata_t &inputdata ) +{ + m_bAlwaysUpdate = false; +} + +void CEnvProjectedTexture::InputSetFOV( inputdata_t &inputdata ) +{ + m_flLightFOV = inputdata.value.Float(); +#ifdef MAPBASE + m_flLightHorFOV = inputdata.value.Float(); +#endif +} + +#ifdef MAPBASE +void CEnvProjectedTexture::InputSetVerFOV( inputdata_t &inputdata ) +{ + m_flLightFOV = inputdata.value.Float(); +} + +void CEnvProjectedTexture::InputSetHorFOV( inputdata_t &inputdata ) +{ + m_flLightHorFOV = inputdata.value.Float(); +} +#endif + +void CEnvProjectedTexture::InputSetTarget( inputdata_t &inputdata ) +{ +#ifdef MAPBASE + // env_projectedtexture's "Target" uses FIELD_EHANDLE while Mapbase's base entity "SetTarget" uses FIELD_STRING + if (inputdata.value.FieldType() != FIELD_EHANDLE) + inputdata.value.Convert(FIELD_EHANDLE, this, inputdata.pActivator, inputdata.pCaller); +#endif + m_hTargetEntity = inputdata.value.Entity(); +} + +void CEnvProjectedTexture::InputSetCameraSpace( inputdata_t &inputdata ) +{ + m_bCameraSpace = inputdata.value.Bool(); +} + +void CEnvProjectedTexture::InputSetLightOnlyTarget( inputdata_t &inputdata ) +{ + m_bLightOnlyTarget = inputdata.value.Bool(); +} + +void CEnvProjectedTexture::InputSetLightWorld( inputdata_t &inputdata ) +{ + m_bLightWorld = inputdata.value.Bool(); +} + +void CEnvProjectedTexture::InputSetEnableShadows( inputdata_t &inputdata ) +{ + m_bEnableShadows = inputdata.value.Bool(); +} + +void CEnvProjectedTexture::InputSetLightColor( inputdata_t &inputdata ) +{ + m_LightColor = inputdata.value.Color32(); +} + +void CEnvProjectedTexture::InputSetAmbient( inputdata_t &inputdata ) +{ + m_flAmbient = inputdata.value.Float(); +} + +void CEnvProjectedTexture::InputSetSpotlightTexture( inputdata_t &inputdata ) +{ + Q_strcpy( m_SpotlightTextureName.GetForModify(), inputdata.value.String() ); +} + +#ifdef MAPBASE +void CEnvProjectedTexture::InputSetSpotlightFrame( inputdata_t &inputdata ) +{ + m_nSpotlightTextureFrame = inputdata.value.Int(); +} + +void CEnvProjectedTexture::InputSetBrightness( inputdata_t &inputdata ) +{ + m_flBrightnessScale = inputdata.value.Float(); +} + +void CEnvProjectedTexture::InputSetColorTransitionTime( inputdata_t &inputdata ) +{ + m_flColorTransitionTime = inputdata.value.Float(); +} + +void CEnvProjectedTexture::InputSetNearZ( inputdata_t &inputdata ) +{ + m_flNearZ = inputdata.value.Float(); +} + +void CEnvProjectedTexture::InputSetFarZ( inputdata_t &inputdata ) +{ + m_flFarZ = inputdata.value.Float(); +} +#endif + +#ifdef MAPBASE +void CEnvProjectedTexture::Spawn( void ) +{ + // Set m_flLightHorFOV to m_flLightFOV if it's still 0, indicating either legacy support or desire to do this + if (m_flLightHorFOV == 0.0f) + { + m_flLightHorFOV = m_flLightFOV; + } + + m_bState = ( ( GetSpawnFlags() & ENV_PROJECTEDTEXTURE_STARTON ) != 0 ); + m_bAlwaysUpdate = ( ( GetSpawnFlags() & ENV_PROJECTEDTEXTURE_ALWAYSUPDATE ) != 0 ); + + BaseClass::Spawn(); +} +#endif + +void CEnvProjectedTexture::Activate( void ) +{ +#ifndef MAPBASE // Putting this in Activate() breaks projected textures which start off or don't start always updating in savegames. Moved to Spawn() instead + m_bState = ( ( GetSpawnFlags() & ENV_PROJECTEDTEXTURE_STARTON ) != 0 ); + m_bAlwaysUpdate = ( ( GetSpawnFlags() & ENV_PROJECTEDTEXTURE_ALWAYSUPDATE ) != 0 ); +#endif + + SetThink( &CEnvProjectedTexture::InitialThink ); + SetNextThink( gpGlobals->curtime + 0.1f ); + +#ifdef MAPBASE + if (m_bProjectedTextureVersion == 0 && GetMoveParent()) + { + // Pre-Mapbase projected textures should use the VDC parenting fix. + m_bAlwaysUpdate = true; + } +#endif + + BaseClass::Activate(); +} + +#ifdef MAPBASE +void CEnvProjectedTexture::SetParent( CBaseEntity* pNewParent, int iAttachment ) +{ + BaseClass::SetParent( pNewParent, iAttachment ); + + if (m_bProjectedTextureVersion == 0) + { + // Pre-Mapbase projected textures should use the VDC parenting fix. + // Since the ASW changes structure projected textures differently, + // we have to set it here on the server when our parent changes. + // Don't run this code if we already have the spawnflag though. + if ( ( GetSpawnFlags() & ENV_PROJECTEDTEXTURE_ALWAYSUPDATE ) == 0 ) + { + m_bAlwaysUpdate = GetMoveParent() != NULL; + } + } +} +#endif + +void CEnvProjectedTexture::InitialThink( void ) +{ + m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target ); +} + +int CEnvProjectedTexture::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +#else + #define ENV_PROJECTEDTEXTURE_STARTON (1<<0) +#ifdef MAPBASE +#define ENV_PROJECTEDTEXTURE_ALWAYSUPDATE (1<<1) +#endif //----------------------------------------------------------------------------- // Purpose: @@ -31,13 +482,21 @@ public: void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputAlwaysUpdateOn( inputdata_t &inputdata ); + void InputAlwaysUpdateOff( inputdata_t &inputdata ); +#endif void InputSetFOV( inputdata_t &inputdata ); void InputSetTarget( inputdata_t &inputdata ); void InputSetCameraSpace( inputdata_t &inputdata ); void InputSetLightOnlyTarget( inputdata_t &inputdata ); void InputSetLightWorld( inputdata_t &inputdata ); void InputSetEnableShadows( inputdata_t &inputdata ); +#ifndef MAPBASE // void InputSetLightColor( inputdata_t &inputdata ); +#else + void InputSetLightColor( inputdata_t &inputdata ); +#endif void InputSetSpotlightTexture( inputdata_t &inputdata ); void InputSetAmbient( inputdata_t &inputdata ); @@ -48,6 +507,9 @@ public: private: CNetworkVar( bool, m_bState ); +#ifdef MAPBASE + CNetworkVar( bool, m_bAlwaysUpdate ); +#endif CNetworkVar( float, m_flLightFOV ); CNetworkVar( bool, m_bEnableShadows ); CNetworkVar( bool, m_bLightOnlyTarget ); @@ -73,7 +535,11 @@ BEGIN_DATADESC( CEnvProjectedTexture ) DEFINE_KEYFIELD( m_bLightWorld, FIELD_BOOLEAN, "lightworld" ), DEFINE_KEYFIELD( m_bCameraSpace, FIELD_BOOLEAN, "cameraspace" ), DEFINE_KEYFIELD( m_flAmbient, FIELD_FLOAT, "ambient" ), +#ifndef MAPBASE DEFINE_AUTO_ARRAY_KEYFIELD( m_SpotlightTextureName, FIELD_CHARACTER, "texturename" ), +#else + DEFINE_AUTO_ARRAY( m_SpotlightTextureName, FIELD_CHARACTER ), +#endif DEFINE_KEYFIELD( m_nSpotlightTextureFrame, FIELD_INTEGER, "textureframe" ), DEFINE_KEYFIELD( m_flNearZ, FIELD_FLOAT, "nearz" ), DEFINE_KEYFIELD( m_flFarZ, FIELD_FLOAT, "farz" ), @@ -82,14 +548,22 @@ BEGIN_DATADESC( CEnvProjectedTexture ) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysUpdateOn", InputAlwaysUpdateOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysUpdateOff", InputAlwaysUpdateOff ), +#endif DEFINE_INPUTFUNC( FIELD_FLOAT, "FOV", InputSetFOV ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "Target", InputSetTarget ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "CameraSpace", InputSetCameraSpace ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "LightOnlyTarget", InputSetLightOnlyTarget ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "LightWorld", InputSetLightWorld ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "EnableShadows", InputSetEnableShadows ), +#ifndef MAPBASE // this is broken . . need to be able to set color and intensity like light_dynamic // DEFINE_INPUTFUNC( FIELD_COLOR32, "LightColor", InputSetLightColor ), +#else + DEFINE_INPUTFUNC( FIELD_STRING, "LightColor", InputSetLightColor ), +#endif DEFINE_INPUTFUNC( FIELD_FLOAT, "Ambient", InputSetAmbient ), DEFINE_INPUTFUNC( FIELD_STRING, "SpotlightTexture", InputSetSpotlightTexture ), DEFINE_THINKFUNC( InitialThink ), @@ -98,6 +572,9 @@ END_DATADESC() IMPLEMENT_SERVERCLASS_ST( CEnvProjectedTexture, DT_EnvProjectedTexture ) SendPropEHandle( SENDINFO( m_hTargetEntity ) ), SendPropBool( SENDINFO( m_bState ) ), +#ifdef MAPBASE + SendPropBool( SENDINFO( m_bAlwaysUpdate ) ), +#endif SendPropFloat( SENDINFO( m_flLightFOV ) ), SendPropBool( SENDINFO( m_bEnableShadows ) ), SendPropBool( SENDINFO( m_bLightOnlyTarget ) ), @@ -161,6 +638,12 @@ bool CEnvProjectedTexture::KeyValue( const char *szKeyName, const char *szValue UTIL_ColorStringToLinearFloatColor( tmp, szValue ); m_LinearFloatLightColor = tmp; } +#ifdef MAPBASE + else if ( FStrEq(szKeyName, "texturename") ) + { + Q_strcpy(m_SpotlightTextureName.GetForModify(), szValue); + } +#endif else { return BaseClass::KeyValue( szKeyName, szValue ); @@ -179,6 +662,18 @@ void CEnvProjectedTexture::InputTurnOff( inputdata_t &inputdata ) m_bState = false; } +#ifdef MAPBASE +void CEnvProjectedTexture::InputAlwaysUpdateOn( inputdata_t &inputdata ) +{ + m_bAlwaysUpdate = true; +} + +void CEnvProjectedTexture::InputAlwaysUpdateOff( inputdata_t &inputdata ) +{ + m_bAlwaysUpdate = false; +} +#endif + void CEnvProjectedTexture::InputSetFOV( inputdata_t &inputdata ) { m_flLightFOV = inputdata.value.Float(); @@ -209,10 +704,19 @@ void CEnvProjectedTexture::InputSetEnableShadows( inputdata_t &inputdata ) m_bEnableShadows = inputdata.value.Bool(); } +#ifndef MAPBASE //void CEnvProjectedTexture::InputSetLightColor( inputdata_t &inputdata ) //{ // m_cLightColor = inputdata.value.Color32(); //} +#else +void CEnvProjectedTexture::InputSetLightColor( inputdata_t &inputdata ) +{ + Vector tmp; + UTIL_ColorStringToLinearFloatColor( tmp, inputdata.value.String() ); + m_LinearFloatLightColor = tmp; +} +#endif void CEnvProjectedTexture::InputSetAmbient( inputdata_t &inputdata ) { @@ -230,6 +734,9 @@ void CEnvProjectedTexture::Activate( void ) { m_bState = true; } +#ifdef MAPBASE + m_bAlwaysUpdate = ( ( GetSpawnFlags() & ENV_PROJECTEDTEXTURE_ALWAYSUPDATE ) != 0 ); +#endif SetThink( &CEnvProjectedTexture::InitialThink ); SetNextThink( gpGlobals->curtime + 0.1f ); @@ -239,7 +746,19 @@ void CEnvProjectedTexture::Activate( void ) void CEnvProjectedTexture::InitialThink( void ) { +#ifndef MAPBASE m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target ); +#else + if (m_hTargetEntity == NULL && m_target != NULL_STRING) + m_hTargetEntity = gEntList.FindEntityByName(NULL, m_target); + if (m_hTargetEntity == NULL) + return; + Vector vecToTarget = (m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin()); + QAngle vecAngles; + VectorAngles(vecToTarget, vecAngles); + SetAbsAngles(vecAngles); + SetNextThink(gpGlobals->curtime + 0.1); +#endif } int CEnvProjectedTexture::UpdateTransmitState() @@ -247,6 +766,8 @@ int CEnvProjectedTexture::UpdateTransmitState() return SetTransmitState( FL_EDICT_ALWAYS ); } +#endif + // Console command for creating env_projectedtexture entities void CC_CreateFlashlight( const CCommand &args ) diff --git a/mp/src/game/server/env_projectedtexture.h b/mp/src/game/server/env_projectedtexture.h new file mode 100644 index 00000000..06ff04ee --- /dev/null +++ b/mp/src/game/server/env_projectedtexture.h @@ -0,0 +1,119 @@ + +#ifndef ENV_PROJECTEDTEXTURE_H +#define ENV_PROJECTEDTEXTURE_H +#ifdef _WIN32 +#pragma once +#endif + +#define ENV_PROJECTEDTEXTURE_STARTON (1<<0) +#define ENV_PROJECTEDTEXTURE_ALWAYSUPDATE (1<<1) + +#ifdef ASW_PROJECTED_TEXTURES +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CEnvProjectedTexture : public CPointEntity +{ + DECLARE_CLASS( CEnvProjectedTexture, CPointEntity ); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CEnvProjectedTexture(); + bool KeyValue( const char *szKeyName, const char *szValue ); + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + + // Always transmit to clients + virtual int UpdateTransmitState(); +#ifdef MAPBASE + virtual void Spawn( void ); +#endif + virtual void Activate( void ); +#ifdef MAPBASE + void SetParent( CBaseEntity* pNewParent, int iAttachment = -1 ); +#endif + + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + void InputAlwaysUpdateOn( inputdata_t &inputdata ); + void InputAlwaysUpdateOff( inputdata_t &inputdata ); + void InputSetFOV( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetVerFOV( inputdata_t &inputdata ); + void InputSetHorFOV( inputdata_t &inputdata ); +#endif + void InputSetTarget( inputdata_t &inputdata ); + void InputSetCameraSpace( inputdata_t &inputdata ); + void InputSetLightOnlyTarget( inputdata_t &inputdata ); + void InputSetLightWorld( inputdata_t &inputdata ); + void InputSetEnableShadows( inputdata_t &inputdata ); + void InputSetLightColor( inputdata_t &inputdata ); + void InputSetSpotlightTexture( inputdata_t &inputdata ); + void InputSetAmbient( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetSpotlightFrame( inputdata_t &inputdata ); + void InputSetBrightness( inputdata_t &inputdata ); + void InputSetColorTransitionTime( inputdata_t &inputdata ); + void InputSetConstant( inputdata_t &inputdata ) { m_flConstantAtten = CorrectConstantAtten(inputdata.value.Float()); } + void InputSetLinear( inputdata_t &inputdata ) { m_flLinearAtten = CorrectLinearAtten(inputdata.value.Float()); } + void InputSetQuadratic( inputdata_t &inputdata ) { m_flQuadraticAtten = CorrectQuadraticAtten(inputdata.value.Float()); } + void InputSetShadowAtten( inputdata_t &inputdata ) { m_flShadowAtten = inputdata.value.Float(); } + void InputSetNearZ( inputdata_t &inputdata ); + void InputSetFarZ( inputdata_t &inputdata ); + void InputAlwaysDrawOn( inputdata_t &inputdata ) { m_bAlwaysDraw = true; } + void InputAlwaysDrawOff( inputdata_t &inputdata ) { m_bAlwaysDraw = false; } + void InputStopFollowingTarget( inputdata_t &inputdata ) { m_bDontFollowTarget = true; } + void InputStartFollowingTarget( inputdata_t &inputdata ) { m_bDontFollowTarget = false; } + + // Corrects keyvalue/input attenuation for internal FlashlightEffect_t attenuation. + float CorrectConstantAtten( float fl ) { return fl * 0.5f; } + float CorrectLinearAtten( float fl ) { return fl * 100.0f; } + float CorrectQuadraticAtten( float fl ) { return fl * 10000.0f; } +#endif + + void InitialThink( void ); + + CNetworkHandle( CBaseEntity, m_hTargetEntity ); +#ifdef MAPBASE + CNetworkVar( bool, m_bDontFollowTarget ); +#endif + +private: + + CNetworkVar( bool, m_bState ); + CNetworkVar( bool, m_bAlwaysUpdate ); + CNetworkVar( float, m_flLightFOV ); +#ifdef MAPBASE + CNetworkVar( float, m_flLightHorFOV ); +#endif + CNetworkVar( bool, m_bEnableShadows ); + CNetworkVar( bool, m_bLightOnlyTarget ); + CNetworkVar( bool, m_bLightWorld ); + CNetworkVar( bool, m_bCameraSpace ); + CNetworkVar( float, m_flBrightnessScale ); + CNetworkColor32( m_LightColor ); + CNetworkVar( float, m_flColorTransitionTime ); + CNetworkVar( float, m_flAmbient ); + CNetworkString( m_SpotlightTextureName, MAX_PATH ); + CNetworkVar( int, m_nSpotlightTextureFrame ); + CNetworkVar( float, m_flNearZ ); + CNetworkVar( float, m_flFarZ ); + CNetworkVar( int, m_nShadowQuality ); +#ifdef MAPBASE + CNetworkVar( float, m_flConstantAtten ); + CNetworkVar( float, m_flLinearAtten ); + CNetworkVar( float, m_flQuadraticAtten ); + CNetworkVar( float, m_flShadowAtten ); + + CNetworkVar( bool, m_bAlwaysDraw ); + + // 1 = New projected texture + // 0 = Non-Mapbase projected texture, e.g. one that uses the VDC parenting fix instead of the spawnflag + // Not needed on the client right now, change to CNetworkVar when it actually is needed + bool m_bProjectedTextureVersion; +#endif +}; +#endif + + +#endif // ENV_PROJECTEDTEXTURE_H \ No newline at end of file diff --git a/mp/src/game/server/env_tonemap_controller.cpp b/mp/src/game/server/env_tonemap_controller.cpp index 613d4548..39998e4b 100644 --- a/mp/src/game/server/env_tonemap_controller.cpp +++ b/mp/src/game/server/env_tonemap_controller.cpp @@ -8,6 +8,9 @@ #include "baseentity.h" #include "entityoutput.h" #include "convar.h" + +#include "player.h" //Tony; need player.h so we can trigger inputs on the player, from our inputs! + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -83,7 +86,11 @@ BEGIN_DATADESC( CEnvTonemapController ) DEFINE_INPUTFUNC( FIELD_VOID, "UseDefaultAutoExposure", InputUseDefaultAutoExposure ), DEFINE_INPUTFUNC( FIELD_VOID, "UseDefaultBloomScale", InputUseDefaultBloomScale ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetBloomScale", InputSetBloomScale ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetBloomScaleRange", InputSetBloomScaleRange ), +#else DEFINE_INPUTFUNC( FIELD_FLOAT, "SetBloomScaleRange", InputSetBloomScaleRange ), +#endif END_DATADESC() IMPLEMENT_SERVERCLASS_ST( CEnvTonemapController, DT_EnvTonemapController ) @@ -118,6 +125,22 @@ int CEnvTonemapController::UpdateTransmitState() //----------------------------------------------------------------------------- void CEnvTonemapController::InputSetTonemapScale( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputSetTonemapScale\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputSetTonemapScale( inputdata ); + return; + } + } + } + float flRemapped = inputdata.value.Float(); mat_hdr_tonemapscale.SetValue( flRemapped ); } @@ -127,6 +150,10 @@ void CEnvTonemapController::InputSetTonemapScale( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CEnvTonemapController::InputBlendTonemapScale( inputdata_t &inputdata ) { + //Tony; TODO!!! -- tonemap scale blending does _not_ work properly in multiplayer.. + if ( ( gpGlobals->maxClients > 1 ) ) + return; + char parseString[255]; Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString)); @@ -177,6 +204,22 @@ void CEnvTonemapController::InputSetBloomScaleRange( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CEnvTonemapController::InputSetTonemapRate( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputSetTonemapRate\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputSetTonemapRate( inputdata ); + return; + } + } + } + // TODO: There should be a better way to do this. ConVarRef mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate" ); if ( mat_hdr_manual_tonemap_rate.IsValid() ) @@ -208,6 +251,22 @@ void CEnvTonemapController::UpdateTonemapScaleBlend( void ) //----------------------------------------------------------------------------- void CEnvTonemapController::InputSetAutoExposureMin( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputSetAutoExposureMin\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputSetAutoExposureMin( inputdata ); + return; + } + } + } + m_flCustomAutoExposureMin = inputdata.value.Float(); m_bUseCustomAutoExposureMin = true; } @@ -217,6 +276,22 @@ void CEnvTonemapController::InputSetAutoExposureMin( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CEnvTonemapController::InputSetAutoExposureMax( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputSetAutoExposureMax\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputSetAutoExposureMax( inputdata ); + return; + } + } + } + m_flCustomAutoExposureMax = inputdata.value.Float(); m_bUseCustomAutoExposureMax = true; } @@ -226,6 +301,22 @@ void CEnvTonemapController::InputSetAutoExposureMax( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CEnvTonemapController::InputUseDefaultAutoExposure( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputUseDefaultAutoExposure\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputUseDefaultAutoExposure( inputdata ); + return; + } + } + } + m_bUseCustomAutoExposureMin = false; m_bUseCustomAutoExposureMax = false; } @@ -235,6 +326,22 @@ void CEnvTonemapController::InputUseDefaultAutoExposure( inputdata_t &inputdata //----------------------------------------------------------------------------- void CEnvTonemapController::InputSetBloomScale( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputSetBloomScale\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputSetBloomScale( inputdata ); + return; + } + } + } + m_flCustomBloomScale = inputdata.value.Float(); m_flCustomBloomScaleMinimum = m_flCustomBloomScale; m_bUseCustomBloomScale = true; @@ -245,5 +352,21 @@ void CEnvTonemapController::InputSetBloomScale( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CEnvTonemapController::InputUseDefaultBloomScale( inputdata_t &inputdata ) { + //Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out. + //if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values. + if ( ( gpGlobals->maxClients > 1 ) ) + { + if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() ) + { +// DevMsg("activator is a player: InputUseDefaultBloomScale\n"); + CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator); + if (pPlayer) + { + pPlayer->InputUseDefaultBloomScale( inputdata ); + return; + } + } + } + m_bUseCustomBloomScale = false; } diff --git a/mp/src/game/server/env_zoom.cpp b/mp/src/game/server/env_zoom.cpp index 2a9f8478..ec679152 100644 --- a/mp/src/game/server/env_zoom.cpp +++ b/mp/src/game/server/env_zoom.cpp @@ -23,6 +23,10 @@ public: void InputZoom( inputdata_t &inputdata ); void InputUnZoom( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputUnZoomWithRate( inputdata_t &inputdata ); + void InputSetZoomRate( inputdata_t &inputdata ); +#endif int GetFOV( void ) { return m_nFOV; } float GetSpeed( void ) { return m_flSpeed; } @@ -43,6 +47,10 @@ BEGIN_DATADESC( CEnvZoom ) DEFINE_INPUTFUNC( FIELD_VOID, "Zoom", InputZoom ), DEFINE_INPUTFUNC( FIELD_VOID, "UnZoom", InputUnZoom ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "UnZoomWithRate", InputUnZoomWithRate ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetZoomRate", InputSetZoomRate ), +#endif END_DATADESC() @@ -114,3 +122,29 @@ void CEnvZoom::InputUnZoom( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvZoom::InputUnZoomWithRate( inputdata_t &inputdata ) +{ + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + + if ( pPlayer ) + { + // Stuff the values + pPlayer->SetFOV( this, 0, m_flSpeed, m_nFOV ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvZoom::InputSetZoomRate( inputdata_t &inputdata ) +{ + m_flSpeed = inputdata.value.Float(); +} +#endif + diff --git a/mp/src/game/server/envmicrophone.cpp b/mp/src/game/server/envmicrophone.cpp index 9e85b0d9..ee78f87f 100644 --- a/mp/src/game/server/envmicrophone.cpp +++ b/mp/src/game/server/envmicrophone.cpp @@ -44,6 +44,12 @@ BEGIN_DATADESC( CEnvMicrophone ) DEFINE_KEYFIELD(m_iszListenFilter, FIELD_STRING, "ListenFilter"), DEFINE_FIELD(m_hListenFilter, FIELD_EHANDLE), DEFINE_FIELD(m_hSpeaker, FIELD_EHANDLE), +#ifdef MAPBASE + DEFINE_KEYFIELD(m_iszLandmarkName, FIELD_STRING, "landmark"), + DEFINE_FIELD(m_hLandmark, FIELD_EHANDLE), + DEFINE_KEYFIELD(m_flPitchScale, FIELD_FLOAT, "PitchScale"), + DEFINE_KEYFIELD(m_nChannel, FIELD_INTEGER, "channel"), +#endif // DEFINE_FIELD(m_bAvoidFeedback, FIELD_BOOLEAN), // DONT SAVE DEFINE_KEYFIELD(m_iSpeakerDSPPreset, FIELD_INTEGER, "speaker_dsp_preset" ), DEFINE_KEYFIELD(m_flMaxRange, FIELD_FLOAT, "MaxRange"), @@ -52,6 +58,11 @@ BEGIN_DATADESC( CEnvMicrophone ) DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), DEFINE_INPUTFUNC(FIELD_STRING, "SetSpeakerName", InputSetSpeakerName), +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_INTEGER, "SetDSPPreset", InputSetDSPPreset), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPitchScale", InputSetPitchScale ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetChannel", InputSetChannel ), +#endif DEFINE_OUTPUT(m_SoundLevel, "SoundLevel"), DEFINE_OUTPUT(m_OnRoutedSound, "OnRoutedSound" ), @@ -190,6 +201,13 @@ void CEnvMicrophone::ActivateSpeaker( void ) s_Microphones.AddToTail( this ); } } + +#ifdef MAPBASE + if (m_iszLandmarkName != NULL_STRING) + { + m_hLandmark = gEntList.FindEntityByName(NULL, m_iszLandmarkName, this, NULL, NULL); + } +#endif } //----------------------------------------------------------------------------- @@ -234,6 +252,36 @@ void CEnvMicrophone::InputSetSpeakerName( inputdata_t &inputdata ) SetSpeakerName( inputdata.value.StringID() ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvMicrophone::InputSetDSPPreset( inputdata_t &inputdata ) +{ + m_iSpeakerDSPPreset = inputdata.value.Int(); + ActivateSpeaker(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvMicrophone::InputSetPitchScale( inputdata_t &inputdata ) +{ + m_flPitchScale = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvMicrophone::InputSetChannel( inputdata_t &inputdata ) +{ + m_nChannel = inputdata.value.Int(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Checks whether this microphone can hear a given sound, and at what // relative volume level. @@ -473,6 +521,21 @@ MicrophoneResult_t CEnvMicrophone::SoundPlayed( int entindex, const char *soundn } } +#ifdef MAPBASE + // Something similar to trigger_teleport landmarks for sounds transmitting to speaker + Vector vecOrigin = m_hSpeaker->GetAbsOrigin(); + if (m_hLandmark) + { + Vector vecSoundPos; + if (pOrigin) + vecSoundPos = *pOrigin; + else if (CBaseEntity *pEntity = CBaseEntity::Instance(engine->PEntityOfEntIndex(entindex))) + vecSoundPos = pEntity->GetAbsOrigin(); + + vecOrigin += (vecSoundPos - m_hLandmark->GetAbsOrigin()); + } +#endif + m_bAvoidFeedback = true; // Add the speaker flag. Detected at playback and applies the speaker filter. @@ -480,13 +543,25 @@ MicrophoneResult_t CEnvMicrophone::SoundPlayed( int entindex, const char *soundn CPASAttenuationFilter filter( m_hSpeaker ); EmitSound_t ep; +#ifdef MAPBASE + ep.m_nChannel = m_nChannel; +#else ep.m_nChannel = CHAN_STATIC; +#endif ep.m_pSoundName = soundname; ep.m_flVolume = flVolume; ep.m_SoundLevel = soundlevel; ep.m_nFlags = iFlags; +#ifdef MAPBASE + if (m_flPitchScale != 1.0f) + ep.m_nPitch = (int)((float)iPitch * m_flPitchScale); + else + ep.m_nPitch = iPitch; + ep.m_pOrigin = &vecOrigin; +#else ep.m_nPitch = iPitch; ep.m_pOrigin = &m_hSpeaker->GetAbsOrigin(); +#endif ep.m_flSoundTime = soundtime; ep.m_nSpeakerEntity = entindex; diff --git a/mp/src/game/server/envmicrophone.h b/mp/src/game/server/envmicrophone.h index 1cad0761..cf8e729e 100644 --- a/mp/src/game/server/envmicrophone.h +++ b/mp/src/game/server/envmicrophone.h @@ -54,6 +54,11 @@ public: void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); void InputSetSpeakerName( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetDSPPreset( inputdata_t &inputdata ); + void InputSetPitchScale( inputdata_t &inputdata ); + void InputSetChannel( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); @@ -79,6 +84,12 @@ private: int m_iSpeakerDSPPreset; // Speaker DSP preset to use when this microphone is enabled string_t m_iszListenFilter; CHandle m_hListenFilter; +#ifdef MAPBASE + string_t m_iszLandmarkName; + EHANDLE m_hLandmark; + float m_flPitchScale = 1.0f; + int m_nChannel = CHAN_STATIC; +#endif COutputFloat m_SoundLevel; // Fired when the sampled volume level changes. COutputEvent m_OnRoutedSound; // Fired when a sound has been played through our speaker diff --git a/mp/src/game/server/episodic/ai_behavior_passenger_companion.cpp b/mp/src/game/server/episodic/ai_behavior_passenger_companion.cpp index f96c2a55..a9568c69 100644 --- a/mp/src/game/server/episodic/ai_behavior_passenger_companion.cpp +++ b/mp/src/game/server/episodic/ai_behavior_passenger_companion.cpp @@ -530,7 +530,11 @@ void CAI_PassengerBehaviorCompanion::GatherConditions( void ) AIEnemiesIter_t iter; for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) { +#ifdef MAPBASE + if( GetOuter()->IRelationType( pEMemory->hEnemy ) <= D_FR ) +#else if( GetOuter()->IRelationType( pEMemory->hEnemy ) == D_HT ) +#endif { if( pEMemory->timeLastSeen == gpGlobals->curtime ) { diff --git a/mp/src/game/server/episodic/npc_hunter.cpp b/mp/src/game/server/episodic/npc_hunter.cpp index d55053bc..ac904b81 100644 --- a/mp/src/game/server/episodic/npc_hunter.cpp +++ b/mp/src/game/server/episodic/npc_hunter.cpp @@ -63,6 +63,9 @@ #include "weapon_striderbuster.h" #include "monstermaker.h" #include "weapon_rpg.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -201,11 +204,19 @@ int g_interactionHunterFoundEnemy = 0; //----------------------------------------------------------------------------- // Local stuff. //----------------------------------------------------------------------------- +#ifdef MAPBASE +#define s_iszStriderClassname gm_isz_class_Strider +#define s_iszPhysPropClassname gm_isz_class_PropPhysics +static string_t s_iszStriderBusterClassname; +static string_t s_iszMagnadeClassname; +static string_t s_iszHuntersToRunOver; +#else static string_t s_iszStriderClassname; static string_t s_iszStriderBusterClassname; static string_t s_iszMagnadeClassname; static string_t s_iszPhysPropClassname; static string_t s_iszHuntersToRunOver; +#endif //----------------------------------------------------------------------------- @@ -1973,11 +1984,17 @@ void CNPC_Hunter::Activate() { BaseClass::Activate(); +#ifdef MAPBASE + s_iszStriderBusterClassname = AllocPooledString( "weapon_striderbuster" ); + s_iszMagnadeClassname = AllocPooledString( "npc_grenade_magna" ); + s_iszHuntersToRunOver = AllocPooledString( "hunters_to_run_over" ); +#else s_iszStriderBusterClassname = AllocPooledString( "weapon_striderbuster" ); s_iszStriderClassname = AllocPooledString( "npc_strider" ); s_iszMagnadeClassname = AllocPooledString( "npc_grenade_magna" ); s_iszPhysPropClassname = AllocPooledString( "prop_physics" ); s_iszHuntersToRunOver = AllocPooledString( "hunters_to_run_over" ); +#endif // If no one has initialized the hunters to run over counter, just zero it out. if ( !GlobalEntity_IsInTable( s_iszHuntersToRunOver ) ) @@ -3487,7 +3504,13 @@ void CNPC_Hunter::StartTask( const Task_t *pTask ) { SetLastAttackTime( gpGlobals->curtime ); +#ifdef MAPBASE + // The "VS_PLAYER" animation looks better than the regular animation when used against non-humans + if ( GetEnemy() && (GetEnemy()->IsPlayer() || + (GetEnemy()->IsCombatCharacter() && GetEnemy()->MyCombatCharacterPointer()->GetHullType() != HULL_HUMAN)) ) +#else if ( GetEnemy() && GetEnemy()->IsPlayer() ) +#endif { ResetIdealActivity( ( Activity )ACT_HUNTER_MELEE_ATTACK1_VS_PLAYER ); } @@ -4089,7 +4112,11 @@ bool CNPC_Hunter::HandleChargeImpact( Vector vecImpact, CBaseEntity *pEntity ) } // Hit anything we don't like +#ifdef MAPBASE + if ( IRelationType( pEntity ) <= D_FR && ( GetNextAttack() < gpGlobals->curtime ) ) +#else if ( IRelationType( pEntity ) == D_HT && ( GetNextAttack() < gpGlobals->curtime ) ) +#endif { EmitSound( "NPC_Hunter.ChargeHitEnemy" ); @@ -4371,7 +4398,11 @@ void CNPC_Hunter::HandleAnimEvent( animevent_t *pEvent ) //----------------------------------------------------------------------------- void CNPC_Hunter::AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ) { +#ifdef MAPBASE + if ( nDisposition == D_HT && pEntity->ClassMatches(gm_isz_class_Bullseye) ) +#else if ( nDisposition == D_HT && pEntity->ClassMatches("npc_bullseye") ) +#endif UpdateEnemyMemory( pEntity, pEntity->GetAbsOrigin() ); BaseClass::AddEntityRelationship( pEntity, nDisposition, nPriority ); } @@ -5006,7 +5037,11 @@ int CNPC_Hunter::RangeAttack2Conditions( float flDot, float flDist ) { return COND_TOO_FAR_TO_ATTACK; } +#ifdef MAPBASE + else if ( !bIsBuster && ( !GetEnemy() || !GetEnemy()->ClassMatches( gm_isz_class_Bullseye ) ) && flDist < hunter_flechette_min_range.GetFloat() ) +#else else if ( !bIsBuster && ( !GetEnemy() || !GetEnemy()->ClassMatches( "npc_bullseye" ) ) && flDist < hunter_flechette_min_range.GetFloat() ) +#endif { return COND_TOO_CLOSE_TO_ATTACK; } @@ -5611,6 +5646,40 @@ int CNPC_Hunter::OnTakeDamage( const CTakeDamageInfo &info ) } } } +#ifdef MAPBASE + else if (info.GetDamageType() == DMG_CLUB && + info.GetInflictor() && info.GetInflictor()->IsNPC()) + { + // If the *inflictor* is a NPC doing club damage, it's most likely an antlion guard or even another hunter charging us. + // Add DMG_CRUSH so we ragdoll immediately if we die. + myInfo.AddDamageType( DMG_CRUSH ); + } + + + // "So, do you know what the alternative fire method does on the AR2? It kills hunters. How did--" + // "No, only Freeman's does it!" + // "What do you mean 'Only Freeman's does it'?" + // "Only energy balls fired by the player can dissolve hunters. Energy balls fired by NPCs only do a metered amount of damage." + // "...Huh. Well, in that case, we'll just use rocket launchers." + // + // That instructor was straight-up lying to those rebels, but now he's telling the truth. + // Hunters die to NPC balls instantly and act like it was a player ball. + // Implementation is sketchy, but it was the best I could do. + if (myInfo.GetDamageType() & DMG_DISSOLVE && + info.GetAttacker() && info.GetAttacker()->IsNPC() && + info.GetInflictor() && info.GetInflictor()->ClassMatches("prop_combine_ball")) + { + // We divide by the ally damage scale to counter its usage in OnTakeDamage_Alive. + myInfo.SetDamage( (float)GetMaxHealth() / sk_hunter_citizen_damage_scale.GetFloat() ); + + myInfo.AddDamageType( DMG_CRUSH ); + //myInfo.SetDamagePosition( info.GetInflictor()->GetAbsOrigin() ); + //myInfo.SetDamageForce( info.GetInflictor()->GetAbsVelocity() ); + + // Make the NPC's ball explode + info.GetInflictor()->AcceptInput( "Explode", this, this, variant_t(), 0 ); + } +#endif return BaseClass::OnTakeDamage( myInfo ); } diff --git a/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp b/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp index 368f1b9f..69bb4c3f 100644 --- a/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp +++ b/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp @@ -328,6 +328,9 @@ BEGIN_DATADESC( CPropJeepEpisodic ) DEFINE_ARRAY( m_hHazardLights, FIELD_EHANDLE, NUM_HAZARD_LIGHTS ), DEFINE_FIELD( m_flCargoStartTime, FIELD_TIME ), DEFINE_FIELD( m_bBlink, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bNoHazardLights, FIELD_BOOLEAN, "NoHazardLights" ), +#endif DEFINE_FIELD( m_bRadarEnabled, FIELD_BOOLEAN ), DEFINE_FIELD( m_bRadarDetectsEnemies, FIELD_BOOLEAN ), DEFINE_FIELD( m_hRadarScreen, FIELD_EHANDLE ), @@ -357,13 +360,20 @@ BEGIN_DATADESC( CPropJeepEpisodic ) DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadarDetectEnemies", InputEnableRadarDetectEnemies ), DEFINE_INPUTFUNC( FIELD_VOID, "AddBusterToCargo", InputAddBusterToCargo ), DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ), +#ifndef MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "DisablePhysGun", InputDisablePhysGun ), DEFINE_INPUTFUNC( FIELD_VOID, "EnablePhysGun", InputEnablePhysGun ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "CreateLinkController", InputCreateLinkController ), DEFINE_INPUTFUNC( FIELD_VOID, "DestroyLinkController", InputDestroyLinkController ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetCargoHopperVisibility", InputSetCargoVisibility ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnableHazardLights", InputEnableHazardLights ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableHazardLights", InputDisableHazardLights ), +#endif + END_DATADESC(); IMPLEMENT_SERVERCLASS_ST(CPropJeepEpisodic, DT_CPropJeepEpisodic) @@ -1354,6 +1364,11 @@ void CPropJeepEpisodic::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iBu //----------------------------------------------------------------------------- void CPropJeepEpisodic::CreateHazardLights( void ) { +#ifdef MAPBASE + if (m_bNoHazardLights) + return; +#endif + static const char *s_szAttach[NUM_HAZARD_LIGHTS] = { "rearlight_r", @@ -1412,6 +1427,26 @@ void CPropJeepEpisodic::DestroyHazardLights( void ) SetContextThink( NULL, gpGlobals->curtime, "HazardBlink" ); } +#ifdef MAPBASE +void CPropJeepEpisodic::InputEnableHazardLights( inputdata_t &data ) +{ + if (m_bNoHazardLights) + { + m_bNoHazardLights = false; + CreateHazardLights(); + } +} + +void CPropJeepEpisodic::InputDisableHazardLights( inputdata_t &data ) +{ + if (!m_bNoHazardLights) + { + m_bNoHazardLights = true; + DestroyHazardLights(); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : nRole - @@ -1664,6 +1699,7 @@ void CPropJeepEpisodic::InputOutsideTransition( inputdata_t &inputdata ) Warning("No valid vehicle teleport points!\n"); } +#ifndef MAPBASE //----------------------------------------------------------------------------- // Purpose: Stop players punting the car around. //----------------------------------------------------------------------------- @@ -1678,6 +1714,7 @@ void CPropJeepEpisodic::InputEnablePhysGun( inputdata_t &data ) { RemoveEFlags( EFL_NO_PHYSCANNON_INTERACTION ); } +#endif //----------------------------------------------------------------------------- // Create and parent two radial node link controllers. diff --git a/mp/src/game/server/episodic/vehicle_jeep_episodic.h b/mp/src/game/server/episodic/vehicle_jeep_episodic.h index 70cb5894..869cf33a 100644 --- a/mp/src/game/server/episodic/vehicle_jeep_episodic.h +++ b/mp/src/game/server/episodic/vehicle_jeep_episodic.h @@ -104,9 +104,15 @@ private: void InputEnableRadarDetectEnemies( inputdata_t &data ); void InputAddBusterToCargo( inputdata_t &data ); void InputSetCargoVisibility( inputdata_t &data ); +#ifdef MAPBASE + void InputEnableHazardLights( inputdata_t &data ); + void InputDisableHazardLights( inputdata_t &data ); +#endif void InputOutsideTransition( inputdata_t &data ); +#ifndef MAPBASE void InputDisablePhysGun( inputdata_t &data ); void InputEnablePhysGun( inputdata_t &data ); +#endif void InputCreateLinkController( inputdata_t &data ); void InputDestroyLinkController( inputdata_t &data ); void CreateAvoidanceZone( void ); @@ -116,6 +122,10 @@ private: bool m_bAddingCargo; bool m_bBlink; +#ifdef MAPBASE + bool m_bNoHazardLights; +#endif + float m_flCargoStartTime; // Time when the cargo was first added to the vehicle (used for animating into hold) float m_flNextAvoidBroadcastTime; // Next time we'll warn entity to move out of us diff --git a/mp/src/game/server/eventqueue.h b/mp/src/game/server/eventqueue.h index 1c7f030e..61b0d252 100644 --- a/mp/src/game/server/eventqueue.h +++ b/mp/src/game/server/eventqueue.h @@ -40,9 +40,14 @@ class CEventQueue { public: // pushes an event into the queue, targeting a string name (m_iName), or directly by a pointer - void AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); +#ifdef MAPBASE_VSCRIPT + intptr_t AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); + intptr_t AddEvent( CBaseEntity *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); +#else + void AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); + void AddEvent( CBaseEntity *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); +#endif void AddEvent( CBaseEntity *target, const char *action, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); - void AddEvent( CBaseEntity *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); void CancelEvents( CBaseEntity *pCaller ); void CancelEventOn( CBaseEntity *pTarget, const char *sInputName ); @@ -66,6 +71,12 @@ public: void Dump( void ); +#ifdef MAPBASE_VSCRIPT + void CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ); + bool RemoveEvent( intptr_t event ); + float GetTimeLeft( intptr_t event ); +#endif // MAPBASE_VSCRIPT + private: void AddEvent( EventQueuePrioritizedEvent_t *event ); diff --git a/mp/src/game/server/explode.cpp b/mp/src/game/server/explode.cpp index 42d8df35..d0321356 100644 --- a/mp/src/game/server/explode.cpp +++ b/mp/src/game/server/explode.cpp @@ -17,6 +17,10 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +ConVar explosion_sparks("explosion_sparks", "0", FCVAR_NONE); +#endif + //----------------------------------------------------------------------------- // Purpose: Spark shower, created by the explosion entity. //----------------------------------------------------------------------------- @@ -111,6 +115,9 @@ public: // Input handlers void InputExplode( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetIgnoredEntity( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); @@ -150,6 +157,9 @@ BEGIN_DATADESC( CEnvExplosion ) // Inputs DEFINE_INPUTFUNC(FIELD_VOID, "Explode", InputExplode), +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_EHANDLE, "SetIgnoredEntity", InputSetIgnoredEntity), +#endif END_DATADESC() @@ -352,7 +362,11 @@ void CEnvExplosion::InputExplode( inputdata_t &inputdata ) SetNextThink( gpGlobals->curtime + 0.3 ); // Only do these effects if we're not submerged +#ifdef MAPBASE + if ( explosion_sparks.GetBool() && !(UTIL_PointContents( GetAbsOrigin() ) & CONTENTS_WATER) ) +#else if ( UTIL_PointContents( GetAbsOrigin() ) & CONTENTS_WATER ) +#endif { // draw sparks if ( !( m_spawnflags & SF_ENVEXPLOSION_NOSPARKS ) ) @@ -369,6 +383,16 @@ void CEnvExplosion::InputExplode( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for setting the ignored entity. +//----------------------------------------------------------------------------- +void CEnvExplosion::InputSetIgnoredEntity( inputdata_t &inputdata ) +{ + m_hEntityIgnore = inputdata.value.Entity(); +} +#endif + void CEnvExplosion::Smoke( void ) { diff --git a/mp/src/game/server/filters.cpp b/mp/src/game/server/filters.cpp index 6179254d..8b7dcfc9 100644 --- a/mp/src/game/server/filters.cpp +++ b/mp/src/game/server/filters.cpp @@ -9,6 +9,12 @@ #include "entitylist.h" #include "ai_squad.h" #include "ai_basenpc.h" +#ifdef MAPBASE +#include "mapbase/matchers.h" +#include "AI_Criteria.h" +#include "ai_hint.h" +#include "mapbase/GlobalStrings.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -21,9 +27,16 @@ LINK_ENTITY_TO_CLASS(filter_base, CBaseFilter); BEGIN_DATADESC( CBaseFilter ) DEFINE_KEYFIELD(m_bNegated, FIELD_BOOLEAN, "Negated"), +#ifdef MAPBASE + DEFINE_KEYFIELD(m_bPassCallerWhenTested, FIELD_BOOLEAN, "PassCallerWhenTested"), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_INPUT, "TestActivator", InputTestActivator ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "TestEntity", InputTestEntity ), + DEFINE_INPUTFUNC( FIELD_INPUT, "SetField", InputSetField ), +#endif // Outputs DEFINE_OUTPUT( m_OnPass, "OnPass"), @@ -31,6 +44,18 @@ BEGIN_DATADESC( CBaseFilter ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseFilter, CBaseEntity, "All entities which could be used as filters." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesFilter, "PassesFilter", "Check if the given caller and entity pass the filter. The caller is the one who requests the filter result; For example, the entity being damaged when using this as a damage filter." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesDamageFilter, "PassesDamageFilter", "Check if the given caller and damage info pass the damage filter, with the second parameter being a CTakeDamageInfo instance. The caller is the one who requests the filter result; For example, the entity being damaged when using this as a damage filter." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesFinalDamageFilter, "PassesFinalDamageFilter", "Used by filter_damage_redirect to distinguish between standalone filter calls and actually damaging an entity. Returns true if there's no unique behavior. Parameters are identical to PassesDamageFilter." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptBloodAllowed, "BloodAllowed", "Check if the given caller and damage info allow for the production of blood." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptDamageMod, "DamageMod", "Mods the damage info with the given caller." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- bool CBaseFilter::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) @@ -46,17 +71,37 @@ bool CBaseFilter::PassesFilter( CBaseEntity *pCaller, CBaseEntity *pEntity ) } +#ifdef MAPBASE +bool CBaseFilter::PassesDamageFilter(CBaseEntity *pCaller, const CTakeDamageInfo &info) +{ + bool baseResult = PassesDamageFilterImpl(pCaller, info); + return (m_bNegated) ? !baseResult : baseResult; +} +#endif + bool CBaseFilter::PassesDamageFilter(const CTakeDamageInfo &info) { +#ifdef MAPBASE + Warning("WARNING: Deprecated usage of PassesDamageFilter!\n"); + bool baseResult = PassesDamageFilterImpl(NULL, info); +#else bool baseResult = PassesDamageFilterImpl(info); +#endif return (m_bNegated) ? !baseResult : baseResult; } +#ifdef MAPBASE +bool CBaseFilter::PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) +{ + return PassesFilterImpl( pCaller, info.GetAttacker() ); +} +#else bool CBaseFilter::PassesDamageFilterImpl( const CTakeDamageInfo &info ) { return PassesFilterImpl( NULL, info.GetAttacker() ); } +#endif //----------------------------------------------------------------------------- // Purpose: Input handler for testing the activator. If the activator passes the @@ -66,14 +111,57 @@ void CBaseFilter::InputTestActivator( inputdata_t &inputdata ) { if ( PassesFilter( inputdata.pCaller, inputdata.pActivator ) ) { +#ifdef MAPBASE + m_OnPass.FireOutput( inputdata.pActivator, m_bPassCallerWhenTested ? inputdata.pCaller : this ); +#else m_OnPass.FireOutput( inputdata.pActivator, this ); +#endif } else { +#ifdef MAPBASE + m_OnFail.FireOutput( inputdata.pActivator, m_bPassCallerWhenTested ? inputdata.pCaller : this ); +#else m_OnFail.FireOutput( inputdata.pActivator, this ); +#endif } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for testing the activator. If the activator passes the +// filter test, the OnPass output is fired. If not, the OnFail output is fired. +//----------------------------------------------------------------------------- +void CBaseFilter::InputTestEntity( inputdata_t &inputdata ) +{ + if ( PassesFilter( inputdata.pCaller, inputdata.value.Entity() ) ) + { + m_OnPass.FireOutput( inputdata.value.Entity(), m_bPassCallerWhenTested ? inputdata.pCaller : this ); + } + else + { + m_OnFail.FireOutput( inputdata.value.Entity(), m_bPassCallerWhenTested ? inputdata.pCaller : this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tries to set the filter's target since most filters use "filtername" anyway +//----------------------------------------------------------------------------- +void CBaseFilter::InputSetField( inputdata_t& inputdata ) +{ + KeyValue("filtername", inputdata.value.String()); + Activate(); +} +#endif + +#ifdef MAPBASE_VSCRIPT +bool CBaseFilter::ScriptPassesFilter( HSCRIPT pCaller, HSCRIPT pEntity ) { return PassesFilter( ToEnt(pCaller), ToEnt(pEntity) ); } +bool CBaseFilter::ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } +bool CBaseFilter::ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesFinalDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } +bool CBaseFilter::ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? BloodAllowed( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } +bool CBaseFilter::ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? DamageMod( ToEnt( pCaller ), *HScriptToClass( pInfo ) ) : NULL; } +#endif + // ################################################################### // > FilterMultiple @@ -97,8 +185,18 @@ class CFilterMultiple : public CBaseFilter EHANDLE m_hFilter[MAX_FILTERS]; bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ); +#ifdef MAPBASE + bool PassesDamageFilterImpl(CBaseEntity *pCaller, const CTakeDamageInfo &info); +#else bool PassesDamageFilterImpl(const CTakeDamageInfo &info); +#endif void Activate(void); + +#ifdef MAPBASE + bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ); + bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ); + bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ); +#endif }; LINK_ENTITY_TO_CLASS(filter_multi, CFilterMultiple); @@ -145,6 +243,13 @@ void CFilterMultiple::Activate( void ) Warning("filter_multi: Tried to add entity (%s) which is not a filter entity!\n", STRING( m_iFilterName[i] ) ); continue; } +#ifdef MAPBASE + else if ( pFilter == this ) + { + Warning("filter_multi: Tried to add itself!\n"); + continue; + } +#endif // Take this entity and increment out array pointer m_hFilter[nNextFilter] = pFilter; @@ -198,7 +303,11 @@ bool CFilterMultiple::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEnti // Purpose: Returns true if the entity passes our filter, false if not. // Input : pEntity - Entity to test. //----------------------------------------------------------------------------- +#ifdef MAPBASE +bool CFilterMultiple::PassesDamageFilterImpl(CBaseEntity *pCaller, const CTakeDamageInfo &info) +#else bool CFilterMultiple::PassesDamageFilterImpl(const CTakeDamageInfo &info) +#endif { // Test against each filter if (m_nFilterType == FILTER_AND) @@ -208,7 +317,11 @@ bool CFilterMultiple::PassesDamageFilterImpl(const CTakeDamageInfo &info) if (m_hFilter[i] != NULL) { CBaseFilter* pFilter = (CBaseFilter *)(m_hFilter[i].Get()); +#ifdef MAPBASE + if (!pFilter->PassesDamageFilter(pCaller, info)) +#else if (!pFilter->PassesDamageFilter(info)) +#endif { return false; } @@ -223,7 +336,11 @@ bool CFilterMultiple::PassesDamageFilterImpl(const CTakeDamageInfo &info) if (m_hFilter[i] != NULL) { CBaseFilter* pFilter = (CBaseFilter *)(m_hFilter[i].Get()); +#ifdef MAPBASE + if (pFilter->PassesDamageFilter(pCaller, info)) +#else if (pFilter->PassesDamageFilter(info)) +#endif { return true; } @@ -233,6 +350,125 @@ bool CFilterMultiple::PassesDamageFilterImpl(const CTakeDamageInfo &info) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Returns true if blood should be allowed, false if not. +// Input : pEntity - Entity to test. +//----------------------------------------------------------------------------- +bool CFilterMultiple::BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) +{ + // Test against each filter + if (m_nFilterType == FILTER_AND) + { + for (int i=0;iBloodAllowed(pCaller, info)) + { + return false; + } + } + } + return true; + } + else // m_nFilterType == FILTER_OR + { + for (int i=0;iBloodAllowed(pCaller, info)) + { + return true; + } + } + } + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the entity passes our filter, false if not. +// Input : pEntity - Entity to test. +//----------------------------------------------------------------------------- +bool CFilterMultiple::PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) +{ + // Test against each filter + if (m_nFilterType == FILTER_AND) + { + for (int i=0;iPassesFinalDamageFilter(pCaller, info)) + { + return false; + } + } + } + return true; + } + else // m_nFilterType == FILTER_OR + { + for (int i=0;iPassesFinalDamageFilter(pCaller, info)) + { + return true; + } + } + } + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if damage should be modded, false if not. +// Input : pEntity - Entity to test. +//----------------------------------------------------------------------------- +bool CFilterMultiple::DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) +{ + // Test against each filter + if (m_nFilterType == FILTER_AND) + { + for (int i=0;iDamageMod(pCaller, info)) + { + return false; + } + } + } + return true; + } + else // m_nFilterType == FILTER_OR + { + for (int i=0;iDamageMod(pCaller, info)) + { + return true; + } + } + } + return false; + } +} +#endif + // ################################################################### // > FilterName @@ -257,6 +493,14 @@ public: return pEntity->NameMatches( STRING(m_iFilterName) ); } } + +#ifdef MAPBASE + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterName = inputdata.value.StringID(); + } +#endif }; LINK_ENTITY_TO_CLASS( filter_activator_name, CFilterName ); @@ -285,6 +529,14 @@ public: { return pEntity->ClassMatches( STRING(m_iFilterClass) ); } + +#ifdef MAPBASE + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterClass = inputdata.value.StringID(); + } +#endif }; LINK_ENTITY_TO_CLASS( filter_activator_class, CFilterClass ); @@ -312,6 +564,14 @@ public: { return ( pEntity->GetTeamNumber() == m_iFilterTeam ); } + +#ifdef MAPBASE + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_INTEGER); + m_iFilterTeam = inputdata.value.Int(); + } +#endif }; LINK_ENTITY_TO_CLASS( filter_activator_team, FilterTeam ); @@ -342,6 +602,14 @@ public: return ( pEntity->VPhysicsGetObject()->GetMass() > m_fFilterMass ); } + +#ifdef MAPBASE + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_FLOAT); + m_fFilterMass = inputdata.value.Float(); + } +#endif }; LINK_ENTITY_TO_CLASS( filter_activator_mass_greater, CFilterMassGreater ); @@ -370,12 +638,58 @@ protected: return true; } +#ifdef MAPBASE + bool PassesDamageFilterImpl(CBaseEntity *pCaller, const CTakeDamageInfo &info) +#else bool PassesDamageFilterImpl(const CTakeDamageInfo &info) +#endif { +#ifdef MAPBASE + switch (m_iFilterType) + { + case 1: return (info.GetDamageType() & m_iDamageType) != 0; + case 2: + { + int iRecvDT = info.GetDamageType(); + int iOurDT = m_iDamageType; + while (iRecvDT) + { + if (iRecvDT & iOurDT) + return true; + + iRecvDT >>= 1; iOurDT >>= 1; + } + return false; + } break; + } +#endif return info.GetDamageType() == m_iDamageType; } +#ifdef MAPBASE + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_INTEGER); + m_iDamageType = inputdata.value.Int(); + } + + bool KeyValue( const char *szKeyName, const char *szValue ) + { + if (FStrEq( szKeyName, "damageor" ) || FStrEq( szKeyName, "damagepresets" )) + { + m_iDamageType |= atoi( szValue ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; + } +#endif + int m_iDamageType; +#ifdef MAPBASE + int m_iFilterType; +#endif }; LINK_ENTITY_TO_CLASS( filter_damage_type, FilterDamageType ); @@ -384,6 +698,9 @@ BEGIN_DATADESC( FilterDamageType ) // Keyfields DEFINE_KEYFIELD( m_iDamageType, FIELD_INTEGER, "damagetype" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iFilterType, FIELD_INTEGER, "FilterType" ), +#endif END_DATADESC() @@ -403,7 +720,19 @@ class CFilterEnemy : public CBaseFilter public: virtual bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ); - virtual bool PassesDamageFilterImpl( const CTakeDamageInfo &info ); +#ifdef MAPBASE + virtual bool PassesDamageFilterImpl(CBaseEntity *pCaller, const CTakeDamageInfo &info); +#else + virtual bool PassesDamageFilterImpl(const CTakeDamageInfo &info); +#endif + +#ifdef MAPBASE + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iszEnemyName = inputdata.value.StringID(); + } +#endif private: @@ -415,7 +744,9 @@ private: float m_flRadius; // Radius (enemies are acquired at this range) float m_flOuterRadius; // Outer radius (enemies are LOST at this range) int m_nMaxSquadmatesPerEnemy; // Maximum number of squadmates who may share the same enemy +#ifndef MAPBASE string_t m_iszPlayerName; // "!player" +#endif }; //----------------------------------------------------------------------------- @@ -452,7 +783,11 @@ bool CFilterEnemy::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +#ifdef MAPBASE +bool CFilterEnemy::PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) +#else bool CFilterEnemy::PassesDamageFilterImpl( const CTakeDamageInfo &info ) +#endif { // NOTE: This function has no meaning to this implementation of the filter class! Assert( 0 ); @@ -470,6 +805,9 @@ bool CFilterEnemy::PassesNameFilter( CBaseEntity *pEnemy ) if ( m_iszEnemyName == NULL_STRING ) return true; +#ifdef MAPBASE + if ( m_iszEnemyName == gm_isz_name_player ) +#else // Cache off the special case player name if ( m_iszPlayerName == NULL_STRING ) { @@ -477,6 +815,7 @@ bool CFilterEnemy::PassesNameFilter( CBaseEntity *pEnemy ) } if ( m_iszEnemyName == m_iszPlayerName ) +#endif { if ( pEnemy->IsPlayer() ) { @@ -488,7 +827,11 @@ bool CFilterEnemy::PassesNameFilter( CBaseEntity *pEnemy ) } // May be either a targetname or classname +#ifdef MAPBASE + bool bNameOrClassnameMatches = ( pEnemy->NameMatches(STRING(m_iszEnemyName)) || pEnemy->ClassMatches(STRING(m_iszEnemyName)) ); +#else bool bNameOrClassnameMatches = ( m_iszEnemyName == pEnemy->GetEntityName() || m_iszEnemyName == pEnemy->m_iClassname ); +#endif // We only leave this code block in a state meaning we've "succeeded" in any context if ( m_bNegated ) @@ -626,6 +969,1345 @@ BEGIN_DATADESC( CFilterEnemy ) DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "filter_radius" ), DEFINE_KEYFIELD( m_flOuterRadius, FIELD_FLOAT, "filter_outer_radius" ), DEFINE_KEYFIELD( m_nMaxSquadmatesPerEnemy, FIELD_INTEGER, "filter_max_per_enemy" ), +#ifndef MAPBASE DEFINE_FIELD( m_iszPlayerName, FIELD_STRING ), +#endif END_DATADESC() + +#ifdef MAPBASE +// ################################################################### +// > CFilterModel +// ################################################################### +class CFilterModel : public CBaseFilter +{ + DECLARE_CLASS( CFilterModel, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterModel; + string_t m_strFilterSkin; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (FStrEq(STRING(m_strFilterSkin), "-1") /*m_strFilterSkin == NULL_STRING|| FStrEq(STRING(m_strFilterSkin), "")*/) + return Matcher_NamesMatch(STRING(m_iFilterModel), STRING(pEntity->GetModelName())); + else if (pEntity->GetBaseAnimating()) + { + //DevMsg("Skin isn't null\n"); + return Matcher_NamesMatch(STRING(m_iFilterModel), STRING(pEntity->GetModelName())) && Matcher_Match(STRING(m_strFilterSkin), pEntity->GetBaseAnimating()->m_nSkin); + } + return false; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterModel = inputdata.value.StringID(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_model, CFilterModel ); + +BEGIN_DATADESC( CFilterModel ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterModel, FIELD_STRING, "filtermodel" ), + DEFINE_KEYFIELD( m_iFilterModel, FIELD_STRING, "filtername" ), + DEFINE_KEYFIELD( m_strFilterSkin, FIELD_STRING, "skin" ), + +END_DATADESC() + +// ################################################################### +// > CFilterContext +// ################################################################### +class CFilterContext : public CBaseFilter +{ + DECLARE_CLASS( CFilterContext, CBaseFilter ); + DECLARE_DATADESC(); + +public: + bool m_bAny; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + bool passes = false; + ResponseContext_t curcontext; + const char *contextvalue; + for (int i = 0; i < GetContextCount(); i++) + { + curcontext = m_ResponseContexts[i]; + if (!pEntity->HasContext(STRING(curcontext.m_iszName), NULL)) + { + if (m_bAny) + continue; + else + return false; + } + + contextvalue = pEntity->GetContextValue(STRING(curcontext.m_iszName)); + if (Matcher_NamesMatch(STRING(m_ResponseContexts[i].m_iszValue), contextvalue)) + { + passes = true; + if (m_bAny) + break; + } + else if (!m_bAny) + { + return false; + } + } + + return passes; + } + + void InputSetField( inputdata_t& inputdata ) + { + m_ResponseContexts.RemoveAll(); + AddContext(inputdata.value.String()); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_context, CFilterContext ); + +BEGIN_DATADESC( CFilterContext ) + + // Keyfields + DEFINE_KEYFIELD( m_bAny, FIELD_BOOLEAN, "any" ), + +END_DATADESC() + +// ################################################################### +// > CFilterSquad +// ################################################################### +class CFilterSquad : public CBaseFilter +{ + DECLARE_CLASS( CFilterSquad, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterName; + bool m_bAllowSilentSquadMembers; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if (pEntity && pNPC) + { + if (pNPC->GetSquad() && Matcher_NamesMatch(STRING(m_iFilterName), pNPC->GetSquad()->GetName())) + { + if (CAI_Squad::IsSilentMember(pNPC)) + { + return m_bAllowSilentSquadMembers; + } + else + { + return true; + } + } + } + + return false; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterName = inputdata.value.StringID(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_squad, CFilterSquad ); + +BEGIN_DATADESC( CFilterSquad ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ), + DEFINE_KEYFIELD( m_bAllowSilentSquadMembers, FIELD_BOOLEAN, "allowsilentmembers" ), + +END_DATADESC() + +// ################################################################### +// > CFilterHintGroup +// ################################################################### +class CFilterHintGroup : public CBaseFilter +{ + DECLARE_CLASS( CFilterHintGroup, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterName; + ThreeState_t m_fHintLimiting; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (!pEntity) + return false; + + if (CAI_BaseNPC *pNPC = pEntity->MyNPCPointer()) + { + if (pNPC->GetHintGroup() == NULL_STRING || Matcher_NamesMatch(STRING(m_iFilterName), STRING(pNPC->GetHintGroup()))) + { + switch (m_fHintLimiting) + { + case TRS_FALSE: return !pNPC->IsLimitingHintGroups(); break; + case TRS_TRUE: return pNPC->IsLimitingHintGroups(); break; + } + + return true; + } + } + else if (CAI_Hint *pHint = dynamic_cast(pEntity)) + { + // Just in case someone somehow puts a hint node through a filter. + // Maybe they'd use a point_advanced_finder or something, I dunno. + return Matcher_NamesMatch(STRING(m_iFilterName), STRING(pHint->GetGroup())); + } + + return false; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterName = inputdata.value.StringID(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_hintgroup, CFilterHintGroup ); + +BEGIN_DATADESC( CFilterHintGroup ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ), + DEFINE_KEYFIELD( m_fHintLimiting, FIELD_INTEGER, "hintlimiting" ), + +END_DATADESC() + +extern bool ReadUnregisteredKeyfields(CBaseEntity *pTarget, const char *szKeyName, variant_t *variant); + +// ################################################################### +// > CFilterKeyfield +// ################################################################### +class CFilterKeyfield : public CBaseFilter +{ + DECLARE_CLASS( CFilterKeyfield, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterKey; + string_t m_iFilterValue; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + variant_t var; + bool found = (pEntity->ReadKeyField(STRING(m_iFilterKey), &var) || ReadUnregisteredKeyfields(pEntity, STRING(m_iFilterKey), &var)); + return m_iFilterValue != NULL_STRING ? Matcher_Match(STRING(m_iFilterValue), var.String()) : found; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterKey = inputdata.value.StringID(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_keyfield, CFilterKeyfield ); + +BEGIN_DATADESC( CFilterKeyfield ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterKey, FIELD_STRING, "keyname" ), + DEFINE_KEYFIELD( m_iFilterValue, FIELD_STRING, "value" ), + +END_DATADESC() + +// ################################################################### +// > CFilterRelationship +// ################################################################### +class CFilterRelationship : public CBaseFilter +{ + DECLARE_CLASS( CFilterKeyfield, CBaseFilter ); + DECLARE_DATADESC(); + +public: + Disposition_t m_iDisposition; + string_t m_iszPriority; // string_t to support matchers + bool m_bInvertTarget; + bool m_bReciprocal; + EHANDLE m_hTarget; + + bool RelationshipPasses(CBaseCombatCharacter *pBCC, CBaseEntity *pTarget) + { + if (!pBCC || !pTarget) + return m_iDisposition == D_NU; + + Disposition_t disposition = pBCC->IRelationType(pTarget); + int priority = pBCC->IRelationPriority(pTarget); + + bool passes = (disposition == m_iDisposition); + if (!passes) + return false; + + if (m_iszPriority != NULL_STRING) + { + passes = Matcher_Match(STRING(m_iszPriority), priority); + } + + return passes; + } + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + CBaseEntity *pSubject = NULL; + if (m_target != NULL_STRING) + { + if (!m_hTarget) + { + m_hTarget = gEntList.FindEntityGeneric(NULL, STRING(m_target), pCaller, pEntity, pCaller); + } + pSubject = m_hTarget; + } + + if (!pSubject) + pSubject = pCaller; + + // No subject or entity, cannot continue + if (!pSubject || !pEntity) + return m_iDisposition == D_NU; + + CBaseCombatCharacter *pBCC1 = !m_bInvertTarget ? pSubject->MyCombatCharacterPointer() : pEntity->MyCombatCharacterPointer(); + CBaseEntity *pTarget = m_bInvertTarget ? pSubject : pEntity; + if (!pBCC1) + { + //Warning("Error: %s subject %s is not a character that uses relationships!\n", GetDebugName(), !m_bInvertTarget ? pSubject->GetDebugName() : pEntity->GetDebugName()); + return m_iDisposition == D_NU; + } + + bool passes = RelationshipPasses(pBCC1, pTarget); + if (m_bReciprocal) + passes = RelationshipPasses(pTarget->MyCombatCharacterPointer(), pBCC1); + + return passes; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_target = inputdata.value.StringID(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_relationship, CFilterRelationship ); + +BEGIN_DATADESC( CFilterRelationship ) + + // Keyfields + DEFINE_KEYFIELD( m_iDisposition, FIELD_INTEGER, "disposition" ), + DEFINE_KEYFIELD( m_iszPriority, FIELD_STRING, "rank" ), + DEFINE_KEYFIELD( m_bInvertTarget, FIELD_BOOLEAN, "inverttarget" ), + DEFINE_KEYFIELD( m_bReciprocal, FIELD_BOOLEAN, "Reciprocal" ), + DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ), + +END_DATADESC() + +// ################################################################### +// > CFilterClassify +// ################################################################### +class CFilterClassify : public CBaseFilter +{ + DECLARE_CLASS( CFilterClassify, CBaseFilter ); + DECLARE_DATADESC(); + +public: + Class_T m_iFilterClassify; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + return pEntity->Classify() == m_iFilterClassify; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_INTEGER); + m_iFilterClassify = (Class_T)inputdata.value.Int(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_classify, CFilterClassify ); + +BEGIN_DATADESC( CFilterClassify ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterClassify, FIELD_INTEGER, "filterclassify" ), + +END_DATADESC() + +// ################################################################### +// > CFilterCriteria +// ################################################################### +class CFilterCriteria : public CBaseFilter +{ + DECLARE_CLASS( CFilterCriteria, CBaseFilter ); + DECLARE_DATADESC(); + +public: + bool m_bAny; + bool m_bFull; // All criteria functions are gathered + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (!pEntity) + return false; + + AI_CriteriaSet set; + pEntity->ModifyOrAppendCriteria( set ); + if (m_bFull) + { + // Meeets the full wrath of the response criteria + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if( pPlayer ) + pPlayer->ModifyOrAppendPlayerCriteria( set ); + + pEntity->ReAppendContextCriteria( set ); + } + + bool passes = false; + const char *contextname; + const char *contextvalue; + const char *matchingvalue; + for (int i = 0; i < set.GetCount(); i++) + { + contextname = set.GetName(i); + contextvalue = set.GetValue(i); + + matchingvalue = GetContextValue(contextname); + if (matchingvalue == NULL) + { + if (m_bAny) + continue; + else + return false; + } + else + { + if (Matcher_NamesMatch(matchingvalue, contextvalue)) + { + passes = true; + if (m_bAny) + break; + } + else if (!m_bAny) + { + return false; + } + } + } + + return passes; + } + + void InputSetField( inputdata_t& inputdata ) + { + m_ResponseContexts.RemoveAll(); + AddContext(inputdata.value.String()); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_criteria, CFilterCriteria ); + +BEGIN_DATADESC( CFilterCriteria ) + + // Keyfields + DEFINE_KEYFIELD( m_bAny, FIELD_BOOLEAN, "any" ), + DEFINE_KEYFIELD( m_bFull, FIELD_BOOLEAN, "full" ), + +END_DATADESC() + +extern bool TestEntityTriggerIntersection_Accurate( CBaseEntity *pTrigger, CBaseEntity *pEntity ); + +// ################################################################### +// > CFilterInVolume +// Passes when the entity is within the specified volume. +// ################################################################### +class CFilterInVolume : public CBaseFilter +{ + DECLARE_CLASS( CFilterInVolume, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iszVolumeTester; + + void Spawn() + { + BaseClass::Spawn(); + + // Assume no string = use activator + if (m_iszVolumeTester == NULL_STRING) + m_iszVolumeTester = AllocPooledString("!activator"); + } + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + CBaseEntity *pVolume = gEntList.FindEntityByNameNearest(STRING(m_target), pEntity->GetLocalOrigin(), 0, this, pEntity, pCaller); + if (!pVolume) + { + Msg("%s cannot find volume %s\n", GetDebugName(), STRING(m_target)); + return false; + } + + CBaseEntity *pTarget = gEntList.FindEntityByName(NULL, STRING(m_iszVolumeTester), this, pEntity, pCaller); + if (pTarget) + return TestEntityTriggerIntersection_Accurate(pVolume, pTarget); + else + { + Msg("%s cannot find target entity %s, returning false\n", GetDebugName(), STRING(m_iszVolumeTester)); + return false; + } + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iszVolumeTester = inputdata.value.StringID(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_involume, CFilterInVolume ); + +BEGIN_DATADESC( CFilterInVolume ) + + // Keyfields + DEFINE_KEYFIELD( m_iszVolumeTester, FIELD_STRING, "tester" ), + +END_DATADESC() + +// ################################################################### +// > CFilterSurfaceProp +// ################################################################### +class CFilterSurfaceData : public CBaseFilter +{ + DECLARE_CLASS( CFilterSurfaceData, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterSurface; + int m_iSurfaceIndex; + + enum + { + SURFACETYPE_SURFACEPROP, + SURFACETYPE_GAMEMATERIAL, + }; + + // Gets the surfaceprop's game material and filters by that. + int m_iSurfaceType; + + void ParseSurfaceIndex() + { + m_iSurfaceIndex = physprops->GetSurfaceIndex(STRING(m_iFilterSurface)); + + switch (m_iSurfaceType) + { + case SURFACETYPE_GAMEMATERIAL: + { + const surfacedata_t *pSurfaceData = physprops->GetSurfaceData(m_iSurfaceIndex); + if (pSurfaceData) + m_iSurfaceIndex = pSurfaceData->game.material; + else + Warning("Can't get surface data for %s\n", STRING(m_iFilterSurface)); + } break; + } + } + + void Activate() + { + ParseSurfaceIndex(); + } + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (pEntity->VPhysicsGetObject()) + { + int iMatIndex = pEntity->VPhysicsGetObject()->GetMaterialIndex(); + switch (m_iSurfaceType) + { + case SURFACETYPE_GAMEMATERIAL: + { + const surfacedata_t *pSurfaceData = physprops->GetSurfaceData(iMatIndex); + if (pSurfaceData) + return m_iSurfaceIndex == pSurfaceData->game.material; + } + default: + return iMatIndex == m_iSurfaceIndex; + } + } + + return false; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + m_iFilterSurface = inputdata.value.StringID(); + ParseSurfaceIndex(); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_surfacedata, CFilterSurfaceData ); + +BEGIN_DATADESC( CFilterSurfaceData ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterSurface, FIELD_STRING, "filterstring" ), + DEFINE_KEYFIELD( m_iSurfaceType, FIELD_INTEGER, "SurfaceType" ), + +END_DATADESC() + +// =================================================================== +// Redirect filters +// +// Redirects certain data to a specific filter. +// =================================================================== +class CBaseFilterRedirect : public CBaseFilter +{ + DECLARE_CLASS( CBaseFilterRedirect, CBaseFilter ); + +public: + inline CBaseEntity *GetTargetFilter() + { + // Yes, this hijacks damage filter functionality. + // It's not like it was using it before anyway. + return m_hDamageFilter.Get(); + } + + bool RedirectToFilter( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (GetTargetFilter() && pEntity) + { + CBaseFilter *pFilter = static_cast(GetTargetFilter()); + return pFilter->PassesFilter(pCaller, pEntity); + } + + return pEntity != NULL; + } + + bool RedirectToDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (GetTargetFilter()) + { + CBaseFilter *pFilter = static_cast(GetTargetFilter()); + return pFilter->PassesDamageFilter(pCaller, info); + } + + return true; + } + + virtual bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + return RedirectToDamageFilter( pCaller, info ); + } + + virtual bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + return RedirectToFilter( pCaller, pEntity ); + } + + virtual bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (GetTargetFilter()) + { + CBaseFilter *pFilter = static_cast(GetTargetFilter()); + return pFilter->BloodAllowed(pCaller, info); + } + + return true; + } + + virtual bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) + { + if (GetTargetFilter()) + { + CBaseFilter *pFilter = static_cast(GetTargetFilter()); + return pFilter->DamageMod( pCaller, info ); + } + + return true; + } + + void InputSetField( inputdata_t& inputdata ) + { + inputdata.value.Convert(FIELD_STRING); + InputSetDamageFilter(inputdata); + } + + enum + { + REDIRECT_MUST_PASS_TO_DAMAGE_CALLER, // Must pass to damage caller, if damage is allowed + REDIRECT_MUST_PASS_TO_ACT, // Must pass to do action + REDIRECT_MUST_PASS_ACTIVATORS, // Each activator must pass this filter + }; +}; + +// ################################################################### +// > CFilterRedirectInflictor +// Uses the specified filter to filter by damage inflictor. +// ################################################################### +class CFilterRedirectInflictor : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterRedirectInflictor, CBaseFilterRedirect ); + +public: + bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + return RedirectToFilter(pCaller, info.GetInflictor()); + } +}; + +LINK_ENTITY_TO_CLASS( filter_redirect_inflictor, CFilterRedirectInflictor ); + +// ################################################################### +// > CFilterRedirectWeapon +// Uses the specified filter to filter by either the entity's active weapon or the weapon causing damage, +// depending on the context. +// ################################################################### +class CFilterRedirectWeapon : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterRedirectWeapon, CBaseFilterRedirect ); + +public: + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + CBaseCombatCharacter *pBCC = pEntity->MyCombatCharacterPointer(); + if (pBCC && pBCC->GetActiveWeapon()) + { + return RedirectToFilter( pCaller, pBCC->GetActiveWeapon() ); + } + + return false; + } + + bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + // Pass any weapon found in the damage info + if (info.GetWeapon()) + { + return RedirectToFilter( pCaller, info.GetWeapon() ); + } + + // Check the attacker's active weapon instead + if (info.GetAttacker()) + { + return PassesFilterImpl( pCaller, info.GetAttacker() ); + } + + // No weapon to check + return false; + } +}; + +LINK_ENTITY_TO_CLASS( filter_redirect_weapon, CFilterRedirectWeapon ); + +// ################################################################### +// > CFilterRedirectOwner +// Uses the specified filter to filter by owner entity. +// ################################################################### +class CFilterRedirectOwner : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterRedirectOwner, CBaseFilterRedirect ); + +public: + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (pEntity->GetOwnerEntity()) + { + return RedirectToFilter(pCaller, pEntity->GetOwnerEntity()); + } + + return false; + } +}; + +LINK_ENTITY_TO_CLASS( filter_redirect_owner, CFilterRedirectOwner ); + +// ################################################################### +// > CFilterDamageTransfer +// Transfers damage to another entity. +// ################################################################### +class CFilterDamageTransfer : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterDamageTransfer, CBaseFilterRedirect ); + DECLARE_DATADESC(); + +public: + void Spawn() + { + BaseClass::Spawn(); + + // Assume no string = use activator + if (m_target == NULL_STRING) + m_target = AllocPooledString("!activator"); + + // A number less than or equal to 0 is always synonymous with no limit + if (m_iMaxEntities <= 0) + m_iMaxEntities = MAX_EDICTS; + } + + // Some secondary filter modes shouldn't be used in non-final filter passes + // Always return true on non-standard secondary filter modes + /* + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + return true; + } + */ + + // A hack because of the way final damage filtering now works. + bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (!m_bCallerDamageAllowed) + return false; + else + return m_iSecondaryFilterMode == REDIRECT_MUST_PASS_TO_DAMAGE_CALLER && GetTargetFilter() ? RedirectToDamageFilter(pCaller, info) : true; + } + + // PassesFinalDamageFilter() was created for the express purpose of having filter_damage_transfer function without + // passing damage on filter checks that don't actually lead to us taking damage in the first place. + // PassesFinalDamageFilter() is only called in certain base entity functions where we DEFINITELY will take damage otherwise. + bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_iSecondaryFilterMode == REDIRECT_MUST_PASS_TO_ACT) + { + // Transfer only if the secondary filter passes + if (!RedirectToDamageFilter(pCaller, info)) + { + // Otherwise just return the other flag + return m_bCallerDamageAllowed; + } + } + + CBaseEntity *pTarget = gEntList.FindEntityGeneric(NULL, STRING(m_target), this, info.GetAttacker(), pCaller); + int iNumDamaged = 0; + while (pTarget) + { + // Avoid recursive loops! + if (pTarget->m_hDamageFilter != this) + { + CTakeDamageInfo info2 = info; + + // Adjust damage position stuff + if (m_bAdjustDamagePosition) + { + info2.SetDamagePosition(pTarget->GetAbsOrigin() + (pCaller->GetAbsOrigin() - info.GetDamagePosition())); + + if (pCaller->IsCombatCharacter() && pTarget->IsCombatCharacter()) + pTarget->MyCombatCharacterPointer()->SetLastHitGroup(pCaller->MyCombatCharacterPointer()->LastHitGroup()); + } + + if (m_iSecondaryFilterMode != REDIRECT_MUST_PASS_ACTIVATORS || RedirectToFilter(pCaller, pTarget)) + { + pTarget->TakeDamage(info2); + iNumDamaged++; + } + } + + if (iNumDamaged < m_iMaxEntities) + pTarget = gEntList.FindEntityGeneric(pTarget, STRING(m_target), this, info.GetAttacker(), pCaller); + else + break; + } + + // We've transferred the damage, now determine whether the caller should take damage. + // Boolean surpasses all. + if (!m_bCallerDamageAllowed) + return false; + else + return m_iSecondaryFilterMode == REDIRECT_MUST_PASS_TO_DAMAGE_CALLER && GetTargetFilter() ? RedirectToDamageFilter(pCaller, info) : true; + } + + /* + void InputSetTarget( inputdata_t& inputdata ) + { + m_target = inputdata.value.StringID(); + m_hTarget = NULL; + } + */ + + inline CBaseEntity *GetTarget(CBaseEntity *pCaller, CBaseEntity *pActivator) + { + return gEntList.FindEntityGeneric(NULL, STRING(m_target), this, pActivator, pCaller); + } + + //EHANDLE m_hTarget; + + bool m_bAdjustDamagePosition; + + // See CBaseRedirectFilter enum for more info + int m_iSecondaryFilterMode; + + // If enabled, the caller can be damaged after the transfer. If disabled, the caller cannot. + bool m_bCallerDamageAllowed; + + int m_iMaxEntities = MAX_EDICTS; +}; + +LINK_ENTITY_TO_CLASS( filter_damage_transfer, CFilterDamageTransfer ); + +BEGIN_DATADESC( CFilterDamageTransfer ) + + //DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_bAdjustDamagePosition, FIELD_BOOLEAN, "AdjustDamagePosition" ), + DEFINE_KEYFIELD( m_iMaxEntities, FIELD_INTEGER, "MaxEntities" ), + DEFINE_KEYFIELD( m_iSecondaryFilterMode, FIELD_INTEGER, "SecondaryFilterMode" ), + DEFINE_KEYFIELD( m_bCallerDamageAllowed, FIELD_BOOLEAN, "CallerDamageAllowed" ), + +END_DATADESC() + +// ################################################################### +// > CFilterBloodControl +// Takes advantage of hacks created for filter_damage_transfer to control blood. +// ################################################################### +class CFilterBloodControl : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterBloodControl, CBaseFilterRedirect ); + DECLARE_DATADESC(); +public: + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (GetTargetFilter() && m_bSecondaryFilterIsDamageFilter) + return RedirectToFilter(pCaller, pEntity); + + return true; + } + + bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_bBloodDisabled) + return false; + + return GetTargetFilter() ? RedirectToDamageFilter(pCaller, info) : true; + } + + void InputDisableBlood( inputdata_t &inputdata ) { m_bBloodDisabled = true; } + void InputEnableBlood( inputdata_t &inputdata ) { m_bBloodDisabled = false; } + + bool m_bBloodDisabled; + + // Uses the secondary filter as a damage filter instead of just a blood filter + bool m_bSecondaryFilterIsDamageFilter; +}; + +LINK_ENTITY_TO_CLASS( filter_blood_control, CFilterBloodControl ); + +BEGIN_DATADESC( CFilterBloodControl ) + + DEFINE_KEYFIELD( m_bBloodDisabled, FIELD_BOOLEAN, "BloodDisabled" ), + DEFINE_KEYFIELD( m_bSecondaryFilterIsDamageFilter, FIELD_BOOLEAN, "SecondaryFilterMode" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "DisableBlood", InputDisableBlood ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableBlood", InputEnableBlood ), + +END_DATADESC() + +// ################################################################### +// > CFilterDamageMod +// Modifies damage. +// ################################################################### +class CFilterDamageMod : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterDamageMod, CBaseFilterRedirect ); + DECLARE_DATADESC(); +public: + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (GetTargetFilter() && m_iSecondaryFilterMode == REDIRECT_MUST_PASS_TO_DAMAGE_CALLER) + return RedirectToFilter(pCaller, pEntity); + + return true; + } + + bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (GetTargetFilter() && m_iSecondaryFilterMode == REDIRECT_MUST_PASS_TO_DAMAGE_CALLER) + return RedirectToDamageFilter( pCaller, info ); + + return true; + } + + bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) + { + if (GetTargetFilter()) + { + bool bPass = true; + + switch (m_iSecondaryFilterMode) + { + case REDIRECT_MUST_PASS_TO_DAMAGE_CALLER: + case REDIRECT_MUST_PASS_TO_ACT: bPass = (RedirectToDamageFilter( pCaller, info )); break; + + case REDIRECT_MUST_PASS_ACTIVATORS: bPass = (info.GetAttacker() && RedirectToFilter(pCaller, info.GetAttacker())); break; + } + + if (!bPass) + return false; + } + + if (m_flDamageMultiplier != 1.0f) + info.ScaleDamage(m_flDamageMultiplier); + if (m_flDamageAddend != 0.0f) + info.AddDamage(m_flDamageAddend); + + if (m_iDamageBitsAdded != 0) + info.AddDamageType(m_iDamageBitsAdded); + if (m_iDamageBitsRemoved != 0) + info.AddDamageType(~m_iDamageBitsRemoved); + + if (m_iszNewAttacker != NULL_STRING) + { + if (!m_hNewAttacker) + m_hNewAttacker = gEntList.FindEntityByName(NULL, m_iszNewAttacker, this, info.GetAttacker(), pCaller); + info.SetAttacker(m_hNewAttacker); + } + if (m_iszNewInflictor != NULL_STRING) + { + if (!m_hNewInflictor) + m_hNewInflictor = gEntList.FindEntityByName(NULL, m_iszNewInflictor, this, info.GetAttacker(), pCaller); + info.SetInflictor(m_hNewInflictor); + } + if (m_iszNewWeapon != NULL_STRING) + { + if (!m_hNewWeapon) + m_hNewWeapon = gEntList.FindEntityByName(NULL, m_iszNewWeapon, this, info.GetAttacker(), pCaller); + info.SetWeapon(m_hNewWeapon); + } + + return true; + } + + void InputSetNewAttacker( inputdata_t &inputdata ) { m_iszNewAttacker = inputdata.value.StringID(); m_hNewAttacker = NULL; } + void InputSetNewInflictor( inputdata_t &inputdata ) { m_iszNewInflictor = inputdata.value.StringID(); m_hNewInflictor = NULL; } + void InputSetNewWeapon( inputdata_t &inputdata ) { m_iszNewWeapon = inputdata.value.StringID(); m_hNewWeapon = NULL; } + + float m_flDamageMultiplier = 1.0f; + float m_flDamageAddend; + int m_iDamageBitsAdded; + int m_iDamageBitsRemoved; + + string_t m_iszNewAttacker; EHANDLE m_hNewAttacker; + string_t m_iszNewInflictor; EHANDLE m_hNewInflictor; + string_t m_iszNewWeapon; EHANDLE m_hNewWeapon; + + // See CBaseRedirectFilter enum for more info + int m_iSecondaryFilterMode; +}; + +LINK_ENTITY_TO_CLASS( filter_damage_mod, CFilterDamageMod ); + +BEGIN_DATADESC( CFilterDamageMod ) + + DEFINE_KEYFIELD( m_iszNewAttacker, FIELD_STRING, "NewAttacker" ), + DEFINE_KEYFIELD( m_iszNewInflictor, FIELD_STRING, "NewInflictor" ), + DEFINE_KEYFIELD( m_iszNewWeapon, FIELD_STRING, "NewWeapon" ), + DEFINE_FIELD( m_hNewAttacker, FIELD_EHANDLE ), + DEFINE_FIELD( m_hNewInflictor, FIELD_EHANDLE ), + DEFINE_FIELD( m_hNewWeapon, FIELD_EHANDLE ), + + DEFINE_INPUT( m_flDamageMultiplier, FIELD_FLOAT, "SetDamageMultiplier" ), + DEFINE_INPUT( m_flDamageAddend, FIELD_FLOAT, "SetDamageAddend" ), + DEFINE_INPUT( m_iDamageBitsAdded, FIELD_INTEGER, "SetDamageBitsAdded" ), + DEFINE_INPUT( m_iDamageBitsRemoved, FIELD_INTEGER, "SetDamageBitsRemoved" ), + + DEFINE_KEYFIELD( m_iSecondaryFilterMode, FIELD_INTEGER, "SecondaryFilterMode" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetNewAttacker", InputSetNewAttacker ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetNewInflictor", InputSetNewInflictor ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetNewWeapon", InputSetNewWeapon ), + +END_DATADESC() + +// ################################################################### +// > CFilterDamageLogic +// Fires outputs from damage information. +// ################################################################### +class CFilterDamageLogic : public CBaseFilterRedirect +{ + DECLARE_CLASS( CFilterDamageLogic, CBaseFilterRedirect ); + DECLARE_DATADESC(); +public: + bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + bool bPassesFilter = !GetTargetFilter() || RedirectToDamageFilter( pCaller, info ); + if (!bPassesFilter) + { + if (m_iSecondaryFilterMode == 2) + return true; + else if (m_iSecondaryFilterMode != 1) + return false; + } + + CBaseEntity *pActivator = info.GetAttacker(); + + m_OutInflictor.Set( info.GetInflictor(), pActivator, pCaller ); + m_OutAttacker.Set( info.GetAttacker(), pActivator, pCaller ); + m_OutWeapon.Set( info.GetWeapon(), pActivator, pCaller ); + + m_OutDamage.Set( info.GetDamage(), pActivator, pCaller ); + m_OutMaxDamage.Set( info.GetMaxDamage(), pActivator, pCaller ); + m_OutBaseDamage.Set( info.GetBaseDamage(), pActivator, pCaller ); + + m_OutDamageType.Set( info.GetDamageType(), pActivator, pCaller ); + m_OutDamageCustom.Set( info.GetDamageCustom(), pActivator, pCaller ); + m_OutDamageStats.Set( info.GetDamageStats(), pActivator, pCaller ); + m_OutAmmoType.Set( info.GetAmmoType(), pActivator, pCaller ); + + m_OutDamageForce.Set( info.GetDamageForce(), pActivator, pCaller ); + m_OutDamagePosition.Set( info.GetDamagePosition(), pActivator, pCaller ); + + m_OutForceFriendlyFire.Set( info.IsForceFriendlyFire() ? 1 : 0, pActivator, pCaller ); + + return bPassesFilter; + } + + bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (GetTargetFilter() && m_iSecondaryFilterMode != 2) + return RedirectToDamageFilter( pCaller, info ); + + return true; + } + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (GetTargetFilter() && m_iSecondaryFilterMode != 2) + return RedirectToFilter( pCaller, pEntity ); + + return true; + } + + // 0 = Use as a regular damage filter. If it doesn't pass, damage won't be outputted. + // 1 = Fire outputs even if the secondary filter doesn't pass. + // 2 = Only use the secondary filter for whether to output damage, other damage is actually dealt. + int m_iSecondaryFilterMode; + + // Outputs + COutputEHANDLE m_OutInflictor; + COutputEHANDLE m_OutAttacker; + COutputEHANDLE m_OutWeapon; + + COutputFloat m_OutDamage; + COutputFloat m_OutMaxDamage; + COutputFloat m_OutBaseDamage; + + COutputInt m_OutDamageType; + COutputInt m_OutDamageCustom; + COutputInt m_OutDamageStats; + COutputInt m_OutAmmoType; + + COutputVector m_OutDamageForce; + COutputPositionVector m_OutDamagePosition; + + COutputInt m_OutForceFriendlyFire; +}; + +LINK_ENTITY_TO_CLASS( filter_damage_logic, CFilterDamageLogic ); + +BEGIN_DATADESC( CFilterDamageLogic ) + + DEFINE_KEYFIELD( m_iSecondaryFilterMode, FIELD_INTEGER, "SecondaryFilterMode" ), + + // Outputs + DEFINE_OUTPUT( m_OutInflictor, "OutInflictor" ), + DEFINE_OUTPUT( m_OutAttacker, "OutAttacker" ), + DEFINE_OUTPUT( m_OutWeapon, "OutWeapon" ), + + DEFINE_OUTPUT( m_OutDamage, "OutDamage" ), + DEFINE_OUTPUT( m_OutMaxDamage, "OutMaxDamage" ), + DEFINE_OUTPUT( m_OutBaseDamage, "OutBaseDamage" ), + + DEFINE_OUTPUT( m_OutDamageType, "OutDamageType" ), + DEFINE_OUTPUT( m_OutDamageCustom, "OutDamageCustom" ), + DEFINE_OUTPUT( m_OutDamageStats, "OutDamageStats" ), + DEFINE_OUTPUT( m_OutAmmoType, "OutAmmoType" ), + + DEFINE_OUTPUT( m_OutDamageForce, "OutDamageForce" ), + DEFINE_OUTPUT( m_OutDamagePosition, "OutDamagePosition" ), + + DEFINE_OUTPUT( m_OutForceFriendlyFire, "OutForceFriendlyFire" ), + +END_DATADESC() +#endif + +#ifdef MAPBASE_VSCRIPT +ScriptHook_t g_Hook_PassesFilter; +ScriptHook_t g_Hook_PassesDamageFilter; +ScriptHook_t g_Hook_PassesFinalDamageFilter; +ScriptHook_t g_Hook_BloodAllowed; +ScriptHook_t g_Hook_DamageMod; + +// ################################################################### +// > CFilterScript +// ################################################################### +class CFilterScript : public CBaseFilter +{ + DECLARE_CLASS( CFilterScript, CBaseFilter ); + DECLARE_DATADESC(); + DECLARE_ENT_SCRIPTDESC(); + +public: + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (m_ScriptScope.IsInitialized()) + { + // caller, activator + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pCaller ), ToHScript( pEntity ) }; + if ( !g_Hook_PassesFilter.Call( m_ScriptScope, &functionReturn, args ) ) + { + Warning( "%s: No PassesFilter function\n", GetDebugName() ); + } + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + // caller, info + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pCaller ), pInfo }; + if ( !g_Hook_PassesDamageFilter.Call( m_ScriptScope, &functionReturn, args ) ) + { + // Fall back to main filter function + g_pScriptVM->RemoveInstance( pInfo ); + return PassesFilterImpl( pCaller, info.GetAttacker() ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + // caller, info + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pCaller ), pInfo }; + if ( !g_Hook_PassesFinalDamageFilter.Call( m_ScriptScope, &functionReturn, args ) ) + { + g_pScriptVM->RemoveInstance( pInfo ); + return BaseClass::PassesFinalDamageFilter( pCaller, info ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + // caller, info + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pCaller ), pInfo }; + if ( !g_Hook_BloodAllowed.Call( m_ScriptScope, &functionReturn, args ) ) + { + g_pScriptVM->RemoveInstance( pInfo ); + return BaseClass::BloodAllowed( pCaller, info ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( &info ); + + // caller, info + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pCaller ), pInfo }; + if ( !g_Hook_DamageMod.Call( m_ScriptScope, &functionReturn, args ) ) + { + g_pScriptVM->RemoveInstance( pInfo ); + return BaseClass::DamageMod( pCaller, info ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } +}; + +LINK_ENTITY_TO_CLASS( filter_script, CFilterScript ); + +BEGIN_DATADESC( CFilterScript ) +END_DATADESC() + +BEGIN_ENT_SCRIPTDESC( CFilterScript, CBaseFilter, "The filter_script entity which allows VScript functions to hook onto filter methods." ) + + // + // Hooks + // + + // The CFilterScript class is visible in the help string, so "A hook used by filter_script" is redundant, but these names are also + // used for functions in CBaseFilter. In order to reduce confusion, the description emphasizes that these are hooks. + BEGIN_SCRIPTHOOK( g_Hook_PassesFilter, "PassesFilter", FIELD_BOOLEAN, "A hook used by filter_script to determine what entities should pass it. Return true if the entity should pass or false if it should not. This hook is required for regular filtering." ) + DEFINE_SCRIPTHOOK_PARAM( "caller", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "activator", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_PassesDamageFilter, "PassesDamageFilter", FIELD_BOOLEAN, "A hook used by filter_script to determine what damage should pass it when it's being used as a damage filter. Return true if the info should pass or false if it should not. If this hook is not defined in a filter_script, damage filter requests will instead check PassesFilter with the attacker as the activator." ) + DEFINE_SCRIPTHOOK_PARAM( "caller", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_PassesFinalDamageFilter, "PassesFinalDamageFilter", FIELD_BOOLEAN, "A completely optional hook used by filter_script which only runs when the entity will take damage. This is different from PassesDamageFilter, which is sometimes used in cases where damage is not actually about to be taken. This also runs after a regular PassesDamageFilter check. Return true if the info should pass or false if it should not. If this hook is not defined, it will always return true." ) + DEFINE_SCRIPTHOOK_PARAM( "caller", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_BloodAllowed, "BloodAllowed", FIELD_BOOLEAN, "A completely optional hook used by filter_script to determine if a caller is allowed to emit blood after taking damage. Return true if blood should be allowed or false if it should not. If this hook is not defined, it will always return true." ) + DEFINE_SCRIPTHOOK_PARAM( "caller", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_DamageMod, "DamageMod", FIELD_BOOLEAN, "A completely optional hook used by filter_script to modify damage being taken by an entity. You are free to use CTakeDamageInfo functions on the damage info handle and it will change how the caller is damaged. Returning true or false currently has no effect on vanilla code, but you should generally return true if the damage info has been modified by your code and false if it was not. If this hook is not defined, it will always return false." ) + DEFINE_SCRIPTHOOK_PARAM( "caller", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + +END_SCRIPTDESC() +#endif diff --git a/mp/src/game/server/filters.h b/mp/src/game/server/filters.h index 483de243..faaa20fd 100644 --- a/mp/src/game/server/filters.h +++ b/mp/src/game/server/filters.h @@ -38,15 +38,47 @@ class CBaseFilter : public CLogicalEntity public: DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif bool PassesFilter( CBaseEntity *pCaller, CBaseEntity *pEntity ); +#ifdef MAPBASE + bool PassesDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ); + + // This was made for filter_damage_transfer. Should return true on all other filters. + virtual bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) { return true; } + + virtual bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) { return true; } + + virtual bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) { return false; } + + // Deprecated. Pass the caller in front. bool PassesDamageFilter( const CTakeDamageInfo &info ); +#else + bool PassesDamageFilter( const CTakeDamageInfo &info ); +#endif + +#ifdef MAPBASE_VSCRIPT + bool ScriptPassesFilter( HSCRIPT pCaller, HSCRIPT pEntity ); + bool ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ); + bool ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ); + bool ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ); + bool ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ); +#endif bool m_bNegated; // Inputs void InputTestActivator( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputTestEntity( inputdata_t &inputdata ); + virtual void InputSetField( inputdata_t &inputdata ); + + bool m_bPassCallerWhenTested; +#endif + // Outputs COutputEvent m_OnPass; // Fired when filter is passed COutputEvent m_OnFail; // Fired when filter is failed @@ -54,7 +86,49 @@ public: protected: virtual bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ); +#ifdef MAPBASE + virtual bool PassesDamageFilterImpl(CBaseEntity *pCaller, const CTakeDamageInfo &info); +#else virtual bool PassesDamageFilterImpl(const CTakeDamageInfo &info); +#endif }; +#ifdef MAPBASE +//========================================================= +// Trace filter that uses a filter entity. +// If the regular trace filter stuff tells this trace to hit an entity, it will go through a filter entity. +// If the entity passes the filter, the trace will go through. +// This can be negated with m_bHitIfPassed, meaning entities that pass will be hit. +// Use m_bFilterExclusive to make the filter the sole factor in hitting an entity. +//========================================================= +class CTraceFilterEntityFilter : public CTraceFilterSimple +{ +public: + CTraceFilterEntityFilter( const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple( passentity, collisionGroup ) {} + CTraceFilterEntityFilter( int collisionGroup ) : CTraceFilterSimple( NULL, collisionGroup ) {} + + bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + bool base = CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ); + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + + if (m_bFilterExclusive && m_pFilter) + return m_pFilter->PassesFilter(m_pCaller, pEntity) ? m_bHitIfPassed : !m_bHitIfPassed; + else if (m_pFilter && (base ? !m_bHitIfPassed : m_bHitIfPassed)) + { + return m_bHitIfPassed ? m_pFilter->PassesFilter(m_pCaller, pEntity) : !m_pFilter->PassesFilter(m_pCaller, pEntity); + } + + return base; + } + + CBaseFilter *m_pFilter; + CBaseEntity *m_pCaller; + + bool m_bHitIfPassed; + bool m_bFilterExclusive; + +}; +#endif + #endif // FILTERS_H diff --git a/mp/src/game/server/fire.cpp b/mp/src/game/server/fire.cpp index ee5fd24c..a4fb9720 100644 --- a/mp/src/game/server/fire.cpp +++ b/mp/src/game/server/fire.cpp @@ -17,11 +17,16 @@ #include "ispatialpartition.h" #include "collisionutils.h" #include "tier0/vprof.h" +#ifdef MAPBASE +#include "weapon_flaregun.h" +#include "mapbase/GlobalStrings.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifndef MAPBASE /******************************************************************** NOTE: if you are looking at this file becase you would like flares to be considered as fires (and thereby trigger gas traps), be aware @@ -40,7 +45,13 @@ For some partial work towards this end, see changelist 192474. ********************************************************************/ - +#else +// ================================================================ // +// env_firesensors now have the ability to sense flares, toggled via spawnflag. +// This is different from integrating it with the greater fire system and easily preserves original behavior. +// You could try treating flares as real fires yourself if you think it's an issue. +// ================================================================ // +#endif @@ -243,7 +254,11 @@ IterationRetval_t CFireSphere::EnumElement( IHandleEntity *pHandleEntity ) { // UNDONE: Measure which of these is faster // CFire *pFire = dynamic_cast(pEntity); +#ifdef MAPBASE + if ( !EntIsClass( pEntity, gm_isz_class_EnvFire ) ) +#else if ( !FClassnameIs( pEntity, "env_fire" ) ) +#endif return ITERATION_CONTINUE; CFire *pFire = static_cast(pEntity); @@ -995,7 +1010,11 @@ void CFire::Update( float simTime ) { continue; } +#ifdef MAPBASE + else if ( EntIsClass( pOther, gm_isz_class_EnvFire ) ) +#else else if ( FClassnameIs( pOther, "env_fire" ) ) +#endif { if ( fireCount < ARRAYSIZE(pFires) ) { @@ -1307,6 +1326,9 @@ void CEnvFireSource::InputDisable( inputdata_t &inputdata ) // CEnvFireSensor detects changes in heat //================================================== #define SF_FIRESENSOR_START_ON 1 +#ifdef MAPBASE +#define SF_FIRESENSOR_ACCEPT_FLARES 2 +#endif class CEnvFireSensor : public CBaseEntity { @@ -1318,6 +1340,9 @@ public: void TurnOff(); void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + int DrawDebugTextOverlays(void); +#endif DECLARE_DATADESC(); @@ -1328,6 +1353,10 @@ private: float m_targetLevel; float m_targetTime; float m_levelTime; +#ifdef MAPBASE + // Only stored for access in debug overlays, don't save + float m_curheat; +#endif COutputEvent m_OnHeatLevelStart; COutputEvent m_OnHeatLevelEnd; @@ -1385,6 +1414,28 @@ void CEnvFireSensor::Think() heat += pFires[i]->GetHeatLevel(); } +#ifdef MAPBASE + if (HasSpawnFlags(SF_FIRESENSOR_ACCEPT_FLARES)) + { + // Also look for nearby flares + CBaseEntity *pEntity = gEntList.FindEntityByClassnameWithin( NULL, "env_flare", GetAbsOrigin(), m_radius ); + while (pEntity) + { + CFlare *pFlare = static_cast(pEntity); + if (pFlare) + { + heat += (pFlare->m_flTimeBurnOut > -1.0 ? (pFlare->m_flTimeBurnOut - gpGlobals->curtime) : 32); + } + + pEntity = gEntList.FindEntityByClassnameWithin( pEntity, "env_flare", GetAbsOrigin(), m_radius ); + } + } +#endif + +#ifdef MAPBASE + m_curheat = heat; +#endif + if ( heat >= m_targetLevel ) { m_levelTime += time; @@ -1442,6 +1493,28 @@ void CEnvFireSensor::InputDisable( inputdata_t &inputdata ) TurnOff(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CEnvFireSensor::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + // print flame size + Q_snprintf(tempstr, sizeof(tempstr), " Current Heat: %f", m_curheat); + EntityText(text_offset,tempstr,0); + text_offset++; + } + return text_offset; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Draw any debug text overlays // Output : Current text offset from the top diff --git a/mp/src/game/server/fish.cpp b/mp/src/game/server/fish.cpp index e6620068..553c7659 100644 --- a/mp/src/game/server/fish.cpp +++ b/mp/src/game/server/fish.cpp @@ -540,6 +540,19 @@ BEGIN_DATADESC( CFishPool ) DEFINE_FIELD( m_isDormant, FIELD_BOOLEAN ), DEFINE_UTLVECTOR( m_fishes, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_INPUT( m_nSkin, FIELD_INTEGER, "skin" ), + + DEFINE_KEYFIELD( m_flLoudPanicRange, FIELD_FLOAT, "LoudPanicRange" ), + DEFINE_KEYFIELD( m_flQuietPanicRange, FIELD_FLOAT, "QuietPanicRange" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "SpawnFish", InputSpawnFish ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "PanicLoudFromPoint", InputPanicLoudFromPoint ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "PanicQuietFromPoint", InputPanicQuietFromPoint ), + + DEFINE_OUTPUT( m_OnSpawnFish, "OnSpawnFish" ), +#endif + DEFINE_THINKFUNC( Update ), END_DATADESC() @@ -553,6 +566,14 @@ CFishPool::CFishPool( void ) m_swimDepth = 0.0f; m_isDormant = false; +#ifdef MAPBASE + m_nSkin = 0; + + // Original defaults + m_flLoudPanicRange = 500.0f; + m_flQuietPanicRange = 75.0f; +#endif + m_visTimer.Start( 0.5f ); ListenForGameEvent( "player_shoot" ); @@ -588,6 +609,10 @@ void CFishPool::Spawn() CHandle hFish; hFish.Set( fish ); m_fishes.AddToTail( hFish ); +#ifdef MAPBASE + fish->m_nSkin = m_nSkin; + m_OnSpawnFish.Set( hFish, fish, this ); +#endif } } } @@ -638,10 +663,14 @@ void CFishPool::FireGameEvent( IGameEvent *event ) CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) ); // the fish panic +#ifdef MAPBASE + float range = (Q_strcmp( "player_footstep", event->GetName() )) ? m_flLoudPanicRange : m_flQuietPanicRange; +#else const float loudRange = 500.0f; const float quietRange = 75.0f; float range = (Q_strcmp( "player_footstep", event->GetName() )) ? loudRange : quietRange; +#endif for( int i=0; iResetVisible(); } @@ -731,3 +769,63 @@ void CFishPool::Update( void ) } } +#ifdef MAPBASE +//------------------------------------------------------------------------------------------------------------- +/** + * Inputs + */ +void CFishPool::InputSpawnFish( inputdata_t &inputdata ) +{ + QAngle heading( 0.0f, RandomFloat( 0, 360.0f ), 0.0f ); + + CFish *fish = (CFish *)Create( "fish", GetAbsOrigin(), heading, this ); + fish->Initialize( this, m_fishes.Count() ); + + if (fish) + { + CHandle hFish; + hFish.Set( fish ); + m_fishes.AddToTail( hFish ); +#ifdef MAPBASE + m_OnSpawnFish.Set( hFish, fish, this ); +#endif + } +} + +void CFishPool::InputPanicLoudFromPoint( inputdata_t &inputdata ) +{ + // Make the fish panic from this point + Vector vecPoint; + inputdata.value.Vector3D( vecPoint ); + for( int i=0; iGetAbsOrigin()).IsLengthGreaterThan( m_flLoudPanicRange )) + { + // event too far away to care + continue; + } + + m_fishes[i]->Panic(); + } +} + +void CFishPool::InputPanicQuietFromPoint( inputdata_t &inputdata ) +{ + // Make the fish panic from this point + Vector vecPoint; + inputdata.value.Vector3D( vecPoint ); + for( int i=0; iGetAbsOrigin()).IsLengthGreaterThan( m_flQuietPanicRange )) + { + // event too far away to care + continue; + } + + m_fishes[i]->Panic(); + } +} +#endif + diff --git a/mp/src/game/server/fish.h b/mp/src/game/server/fish.h index 6eb7e64d..257bdbcd 100644 --- a/mp/src/game/server/fish.h +++ b/mp/src/game/server/fish.h @@ -109,6 +109,12 @@ public: float GetWaterLevel( void ) const; ///< return Z coordinate of water in world coords float GetMaxRange( void ) const; ///< return how far a fish is allowed to wander +#ifdef MAPBASE + void InputSpawnFish( inputdata_t &inputdata ); + void InputPanicLoudFromPoint( inputdata_t &inputdata ); + void InputPanicQuietFromPoint( inputdata_t &inputdata ); +#endif + private: int m_fishCount; ///< number of fish in the pool float m_maxRange; ///< how far a fish is allowed to wander @@ -120,6 +126,15 @@ private: CUtlVector< CHandle > m_fishes; ///< vector of all fish in this pool +#ifdef MAPBASE + int m_nSkin; // Sets the skin of spawned fish + + float m_flLoudPanicRange; + float m_flQuietPanicRange; + + COutputEHANDLE m_OnSpawnFish; +#endif + CountdownTimer m_visTimer; ///< for throttling line of sight checks between all fish }; diff --git a/mp/src/game/server/fogcontroller.cpp b/mp/src/game/server/fogcontroller.cpp index 7057982a..3ed75060 100644 --- a/mp/src/game/server/fogcontroller.cpp +++ b/mp/src/game/server/fogcontroller.cpp @@ -387,5 +387,17 @@ void CFogSystem::LevelInitPostEntity( void ) pPlayer->InitFogController(); } } + else + { + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer && ( pPlayer->m_Local.m_PlayerFog.m_hCtrl.Get() == NULL ) ) + { + pPlayer->InitFogController(); + } + } + } } diff --git a/mp/src/game/server/fourwheelvehiclephysics.cpp b/mp/src/game/server/fourwheelvehiclephysics.cpp index 3a70d02d..ba4a827a 100644 --- a/mp/src/game/server/fourwheelvehiclephysics.cpp +++ b/mp/src/game/server/fourwheelvehiclephysics.cpp @@ -148,6 +148,39 @@ BEGIN_DATADESC_NO_BASE( CFourWheelVehiclePhysics ) DEFINE_FIELD( m_bLastSkid, FIELD_BOOLEAN ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CFourWheelVehiclePhysics, "Handler for four-wheel vehicle physics." ) + + DEFINE_SCRIPTFUNC( SetThrottle, "Sets the throttle." ) + DEFINE_SCRIPTFUNC( SetMaxThrottle, "Sets the max throttle." ) + DEFINE_SCRIPTFUNC( SetMaxReverseThrottle, "Sets the max reverse throttle." ) + DEFINE_SCRIPTFUNC( SetSteering, "Sets the steering." ) + DEFINE_SCRIPTFUNC( SetSteeringDegrees, "Sets the degrees of steering." ) + DEFINE_SCRIPTFUNC( SetAction, "Sets the action." ) + DEFINE_SCRIPTFUNC( SetHandbrake, "Sets the handbrake." ) + DEFINE_SCRIPTFUNC( SetBoost, "Sets the boost." ) + DEFINE_SCRIPTFUNC( SetHasBrakePedal, "Sets whether a handbrake pedal exists." ) + + DEFINE_SCRIPTFUNC( SetDisableEngine, "Sets whether the engine is disabled." ) + DEFINE_SCRIPTFUNC( IsEngineDisabled, "Checks whether the engine is disabled." ) + + DEFINE_SCRIPTFUNC( EnableMotion, "Enables vehicle motion." ) + DEFINE_SCRIPTFUNC( DisableMotion, "Disables vehicle motion." ) + + DEFINE_SCRIPTFUNC( GetSpeed, "Gets the speed." ) + DEFINE_SCRIPTFUNC( GetMaxSpeed, "Gets the max speed." ) + DEFINE_SCRIPTFUNC( GetRPM, "Gets the RPM." ) + DEFINE_SCRIPTFUNC( GetThrottle, "Gets the throttle." ) + DEFINE_SCRIPTFUNC( HasBoost, "Checks if the vehicle has the ability to boost." ) + DEFINE_SCRIPTFUNC( BoostTimeLeft, "Gets how much time is left in any current boost." ) + DEFINE_SCRIPTFUNC( IsBoosting, "Checks if the vehicle is boosting." ) + DEFINE_SCRIPTFUNC( GetHLSpeed, "Gets HL speed." ) + DEFINE_SCRIPTFUNC( GetSteering, "Gets the steeering." ) + DEFINE_SCRIPTFUNC( GetSteeringDegrees, "Gets the degrees of steeering." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Constructor diff --git a/mp/src/game/server/func_areaportal.cpp b/mp/src/game/server/func_areaportal.cpp index ca391317..d2e50a35 100644 --- a/mp/src/game/server/func_areaportal.cpp +++ b/mp/src/game/server/func_areaportal.cpp @@ -42,6 +42,11 @@ public: virtual bool UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient ); +#ifdef MAPBASE + // For func_areaportal_oneway. + int GetPortalState() { return m_state; } +#endif + DECLARE_DATADESC(); private: @@ -189,3 +194,197 @@ int CAreaPortal::UpdateTransmitState() return SetTransmitState( FL_EDICT_DONTSEND ); } +#ifdef MAPBASE +// An areaportal that automatically closes and opens depending on the direction of the client. +// http://developer.valvesoftware.com/wiki/CAreaPortalOneWay +class CAreaPortalOneWay : public CAreaPortal // CAPOW! +{ + DECLARE_CLASS( CAreaPortalOneWay, CAreaPortal ); + DECLARE_DATADESC(); + +public: + Vector m_vecOpenVector; + bool m_bAvoidPop; + bool m_bOneWayActive; + + void Spawn(); + void Activate(); + int Restore(IRestore &restore); + bool UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient ); + + void InputDisableOneWay( inputdata_t &inputdata ); + void InputEnableOneWay( inputdata_t &inputdata ); + void InputToggleOneWay( inputdata_t &inputdata ); + void InputInvertOneWay( inputdata_t &inputdata ); + +protected: + void RemoteUpdate( bool IsOpen ); + + bool m_bRemotelyUpdated; + bool m_bRemoteCalcWasOpen; + CHandle m_hNextPortal; // This get saved to disc... + CAreaPortalOneWay* m_pNextPortal; // ...while this gets used at runtime, avoiding loads of casts + +private: + void UpdateNextPortal( bool IsOpen ); + + // These two are irrelevant once the entity has established itself + string_t m_strGroupName; + Vector m_vecOrigin_; // The portal won't compile properly if vecOrigin itself has a value, but it's okay to move something in at runtime +}; + +LINK_ENTITY_TO_CLASS( func_areaportal_oneway, CAreaPortalOneWay ); + +BEGIN_DATADESC( CAreaPortalOneWay ) + DEFINE_KEYFIELD( m_vecOpenVector, FIELD_VECTOR, "onewayfacing" ), + DEFINE_KEYFIELD( m_bAvoidPop, FIELD_BOOLEAN, "avoidpop" ), + DEFINE_KEYFIELD_NOT_SAVED( m_vecOrigin_, FIELD_VECTOR, "origin_" ), + DEFINE_KEYFIELD_NOT_SAVED( m_strGroupName, FIELD_STRING, "group" ), + DEFINE_FIELD( m_bOneWayActive, FIELD_BOOLEAN ), + DEFINE_FIELD( m_hNextPortal, FIELD_EHANDLE ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableOneWay", InputDisableOneWay ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableOneWay", InputEnableOneWay ), + DEFINE_INPUTFUNC( FIELD_VOID, "ToggleOneWay", InputToggleOneWay ), + DEFINE_INPUTFUNC( FIELD_VOID, "InvertOneWay", InputInvertOneWay ), +END_DATADESC() + +void CAreaPortalOneWay::Spawn() +{ + // Convert our angle from Hammer to a proper vector + QAngle angOpenDir = QAngle( m_vecOpenVector.x, m_vecOpenVector.y, m_vecOpenVector.z ); + AngleVectors( angOpenDir, &m_vecOpenVector ); + + SetLocalOrigin(m_vecOrigin_); + m_bOneWayActive = true; + m_bRemotelyUpdated = false; + + BaseClass::Spawn(); +} + +void CAreaPortalOneWay::Activate() +{ + // Optimisation: share open/closed value for CAPOWs with the same GroupName. + if (m_strGroupName != NULL_STRING) + { + for( unsigned short i = GetPortalListElement(); i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) ) + { + CAreaPortalOneWay* pCur = dynamic_cast(g_AreaPortals[i]); + + if ( pCur && pCur != this && strcmp( STRING(m_strGroupName),STRING(pCur->m_strGroupName) ) == 0 ) + { + m_pNextPortal = pCur; + m_hNextPortal = pCur; + break; + } + } + } + + BaseClass::Activate(); +} + +int CAreaPortalOneWay::Restore(IRestore &restore) +{ + if ( m_hNextPortal.IsValid() ) + m_pNextPortal = m_hNextPortal.Get(); + + return BaseClass::Restore(restore); +} + +// Disable the CAPOW (becomes a normal AP) +void CAreaPortalOneWay::InputDisableOneWay( inputdata_t &inputdata ) +{ + m_bOneWayActive = false; +} + +// Re-enable the CAPOW +void CAreaPortalOneWay::InputEnableOneWay( inputdata_t &inputdata ) +{ + m_bOneWayActive = true; +} + +// Toggle CAPOW +void CAreaPortalOneWay::InputToggleOneWay( inputdata_t &inputdata ) +{ + m_bOneWayActive = !m_bOneWayActive; +} + +// Flip the one way direction +void CAreaPortalOneWay::InputInvertOneWay( inputdata_t &inputdata ) +{ + m_vecOpenVector.Negate(); +} + +// Recieve a shared state from another CAPOW, then pass it on to the next +void CAreaPortalOneWay::RemoteUpdate( bool IsOpen ) +{ + m_bRemotelyUpdated = true; + m_bRemoteCalcWasOpen = IsOpen; + UpdateNextPortal(IsOpen); +} + +// Inline func since this code is required three times +inline void CAreaPortalOneWay::UpdateNextPortal( bool IsOpen ) +{ + if (m_pNextPortal) + m_pNextPortal->RemoteUpdate(IsOpen); +} + +#define VIEWER_PADDING 80 // Value copied from func_areaportalbase.cpp + +bool CAreaPortalOneWay::UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient ) +{ + if (!m_bOneWayActive) + return BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient ); + + if( m_portalNumber == -1 || GetPortalState() == AREAPORTAL_CLOSED ) + { + bIsOpenOnClient = false; + return false; + } + + // Has another CAPOW on our plane already done a calculation? + // Note that the CAPOW chain is traversed with new values in RemoteUpdate(), NOT here + if (m_bRemotelyUpdated) + { + m_bRemotelyUpdated = false; + return m_bRemoteCalcWasOpen ? BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient ) : false; + } + + // *********************** + // If we've got this far then we're the first CAPOW in the chain this frame + // and need to calculate a value and pass it along said chain ourselves + // *********************** + + float dist = VIEWER_PADDING; // Assume open for backfacing tests... + VPlane plane; + if( engine->GetAreaPortalPlane(vOrigin,m_portalNumber,&plane) ) + dist = plane.DistTo(vOrigin); // ...but if we find a plane, use a genuine figure instead. + // This is done because GetAreaPortalPlane only works for + // portals facing the current area. + + // We can use LocalOrigin here because APs never have parents. + float dot = DotProduct(m_vecOpenVector,vOrigin - GetLocalOrigin()); + + if( dot > 0 ) + { + // We are on the open side of the portal. Pass the result on! + UpdateNextPortal(true); + + // The following backfacing check is the inverse of CFuncAreaPortalBase's: + // it /closes/ the portal if the camera is /behind/ the plane. IsOpenOnClient + // is left alone as per func_areaportalbase.h + return dist < -VIEWER_PADDING ? false : true; + } + else // Closed side + { + // To avoid latency pop when crossing the portal's plane, it is only + // closed on the client if said client is outside the "padding zone". + if ( !m_bAvoidPop || (m_bAvoidPop && dist > VIEWER_PADDING) ) + bIsOpenOnClient = false; + + // We are definitely closed on the server, however. + UpdateNextPortal(false); + return false; + } +} +#endif diff --git a/mp/src/game/server/func_areaportalbase.h b/mp/src/game/server/func_areaportalbase.h index ff6c167e..124ece47 100644 --- a/mp/src/game/server/func_areaportalbase.h +++ b/mp/src/game/server/func_areaportalbase.h @@ -95,6 +95,10 @@ public: // see into area 2. virtual bool UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient ); +#ifdef MAPBASE + // For func_areaportal_oneway. + unsigned short GetPortalListElement() { return m_AreaPortalsElement; } +#endif public: diff --git a/mp/src/game/server/func_break.cpp b/mp/src/game/server/func_break.cpp index e7043ea9..7fd23009 100644 --- a/mp/src/game/server/func_break.cpp +++ b/mp/src/game/server/func_break.cpp @@ -220,6 +220,10 @@ bool CBreakable::KeyValue( const char *szKeyName, const char *szValue ) int object = atoi( szValue ); if ( object > 0 && object < ARRAYSIZE(pSpawnObjects) ) m_iszSpawnObject = MAKE_STRING( pSpawnObjects[object] ); +#ifdef MAPBASE + else + m_iszSpawnObject = AllocPooledString(szValue); +#endif } else if (FStrEq(szKeyName, "propdata") ) { @@ -431,9 +435,10 @@ void CBreakable::Precache( void ) case matCinderBlock: pGibName = "ConcreteChunks"; break; + #endif -#if HL2_EPISODIC +#if HL2_EPISODIC || MAPBASE case matNone: pGibName = ""; break; diff --git a/mp/src/game/server/func_breakablesurf.cpp b/mp/src/game/server/func_breakablesurf.cpp index 902722b5..7ce51e8f 100644 --- a/mp/src/game/server/func_breakablesurf.cpp +++ b/mp/src/game/server/func_breakablesurf.cpp @@ -35,6 +35,9 @@ // Spawn flags #define SF_BREAKABLESURF_CRACK_DECALS 0x00000001 #define SF_BREAKABLESURF_DAMAGE_FROM_HELD_OBJECTS 0x00000002 +#ifdef MAPBASE +#define SF_BREAKABLESURF_PLAY_BREAK_SOUND 0x00000004 +#endif //############################################################################# // > CWindowPane @@ -609,7 +612,15 @@ void CBreakableSurface::Die( CBaseEntity *pBreaker, const Vector &vAttackDir ) return; // Play a break sound +#ifdef MAPBASE + if ( HasSpawnFlags(SF_BREAKABLESURF_PLAY_BREAK_SOUND) ) + { + Vector centerPos = (m_vLLVertex + m_vURVertex) / 2; + PhysBreakSound( this, VPhysicsGetObject(), centerPos ); + } +#else PhysBreakSound( this, VPhysicsGetObject(), GetAbsOrigin() ); +#endif m_bIsBroken = true; m_iHealth = 0.0f; diff --git a/mp/src/game/server/func_lod.cpp b/mp/src/game/server/func_lod.cpp index f88c4c0d..e1c41c12 100644 --- a/mp/src/game/server/func_lod.cpp +++ b/mp/src/game/server/func_lod.cpp @@ -28,6 +28,9 @@ public: // (waits until it's out of the view frustrum or until there's a lot of motion) // (m_fDisappearDist+): the bmodel is forced to be invisible CNetworkVar( float, m_fDisappearDist ); +#ifdef MAPBASE + CNetworkVar( float, m_fDisappearMaxDist ); +#endif // CBaseEntity overrides. public: @@ -41,6 +44,9 @@ public: IMPLEMENT_SERVERCLASS_ST(CFunc_LOD, DT_Func_LOD) SendPropFloat(SENDINFO(m_fDisappearDist), 0, SPROP_NOSCALE), +#ifdef MAPBASE + SendPropFloat(SENDINFO(m_fDisappearMaxDist), 0, SPROP_NOSCALE), +#endif END_SEND_TABLE() @@ -53,6 +59,9 @@ LINK_ENTITY_TO_CLASS(func_lod, CFunc_LOD); BEGIN_DATADESC( CFunc_LOD ) DEFINE_FIELD( m_fDisappearDist, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_FIELD( m_fDisappearMaxDist, FIELD_FLOAT ), +#endif END_DATADESC() @@ -98,6 +107,12 @@ bool CFunc_LOD::KeyValue( const char *szKeyName, const char *szValue ) { m_fDisappearDist = (float)atof(szValue); } +#ifdef MAPBASE + else if (FStrEq(szKeyName, "DisappearMaxDist")) + { + m_fDisappearMaxDist = (float)atof(szValue); + } +#endif else if (FStrEq(szKeyName, "Solid")) { if (atoi(szValue) != 0) diff --git a/mp/src/game/server/func_movelinear.cpp b/mp/src/game/server/func_movelinear.cpp index 397fefd1..0fd92a82 100644 --- a/mp/src/game/server/func_movelinear.cpp +++ b/mp/src/game/server/func_movelinear.cpp @@ -39,6 +39,9 @@ BEGIN_DATADESC( CFuncMoveLinear ) DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "BlockDamage"), DEFINE_KEYFIELD( m_flStartPosition, FIELD_FLOAT, "StartPosition"), DEFINE_KEYFIELD( m_flMoveDistance, FIELD_FLOAT, "MoveDistance"), +#ifdef MAPBASE + DEFINE_FIELD( m_vecReference, FIELD_VECTOR ), +#endif // DEFINE_PHYSPTR( m_pFluidController ), // Inputs @@ -84,9 +87,16 @@ void CFuncMoveLinear::Spawn( void ) m_flMoveDistance = DotProductAbs( m_vecMoveDir, vecOBB ) - m_flLip; } +#ifdef MAPBASE + m_vecPosition1 = GetLocalOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition); + m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance); + m_vecFinalDest = GetLocalOrigin(); + m_vecReference = GetLocalOrigin(); +#else m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition); m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance); m_vecFinalDest = GetAbsOrigin(); +#endif SetTouch( NULL ); @@ -116,6 +126,30 @@ bool CFuncMoveLinear::ShouldSavePhysics( void ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets the movement parent of this entity. This entity will be moved +// to a local coordinate calculated from its current absolute offset +// from the parent entity and will then follow the parent entity. +// Input : pParentEntity - This entity's new parent in the movement hierarchy. +//----------------------------------------------------------------------------- +void CFuncMoveLinear::SetParent( CBaseEntity *pParentEntity, int iAttachment ) +{ + Vector oldLocal = GetLocalOrigin(); + + BaseClass::SetParent( pParentEntity, iAttachment ); + + // SOLID_NONE indicates we haven't spawned yet + if (GetSolid() != SOLID_NONE) + { + m_vecReference = ((m_vecReference - oldLocal) + GetLocalOrigin()); + m_vecPosition1 = m_vecReference - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition); + m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance); + m_vecFinalDest = m_vecReference - m_vecFinalDest; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -256,6 +290,16 @@ void CFuncMoveLinear::MoveDone( void ) SetNextThink( gpGlobals->curtime + 0.1f ); BaseClass::MoveDone(); +#ifdef MAPBASE + if ( GetLocalOrigin() == m_vecPosition2 ) + { + m_OnFullyOpen.FireOutput( this, this ); + } + else if ( GetLocalOrigin() == m_vecPosition1 ) + { + m_OnFullyClosed.FireOutput( this, this ); + } +#else if ( GetAbsOrigin() == m_vecPosition2 ) { m_OnFullyOpen.FireOutput( this, this ); @@ -264,6 +308,7 @@ void CFuncMoveLinear::MoveDone( void ) { m_OnFullyClosed.FireOutput( this, this ); } +#endif } @@ -277,8 +322,9 @@ void CFuncMoveLinear::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TY if ( value > 1.0 ) value = 1.0; + Vector move = m_vecPosition1 + (value * (m_vecPosition2 - m_vecPosition1)); - + Vector delta = move - GetLocalOrigin(); float speed = delta.Length() * 10; @@ -380,6 +426,36 @@ int CFuncMoveLinear::DrawDebugTextOverlays(void) if (m_debugOverlays & OVERLAY_TEXT_BIT) { +#ifdef MAPBASE + if (GetMoveParent()) + { + Vector vecReference, vecPosition1, vecPosition2; + QAngle angReference; + + vecReference = m_vecFinalDest + GetMoveParent()->GetAbsOrigin(); + angReference = GetAbsAngles(); + vecPosition1 = vecReference + m_vecPosition1; + vecPosition2 = vecReference + m_vecPosition2; + + NDebugOverlay::Axis( vecReference, angReference, 12.0f, true, 0.15f ); + NDebugOverlay::Axis( vecPosition1, angReference, 5.0f, true, 0.15f ); + NDebugOverlay::Axis( vecPosition2, angReference, 2.5f, true, 0.15f ); + + char tempstr[512]; + float flTravelDist = (vecPosition1 - vecPosition2).Length(); + float flCurDist = (vecPosition1 - GetAbsOrigin()).Length(); + Q_snprintf(tempstr,sizeof(tempstr),"Current Pos: %3.3f",flCurDist/flTravelDist); + EntityText(text_offset,tempstr,0); + text_offset++; + + float flTargetDist = (vecPosition1 - m_vecFinalDest).Length(); + Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",flTargetDist/flTravelDist); + EntityText(text_offset,tempstr,0); + text_offset++; + } + else + { +#else char tempstr[512]; float flTravelDist = (m_vecPosition1 - m_vecPosition2).Length(); float flCurDist = (m_vecPosition1 - GetLocalOrigin()).Length(); @@ -391,6 +467,10 @@ int CFuncMoveLinear::DrawDebugTextOverlays(void) Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",flTargetDist/flTravelDist); EntityText(text_offset,tempstr,0); text_offset++; +#endif +#ifdef MAPBASE + } +#endif } return text_offset; } diff --git a/mp/src/game/server/func_movelinear.h b/mp/src/game/server/func_movelinear.h index 8f09adc2..ac58f963 100644 --- a/mp/src/game/server/func_movelinear.h +++ b/mp/src/game/server/func_movelinear.h @@ -27,6 +27,10 @@ public: bool CreateVPhysics( void ); bool ShouldSavePhysics( void ); +#ifdef MAPBASE + void SetParent( CBaseEntity* pNewParent, int iAttachment = -1 ); +#endif + void MoveTo(Vector vPosition, float flSpeed); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void MoveDone( void ); @@ -53,6 +57,11 @@ public: float m_flBlockDamage; // Damage inflicted when blocked. float m_flStartPosition; // Position of brush when spawned float m_flMoveDistance; // Total distance the brush can move +#ifdef MAPBASE + // For the parenting fix. + // Prevents position inconsistencies when changing parent. + Vector m_vecReference; +#endif IPhysicsFluidController *m_pFluidController; diff --git a/mp/src/game/server/func_reflective_glass.cpp b/mp/src/game/server/func_reflective_glass.cpp index d68840ef..187556b5 100644 --- a/mp/src/game/server/func_reflective_glass.cpp +++ b/mp/src/game/server/func_reflective_glass.cpp @@ -15,13 +15,44 @@ class CFuncReflectiveGlass : public CFuncBrush DECLARE_DATADESC(); DECLARE_CLASS( CFuncReflectiveGlass, CFuncBrush ); DECLARE_SERVERCLASS(); + + CFuncReflectiveGlass() + { +#ifdef MAPBASE + m_iszReflectRenderTarget = AllocPooledString( "_rt_WaterReflection" ); + m_iszRefractRenderTarget = AllocPooledString( "_rt_WaterRefraction" ); +#endif + } + +#ifdef MAPBASE + void InputSetReflectRenderTarget( inputdata_t &inputdata ) { m_iszReflectRenderTarget = inputdata.value.StringID(); } + void InputSetRefractRenderTarget( inputdata_t &inputdata ) { m_iszRefractRenderTarget = inputdata.value.StringID(); } + + CNetworkVar( string_t, m_iszReflectRenderTarget ); + CNetworkVar( string_t, m_iszRefractRenderTarget ); +#endif }; // automatically hooks in the system's callbacks BEGIN_DATADESC( CFuncReflectiveGlass ) + +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszReflectRenderTarget, FIELD_STRING, "ReflectRenderTarget" ), + DEFINE_KEYFIELD( m_iszRefractRenderTarget, FIELD_STRING, "RefractRenderTarget" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetReflectRenderTarget", InputSetReflectRenderTarget ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRefractRenderTarget", InputSetRefractRenderTarget ), +#endif + END_DATADESC() LINK_ENTITY_TO_CLASS( func_reflective_glass, CFuncReflectiveGlass ); IMPLEMENT_SERVERCLASS_ST( CFuncReflectiveGlass, DT_FuncReflectiveGlass ) + +#ifdef MAPBASE + SendPropStringT( SENDINFO( m_iszReflectRenderTarget ) ), + SendPropStringT( SENDINFO( m_iszRefractRenderTarget ) ), +#endif + END_SEND_TABLE() diff --git a/mp/src/game/server/game_ui.cpp b/mp/src/game/server/game_ui.cpp index 0e57ada9..dd2e2032 100644 --- a/mp/src/game/server/game_ui.cpp +++ b/mp/src/game/server/game_ui.cpp @@ -38,6 +38,9 @@ public: // Input handlers void InputDeactivate( inputdata_t &inputdata ); void InputActivate( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputGetButtons( inputdata_t &inputdata ); +#endif void Think( void ); void Deactivate( CBaseEntity *pActivator ); @@ -54,6 +57,14 @@ public: COutputEvent m_pressedBack; COutputEvent m_pressedAttack; COutputEvent m_pressedAttack2; +#ifdef MAPBASE + COutputEvent m_pressedUse; + COutputEvent m_pressedJump; + COutputEvent m_pressedCrouch; + COutputEvent m_pressedAttack3; + COutputEvent m_pressedSprint; + COutputEvent m_pressedReload; +#endif COutputEvent m_unpressedMoveLeft; COutputEvent m_unpressedMoveRight; @@ -61,12 +72,24 @@ public: COutputEvent m_unpressedBack; COutputEvent m_unpressedAttack; COutputEvent m_unpressedAttack2; +#ifdef MAPBASE + COutputEvent m_unpressedUse; + COutputEvent m_unpressedJump; + COutputEvent m_unpressedCrouch; + COutputEvent m_unpressedAttack3; + COutputEvent m_unpressedSprint; + COutputEvent m_unpressedReload; +#endif COutputFloat m_xaxis; COutputFloat m_yaxis; COutputFloat m_attackaxis; COutputFloat m_attack2axis; +#ifdef MAPBASE + COutputInt m_OutButtons; +#endif + bool m_bForceUpdate; int m_nLastButtonState; @@ -84,6 +107,9 @@ BEGIN_DATADESC( CGameUI ) DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), DEFINE_INPUTFUNC( FIELD_STRING, "Activate", InputActivate ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "GetButtons", InputGetButtons ), +#endif DEFINE_OUTPUT( m_playerOn, "PlayerOn" ), DEFINE_OUTPUT( m_playerOff, "PlayerOff" ), @@ -94,6 +120,14 @@ BEGIN_DATADESC( CGameUI ) DEFINE_OUTPUT( m_pressedBack, "PressedBack" ), DEFINE_OUTPUT( m_pressedAttack, "PressedAttack" ), DEFINE_OUTPUT( m_pressedAttack2, "PressedAttack2" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_pressedUse, "PressedUse" ), + DEFINE_OUTPUT( m_pressedJump, "PressedJump" ), + DEFINE_OUTPUT( m_pressedCrouch, "PressedCrouch" ), + DEFINE_OUTPUT( m_pressedAttack3, "PressedAttack3" ), + DEFINE_OUTPUT( m_pressedSprint, "PressedSprint" ), + DEFINE_OUTPUT( m_pressedReload, "PressedReload" ), +#endif DEFINE_OUTPUT( m_unpressedMoveLeft, "UnpressedMoveLeft" ), DEFINE_OUTPUT( m_unpressedMoveRight, "UnpressedMoveRight" ), @@ -101,6 +135,16 @@ BEGIN_DATADESC( CGameUI ) DEFINE_OUTPUT( m_unpressedBack, "UnpressedBack" ), DEFINE_OUTPUT( m_unpressedAttack, "UnpressedAttack" ), DEFINE_OUTPUT( m_unpressedAttack2, "UnpressedAttack2" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_unpressedUse, "UnpressedUse" ), + DEFINE_OUTPUT( m_unpressedJump, "UnpressedJump" ), + DEFINE_OUTPUT( m_unpressedCrouch, "UnpressedCrouch" ), + DEFINE_OUTPUT( m_unpressedAttack3, "UnpressedAttack3" ), + DEFINE_OUTPUT( m_unpressedSprint, "UnpressedSprint" ), + DEFINE_OUTPUT( m_unpressedReload, "UnpressedReload" ), + + DEFINE_OUTPUT( m_OutButtons, "OutButtons" ), +#endif DEFINE_OUTPUT( m_xaxis, "XAxis" ), DEFINE_OUTPUT( m_yaxis, "YAxis" ), @@ -170,7 +214,11 @@ void CGameUI::Deactivate( CBaseEntity *pActivator ) } else { +#ifdef MAPBASE + Warning("%s Deactivate(): I have no player when called by %s!\n", GetEntityName().ToCStr(), pActivator ? pActivator->GetEntityName().ToCStr() : "(null)"); +#else Warning("%s Deactivate(): I have no player when called by %s!\n", GetEntityName().ToCStr(), pActivator->GetEntityName().ToCStr()); +#endif } // Stop thinking @@ -297,6 +345,13 @@ void CGameUI::Think( void ) if ((( pPlayer->m_afButtonPressed & IN_USE ) && ( m_spawnflags & SF_GAMEUI_USE_DEACTIVATES )) || (( pPlayer->m_afButtonPressed & IN_JUMP ) && ( m_spawnflags & SF_GAMEUI_JUMP_DEACTIVATES ))) { +#ifdef MAPBASE + if (pPlayer->m_afButtonPressed & IN_USE) + m_pressedUse.FireOutput( pPlayer, this, 0 ); + if (pPlayer->m_afButtonPressed & IN_JUMP) + m_pressedJump.FireOutput( pPlayer, this, 0 ); +#endif + Deactivate( pPlayer ); return; } @@ -380,6 +435,80 @@ void CGameUI::Think( void ) } } +#ifdef MAPBASE + if ( nButtonsChanged & IN_USE ) + { + if ( m_nLastButtonState & IN_USE ) + { + m_unpressedUse.FireOutput( pPlayer, this, 0 ); + } + else + { + m_pressedUse.FireOutput( pPlayer, this, 0 ); + } + } + + if ( nButtonsChanged & IN_JUMP ) + { + if ( m_nLastButtonState & IN_JUMP ) + { + m_unpressedJump.FireOutput( pPlayer, this, 0 ); + } + else + { + m_pressedJump.FireOutput( pPlayer, this, 0 ); + } + } + + if ( nButtonsChanged & IN_DUCK ) + { + if ( m_nLastButtonState & IN_DUCK ) + { + m_unpressedCrouch.FireOutput( pPlayer, this, 0 ); + } + else + { + m_pressedCrouch.FireOutput( pPlayer, this, 0 ); + } + } + + if ( nButtonsChanged & IN_ATTACK3 ) + { + if ( m_nLastButtonState & IN_ATTACK3 ) + { + m_unpressedAttack3.FireOutput( pPlayer, this, 0 ); + } + else + { + m_pressedAttack3.FireOutput( pPlayer, this, 0 ); + } + } + + if ( nButtonsChanged & IN_SPEED ) + { + if ( m_nLastButtonState & IN_SPEED ) + { + m_unpressedSprint.FireOutput( pPlayer, this, 0 ); + } + else + { + m_pressedSprint.FireOutput( pPlayer, this, 0 ); + } + } + + if ( nButtonsChanged & IN_RELOAD ) + { + if ( m_nLastButtonState & IN_RELOAD ) + { + m_unpressedReload.FireOutput( pPlayer, this, 0 ); + } + else + { + m_pressedReload.FireOutput( pPlayer, this, 0 ); + } + } +#endif + // Setup for the next frame m_nLastButtonState = pPlayer->m_nButtons; @@ -437,3 +566,13 @@ void CGameUI::Think( void ) m_bForceUpdate = false; } + +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: Gets and outputs the player's current buttons +//------------------------------------------------------------------------------ +void CGameUI::InputGetButtons( inputdata_t &inputdata ) +{ + m_OutButtons.Set(m_player ? m_player->m_nButtons : m_nLastButtonState, m_player, this); +} +#endif diff --git a/mp/src/game/server/gameinterface.cpp b/mp/src/game/server/gameinterface.cpp index f6bbebb4..32a61fc9 100644 --- a/mp/src/game/server/gameinterface.cpp +++ b/mp/src/game/server/gameinterface.cpp @@ -89,6 +89,12 @@ #include "tier3/tier3.h" #include "serverbenchmark_base.h" #include "querycache.h" +#ifdef MAPBASE +#include "world.h" +#endif + +#include "vscript/ivscript.h" +#include "vscript_server.h" #ifdef TF_DLL @@ -183,6 +189,7 @@ IServerEngineTools *serverenginetools = NULL; ISceneFileCache *scenefilecache = NULL; IXboxSystem *xboxsystem = NULL; // Xbox 360 only IMatchmaking *matchmaking = NULL; // Xbox 360 only +IScriptManager *scriptmanager = NULL; #if defined( REPLAY_ENABLED ) IReplaySystem *g_pReplay = NULL; IServerReplayContext *g_pReplayServerContext = NULL; @@ -625,6 +632,16 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, if ( IsX360() && (matchmaking = (IMatchmaking *)appSystemFactory( VENGINE_MATCHMAKING_VERSION, NULL )) == NULL ) return false; + if (!CommandLine()->CheckParm("-noscripting")) + { + scriptmanager = (IScriptManager*)appSystemFactory(VSCRIPT_INTERFACE_VERSION, NULL); + + if (scriptmanager == nullptr) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + } + } + // If not running dedicated, grab the engine vgui interface if ( !engine->IsDedicatedServer() ) { @@ -682,6 +699,7 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAchievementSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() ); // The string system must init first + shutdown last IGameSystem::Add( GameStringSystem() ); @@ -702,6 +720,9 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, IGameSystem::Add( SoundEmitterSystem() ); // load Mod specific game events ( MUST be before InitAllSystems() so it can pickup the mod specific events) +#ifdef MAPBASE + gameeventmanager->LoadEventsFromFile("resource/MapbaseEvents.res"); +#endif gameeventmanager->LoadEventsFromFile("resource/ModEvents.res"); #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out @@ -757,6 +778,7 @@ void CServerGameDLL::DLLShutdown( void ) // Due to dependencies, these are not autogamesystems ModelSoundsCacheShutdown(); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAchievementSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); @@ -1082,7 +1104,7 @@ bool g_bCheckForChainedActivate; { \ if ( bCheck ) \ { \ - AssertMsg( g_bReceivedChainedActivate, "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \ + AssertMsg( g_bReceivedChainedActivate == true, "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \ } \ g_bCheckForChainedActivate = false; \ } @@ -1722,9 +1744,41 @@ static TITLECOMMENT gTitleComments[] = #endif }; +#ifdef MAPBASE +extern CUtlVector *Mapbase_GetChapterMaps(); +extern CUtlVector *Mapbase_GetChapterList(); +#endif + #ifdef _XBOX void CServerGameDLL::GetTitleName( const char *pMapName, char* pTitleBuff, int titleBuffSize ) { +#ifdef MAPBASE + // Check the world entity for a chapter title + if ( CWorld *pWorld = GetWorldEntity() ) + { + const char *pWorldChapter = pWorld->GetChapterTitle(); + if ( pWorldChapter && pWorldChapter[0] != '\0' ) + { + Q_strncpy( chapterTitle, pWorldChapter, sizeof( chapterTitle ) ); + return; + } + } + + // Look in the mod's chapter list + CUtlVector *ModChapterComments = Mapbase_GetChapterMaps(); + if (ModChapterComments->Count() > 0) + { + for ( int i = 0; i < ModChapterComments->Count(); i++ ) + { + if ( !Q_strnicmp( mapname, ModChapterComments->Element(i).pBSPName, strlen(ModChapterComments->Element(i).pBSPName) ) ) + { + Q_strncpy( pTitleBuff, ModChapterComments->Element(i).pTitleName, titleBuffSize ); + return; + } + } + } +#endif + // Try to find a matching title comment for this mapname for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ ) { @@ -1734,6 +1788,7 @@ void CServerGameDLL::GetTitleName( const char *pMapName, char* pTitleBuff, int t return; } } + Q_strncpy( pTitleBuff, pMapName, titleBuffSize ); } #endif @@ -1771,6 +1826,44 @@ void CServerGameDLL::GetSaveComment( char *text, int maxlength, float flMinutes, break; } } + +#ifdef MAPBASE + // Look in the mod's chapter list + CUtlVector *ModChapterComments = Mapbase_GetChapterMaps(); + if (ModChapterComments->Count() > 0) + { + for ( int i = 0; i < ModChapterComments->Count(); i++ ) + { + if ( !Q_strnicmp( mapname, ModChapterComments->Element(i).pBSPName, strlen(ModChapterComments->Element(i).pBSPName) ) ) + { + // found one + int j; + + // Got a message, post-process it to be save name friendly + Q_strncpy( comment, ModChapterComments->Element(i).pTitleName, sizeof( comment ) ); + pName = comment; + j = 0; + // Strip out CRs + while ( j < 64 && comment[j] ) + { + if ( comment[j] == '\n' || comment[j] == '\r' ) + comment[j] = 0; + else + j++; + } + break; + } + } + } + + // Check the world entity for a chapter title + if ( CWorld *pWorld = GetWorldEntity() ) + { + const char *pWorldChapter = pWorld->GetChapterTitle(); + if ( pWorldChapter && pWorldChapter[0] != '\0' ) + pName = pWorldChapter; + } +#endif // If we didn't get one, use the designer's map name, or the BSP name itself if ( !pName ) @@ -2121,6 +2214,68 @@ void UpdateChapterRestrictions( const char *mapname ) } } +#ifdef MAPBASE + // Look in the mod's chapter list + CUtlVector *ModChapterComments = Mapbase_GetChapterMaps(); + if (ModChapterComments->Count() > 0) + { + for ( int i = 0; i < ModChapterComments->Count(); i++ ) + { + if ( !Q_strnicmp( mapname, ModChapterComments->Element(i).pBSPName, strlen(ModChapterComments->Element(i).pBSPName) ) ) + { + // found + Q_strncpy( chapterTitle, ModChapterComments->Element(i).pTitleName, sizeof( chapterTitle ) ); + int j = 0; + while ( j < 64 && chapterTitle[j] ) + { + if ( chapterTitle[j] == '\n' || chapterTitle[j] == '\r' ) + chapterTitle[j] = 0; + else + j++; + } + + // Mods can order their own custom chapter names, + // allowing for more flexible string name usage, multiple names in one chapter, etc. + CUtlVector *ModChapterList = Mapbase_GetChapterList(); + for ( int i = 0; i < ModChapterList->Count(); i++ ) + { + if ( !Q_strnicmp( chapterTitle, ModChapterList->Element(i).pChapterName, strlen(chapterTitle) ) ) + { + // ok we have the string, see if it's newer + int nNewChapter = ModChapterList->Element(i).iChapter; + int nUnlockedChapter = sv_unlockedchapters.GetInt(); + + if ( nUnlockedChapter < nNewChapter ) + { + // ok we're at a higher chapter, unlock + sv_unlockedchapters.SetValue( nNewChapter ); + + // HACK: Call up through a better function than this? 7/23/07 - jdw + if ( IsX360() ) + { + engine->ServerCommand( "host_writeconfig\n" ); + } + } + + g_nCurrentChapterIndex = nNewChapter; + return; + } + } + + break; + } + } + } + + // Check the world entity for a chapter title. + if ( CWorld *pWorld = GetWorldEntity() ) + { + const char *pWorldChapter = pWorld->GetChapterTitle(); + if ( pWorldChapter && pWorldChapter[0] != '\0' ) + Q_strncpy( chapterTitle, pWorldChapter, sizeof( chapterTitle ) ); + } +#endif + if ( !chapterTitle[0] ) return; diff --git a/mp/src/game/server/gameinterface.h b/mp/src/game/server/gameinterface.h index 3092d4f3..75b05f91 100644 --- a/mp/src/game/server/gameinterface.h +++ b/mp/src/game/server/gameinterface.h @@ -224,5 +224,22 @@ public: }; EXPOSE_SINGLE_INTERFACE( CServerGameTags, IServerGameTags, INTERFACEVERSION_SERVERGAMETAGS ); +#ifdef MAPBASE +// +// Dynamic mod-based mod title comments +// +typedef struct +{ + char pBSPName[64]; + char pTitleName[64]; +} MODTITLECOMMENT; + +typedef struct +{ + int iChapter; + char pChapterName[64]; +} MODCHAPTER; +#endif + #endif // GAMEINTERFACE_H diff --git a/mp/src/game/server/genericactor.cpp b/mp/src/game/server/genericactor.cpp index dd518a4e..a20cc7a7 100644 --- a/mp/src/game/server/genericactor.cpp +++ b/mp/src/game/server/genericactor.cpp @@ -17,6 +17,9 @@ #include "tier1/strtools.h" #include "vstdlib/random.h" #include "engine/IEngineSound.h" +#ifdef MAPBASE +#include "ai_speech.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -48,6 +51,9 @@ public: void TempGunEffect( void ); string_t m_strHullName; +#ifdef MAPBASE + Class_T m_iClassify = CLASS_NONE; +#endif DECLARE_DATADESC(); }; @@ -56,6 +62,9 @@ LINK_ENTITY_TO_CLASS( generic_actor, CGenericActor ); BEGIN_DATADESC( CGenericActor ) DEFINE_KEYFIELD(m_strHullName, FIELD_STRING, "hull_name" ), +#ifdef MAPBASE + DEFINE_INPUT(m_iClassify, FIELD_INTEGER, "SetClassify" ), +#endif END_DATADESC() @@ -66,7 +75,11 @@ END_DATADESC() //========================================================= Class_T CGenericActor::Classify ( void ) { +#ifdef MAPBASE + return m_iClassify; +#else return CLASS_NONE; +#endif } //========================================================= @@ -138,7 +151,12 @@ void CGenericActor::Spawn() m_flFieldOfView = 0.5;// indicates the width of this NPC's forward view cone ( as a dotproduct result ) m_NPCState = NPC_STATE_NONE; +#ifdef MAPBASE + CapabilitiesAdd( bits_CAP_SQUAD ); + CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_DOORS_GROUP ); +#else CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS ); +#endif // remove head turn if no eyes or forward attachment if (LookupAttachment( "eyes" ) > 0 && LookupAttachment( "forward" ) > 0) @@ -173,6 +191,163 @@ void CGenericActor::Precache() +#ifdef MAPBASE +//========================================================= +#define TLK_ACTOR_PAIN "TLK_WOUND" +#define TLK_ACTOR_DEATH "TLK_DEATH" +#define TLK_ACTOR_ALERT "TLK_STARTCOMBAT" +#define TLK_ACTOR_IDLE "TLK_IDLE" +#define TLK_ACTOR_FEAR "TLK_FEAR" +#define TLK_ACTOR_LOSTENEMY "TLK_LOSTENEMY" +#define TLK_ACTOR_FOUNDENEMY "TLK_REFINDENEMY" +//========================================================= +// Enhanced generic actor with built-in response system usage, weapon capabilities, and more. +//========================================================= +class CGenericActorCustom : public CGenericActor +{ +private: + DECLARE_CLASS( CGenericActorCustom, CGenericActor ); +public: + //DECLARE_DATADESC(); + + CGenericActorCustom() { } + void Spawn( void ); + void Precache( void ); + + bool KeyValue( const char *szKeyName, const char *szValue ); + + void SpeakIfAllowed( const char *concept, AI_CriteriaSet *modifiers = NULL ); + void ModifyOrAppendCriteria( AI_CriteriaSet& set ); + + void PainSound( const CTakeDamageInfo &info ); + void DeathSound( const CTakeDamageInfo &info ); + void AlertSound( void ); + void IdleSound( void ); + void FearSound( void ); + void LostEnemySound( void ); + void FoundEnemySound( void ); +}; + +LINK_ENTITY_TO_CLASS( generic_actor_custom, CGenericActorCustom ); + +//BEGIN_DATADESC( CGenericActorCustom ) +//END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::Spawn() +{ + BaseClass::Spawn(); + + CapabilitiesAdd( bits_CAP_USE_WEAPONS ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::Precache() +{ + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CGenericActorCustom::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "UseShotRegulator")) + { + if (atoi(szValue) > 0) + CapabilitiesAdd( bits_CAP_USE_SHOT_REGULATOR ); + else + CapabilitiesRemove( bits_CAP_USE_SHOT_REGULATOR ); + + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Speak concept +//----------------------------------------------------------------------------- +void CGenericActorCustom::SpeakIfAllowed( const char *concept, AI_CriteriaSet *modifiers ) +{ + Speak( concept, modifiers ? *modifiers : AI_CriteriaSet() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::PainSound( const CTakeDamageInfo &info ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendDamageCriteria( modifiers, info ); + SpeakIfAllowed( TLK_ACTOR_PAIN, &modifiers ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::DeathSound( const CTakeDamageInfo &info ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendDamageCriteria( modifiers, info ); + SpeakIfAllowed( TLK_ACTOR_DEATH, &modifiers ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::AlertSound( void ) +{ + SpeakIfAllowed( TLK_ACTOR_ALERT ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::IdleSound( void ) +{ + SpeakIfAllowed( TLK_ACTOR_IDLE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::FearSound( void ) +{ + SpeakIfAllowed( TLK_ACTOR_FEAR ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::LostEnemySound( void ) +{ + SpeakIfAllowed( TLK_ACTOR_LOSTENEMY ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGenericActorCustom::FoundEnemySound( void ) +{ + SpeakIfAllowed( TLK_ACTOR_FOUNDENEMY ); +} +#endif diff --git a/mp/src/game/server/genericmonster.cpp b/mp/src/game/server/genericmonster.cpp index ed1be819..71fd197f 100644 --- a/mp/src/game/server/genericmonster.cpp +++ b/mp/src/game/server/genericmonster.cpp @@ -17,6 +17,12 @@ #include "physics_bone_follower.h" #include "ai_baseactor.h" #include "ai_senses.h" +#ifdef MAPBASE +/* +#include "ai_basenpc_flyer.h" +#include "player_pickup.h" +*/ +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -470,3 +476,97 @@ void CNPC_Furniture::DrawDebugGeometryOverlays( void ) BaseClass::DrawDebugGeometryOverlays(); } + +#ifdef MAPBASE +/* +//========================================================= +// Generic flying monster +//========================================================= + +class CGenericFlyingMonster : public CAI_BaseFlyingBot +{ +public: + DECLARE_CLASS( CGenericFlyingMonster, CAI_BaseFlyingBot ); + + CGenericFlyingMonster(); + + void Spawn( void ); + void Precache( void ); + int GetSoundInterests ( void ); +}; + +LINK_ENTITY_TO_CLASS( monster_flying_generic, CGenericFlyingMonster ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGenericFlyingMonster::CGenericFlyingMonster() +{ +} + +//========================================================= +// GetSoundInterests - generic NPC can't hear. +//========================================================= +int CGenericFlyingMonster::GetSoundInterests ( void ) +{ + return NULL; +} + +//========================================================= +// Spawn +//========================================================= +void CGenericFlyingMonster::Spawn() +{ + Precache(); + + SetModel( STRING( GetModelName() ) ); + + if ( FStrEq( STRING( GetModelName() ), "models/player.mdl" ) || FStrEq( STRING( GetModelName() ), "models/holo.mdl" ) ) + UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX); + else + UTIL_SetSize(this, NAI_Hull::Mins(HULL_HUMAN), NAI_Hull::Maxs(HULL_HUMAN)); + + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + SetMoveType( MOVETYPE_FLY ); + m_bloodColor = BLOOD_COLOR_RED; + m_flFieldOfView = 0.5;// indicates the width of this NPC's forward view cone ( as a dotproduct result ) + m_NPCState = NPC_STATE_NONE; + + CapabilitiesAdd( bits_CAP_MOVE_FLY ); + + SetNavType( NAV_FLY ); + + AddFlag( FL_FLY ); + + NPCInit(); + if ( !HasSpawnFlags(SF_GENERICNPC_NOTSOLID) ) + { + trace_t tr; + UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), MASK_SOLID, &tr ); + if ( tr.startsolid ) + { + Msg("Placed npc_generic in solid!!! (%s)\n", STRING(GetModelName()) ); + m_spawnflags |= SF_GENERICNPC_NOTSOLID; + } + } + + if ( HasSpawnFlags(SF_GENERICNPC_NOTSOLID) ) + { + AddSolidFlags( FSOLID_NOT_SOLID ); + m_takedamage = DAMAGE_NO; + VPhysicsDestroyObject(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: precaches all resources this NPC needs +//----------------------------------------------------------------------------- +void CGenericFlyingMonster::Precache() +{ + BaseClass::Precache(); + + PrecacheModel( STRING( GetModelName() ) ); +} +*/ +#endif diff --git a/mp/src/game/server/globalstate.cpp b/mp/src/game/server/globalstate.cpp index d8da390c..49a984ab 100644 --- a/mp/src/game/server/globalstate.cpp +++ b/mp/src/game/server/globalstate.cpp @@ -144,6 +144,28 @@ public: return m_list.Count(); } +#ifdef MAPBASE_VSCRIPT + virtual void RegisterVScript() + { + g_pScriptVM->RegisterInstance( this, "Globals" ); + } + + int ScriptAddEntity( const char *pGlobalname, const char *pMapName, int state ) + { + return AddEntity( pGlobalname, pMapName, (GLOBALESTATE)state ); + } + + void ScriptSetState( int globalIndex, int state ) + { + SetState( globalIndex, (GLOBALESTATE)state ); + } + + int ScriptGetState( int globalIndex ) + { + return (int)GetState( globalIndex ); + } +#endif + void Reset( void ); int Save( ISave &save ); int Restore( IRestore &restore ); @@ -324,3 +346,15 @@ CON_COMMAND(server_game_time, "Gives the game time in seconds (server's curtime) ShowServerGameTime(); } + +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CGlobalState, SCRIPT_SINGLETON "Global state system." ) + DEFINE_SCRIPTFUNC( GetIndex, "Gets the index of the specified global name. Returns -1 if it does not exist." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddEntity, "AddGlobal", "Adds a new global with a specific map name and state. Returns its index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetState, "GetState", "Gets the state of the specified global." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetState, "SetState", "Sets the state of the specified global." ) + DEFINE_SCRIPTFUNC( GetCounter, "Gets the counter of the specified global." ) + DEFINE_SCRIPTFUNC( SetCounter, "Sets the counter of the specified global." ) + DEFINE_SCRIPTFUNC( AddToCounter, "Adds to the counter of the specified global." ) +END_SCRIPTDESC(); +#endif diff --git a/mp/src/game/server/hl2/ai_behavior_actbusy.cpp b/mp/src/game/server/hl2/ai_behavior_actbusy.cpp index 9f1ac3ef..9a4f09e6 100644 --- a/mp/src/game/server/hl2/ai_behavior_actbusy.cpp +++ b/mp/src/game/server/hl2/ai_behavior_actbusy.cpp @@ -18,6 +18,9 @@ #include "SoundEmitterSystem/isoundemittersystembase.h" #include "entityblocker.h" #include "npcevent.h" +#ifdef MAPBASE +#include "interval.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -58,6 +61,9 @@ BEGIN_DATADESC( CAI_ActBusyBehavior ) DEFINE_FIELD( m_bInQueue, FIELD_BOOLEAN ), DEFINE_FIELD( m_iCurrentBusyAnim, FIELD_INTEGER ), DEFINE_FIELD( m_hActBusyGoal, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_FIELD( m_hNextActBusyGoal, FIELD_EHANDLE ), +#endif DEFINE_FIELD( m_bNeedToSetBounds, FIELD_BOOLEAN ), DEFINE_FIELD( m_hSeeEntity, FIELD_EHANDLE ), DEFINE_FIELD( m_fTimeLastSawSeeEntity, FIELD_TIME ), @@ -65,6 +71,9 @@ BEGIN_DATADESC( CAI_ActBusyBehavior ) DEFINE_FIELD( m_bExitedBusyToDueSeeEnemy, FIELD_BOOLEAN ), DEFINE_FIELD( m_iNumConsecutivePathFailures, FIELD_INTEGER ), DEFINE_FIELD( m_bAutoFireWeapon, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_FIELD( m_flNextAutoFireTime, FIELD_TIME ), +#endif DEFINE_FIELD( m_flDeferUntil, FIELD_TIME ), DEFINE_FIELD( m_iNumEnemiesInSafeZone, FIELD_INTEGER ), END_DATADESC(); @@ -91,7 +100,11 @@ public: virtual void LevelShutdownPostEntity( void ); // Read in the data from the anim data file +#ifdef MAPBASE + void ParseAnimDataFile( const char *file = "scripts/actbusy.txt" ); +#else void ParseAnimDataFile( void ); +#endif // Parse a keyvalues section into an act busy anim bool ParseActBusyFromKV( busyanim_t *pAnim, KeyValues *pSection ); @@ -107,6 +120,13 @@ protected: CActBusyAnimData g_ActBusyAnimDataSystem; +#ifdef MAPBASE +void ParseCustomActbusyFile( const char *file ) +{ + g_ActBusyAnimDataSystem.ParseAnimDataFile(file); +} +#endif + //----------------------------------------------------------------------------- // Inherited from IAutoServerSystem //----------------------------------------------------------------------------- @@ -126,10 +146,18 @@ void CActBusyAnimData::LevelShutdownPostEntity( void ) //----------------------------------------------------------------------------- // Clear out the stats + their history //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CActBusyAnimData::ParseAnimDataFile( const char *file ) +#else void CActBusyAnimData::ParseAnimDataFile( void ) +#endif { KeyValues *pKVAnimData = new KeyValues( "ActBusyAnimDatafile" ); +#ifdef MAPBASE + if ( pKVAnimData->LoadFromFile( filesystem, file ) ) +#else if ( pKVAnimData->LoadFromFile( filesystem, "scripts/actbusy.txt" ) ) +#endif { // Now try and parse out each act busy anim KeyValues *pKVAnim = pKVAnimData->GetFirstSubKey(); @@ -207,6 +235,10 @@ bool CActBusyAnimData::ParseActBusyFromKV( busyanim_t *pAnim, KeyValues *pSectio pAnim->iBusyInterruptType = BA_INT_NONE; } +#ifdef MAPBASE + pAnim->bTranslateActivity = pSection->GetBool("translateactivity", false); +#endif + return true; } @@ -271,7 +303,11 @@ void CAI_ActBusyBehavior::Enable( CAI_ActBusyGoal *pGoal, float flRange, bool bV m_bMovingToBusy = false; m_bNeedsToPlayExitAnim = false; m_bLeaving = false; +#ifdef MAPBASE + m_flNextBusySearchTime = gpGlobals->curtime + (m_hActBusyGoal.Get() ? m_hActBusyGoal->NextBusySearchInterval().start : ai_actbusy_search_time.GetFloat()); +#else m_flNextBusySearchTime = gpGlobals->curtime + ai_actbusy_search_time.GetFloat(); +#endif m_flEndBusyAt = 0; m_bVisibleOnly = bVisibleOnly; m_bInQueue = dynamic_cast(m_hActBusyGoal.Get()) != NULL; @@ -354,6 +390,16 @@ void CAI_ActBusyBehavior::ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintN { Assert( !m_bLeaving ); +#ifdef MAPBASE + if ( m_bNeedsToPlayExitAnim && !bTeleportToBusy ) + { + if ( HasAnimForActBusy( m_iCurrentBusyAnim, BA_EXIT ) ) + { + m_hNextActBusyGoal = pGoal; + //m_bNextActBusyVisOnly = bVisibleOnly; + } + } +#else if ( m_bNeedsToPlayExitAnim ) { // If we hit this, the mapmaker's told this NPC to actbusy somewhere while it's still in an actbusy. @@ -364,6 +410,7 @@ void CAI_ActBusyBehavior::ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintN return; } } +#endif if ( ai_debug_actbusy.GetInt() == 4 ) { @@ -379,7 +426,18 @@ void CAI_ActBusyBehavior::ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintN Msg("\n"); } +#ifdef MAPBASE + if (!m_hNextActBusyGoal) + { + Enable( pGoal, m_flBusySearchRange, bVisibleOnly ); + } + else + { + Enable( NULL, m_flBusySearchRange, bVisibleOnly ); + } +#else Enable( pGoal, m_flBusySearchRange, bVisibleOnly ); +#endif m_bForceActBusy = true; m_flForcedMaxTime = flMaxTime; m_bTeleportToBusy = bTeleportToBusy; @@ -703,7 +761,12 @@ bool CAI_ActBusyBehavior::ShouldIgnoreSound( CSound *pSound ) //----------------------------------------------------------------------------- void CAI_ActBusyBehavior::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) { +#ifdef MAPBASE + // Now that this has been extended beyond Alyx, it doesn't just need to be the player anymore + if( IsCombatActBusy() && IsInSafeZone( pAttacker ) ) +#else if( IsCombatActBusy() && pSquadmate->IsPlayer() && IsInSafeZone( pAttacker ) ) +#endif { SetCondition( COND_ACTBUSY_AWARE_OF_ENEMY_IN_SAFE_ZONE ); // Break the actbusy, if we're running it. m_flDeferUntil = gpGlobals->curtime + 4.0f; // Stop actbusying and go deal with that enemy!! @@ -804,7 +867,12 @@ void CAI_ActBusyBehavior::GatherConditions( void ) SetCondition( COND_ACTBUSY_LOST_SEE_ENTITY ); m_hActBusyGoal->NPCLostSeeEntity( GetOuter() ); +#ifdef MAPBASE + // Now that this has been extended beyond Alyx, this could just apply to player allies in general + if( IsCombatActBusy() && (m_hSeeEntity->IsPlayer() && GetOuter()->IsPlayerAlly()) ) +#else if( IsCombatActBusy() && (GetOuter()->Classify() == CLASS_PLAYER_ALLY_VITAL && m_hSeeEntity->IsPlayer()) ) +#endif { // Defer any actbusying for several seconds. This serves as a heuristic for waiting // for the player to settle after moving out of the room. This helps Alyx pick a more @@ -892,13 +960,21 @@ void CAI_ActBusyBehavior::GatherConditions( void ) } } +#ifdef MAPBASE + if( m_bAutoFireWeapon && m_flNextAutoFireTime <= gpGlobals->curtime && random->RandomInt(0, 4) <= 3 ) +#else if( m_bAutoFireWeapon && random->RandomInt(0, 5) <= 3 ) +#endif { CBaseCombatWeapon *pWeapon = GetOuter()->GetActiveWeapon(); if( pWeapon ) { pWeapon->Operator_ForceNPCFire( GetOuter(), false ); +#ifdef MAPBASE + pWeapon->DoMuzzleFlash(); + m_flNextAutoFireTime = gpGlobals->curtime + pWeapon->GetFireRate(); +#endif } } @@ -1158,7 +1234,11 @@ int CAI_ActBusyBehavior::SelectScheduleWhileNotBusy( int iBase ) } else { +#ifdef MAPBASE + m_flNextBusySearchTime = gpGlobals->curtime + (m_hActBusyGoal ? RandomInterval(m_hActBusyGoal->NextBusySearchInterval()) : RandomFloat(ai_actbusy_search_time.GetFloat(), ai_actbusy_search_time.GetFloat()*2)); +#else m_flNextBusySearchTime = gpGlobals->curtime + RandomFloat(ai_actbusy_search_time.GetFloat(), ai_actbusy_search_time.GetFloat()*2); +#endif } // We may already have a node @@ -1349,6 +1429,23 @@ int CAI_ActBusyBehavior::SelectSchedule() if ( m_bLeaving ) return SelectScheduleForLeaving(); +#ifdef MAPBASE + if (m_hNextActBusyGoal) + { + if (m_bBusy) + { + m_flEndBusyAt = gpGlobals->curtime; + } + else + { + // Next busy + // (the parameters should've been safely stored when we transferred) + Enable(m_hNextActBusyGoal, m_flBusySearchRange, m_bVisibleOnly); + m_hNextActBusyGoal = NULL; + } + } +#endif + // NPCs should not be busy if the actbusy behaviour has been disabled, or if they've received player squad commands bool bShouldNotBeBusy = (!m_bEnabled || HasCondition( COND_PLAYER_ADDED_TO_SQUAD ) || HasCondition( COND_RECEIVED_ORDERS ) ); if ( bShouldNotBeBusy ) @@ -1615,7 +1712,20 @@ bool CAI_ActBusyBehavior::HasAnimForActBusy( int iActBusy, busyanimparts_t AnimP // Try and play the activity second if ( pBusyAnim->iActivities[AnimPart] != ACT_INVALID ) +#ifdef MAPBASE + { + if (pBusyAnim->bTranslateActivity == true) + { + return GetOuter()->HaveSequenceForActivity( GetOuter()->TranslateActivity(pBusyAnim->iActivities[AnimPart]) ); + } + else + { + return GetOuter()->HaveSequenceForActivity( pBusyAnim->iActivities[AnimPart] ); + } + } +#else return GetOuter()->HaveSequenceForActivity( pBusyAnim->iActivities[AnimPart] ); +#endif return false; } @@ -1677,7 +1787,11 @@ bool CAI_ActBusyBehavior::PlayAnimForActBusy( busyanimparts_t AnimPart ) // Try and play the activity second if ( pBusyAnim->iActivities[AnimPart] != ACT_INVALID ) { +#ifdef MAPBASE + GetOuter()->SetIdealActivity( pBusyAnim->bTranslateActivity ? GetOuter()->TranslateActivity(pBusyAnim->iActivities[AnimPart]) : pBusyAnim->iActivities[AnimPart] ); +#else GetOuter()->SetIdealActivity( pBusyAnim->iActivities[AnimPart] ); +#endif return true; } @@ -2196,7 +2310,11 @@ void CAI_ActBusyBehavior::NotifyBusyEnding( void ) } else { +#ifdef MAPBASE + m_flNextBusySearchTime = gpGlobals->curtime + (m_hActBusyGoal ? RandomInterval(m_hActBusyGoal->NextBusySearchInterval()) : RandomFloat(ai_actbusy_search_time.GetFloat(), ai_actbusy_search_time.GetFloat()*2)); +#else m_flNextBusySearchTime = gpGlobals->curtime + (RandomFloat(ai_actbusy_search_time.GetFloat(), ai_actbusy_search_time.GetFloat()*2)); +#endif } } @@ -2306,6 +2424,12 @@ BEGIN_DATADESC( CAI_ActBusyGoal ) DEFINE_KEYFIELD( m_bVisibleOnly, FIELD_BOOLEAN, "visibleonly" ), DEFINE_KEYFIELD( m_iType, FIELD_INTEGER, "type" ), DEFINE_KEYFIELD( m_bAllowCombatActBusyTeleport, FIELD_BOOLEAN, "allowteleport" ), +#ifdef MAPBASE + // interval_t's can be saved. No instance of its use exists in vanilla Source 2013, + // so it's either an unused field type or something used in the engine. It appears to be functional either way. + // I added built-in keyvalue support as an experiment, and this keyvalue is part of said experiment. + DEFINE_KEYFIELD( m_NextBusySearch, FIELD_INTERVAL, "NextBusy" ), +#endif DEFINE_KEYFIELD( m_iszSeeEntityName, FIELD_STRING, "SeeEntity" ), DEFINE_KEYFIELD( m_flSeeEntityTimeout, FIELD_FLOAT, "SeeEntityTimeout" ), DEFINE_KEYFIELD( m_iszSafeZoneVolume, FIELD_STRING, "SafeZone" ), @@ -2316,15 +2440,33 @@ BEGIN_DATADESC( CAI_ActBusyGoal ) DEFINE_INPUTFUNC( FIELD_STRING, "ForceNPCToActBusy", InputForceNPCToActBusy ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "ForceThisNPCToActBusy", InputForceThisNPCToActBusy ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "ForceThisNPCToLeave", InputForceThisNPCToLeave ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "ForceThisNPCToStopBusy", InputForceThisNPCToStopBusy ), +#endif // Outputs DEFINE_OUTPUT( m_OnNPCStartedBusy, "OnNPCStartedBusy" ), DEFINE_OUTPUT( m_OnNPCFinishedBusy, "OnNPCFinishedBusy" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnNPCStartedLeavingBusy, "OnNPCStartedLeavingBusy" ), + DEFINE_OUTPUT( m_OnNPCMovingToBusy, "OnNPCMovingToBusy" ), + DEFINE_OUTPUT( m_OnNPCAbortedMoveTo, "OnNPCAbortedMoveTo" ), +#endif DEFINE_OUTPUT( m_OnNPCLeft, "OnNPCLeft" ), DEFINE_OUTPUT( m_OnNPCLostSeeEntity, "OnNPCLostSeeEntity" ), DEFINE_OUTPUT( m_OnNPCSeeEnemy, "OnNPCSeeEnemy" ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_ActBusyGoal, CAI_GoalEntity, "A goal entity which makes NPCs act busy." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptForceBusy, "ForceBusy", "Force a NPC to act busy." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptForceBusyComplex, "ForceBusyComplex", "Force a NPC to act busy with additional parameters." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopBusy, "StopBusy", "Force a NPC to stop busying." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2586,12 +2728,30 @@ void CAI_ActBusyGoal::InputForceThisNPCToLeave( inputdata_t &inputdata ) pBehavior->ForceActBusyLeave(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Forces a specific NPC to stop acting busy +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::InputForceThisNPCToStopBusy( inputdata_t &inputdata ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( inputdata.value.Entity(), "InputForceThisNPCToStopBusy" ); + if ( !pBehavior ) + return; + + // Just stop busying + pBehavior->StopBusying(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pNPC - //----------------------------------------------------------------------------- void CAI_ActBusyGoal::NPCMovingToBusy( CAI_BaseNPC *pNPC ) { +#ifdef MAPBASE + m_OnNPCMovingToBusy.Set( pNPC, pNPC, this ); +#endif } //----------------------------------------------------------------------------- @@ -2608,6 +2768,9 @@ void CAI_ActBusyGoal::NPCStartedBusy( CAI_BaseNPC *pNPC ) //----------------------------------------------------------------------------- void CAI_ActBusyGoal::NPCStartedLeavingBusy( CAI_BaseNPC *pNPC ) { +#ifdef MAPBASE + m_OnNPCStartedLeavingBusy.Set( pNPC, pNPC, this ); +#endif } //----------------------------------------------------------------------------- @@ -2616,6 +2779,9 @@ void CAI_ActBusyGoal::NPCStartedLeavingBusy( CAI_BaseNPC *pNPC ) //----------------------------------------------------------------------------- void CAI_ActBusyGoal::NPCAbortedMoveTo( CAI_BaseNPC *pNPC ) { +#ifdef MAPBASE + m_OnNPCAbortedMoveTo.Set( pNPC, pNPC, this ); +#endif } //----------------------------------------------------------------------------- @@ -2650,6 +2816,64 @@ void CAI_ActBusyGoal::NPCSeeEnemy( CAI_BaseNPC *pNPC ) m_OnNPCSeeEnemy.Set( pNPC, pNPC, this ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +interval_t &CAI_ActBusyGoal::NextBusySearchInterval() +{ + if (m_NextBusySearch.start == 0) + { + // Return an interval_t version of the convar + static interval_t defaultInterval; + defaultInterval.start = ai_actbusy_search_time.GetFloat(); + defaultInterval.range = ai_actbusy_search_time.GetFloat(); // Range is end - start, so we don't have to multiply it here + return defaultInterval; + } + + return m_NextBusySearch; +} +#endif + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::ScriptForceBusy( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( ToEnt( hNPC ), "ForceBusy (vscript)" ); + if ( !pBehavior ) + return; + + // Tell the NPC to immediately act busy + pBehavior->SetBusySearchRange( m_flBusySearchRange ); + pBehavior->ForceActBusy( this, dynamic_cast(ToEnt( hHint )), NO_MAX_TIME, false, bTeleportOnly ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::ScriptForceBusyComplex( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly, bool bVisibleOnly, bool bUseNearestBusy, float flMaxTime, int activity, HSCRIPT pSeeEntity ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( ToEnt( hNPC ), "ForceBusyComplex (vscript)" ); + if ( !pBehavior ) + return; + + // Tell the NPC to immediately act busy + pBehavior->SetBusySearchRange( m_flBusySearchRange ); + pBehavior->ForceActBusy( this, dynamic_cast(ToEnt( hHint )), flMaxTime, bVisibleOnly, bTeleportOnly, bUseNearestBusy, ToEnt( pSeeEntity ), (Activity)activity ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::ScriptStopBusy( HSCRIPT hNPC ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( ToEnt( hNPC ), "StopBusy (vscript)" ); + if ( !pBehavior ) + return; + + // Just stop busying + pBehavior->StopBusying(); +} +#endif + //========================================================================================================== // ACT BUSY QUEUE //========================================================================================================== diff --git a/mp/src/game/server/hl2/ai_behavior_actbusy.h b/mp/src/game/server/hl2/ai_behavior_actbusy.h index 750b600f..a33dd438 100644 --- a/mp/src/game/server/hl2/ai_behavior_actbusy.h +++ b/mp/src/game/server/hl2/ai_behavior_actbusy.h @@ -50,6 +50,9 @@ struct busyanim_t float flMaxTime; // Max time spent in this busy animation. 0 means continue until interrupted. busyinterrupt_t iBusyInterruptType; bool bUseAutomovement; +#ifdef MAPBASE + bool bTranslateActivity; +#endif }; struct busysafezone_t @@ -181,6 +184,10 @@ private: bool m_bInQueue; int m_iCurrentBusyAnim; CHandle m_hActBusyGoal; +#ifdef MAPBASE + // So exit animations can play + CHandle m_hNextActBusyGoal; +#endif bool m_bNeedToSetBounds; EHANDLE m_hSeeEntity; float m_fTimeLastSawSeeEntity; @@ -189,6 +196,9 @@ private: int m_iNumConsecutivePathFailures; // Count how many times we failed to find a path to a node, so we can consider teleporting. bool m_bAutoFireWeapon; +#ifdef MAPBASE + float m_flNextAutoFireTime; +#endif float m_flDeferUntil; int m_iNumEnemiesInSafeZone; @@ -225,6 +235,16 @@ public: int GetType() { return m_iType; } bool IsCombatActBusyTeleportAllowed() { return m_bAllowCombatActBusyTeleport; } +#ifdef MAPBASE + interval_t &NextBusySearchInterval(); +#endif + +#ifdef MAPBASE_VSCRIPT + void ScriptForceBusy( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly ); + void ScriptForceBusyComplex( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly, bool bVisibleOnly, bool bUseNearestBusy, float flMaxTime, int activity, HSCRIPT pSeeEntity ); + void ScriptStopBusy( HSCRIPT hNPC ); +#endif + protected: CAI_ActBusyBehavior *GetBusyBehaviorForNPC( const char *pszActorName, CBaseEntity *pActivator, CBaseEntity *pCaller, const char *sInputName ); CAI_ActBusyBehavior *GetBusyBehaviorForNPC( CBaseEntity *pEntity, const char *sInputName ); @@ -238,14 +258,23 @@ protected: void InputForceNPCToActBusy( inputdata_t &inputdata ); void InputForceThisNPCToActBusy( inputdata_t &inputdata ); void InputForceThisNPCToLeave( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputForceThisNPCToStopBusy( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif protected: float m_flBusySearchRange; bool m_bVisibleOnly; int m_iType; bool m_bAllowCombatActBusyTeleport; +#ifdef MAPBASE + interval_t m_NextBusySearch; +#endif public: // Let the actbusy behavior query these so we don't have to duplicate the data. @@ -257,6 +286,11 @@ public: protected: COutputEHANDLE m_OnNPCStartedBusy; COutputEHANDLE m_OnNPCFinishedBusy; +#ifdef MAPBASE + COutputEHANDLE m_OnNPCStartedLeavingBusy; + COutputEHANDLE m_OnNPCMovingToBusy; + COutputEHANDLE m_OnNPCAbortedMoveTo; +#endif COutputEHANDLE m_OnNPCLeft; COutputEHANDLE m_OnNPCLostSeeEntity; COutputEHANDLE m_OnNPCSeeEnemy; diff --git a/mp/src/game/server/hl2/ai_behavior_functank.cpp b/mp/src/game/server/hl2/ai_behavior_functank.cpp index 0c1ae3bf..f0a32601 100644 --- a/mp/src/game/server/hl2/ai_behavior_functank.cpp +++ b/mp/src/game/server/hl2/ai_behavior_functank.cpp @@ -50,6 +50,12 @@ bool CAI_FuncTankBehavior::CanSelectSchedule() if ( !m_hFuncTank ) return false; +#ifdef MAPBASE + // We're glued to our func_tank, don't get off of it + if ( m_hFuncTank->m_bControllerGlued ) + return true; +#endif + // Are you alive, in a script? if ( !GetOuter()->IsInterruptable() ) return false; @@ -92,6 +98,20 @@ void CAI_FuncTankBehavior::PrescheduleThink() } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_FuncTankBehavior::IsInterruptable( void ) +{ + if ( m_hFuncTank && m_hFuncTank->m_bControllerGlued ) + return false; + + return BaseClass::IsInterruptable(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -114,6 +134,15 @@ int CAI_FuncTankBehavior::SelectSchedule() // If we are not mounted to a func_tank look for one. if ( !IsMounted() ) { +#ifdef MAPBASE + // Forced mounts use a special schedule. + // If our outer is parented, automatically grab the tank if we're in its control volume. + if (HasCondition(COND_FUNCTANK_FORCED) || (GetOuter()->GetParent() && m_hFuncTank->m_hControlVolume)) + { + return SCHED_FORCE_MOUNT_FUNCTANK; + } +#endif + return SCHED_MOVE_TO_FUNCTANK; } @@ -180,6 +209,10 @@ void CAI_FuncTankBehavior::Dismount( void ) // Set this condition to force breakout of any func_tank behavior schedules SetCondition( COND_FUNCTANK_DISMOUNT ); + +#ifdef MAPBASE + ClearCondition( COND_FUNCTANK_FORCED ); +#endif } //----------------------------------------------------------------------------- @@ -204,7 +237,11 @@ int CAI_FuncTankBehavior::OnTakeDamage_Alive( const CTakeDamageInfo &info ) if ( m_hFuncTank && bValidDismountAttacker == true ) { +#ifdef MAPBASE + if ( !m_hFuncTank->IsEntityInViewCone( pAttacker ) && !m_hFuncTank->m_bControllerGlued ) +#else if ( !m_hFuncTank->IsEntityInViewCone( pAttacker ) ) +#endif { SetCondition( COND_FUNCTANK_DISMOUNT ); } @@ -571,7 +608,12 @@ void CAI_FuncTankBehavior::GatherConditions() } } +#ifdef MAPBASE + // So they don't unholster every time there's a tank in the map looking for NPCs + if (!m_hFuncTank && m_bMounted) +#else if ( !m_hFuncTank ) +#endif { m_bMounted = false; GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_UNHOLSTERED ); @@ -704,6 +746,9 @@ AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FuncTankBehavior ) DECLARE_TASK( TASK_FUNCTANK_ANNOUNCE_SCAN ) DECLARE_CONDITION( COND_FUNCTANK_DISMOUNT ) +#ifdef MAPBASE + DECLARE_CONDITION( COND_FUNCTANK_FORCED ) +#endif //========================================================= //========================================================= @@ -773,4 +818,20 @@ AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FuncTankBehavior ) " Interrupts" ) +#ifdef MAPBASE + //========================================================= + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_FORCE_MOUNT_FUNCTANK, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_FACE_FUNCTANK 0" + " TASK_HOLSTER_WEAPON 0" + " " + " Interrupts" + ) +#endif + AI_END_CUSTOM_SCHEDULE_PROVIDER() diff --git a/mp/src/game/server/hl2/ai_behavior_functank.h b/mp/src/game/server/hl2/ai_behavior_functank.h index a8d61466..a9f19496 100644 --- a/mp/src/game/server/hl2/ai_behavior_functank.h +++ b/mp/src/game/server/hl2/ai_behavior_functank.h @@ -49,6 +49,9 @@ public: void BeginScheduleSelection(); void EndScheduleSelection(); void PrescheduleThink(); +#ifdef MAPBASE + bool IsInterruptable( void ); +#endif Activity NPC_TranslateActivity( Activity activity ); @@ -61,6 +64,9 @@ public: SCHED_FIRE_FUNCTANK, SCHED_SCAN_WITH_FUNCTANK, SCHED_FAIL_MOVE_TO_FUNCTANK, +#ifdef MAPBASE + SCHED_FORCE_MOUNT_FUNCTANK, +#endif }; // Tasks @@ -82,6 +88,9 @@ public: enum { COND_FUNCTANK_DISMOUNT = BaseClass::NEXT_CONDITION, +#ifdef MAPBASE + COND_FUNCTANK_FORCED, +#endif NEXT_CONDITION, }; @@ -104,6 +113,12 @@ public: bool IsMounted( void ) { return m_bMounted; } +#ifdef MAPBASE + void SetMounted( bool bMounted ) { m_bMounted = bMounted; } + + bool CanUnholsterWeapon( void ) { return !IsMounted(); } +#endif + private: // Schedule diff --git a/mp/src/game/server/hl2/ai_behavior_police.cpp b/mp/src/game/server/hl2/ai_behavior_police.cpp index eb1b45b3..ffb8dd96 100644 --- a/mp/src/game/server/hl2/ai_behavior_police.cpp +++ b/mp/src/game/server/hl2/ai_behavior_police.cpp @@ -11,6 +11,9 @@ #include "ai_memory.h" #include "collisionutils.h" #include "npc_metropolice.h" +#ifdef MAPBASE +#include "npc_combine.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -125,12 +128,52 @@ void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, SentencePri if ( pCop != NULL ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + pCop->SpeakIfAllowed( pSentence, nSoundPriority, nCriteria ); +#else CAI_Sentence< CNPC_MetroPolice > *pSentences = pCop->GetSentences(); pSentences->Speak( pSentence, nSoundPriority, nCriteria ); +#endif } +#ifdef MAPBASE + else if ( CNPC_Combine *pCombine = dynamic_cast(GetOuter()) ) + { + pCombine->SpeakIfAllowed( pSentence, nSoundPriority, nCriteria ); + } + else if ( GetOuter()->GetExpresser() ) + { + GetOuter()->GetExpresser()->Speak( pSentence ); + } +#endif } +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, const char *modifiers, SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria ) +{ + // If we're a cop, turn the baton on + CNPC_MetroPolice *pCop = dynamic_cast(GetOuter()); + + if ( pCop != NULL ) + { + pCop->SpeakIfAllowed( pSentence, modifiers, nSoundPriority, nCriteria ); + } +#ifdef MAPBASE + else if ( CNPC_Combine *pCombine = dynamic_cast(GetOuter()) ) + { + pCombine->SpeakIfAllowed( pSentence, modifiers, nSoundPriority, nCriteria ); + } + else if ( GetOuter()->GetExpresser() ) + { + GetOuter()->GetExpresser()->Speak( pSentence, modifiers ); + } +#endif +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -175,6 +218,10 @@ void CAI_PolicingBehavior::GatherConditions( void ) // See if we need to knock out our target immediately if ( ShouldKnockOutTarget( pTarget ) ) { +#ifdef MAPBASE + // If this isn't actually an enemy of ours and we're already warning, don't set this condition + if (GetOuter()->IRelationType( m_hPoliceGoal->GetTarget() ) <= D_FR || !IsCurSchedule(SCHED_POLICE_WARN_TARGET, false)) +#endif SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ); } @@ -189,6 +236,10 @@ void CAI_PolicingBehavior::GatherConditions( void ) if ( flDistSqr < (m_hPoliceGoal->GetRadius()*m_hPoliceGoal->GetRadius()) ) { +#ifdef MAPBASE + // If this isn't actually an enemy of ours and we're already warning, don't set this condition + if (GetOuter()->IRelationType( m_hPoliceGoal->GetTarget() ) <= D_FR || !IsCurSchedule(SCHED_POLICE_WARN_TARGET, false)) +#endif SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ); } } @@ -214,6 +265,9 @@ void CAI_PolicingBehavior::AnnouncePolicing( void ) "METROPOLICE_MOVE_ALONG_C", }; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + HostSpeakSentence(TLK_COP_MOVE_ALONG, UTIL_VarArgs("numwarnings:%i", m_nNumWarnings), SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL); +#else if ( m_nNumWarnings <= 3 ) { HostSpeakSentence( pWarnings[ m_nNumWarnings - 1 ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); @@ -226,6 +280,7 @@ void CAI_PolicingBehavior::AnnouncePolicing( void ) int iSentence = RandomInt( 0, 1 ); HostSpeakSentence( pWarnings[ iSentence ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); } +#endif } //----------------------------------------------------------------------------- @@ -239,6 +294,12 @@ int CAI_PolicingBehavior::TranslateSchedule( int scheduleType ) { if ( m_hPoliceGoal->ShouldRemainAtPost() && !MaintainGoalPosition() ) return BaseClass::TranslateSchedule( SCHED_COMBAT_FACE ); + +#ifdef MAPBASE + // If this isn't actually an enemy of ours, keep warning + if ( GetOuter()->IRelationType(m_hPoliceGoal->GetTarget()) > D_FR ) + return BaseClass::TranslateSchedule( SCHED_POLICE_WARN_TARGET ); +#endif } return BaseClass::TranslateSchedule( scheduleType ); @@ -335,7 +396,11 @@ void CAI_PolicingBehavior::StartTask( const Task_t *pTask ) if ( GetNavigator()->SetGoal( harassPos, pTask->flTaskData ) ) { +#ifdef MAPBASE + GetNavigator()->SetMovementActivity( GetOuter()->TranslateActivity(ACT_WALK_ANGRY) ); +#else GetNavigator()->SetMovementActivity( (Activity) ACT_WALK_ANGRY ); +#endif GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetTarget() ); TaskComplete(); } diff --git a/mp/src/game/server/hl2/ai_behavior_police.h b/mp/src/game/server/hl2/ai_behavior_police.h index 84f132be..ed1f58da 100644 --- a/mp/src/game/server/hl2/ai_behavior_police.h +++ b/mp/src/game/server/hl2/ai_behavior_police.h @@ -68,6 +68,9 @@ public: private: void HostSpeakSentence( const char *pSentence, SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria ); +#ifdef MAPBASE + void HostSpeakSentence( const char *pSentence, const char *modifiers, SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria ); +#endif int TranslateSchedule( int scheduleType ); diff --git a/mp/src/game/server/hl2/cbasehelicopter.cpp b/mp/src/game/server/hl2/cbasehelicopter.cpp index 1243fe18..2b46bce6 100644 --- a/mp/src/game/server/hl2/cbasehelicopter.cpp +++ b/mp/src/game/server/hl2/cbasehelicopter.cpp @@ -101,6 +101,10 @@ BEGIN_DATADESC( CBaseHelicopter ) DEFINE_FIELD( m_bSuppressSound, FIELD_BOOLEAN ), DEFINE_FIELD( m_flStartupTime, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bAllowAnyDamage, FIELD_BOOLEAN, "AllowAnyDamage" ), +#endif + DEFINE_FIELD( m_cullBoxMins, FIELD_VECTOR ), DEFINE_FIELD( m_cullBoxMaxs, FIELD_VECTOR ), @@ -193,8 +197,15 @@ void CBaseHelicopter::Spawn( void ) AddFlag( FL_NPC ); +#ifdef MAPBASE + if (m_flMaxSpeed == 0) + m_flMaxSpeed = BASECHOPPER_MAX_SPEED; + if (m_flMaxSpeedFiring == 0) + m_flMaxSpeedFiring = BASECHOPPER_MAX_FIRING_SPEED; +#else m_flMaxSpeed = BASECHOPPER_MAX_SPEED; m_flMaxSpeedFiring = BASECHOPPER_MAX_FIRING_SPEED; +#endif m_takedamage = DAMAGE_AIM; // Don't start up if the level designer has asked the @@ -1110,6 +1121,11 @@ void CBaseHelicopter::InputDisableRotorSound( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CBaseHelicopter::InputKill( inputdata_t &inputdata ) { +#ifdef MAPBASE + // Finally, an InputKill override that makes sense. + m_OnKilled.FireOutput( inputdata.pActivator, this ); +#endif + StopRotorWash(); m_bSuppressSound = true; @@ -1269,7 +1285,11 @@ void CBaseHelicopter::TraceAttack( const CTakeDamageInfo &info, const Vector &ve // Take no damage from trace attacks unless it's blast damage. RadiusDamage() sometimes calls // TraceAttack() as a means for delivering blast damage. Usually when the explosive penetrates // the target. (RPG missiles do this sometimes). +#ifdef MAPBASE + if ( info.GetDamageType() & (DMG_BLAST|DMG_AIRBOAT) || m_bAllowAnyDamage ) +#else if( info.GetDamageType() & (DMG_BLAST|DMG_AIRBOAT) ) +#endif { BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); } @@ -1520,9 +1540,23 @@ bool CBaseHelicopter::ChooseEnemy( void ) // New enemy! Clear the timers and set conditions. SetEnemy( pNewEnemy ); m_flLastSeen = m_flPrevSeen = gpGlobals->curtime; +#ifdef MAPBASE + Remember( ( pNewEnemy->IsPlayer() ) ? bits_MEMORY_HAD_PLAYER : bits_MEMORY_HAD_ENEMY ); +#endif } else { +#ifdef MAPBASE + if (!pNewEnemy) + { + if ( HasMemory( bits_MEMORY_HAD_PLAYER ) ) + { + m_OnLostPlayer.FireOutput( GetEnemy(), this ); + } + m_OnLostEnemy.FireOutput( GetEnemy(), this ); + } +#endif + SetEnemy( NULL ); SetState( NPC_STATE_ALERT ); } @@ -1542,6 +1576,45 @@ bool CBaseHelicopter::ChooseEnemy( void ) //----------------------------------------------------------------------------- void CBaseHelicopter::GatherEnemyConditions( CBaseEntity *pEnemy ) { +#ifdef MAPBASE + // --------------------------- + // Ported from CAI_BaseNPC. Need it to fire outputs without setting all kinds of conditions + // --------------------------- + if ( HasCondition( COND_NEW_ENEMY ) || GetSenses()->GetTimeLastUpdate( GetEnemy() ) == gpGlobals->curtime ) + { + bool bSensesDidSee = GetSenses()->DidSeeEntity( pEnemy ); + + if ( !bSensesDidSee && ( ( EnemyDistance( pEnemy ) >= GetSenses()->GetDistLook() ) || !FVisible( pEnemy, MASK_BLOCKLOS ) ) ) + { + // No LOS to enemy + if (HasMemory( bits_MEMORY_HAD_LOS )) + { + // Send output event + if (GetEnemy()->IsPlayer()) + { + m_OnLostPlayerLOS.FireOutput( GetEnemy(), this ); + } + m_OnLostEnemyLOS.FireOutput( GetEnemy(), this ); + } + Forget( bits_MEMORY_HAD_LOS ); + } + else + { + if (!HasMemory( bits_MEMORY_HAD_LOS )) + { + // Send output event + EHANDLE hEnemy; + hEnemy.Set( GetEnemy() ); + + if (GetEnemy()->IsPlayer()) + m_OnFoundPlayer.Set(hEnemy, hEnemy, this); + m_OnFoundEnemy.Set(hEnemy, hEnemy, this); + } + Remember( bits_MEMORY_HAD_LOS ); + } + } +#endif + // ------------------- // If enemy is dead // ------------------- @@ -1595,3 +1668,103 @@ void ExpandBBox(Vector &vecMins, Vector &vecMaxs) vecMins.Init(-maxval, -maxval, -maxval); vecMaxs.Init(maxval, maxval, maxval); } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// A custom helicopter +//----------------------------------------------------------------------------- +class CNPC_CustomHelicopter : public CBaseHelicopter +{ +public: + DECLARE_CLASS( CNPC_CustomHelicopter, CBaseHelicopter ); + DECLARE_DATADESC(); + + CNPC_CustomHelicopter(); + ~CNPC_CustomHelicopter(); + + virtual void Precache( void ); + virtual void Spawn( void ); + + void InitializeRotorSound( void ); + + float GetAcceleration( void ) { return m_flAcceleration; } + + float m_flAcceleration; + + string_t m_iszRotorSound; + string_t m_iszRotorBlast; +}; + +LINK_ENTITY_TO_CLASS( npc_helicopter_custom, CNPC_CustomHelicopter ); + +BEGIN_DATADESC( CNPC_CustomHelicopter ) + + DEFINE_KEYFIELD( m_flMaxSpeed, FIELD_FLOAT, "MaxSpeed" ), + DEFINE_KEYFIELD( m_flMaxSpeedFiring, FIELD_FLOAT, "MaxSpeedfiring" ), + + DEFINE_KEYFIELD( m_flAcceleration, FIELD_FLOAT, "Acceleration" ), + + DEFINE_KEYFIELD( m_iszRotorSound, FIELD_STRING, "RotorSound" ), + DEFINE_KEYFIELD( m_iszRotorBlast, FIELD_STRING, "RotorBlast" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNPC_CustomHelicopter::CNPC_CustomHelicopter() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNPC_CustomHelicopter::~CNPC_CustomHelicopter() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CustomHelicopter::Precache( void ) +{ + PrecacheModel( STRING(GetModelName()) ); + + PrecacheScriptSound( STRING(m_iszRotorSound) ); + PrecacheScriptSound( STRING(m_iszRotorBlast) ); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CustomHelicopter::Spawn( void ) +{ + SetModel( STRING(GetModelName()) ); + + BaseClass::Spawn(); +} + +//------------------------------------------------------------------------------ +// Purpose: Create our rotor sound +//------------------------------------------------------------------------------ +void CNPC_CustomHelicopter::InitializeRotorSound( void ) +{ + if ( !m_pRotorSound ) + { + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + CPASAttenuationFilter filter( this ); + + m_pRotorSound = controller.SoundCreate( filter, entindex(), STRING(m_iszRotorSound) ); + m_pRotorBlast = controller.SoundCreate( filter, entindex(), STRING(m_iszRotorBlast) ); + } + else + { + Assert(m_pRotorSound); + Assert(m_pRotorBlast); + } + + BaseClass::InitializeRotorSound(); +} +#endif diff --git a/mp/src/game/server/hl2/cbasehelicopter.h b/mp/src/game/server/hl2/cbasehelicopter.h index 4ca3b091..fcf74291 100644 --- a/mp/src/game/server/hl2/cbasehelicopter.h +++ b/mp/src/game/server/hl2/cbasehelicopter.h @@ -215,6 +215,10 @@ protected: EHANDLE m_hRotorWash; // Attached rotorwash entity +#ifdef MAPBASE + bool m_bAllowAnyDamage; +#endif + // Inputs void InputActivate( inputdata_t &inputdata ); diff --git a/mp/src/game/server/hl2/combine_mine.cpp b/mp/src/game/server/hl2/combine_mine.cpp index 4eb7283f..f813abc9 100644 --- a/mp/src/game/server/hl2/combine_mine.cpp +++ b/mp/src/game/server/hl2/combine_mine.cpp @@ -87,9 +87,18 @@ BEGIN_DATADESC( CBounceBomb ) DEFINE_FIELD( m_bFoeNearest, FIELD_BOOLEAN ), DEFINE_FIELD( m_flIgnoreWorldTime, FIELD_TIME ), DEFINE_KEYFIELD( m_bDisarmed, FIELD_BOOLEAN, "StartDisarmed" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iInitialState, FIELD_INTEGER, "InitialState" ), + DEFINE_KEYFIELD( m_bCheapWarnSound, FIELD_BOOLEAN, "CheapWarnSound" ), + DEFINE_KEYFIELD( m_iLOSMask, FIELD_INTEGER, "LOSMask" ), +#endif DEFINE_KEYFIELD( m_iModification, FIELD_INTEGER, "Modification" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bPlacedByPlayer, FIELD_BOOLEAN, "Friendly" ), +#else DEFINE_FIELD( m_bPlacedByPlayer, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_bHeldByPhysgun, FIELD_BOOLEAN ), DEFINE_FIELD( m_iFlipAttempts, FIELD_INTEGER ), @@ -97,6 +106,16 @@ BEGIN_DATADESC( CBounceBomb ) DEFINE_FIELD( m_flTimeGrabbed, FIELD_TIME ), DEFINE_FIELD( m_iMineState, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bFilterExclusive, FIELD_BOOLEAN, "FilterExclusive" ), + DEFINE_KEYFIELD( m_iszEnemyFilter, FIELD_STRING, "enemyfilter" ), + DEFINE_FIELD( m_hEnemyFilter, FIELD_EHANDLE ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetEnemyFilter", InputSetEnemyFilter ), + DEFINE_KEYFIELD( m_iszFriendFilter, FIELD_STRING, "friendfilter" ), + DEFINE_FIELD( m_hFriendFilter, FIELD_EHANDLE ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFriendFilter", InputSetFriendFilter ), +#endif + // Physics Influence DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ), DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ), @@ -106,6 +125,14 @@ BEGIN_DATADESC( CBounceBomb ) DEFINE_OUTPUT( m_OnPulledUp, "OnPulledUp" ), DEFINE_INPUTFUNC( FIELD_VOID, "Disarm", InputDisarm ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "Bounce", InputBounce ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "BounceAtTarget", InputBounceAtTarget ), + + DEFINE_OUTPUT( m_OnTriggered, "OnTriggered" ), + DEFINE_OUTPUT( m_OnExplode, "OnExplode" ), +#endif + END_DATADESC() string_t CBounceBomb::gm_iszFloorTurretClassname; @@ -130,6 +157,13 @@ void CBounceBomb::Precache() gm_iszFloorTurretClassname = AllocPooledString( "npc_turret_floor" ); gm_iszGroundTurretClassname = AllocPooledString( "npc_turret_ground" ); + +#ifdef MAPBASE + if (m_iszEnemyFilter != NULL_STRING) + m_hEnemyFilter = dynamic_cast(gEntList.FindEntityByName(NULL, STRING(m_iszEnemyFilter), this)); + if (m_iszFriendFilter != NULL_STRING) + m_hFriendFilter = dynamic_cast(gEntList.FindEntityByName( NULL, STRING(m_iszFriendFilter), this )); +#endif } //--------------------------------------------------------- @@ -178,10 +212,23 @@ void CBounceBomb::Spawn() { SetMineState( MINE_STATE_DORMANT ); } +#ifdef MAPBASE + else + { + // NOTE: MINE_STATE_DEPLOY and MINE_STATE_DORMANT are swapped in this case! + if (m_iInitialState == 0) + SetMineState( MINE_STATE_DEPLOY ); + else if (m_iInitialState == 1) + SetMineState( MINE_STATE_DORMANT ); + else + SetMineState( m_iInitialState ); + } +#else else { SetMineState( MINE_STATE_DEPLOY ); } +#endif // default to a different skin for cavern turrets (unless explicitly overridden) if ( m_iModification == MINE_MODIFICATION_CAVERN ) @@ -261,8 +308,12 @@ void CBounceBomb::SetMineState( int iState ) { case MINE_STATE_DORMANT: { +#ifdef MAPBASE + SilenceWarnSound( 0.1 ); +#else CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); controller.SoundChangeVolume( m_pWarnSound, 0.0, 0.1 ); +#endif UpdateLight( false, 0, 0, 0, 0 ); SetThink( NULL ); } @@ -270,8 +321,12 @@ void CBounceBomb::SetMineState( int iState ) case MINE_STATE_CAPTIVE: { +#ifdef MAPBASE + SilenceWarnSound( 0.2 ); +#else CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); controller.SoundChangeVolume( m_pWarnSound, 0.0, 0.2 ); +#endif // Unhook unsigned int flags = VPhysicsGetObject()->GetCallbackFlags(); @@ -314,8 +369,12 @@ void CBounceBomb::SetMineState( int iState ) // Scare NPC's CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), 300, 1.0f, this ); +#ifdef MAPBASE + SilenceWarnSound( 0.2 ); +#else CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); controller.SoundChangeVolume( m_pWarnSound, 0.0, 0.2 ); +#endif SetTouch( &CBounceBomb::ExplodeTouch ); unsigned int flags = VPhysicsGetObject()->GetCallbackFlags(); @@ -352,7 +411,11 @@ void CBounceBomb::SetMineState( int iState ) else { SetThink( &CBounceBomb::BounceThink ); +#ifdef MAPBASE + SetNextThink( gpGlobals->curtime + m_flExplosionDelay ); +#else SetNextThink( gpGlobals->curtime + 0.5 ); +#endif } } break; @@ -773,7 +836,11 @@ void CBounceBomb::Wake( bool bAwake ) CReliableBroadcastRecipientFilter filter; +#ifdef MAPBASE + if( !m_pWarnSound && !m_bCheapWarnSound ) +#else if( !m_pWarnSound ) +#endif { m_pWarnSound = controller.SoundCreate( filter, entindex(), "NPC_CombineMine.ActiveLoop" ); controller.Play( m_pWarnSound, 1.0, PITCH_NORM ); @@ -785,7 +852,11 @@ void CBounceBomb::Wake( bool bAwake ) if( m_bFoeNearest ) { EmitSound( "NPC_CombineMine.TurnOn" ); +#ifdef MAPBASE + UpdateWarnSound( 1.0, 0.1 ); +#else controller.SoundChangeVolume( m_pWarnSound, 1.0, 0.1 ); +#endif } unsigned char r, g, b; @@ -811,7 +882,11 @@ void CBounceBomb::Wake( bool bAwake ) } SetNearestNPC( NULL ); +#ifdef MAPBASE + SilenceWarnSound( 0.1 ); +#else controller.SoundChangeVolume( m_pWarnSound, 0.0, 0.1 ); +#endif UpdateLight( false, 0, 0, 0, 0 ); } @@ -845,6 +920,27 @@ float CBounceBomb::FindNearestNPC() if( pNPC->EyePosition().z < GetAbsOrigin().z ) continue; +#ifdef MAPBASE + bool bPassesFilter = false; + if (m_hEnemyFilter || m_hFriendFilter) + { + // If we have an enemy or friend filter, always accept those who pass it + // If we're only supposed to be using filters, only find entities that pass one of them + + if (m_hEnemyFilter && m_hEnemyFilter->PassesFilter( this, pNPC )) + bPassesFilter = true; + + else if (m_hFriendFilter && m_hFriendFilter->PassesFilter( this, pNPC )) + bPassesFilter = true; + + if (m_bFilterExclusive && !bPassesFilter) + continue; + } + + if (!bPassesFilter) + { +#endif + // Disregard things that want to be disregarded if( pNPC->Classify() == CLASS_NONE ) continue; @@ -857,13 +953,21 @@ float CBounceBomb::FindNearestNPC() if( pNPC->m_iClassname == gm_iszFloorTurretClassname || pNPC->m_iClassname == gm_iszGroundTurretClassname ) continue; +#ifdef MAPBASE + } +#endif + float flDist = (GetAbsOrigin() - pNPC->GetAbsOrigin()).LengthSqr(); if( flDist < flNearest ) { // Now do a visibility test. +#ifdef MAPBASE + if( FVisible( pNPC, m_iLOSMask ) ) +#else if( FVisible( pNPC, MASK_SOLID_BRUSHONLY ) ) +#endif { flNearest = flDist; SetNearestNPC( pNPC ); @@ -872,19 +976,68 @@ float CBounceBomb::FindNearestNPC() } } +#ifdef MAPBASE_MP + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) ) + { + bool bPassesFilter = true; + if ((m_hEnemyFilter || m_hFriendFilter) && m_bFilterExclusive) + { + // If we have an enemy or friend filter, and that's all we're supposed to be using, + // don't accept the player if they don't pass our filters + + if (m_hEnemyFilter && !m_hEnemyFilter->PassesFilter( this, pPlayer )) + bPassesFilter = false; + + else if (m_hFriendFilter && !m_hFriendFilter->PassesFilter( this, pPlayer )) + bPassesFilter = false; + } + + float flDist = (pPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); + + if( flDist < flNearest && FVisible( pPlayer, m_iLOSMask ) ) + { + flNearest = flDist; + SetNearestNPC( pPlayer ); + } + } + } +#else // finally, check the player. CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); if( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) ) { +#ifdef MAPBASE + bool bPassesFilter = true; + if ((m_hEnemyFilter || m_hFriendFilter) && m_bFilterExclusive) + { + // If we have an enemy or friend filter, and that's all we're supposed to be using, + // don't accept the player if they don't pass our filters + + if (m_hEnemyFilter && !m_hEnemyFilter->PassesFilter( this, pPlayer )) + bPassesFilter = false; + + else if (m_hFriendFilter && !m_hFriendFilter->PassesFilter( this, pPlayer )) + bPassesFilter = false; + } +#endif + float flDist = (pPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); +#ifdef MAPBASE + if( flDist < flNearest && FVisible( pPlayer, m_iLOSMask ) && bPassesFilter ) +#else if( flDist < flNearest && FVisible( pPlayer, MASK_SOLID_BRUSHONLY ) ) +#endif { flNearest = flDist; SetNearestNPC( pPlayer ); } } +#endif if( m_hNearestNPC.Get() ) { @@ -894,8 +1047,9 @@ float CBounceBomb::FindNearestNPC() if( m_bFoeNearest ) { // Changing state to where a friend is nearest. - +#ifndef MAPBASE if( IsFriend( m_hNearestNPC ) ) +#endif { // Friend UpdateLight( true, 0, 255, 0, 190 ); @@ -921,6 +1075,14 @@ float CBounceBomb::FindNearestNPC() //--------------------------------------------------------- bool CBounceBomb::IsFriend( CBaseEntity *pEntity ) { +#ifdef MAPBASE + if (m_hFriendFilter && m_hFriendFilter->PassesFilter(this, pEntity)) + return true; + + if (m_hEnemyFilter && m_hEnemyFilter->PassesFilter(this, pEntity)) + return false; +#endif + int classify = pEntity->Classify(); bool bIsCombine = false; @@ -930,10 +1092,16 @@ bool CBounceBomb::IsFriend( CBaseEntity *pEntity ) return false; } - if( classify == CLASS_METROPOLICE || + if( classify == CLASS_METROPOLICE || classify == CLASS_COMBINE || classify == CLASS_MILITARY || classify == CLASS_COMBINE_HUNTER || +#ifdef MAPBASE + classify == CLASS_MANHACK || + classify == CLASS_STALKER || + classify == CLASS_PROTOSNIPER || + classify == CLASS_COMBINE_GUNSHIP || +#endif classify == CLASS_SCANNER ) { bIsCombine = true; @@ -976,7 +1144,12 @@ void CBounceBomb::SearchThink() if( m_pConstraint && gpGlobals->curtime - m_flTimeGrabbed >= 1.0f ) { +#ifdef MAPBASE + // We don't already store our holder for some reason + m_OnPulledUp.FireOutput( UTIL_GetLocalPlayer(), this ); +#else m_OnPulledUp.FireOutput( this, this ); +#endif SetMineState( MINE_STATE_CAPTIVE ); return; } @@ -1002,6 +1175,9 @@ void CBounceBomb::SearchThink() if( flNearestNPCDist <= BOUNCEBOMB_DETONATE_RADIUS && !IsFriend( m_hNearestNPC ) ) { +#ifdef MAPBASE + m_OnTriggered.FireOutput( m_hNearestNPC, this ); +#endif if( m_bBounce ) { SetMineState( MINE_STATE_TRIGGERED ); @@ -1087,6 +1263,11 @@ void CBounceBomb::ExplodeThink() { ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), (pThrower) ? pThrower : this, BOUNCEBOMB_EXPLODE_DAMAGE, BOUNCEBOMB_EXPLODE_RADIUS, true); } + +#ifdef MAPBASE + m_OnExplode.FireOutput( m_hNearestNPC, this ); +#endif + UTIL_Remove( this ); } @@ -1145,6 +1326,82 @@ void CBounceBomb::CloseHooks() #endif } +#ifdef MAPBASE +extern int g_interactionBarnacleVictimBite; +extern int g_interactionBarnacleVictimFinalBite; +extern int ACT_BARNACLE_BITE_SMALL_THINGS; +//----------------------------------------------------------------------------- +// Purpose: Uses the new CBaseEntity interaction implementation and +// replaces the dynamic_casting from npc_barnacle +// Input : The type of interaction, extra info pointer, and who started it +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CBounceBomb::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ) +{ + // This was originally done in npc_barnacle itself, but + // we've transitioned to interactions so we could extend special behavior to others + // without just adding more casting. + if ( interactionType == g_interactionBarnacleVictimBite ) + { + Assert( sourceEnt && sourceEnt->IsNPC() ); + sourceEnt->MyNPCPointer()->SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS ); + return true; + } + else if ( interactionType == g_interactionBarnacleVictimFinalBite ) + { + ExplodeThink(); + return true; + } + + return BaseClass::HandleInteraction(interactionType, data, sourceEnt); +} + +//----------------------------------------------------------------------------- +void CBounceBomb::UpdateWarnSound( float flVolume, float flDelta ) +{ + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + if (m_bCheapWarnSound && !m_pWarnSound) + { + CReliableBroadcastRecipientFilter filter; + //m_pWarnSound = controller.SoundCreate( filter, entindex(), "NPC_CombineMine.ActiveLoop" ); + //controller.Play( m_pWarnSound, flVolume, PITCH_NORM ); + + EmitSound_t params; + params.m_pSoundName = "NPC_CombineMine.ActiveLoop"; + params.m_flVolume = flVolume; + params.m_nPitch = PITCH_NORM; + + EmitSound( filter, entindex(), params ); + } + else + { + controller.SoundChangeVolume( m_pWarnSound, flVolume, flDelta ); + } +} + +void CBounceBomb::SilenceWarnSound( float flDelta ) +{ + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + if (m_bCheapWarnSound) + { + //if ( m_pWarnSound ) + //{ + // controller.SoundDestroy( m_pWarnSound ); + //} + + StopSound( "NPC_CombineMine.ActiveLoop" ); + } + else + { + if ( m_pWarnSound ) + { + controller.SoundChangeVolume( m_pWarnSound, 0.0, flDelta ); + } + } +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::InputDisarm( inputdata_t &inputdata ) @@ -1165,6 +1422,40 @@ void CBounceBomb::InputDisarm( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBounceBomb::InputSetEnemyFilter( inputdata_t &inputdata ) +{ + m_iszEnemyFilter = inputdata.value.StringID(); + m_hEnemyFilter = dynamic_cast(gEntList.FindEntityByName( NULL, STRING(m_iszEnemyFilter), this )); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBounceBomb::InputSetFriendFilter( inputdata_t &inputdata ) +{ + m_iszFriendFilter = inputdata.value.StringID(); + m_hFriendFilter = dynamic_cast(gEntList.FindEntityByName( NULL, STRING(m_iszFriendFilter), this )); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CBounceBomb::InputBounce( inputdata_t &inputdata ) +{ + m_hNearestNPC = NULL; + SetMineState(MINE_STATE_TRIGGERED); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CBounceBomb::InputBounceAtTarget( inputdata_t &inputdata ) +{ + m_hNearestNPC = inputdata.value.Entity(); + SetMineState(MINE_STATE_TRIGGERED); +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason ) diff --git a/mp/src/game/server/hl2/combine_mine.h b/mp/src/game/server/hl2/combine_mine.h index 48dbb468..fdcd68da 100644 --- a/mp/src/game/server/hl2/combine_mine.h +++ b/mp/src/game/server/hl2/combine_mine.h @@ -24,13 +24,20 @@ class CSoundPatch; #define BOUNCEBOMB_EXPLODE_RADIUS 125.0 #define BOUNCEBOMB_EXPLODE_DAMAGE 150.0 #include "player_pickup.h" +#ifdef MAPBASE +#include "filters.h" +#endif class CBounceBomb : public CBaseAnimating, public CDefaultPlayerPickupVPhysics { DECLARE_CLASS( CBounceBomb, CBaseAnimating ); public: +#ifdef MAPBASE + CBounceBomb() { m_pWarnSound = NULL; m_bPlacedByPlayer = false; m_flExplosionDelay = 0.5f; m_iLOSMask = MASK_SOLID_BRUSHONLY; } +#else CBounceBomb() { m_pWarnSound = NULL; m_bPlacedByPlayer = false; } +#endif void Precache(); void Spawn(); void OnRestore(); @@ -76,6 +83,14 @@ public: void OpenHooks( bool bSilent = false ); void CloseHooks(); +#ifdef MAPBASE + // Uses the new CBaseEntity interaction implementation and replaces the dynamic_casting from npc_barnacle + bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ); + + void UpdateWarnSound( float flVolume, float flDelta ); + void SilenceWarnSound( float flDelta ); +#endif + DECLARE_DATADESC(); static string_t gm_iszFloorTurretClassname; @@ -104,6 +119,13 @@ private: float m_flIgnoreWorldTime; bool m_bDisarmed; +#ifdef MAPBASE + int m_iInitialState; + bool m_bCheapWarnSound; + + // Allows control over the mask used in LOS + int m_iLOSMask; +#endif bool m_bPlacedByPlayer; @@ -119,8 +141,27 @@ private: IPhysicsConstraint *m_pConstraint; int m_iMineState; +#ifdef MAPBASE + // Makes the filters the exclusive factor in determining friend/foe + bool m_bFilterExclusive; + + string_t m_iszEnemyFilter; + CHandle m_hEnemyFilter; + void InputSetEnemyFilter( inputdata_t &inputdata ); + + string_t m_iszFriendFilter; + CHandle m_hFriendFilter; + void InputSetFriendFilter( inputdata_t &inputdata ); +#endif + COutputEvent m_OnPulledUp; void InputDisarm( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputBounce( inputdata_t &inputdata ); + void InputBounceAtTarget( inputdata_t &inputdata ); + COutputEvent m_OnTriggered; + COutputEvent m_OnExplode; +#endif }; diff --git a/mp/src/game/server/hl2/env_headcrabcanister.cpp b/mp/src/game/server/hl2/env_headcrabcanister.cpp index ed1883d1..e94fe3f1 100644 --- a/mp/src/game/server/hl2/env_headcrabcanister.cpp +++ b/mp/src/game/server/hl2/env_headcrabcanister.cpp @@ -95,6 +95,9 @@ private: void InputOpenCanister( inputdata_t &inputdata ); void InputSpawnHeadcrabs( inputdata_t &inputdata ); void InputStopSmoke( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputStopHissing( inputdata_t &inputdata ); +#endif // Think(s) void HeadcrabCanisterSkyboxThink( void ); @@ -152,6 +155,9 @@ private: COutputEHANDLE m_OnLaunched; COutputEvent m_OnImpacted; COutputEvent m_OnOpened; +#ifdef MAPBASE + COutputEHANDLE m_OnCrab; +#endif // Only for skybox only cannisters. float m_flMinRefireTime; @@ -201,11 +207,17 @@ BEGIN_DATADESC( CEnvHeadcrabCanister ) DEFINE_INPUTFUNC( FIELD_VOID, "OpenCanister", InputOpenCanister ), DEFINE_INPUTFUNC( FIELD_VOID, "SpawnHeadcrabs", InputSpawnHeadcrabs ), DEFINE_INPUTFUNC( FIELD_VOID, "StopSmoke", InputStopSmoke ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "StopHissing", InputStopHissing ), +#endif // Outputs DEFINE_OUTPUT( m_OnLaunched, "OnLaunched" ), DEFINE_OUTPUT( m_OnImpacted, "OnImpacted" ), DEFINE_OUTPUT( m_OnOpened, "OnOpened" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnCrab, "OnCrab" ), +#endif END_DATADESC() @@ -545,6 +557,17 @@ void CEnvHeadcrabCanister::InputStopSmoke( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvHeadcrabCanister::InputStopHissing( inputdata_t &inputdata ) +{ + StopSound( "HeadcrabCanister.AfterLanding" ); +} +#endif + //============================================================================= // // Enumerator for swept bbox collision. @@ -725,6 +748,10 @@ void CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink() pHeadCrab->SetLocalOrigin( vec3_origin ); pHeadCrab->SetLocalAngles( vec3_angle ); pHeadCrab->CrawlFromCanister(); + +#ifdef MAPBASE + m_OnCrab.Set(pHeadCrab, pHeadCrab, this); +#endif } if ( m_nHeadcrabCount != 0 ) diff --git a/mp/src/game/server/hl2/env_speaker.cpp b/mp/src/game/server/hl2/env_speaker.cpp index dd141eca..872dbcbc 100644 --- a/mp/src/game/server/hl2/env_speaker.cpp +++ b/mp/src/game/server/hl2/env_speaker.cpp @@ -21,6 +21,9 @@ #include "ndebugoverlay.h" #include "soundscape.h" #include "AI_ResponseSystem.h" +#ifdef MAPBASE +#include "sceneentity.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -35,6 +38,10 @@ LINK_ENTITY_TO_CLASS( env_speaker, CSpeaker ); BEGIN_DATADESC( CSpeaker ) +#ifdef MAPBASE + DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ), +#endif + DEFINE_KEYFIELD( m_delayMin, FIELD_FLOAT, "delaymin" ), DEFINE_KEYFIELD( m_delayMax, FIELD_FLOAT, "delaymax" ), DEFINE_KEYFIELD( m_iszRuleScriptFile, FIELD_STRING, "rulescript" ), @@ -50,6 +57,10 @@ BEGIN_DATADESC( CSpeaker ) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnSpeak, "OnSpeak" ), +#endif + END_DATADESC() @@ -181,6 +192,100 @@ void CSpeaker::SpeakerThink( void ) g_AIFoesTalkSemaphore.Acquire( 5, this ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline CBaseEntity *CSpeaker::GetTarget() +{ + if (!m_hTarget && m_target != NULL_STRING) + m_hTarget = gEntList.FindEntityByName(NULL, STRING(m_target), this, NULL, this); + return m_hTarget; +} + +//----------------------------------------------------------------------------- +// Purpose: Copied from CBaseEntity so we could use a !target for everything +// Input : *conceptName - +//----------------------------------------------------------------------------- +void CSpeaker::DispatchResponse( const char *conceptName ) +{ + IResponseSystem *rs = GetResponseSystem(); + if ( !rs ) + return; + + CBaseEntity *pTarget = GetTarget(); + if (!pTarget) + pTarget = this; + + // See CBaseEntity and stuff... + AI_CriteriaSet set; + set.AppendCriteria( "concept", conceptName, CONCEPT_WEIGHT ); + ModifyOrAppendCriteria( set ); + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if( pPlayer ) + pPlayer->ModifyOrAppendPlayerCriteria( set ); + ReAppendContextCriteria( set ); + AI_Response result; + bool found = rs->FindBestResponse( set, result ); + if ( !found ) + { + return; + } + + // Handle the response here... + const char *response = result.GetResponsePtr(); + if (response[0] == '$') + { + response = GetContextValue( response ); + PrecacheScriptSound( response ); + } + + switch ( result.GetType() ) + { + case RESPONSE_SPEAK: + { + pTarget->EmitSound( response ); + } + break; + case RESPONSE_SENTENCE: + { + int sentenceIndex = SENTENCEG_Lookup( response ); + if( sentenceIndex == -1 ) + { + // sentence not found + break; + } + + // FIXME: Get pitch from npc? + CPASAttenuationFilter filter( pTarget ); + CBaseEntity::EmitSentenceByIndex( filter, pTarget->entindex(), CHAN_VOICE, sentenceIndex, 1, result.GetSoundLevel(), 0, PITCH_NORM ); + } + break; + case RESPONSE_SCENE: + { + CBaseFlex *pFlex = NULL; + if (pTarget != this) + { + // Attempt to get flex on the target + pFlex = dynamic_cast(pTarget); + } + InstancedScriptedScene(pFlex, response); + } + break; + case RESPONSE_PRINT: + { + + } + break; + default: + break; + } + + // AllocPooledString? + m_OnSpeak.Set(MAKE_STRING(response), pTarget, this); +} +#endif + void CSpeaker::InputTurnOn( inputdata_t &inputdata ) { diff --git a/mp/src/game/server/hl2/env_speaker.h b/mp/src/game/server/hl2/env_speaker.h index 20fbeb6b..a28a2a47 100644 --- a/mp/src/game/server/hl2/env_speaker.h +++ b/mp/src/game/server/hl2/env_speaker.h @@ -36,6 +36,13 @@ protected: void SpeakerThink( void ); +#ifdef MAPBASE + EHANDLE m_hTarget; + virtual void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); m_hTarget = NULL; } + CBaseEntity *GetTarget(); + virtual void DispatchResponse( const char *conceptName ); +#endif + void InputToggle( inputdata_t &inputdata ); float m_delayMin; @@ -49,6 +56,10 @@ public: void InputTurnOff( inputdata_t &inputdata ); void InputTurnOn( inputdata_t &inputdata ); + +#ifdef MAPBASE + COutputString m_OnSpeak; +#endif }; #endif // ENV_SPEAKER_H diff --git a/mp/src/game/server/hl2/func_recharge.cpp b/mp/src/game/server/hl2/func_recharge.cpp index 37c42637..c3bf74c1 100644 --- a/mp/src/game/server/hl2/func_recharge.cpp +++ b/mp/src/game/server/hl2/func_recharge.cpp @@ -35,6 +35,9 @@ public: DECLARE_CLASS( CRecharge, CBaseToggle ); void Spawn( ); +#ifdef MAPBASE + void Precache( void ); +#endif bool CreateVPhysics(); int DrawDebugTextOverlays(void); void Off(void); @@ -45,6 +48,10 @@ public: private: void InputRecharge( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetCharge( inputdata_t &inputdata ); + void InputSetChargeNoMax( inputdata_t &inputdata ); +#endif float MaxJuice() const; void UpdateJuice( int newJuice ); @@ -56,6 +63,10 @@ private: int m_iJuice; int m_iOn; // 0 = off, 1 = startup, 2 = going float m_flSoundTime; +#ifdef MAPBASE + int m_iMaxJuice; + int m_iIncrementValue; +#endif int m_nState; @@ -70,9 +81,16 @@ BEGIN_DATADESC( CRecharge ) DEFINE_FIELD( m_flNextCharge, FIELD_TIME ), DEFINE_FIELD( m_iReactivate, FIELD_INTEGER), - DEFINE_FIELD( m_iJuice, FIELD_INTEGER), +#ifdef MAPBASE + DEFINE_KEYFIELD(m_iJuice, FIELD_INTEGER, "Charge"), +#else + DEFINE_FIELD(m_iJuice, FIELD_INTEGER), +#endif DEFINE_FIELD( m_iOn, FIELD_INTEGER), DEFINE_FIELD( m_flSoundTime, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_INPUT( m_iIncrementValue, FIELD_INTEGER, "SetIncrementValue" ), +#endif DEFINE_FIELD( m_nState, FIELD_INTEGER ), // Function Pointers @@ -86,6 +104,10 @@ BEGIN_DATADESC( CRecharge ) DEFINE_OUTPUT(m_OnPlayerUse, "OnPlayerUse" ), DEFINE_INPUTFUNC( FIELD_VOID, "Recharge", InputRecharge ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCharge", InputSetCharge ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetChargeNoMax", InputSetChargeNoMax ), +#endif END_DATADESC() @@ -123,13 +145,30 @@ void CRecharge::Spawn() SetModel( STRING( GetModelName() ) ); +#ifdef MAPBASE + // In case the juice was overridden + if (m_iJuice == 0) + UpdateJuice( MaxJuice() ); + else if (m_iJuice == -1) + m_iJuice = 0; +#else UpdateJuice( MaxJuice() ); +#endif m_nState = 0; CreateVPhysics(); } +#ifdef MAPBASE +void CRecharge::Precache( void ) +{ + PrecacheScriptSound( "SuitRecharge.Deny" ); + PrecacheScriptSound( "SuitRecharge.Start" ); + PrecacheScriptSound( "SuitRecharge.ChargingLoop" ); +} +#endif + bool CRecharge::CreateVPhysics() { VPhysicsInitStatic(); @@ -156,6 +195,14 @@ int CRecharge::DrawDebugTextOverlays(void) //----------------------------------------------------------------------------- float CRecharge::MaxJuice() const { +#ifdef MAPBASE + if ( m_iMaxJuice != 0 ) + { + // It must've been overridden by the mapper + return m_iMaxJuice; + } +#endif + if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) ) { return sk_suitcharger_citadel.GetFloat(); @@ -199,6 +246,18 @@ void CRecharge::InputRecharge( inputdata_t &inputdata ) Recharge(); } +#ifdef MAPBASE +void CRecharge::InputSetCharge( inputdata_t &inputdata ) +{ + m_iMaxJuice = m_iJuice = inputdata.value.Int(); +} + +void CRecharge::InputSetChargeNoMax( inputdata_t &inputdata ) +{ + UpdateJuice(inputdata.value.Int()); +} +#endif + void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // if it's not a player, ignore @@ -287,6 +346,11 @@ void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE use } } +#ifdef MAPBASE + if (m_iIncrementValue != 0) + nIncrementArmor = m_iIncrementValue; +#endif + if (pl->ArmorValue() < nMaxArmor) { UpdateJuice( m_iJuice - nIncrementArmor ); @@ -350,6 +414,9 @@ public: private: void InputRecharge( inputdata_t &inputdata ); void InputSetCharge( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetChargeNoMax( inputdata_t &inputdata ); +#endif float MaxJuice() const; void UpdateJuice( int newJuice ); void Precache( void ); @@ -365,6 +432,9 @@ private: int m_nState; int m_iCaps; int m_iMaxJuice; +#ifdef MAPBASE + int m_iIncrementValue; +#endif COutputFloat m_OutRemainingCharge; COutputEvent m_OnHalfEmpty; @@ -380,12 +450,21 @@ BEGIN_DATADESC( CNewRecharge ) DEFINE_FIELD( m_flNextCharge, FIELD_TIME ), DEFINE_FIELD( m_iReactivate, FIELD_INTEGER), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iJuice, FIELD_INTEGER, "Charge" ), +#else DEFINE_FIELD( m_iJuice, FIELD_INTEGER), +#endif DEFINE_FIELD( m_iOn, FIELD_INTEGER), DEFINE_FIELD( m_flSoundTime, FIELD_TIME ), DEFINE_FIELD( m_nState, FIELD_INTEGER ), DEFINE_FIELD( m_iCaps, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iMaxJuice, FIELD_INTEGER, "MaxCharge" ), + DEFINE_INPUT( m_iIncrementValue, FIELD_INTEGER, "SetIncrementValue" ), +#else DEFINE_FIELD( m_iMaxJuice, FIELD_INTEGER ), +#endif // Function Pointers DEFINE_FUNCTION( Off ), @@ -400,6 +479,9 @@ BEGIN_DATADESC( CNewRecharge ) DEFINE_INPUTFUNC( FIELD_VOID, "Recharge", InputRecharge ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCharge", InputSetCharge ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetChargeNoMax", InputSetChargeNoMax ), +#endif END_DATADESC() @@ -412,6 +494,10 @@ LINK_ENTITY_TO_CLASS( item_suitcharger, CNewRecharge); #define CITADEL_CHARGES_PER_SECOND 10 / CHARGE_RATE #define CALLS_PER_SECOND 7.0f * CHARGES_PER_SECOND +#ifdef MAPBASE +#define CUSTOM_CHARGES_PER_SECOND(inc) inc / CHARGE_RATE +#endif + bool CNewRecharge::KeyValue( const char *szKeyName, const char *szValue ) { @@ -436,7 +522,14 @@ bool CNewRecharge::KeyValue( const char *szKeyName, const char *szValue ) void CNewRecharge::Precache( void ) { +#ifdef MAPBASE + if ( GetModelName() == NULL_STRING ) + SetModelName( AllocPooledString(HEALTH_CHARGER_MODEL_NAME) ); + + PrecacheModel( STRING(GetModelName()) ); +#else PrecacheModel( HEALTH_CHARGER_MODEL_NAME ); +#endif PrecacheScriptSound( "SuitRecharge.Deny" ); PrecacheScriptSound( "SuitRecharge.Start" ); @@ -446,6 +539,14 @@ void CNewRecharge::Precache( void ) void CNewRecharge::SetInitialCharge( void ) { +#ifdef MAPBASE + if ( m_iMaxJuice != 0 ) + { + // It must've been overridden by the mapper + return; + } +#endif + if ( HasSpawnFlags( SF_KLEINER_RECHARGER ) ) { // The charger in Kleiner's lab. @@ -470,14 +571,31 @@ void CNewRecharge::Spawn() SetSolid( SOLID_VPHYSICS ); CreateVPhysics(); +#ifdef MAPBASE + SetModel( STRING(GetModelName()) ); +#else SetModel( HEALTH_CHARGER_MODEL_NAME ); +#endif AddEffects( EF_NOSHADOW ); ResetSequence( LookupSequence( "idle" ) ); SetInitialCharge(); +#ifdef MAPBASE + // In case the juice was overridden + if (m_iJuice == 0) + UpdateJuice( MaxJuice() ); + else if (m_iJuice == -1) + { + UpdateJuice( 0 ); + ResetSequence( LookupSequence( "empty" ) ); + } + else + UpdateJuice( m_iJuice ); +#else UpdateJuice( MaxJuice() ); +#endif m_nState = 0; m_iCaps = FCAP_CONTINUOUS_USE; @@ -579,14 +697,26 @@ void CNewRecharge::InputRecharge( inputdata_t &inputdata ) void CNewRecharge::InputSetCharge( inputdata_t &inputdata ) { - ResetSequence( LookupSequence( "idle" ) ); - int iJuice = inputdata.value.Int(); m_flJuice = m_iMaxJuice = m_iJuice = iJuice; + + ResetSequence( m_iJuice > 0 ? LookupSequence( "idle" ) : LookupSequence( "empty" ) ); StudioFrameAdvance(); } +#ifdef MAPBASE +void CNewRecharge::InputSetChargeNoMax( inputdata_t &inputdata ) +{ + m_flJuice = inputdata.value.Float(); + + UpdateJuice(m_flJuice); + + ResetSequence( m_iJuice > 0 ? LookupSequence( "idle" ) : LookupSequence( "empty" ) ); + StudioFrameAdvance(); +} +#endif + void CNewRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // if it's not a player, ignore @@ -606,6 +736,11 @@ void CNewRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) ) flCharges = CITADEL_CHARGES_PER_SECOND; +#ifdef MAPBASE + if ( m_iIncrementValue != 0 ) + flCharges = CUSTOM_CHARGES_PER_SECOND(m_iIncrementValue); +#endif + m_flJuice -= flCharges / flCalls; StudioFrameAdvance(); } @@ -667,6 +802,11 @@ void CNewRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE } } +#ifdef MAPBASE + if (m_iIncrementValue != 0) + nIncrementArmor = m_iIncrementValue; +#endif + // If we're over our limit, debounce our keys if ( pPlayer->ArmorValue() >= nMaxArmor) { diff --git a/mp/src/game/server/hl2/func_tank.cpp b/mp/src/game/server/hl2/func_tank.cpp index db5a2e32..963a0e54 100644 --- a/mp/src/game/server/hl2/func_tank.cpp +++ b/mp/src/game/server/hl2/func_tank.cpp @@ -39,6 +39,10 @@ #include "particle_parse.h" // NVNT turret recoil #include "haptics/haptic_utils.h" +#ifdef MAPBASE +#include "shot_manipulator.h" +#include "filters.h" +#endif #ifdef HL2_DLL #include "hl2_player.h" @@ -49,6 +53,10 @@ extern Vector PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint); +#ifdef MAPBASE +extern ConVar ai_debug_shoot_positions; +#endif + ConVar mortar_visualize("mortar_visualize", "0" ); BEGIN_DATADESC( CFuncTank ) @@ -70,13 +78,19 @@ BEGIN_DATADESC( CFuncTank ) DEFINE_KEYFIELD( m_spriteScale, FIELD_FLOAT, "spritescale" ), DEFINE_KEYFIELD( m_iszSpriteSmoke, FIELD_STRING, "spritesmoke" ), DEFINE_KEYFIELD( m_iszSpriteFlash, FIELD_STRING, "spriteflash" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszShootSound, FIELD_SOUNDNAME, "shootsound" ), +#endif +#ifndef AMMOTYPE_MOVED DEFINE_KEYFIELD( m_bulletType, FIELD_INTEGER, "bullet" ), +#endif DEFINE_FIELD( m_nBulletCount, FIELD_INTEGER ), DEFINE_KEYFIELD( m_spread, FIELD_INTEGER, "firespread" ), DEFINE_KEYFIELD( m_iBulletDamage, FIELD_INTEGER, "bullet_damage" ), DEFINE_KEYFIELD( m_iBulletDamageVsPlayer, FIELD_INTEGER, "bullet_damage_vs_player" ), DEFINE_KEYFIELD( m_iszMaster, FIELD_STRING, "master" ), +#ifndef AMMOTYPE_MOVED #ifdef HL2_EPISODIC DEFINE_KEYFIELD( m_iszAmmoType, FIELD_STRING, "ammotype" ), DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), @@ -85,6 +99,7 @@ BEGIN_DATADESC( CFuncTank ) DEFINE_FIELD( m_iMediumAmmoType, FIELD_INTEGER ), DEFINE_FIELD( m_iLargeAmmoType, FIELD_INTEGER ), #endif // HL2_EPISODIC +#endif // AMMOTYPE_MOVED DEFINE_KEYFIELD( m_soundStartRotate, FIELD_SOUNDNAME, "rotatestartsound" ), DEFINE_KEYFIELD( m_soundStopRotate, FIELD_SOUNDNAME, "rotatestopsound" ), @@ -115,7 +130,11 @@ BEGIN_DATADESC( CFuncTank ) DEFINE_FIELD( m_hControlVolume, FIELD_EHANDLE ), DEFINE_KEYFIELD( m_iszControlVolume, FIELD_STRING, "control_volume" ), DEFINE_FIELD( m_flNextControllerSearch, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bShouldFindNPCs, FIELD_BOOLEAN, "ShouldFindNPCs" ), +#else DEFINE_FIELD( m_bShouldFindNPCs, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_bNPCInRoute, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_iszNPCManPoint, FIELD_STRING, "npc_man_point" ), DEFINE_FIELD( m_bReadyToFire, FIELD_BOOLEAN ), @@ -140,6 +159,16 @@ BEGIN_DATADESC( CFuncTank ) DEFINE_KEYFIELD( m_iEffectHandling, FIELD_INTEGER, "effecthandling" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bDontHitController, FIELD_BOOLEAN, "DontHitController" ), + DEFINE_KEYFIELD( m_bControllerGlued, FIELD_BOOLEAN, "ControllerGlued" ), + + DEFINE_KEYFIELD( m_iszTraceFilter, FIELD_STRING, "TraceFilter" ), + DEFINE_FIELD( m_hTraceFilter, FIELD_EHANDLE ), + + DEFINE_KEYFIELD( m_flPlayerBBoxDist, FIELD_FLOAT, "PlayerBBoxDist" ), +#endif + // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), @@ -151,6 +180,10 @@ BEGIN_DATADESC( CFuncTank ) DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetTargetEntity", InputSetTargetEntity ), DEFINE_INPUTFUNC( FIELD_VOID, "ClearTargetEntity", InputClearTargetEntity ), DEFINE_INPUTFUNC( FIELD_STRING, "FindNPCToManTank", InputFindNPCToManTank ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "TeleportNPCToManTank", InputTeleportNPCToManTank ), + DEFINE_INPUTFUNC( FIELD_STRING, "ForceNPCToManTank", InputForceNPCToManTank ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "StopFindingNPCs", InputStopFindingNPCs ), DEFINE_INPUTFUNC( FIELD_VOID, "StartFindingNPCs", InputStartFindingNPCs ), DEFINE_INPUTFUNC( FIELD_VOID, "ForceNPCOff", InputForceNPCOff ), @@ -178,6 +211,10 @@ CFuncTank::CFuncTank() m_bNPCInRoute = false; m_flNextControllerSearch = 0; m_bShouldFindNPCs = true; + +#ifdef MAPBASE + m_flPlayerBBoxDist = 24; +#endif } //----------------------------------------------------------------------------- @@ -353,7 +390,11 @@ void CFuncTank::InputFindNPCToManTank( inputdata_t &inputdata ) return; // NPC assigned to man the func_tank? +#ifdef MAPBASE + CBaseEntity *pEntity = gEntList.FindEntityByNameNearest( inputdata.value.String(), GetAbsOrigin(), 0, this, inputdata.pActivator, inputdata.pCaller ); +#else CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.StringID() ); +#endif if ( pEntity ) { CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); @@ -375,6 +416,124 @@ void CFuncTank::InputFindNPCToManTank( inputdata_t &inputdata ) NPC_FindController(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for telling the func_tank to teleport an NPC to man it. +//----------------------------------------------------------------------------- +void CFuncTank::InputTeleportNPCToManTank( inputdata_t &inputdata ) +{ + // Verify the func_tank is controllable and available. + if ( !IsNPCControllable() && !IsNPCSetController() ) + return; + + // If we have a controller already - don't look for one. + if ( HasController() ) + return; + + // NPC assigned to man the func_tank? + CBaseEntity *pEntity = gEntList.FindEntityByNameNearest( inputdata.value.String(), GetAbsOrigin(), 0, this, inputdata.pActivator, inputdata.pCaller ); + if ( pEntity ) + { + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if ( pNPC ) + { + // Verify the npc has the func_tank controller behavior. + CAI_FuncTankBehavior *pBehavior; + if ( pNPC->GetBehavior( &pBehavior ) ) + { + Vector vecVec; + QAngle angAng; + Vector vecVel = vec3_origin; + if ( m_iszNPCManPoint != NULL_STRING ) + { + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_iszNPCManPoint, this ); + if ( pEntity ) + { + vecVec = pEntity->GetAbsOrigin(); + } + } + + angAng = pNPC->GetAbsAngles(); + angAng.y = UTIL_VecToYaw ( GetAbsOrigin() - vecVec ); // Yaw from man point to turret + + pNPC->Teleport(&vecVec, &angAng, &vecVel); + + m_hController = pNPC; + pBehavior->SetFuncTank( this ); + NPC_SetInRoute( true ); + +#if 1 + pNPC->GetMotor()->SetIdealYawToTarget( GetAbsOrigin() ); + pNPC->SetTurnActivity(); + + pNPC->DoHolster(); + + pNPC->SpeakSentence( FUNCTANK_SENTENCE_JUST_MOUNTED ); + + // We are at the correct position and facing for the func_tank, mount it. + StartControl( pNPC ); + pNPC->ClearEnemyMemory(); + pBehavior->SetMounted(true); + + pNPC->SetIdealActivity( ACT_IDLE_MANNEDGUN ); +#endif + + return; + } + } + } + else + { + Warning("%s unable to find NPC \"%s\" to teleport to tank\n", GetDebugName(), inputdata.value.String()); + } + + // NPC_FindController() doesn't return a NPC and teleporting a random NPC seems kind of dangerous anyway. +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for telling the func_tank to find a NPC and instantly make them man it, no matter what they're doing or where they are. +//----------------------------------------------------------------------------- +void CFuncTank::InputForceNPCToManTank( inputdata_t &inputdata ) +{ + // Verify the func_tank is controllable and available. + if ( !IsNPCControllable() && !IsNPCSetController() ) + return; + + // If we have a controller already - don't look for one. + if ( HasController() ) + return; + + // NPC assigned to man the func_tank? + CBaseEntity *pEntity = gEntList.FindEntityByNameNearest( inputdata.value.String(), GetAbsOrigin(), 0, this, inputdata.pActivator, inputdata.pCaller ); + if ( pEntity ) + { + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if ( pNPC ) + { + // Verify the npc has the func_tank controller behavior. + CAI_FuncTankBehavior *pBehavior; + if ( pNPC->GetBehavior( &pBehavior ) ) + { + // Set the forced condition + pBehavior->SetCondition( CAI_FuncTankBehavior::COND_FUNCTANK_FORCED ); + + m_hController = pNPC; + pBehavior->SetFuncTank( this ); + NPC_SetInRoute( true ); + + return; + } + } + } + else + { + Warning("%s unable to find NPC \"%s\" to force to tank\n", GetDebugName(), inputdata.value.String()); + } + + // NPC_FindController() doesn't return a NPC and teleporting a random NPC seems kind of dangerous anyway. +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -735,13 +894,16 @@ void CFuncTank::Spawn( void ) { Precache(); +#ifndef AMMOTYPE_MOVED #ifdef HL2_EPISODIC - m_iAmmoType = GetAmmoDef()->Index( STRING( m_iszAmmoType ) ); + m_iAmmoType = GetAmmoDef()->Index(STRING(m_iszAmmoType)); #else - m_iSmallAmmoType = GetAmmoDef()->Index("Pistol"); - m_iMediumAmmoType = GetAmmoDef()->Index("SMG1"); - m_iLargeAmmoType = GetAmmoDef()->Index("AR2"); -#endif // HL2_EPISODIC + m_iSmallAmmoType = GetAmmoDef()->Index("Pistol"); + m_iMediumAmmoType = GetAmmoDef()->Index("SMG1"); + m_iLargeAmmoType = GetAmmoDef()->Index("AR2"); +#endif // HL2_EPISODIC +#endif // AMMOTYPE_MOVED + SetMoveType( MOVETYPE_PUSH ); // so it doesn't get pushed by anything SetSolid( SOLID_VPHYSICS ); @@ -878,6 +1040,15 @@ void CFuncTank::Activate( void ) m_nBarrelAttachment = pAnim->LookupAttachment( STRING(m_iszBarrelAttachment) ); } } + +#ifdef MAPBASE + if ( m_iszTraceFilter != NULL_STRING ) + { + m_hTraceFilter = dynamic_cast(gEntList.FindEntityByName( NULL, STRING(m_iszTraceFilter) )); + if (!m_hTraceFilter) + Warning("WARNING: %s trace filter %s is not a filter!\n", GetDebugName(), STRING(m_iszTraceFilter)); + } +#endif } bool CFuncTank::CreateVPhysics() @@ -893,6 +1064,10 @@ void CFuncTank::Precache( void ) PrecacheModel( STRING(m_iszSpriteSmoke) ); if ( m_iszSpriteFlash != NULL_STRING ) PrecacheModel( STRING(m_iszSpriteFlash) ); +#ifdef MAPBASE + if ( m_iszShootSound != NULL_STRING ) + PrecacheScriptSound( STRING(m_iszShootSound) ); +#endif if ( m_soundStartRotate != NULL_STRING ) PrecacheScriptSound( STRING(m_soundStartRotate) ); @@ -1046,6 +1221,16 @@ bool CFuncTank::StartControl( CBaseCombatCharacter *pController ) SetNextThink( gpGlobals->curtime + 0.1f ); // Let the map maker know a controller has been found +#ifdef MAPBASE + if ( m_hController->IsPlayer() ) + { + m_OnGotPlayerController.FireOutput( m_hController, this ); + } + else + { + m_OnGotController.FireOutput( m_hController, this ); + } +#else if ( m_hController->IsPlayer() ) { m_OnGotPlayerController.FireOutput( this, this ); @@ -1054,6 +1239,7 @@ bool CFuncTank::StartControl( CBaseCombatCharacter *pController ) { m_OnGotController.FireOutput( this, this ); } +#endif OnStartControlled(); return true; @@ -1071,8 +1257,13 @@ void CFuncTank::StopControl() OnStopControlled(); +#ifdef MAPBASE + // Arm player/npc weapon if they're not in a vehicle. + if ( !m_hController->IsInAVehicle() && m_hController->GetActiveWeapon() ) +#else // Arm player/npc weapon. if ( m_hController->GetActiveWeapon() ) +#endif { m_hController->GetActiveWeapon()->Deploy(); } @@ -1087,6 +1278,16 @@ void CFuncTank::StopControl() SetNextThink( TICK_NEVER_THINK ); // Let the map maker know a controller has been lost. +#ifdef MAPBASE + if ( m_hController->IsPlayer() ) + { + m_OnLostPlayerController.FireOutput( m_hController, this ); + } + else + { + m_OnLostController.FireOutput( m_hController, this ); + } +#else if ( m_hController->IsPlayer() ) { m_OnLostPlayerController.FireOutput( this, this ); @@ -1095,6 +1296,7 @@ void CFuncTank::StopControl() { m_OnLostController.FireOutput( this, this ); } +#endif // Reset the func_tank as unmanned (player/npc). if ( m_hController->IsPlayer() ) @@ -1161,7 +1363,12 @@ void CFuncTank::ControllerPostFrame( void ) Vector start = WorldBarrelPosition(); Vector dir = forward; +#ifdef MAPBASE + CTraceFilterSimple traceFilter = GetTraceFilter(); + UTIL_TraceHull( start, start + forward * 8192, -Vector(8,8,8), Vector(8,8,8), MASK_SHOT, &traceFilter, &tr ); +#else UTIL_TraceHull( start, start + forward * 8192, -Vector(8,8,8), Vector(8,8,8), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); +#endif if( tr.m_pEnt && tr.m_pEnt->m_takedamage != DAMAGE_NO && (tr.m_pEnt->GetFlags() & FL_AIMTARGET) ) { @@ -1189,6 +1396,9 @@ void CFuncTank::ControllerPostFrame( void ) if( --m_iAmmoCount == 0 ) { // Kick the player off the gun, and make myself not usable. +#ifdef MAPBASE + m_OnAmmoDepleted.FireOutput(pPlayer, this); +#endif m_spawnflags &= ~SF_TANK_CANCONTROL; StopControl(); return; @@ -1335,6 +1545,11 @@ void CFuncTank::NPC_Fire( void ) { SetNextAttack( gpGlobals->curtime + m_fireTime ); } + +#ifdef MAPBASE + // This is now needed in some cases + m_fireLast = gpGlobals->curtime; +#endif } @@ -1660,6 +1875,15 @@ QAngle CFuncTank::AimBarrelAt( const Vector &parentTarget ) { return GetLocalAngles(); } +#ifdef MAPBASE + else if ( m_barrelPos.LengthSqr() == 0.0f ) + { + // Do a simpler calculation that doesn't take barrel into account + float targetToCenterYaw = atan2( target.y, target.x ); + float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) ); + return QAngle( -RAD2DEG( targetToCenterPitch ), RAD2DEG( targetToCenterYaw ), 0 ); + } +#endif else { // We're trying to aim the offset barrel at an arbitrary point. @@ -1706,8 +1930,14 @@ void CFuncTank::CalcPlayerCrosshairTarget( Vector *pVecTarget ) vecDir = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT ); } +#ifdef MAPBASE + CTraceFilterSimple traceFilter = GetTraceFilter(); + UTIL_TraceLine( vecStart + vecDir * m_flPlayerBBoxDist, vecStart + vecDir * 8192, MASK_BLOCKLOS_AND_NPCS, &traceFilter, &tr ); + //DebugDrawLine(tr.startpos, tr.endpos, 222, 222, 0, false, 0.1); +#else // Make sure to start the trace outside of the player's bbox! UTIL_TraceLine( vecStart + vecDir * 24, vecStart + vecDir * 8192, MASK_BLOCKLOS_AND_NPCS, this, COLLISION_GROUP_NONE, &tr ); +#endif *pVecTarget = tr.endpos; } @@ -1822,11 +2052,15 @@ bool CFuncTank::RotateTankToAngles( const QAngle &angles, float *pDistX, float * //----------------------------------------------------------------------------- // We lost our target! //----------------------------------------------------------------------------- -void CFuncTank::LostTarget( void ) +void CFuncTank::LostTarget( CBaseEntity *pTarget ) { if (m_fireLast != 0) { +#ifdef MAPBASE + m_OnLoseTarget.Set(pTarget, pTarget, this); +#else m_OnLoseTarget.FireOutput(this, this); +#endif m_fireLast = 0; } } @@ -1929,7 +2163,11 @@ void CFuncTank::AimFuncTankAtTarget( void ) m_hTarget = FindTarget( m_targetEntityName, NULL ); } +#ifdef MAPBASE + LostTarget(pEntity); +#else LostTarget(); +#endif return; } @@ -1953,8 +2191,13 @@ void CFuncTank::AimFuncTankAtTarget( void ) { if ( m_hTarget ) { +#ifdef MAPBASE + LostTarget(m_hTarget); + m_hTarget = NULL; +#else m_hTarget = NULL; LostTarget(); +#endif } return; } @@ -2053,18 +2296,30 @@ void CFuncTank::AimFuncTankAtTarget( void ) { if (m_fireLast == 0) { +#ifdef MAPBASE + m_OnAquireTarget.Set(pTarget, pTarget, this); +#else m_OnAquireTarget.FireOutput(this, this); +#endif } FiringSequence( barrelEnd, forward, this ); } else { +#ifdef MAPBASE + LostTarget(pTarget); +#else LostTarget(); +#endif } } else { +#ifdef MAPBASE + LostTarget(pTarget); +#else LostTarget(); +#endif } } @@ -2203,6 +2458,55 @@ const char *CFuncTank::GetTracerType( void ) //----------------------------------------------------------------------------- void CFuncTank::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ) { +#ifdef MAPBASE + bool bSpriteSmoke = m_iszSpriteSmoke != NULL_STRING; + bool bSpriteFlash = m_iszSpriteFlash != NULL_STRING; + + if (bSpriteSmoke || bSpriteFlash) + { + if (bSpriteSmoke) + { + CSprite *pSmoke = CSprite::SpriteCreate( STRING(m_iszSpriteSmoke), barrelEnd, TRUE ); + pSmoke->AnimateAndDie( random->RandomFloat( 15.0, 20.0 ) ); + pSmoke->SetTransparency( kRenderTransAlpha, m_clrRender->r, m_clrRender->g, m_clrRender->b, 255, kRenderFxNone ); + + Vector vecVelocity( 0, 0, random->RandomFloat(40, 80) ); + pSmoke->SetAbsVelocity( vecVelocity ); + pSmoke->SetScale( m_spriteScale ); + } + + if (bSpriteFlash) + { + CSprite *pFlash = CSprite::SpriteCreate( STRING(m_iszSpriteFlash), barrelEnd, TRUE ); + pFlash->AnimateAndDie( 5 ); + pFlash->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation ); + pFlash->SetScale( m_spriteScale ); + } + } + else if ( m_iEffectHandling == EH_AR2 ) + { + DoMuzzleFlash(); + } + else if ( m_iEffectHandling == EH_COMBINE_CANNON ) + { + DoMuzzleFlash(); + } + + if (m_iszShootSound != NULL_STRING) + { + EmitSound(STRING(m_iszShootSound)); + } + else if ( m_iEffectHandling == EH_AR2 ) + { + // Play the AR2 sound + EmitSound( "Weapon_functank.Single" ); + } + else if ( m_iEffectHandling == EH_COMBINE_CANNON ) + { + // Play the cannon sound + EmitSound( "NPC_Combine_Cannon.FireBullet" ); + } +#else // If we have a specific effect handler, apply it's effects if ( m_iEffectHandling == EH_AR2 ) { @@ -2238,6 +2542,7 @@ void CFuncTank::Fire( int bulletCount, const Vector &barrelEnd, const Vector &fo pSprite->SetScale( m_spriteScale ); } } +#endif if( pAttacker && pAttacker->IsPlayer() ) { @@ -2252,7 +2557,11 @@ void CFuncTank::Fire( int bulletCount, const Vector &barrelEnd, const Vector &fo } +#ifdef MAPBASE + m_OnFire.FireOutput(pAttacker, this); +#else m_OnFire.FireOutput(this, this); +#endif m_bReadyToFire = false; } @@ -2384,6 +2693,84 @@ bool CFuncTank::IsEntityInViewCone( CBaseEntity *pEntity ) return true; } +#ifdef MAPBASE +//========================================================= +// I decided to make a custom trace filter for func_tank trace filters. +// If the base class thinks the trace should hit the entity, it goes through +// a filter and if it passes the filter, the trace passes the entity instead. +// +// This is different from CTraceFilterEntityFilter because it's simplified and can use its own exclusion list. +// (it also came before it) +//========================================================= +class CTankTraceFilter : public CTraceFilterSimpleList +{ +public: + CTankTraceFilter( int collisionGroup ) : CTraceFilterSimpleList( collisionGroup ) {} + + bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + bool base = CTraceFilterSimpleList::ShouldHitEntity( pHandleEntity, contentsMask ); + + // Our base is telling us to hit. If it passes the filter, don't. + if ( base && m_pFilter ) + { + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + return !m_pFilter->PassesFilter(m_pCaller, pEntity); + + // TODO: Should we use this code from CBulletsTraceFilter? + /* + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + CBaseEntity *pPassEntity = EntityFromEntityHandle( m_PassEntities[0] ); + if ( pEntity && pPassEntity && pEntity->GetOwnerEntity() == pPassEntity && + pPassEntity->IsSolidFlagSet(FSOLID_NOT_SOLID) && pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMBOXTEST ) && + pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ) ) + { + // It's a bone follower of the entity to ignore (toml 8/3/2007) + return false; + } + */ + } + + return base; + } + + CBaseFilter *m_pFilter; + CBaseEntity *m_pCaller; + +}; + +//----------------------------------------------------------------------------- +// Purpose: Gets our general trace filter we use for LOS, trace locations, etc. +// This was created so we could easily change the trace filter func_tank generally uses, +// but it can be overridden by derived classes, so there's that. +//----------------------------------------------------------------------------- +CTraceFilterSimple CFuncTank::GetTraceFilter() +{ + //CTraceFilterSkipTwoEntities traceFilter( this, GetParent(), COLLISION_GROUP_NONE ); + + CTankTraceFilter traceFilter( COLLISION_GROUP_NONE ); + traceFilter.SetPassEntity(this); + + if (GetParent()) + { + CBaseEntity *pParent = GetParent(); + traceFilter.AddEntityToIgnore(pParent); + + // Add the parent's parent too. (for func_tanks mounted on moving things, like vehicles) + if (pParent->GetParent()) + traceFilter.AddEntityToIgnore(pParent->GetParent()); + } + + if (m_bDontHitController) + traceFilter.AddEntityToIgnore(GetController()); + + traceFilter.m_pFilter = m_hTraceFilter.Get(); + traceFilter.m_pCaller = this; + + return traceFilter; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Return true if this func tank can see the enemy //----------------------------------------------------------------------------- @@ -2398,7 +2785,11 @@ bool CFuncTank::HasLOSTo( CBaseEntity *pEntity ) trace_t tr; // Ignore the func_tank and any prop it's parented to +#ifdef MAPBASE + CTraceFilterSimple traceFilter = GetTraceFilter(); +#else CTraceFilterSkipTwoEntities traceFilter( this, GetParent(), COLLISION_GROUP_NONE ); +#endif // UNDONE: Should this hit BLOCKLOS brushes? AI_TraceLine( vecBarrelEnd, vecTarget, MASK_BLOCKLOS_AND_NPCS, &traceFilter, &tr ); @@ -2428,10 +2819,76 @@ class CFuncTankGun : public CFuncTank public: DECLARE_CLASS( CFuncTankGun, CFuncTank ); +#ifdef AMMOTYPE_MOVED + DECLARE_DATADESC(); + + string_t m_iszAmmoType; // The name of the ammodef that we use when we fire. Bullet damage still comes from keyvalues. + int m_iAmmoType; // The cached index of the ammodef that we use when we fire. +#endif // !AMMOTYPE_MOVED + +#ifdef AMMOTYPE_MOVED + void Spawn( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); +#endif // AMMOTYPE_MOVED + + void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ); }; + +#ifdef AMMOTYPE_MOVED +BEGIN_DATADESC(CFuncTankGun) + + DEFINE_KEYFIELD( m_iszAmmoType, FIELD_STRING, "ammotype" ), + DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), + +END_DATADESC() +#endif // AMMOTYPE_MOVED + LINK_ENTITY_TO_CLASS( func_tank, CFuncTankGun ); +#ifdef AMMOTYPE_MOVED +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFuncTankGun::Spawn( void ) +{ + if (m_iszAmmoType != NULL_STRING) + m_iAmmoType = GetAmmoDef()->Index(STRING(m_iszAmmoType)); + + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: Caches entity key values until spawn is called. +// Input : szKeyName - +// szValue - +// Output : +//----------------------------------------------------------------------------- +bool CFuncTankGun::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "bullet")) + { + switch (atoi(szValue)) + { + case TANK_BULLET_SMALL: + m_iAmmoType = GetAmmoDef()->Index("Pistol"); + break; + case TANK_BULLET_MEDIUM: + m_iAmmoType = GetAmmoDef()->Index("SMG1"); + break; + case TANK_BULLET_LARGE: + m_iAmmoType = GetAmmoDef()->Index("AR2"); + break; + default: + m_iAmmoType = -1; + } + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} +#endif // AMMOTYPE_MOVED + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2459,7 +2916,27 @@ void CFuncTankGun::Fire( int bulletCount, const Vector &barrelEnd, const Vector info.m_pAttacker = pAttacker; info.m_pAdditionalIgnoreEnt = GetParent(); -#ifdef HL2_EPISODIC +#ifdef MAPBASE + CUtlVector ignorelist; + + if (pAttacker) + { + if (m_bDontHitController) + { + ignorelist.AddToTail(pAttacker); + } + + // Ignore any vehicle our controller is in + if (pAttacker->MyCombatCharacterPointer() && pAttacker->MyCombatCharacterPointer()->IsInAVehicle()) + { + ignorelist.AddToTail(pAttacker->MyCombatCharacterPointer()->GetVehicleEntity()); + } + } + + info.m_pIgnoreEntList = &ignorelist; +#endif + +#if defined(HL2_EPISODIC) || defined(AMMOTYPE_MOVED) if ( m_iAmmoType != -1 ) { for ( i = 0; i < bulletCount; i++ ) @@ -2515,7 +2992,11 @@ public: color32 m_flPulseColor; float m_flPulseLife; float m_flPulseLag; +#ifdef MAPBASE + #define m_sPulseFireSound m_iszShootSound +#else string_t m_sPulseFireSound; +#endif }; LINK_ENTITY_TO_CLASS( func_tankpulselaser, CFuncTankPulseLaser ); @@ -2695,6 +3176,82 @@ void CFuncTankLaser::Fire( int bulletCount, const Vector &barrelEnd, const Vecto } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Missile for func_tankrocket so kills are credited properly and don't kill friendlies +//----------------------------------------------------------------------------- +class CFuncTankMissile : public CMissile +{ + DECLARE_CLASS( CFuncTankMissile, CMissile ); + DECLARE_DATADESC(); + +public: + virtual void Spawn( void ); + + EHANDLE m_hTurret; + +private: + void FTnkMissileTouch( CBaseEntity *pOther ); +}; + +BEGIN_DATADESC( CFuncTankMissile ) + + DEFINE_FIELD( m_hTurret, FIELD_EHANDLE ), + + // Function Pointers + DEFINE_FUNCTION( FTnkMissileTouch ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( func_tankrocket_missile, CFuncTankMissile ); + +void CFuncTankMissile::Spawn( void ) +{ + Precache(); + + SetSolid( SOLID_BBOX ); + SetModel("models/weapons/w_missile_launch.mdl"); + UTIL_SetSize( this, -Vector(4,4,4), Vector(4,4,4) ); + + SetTouch( &CFuncTankMissile::FTnkMissileTouch ); + + SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + SetThink( &CMissile::IgniteThink ); + + SetNextThink( gpGlobals->curtime + 0.3f ); + + m_takedamage = DAMAGE_YES; + m_iHealth = m_iMaxHealth = 100; + m_bloodColor = DONT_BLEED; + + AddFlag( FL_OBJECT ); +} + +//----------------------------------------------------------------------------- +// The actual explosion +//----------------------------------------------------------------------------- +void CFuncTankMissile::FTnkMissileTouch( CBaseEntity *pOther ) +{ + Assert( pOther ); + + // Don't touch triggers (but DO hit weapons) + if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON ) + { + // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. + if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) ) + return; + } + + // Do not touch the turret we fired from + if (pOther == m_hTurret.Get()) + { + return; + } + + Explode(); +} +#endif + class CFuncTankRocket : public CFuncTank { public: @@ -2720,13 +3277,23 @@ LINK_ENTITY_TO_CLASS( func_tankrocket, CFuncTankRocket ); void CFuncTankRocket::Precache( void ) { +#ifdef MAPBASE + UTIL_PrecacheOther( "func_tankrocket_missile" ); +#else UTIL_PrecacheOther( "rpg_missile" ); +#endif CFuncTank::Precache(); } void CFuncTankRocket::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ) { +#ifdef MAPBASE + CFuncTankMissile *pRocket = (CFuncTankMissile*)CBaseEntity::Create( "func_tankrocket_missile", barrelEnd, GetAbsAngles(), GetController() ); + pRocket->AddEffects( EF_NOSHADOW ); + pRocket->m_hTurret.Set(this); +#else CMissile *pRocket = (CMissile *) CBaseEntity::Create( "rpg_missile", barrelEnd, GetAbsAngles(), this ); +#endif pRocket->DumbFire(); pRocket->SetNextThink( gpGlobals->curtime + 0.1f ); @@ -2743,6 +3310,9 @@ void CFuncTankRocket::Fire( int bulletCount, const Vector &barrelEnd, const Vect CFuncTank::Fire( bulletCount, barrelEnd, forward, this, bIgnoreSpread ); } +#ifdef MAPBASE +static const char *s_pAirboatGunThinkContext = "AirboatGunThinkContext"; +#endif //----------------------------------------------------------------------------- // Airboat gun @@ -2753,15 +3323,32 @@ public: DECLARE_CLASS( CFuncTankAirboatGun, CFuncTank ); DECLARE_DATADESC(); +#ifdef MAPBASE + CFuncTankAirboatGun() + { + // -1 = original behavior + m_spread = -1; + } +#endif + void Precache( void ); virtual void Spawn(); virtual void Activate(); virtual void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ); virtual void ControllerPostFrame(); +#ifdef MAPBASE + virtual void FuncTankAirboatGunThink(); + virtual void TankActivate(void); + virtual void TankDeactivate(void); + virtual void OnStartControlled(); +#endif virtual void OnStopControlled(); virtual const char *GetTracerType( void ); virtual Vector WorldBarrelPosition( void ); virtual void DoImpactEffect( trace_t &tr, int nDamageType ); +#ifdef MAPBASE + virtual void StopLoopingSounds() { DestroySounds(); BaseClass::StopLoopingSounds(); } +#endif private: void CreateSounds(); @@ -2778,6 +3365,12 @@ private: CHandle m_hAirboatGunModel; int m_nGunBarrelAttachment; float m_flLastImpactEffectTime; + +#ifdef MAPBASE + float m_flHeavyShotInterval = 0.2f; + int m_iHeavyShotSpread; + bool m_bUseDamageKV; +#endif }; @@ -2793,6 +3386,15 @@ BEGIN_DATADESC( CFuncTankAirboatGun ) // DEFINE_FIELD( m_hAirboatGunModel, FIELD_EHANDLE ), // DEFINE_FIELD( m_nGunBarrelAttachment, FIELD_INTEGER ), DEFINE_FIELD( m_flLastImpactEffectTime, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flHeavyShotInterval, FIELD_FLOAT, "heavy_shot_interval" ), + DEFINE_KEYFIELD( m_iHeavyShotSpread, FIELD_INTEGER, "heavy_shot_spread" ), + DEFINE_KEYFIELD( m_bUseDamageKV, FIELD_BOOLEAN, "use_damage_kv" ), +#endif + +#ifdef MAPBASE + DEFINE_THINKFUNC( FuncTankAirboatGunThink ), +#endif END_DATADESC() @@ -2805,7 +3407,16 @@ LINK_ENTITY_TO_CLASS( func_tankairboatgun, CFuncTankAirboatGun ); void CFuncTankAirboatGun::Precache( void ) { BaseClass::Precache(); +#ifdef MAPBASE + // Odd placement, but it works + if (m_iszShootSound == NULL_STRING) + { + m_iszShootSound = AllocPooledString("Airboat.FireGunLoop"); + PrecacheScriptSound(STRING(m_iszShootSound)); + } +#else PrecacheScriptSound( "Airboat.FireGunLoop" ); +#endif PrecacheScriptSound( "Airboat.FireGunRevDown"); CreateSounds(); } @@ -2820,6 +3431,10 @@ void CFuncTankAirboatGun::Spawn( void ) m_flNextHeavyShotTime = 0.0f; m_bIsFiring = false; m_flLastImpactEffectTime = -1; + +#ifdef MAPBASE + SetContextThink( &CFuncTankAirboatGun::FuncTankAirboatGunThink, gpGlobals->curtime, s_pAirboatGunThinkContext ); +#endif } @@ -2838,6 +3453,13 @@ void CFuncTankAirboatGun::Activate() m_nGunBarrelAttachment = m_hAirboatGunModel->LookupAttachment( "muzzle" ); } } +#ifdef MAPBASE + else if (GetParent() && GetParent()->GetBaseAnimating()) + { + m_hAirboatGunModel = GetParent()->GetBaseAnimating(); + m_nGunBarrelAttachment = GetGunBarrelAttachment(); + } +#endif } @@ -2851,7 +3473,11 @@ void CFuncTankAirboatGun::CreateSounds() CPASAttenuationFilter filter( this ); if (!m_pGunFiringSound) { +#ifdef MAPBASE + m_pGunFiringSound = controller.SoundCreate( filter, entindex(), STRING(m_iszShootSound) ); +#else m_pGunFiringSound = controller.SoundCreate( filter, entindex(), "Airboat.FireGunLoop" ); +#endif controller.Play( m_pGunFiringSound, 0, 100 ); } } @@ -2914,11 +3540,73 @@ void CFuncTankAirboatGun::ControllerPostFrame( void ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Maintains airboat gun sounds on NPCs +//----------------------------------------------------------------------------- +void CFuncTankAirboatGun::FuncTankAirboatGunThink( void ) +{ + if (!GetController()) + { + if (m_fireLast != 0) + { + StartFiring(); + } + else + { + StopFiring(); + } + } + else if (GetController()->IsNPC()) + { + // Attempt to estimate when we wouldn't be firing + if ((gpGlobals->curtime - m_fireLast - (1.0 / m_fireRate)) < 0.1f) + { + StartFiring(); + } + else + { + StopFiring(); + } + } + + SetNextThink( gpGlobals->curtime + 0.05f, s_pAirboatGunThinkContext ); +} + + +void CFuncTankAirboatGun::TankActivate(void) +{ + SetNextThink( gpGlobals->curtime + 0.05f, s_pAirboatGunThinkContext ); + BaseClass::TankActivate(); +} + +void CFuncTankAirboatGun::TankDeactivate(void) +{ + DevMsg("Tank deactivate\n"); + SetNextThink( TICK_NEVER_THINK, s_pAirboatGunThinkContext ); + StopFiring(); + BaseClass::TankDeactivate(); +} + +void CFuncTankAirboatGun::OnStartControlled() +{ + if (GetController() && GetController()->IsNPC()) + SetNextThink( gpGlobals->curtime + 0.05f, s_pAirboatGunThinkContext ); + + BaseClass::OnStartControlled(); +} +#endif + + //----------------------------------------------------------------------------- // Stop controlled //----------------------------------------------------------------------------- void CFuncTankAirboatGun::OnStopControlled() { +#ifdef MAPBASE + DevMsg("Tank stop control\n"); + SetNextThink( TICK_NEVER_THINK, s_pAirboatGunThinkContext ); +#endif StopFiring(); BaseClass::OnStopControlled(); } @@ -2999,16 +3687,35 @@ void CFuncTankAirboatGun::Fire( int bulletCount, const Vector &barrelEnd, const info.m_flDistance = 4096; info.m_iAmmoType = ammoType; +#ifdef MAPBASE + info.m_pAttacker = pAttacker; + info.m_pAdditionalIgnoreEnt = GetParent(); + + if (m_bUseDamageKV) + { + info.m_flDamage = m_iBulletDamage; + info.m_iPlayerDamage = m_iBulletDamageVsPlayer; + } +#endif + if ( gpGlobals->curtime >= m_flNextHeavyShotTime ) { info.m_iShots = 1; +#ifdef MAPBASE + info.m_vecSpread = gTankSpread[m_iHeavyShotSpread]; +#else info.m_vecSpread = VECTOR_CONE_PRECALCULATED; +#endif info.m_flDamageForceScale = 1000.0f; } else { info.m_iShots = 2; +#ifdef MAPBASE + info.m_vecSpread = m_spread != -1 ? gTankSpread[m_spread] : VECTOR_CONE_5DEGREES; +#else info.m_vecSpread = VECTOR_CONE_5DEGREES; +#endif } FireBullets( info ); @@ -3016,10 +3723,39 @@ void CFuncTankAirboatGun::Fire( int bulletCount, const Vector &barrelEnd, const DoMuzzleFlash(); // NOTE: This must occur after FireBullets +#ifdef MAPBASE + if ( gpGlobals->curtime >= m_flNextHeavyShotTime && m_flHeavyShotInterval != -1 ) +#else if ( gpGlobals->curtime >= m_flNextHeavyShotTime ) +#endif { +#ifdef MAPBASE + m_flNextHeavyShotTime = gpGlobals->curtime + m_flHeavyShotInterval; +#else m_flNextHeavyShotTime = gpGlobals->curtime + AIRBOAT_GUN_HEAVY_SHOT_INTERVAL; +#endif } + +#ifdef MAPBASE + // Things from CFuncTank::Fire(). + // We can't use everything because it overrides a few things. + if( pAttacker && pAttacker->IsPlayer() ) + { + if ( IsX360() ) + { + // Now, if you're playing Mapbase on the Xbox 360, the airboat gun turret will make your controller rumble! + // Isn't that lovely? Hmm? + UTIL_PlayerByIndex(1)->RumbleEffect( RUMBLE_AR2, 0, RUMBLE_FLAG_RESTART | RUMBLE_FLAG_RANDOM_AMPLITUDE ); + } + else + { + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, barrelEnd + forward * 32.0f, 32.0f, 0.2f, pAttacker, SOUNDENT_CHANNEL_WEAPON ); + } + } + + m_OnFire.FireOutput(pAttacker, this); + m_bReadyToFire = false; +#endif } @@ -3216,7 +3952,11 @@ class CMortarShell : public CBaseEntity public: DECLARE_CLASS( CMortarShell, CBaseEntity ); +#ifdef MAPBASE + static CMortarShell *Create( const Vector &vecStart, const trace_t &tr, const Vector &vecShotDir, float flImpactDelay, float flWarnDelay, string_t warnSound ); +#else static CMortarShell *Create( const Vector &vecStart, const Vector &vecTarget, const Vector &vecShotDir, float flImpactDelay, float flWarnDelay, string_t warnSound ); +#endif void Spawn( void ); void Precache( void ); @@ -3226,6 +3966,15 @@ public: void FadeThink( void ); int UpdateTransmitState( void ); +#ifdef MAPBASE + void SetRadius(float fl) { m_flRadius = fl; } + void SetMagnitude(int i) { m_Magnitude = i; } + +public: + + bool m_bDontHitController; +#endif + private: void FixUpImpactPoint( const Vector &initialPos, const Vector &initialNormal, Vector *endPos, Vector *endNormal ); @@ -3240,6 +3989,9 @@ private: Vector m_vecFiredFrom; Vector m_vecFlyDir; float m_flSpawnedTime; +#ifdef MAPBASE + int m_Magnitude; +#endif CHandle m_pBeamEffect[4]; @@ -3268,6 +4020,10 @@ BEGIN_DATADESC( CMortarShell ) DEFINE_AUTO_ARRAY( m_pBeamEffect, FIELD_EHANDLE), DEFINE_FIELD( m_flRadius, FIELD_FLOAT ), DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ), +#ifdef MAPBASE + DEFINE_FIELD( m_bDontHitController, FIELD_BOOLEAN ), + DEFINE_FIELD( m_Magnitude, FIELD_INTEGER ), +#endif DEFINE_FUNCTION( FlyThink ), DEFINE_FUNCTION( FadeThink ), @@ -3327,13 +4083,19 @@ void CMortarShell::FixUpImpactPoint( const Vector &initialPos, const Vector &ini #define MORTAR_BLAST_DAMAGE 50 #define MORTAR_BLAST_HEIGHT 7500 +#ifdef MAPBASE +CMortarShell *CMortarShell::Create( const Vector &vecStart, const trace_t &tr, const Vector &vecShotDir, float flImpactDelay, float flWarnDelay, string_t warnSound ) +#else CMortarShell *CMortarShell::Create( const Vector &vecStart, const Vector &vecTarget, const Vector &vecShotDir, float flImpactDelay, float flWarnDelay, string_t warnSound ) +#endif { CMortarShell *pShell = (CMortarShell *)CreateEntityByName("mortarshell" ); +#ifndef MAPBASE // Place the mortar shell at the target location so that it can make the sound and explode. trace_t tr; UTIL_TraceLine( vecTarget, vecTarget + ( vecShotDir * 128.0f ), MASK_SOLID_BRUSHONLY, pShell, COLLISION_GROUP_NONE, &tr ); +#endif Vector targetPos, targetNormal; pShell->FixUpImpactPoint( tr.endpos, tr.plane.normal, &targetPos, &targetNormal ); @@ -3619,7 +4381,11 @@ void CMortarShell::Impact( void ) // Fire the bullets Vector vecSrc, vecShootDir; +#ifdef MAPBASE + float flRadius = m_flRadius; +#else float flRadius = MORTAR_BLAST_RADIUS; +#endif trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 128 ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); @@ -3680,7 +4446,11 @@ void CMortarShell::Impact( void ) FBEAM_FADEOUT ); +#ifdef MAPBASE + RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), m_Magnitude / 2, (DMG_BLAST|DMG_DISSOLVE) ), GetAbsOrigin(), flRadius, CLASS_NONE, m_bDontHitController ? GetOwnerEntity() : NULL ); +#else RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), MORTAR_BLAST_DAMAGE, (DMG_BLAST|DMG_DISSOLVE) ), GetAbsOrigin(), MORTAR_BLAST_RADIUS, CLASS_NONE, NULL ); +#endif EmitSound( "Weapon_Mortar.Impact" ); @@ -3772,7 +4542,11 @@ public: int m_Magnitude; float m_fireDelay; +#ifdef MAPBASE + #define m_fireStartSound m_iszShootSound +#else string_t m_fireStartSound; +#endif //string_t m_fireEndSound; string_t m_incomingSound; @@ -3781,6 +4555,11 @@ public: bool m_fLastShotMissed; +#ifdef MAPBASE + float m_flRadius; + int m_iMortarTraceMask = MASK_SOLID_BRUSHONLY; +#endif + // store future firing event CBaseEntity *m_pAttacker; }; @@ -3799,6 +4578,11 @@ BEGIN_DATADESC( CFuncTankMortar ) DEFINE_FIELD( m_fLastShotMissed, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ), + DEFINE_KEYFIELD( m_iMortarTraceMask, FIELD_INTEGER, "trace_mask" ), +#endif + DEFINE_FIELD( m_pAttacker, FIELD_CLASSPTR ), // Inputs @@ -3933,13 +4717,21 @@ void CFuncTankMortar::Fire( int bulletCount, const Vector &barrelEnd, const Vect vecSpot.z = GetAbsOrigin().z; // Trace up to find the fake 'apex' of the shell. The skybox or 1024 units, whichever comes first. +#ifdef MAPBASE + UTIL_TraceLine( vecSpot, vecSpot + Vector(0, 0, 1024), m_iMortarTraceMask, NULL, COLLISION_GROUP_NONE, &tr ); +#else UTIL_TraceLine( vecSpot, vecSpot + Vector(0, 0, 1024), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); +#endif vecSpot = tr.endpos; //NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, false, 5 ); // Now trace from apex to target +#ifdef MAPBASE + UTIL_TraceLine( vecSpot, vecProjectedPosition, m_iMortarTraceMask, NULL, COLLISION_GROUP_NONE, &tr ); +#else UTIL_TraceLine( vecSpot, vecProjectedPosition, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); +#endif if( mortar_visualize.GetBool() ) { @@ -3962,7 +4754,16 @@ void CFuncTankMortar::Fire( int bulletCount, const Vector &barrelEnd, const Vect Vector vecFinalDir = tr.endpos - tr.startpos; VectorNormalize( vecFinalDir ); +#ifdef MAPBASE + CMortarShell *pShell = CMortarShell::Create( barrelEnd, tr, vecFinalDir, m_fireDelay, m_flWarningTime, m_incomingSound ); + pShell->SetOwnerEntity(GetController()); + pShell->m_bDontHitController = m_bDontHitController; + if (m_flRadius != 0) + pShell->SetRadius(m_flRadius); + pShell->SetMagnitude(m_Magnitude); +#else CMortarShell::Create( barrelEnd, tr.endpos, vecFinalDir, m_fireDelay, m_flWarningTime, m_incomingSound ); +#endif BaseClass::Fire( bulletCount, barrelEnd, vecForward, this, bIgnoreSpread ); } @@ -4063,6 +4864,9 @@ private: Vector m_vecTrueForward; bool m_bShouldHarrass; bool m_bLastTargetWasNPC; // Tells whether the last entity we fired a shot at was an NPC (otherwise it was the player) +#ifdef MAPBASE + bool m_bControllableVersion = false; // Allows new behavior that makes player/NPC control easier. Doesn't use spawnflag for legacy purposes, as if anyone used this as a regular tank before. +#endif }; BEGIN_DATADESC( CFuncTankCombineCannon ) @@ -4074,6 +4878,9 @@ BEGIN_DATADESC( CFuncTankCombineCannon ) DEFINE_FIELD( m_vecTrueForward, FIELD_VECTOR ), DEFINE_FIELD( m_bShouldHarrass, FIELD_BOOLEAN ), DEFINE_FIELD( m_bLastTargetWasNPC, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bControllableVersion, FIELD_BOOLEAN, "ControllableVersion" ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "EnableHarrass", InputEnableHarrass ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableHarrass", InputDisableHarrass ), @@ -4100,7 +4907,12 @@ void CFuncTankCombineCannon::Spawn() { BaseClass::Spawn(); m_flTimeBeamOn = gpGlobals->curtime; +#ifdef MAPBASE + if (!m_bControllableVersion) + CreateBeam(); +#else CreateBeam(); +#endif m_bShouldHarrass = true; @@ -4154,8 +4966,14 @@ void CFuncTankCombineCannon::DestroyBeam() //--------------------------------------------------------- void CFuncTankCombineCannon::AdjustRateOfFire() { +#ifdef MAPBASE + // Only maintain 1.5 rounds per second if we're using legacy behavior. + if (!m_bControllableVersion) + m_fireRate = 1.5; +#else // Maintain 1.5 rounds per second rate of fire. m_fireRate = 1.5; +#endif /* if( m_hTarget.Get() != NULL && m_hTarget->IsPlayer() ) { @@ -4208,6 +5026,12 @@ void CFuncTankCombineCannon::FuncTankPostThink() { AdjustRateOfFire(); +#ifdef MAPBASE + // Controllables don't sweep + if (m_bControllableVersion) + return; +#endif + if( m_hTarget.Get() == NULL ) { if( gpGlobals->curtime > m_flTimeNextSweep ) @@ -4263,7 +5087,11 @@ void CFuncTankCombineCannon::FuncTankPostThink() // Ignore the func_tank and any prop it's parented to, and check line of sight to the point // Trace to the point. If an opaque trace doesn't reach the point, that means the beam hit // something closer, (including a blockLOS), so try again. +#ifdef MAPBASE + CTraceFilterSimple traceFilter = GetTraceFilter(); +#else CTraceFilterSkipTwoEntities traceFilter( this, GetParent(), COLLISION_GROUP_NONE ); +#endif AI_TraceLine( vecBarrelEnd, vecTest, MASK_BLOCKLOS_AND_NPCS, &traceFilter, &trLOS ); AI_TraceLine( vecBarrelEnd, vecTest, MASK_SHOT, &traceFilter, &trShoot ); @@ -4320,13 +5148,23 @@ void CFuncTankCombineCannon::FuncTankPostThink() //--------------------------------------------------------- void CFuncTankCombineCannon::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ) { +#ifdef MAPBASE + // If we're in aim-at-pos mode and not in the new controllable version, don't fire in this mode + if ( HasSpawnFlags(SF_TANK_AIM_AT_POS) && !m_bControllableVersion ) + return; +#else // Specifically do NOT fire in aim at pos mode. This is just for show. if( HasSpawnFlags(SF_TANK_AIM_AT_POS) ) return; +#endif Vector vecAdjustedForward = forward; +#ifdef MAPBASE + if ( !IsPlayerManned() && m_hTarget != NULL ) +#else if( m_hTarget != NULL ) +#endif { Vector vecToTarget = m_hTarget->BodyTarget( barrelEnd, false ) - barrelEnd; VectorNormalize( vecToTarget ); @@ -4429,3 +5267,93 @@ void CFuncTankCombineCannon::InputDisableHarrass( inputdata_t &inputdata ) LINK_ENTITY_TO_CLASS( func_tank_combine_cannon, CFuncTankCombineCannon ); + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Func tank that fires a bunch of outputs instead +//----------------------------------------------------------------------------- +class CFuncTankLogic : public CFuncTank +{ +public: + DECLARE_CLASS(CFuncTankLogic, CFuncTank); + DECLARE_DATADESC(); + + CFuncTankLogic(); + + void Fire(int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread); + +protected: + + bool m_bShootsThroughWater; + + COutputVector m_OnFire_BarrelPos; + COutputVector m_OnFire_BarrelAng; + COutputVector m_OnFire_ShootPos; + COutputEHANDLE m_OnFire_FirstEnt; +}; + +LINK_ENTITY_TO_CLASS(func_tanklogic, CFuncTankLogic); + +BEGIN_DATADESC(CFuncTankLogic) + + //DEFINE_KEYFIELD( m_bDontHitController, FIELD_BOOLEAN, "DontHitController" ), + + DEFINE_OUTPUT( m_OnFire_BarrelPos, "OnFire_BarrelPos" ), + DEFINE_OUTPUT( m_OnFire_BarrelAng, "OnFire_BarrelAng" ), + DEFINE_OUTPUT( m_OnFire_ShootPos, "OnFire_ShootPos" ), + DEFINE_OUTPUT( m_OnFire_FirstEnt, "OnFire_FirstEnt" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CFuncTankLogic::CFuncTankLogic() +{ + // This is overriden by KV later + m_bDontHitController = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFuncTankLogic::Fire(int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread) +{ + // A bunch of stuff from FireBullets() to replicate firing a regular func_tank. + // It mostly just has the trace stuff. + Vector vecDir; + Vector vecEnd; + + trace_t tr; + CTraceFilterSimple traceFilter = GetTraceFilter(); + + CShotManipulator Manipulator( forward ); + vecDir = Manipulator.ApplySpread( bIgnoreSpread ? gTankSpread[0] : gTankSpread[m_spread] ); + + vecEnd = barrelEnd + vecDir * MAX_TRACE_LENGTH; + + int mask = MASK_SHOT; + + if (!m_bShootsThroughWater) + mask |= CONTENTS_WATER; + + AI_TraceLine(barrelEnd, vecEnd, mask, &traceFilter, &tr); + + if ( tr.startsolid ) + { + tr.endpos = tr.startpos; + tr.fraction = 0.0f; + } + + if ( ai_debug_shoot_positions.GetBool() ) + NDebugOverlay::Line(barrelEnd, tr.endpos, 255, 255, 255, false, .1 ); + + BaseClass::Fire(bulletCount, barrelEnd, forward, pAttacker, bIgnoreSpread); + + m_OnFire_BarrelPos.Set(barrelEnd, pAttacker, this); + m_OnFire_BarrelAng.Set(forward, pAttacker, this); + m_OnFire_ShootPos.Set(tr.endpos, pAttacker, this); + m_OnFire_FirstEnt.Set(tr.m_pEnt, tr.m_pEnt, this); +} +#endif // MAPBASE + diff --git a/mp/src/game/server/hl2/func_tank.h b/mp/src/game/server/hl2/func_tank.h index 46b444d5..0ecacce5 100644 --- a/mp/src/game/server/hl2/func_tank.h +++ b/mp/src/game/server/hl2/func_tank.h @@ -55,6 +55,20 @@ enum TANKBULLET #define MORTAR_BLAST_RADIUS 350 +#ifdef MAPBASE +// This moves variables related to ammo types from CFuncTank to CFuncTankGun, as CFuncTankGun and its derivatives are the only classes that use it. +// It also completely replaces the legacy "bullet" keyvalue with the AmmoType keyvalue, making bullet translate to the ammo type variable directly. +// This fixes the issue where some func_tanks don't fire any bullets. +// +// This code was created in September-October 2018 and moving existing variables like this seems risky and somewhat pointless, but the nature of func_tanks +// make this unlikely to cause any problems and helps my OCD. +// +// Disable this preprocessor if it causes problems. +#define AMMOTYPE_MOVED 1 + +class CTraceFilterSimple; +#endif + // Custom damage // env_laser (duration is 0.5 rate of fire) @@ -134,6 +148,13 @@ public: virtual void DoMuzzleFlash( void ); virtual const char *GetTracerType( void ); +#ifdef MAPBASE + virtual CTraceFilterSimple GetTraceFilter(); + + // Needed because func_tankairboatgun needs the barrel + int GetGunBarrelAttachment() { return m_nBarrelAttachment; } +#endif + protected: virtual float GetShotSpeed() { return 0; } @@ -179,6 +200,10 @@ protected: private: void InputFindNPCToManTank( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputTeleportNPCToManTank( inputdata_t &inputdata ); + void InputForceNPCToManTank( inputdata_t &inputdata ); +#endif void InputStopFindingNPCs( inputdata_t &inputdata ); void InputStartFindingNPCs( inputdata_t &inputdata ); void InputForceNPCOff( inputdata_t &inputdata ); @@ -217,7 +242,11 @@ private: bool RotateTankToAngles( const QAngle &angles, float *pDistX = NULL, float *pDistY = NULL ); // We lost our target! +#ifdef MAPBASE + void LostTarget( CBaseEntity *pTarget ); +#else void LostTarget( void ); +#endif // Purpose: void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition ); @@ -237,6 +266,7 @@ protected: int m_iBulletDamage; // 0 means use Bullet type's default damage int m_iBulletDamageVsPlayer; // Damage vs player. 0 means use m_iBulletDamage +#ifndef AMMOTYPE_MOVED #ifdef HL2_EPISODIC string_t m_iszAmmoType; // The name of the ammodef that we use when we fire. Bullet damage still comes from keyvalues. int m_iAmmoType; // The cached index of the ammodef that we use when we fire. @@ -244,7 +274,9 @@ protected: int m_iSmallAmmoType; int m_iMediumAmmoType; int m_iLargeAmmoType; -#endif // HL2_EPISODIC +#endif // HL2_EPISODIC +#endif // !AMMOTYPE_MOVED + int m_spread; // firing spread @@ -255,6 +287,15 @@ protected: int m_nBulletCount; +#ifdef MAPBASE + bool m_bDontHitController; + string_t m_iszTraceFilter; + CHandle m_hTraceFilter; + + // Created to nullify aiming problems when the func_tank is on a vehicle or the player is too close to the barrel + float m_flPlayerBBoxDist; +#endif + private: // This is either the player manning the func_tank, or an NPC. The NPC is either manning the tank, or running @@ -293,6 +334,19 @@ private: string_t m_iszSpriteSmoke; string_t m_iszSpriteFlash; +#ifdef MAPBASE +public: + // This is kind of tricky to implement. + // Some derived classes already have their own shoot sound variables, making this one redundant, etc. + // To rectify this, m_iszShootSound replaces all of them and takes their keyfield names. + // It's like the TF spy of func_tank. + string_t m_iszShootSound; + + // NPC controllers cannot leave. >:) + bool m_bControllerGlued; +private: +#endif + string_t m_iszMaster; // Master entity (game_team_master or multisource) string_t m_soundStartRotate; @@ -327,9 +381,16 @@ private: float m_flNextLeadFactor; float m_flNextLeadFactorTime; +#ifdef MAPBASE +public: + COutputEvent m_OnFire; + COutputEHANDLE m_OnLoseTarget; + COutputEHANDLE m_OnAquireTarget; +#else COutputEvent m_OnFire; COutputEvent m_OnLoseTarget; COutputEvent m_OnAquireTarget; +#endif COutputEvent m_OnAmmoDepleted; COutputEvent m_OnGotController; COutputEvent m_OnLostController; diff --git a/mp/src/game/server/hl2/grenade_ar2.cpp b/mp/src/game/server/hl2/grenade_ar2.cpp index 70841c66..455efb18 100644 --- a/mp/src/game/server/hl2/grenade_ar2.cpp +++ b/mp/src/game/server/hl2/grenade_ar2.cpp @@ -34,6 +34,9 @@ extern ConVar sk_npc_dmg_smg1_grenade; extern ConVar sk_max_smg1_grenade; ConVar sk_smg1_grenade_radius ( "sk_smg1_grenade_radius","0"); +#ifdef MAPBASE +ConVar smg1_grenade_credit_transfer("smg1_grenade_credit_transfer", "1"); +#endif ConVar g_CV_SmokeTrail("smoke_trail", "1", 0); // temporary dust explosion switch @@ -161,6 +164,14 @@ void CGrenadeAR2::GrenadeAR2Think( void ) void CGrenadeAR2::Event_Killed( const CTakeDamageInfo &info ) { +#ifdef MAPBASE + if (smg1_grenade_credit_transfer.GetBool() && info.GetAttacker()->MyCombatCharacterPointer()) + { + CBaseCombatCharacter *pBCC = info.GetAttacker()->MyCombatCharacterPointer(); + SetThrower(pBCC); + SetOwnerEntity(pBCC); + } +#endif Detonate( ); } diff --git a/mp/src/game/server/hl2/grenade_bugbait.cpp b/mp/src/game/server/hl2/grenade_bugbait.cpp index 12e3dda1..7d5d7a47 100644 --- a/mp/src/game/server/hl2/grenade_bugbait.cpp +++ b/mp/src/game/server/hl2/grenade_bugbait.cpp @@ -200,6 +200,11 @@ void CGrenadeBugBait::BugBaitTouch( CBaseEntity *pOther ) // Tell all spawners to now fight to this position g_AntlionMakerManager.BroadcastFightGoal( GetAbsOrigin() ); +#ifdef MAPBASE + m_OnDetonate.FireOutput(GetThrower(), this); + m_OnDetonate_OutPosition.Set(GetAbsOrigin(), GetThrower(), this); +#endif + //Go away UTIL_Remove( this ); } diff --git a/mp/src/game/server/hl2/grenade_frag.cpp b/mp/src/game/server/hl2/grenade_frag.cpp index 1788d191..b0ad60fb 100644 --- a/mp/src/game/server/hl2/grenade_frag.cpp +++ b/mp/src/game/server/hl2/grenade_frag.cpp @@ -11,6 +11,9 @@ #include "Sprite.h" #include "SpriteTrail.h" #include "soundent.h" +#ifdef MAPBASE +#include "mapbase/ai_grenade.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -126,8 +129,31 @@ void CGrenadeFrag::Spawn( void ) SetCollisionGroup( COLLISION_GROUP_WEAPON ); CreateVPhysics(); +#ifdef MAPBASE + if (GetThrower() && GetThrower()->IsNPC()) + { + // One of OnThrowGrenade's useful applications is replacing it with another entity using point_entity_replace. + // However, the grenade is always able to let out a blip before being replaced, which can be confusing/undesirable. + // This code checks to see if OnThrowGrenade is being used for anything, in which case the first blip will be very slightly delayed. + // This doesn't interfere with when the grenade actually detonates and shouldn't be noticable if the grenade is kept by OnThrowGrenade anyway. + CAI_GrenadeUserSink *pGrenadeUser = dynamic_cast(GetThrower()); + if (pGrenadeUser && pGrenadeUser->UsingOnThrowGrenade()) + { + // We delay the blip by 0.05, so replacement must occur within that period in order to skip the blip. + m_flNextBlipTime = gpGlobals->curtime + 0.05f; + } + } + + // Do the blip if m_flNextBlipTime wasn't changed + if (m_flNextBlipTime <= gpGlobals->curtime) + { + BlipSound(); + m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY; + } +#else BlipSound(); m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY; +#endif AddSolidFlags( FSOLID_NOT_STANDABLE ); diff --git a/mp/src/game/server/hl2/grenade_spit.cpp b/mp/src/game/server/hl2/grenade_spit.cpp index 4b713e6d..5f3e8ef9 100644 --- a/mp/src/game/server/hl2/grenade_spit.cpp +++ b/mp/src/game/server/hl2/grenade_spit.cpp @@ -131,7 +131,19 @@ void CGrenadeSpit::GrenadeSpitTouch( CBaseEntity *pOther ) if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) ) { // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. +#ifdef MAPBASE + // But some physics objects that are also triggers (like weapons) shouldn't go through this check. + // + // Note: rpg_missile has the same code, except it properly accounts for weapons in a different way. + // This was discovered after I implemented this and both work fine, but if this ever causes problems, + // use rpg_missile's implementation: + // + // if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON ) + // + if ( pOther->GetMoveType() == MOVETYPE_NONE && (( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY )) ) +#else if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) ) +#endif return; } diff --git a/mp/src/game/server/hl2/hl2_client.cpp b/mp/src/game/server/hl2/hl2_client.cpp index 6125de82..4cd9dd7b 100644 --- a/mp/src/game/server/hl2/hl2_client.cpp +++ b/mp/src/game/server/hl2/hl2_client.cpp @@ -137,6 +137,21 @@ void respawn( CBaseEntity *pEdict, bool fCopyCorpse ) // respawn player pEdict->Spawn(); } +#ifdef MAPBASE + else if (g_pGameRules->AllowSPRespawn()) + { + // In SP respawns, only create corpse if drawing externally + CBasePlayer *pPlayer = (CBasePlayer*)pEdict; + if ( fCopyCorpse && pPlayer->m_bDrawPlayerModelExternally ) + { + // make a copy of the dead body for appearances sake + pPlayer->CreateCorpse(); + } + + // respawn player + pPlayer->Spawn(); + } +#endif else { // restart the entire server engine->ServerCommand("reload\n"); diff --git a/mp/src/game/server/hl2/hl2_player.cpp b/mp/src/game/server/hl2/hl2_player.cpp index e7583f5a..f249c3e6 100644 --- a/mp/src/game/server/hl2/hl2_player.cpp +++ b/mp/src/game/server/hl2/hl2_player.cpp @@ -55,12 +55,21 @@ #include "portal_player.h" #endif // PORTAL +#ifdef MAPBASE +#include "triggers.h" +#include "mapbase/variant_tools.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern ConVar weapon_showproficiency; extern ConVar autoaim_max_dist; +#ifdef MAPBASE +extern ConVar player_squad_autosummon_enabled; +#endif + // Do not touch with without seeing me, please! (sjb) // For consistency's sake, enemy gunfire is traced against a scaled down // version of the player's hull, not the hitboxes for the player's model @@ -107,6 +116,10 @@ ConVar autoaim_unlock_target( "autoaim_unlock_target", "0.8666" ); ConVar sv_stickysprint("sv_stickysprint", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX); +#ifdef MAPBASE +ConVar player_autoswitch_enabled( "player_autoswitch_enabled", "1", FCVAR_NONE, "This convar was added by Mapbase to toggle whether players automatically switch to their ''best'' weapon upon picking up ammo for it after it was dry." ); +#endif + #define FLASH_DRAIN_TIME 1.1111 // 100 units / 90 secs #define FLASH_CHARGE_TIME 50.0f // 100 units / 2 secs @@ -157,12 +170,26 @@ static impactdamagetable_t gCappedPlayerImpactDamageTable = // Flashlight utility bool g_bCacheLegacyFlashlightStatus = true; +#ifdef MAPBASE +extern ThreeState_t Flashlight_GetLegacyVersionKey(); +#endif bool g_bUseLegacyFlashlight; bool Flashlight_UseLegacyVersion( void ) { // If this is the first run through, cache off what the answer should be (cannot change during a session) if ( g_bCacheLegacyFlashlightStatus ) { +#ifdef MAPBASE + // Check if there's a gameinfo setting. + ThreeState_t iGameKey = Flashlight_GetLegacyVersionKey(); + if (iGameKey != TRS_NONE) + { + g_bUseLegacyFlashlight = (iGameKey == TRS_TRUE); + g_bCacheLegacyFlashlightStatus = false; + return g_bUseLegacyFlashlight; + } +#endif + char modDir[MAX_PATH]; if ( UTIL_GetModDir( modDir, sizeof(modDir) ) == false ) return false; @@ -200,6 +227,17 @@ public: COutputInt m_RequestedPlayerHealth; +#ifdef MAPBASE + COutputInt m_OnGetAmmo; + COutputEvent m_PlayerDamaged; + COutputEvent m_OnSquadMemberKilled; + COutputInt m_RequestedPlayerArmor; + COutputFloat m_RequestedPlayerAuxPower; + COutputFloat m_RequestedPlayerFlashBattery; + + COutputEvent m_OnPlayerSpawn; +#endif + void InputRequestPlayerHealth( inputdata_t &inputdata ); void InputSetFlashlightSlowDrain( inputdata_t &inputdata ); void InputSetFlashlightNormalDrain( inputdata_t &inputdata ); @@ -212,14 +250,165 @@ public: #ifdef PORTAL void InputSuppressCrosshair( inputdata_t &inputdata ); #endif // PORTAL2 +#ifdef MAPBASE + void InputRequestPlayerArmor( inputdata_t &inputdata ); + void InputRequestPlayerAuxPower( inputdata_t &inputdata ); + void InputRequestPlayerFlashBattery( inputdata_t &inputdata ); + + void InputGetAmmoOnWeapon( inputdata_t &inputdata ); + + void InputSetHandModel( inputdata_t &inputdata ); + void InputSetHandModelSkin( inputdata_t &inputdata ); + + void InputSetPlayerModel( inputdata_t &inputdata ); + void InputSetPlayerDrawExternally( inputdata_t &inputdata ); +#endif void Activate ( void ); +#ifdef MAPBASE + bool KeyValue( const char *szKeyName, const char *szValue ); + + bool AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ); + + void NotifyPlayerHasProxy(); + + // This is here because the player might not be available when we spawn. + // Hope there wouldn't be enough time for this to need to be saved... + CUtlDict m_QueuedKV; + + int m_MaxArmor = 100; +#endif + bool PassesDamageFilter( const CTakeDamageInfo &info ); EHANDLE m_hPlayer; }; +#ifdef MAPBASE +static CUtlVector g_pCommandRedirects; + +//----------------------------------------------------------------------------- +// Redirects player squad commands +//----------------------------------------------------------------------------- +class CCommandRedirect : public CBaseTrigger +{ + DECLARE_CLASS( CCommandRedirect, CBaseTrigger ); +public: + CCommandRedirect() + { + g_pCommandRedirects.AddToTail(this); + //int i = g_pCommandRedirects.AddToTail(); + //g_pCommandRedirects[i].Set( this ); + } + + ~CCommandRedirect() + { + g_pCommandRedirects.FindAndRemove(this); + /* + for (int i = 0; i < g_pCommandRedirects.Count(); i++) + { + if (g_pCommandRedirects[i].Get() == this) + { + g_pCommandRedirects.Remove( i ); + break; + } + } + */ + } + + void Spawn() + { + BaseClass::Spawn(); + InitTrigger(); + } + + // Will the command point change? + // True = Judged guilty and changed. + // False = Judged not guilty and unchanged. + bool GetVerdict(Vector *defendant, CHL2_Player *pPlayer) + { + // Deliver goal to relevant destinations before sentencing. + m_OnCommandGoal.Set(*defendant, pPlayer, this); + + if (m_target == NULL_STRING) + { + // Abort sentencing. + return false; + } + else if (FStrEq(STRING(m_target), "-1")) + { + // Deliver verdict immediately. + *defendant = Vector(0, 0, 0); + return false; + } + else + { + // Locate entity of interest. + // Player is caller. + // Player squad representative is activator. + CBaseEntity *pEntOfInterest = gEntList.FindEntityGeneric(NULL, STRING(m_target), this, pPlayer->GetSquadCommandRepresentative(), pPlayer); + if (pEntOfInterest) + { + // Deliver their local origin. + *defendant = pEntOfInterest->GetLocalOrigin(); + return true; + } + } + + // No sentence. + return false; + } + + void HandleAllies(CAI_Squad *pSquad, CHL2_Player *pPlayer) + { + if (m_bRepOnly) + { + CBaseEntity *pSquadRep = pPlayer->GetSquadCommandRepresentative(); + if (pSquadRep) + m_OutAlly.Set(pSquadRep, pSquadRep, this); + } + else + { + AISquadIter_t iter; + for ( CBaseEntity *pAllyNpc = pSquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = pSquad->GetNextMember(&iter) ) + { + m_OutAlly.Set(pAllyNpc, pAllyNpc, this); + } + } + } + + bool PassesTriggerFilters(CBaseEntity *pOther) + { + return pOther->IsPlayer() || (pOther->MyNPCPointer() && pOther->MyNPCPointer()->IsInPlayerSquad()); + } + + bool IsDisabled() { return m_bDisabled; } + + DECLARE_DATADESC(); + +private: + bool m_bRepOnly; + + COutputVector m_OnCommandGoal; + COutputEHANDLE m_OutAlly; +}; + +LINK_ENTITY_TO_CLASS( func_commandredirect, CCommandRedirect ); +BEGIN_DATADESC( CCommandRedirect ) + + DEFINE_KEYFIELD( m_bRepOnly, FIELD_BOOLEAN, "reponly" ), + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_OUTPUT( m_OnCommandGoal, "OnCommandGoal" ), + DEFINE_OUTPUT( m_OutAlly, "OutAlly" ), + +END_DATADESC() +#endif + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ @@ -366,10 +555,34 @@ BEGIN_DATADESC( CHL2_Player ) DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreFallDamage", InputIgnoreFallDamage ), DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreFallDamageWithoutReset", InputIgnoreFallDamageWithoutReset ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "OnSquadMemberKilled", OnSquadMemberKilled ), +#else DEFINE_INPUTFUNC( FIELD_VOID, "OnSquadMemberKilled", OnSquadMemberKilled ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "DisableFlashlight", InputDisableFlashlight ), DEFINE_INPUTFUNC( FIELD_VOID, "EnableFlashlight", InputEnableFlashlight ), DEFINE_INPUTFUNC( FIELD_VOID, "ForceDropPhysObjects", InputForceDropPhysObjects ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "SquadForceSummon", InputSquadForceSummon ), + DEFINE_INPUTFUNC( FIELD_INPUT, "SquadForceGoTo", InputSquadForceGoTo ), // FIELD_INPUT so it supports vectors, ehandles, and strings + + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddArmor", InputAddArmor ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveArmor", InputRemoveArmor ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetArmor", InputSetArmor ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "AddAuxPower", InputAddAuxPower ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "RemoveAuxPower", InputRemoveAuxPower ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAuxPower", InputSetAuxPower ), + + DEFINE_INPUTFUNC( FIELD_VOID, "TurnFlashlightOn", InputTurnFlashlightOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnFlashlightOff", InputTurnFlashlightOff ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableGeigerCounter", InputEnableGeigerCounter ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableGeigerCounter", InputDisableGeigerCounter ), + DEFINE_INPUTFUNC( FIELD_VOID, "ShowSquadHUD", InputShowSquadHUD ), + DEFINE_INPUTFUNC( FIELD_VOID, "HideSquadHUD", InputHideSquadHUD ), +#endif DEFINE_SOUNDPATCH( m_sndLeeches ), DEFINE_SOUNDPATCH( m_sndWaterSplashes ), @@ -387,6 +600,23 @@ BEGIN_DATADESC( CHL2_Player ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CHL2_Player, CBasePlayer, "The HL2 player entity." ) + + DEFINE_SCRIPTFUNC_NAMED( SuitPower_Drain, "RemoveAuxPower", "Removes from the player's available aux power." ) + DEFINE_SCRIPTFUNC_NAMED( SuitPower_Charge, "AddAuxPower", "Adds to the player's available aux power." ) + DEFINE_SCRIPTFUNC_NAMED( SuitPower_SetCharge, "SetAuxPower", "Sets the player's available aux power." ) + DEFINE_SCRIPTFUNC_NAMED( SuitPower_GetCurrentPercentage, "GetAuxPower", "Gets the player's available aux power." ) + DEFINE_SCRIPTFUNC( GetFlashlightBattery, "Gets the energy available in the player's flashlight. If the legacy (aux power-based) flashlight is enabled, this returns the aux power." ) + + DEFINE_SCRIPTFUNC( InitCustomSuitDevice, "Initializes a custom suit device. (just sets drain rate for now)" ) + DEFINE_SCRIPTFUNC( AddCustomSuitDevice, "Adds a custom suit device ID. (1-3)" ) + DEFINE_SCRIPTFUNC( RemoveCustomSuitDevice, "Removes a custom suit device ID. (1-3)" ) + DEFINE_SCRIPTFUNC( IsCustomSuitDeviceActive, "Checks if a custom suit device is active." ) + +END_SCRIPTDESC(); +#endif + CHL2_Player::CHL2_Player() { m_nNumMissPositions = 0; @@ -415,6 +645,16 @@ CHL2_Player::CHL2_Player() #endif CSuitPowerDevice SuitDeviceBreather( bits_SUIT_DEVICE_BREATHER, 6.7f ); // 100 units in 15 seconds (plus three padded seconds) +#ifdef MAPBASE +// Default: 100 units in 8 seconds +CSuitPowerDevice SuitDeviceCustom[] = +{ + { bits_SUIT_DEVICE_CUSTOM0, 12.5f }, + { bits_SUIT_DEVICE_CUSTOM1, 12.5f }, + { bits_SUIT_DEVICE_CUSTOM2, 12.5f }, +}; +#endif + IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player) SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable), @@ -1103,6 +1343,203 @@ void CHL2_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) BaseClass::PlayerRunCommand( ucmd, moveHelper ); } +#ifdef MAPBASE +void CHL2_Player::SpawnedAtPoint( CBaseEntity *pSpawnPoint ) +{ + FirePlayerProxyOutput( "OnPlayerSpawn", variant_t(), this, pSpawnPoint ); +} + +//----------------------------------------------------------------------------- + +ConVar hl2_use_hl2dm_anims( "hl2_use_hl2dm_anims", "0", FCVAR_NONE, "Allows SP HL2 players to use HL2:DM animations (for custom player models)" ); + +void CHL2_Player::ResetAnimation( void ) +{ + if (!hl2_use_hl2dm_anims.GetBool()) + return; + + if (IsAlive()) + { + SetSequence( -1 ); + SetActivity( ACT_INVALID ); + + if (!GetAbsVelocity().x && !GetAbsVelocity().y) + SetAnimation( PLAYER_IDLE ); + else if ((GetAbsVelocity().x || GetAbsVelocity().y) && (GetFlags() & FL_ONGROUND)) + SetAnimation( PLAYER_WALK ); + else if (GetWaterLevel() > 1) + SetAnimation( PLAYER_WALK ); + } +} + +// Set the activity based on an event or current state +void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim ) +{ + if (!hl2_use_hl2dm_anims.GetBool()) + { + BaseClass::SetAnimation( playerAnim ); + return; + } + + int animDesired; + + float speed; + + speed = GetAbsVelocity().Length2D(); + + + // bool bRunning = true; + + //Revisit! +/* if ( ( m_nButtons & ( IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) ) ) + { + if ( speed > 1.0f && speed < hl2_normspeed.GetFloat() - 20.0f ) + { + bRunning = false; + } + }*/ + + if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) ) + { + speed = 0; + playerAnim = PLAYER_IDLE; + } + + Activity idealActivity = ACT_HL2MP_RUN; + + // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb) + if ( playerAnim == PLAYER_JUMP ) + { + idealActivity = ACT_HL2MP_JUMP; + } + else if ( playerAnim == PLAYER_DIE ) + { + if ( m_lifeState == LIFE_ALIVE ) + { + return; + } + } + else if ( playerAnim == PLAYER_ATTACK1 ) + { + if ( GetActivity( ) == ACT_HOVER || + GetActivity( ) == ACT_SWIM || + GetActivity( ) == ACT_HOP || + GetActivity( ) == ACT_LEAP || + GetActivity( ) == ACT_DIESIMPLE ) + { + idealActivity = GetActivity( ); + } + else + { + idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK; + } + } + else if ( playerAnim == PLAYER_RELOAD ) + { + idealActivity = ACT_HL2MP_GESTURE_RELOAD; + } + else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK ) + { + if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_HL2MP_JUMP ) // Still jumping + { + idealActivity = GetActivity( ); + } + /* + else if ( GetWaterLevel() > 1 ) + { + if ( speed == 0 ) + idealActivity = ACT_HOVER; + else + idealActivity = ACT_SWIM; + } + */ + else + { + if ( GetFlags() & FL_DUCKING ) + { + if ( speed > 0 ) + { + idealActivity = ACT_HL2MP_WALK_CROUCH; + } + else + { + idealActivity = ACT_HL2MP_IDLE_CROUCH; + } + } + else + { + if ( speed > 0 ) + { + /* + if ( bRunning == false ) + { + idealActivity = ACT_WALK; + } + else + */ + { + idealActivity = ACT_HL2MP_RUN; + } + } + else + { + idealActivity = ACT_HL2MP_IDLE; + } + } + } + } + + if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK ) + { + RestartGesture( Weapon_TranslateActivity( idealActivity ) ); + + // FIXME: this seems a bit wacked + Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 ); + + return; + } + else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD ) + { + RestartGesture( Weapon_TranslateActivity( idealActivity ) ); + return; + } + else + { + SetActivity( idealActivity ); + + animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) ); + + if (animDesired == -1) + { + animDesired = SelectWeightedSequence( idealActivity ); + + if ( animDesired == -1 ) + { + animDesired = 0; + } + } + + // Already using the desired animation? + if ( GetSequence() == animDesired ) + return; + + m_flPlaybackRate = 1.0; + ResetSequence( animDesired ); + SetCycle( 0 ); + return; + } + + // Already using the desired animation? + if ( GetSequence() == animDesired ) + return; + + //Msg( "Set animation to %d\n", animDesired ); + // Reset to first frame of desired animation + ResetSequence( animDesired ); + SetCycle( 0 ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Sets HL2 specific defaults. //----------------------------------------------------------------------------- @@ -1117,6 +1554,17 @@ void CHL2_Player::Spawn(void) BaseClass::Spawn(); +#ifdef MAPBASE + // Ported from CHL2MP_Player. Fixes issues with respawning players in SP + if ( !IsObserver() ) + { + pl.deadflag = false; + RemoveSolidFlags( FSOLID_NOT_SOLID ); + + RemoveEffects( EF_NODRAW ); + } +#endif + // // Our player movement speed is set once here. This will override the cl_xxxx // cvars unless they are set to be lower than this. @@ -1397,10 +1845,52 @@ bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal ) //--------------------------------- // MASK_SHOT on purpose! So that you don't hit the invisible hulls of the NPCs. +#ifdef MAPBASE + // Get either our +USE entity or the gravity gun entity + CBaseEntity *pHeldEntity = GetPlayerHeldEntity(this); + if ( !pHeldEntity ) + PhysCannonGetHeldEntity( GetActiveWeapon() ); + + CTraceFilterSkipTwoEntities filter( this, pHeldEntity, COLLISION_GROUP_INTERACTIVE_DEBRIS ); +#else CTraceFilterSkipTwoEntities filter( this, PhysCannonGetHeldEntity( GetActiveWeapon() ), COLLISION_GROUP_INTERACTIVE_DEBRIS ); +#endif UTIL_TraceLine( EyePosition(), EyePosition() + forward * MAX_COORD_RANGE, MASK_SHOT, &filter, &tr ); +#ifdef MAPBASE + // func_commandredirect handling + if (g_pCommandRedirects.Count() > 0) + { + for (int i = 0; i < g_pCommandRedirects.Count(); i++) + { + CCommandRedirect *pCommandRedirect = static_cast(g_pCommandRedirects[i]); + if (!pCommandRedirect || pCommandRedirect->IsDisabled() || !pCommandRedirect->PointIsWithin(tr.endpos)) + continue; + + // First, GIVE IT OUR ALLIES so it could fire outputs + pCommandRedirect->HandleAllies(m_pPlayerAISquad, this); + + Vector vec = tr.endpos; + if (pCommandRedirect->GetVerdict(&vec, this)) + { + // It doesn't want us moving, so just don't find a goal at all + if (vec.IsZero()) + { + return false; + } + + // Just set our goal to this, the mapper didn't sign up for these checks + pGoal->m_vecGoalLocation = vec; + return true; + } + + // Only one should be necessary + break; + } + } + //else +#endif if( !tr.DidHitWorld() ) { CUtlVector Allies; @@ -1521,7 +2011,11 @@ void CHL2_Player::CommanderUpdate() { CAI_BaseNPC *pCommandRepresentative = GetSquadCommandRepresentative(); bool bFollowMode = false; +#ifdef MAPBASE + if ( pCommandRepresentative && !HasSpawnFlags(SF_PLAYER_HIDE_SQUAD_HUD) ) +#else if ( pCommandRepresentative ) +#endif { bFollowMode = ( pCommandRepresentative->GetCommandGoal() == vec3_invalid ); @@ -1567,8 +2061,22 @@ void CHL2_Player::CommanderUpdate() { m_CommanderUpdateTimer.Set(2.5); +#ifdef MAPBASE + if ( pCommandRepresentative->ShouldAutoSummon() ) + { + if (!HL2GameRules()->AutosummonDisabled() && player_squad_autosummon_enabled.GetBool()) + CommanderExecute( CC_FOLLOW ); + else + { + // Show a hud hint if autosummoning has been disabled + UTIL_HudHintText( this, "#Valve_Hint_Command_recall" ); + //m_CommanderUpdateTimer.Set(10.0); + } + } +#else if ( pCommandRepresentative->ShouldAutoSummon() ) CommanderExecute( CC_FOLLOW ); +#endif } } @@ -1696,6 +2204,95 @@ void CHL2_Player::CommanderMode() } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::InputSquadForceSummon( inputdata_t &inputdata ) +{ + CommanderExecute( CC_FOLLOW ); +} + +//----------------------------------------------------------------------------- +// Purpose: Forces the player's squad to go to a specific location or entity. +//----------------------------------------------------------------------------- +void CHL2_Player::InputSquadForceGoTo( inputdata_t &inputdata ) +{ + CAI_BaseNPC *pPlayerSquadLeader = GetSquadCommandRepresentative(); + + if ( !pPlayerSquadLeader ) + return; + + int i; + CUtlVector Allies; + commandgoal_t goal; + + variant_t var = Variant_ParseInput(inputdata); + + if (var.FieldType() == FIELD_VECTOR) + { + goal.m_pGoalEntity = NULL; + var.Vector3D(goal.m_vecGoalLocation); + } + else + { + goal.m_pGoalEntity = var.FieldType() == FIELD_EHANDLE ? var.Entity().Get() : gEntList.FindEntityByNameNearest(var.String(), pPlayerSquadLeader->GetAbsOrigin(), 0, this, inputdata.pActivator, inputdata.pCaller); + goal.m_vecGoalLocation = vec3_invalid; + } + + AISquadIter_t iter; + for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) + { + if ( pAllyNpc->IsCommandable() ) + Allies.AddToTail( pAllyNpc ); + } + + CAI_BaseNPC * pTargetNpc = (goal.m_pGoalEntity) ? goal.m_pGoalEntity->MyNPCPointer() : NULL; + + bool bHandled = false; + if( pTargetNpc ) + { + bHandled = !CommanderExecuteOne( pTargetNpc, goal, Allies.Base(), Allies.Count() ); + } + + for ( i = 0; !bHandled && i < Allies.Count(); i++ ) + { + if ( Allies[i] != pTargetNpc && Allies[i]->IsPlayerAlly() ) + { + bHandled = !CommanderExecuteOne( Allies[i], goal, Allies.Base(), Allies.Count() ); + } + } + + //CommanderExecute( CC_SEND ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::InputEnableGeigerCounter( inputdata_t &inputdata ) +{ + RemoveSpawnFlags(SF_PLAYER_NO_GEIGER); +} + +void CHL2_Player::InputDisableGeigerCounter( inputdata_t &inputdata ) +{ + AddSpawnFlags(SF_PLAYER_NO_GEIGER); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::InputShowSquadHUD( inputdata_t &inputdata ) +{ + RemoveSpawnFlags(SF_PLAYER_HIDE_SQUAD_HUD); +} + +void CHL2_Player::InputHideSquadHUD( inputdata_t &inputdata ) +{ + AddSpawnFlags(SF_PLAYER_HIDE_SQUAD_HUD); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : iImpulse - @@ -2239,6 +2836,72 @@ void CHL2_Player::InputIgnoreFallDamageWithoutReset( inputdata_t &inputdata ) m_bIgnoreFallDamageResetAfterImpact = false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputAddArmor( inputdata_t &inputdata ) +{ + int iArmor = MIN((GetPlayerProxy() ? GetPlayerProxy()->m_MaxArmor : 100) - ArmorValue(), inputdata.value.Int()); + + IncrementArmorValue( iArmor ); +} + +//----------------------------------------------------------------------------- +// This can also add to the player's armor by passing a negative input +// which can bypass the maximum armor set in the player proxy. +//----------------------------------------------------------------------------- +void CHL2_Player::InputRemoveArmor( inputdata_t &inputdata ) +{ + int iArmor = MIN(ArmorValue(), inputdata.value.Int()); + + IncrementArmorValue( -iArmor ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputSetArmor( inputdata_t &inputdata ) +{ + int iArmor = MIN(GetPlayerProxy() ? GetPlayerProxy()->m_MaxArmor : 100, inputdata.value.Int()); + + SetArmorValue( iArmor ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputAddAuxPower( inputdata_t &inputdata ) +{ + SuitPower_Charge( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputRemoveAuxPower( inputdata_t &inputdata ) +{ + SuitPower_Drain( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputSetAuxPower( inputdata_t &inputdata ) +{ + SuitPower_SetCharge( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputTurnFlashlightOn( inputdata_t &inputdata ) +{ + FlashlightTurnOn(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputTurnFlashlightOff( inputdata_t &inputdata ) +{ + FlashlightTurnOff(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Notification of a player's npc ally in the players squad being killed //----------------------------------------------------------------------------- @@ -2249,6 +2912,10 @@ void CHL2_Player::OnSquadMemberKilled( inputdata_t &data ) user.MakeReliable(); UserMessageBegin( user, "SquadMemberDied" ); MessageEnd(); + +#ifdef MAPBASE + FirePlayerProxyOutput("OnSquadMemberKilled", data.value, data.pActivator, data.value.Entity()); +#endif } //----------------------------------------------------------------------------- @@ -2350,6 +3017,10 @@ int CHL2_Player::OnTakeDamage( const CTakeDamageInfo &info ) gamestats->Event_PlayerDamage( this, info ); +#ifdef MAPBASE + FirePlayerProxyOutput("PlayerDamaged", variant_t(), info.GetAttacker(), this); +#endif + return BaseClass::OnTakeDamage( playerDamage ); } @@ -2463,7 +3134,19 @@ void CHL2_Player::Event_Killed( const CTakeDamageInfo &info ) { BaseClass::Event_Killed( info ); +#ifdef MAPBASE + FirePlayerProxyOutput( "PlayerDied", variant_t(), info.GetAttacker(), this ); + + if (IsSuitEquipped()) + { + // Make sure all devices are deactivated (for respawn) + m_HL2Local.m_bitsActiveDevices = 0x00000000; + m_flSuitPowerLoad = 0; + m_flTimeAllSuitDevicesOff = gpGlobals->curtime; + } +#else FirePlayerProxyOutput( "PlayerDied", variant_t(), this, this ); +#endif NotifyScriptsOfDeath(); } @@ -2577,6 +3260,15 @@ bool CHL2_Player::ShouldKeepLockedAutoaimTarget( EHANDLE hLockedTarget ) return true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::CanAutoSwitchToNextBestWeapon( CBaseCombatWeapon *pWeapon ) +{ + return player_autoswitch_enabled.GetBool(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : iCount - @@ -2618,6 +3310,9 @@ int CHL2_Player::GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound) if ( pWeapon && pWeapon->GetPrimaryAmmoType() == nAmmoIndex ) { +#ifdef MAPBASE + if (CanAutoSwitchToNextBestWeapon(pWeapon)) +#endif SwitchToNextBestWeapon(GetActiveWeapon()); } } @@ -2630,12 +3325,45 @@ int CHL2_Player::GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound) bool CHL2_Player::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { #ifndef HL2MP +#ifdef MAPBASE + if ( pWeapon->ClassMatches( "weapon_stunstick" ) ) + { + switch (HL2GameRules()->GetStunstickPickupBehavior()) + { + // Default, including 0 + default: + { + if ( ApplyBattery( 0.5 ) ) + UTIL_Remove( pWeapon ); + return false; + } break; + + // Allow pickup, if already picked up just apply battery + case 1: + { + if ( Weapon_OwnsThisType("weapon_stunstick") ) + { + if ( ApplyBattery( 0.5 ) ) + UTIL_Remove( pWeapon ); + return false; + } + } break; + + // Don't pickup, don't even apply battery + case 2: return false; + + // Just pickup, never apply battery + case 3: break; + } + } +#else if ( pWeapon->ClassMatches( "weapon_stunstick" ) ) { if ( ApplyBattery( 0.5 ) ) UTIL_Remove( pWeapon ); return false; } +#endif #endif return BaseClass::Weapon_CanUse( pWeapon ); @@ -3154,6 +3882,11 @@ float CHL2_Player::GetHeldObjectMass( IPhysicsObject *pHeldObject ) return mass; } +CBaseEntity *CHL2_Player::GetHeldObject( void ) +{ + return PhysCannonGetHeldEntity( GetActiveWeapon() ); +} + //----------------------------------------------------------------------------- // Purpose: Force the player to drop any physics objects he's carrying //----------------------------------------------------------------------------- @@ -3666,6 +4399,64 @@ void CHL2_Player::DisplayLadderHudHint() #endif//CLIENT_DLL } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CHL2_Player::InitCustomSuitDevice( int iDeviceID, float flDrainRate ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("InitCustomSuitDevice : \"%i\" is not a valid custom device slot\n", iDeviceID); + return; + } + + SuitDeviceCustom[iDeviceID].SetDeviceDrainRate( flDrainRate ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CHL2_Player::AddCustomSuitDevice( int iDeviceID ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("AddCustomSuitDevice : \"%i\" is not a valid custom device slot\n", iDeviceID); + return; + } + + SuitPower_AddDevice( SuitDeviceCustom[iDeviceID] ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CHL2_Player::RemoveCustomSuitDevice( int iDeviceID ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("AddCustomSuitDevice : \"%i\" is not a valid custom device slot\n", iDeviceID); + return; + } + + SuitPower_RemoveDevice( SuitDeviceCustom[iDeviceID] ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CHL2_Player::IsCustomSuitDeviceActive( int iDeviceID ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("IsCustomSuitDeviceActive : \"%i\" is not a valid custom device slot\n", iDeviceID); + return false; + } + + return SuitPower_IsDeviceActive( SuitDeviceCustom[iDeviceID] ); +} +#endif + //----------------------------------------------------------------------------- // Shuts down sounds //----------------------------------------------------------------------------- @@ -3697,6 +4488,22 @@ void CHL2_Player::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) } } +#ifdef MAPBASE +const char *CHL2_Player::GetOverrideStepSound( const char *pszBaseStepSoundName ) +{ + int idx = FindContextByName("footsteps"); + if (idx != -1) + { + const char *szSound = GetContextValue(idx); + if (szSound[0] != '\0') + { + return szSound; + } + } + return pszBaseStepSoundName; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -3751,6 +4558,9 @@ CLogicPlayerProxy *CHL2_Player::GetPlayerProxy( void ) pProxy->m_hPlayer = this; m_hPlayerProxy = pProxy; +#ifdef MAPBASE + pProxy->NotifyPlayerHasProxy(); +#endif } return pProxy; @@ -3774,6 +4584,15 @@ BEGIN_DATADESC( CLogicPlayerProxy ) DEFINE_OUTPUT( m_PlayerHasNoAmmo, "PlayerHasNoAmmo" ), DEFINE_OUTPUT( m_PlayerDied, "PlayerDied" ), DEFINE_OUTPUT( m_PlayerMissedAR2AltFire, "PlayerMissedAR2AltFire" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_PlayerDamaged, "PlayerDamaged" ), + DEFINE_OUTPUT( m_OnSquadMemberKilled, "OnSquadMemberKilled" ), + DEFINE_OUTPUT( m_OnGetAmmo, "OnGetAmmo" ), + DEFINE_OUTPUT( m_RequestedPlayerArmor, "PlayerArmor" ), + DEFINE_OUTPUT( m_RequestedPlayerAuxPower, "PlayerAuxPower" ), + DEFINE_OUTPUT( m_RequestedPlayerFlashBattery, "PlayerFlashBattery" ), + DEFINE_OUTPUT( m_OnPlayerSpawn, "OnPlayerSpawn" ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerHealth", InputRequestPlayerHealth ), DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightSlowDrain", InputSetFlashlightSlowDrain ), DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightNormalDrain", InputSetFlashlightNormalDrain ), @@ -3786,6 +4605,17 @@ BEGIN_DATADESC( CLogicPlayerProxy ) #ifdef PORTAL DEFINE_INPUTFUNC( FIELD_VOID, "SuppressCrosshair", InputSuppressCrosshair ), #endif // PORTAL +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerArmor", InputRequestPlayerArmor ), + DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerAuxPower", InputRequestPlayerAuxPower ), + DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerFlashBattery", InputRequestPlayerFlashBattery ), + DEFINE_INPUTFUNC( FIELD_STRING, "GetAmmoOnWeapon", InputGetAmmoOnWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetHandModel", InputSetHandModel ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelSkin", InputSetHandModelSkin ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerModel", InputSetPlayerModel ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetPlayerDrawExternally", InputSetPlayerDrawExternally ), + DEFINE_INPUT( m_MaxArmor, FIELD_INTEGER, "SetMaxInputArmor" ), +#endif DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), END_DATADESC() @@ -3799,12 +4629,150 @@ void CLogicPlayerProxy::Activate( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CLogicPlayerProxy::KeyValue( const char *szKeyName, const char *szValue ) +{ + bool bPlayerKV = false; + + if (Q_strnicmp(szKeyName, "HandsVM", 7) == 0) + { + if (m_hPlayer) + { + szKeyName += 7; + CBasePlayer *pPlayer = static_cast( m_hPlayer.Get() ); + CBaseViewModel *vm = pPlayer->GetViewModel(1); + if (vm) + { + if (*szKeyName == NULL && PrecacheModel(szValue)) // HandsVM + vm->SetModel(szValue); + else if (FStrEq(szKeyName, "Skin")) // HandsVMSkin + vm->m_nSkin = atoi(szValue); + } + return true; + } + } + else if (FStrEq(szKeyName, "ResponseContext")) + { + bPlayerKV = true; + if (m_hPlayer) + return m_hPlayer->KeyValue(szKeyName, szValue); + } + else if (FStrEq(szKeyName, "HideSquadHUD")) + { + if (m_hPlayer) + { + if (szValue[0] != '0') + m_hPlayer->AddSpawnFlags(SF_PLAYER_HIDE_SQUAD_HUD); + else + m_hPlayer->RemoveSpawnFlags(SF_PLAYER_HIDE_SQUAD_HUD); + return true; + } + } + else if (FStrEq(szKeyName, "PlayerModel")) + { + if (m_hPlayer) + { + if (PrecacheModel( szValue )) + { + m_hPlayer->SetModel( szValue ); + } + return true; + } + } + else + { + if (BaseClass::KeyValue( szKeyName, szValue )) + return true; + + if (m_hPlayer) + { + DevMsg("logic_playerproxy: Passing unhandled keyvalue \"%s, %s\" to player\n", szKeyName, szValue); + return m_hPlayer->KeyValue(szKeyName, szValue); + } + } + + // If we reach this point, player is not available to test unidentified/special KV + // Queue it up + DevMsg("logic_playerproxy: Queueing %s, %s\n", szKeyName, szValue); + m_QueuedKV.Insert(bPlayerKV ? UTIL_VarArgs("&&%s", szKeyName) : szKeyName, AllocPooledString(szValue)); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: calls the appropriate message mapped function in the entity according +// to the fired action. +// Input : char *szInputName - input destination +// *pActivator - entity which initiated this sequence of actions +// *pCaller - entity from which this event is sent +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CLogicPlayerProxy::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ) +{ + bool base = BaseClass::AcceptInput( szInputName, pActivator, pCaller, Value, outputID ); + + if (!base) + { + if (m_hPlayer) + { + DevMsg("logic_playerproxy: Passing unhandled input \"%s\" to player\n", szInputName); + return m_hPlayer->AcceptInput( szInputName, pActivator, pCaller, Value, outputID ); + } + else + { + DevMsg("logic_playerproxy: Player not found!\n"); + + g_EventQueue.AddEvent("!player", szInputName, Value, 0.01f, pActivator, pCaller); + } + } + + return base; +} + +//----------------------------------------------------------------------------- +// Purpose: Notifies logic_playerproxy when player is valid +//----------------------------------------------------------------------------- +void CLogicPlayerProxy::NotifyPlayerHasProxy() +{ + Assert( m_hPlayer != NULL ); + + // Handle any queued keyvalues + int iQueueCount = m_QueuedKV.Count(); + for (int i = 0; i < iQueueCount; i++) + { + const char *name = m_QueuedKV.GetElementName(i); + const char *value = STRING(m_QueuedKV[i]); + DevMsg("logic_playerproxy: Handing over %s, %s from dict\n", name, value); + + if (name[0] == '&' && name[1] == '&') + { + // We're supposed to send this to the player + m_hPlayer->KeyValue(name + 2, value); + } + + KeyValue(name, value); + } + + m_QueuedKV.RemoveAll(); +} +#endif + bool CLogicPlayerProxy::PassesDamageFilter( const CTakeDamageInfo &info ) { if (m_hDamageFilter) { CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get()); +#ifdef MAPBASE + return pFilter->PassesDamageFilter(m_hPlayer.Get(), info); +#else return pFilter->PassesDamageFilter(info); +#endif } return true; @@ -3827,6 +4795,43 @@ void CLogicPlayerProxy::InputRequestPlayerHealth( inputdata_t &inputdata ) m_RequestedPlayerHealth.Set( m_hPlayer->GetHealth(), inputdata.pActivator, inputdata.pCaller ); } +#ifdef MAPBASE +void CLogicPlayerProxy::InputRequestPlayerArmor( inputdata_t &inputdata ) +{ + if ( m_hPlayer == NULL ) + return; + + m_RequestedPlayerArmor.Set( static_cast(m_hPlayer.Get())->ArmorValue(), inputdata.pActivator, inputdata.pCaller ); +} + +void CLogicPlayerProxy::InputRequestPlayerAuxPower( inputdata_t &inputdata ) +{ + if ( m_hPlayer == NULL ) + return; + + m_RequestedPlayerAuxPower.Set( static_cast(m_hPlayer.Get())->SuitPower_GetCurrentPercentage(), inputdata.pActivator, inputdata.pCaller ); +} + +void CLogicPlayerProxy::InputRequestPlayerFlashBattery( inputdata_t &inputdata ) +{ + if ( m_hPlayer == NULL ) + return; + + m_RequestedPlayerFlashBattery.Set( static_cast(m_hPlayer.Get())->GetFlashlightBattery(), inputdata.pActivator, inputdata.pCaller ); +} + +// If it's the EP2 flashlight, it returns the flashlight battery. If it's the legacy flashlight, it returns the aux power. +// Note that this is on CHL2_Player, not CLogicPlayerProxy. +float CHL2_Player::GetFlashlightBattery() +{ +#ifdef HL2_EPISODIC + return Flashlight_UseLegacyVersion() ? SuitPower_GetCurrentPercentage() : m_HL2Local.m_flFlashBattery; +#else + return SuitPower_GetCurrentPercentage(); +#endif +} +#endif + void CLogicPlayerProxy::InputSetFlashlightSlowDrain( inputdata_t &inputdata ) { if( m_hPlayer == NULL ) @@ -3873,6 +4878,99 @@ void CLogicPlayerProxy::InputRequestAmmoState( inputdata_t &inputdata ) m_PlayerHasNoAmmo.FireOutput( this, this, 0 ); } +#ifdef MAPBASE +void CLogicPlayerProxy::InputGetAmmoOnWeapon( inputdata_t &inputdata ) +{ + if( m_hPlayer == NULL ) + return; + + CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); + + const char *szClass = inputdata.value.String(); + + // Support secondary cases + bool bAmmo2 = szClass[0] == '@'; + if (bAmmo2) + szClass++; + + bool bClipOnly = szClass[0] == '#'; + if (bClipOnly) + szClass++; + + if (szClass[0] != NULL) + { + // Find weapon that matches class + for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i ) + { + CBaseCombatWeapon* pCheck = pPlayer->GetWeapon( i ); + + if ( pCheck && FClassnameIs(pCheck, szClass) ) + { + int ammo = 0; + if (!bAmmo2) + { + // Ammo 1 + if (!bClipOnly) + ammo = pPlayer->GetAmmoCount(pCheck->GetPrimaryAmmoType()); + + if (pCheck->UsesClipsForAmmo1()) + ammo += pCheck->m_iClip1; + else + ammo += pCheck->GetPrimaryAmmoCount(); + } + else + { + // Ammo 2 + if (!bClipOnly) + ammo = pPlayer->GetAmmoCount(pCheck->GetSecondaryAmmoType()); + + if (pCheck->UsesClipsForAmmo2()) + ammo += pCheck->m_iClip2; + else + ammo += pCheck->GetSecondaryAmmoCount(); + } + + m_OnGetAmmo.Set( ammo, this, 0 ); + return; + } + } + } + else + { + // Get current weapon ammo + if (CBaseCombatWeapon *pCheck = pPlayer->GetActiveWeapon()) + { + int ammo = 0; + if (!bAmmo2) + { + // Ammo 1 + if (!bClipOnly) + ammo = pPlayer->GetAmmoCount(pCheck->GetPrimaryAmmoType()); + + if (pCheck->UsesClipsForAmmo1()) + ammo += pCheck->m_iClip1; + else + ammo += pCheck->GetPrimaryAmmoCount(); + } + else + { + // Ammo 2 + if (!bClipOnly) + ammo = pPlayer->GetAmmoCount(pCheck->GetSecondaryAmmoType()); + + if (pCheck->UsesClipsForAmmo2()) + ammo += pCheck->m_iClip2; + else + ammo += pCheck->GetSecondaryAmmoCount(); + } + + m_OnGetAmmo.Set( ammo, this, 0 ); + return; + } + } +} +#endif + void CLogicPlayerProxy::InputLowerWeapon( inputdata_t &inputdata ) { if( m_hPlayer == NULL ) @@ -3928,3 +5026,62 @@ void CLogicPlayerProxy::InputSuppressCrosshair( inputdata_t &inputdata ) pPlayer->SuppressCrosshair( true ); } #endif // PORTAL + +#ifdef MAPBASE +void CLogicPlayerProxy::InputSetHandModel( inputdata_t &inputdata ) +{ + if (!m_hPlayer) + return; + + string_t iszModel = inputdata.value.StringID(); + + if (iszModel != NULL_STRING) + PrecacheModel(STRING(iszModel)); + + CBasePlayer *pPlayer = static_cast( m_hPlayer.Get() ); + CBaseViewModel *vm = pPlayer->GetViewModel(1); + if (vm) + vm->SetModel(STRING(iszModel)); +} + +void CLogicPlayerProxy::InputSetHandModelSkin( inputdata_t &inputdata ) +{ + if (!m_hPlayer) + return; + + CBasePlayer *pPlayer = static_cast( m_hPlayer.Get() ); + CBaseViewModel *vm = pPlayer->GetViewModel(1); + if (vm) + vm->m_nSkin = inputdata.value.Int(); +} + +void CLogicPlayerProxy::InputSetPlayerModel( inputdata_t &inputdata ) +{ + if (!m_hPlayer) + return; + + string_t iszModel = inputdata.value.StringID(); + + if (iszModel != NULL_STRING) + PrecacheModel( STRING( iszModel ) ); + else + { + // We're resetting the model. The original model should've been cached to our own model name. + iszModel = GetModelName(); + } + + // Cache the original model as our own model name. + SetModelName( m_hPlayer->GetModelName() ); + + m_hPlayer->SetModel( STRING(iszModel) ); +} + +void CLogicPlayerProxy::InputSetPlayerDrawExternally( inputdata_t &inputdata ) +{ + if (!m_hPlayer) + return; + + CBasePlayer *pPlayer = static_cast(m_hPlayer.Get()); + pPlayer->m_bDrawPlayerModelExternally = inputdata.value.Bool(); +} +#endif diff --git a/mp/src/game/server/hl2/hl2_player.h b/mp/src/game/server/hl2/hl2_player.h index 6e72bb9e..84ae23b9 100644 --- a/mp/src/game/server/hl2/hl2_player.h +++ b/mp/src/game/server/hl2/hl2_player.h @@ -15,6 +15,11 @@ #include "simtimer.h" #include "soundenvelope.h" +// In HL2MP we need to inherit from BaseMultiplayerPlayer! +#if defined ( HL2MP ) +#include "basemultiplayerplayer.h" +#endif + class CAI_Squad; class CPropCombineBall; @@ -70,15 +75,29 @@ public: else return m_flDrainRate; } +#ifdef MAPBASE + void SetDeviceDrainRate( float flDrainRate ) { m_flDrainRate = flDrainRate; } +#endif }; //============================================================================= // >> HL2_PLAYER //============================================================================= +#if defined ( HL2MP ) +class CHL2_Player : public CBaseMultiplayerPlayer +#else class CHL2_Player : public CBasePlayer +#endif { public: +#if defined ( HL2MP ) + DECLARE_CLASS( CHL2_Player, CBaseMultiplayerPlayer ); +#else DECLARE_CLASS( CHL2_Player, CBasePlayer ); +#endif +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif CHL2_Player(); ~CHL2_Player( void ); @@ -107,6 +126,16 @@ public: virtual void Splash( void ); virtual void ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ); +#ifdef MAPBASE + // For the logic_playerproxy output + void SpawnedAtPoint( CBaseEntity *pSpawnPoint ); + + void ResetAnimation( void ); + void SetAnimation( PLAYER_ANIM playerAnim ); + + virtual const char *GetOverrideStepSound( const char *pszBaseStepSoundName ); +#endif + void DrawDebugGeometryOverlays(void); virtual Vector EyeDirection2D( void ); @@ -139,6 +168,11 @@ public: void SetFlashlightEnabled( bool bState ); +#ifdef MAPBASE + // Needed for logic_playerproxy + float GetFlashlightBattery(); +#endif + // Apply a battery bool ApplyBattery( float powerMultiplier = 1.0 ); @@ -159,6 +193,17 @@ public: int GetNumSquadCommandables(); int GetNumSquadCommandableMedics(); +#ifdef MAPBASE + void InputSquadForceSummon( inputdata_t &inputdata ); + void InputSquadForceGoTo( inputdata_t &inputdata ); + + void InputEnableGeigerCounter( inputdata_t &inputdata ); + void InputDisableGeigerCounter( inputdata_t &inputdata ); + + void InputShowSquadHUD( inputdata_t &inputdata ); + void InputHideSquadHUD( inputdata_t &inputdata ); +#endif + // Locator void UpdateLocatorPosition( const Vector &vecPosition ); @@ -195,6 +240,19 @@ public: void InputEnableFlashlight( inputdata_t &inputdata ); void InputDisableFlashlight( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputAddArmor( inputdata_t &inputdata ); + void InputRemoveArmor( inputdata_t &inputdata ); + void InputSetArmor( inputdata_t &inputdata ); + + void InputAddAuxPower( inputdata_t &inputdata ); + void InputRemoveAuxPower( inputdata_t &inputdata ); + void InputSetAuxPower( inputdata_t &inputdata ); + + void InputTurnFlashlightOn( inputdata_t &inputdata ); + void InputTurnFlashlightOff( inputdata_t &inputdata ); +#endif + const impactdamagetable_t &GetPhysicsImpactDamageTable(); virtual int OnTakeDamage( const CTakeDamageInfo &info ); virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info ); @@ -210,6 +268,10 @@ public: void SetLocatorTargetEntity( CBaseEntity *pEntity ) { m_hLocatorTargetEntity.Set( pEntity ); } +#ifdef MAPBASE + virtual bool CanAutoSwitchToNextBestWeapon( CBaseCombatWeapon *pWeapon ); +#endif + virtual int GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound); virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon ); @@ -241,6 +303,7 @@ public: virtual bool IsHoldingEntity( CBaseEntity *pEnt ); virtual void ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldindThis ); virtual float GetHeldObjectMass( IPhysicsObject *pHeldObject ); + virtual CBaseEntity *CHL2_Player::GetHeldObject( void ); virtual bool IsFollowingPhysics( void ) { return (m_afPhysicsFlags & PFLAG_ONBARNACLE) > 0; } void InputForceDropPhysObjects( inputdata_t &data ); @@ -281,6 +344,13 @@ public: // HUD HINTS void DisplayLadderHudHint(); +#ifdef MAPBASE + void InitCustomSuitDevice( int iDeviceID, float flDrainRate ); + void AddCustomSuitDevice( int iDeviceID ); + void RemoveCustomSuitDevice( int iDeviceID ); + bool IsCustomSuitDeviceActive( int iDeviceID ); +#endif + CSoundPatch *m_sndLeeches; CSoundPatch *m_sndWaterSplashes; diff --git a/mp/src/game/server/hl2/hl2_triggers.cpp b/mp/src/game/server/hl2/hl2_triggers.cpp index 55f718fe..9aeee1a9 100644 --- a/mp/src/game/server/hl2/hl2_triggers.cpp +++ b/mp/src/game/server/hl2/hl2_triggers.cpp @@ -589,6 +589,10 @@ class CTriggerWateryDeath : public CBaseTrigger public: DECLARE_DATADESC(); +#ifdef MAPBASE + CTriggerWateryDeath(); +#endif + void Spawn( void ); void Precache( void ); void Touch( CBaseEntity *pOther ); @@ -614,6 +618,13 @@ private: CUtlVector< float > m_flEntityKillTimes; float m_flNextPullSound; float m_flPainValue; + +#ifdef MAPBASE + float m_flBiteInterval; + float m_flPainStep; + float m_flMaxPain; + COutputInt m_OnDamage; +#endif }; BEGIN_DATADESC( CTriggerWateryDeath ) @@ -621,6 +632,12 @@ BEGIN_DATADESC( CTriggerWateryDeath ) DEFINE_UTLVECTOR( m_hLeeches, FIELD_EHANDLE ), DEFINE_FIELD( m_flNextPullSound, FIELD_TIME ), DEFINE_FIELD( m_flPainValue, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flBiteInterval, FIELD_FLOAT, "BiteInterval" ), + DEFINE_KEYFIELD( m_flPainStep, FIELD_FLOAT, "PainStep" ), + DEFINE_KEYFIELD( m_flMaxPain, FIELD_FLOAT, "MaxPain" ), + DEFINE_OUTPUT( m_OnDamage, "OnDamage" ), +#endif END_DATADESC() @@ -631,6 +648,15 @@ LINK_ENTITY_TO_CLASS( trigger_waterydeath, CTriggerWateryDeath ); #define WD_PAINVALUE_STEP 2.0 #define WD_MAX_DAMAGE 15.0f +#ifdef MAPBASE +CTriggerWateryDeath::CTriggerWateryDeath() +{ + m_flBiteInterval = WD_KILLTIME_NEXT_BITE; + m_flPainStep = WD_PAINVALUE_STEP; + m_flMaxPain = WD_MAX_DAMAGE; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Called when spawning, after keyvalues have been handled. //----------------------------------------------------------------------------- @@ -707,6 +733,23 @@ void CTriggerWateryDeath::Touch( CBaseEntity *pOther ) { //EmitSound( filter, entindex(), "WateryDeath.Bite", &pOther->GetAbsOrigin() ); // Kill it +#ifdef MAPBASE + if ( pOther->IsPlayer() ) + { + m_flPainValue = MIN( m_flPainValue + m_flPainStep, m_flMaxPain ); + } + else + { + m_flPainValue = m_flMaxPain; + } + + // Do nothing if there is no damage + if (m_flPainValue <= 0.0f) + { + m_flEntityKillTimes[iIndex] = gpGlobals->curtime + m_flBiteInterval; + return; + } +#else if ( pOther->IsPlayer() ) { m_flPainValue = MIN( m_flPainValue + WD_PAINVALUE_STEP, WD_MAX_DAMAGE ); @@ -715,6 +758,7 @@ void CTriggerWateryDeath::Touch( CBaseEntity *pOther ) { m_flPainValue = WD_MAX_DAMAGE; } +#endif // Use DMG_GENERIC & make the target inflict the damage on himself. // This ensures that if the target is the player, the damage isn't modified by skill @@ -723,7 +767,13 @@ void CTriggerWateryDeath::Touch( CBaseEntity *pOther ) GuessDamageForce( &info, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() ); pOther->TakeDamage( info ); +#ifdef MAPBASE + m_OnDamage.Set(m_flPainValue, pOther, this); + + m_flEntityKillTimes[iIndex] = gpGlobals->curtime + m_flBiteInterval; +#else m_flEntityKillTimes[iIndex] = gpGlobals->curtime + WD_KILLTIME_NEXT_BITE; +#endif } } diff --git a/mp/src/game/server/hl2/item_ammo.cpp b/mp/src/game/server/hl2/item_ammo.cpp index 6b03c3e7..fa54ad1f 100644 --- a/mp/src/game/server/hl2/item_ammo.cpp +++ b/mp/src/game/server/hl2/item_ammo.cpp @@ -15,6 +15,64 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +// ======================================================================== +// >> CItemAmmo +// +// All ammo items now derive from this for multiplier purposes. +// ======================================================================== +class CItemAmmo : public CItem +{ +public: + DECLARE_CLASS( CItemAmmo, CItem ); + DECLARE_DATADESC(); + + int ITEM_GiveAmmo( CBasePlayer *pPlayer, float flCount, const char *pszAmmoName, bool bSuppressSound = false ) + { + int iAmmoType = GetAmmoDef()->Index(pszAmmoName); + if (iAmmoType == -1) + { + Msg("ERROR: Attempting to give unknown ammo type (%s)\n",pszAmmoName); + return 0; + } + + flCount *= g_pGameRules->GetAmmoQuantityScale(iAmmoType); + + // Don't give out less than 1 of anything. + flCount = MAX( 1.0f, flCount ); + + // Mapper-specific ammo multiplier. + // If it results in 0, the ammo will simply be ignored. + // If the ammo multiplier is negative, assume it's actually a direct number to override with. + if (m_flAmmoMultiplier != 1.0f) + { + if (m_flAmmoMultiplier >= 0) + flCount *= m_flAmmoMultiplier; + else + flCount = -m_flAmmoMultiplier; + } + + return pPlayer->GiveAmmo( flCount, iAmmoType, bSuppressSound ); + } + + void InputSetAmmoMultiplier( inputdata_t &inputdata ) { m_flAmmoMultiplier = inputdata.value.Float(); } + + float m_flAmmoMultiplier = 1.0f; +}; + +BEGIN_DATADESC( CItemAmmo ) + + DEFINE_KEYFIELD( m_flAmmoMultiplier, FIELD_FLOAT, "AmmoMultiplier" ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAmmoMultiplier", InputSetAmmoMultiplier ), + +END_DATADESC() + +// Almost all instances of CItem below are for declaring the base class, which is now CItemAmmo. +// This is here so we don't have to #ifdef all of them. +#define CItem CItemAmmo + +#else //--------------------------------------------------------- // Applies ammo quantity scale. //--------------------------------------------------------- @@ -34,6 +92,7 @@ int ITEM_GiveAmmo( CBasePlayer *pPlayer, float flCount, const char *pszAmmoName, return pPlayer->GiveAmmo( flCount, iAmmoType, bSuppressSound ); } +#endif // ======================================================================== // >> BoxSRounds @@ -606,6 +665,10 @@ enum AMMOCRATE_CROSSBOW, AMMOCRATE_AR2_ALTFIRE, AMMOCRATE_SMG_ALTFIRE, +#ifdef MAPBASE + AMMOCRATE_SLAM, + AMMOCRATE_EMPTY, +#endif NUM_AMMO_CRATE_TYPES, }; @@ -648,6 +711,10 @@ protected: COutputEvent m_OnUsed; CHandle< CBasePlayer > m_hActivator; +#ifdef MAPBASE + COutputEvent m_OnAmmoTaken; +#endif + DECLARE_DATADESC(); }; @@ -668,6 +735,10 @@ BEGIN_DATADESC( CItem_AmmoCrate ) DEFINE_OUTPUT( m_OnUsed, "OnUsed" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnAmmoTaken, "OnAmmoTaken" ), +#endif + DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ), DEFINE_THINKFUNC( CrateThink ), @@ -687,12 +758,22 @@ const char *CItem_AmmoCrate::m_lpzModelNames[NUM_AMMO_CRATE_TYPES] = "models/items/ammocrate_rockets.mdl", // RPG rounds "models/items/ammocrate_buckshot.mdl", // Buckshot "models/items/ammocrate_grenade.mdl", // Grenades +#ifdef MAPBASE + "models/items/ammocrate_357.mdl", // 357 + "models/items/ammocrate_xbow.mdl", // Crossbow + "models/items/ammocrate_ar2alt.mdl", // Combine Ball +#else "models/items/ammocrate_smg1.mdl", // 357 "models/items/ammocrate_smg1.mdl", // Crossbow - + //FIXME: This model is incorrect! "models/items/ammocrate_ar2.mdl", // Combine Ball +#endif "models/items/ammocrate_smg2.mdl", // smg grenade +#ifdef MAPBASE + "models/items/ammocrate_slam.mdl", // slam + "models/items/ammocrate_empty.mdl", // empty +#endif }; // Ammo type names @@ -708,6 +789,10 @@ const char *CItem_AmmoCrate::m_lpzAmmoNames[NUM_AMMO_CRATE_TYPES] = "XBowBolt", "AR2AltFire", "SMG1_Grenade", +#ifdef MAPBASE + "slam", + NULL, +#endif }; // Ammo amount given per +use @@ -723,6 +808,10 @@ int CItem_AmmoCrate::m_nAmmoAmounts[NUM_AMMO_CRATE_TYPES] = 50, // Crossbow 3, // AR2 alt-fire 5, +#ifdef MAPBASE + 5, // SLAM + NULL, // Empty +#endif }; const char *CItem_AmmoCrate::m_pGiveWeapon[NUM_AMMO_CRATE_TYPES] = @@ -737,6 +826,10 @@ const char *CItem_AmmoCrate::m_pGiveWeapon[NUM_AMMO_CRATE_TYPES] = NULL, // Crossbow NULL, // AR2 alt-fire NULL, // SMG alt-fire +#ifdef MAPBASE + "weapon_slam", // SLAM + NULL, // Empty +#endif }; #define AMMO_CRATE_CLOSE_DELAY 1.5f @@ -792,6 +885,10 @@ void CItem_AmmoCrate::Precache( void ) //----------------------------------------------------------------------------- void CItem_AmmoCrate::SetupCrate( void ) { +#ifdef MAPBASE + // Custom models might be desired on, say, empty crates with custom textures + if (GetModelName() == NULL_STRING) +#endif SetModelName( AllocPooledString( m_lpzModelNames[m_nAmmoType] ) ); m_nAmmoIndex = GetAmmoDef()->Index( m_lpzAmmoNames[m_nAmmoType] ); @@ -915,13 +1012,24 @@ void CItem_AmmoCrate::HandleAnimEvent( animevent_t *pEvent ) } else { +#ifdef MAPBASE + m_OnAmmoTaken.FireOutput(m_hActivator, this); +#endif SetBodygroup( 1, false ); } } } +#ifdef MAPBASE + // Empty ammo crates should still fire OnAmmoTaken + if ( m_hActivator->GiveAmmo( m_nAmmoAmounts[m_nAmmoType], m_nAmmoIndex ) != 0 || m_nAmmoType == AMMOCRATE_EMPTY ) +#else if ( m_hActivator->GiveAmmo( m_nAmmoAmounts[m_nAmmoType], m_nAmmoIndex ) != 0 ) +#endif { +#ifdef MAPBASE + m_OnAmmoTaken.FireOutput(m_hActivator, this); +#endif SetBodygroup( 1, false ); } m_hActivator = NULL; @@ -979,6 +1087,13 @@ void CItem_AmmoCrate::CrateThink( void ) //----------------------------------------------------------------------------- void CItem_AmmoCrate::InputKill( inputdata_t &data ) { +#ifdef MAPBASE + // Why is this its own function? + // item_dynamic_resupply and item_item_crate are in the same boat. + // I don't understand. + m_OnKilled.FireOutput( data.pActivator, this ); +#endif + UTIL_Remove( this ); } diff --git a/mp/src/game/server/hl2/item_dynamic_resupply.cpp b/mp/src/game/server/hl2/item_dynamic_resupply.cpp index 6bdcff62..bc0a6c96 100644 --- a/mp/src/game/server/hl2/item_dynamic_resupply.cpp +++ b/mp/src/game/server/hl2/item_dynamic_resupply.cpp @@ -116,6 +116,10 @@ private: float m_flDesiredAmmo[ NUM_AMMO_ITEMS ]; bool m_bIsMaster; + +#ifdef MAPBASE + COutputEHANDLE m_OnItem; +#endif }; LINK_ENTITY_TO_CLASS(item_dynamic_resupply, CItem_DynamicResupply); @@ -153,6 +157,10 @@ BEGIN_DATADESC( CItem_DynamicResupply ) DEFINE_FIELD( m_version, FIELD_INTEGER ), DEFINE_FIELD( m_bIsMaster, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnItem, "OnItem" ), +#endif + // Silence, Classcheck! // DEFINE_ARRAY( m_flDesiredHealth, FIELD_FLOAT, NUM_HEALTH_ITEMS ), // DEFINE_ARRAY( m_flDesiredAmmo, FIELD_FLOAT, NUM_AMMO_ITEMS ), @@ -282,6 +290,11 @@ void CItem_DynamicResupply::CheckPVSThink( void ) //----------------------------------------------------------------------------- void CItem_DynamicResupply::InputKill( inputdata_t &data ) { +#ifdef MAPBASE + // What's the point of this being its own function? + m_OnKilled.FireOutput( data.pActivator, this ); +#endif + UTIL_Remove( this ); } @@ -345,7 +358,12 @@ void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBase // If we're supposed to fallback to just a health vial, do that and finish. if ( pMaster->HasSpawnFlags(SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL) ) { +#ifdef MAPBASE + CBaseEntity *pItem = CBaseEntity::Create("item_healthvial", GetAbsOrigin(), GetAbsAngles(), this); + m_OnItem.Set(pItem, pItem, this); +#else CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles(), this ); +#endif if ( iDebug ) { @@ -364,7 +382,12 @@ void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBase { if ( flChoice <= flRatio[i] ) { +#ifdef MAPBASE + CBaseEntity *pItem = CBaseEntity::Create( g_DynamicResupplyAmmoItems[i].sEntityName, GetAbsOrigin(), GetAbsAngles(), this ); + m_OnItem.Set(pItem, pItem, this); +#else CBaseEntity::Create( g_DynamicResupplyAmmoItems[i].sEntityName, GetAbsOrigin(), GetAbsAngles(), this ); +#endif if ( iDebug ) { @@ -546,6 +569,10 @@ bool CItem_DynamicResupply::SpawnItemFromRatio( int nCount, DynamicResupplyItems pEnt->SetAbsVelocity( GetAbsVelocity() ); pEnt->SetLocalAngularVelocity( GetLocalAngularVelocity() ); +#ifdef MAPBASE + m_OnItem.Set(pEnt, pEnt, this); +#endif + // Move the entity up so that it doesn't go below the spawn origin Vector vecWorldMins, vecWorldMaxs; pEnt->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs ); @@ -654,4 +681,9 @@ void DynamicResupply_InitFromAlternateMaster( CBaseEntity *pTargetEnt, string_t memcpy( pTargetResupply->m_flDesiredHealth, pMasterResupply->m_flDesiredHealth, sizeof( pMasterResupply->m_flDesiredHealth ) ); memcpy( pTargetResupply->m_flDesiredAmmo, pMasterResupply->m_flDesiredAmmo, sizeof( pMasterResupply->m_flDesiredAmmo ) ); +#ifdef MAPBASE + if (pMasterResupply->HasSpawnFlags(SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL)) + pTargetResupply->AddSpawnFlags(SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL); +#endif + } \ No newline at end of file diff --git a/mp/src/game/server/hl2/item_healthkit.cpp b/mp/src/game/server/hl2/item_healthkit.cpp index 9a56ee98..628f873e 100644 --- a/mp/src/game/server/hl2/item_healthkit.cpp +++ b/mp/src/game/server/hl2/item_healthkit.cpp @@ -78,7 +78,7 @@ bool CHealthKit::MyTouch( CBasePlayer *pPlayer ) CPASAttenuationFilter filter( pPlayer, "HealthKit.Touch" ); EmitSound( filter, pPlayer->entindex(), "HealthKit.Touch" ); - if ( g_pGameRules->ItemShouldRespawn( this ) ) + if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES ) { Respawn(); } @@ -131,7 +131,7 @@ public: CPASAttenuationFilter filter( pPlayer, "HealthVial.Touch" ); EmitSound( filter, pPlayer->entindex(), "HealthVial.Touch" ); - if ( g_pGameRules->ItemShouldRespawn( this ) ) + if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES ) { Respawn(); } @@ -432,6 +432,17 @@ public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | m_iCaps; } +#ifdef MAPBASE + void InputRecharge( inputdata_t &inputdata ); + void InputSetCharge( inputdata_t &inputdata ); + void InputSetChargeNoMax( inputdata_t &inputdata ); + void UpdateJuice( int newJuice ); + float MaxJuice() const; + void SetInitialCharge( void ); + int m_iMaxJuice; + int m_iIncrementValue; +#endif + float m_flNextCharge; int m_iReactivate ; // DeathMatch Delay until reactvated int m_iJuice; @@ -442,6 +453,11 @@ public: int m_iCaps; COutputFloat m_OutRemainingHealth; +#ifdef MAPBASE + COutputEvent m_OnHalfEmpty; + COutputEvent m_OnEmpty; + COutputEvent m_OnFull; +#endif COutputEvent m_OnPlayerUse; void StudioFrameAdvance ( void ); @@ -458,12 +474,20 @@ BEGIN_DATADESC( CNewWallHealth ) DEFINE_FIELD( m_flNextCharge, FIELD_TIME), DEFINE_FIELD( m_iReactivate, FIELD_INTEGER), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iJuice, FIELD_INTEGER, "Charge" ), +#else DEFINE_FIELD( m_iJuice, FIELD_INTEGER), +#endif DEFINE_FIELD( m_iOn, FIELD_INTEGER), DEFINE_FIELD( m_flSoundTime, FIELD_TIME), DEFINE_FIELD( m_nState, FIELD_INTEGER ), DEFINE_FIELD( m_iCaps, FIELD_INTEGER ), DEFINE_FIELD( m_flJuice, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iMaxJuice, FIELD_INTEGER, "MaxCharge" ), + DEFINE_INPUT( m_iIncrementValue, FIELD_INTEGER, "SetIncrementValue" ), +#endif // Function Pointers DEFINE_FUNCTION( Off ), @@ -471,6 +495,15 @@ BEGIN_DATADESC( CNewWallHealth ) DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), DEFINE_OUTPUT( m_OutRemainingHealth, "OutRemainingHealth"), +#ifdef MAPBASE + DEFINE_OUTPUT(m_OnHalfEmpty, "OnHalfEmpty" ), + DEFINE_OUTPUT(m_OnEmpty, "OnEmpty" ), + DEFINE_OUTPUT(m_OnFull, "OnFull" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Recharge", InputRecharge ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCharge", InputSetCharge ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetChargeNoMax", InputSetChargeNoMax ), +#endif END_DATADESC() @@ -479,6 +512,10 @@ END_DATADESC() #define CHARGES_PER_SECOND 1.0f / CHARGE_RATE #define CALLS_PER_SECOND 7.0f * CHARGES_PER_SECOND +#ifdef MAPBASE +#define CUSTOM_CHARGES_PER_SECOND(inc) inc / CHARGE_RATE +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -503,6 +540,30 @@ bool CNewWallHealth::KeyValue( const char *szKeyName, const char *szValue ) return(BaseClass::KeyValue( szKeyName, szValue )); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNewWallHealth::SetInitialCharge( void ) +{ + if ( m_iMaxJuice != 0 ) + { + // It must've been overridden by the mapper + return; + } + + m_iMaxJuice = sk_healthcharger.GetFloat(); +} + +//----------------------------------------------------------------------------- +// Max juice for recharger +//----------------------------------------------------------------------------- +float CNewWallHealth::MaxJuice() const +{ + return m_iMaxJuice; +} +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -515,12 +576,34 @@ void CNewWallHealth::Spawn(void) SetSolid( SOLID_VPHYSICS ); CreateVPhysics(); +#ifdef MAPBASE + SetModel( STRING(GetModelName()) ); +#else SetModel( HEALTH_CHARGER_MODEL_NAME ); +#endif AddEffects( EF_NOSHADOW ); ResetSequence( LookupSequence( "idle" ) ); +#ifdef MAPBASE + if (m_iIncrementValue == 0) + m_iIncrementValue = 1; + + SetInitialCharge(); + + // In case the juice was overridden + if (m_iJuice == 0) + UpdateJuice( MaxJuice() ); + else if (m_iJuice == -1) + { + UpdateJuice( 0 ); + ResetSequence( LookupSequence( "empty" ) ); + } + else + UpdateJuice( m_iJuice ); +#else m_iJuice = sk_healthcharger.GetFloat(); +#endif m_nState = 0; @@ -530,7 +613,11 @@ void CNewWallHealth::Spawn(void) CreateVPhysics(); m_flJuice = m_iJuice; +#ifdef MAPBASE + SetCycle( 1.0f - ( m_flJuice / MaxJuice() ) ); +#else SetCycle( 1.0f - ( m_flJuice / sk_healthcharger.GetFloat() ) ); +#endif } int CNewWallHealth::DrawDebugTextOverlays(void) @@ -560,7 +647,14 @@ bool CNewWallHealth::CreateVPhysics(void) //----------------------------------------------------------------------------- void CNewWallHealth::Precache(void) { +#ifdef MAPBASE + if ( GetModelName() == NULL_STRING ) + SetModelName( AllocPooledString(HEALTH_CHARGER_MODEL_NAME) ); + + PrecacheModel( STRING(GetModelName()) ); +#else PrecacheModel( HEALTH_CHARGER_MODEL_NAME ); +#endif PrecacheScriptSound( "WallHealth.Deny" ); PrecacheScriptSound( "WallHealth.Start" ); @@ -572,7 +666,11 @@ void CNewWallHealth::StudioFrameAdvance( void ) { m_flPlaybackRate = 0; +#ifdef MAPBASE + float flMaxJuice = MaxJuice() + 0.1f; +#else float flMaxJuice = sk_healthcharger.GetFloat(); +#endif SetCycle( 1.0f - (float)( m_flJuice / flMaxJuice ) ); // Msg( "Cycle: %f - Juice: %d - m_flJuice :%f - Interval: %f\n", (float)GetCycle(), (int)m_iJuice, (float)m_flJuice, GetAnimTimeInterval() ); @@ -588,6 +686,62 @@ void CNewWallHealth::StudioFrameAdvance( void ) m_flAnimTime = gpGlobals->curtime; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : newJuice - +//----------------------------------------------------------------------------- +void CNewWallHealth::UpdateJuice( int newJuice ) +{ + bool reduced = newJuice < m_iJuice; + if ( reduced ) + { + // Fire 1/2 way output and/or empyt output + int oneHalfJuice = (int)(MaxJuice() * 0.5f); + if ( newJuice <= oneHalfJuice && m_iJuice > oneHalfJuice ) + { + m_OnHalfEmpty.FireOutput( this, this ); + } + + if ( newJuice <= 0 ) + { + m_OnEmpty.FireOutput( this, this ); + } + } + else if ( newJuice != m_iJuice && + newJuice == (int)MaxJuice() ) + { + m_OnFull.FireOutput( this, this ); + } + m_iJuice = newJuice; +} + +void CNewWallHealth::InputRecharge( inputdata_t &inputdata ) +{ + Recharge(); +} + +void CNewWallHealth::InputSetCharge( inputdata_t &inputdata ) +{ + int iJuice = inputdata.value.Int(); + + m_flJuice = m_iMaxJuice = m_iJuice = iJuice; + + ResetSequence( m_iJuice > 0 ? LookupSequence( "idle" ) : LookupSequence( "empty" ) ); + StudioFrameAdvance(); +} + +void CNewWallHealth::InputSetChargeNoMax( inputdata_t &inputdata ) +{ + m_flJuice = inputdata.value.Float(); + + UpdateJuice(m_flJuice); + + ResetSequence( m_iJuice > 0 ? LookupSequence( "idle" ) : LookupSequence( "empty" ) ); + StudioFrameAdvance(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pActivator - @@ -614,6 +768,11 @@ void CNewWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP float flCharges = CHARGES_PER_SECOND; float flCalls = CALLS_PER_SECOND; +#ifdef MAPBASE + if ( m_iIncrementValue != 0 ) + flCharges = CUSTOM_CHARGES_PER_SECOND(m_iIncrementValue); +#endif + m_flJuice -= flCharges / flCalls; StudioFrameAdvance(); } @@ -679,13 +838,24 @@ void CNewWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP } // charge the player +#ifdef MAPBASE + if ( pActivator->TakeHealth( m_iIncrementValue, DMG_GENERIC ) ) + { + UpdateJuice(m_iJuice - m_iIncrementValue); + } +#else if ( pActivator->TakeHealth( 1, DMG_GENERIC ) ) { m_iJuice--; } +#endif // Send the output. +#ifdef MAPBASE + float flRemaining = m_iJuice / MaxJuice(); +#else float flRemaining = m_iJuice / sk_healthcharger.GetFloat(); +#endif m_OutRemainingHealth.Set(flRemaining, pActivator, this); // govern the rate of charge @@ -699,7 +869,12 @@ void CNewWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP void CNewWallHealth::Recharge(void) { EmitSound( "WallHealth.Recharge" ); +#ifdef MAPBASE + UpdateJuice(MaxJuice()); + m_flJuice = m_iJuice; +#else m_flJuice = m_iJuice = sk_healthcharger.GetFloat(); +#endif m_nState = 0; ResetSequence( LookupSequence( "idle" ) ); diff --git a/mp/src/game/server/hl2/item_itemcrate.cpp b/mp/src/game/server/hl2/item_itemcrate.cpp index 20ee2ff7..c1d47364 100644 --- a/mp/src/game/server/hl2/item_itemcrate.cpp +++ b/mp/src/game/server/hl2/item_itemcrate.cpp @@ -8,6 +8,9 @@ #include "props.h" #include "items.h" #include "item_dynamic_resupply.h" +#ifdef MAPBASE +#include "point_template.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -16,6 +19,9 @@ const char *pszItemCrateModelName[] = { "models/items/item_item_crate.mdl", "models/items/item_beacon_crate.mdl", +#ifdef MAPBASE + "models/items/item_item_crate.mdl", // Custom model placeholder/fallback, this should never be selected +#endif }; //----------------------------------------------------------------------------- @@ -39,14 +45,32 @@ public: virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); +#ifdef MAPBASE + void InputSetContents( inputdata_t &data ); + void InputSetItemCount( inputdata_t &data ); + void InputMergeContentsWithPlayer( inputdata_t &data ); + + // Item crates always override prop data for custom models + bool OverridePropdata( void ) { return true; } +#endif + protected: virtual void OnBreak( const Vector &vecVelocity, const AngularImpulse &angVel, CBaseEntity *pBreaker ); +#ifdef MAPBASE + bool ShouldRandomizeAngles( CBaseEntity *pEnt ); + #define ITEM_ITEMCRATE_TEMPLATE_TARGET m_strAlternateMaster + CPointTemplate *FindTemplate(); +#endif + private: // Crate types. Add more! enum CrateType_t { CRATE_SPECIFIC_ITEM = 0, +#ifdef MAPBASE + CRATE_POINT_TEMPLATE, +#endif CRATE_TYPE_COUNT, }; @@ -54,6 +78,9 @@ private: { CRATE_APPEARANCE_DEFAULT = 0, CRATE_APPEARANCE_RADAR_BEACON, +#ifdef MAPBASE + CRATE_APPEARANCE_CUSTOM, +#endif }; private: @@ -64,6 +91,9 @@ private: CrateAppearance_t m_CrateAppearance; COutputEvent m_OnCacheInteraction; +#ifdef MAPBASE + COutputEHANDLE m_OnItem; +#endif }; @@ -82,6 +112,13 @@ BEGIN_DATADESC( CItem_ItemCrate ) DEFINE_KEYFIELD( m_CrateAppearance, FIELD_INTEGER, "CrateAppearance" ), DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ), DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnItem, "OnItem" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetContents", InputSetContents ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetItemCount", InputSetItemCount ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "MergeContentsWithPlayer", InputMergeContentsWithPlayer ), +#endif END_DATADESC() @@ -92,8 +129,17 @@ END_DATADESC() void CItem_ItemCrate::Precache( void ) { // Set this here to quiet base prop warnings +#ifdef MAPBASE + // Set our model name here instead of in Spawn() so we could use custom crates. + if (m_CrateAppearance != CRATE_APPEARANCE_CUSTOM) + SetModelName(AllocPooledString(pszItemCrateModelName[m_CrateAppearance])); + + PrecacheModel( STRING(GetModelName()) ); + SetModel( STRING(GetModelName()) ); +#else PrecacheModel( pszItemCrateModelName[m_CrateAppearance] ); SetModel( pszItemCrateModelName[m_CrateAppearance] ); +#endif BaseClass::Precache(); if ( m_CrateType == CRATE_SPECIFIC_ITEM ) @@ -118,7 +164,9 @@ void CItem_ItemCrate::Spawn( void ) } DisableAutoFade(); +#ifndef MAPBASE SetModelName( AllocPooledString( pszItemCrateModelName[m_CrateAppearance] ) ); +#endif if ( NULL_STRING == m_strItemClass ) { @@ -128,7 +176,11 @@ void CItem_ItemCrate::Spawn( void ) } Precache( ); +#ifdef MAPBASE + SetModel( STRING(GetModelName()) ); +#else SetModel( pszItemCrateModelName[m_CrateAppearance] ); +#endif AddEFlags( EFL_NO_ROTORWASH_PUSH ); BaseClass::Spawn( ); } @@ -140,9 +192,76 @@ void CItem_ItemCrate::Spawn( void ) //----------------------------------------------------------------------------- void CItem_ItemCrate::InputKill( inputdata_t &data ) { +#ifdef MAPBASE + // Why is this its own function anyway? + // It just overwrites the death notice stuff. + m_OnKilled.FireOutput(data.pActivator, this); +#endif + UTIL_Remove( this ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &data - +//----------------------------------------------------------------------------- +void CItem_ItemCrate::InputSetContents( inputdata_t &data ) +{ + switch( m_CrateType ) + { + case CRATE_POINT_TEMPLATE: + ITEM_ITEMCRATE_TEMPLATE_TARGET = data.value.StringID(); + break; + + case CRATE_SPECIFIC_ITEM: + default: + m_strItemClass = data.value.StringID(); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &data - +//----------------------------------------------------------------------------- +void CItem_ItemCrate::InputSetItemCount( inputdata_t &data ) +{ + m_nItemCount = data.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &data - +//----------------------------------------------------------------------------- +void CItem_ItemCrate::InputMergeContentsWithPlayer( inputdata_t &data ) +{ + CBasePlayer *pPlayer = ToBasePlayer(data.value.Entity()); + if (!pPlayer) + pPlayer = UTIL_GetLocalPlayer(); + + if (pPlayer) + { + switch( m_CrateType ) + { + case CRATE_POINT_TEMPLATE: + { + Warning( "%s: item_itemcrate MergeContentsWithPlayer is not supported on template crates yet!!!\n", GetDebugName() ); + } break; + + case CRATE_SPECIFIC_ITEM: + default: + { + for (int i = 0; i < m_nItemCount; i++) + { + pPlayer->GiveNamedItem( STRING( m_strItemClass ) ); + } + } break; + } + } +} +#endif + //----------------------------------------------------------------------------- // Item crates blow up immediately @@ -178,6 +297,34 @@ void CItem_ItemCrate::VPhysicsCollision( int index, gamevcollisionevent_t *pEven } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Finds the template for CRATE_POINT_TEMPLATE. +//----------------------------------------------------------------------------- +inline CPointTemplate *CItem_ItemCrate::FindTemplate() +{ + return dynamic_cast(gEntList.FindEntityByName( NULL, STRING(ITEM_ITEMCRATE_TEMPLATE_TARGET) )); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CItem_ItemCrate::ShouldRandomizeAngles( CBaseEntity *pEnt ) +{ + // Angles probably not supposed to be randomized. + if (m_CrateType == CRATE_POINT_TEMPLATE) + return false; + + // If we have only one NPC, it's probably supposed to spawn correctly. + // (if we have a bunch, it's probably a gag) + if (m_nItemCount == 1 && pEnt->IsNPC()) + return false; + + return true; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -189,7 +336,31 @@ void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse & m_OnCacheInteraction.FireOutput(pBreaker,this); +#ifdef MAPBASE + int iCount = m_nItemCount; + CUtlVector hNewEntities; + CPointTemplate *pTemplate = FindTemplate(); + + if (m_CrateType == CRATE_POINT_TEMPLATE) + { + if (pTemplate && pTemplate->CreateInstance(GetLocalOrigin(), GetLocalAngles(), &hNewEntities)) + { + iCount = hNewEntities.Count() * m_nItemCount; + } + else + { + // This only runs if our template can't be found or its template instancing didn't work. + Warning("item_item_crate %s with CRATE_POINT_TEMPLATE couldn't find point_template %s! Falling back to CRATE_SPECIFIC_ITEM...\n", GetDebugName(), STRING(ITEM_ITEMCRATE_TEMPLATE_TARGET)); + m_CrateType = CRATE_SPECIFIC_ITEM; + } + } +#endif + +#ifdef MAPBASE + for ( int i = 0; i < iCount; i++ ) +#else for ( int i = 0; i < m_nItemCount; ++i ) +#endif { CBaseEntity *pSpawn = NULL; switch( m_CrateType ) @@ -198,6 +369,25 @@ void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse & pSpawn = CreateEntityByName( STRING(m_strItemClass) ); break; +#ifdef MAPBASE + case CRATE_POINT_TEMPLATE: + { + if (i >= hNewEntities.Count()) + { + if (!pTemplate || !pTemplate->CreateInstance(GetLocalOrigin(), GetLocalAngles(), &hNewEntities)) + { + pSpawn = NULL; + i = iCount; + break; + } + + i = 0; + iCount -= hNewEntities.Count(); + } + pSpawn = hNewEntities[i]; + } break; +#endif + default: break; } @@ -205,6 +395,55 @@ void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse & if ( !pSpawn ) return; +#ifdef MAPBASE + Vector vecOrigin; + CollisionProp()->RandomPointInBounds(Vector(0.25, 0.25, 0.25), Vector(0.75, 0.75, 0.75), &vecOrigin); + pSpawn->SetAbsOrigin(vecOrigin); + + if (ShouldRandomizeAngles(pSpawn)) + { + // Give a little randomness... + QAngle vecAngles; + vecAngles.x = random->RandomFloat(-20.0f, 20.0f); + vecAngles.y = random->RandomFloat(0.0f, 360.0f); + vecAngles.z = random->RandomFloat(-20.0f, 20.0f); + pSpawn->SetAbsAngles(vecAngles); + + Vector vecActualVelocity; + vecActualVelocity.Random(-10.0f, 10.0f); + // vecActualVelocity += vecVelocity; + pSpawn->SetAbsVelocity(vecActualVelocity); + + QAngle angVel; + AngularImpulseToQAngle(angImpulse, angVel); + pSpawn->SetLocalAngularVelocity(angVel); + } + else + { + // Only modify the Y value. + QAngle vecAngles; + vecAngles.x = 0; + vecAngles.y = GetLocalAngles().y; + vecAngles.z = 0; + pSpawn->SetAbsAngles(vecAngles); + } + + // We handle dynamic resupplies differently + bool bDynResup = FClassnameIs( pSpawn, "item_dynamic_resupply" ); + if (!bDynResup) + m_OnItem.Set(pSpawn, pSpawn, this); + else if (m_OnItem.NumberOfElements() > 0) + { + // This is here so it could fire OnItem for each item + CEventAction *ourlist = m_OnItem.GetActionList(); + char outputdata[256]; + for (CEventAction *ev = ourlist; ev != NULL; ev = ev->m_pNext) + { + Q_snprintf(outputdata, sizeof(outputdata), "%s,%s,%s,%f,%i", STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), STRING(ev->m_iParameter), ev->m_flDelay, ev->m_nTimesToFire); + pSpawn->KeyValue("OnItem", outputdata); + } + } +#else // Give a little randomness... Vector vecOrigin; CollisionProp()->RandomPointInBounds( Vector(0.25, 0.25, 0.25), Vector( 0.75, 0.75, 0.75 ), &vecOrigin ); @@ -224,6 +463,7 @@ void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse & QAngle angVel; AngularImpulseToQAngle( angImpulse, angVel ); pSpawn->SetLocalAngularVelocity( angVel ); +#endif // If we're creating an item, it can't be picked up until it comes to rest // But only if it wasn't broken by a vehicle @@ -236,7 +476,11 @@ void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse & pSpawn->Spawn(); // Avoid missing items drops by a dynamic resupply because they don't think immediately +#ifdef MAPBASE + if (bDynResup) +#else if ( FClassnameIs( pSpawn, "item_dynamic_resupply" ) ) +#endif { if ( m_strAlternateMaster != NULL_STRING ) { diff --git a/mp/src/game/server/hl2/item_suit.cpp b/mp/src/game/server/hl2/item_suit.cpp index 5441234f..67d327b9 100644 --- a/mp/src/game/server/hl2/item_suit.cpp +++ b/mp/src/game/server/hl2/item_suit.cpp @@ -49,7 +49,11 @@ public: else UTIL_EmitSoundSuit(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon +#ifdef MAPBASE + pPlayer->EquipSuit(!HasSpawnFlags(SF_SUIT_SHORTLOGON)); +#else pPlayer->EquipSuit(); +#endif return true; } diff --git a/mp/src/game/server/hl2/npc_BaseZombie.cpp b/mp/src/game/server/hl2/npc_BaseZombie.cpp index e7e5c6fc..bbe1f9d0 100644 --- a/mp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/mp/src/game/server/hl2/npc_BaseZombie.cpp @@ -205,7 +205,11 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_SOUNDPATCH( m_pMoanSound ), DEFINE_FIELD( m_fIsTorso, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), +#else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_flNextFlinch, FIELD_TIME ), DEFINE_FIELD( m_bHeadShot, FIELD_BOOLEAN ), DEFINE_FIELD( m_flBurnDamage, FIELD_FLOAT ), @@ -220,6 +224,11 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_FIELD( m_hObstructor, FIELD_EHANDLE ), DEFINE_FIELD( m_bIsSlumped, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnSwattedProp, "OnSwattedProp" ), + DEFINE_OUTPUT( m_OnCrab, "OnCrab" ), +#endif + END_DATADESC() @@ -311,6 +320,12 @@ bool CNPC_BaseZombie::FindNearestPhysicsObject( int iMaxMass ) pEntity->VPhysicsGetObject()->IsAsleep() && pEntity->VPhysicsGetObject()->IsMoveable() ) { +#ifdef MAPBASE + // Don't swat props that don't want to be swatted + if (pEntity->HasSpawnFlags(SF_PHYSPROP_NO_ZOMBIE_SWAT) && dynamic_cast(pEntity)) + return ITERATION_CONTINUE; +#endif + return CFlaggedEntitiesEnum::EnumElement( pHandleEntity ); } return ITERATION_CONTINUE; @@ -716,6 +731,11 @@ bool CNPC_BaseZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDa if ( info.GetDamageType() & DMG_REMOVENORAGDOLL ) return false; +#ifdef MAPBASE + if ( HasSpawnFlags(SF_ZOMBIE_NO_TORSO) ) + return false; +#endif + if ( m_fIsTorso ) { // Already split. @@ -761,7 +781,11 @@ bool CNPC_BaseZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDa //----------------------------------------------------------------------------- HeadcrabRelease_t CNPC_BaseZombie::ShouldReleaseHeadcrab( const CTakeDamageInfo &info, float flDamageThreshold ) { +#ifdef MAPBASE + if ( m_iHealth <= 0 && !m_fIsHeadless ) +#else if ( m_iHealth <= 0 ) +#endif { if ( info.GetDamageType() & DMG_REMOVENORAGDOLL ) return RELEASE_NO; @@ -853,7 +877,11 @@ int CNPC_BaseZombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) bool bSquashed = IsSquashed(info); bool bKilledByVehicle = ( ( info.GetDamageType() & DMG_VEHICLE ) != 0 ); +#ifdef MAPBASE + if ( !m_fIsTorso && (bChopped || bSquashed) && !bKilledByVehicle && !(info.GetDamageType() & DMG_REMOVENORAGDOLL) && !HasSpawnFlags(SF_ZOMBIE_NO_TORSO) ) +#else if( !m_fIsTorso && (bChopped || bSquashed) && !bKilledByVehicle && !(info.GetDamageType() & DMG_REMOVENORAGDOLL) ) +#endif { if( bChopped ) { @@ -1062,6 +1090,10 @@ bool CNPC_BaseZombie::ShouldIgniteZombieGib( void ) #endif } +#ifdef MAPBASE +extern CBaseAnimating *CreateServerRagdollSubmodel( CBaseAnimating *pOwner, const char *pModelName, const Vector &position, const QAngle &angles, int collisionGroup ); +#endif + //----------------------------------------------------------------------------- // Purpose: Handle the special case of a zombie killed by a physics chopper. //----------------------------------------------------------------------------- @@ -1082,6 +1114,14 @@ void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info ) } } +#ifdef MAPBASE + // Hack for fast zombies using base torso rules. + if (m_iHealth > 0) + { + SetHealth( 0 ); + } +#endif + float flFadeTime = 0.0; if( HasSpawnFlags( SF_NPC_FADE_CORPSE ) ) @@ -1103,9 +1143,32 @@ void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info ) vecLegsForce.z *= -10; } +#ifdef MAPBASE + CBaseEntity *pLegGib = NULL; + if ( m_bForceServerRagdoll ) + { + pLegGib = CreateServerRagdollSubmodel( this, GetLegsModel(), GetAbsOrigin(), GetAbsAngles(), COLLISION_GROUP_INTERACTIVE_DEBRIS ); + pLegGib->VPhysicsGetObject()->AddVelocity(&vecLegsForce, NULL); + if (ShouldIgniteZombieGib()) + static_cast(pLegGib)->Ignite( random->RandomFloat( 8.0, 12.0 ), false ); + + if ( flFadeTime > 0.0 ) + { + pLegGib->SUB_StartFadeOut( flFadeTime, false ); + } + } + else + pLegGib = CreateRagGib( GetLegsModel(), GetAbsOrigin(), GetAbsAngles(), vecLegsForce, flFadeTime, ShouldIgniteZombieGib() ); +#else CBaseEntity *pLegGib = CreateRagGib( GetLegsModel(), GetAbsOrigin(), GetAbsAngles(), vecLegsForce, flFadeTime, ShouldIgniteZombieGib() ); +#endif if ( pLegGib ) { +#ifdef MAPBASE + // Inherit some misc. properties + pLegGib->m_iViewHideFlags = m_iViewHideFlags; +#endif + CopyRenderColorTo( pLegGib ); } @@ -1122,7 +1185,25 @@ void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info ) QAngle TorsoAngles; TorsoAngles = GetAbsAngles(); TorsoAngles.x -= 90.0f; +#ifdef MAPBASE + CBaseEntity *pTorsoGib = NULL; + if ( m_bForceServerRagdoll ) + { + pTorsoGib = CreateServerRagdollSubmodel( this, GetTorsoModel(), GetAbsOrigin() + Vector( 0, 0, 64 ), TorsoAngles, COLLISION_GROUP_INTERACTIVE_DEBRIS ); + pTorsoGib->VPhysicsGetObject()->AddVelocity(&forceVector, NULL); + if (ShouldIgniteZombieGib()) + static_cast(pLegGib)->Ignite( random->RandomFloat( 8.0, 12.0 ), false ); + + if ( flFadeTime > 0.0 ) + { + pTorsoGib->SUB_StartFadeOut( flFadeTime, false ); + } + } + else + pTorsoGib = CreateRagGib( GetTorsoModel(), GetAbsOrigin() + Vector( 0, 0, 64 ), TorsoAngles, forceVector, flFadeTime, ShouldIgniteZombieGib() ); +#else CBaseEntity *pTorsoGib = CreateRagGib( GetTorsoModel(), GetAbsOrigin() + Vector( 0, 0, 64 ), TorsoAngles, forceVector, flFadeTime, ShouldIgniteZombieGib() ); +#endif if ( pTorsoGib ) { CBaseAnimating *pAnimating = dynamic_cast(pTorsoGib); @@ -1131,6 +1212,11 @@ void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info ) pAnimating->SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless ); } +#ifdef MAPBASE + // Inherit some misc. properties + pTorsoGib->m_iViewHideFlags = m_iViewHideFlags; +#endif + pTorsoGib->SetOwnerEntity( this ); CopyRenderColorTo( pTorsoGib ); @@ -1559,6 +1645,10 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) pPhysObj->AddVelocity( &v, &angVelocity ); +#ifdef MAPBASE + m_OnSwattedProp.Set(pPhysicsEntity, pPhysicsEntity, this); +#endif + // If we don't put the object scan time well into the future, the zombie // will re-select the object he just hit as it is flying away from him. // It will likely always be the nearest object because the zombie moved @@ -1616,7 +1706,7 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) const char *pString = pEvent->options; char token[128]; - pString = nexttoken( token, pString, ' ' ); + pString = nexttoken( token, pString, ' ', sizeof(token) ); int boneIndex = GetInteractionPartner()->LookupBone( token ); @@ -1626,7 +1716,7 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) return; } - pString = nexttoken( token, pString, ' ' ); + pString = nexttoken( token, pString, ' ', sizeof( token ) ); if ( !token ) { @@ -2255,7 +2345,23 @@ void CNPC_BaseZombie::BecomeTorso( const Vector &vecTorsoForce, const Vector &ve if ( m_fIsTorso == true ) { // -40 on Z to make up for the +40 on Z that we did above. This stops legs spawning above the head. +#ifdef MAPBASE + CBaseEntity *pGib = NULL; + if ( m_bForceServerRagdoll ) + { + pGib = CreateServerRagdollSubmodel( this, GetLegsModel(), GetAbsOrigin() - Vector(0, 0, 40), GetAbsAngles(), COLLISION_GROUP_INTERACTIVE_DEBRIS ); + pGib->VPhysicsGetObject()->AddVelocity( &vecLegsForce, NULL ); + + if (flFadeTime > 0.0) + { + pGib->SUB_StartFadeOut( flFadeTime, false ); + } + } + else + pGib = CreateRagGib( GetLegsModel(), GetAbsOrigin() - Vector(0, 0, 40), GetAbsAngles(), vecLegsForce, flFadeTime ); +#else CBaseEntity *pGib = CreateRagGib( GetLegsModel(), GetAbsOrigin() - Vector(0, 0, 40), GetAbsAngles(), vecLegsForce, flFadeTime ); +#endif // don't collide with this thing ever if ( pGib ) @@ -2391,7 +2497,22 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve if( fRagdollCrab ) { //Vector vecForce = Vector( 0, 0, random->RandomFloat( 700, 1100 ) ); +#ifdef MAPBASE + CBaseEntity *pGib = NULL; + if ( m_bForceServerRagdoll ) + { + pGib = CreateServerRagdollSubmodel( this, GetHeadcrabModel(), vecOrigin, GetLocalAngles(), COLLISION_GROUP_INTERACTIVE_DEBRIS ); + pGib->VPhysicsGetObject()->AddVelocity(&vecVelocity, NULL); + if (ShouldIgniteZombieGib()) + static_cast(pGib)->Ignite( random->RandomFloat( 8.0, 12.0 ), false ); + + pGib->SUB_StartFadeOut( 15, false ); + } + else + pGib = CreateRagGib( GetHeadcrabModel(), vecOrigin, GetLocalAngles(), vecVelocity, 15, ShouldIgniteZombieGib() ); +#else CBaseEntity *pGib = CreateRagGib( GetHeadcrabModel(), vecOrigin, GetLocalAngles(), vecVelocity, 15, ShouldIgniteZombieGib() ); +#endif if ( pGib ) { @@ -2410,6 +2531,11 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve return; } +#ifdef MAPBASE + // Inherit some misc. properties + pGib->m_iViewHideFlags = m_iViewHideFlags; +#endif + pGib->SetOwnerEntity( this ); CopyRenderColorTo( pGib ); @@ -2449,6 +2575,12 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve // add on the parent flags pCrab->AddSpawnFlags( m_spawnflags & ZOMBIE_CRAB_INHERITED_SPAWNFLAGS ); + +#ifdef MAPBASE + // Inherit some misc. properties + pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll; + pCrab->m_iViewHideFlags = m_iViewHideFlags; +#endif // make me the crab's owner to avoid collision issues pCrab->SetOwnerEntity( this ); @@ -2502,6 +2634,10 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve CopyRenderColorTo( pCrab ); pCrab->Activate(); + +#ifdef MAPBASE + m_OnCrab.Set( pCrab, pCrab, this ); +#endif } if( fRemoveHead ) diff --git a/mp/src/game/server/hl2/npc_BaseZombie.h b/mp/src/game/server/hl2/npc_BaseZombie.h index d4800f3c..743186de 100644 --- a/mp/src/game/server/hl2/npc_BaseZombie.h +++ b/mp/src/game/server/hl2/npc_BaseZombie.h @@ -42,6 +42,10 @@ extern int AE_ZOMBIE_POUND; #define ZOMBIE_BLOOD_RIGHT_HAND 1 #define ZOMBIE_BLOOD_BOTH_HANDS 2 #define ZOMBIE_BLOOD_BITE 3 + +#ifdef MAPBASE +#define SF_ZOMBIE_NO_TORSO ( 1 << 15 ) +#endif enum HeadcrabRelease_t @@ -259,6 +263,10 @@ protected: float m_flBurnDamageResetTime; // Time at which we reset the burn damage. EHANDLE m_hPhysicsEnt; +#ifdef MAPBASE + COutputEHANDLE m_OnSwattedProp; + COutputEHANDLE m_OnCrab; +#endif float m_flNextMoanSound; float m_flNextSwat; diff --git a/mp/src/game/server/hl2/npc_PoisonZombie.cpp b/mp/src/game/server/hl2/npc_PoisonZombie.cpp index eafebe2d..1611e15b 100644 --- a/mp/src/game/server/hl2/npc_PoisonZombie.cpp +++ b/mp/src/game/server/hl2/npc_PoisonZombie.cpp @@ -285,7 +285,11 @@ void CNPC_PoisonZombie::Spawn( void ) { Precache(); +#ifndef MAPBASE // Controlled by KV m_fIsTorso = m_fIsHeadless = false; +#else + m_fIsTorso = false; +#endif #ifdef HL2_EPISODIC SetBloodColor( BLOOD_COLOR_ZOMBIE ); @@ -632,10 +636,16 @@ void CNPC_PoisonZombie::BreatheOffShort( void ) { if ( m_bNearEnemy ) { +#ifdef MAPBASE + if (m_pFastBreathSound) +#endif ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pFastBreathSound, SOUNDCTRL_CHANGE_VOLUME, envPoisonZombieBreatheVolumeOffShort, ARRAYSIZE(envPoisonZombieBreatheVolumeOffShort) ); } else { +#ifdef MAPBASE + if (m_pSlowBreathSound) +#endif ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pSlowBreathSound, SOUNDCTRL_CHANGE_VOLUME, envPoisonZombieBreatheVolumeOffShort, ARRAYSIZE(envPoisonZombieBreatheVolumeOffShort) ); } } diff --git a/mp/src/game/server/hl2/npc_alyx.cpp b/mp/src/game/server/hl2/npc_alyx.cpp index f9dc1286..72b3f328 100644 --- a/mp/src/game/server/hl2/npc_alyx.cpp +++ b/mp/src/game/server/hl2/npc_alyx.cpp @@ -33,6 +33,10 @@ END_DATADESC() int AE_ALYX_EMPTOOL_ATTACHMENT; int AE_ALYX_EMPTOOL_SEQUENCE; +#ifdef MAPBASE +ConVar sk_alyx_health( "sk_alyx_health", "80" ); +#endif + //========================================================= // Classify - indicates this NPC's place in the // relationship table. @@ -127,7 +131,11 @@ void CNPC_Alyx::Spawn() AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL | EFL_NO_PHYSCANNON_INTERACTION ); +#ifdef MAPBASE + m_iHealth = sk_alyx_health.GetInt(); +#else m_iHealth = 80; +#endif NPCInit(); } diff --git a/mp/src/game/server/hl2/npc_alyx.h b/mp/src/game/server/hl2/npc_alyx.h index 96c1c917..551460b2 100644 --- a/mp/src/game/server/hl2/npc_alyx.h +++ b/mp/src/game/server/hl2/npc_alyx.h @@ -31,6 +31,13 @@ public: bool IsReadinessCapable() { return false; } void DeathSound( const CTakeDamageInfo &info ); +#ifdef MAPBASE + // Alyx was never meant to automatically unholster her weapon in non-episodic Half-Life 2. + // Now that all allies can holster/unholster, this is a precaution in case it breaks anything. + // Try OnFoundEnemy > UnholsterWeapon if you want Alyx to automatically unholster in non-episodic HL2 maps. + bool CanUnholsterWeapon() { return false; } +#endif + EHANDLE m_hEmpTool; DECLARE_DATADESC(); diff --git a/mp/src/game/server/hl2/npc_alyx_episodic.cpp b/mp/src/game/server/hl2/npc_alyx_episodic.cpp index e98c89f5..19616e10 100644 --- a/mp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/mp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -36,6 +36,9 @@ #include "ai_interactions.h" #include "weapon_flaregun.h" #include "env_debughistory.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#endif extern Vector PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint); @@ -72,6 +75,10 @@ bool IsInCommentaryMode( void ); #define ALYX_MIN_ENEMY_HEALTH_TO_CROUCH 15 #define ALYX_CROUCH_DELAY 5 // Time after crouching before Alyx will crouch again +#ifdef MAPBASE +ConVar sk_alyx_health( "sk_alyx_health", "80" ); +#endif + //----------------------------------------------------------------------------- // Interactions //----------------------------------------------------------------------------- @@ -136,14 +143,21 @@ END_DATADESC() static int AE_ALYX_EMPTOOL_ATTACHMENT; static int AE_ALYX_EMPTOOL_SEQUENCE; static int AE_ALYX_EMPTOOL_USE; +#ifndef MAPBASE static int COMBINE_AE_BEGIN_ALTFIRE; static int COMBINE_AE_ALTFIRE; +#endif ConVar npc_alyx_readiness( "npc_alyx_readiness", "1" ); ConVar npc_alyx_force_stop_moving( "npc_alyx_force_stop_moving", "1" ); ConVar npc_alyx_readiness_transitions( "npc_alyx_readiness_transitions", "1" ); ConVar npc_alyx_crouch( "npc_alyx_crouch", "1" ); +#ifdef MAPBASE +ConVar npc_alyx_interact_manhacks( "npc_alyx_interact_manhacks", "1" ); +ConVar npc_alyx_interact_turrets( "npc_alyx_interact_turrets", "0" ); +#endif + // global pointer to Alyx for fast lookups CEntityClassList g_AlyxList; template <> CNPC_Alyx *CEntityClassList::m_pClassList = NULL; @@ -264,6 +278,7 @@ void CNPC_Alyx::HandleAnimEvent( animevent_t *pEvent ) } return; } +#ifndef MAPBASE else if ( pEvent->event == COMBINE_AE_BEGIN_ALTFIRE ) { EmitSound( "Weapon_CombineGuard.Special1" ); @@ -280,6 +295,7 @@ void CNPC_Alyx::HandleAnimEvent( animevent_t *pEvent ) return; } +#endif switch( pEvent->event ) { @@ -334,7 +350,11 @@ void CNPC_Alyx::Spawn() AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL | EFL_NO_PHYSCANNON_INTERACTION ); +#ifdef MAPBASE + m_iHealth = sk_alyx_health.GetInt(); +#else m_iHealth = 80; +#endif m_bloodColor = DONT_BLEED; NPCInit(); @@ -379,9 +399,15 @@ void CNPC_Alyx::Precache() UTIL_PrecacheOther( "env_alyxemp" ); CLASSNAME_ALYXGUN = AllocPooledString( "weapon_alyxgun" ); +#ifdef MAPBASE + CLASSNAME_SMG1 = gm_iszSMG1Classname; + CLASSNAME_SHOTGUN = gm_iszShotgunClassname; + CLASSNAME_AR2 = gm_iszAR2Classname; +#else CLASSNAME_SMG1 = AllocPooledString( "weapon_smg1" ); CLASSNAME_SHOTGUN = AllocPooledString( "weapon_shotgun" ); CLASSNAME_AR2 = AllocPooledString( "weapon_ar2" ); +#endif } //----------------------------------------------------------------------------- @@ -391,7 +417,15 @@ void CNPC_Alyx::Activate( void ) { // Alyx always kicks her health back up to full after loading a savegame. // Avoids problems with players saving the game in places where she dies immediately afterwards. +#ifdef MAPBASE + // Alyx's health can be >80 thanks to the new convar, and we don't want a 1000-health Alyx to reload + // from 1 health to 1000 health, so this should only kick in if her health is less than her default + // (we also probably don't want this to happen if she's not an ally) + if (IsPlayerAlly() && m_iHealth < 80) + m_iHealth = 80; +#else m_iHealth = 80; +#endif BaseClass::Activate(); @@ -426,6 +460,19 @@ void CNPC_Alyx::Activate( void ) { g_HackOutland10DamageHack = true; } + +#ifdef MAPBASE + // Please, this is not the worst hack you've caught me doing. + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i]; + + if (pInteraction->iszMyWeapon == CLASSNAME_ALYXGUN) + pInteraction->iszMyWeapon = AllocPooledString("WEPCLASS_HANDGUN"); + else if (pInteraction->iszMyWeapon == CLASSNAME_SHOTGUN) + pInteraction->iszMyWeapon = AllocPooledString("!=WEPCLASS_HANDGUN"); + } +#endif } //----------------------------------------------------------------------------- @@ -612,11 +659,13 @@ void CNPC_Alyx::PrescheduleThink( void ) } } +#ifndef MAPBASE // See CAI_BaseNPC // If Alyx is in combat, and she doesn't have her gun out, fetch it if ( GetState() == NPC_STATE_COMBAT && IsWeaponHolstered() && !m_FuncTankBehavior.IsRunning() ) { SetDesiredWeaponState( DESIREDWEAPONSTATE_UNHOLSTERED ); } +#endif // If we're in stealth mode, and we can still see the stealth node, keep using it if ( GetReadinessLevel() == AIRL_STEALTH ) @@ -759,11 +808,12 @@ void CNPC_Alyx::GatherConditions() } } - +#ifndef MAPBASE // Moved to CNPC_PlayerCompanion if ( m_NPCState == NPC_STATE_COMBAT ) { DoCustomCombatAI(); } +#endif if( HasInteractTarget() ) { @@ -928,7 +978,12 @@ bool CNPC_Alyx::IsValidEnemy( CBaseEntity *pEnemy ) return false; } +#ifdef MAPBASE + // Come to the defense of anyone we like, not just ourselves or the player. + if( pEnemy->GetEnemy() != this && IRelationType(pEnemy->GetEnemy()) == D_LI ) +#else if( pEnemy->GetEnemy() != this && !pEnemy->GetEnemy()->IsPlayer() ) +#endif { return false; } @@ -991,7 +1046,12 @@ void CNPC_Alyx::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo & return; } +#ifdef MAPBASE + // Don't do the custom target thing against dissolve or blast damage (Alyx can do that with companion grenades/balls) + if( !HasShotgun() && !(info.GetDamageType() & (DMG_DISSOLVE | DMG_BLAST)) ) +#else if( !HasShotgun() ) +#endif { CAI_BaseNPC *pTarget = CreateCustomTarget( pVictim->GetAbsOrigin(), 2.0f ); @@ -1849,6 +1909,29 @@ int CNPC_Alyx::TranslateSchedule( int scheduleType ) //Warning("CROUCH: Standing, no enemy.\n" ); Stand(); } + +#ifdef MAPBASE + // This stuff was ported from npc_playercompanion to help Alyx use grenades. + if (HasGrenades() && !IsCrouching()) + { + if (CanAltFireEnemy( true ) && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK )) + { + return SCHED_PC_AR2_ALTFIRE; + } + + if ( !OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) ) + { + // Try throwing a grenade if Alyx is in a squad that already has attacking well in hand. + if ( CanGrenadeEnemy() ) + { + if ( OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + return SCHED_RANGE_ATTACK2; + } + } + } + } +#endif } return SCHED_ALYX_RANGE_ATTACK1; @@ -2941,7 +3024,12 @@ bool CNPC_Alyx::Crouch( void ) return false; // Alyx will ignore crouch requests while she has the shotgun +#ifdef MAPBASE + // Alyx will ignore crouch requests from anything that isn't a "pistol", e.g. her Alyx gun. + if ( GetActiveWeapon() && GetActiveWeapon()->WeaponClassify() != WEPCLASS_HANDGUN ) +#else if ( HasShotgun() ) +#endif return false; bool bWasStanding = !IsCrouching(); @@ -2974,9 +3062,13 @@ void CNPC_Alyx::DesireCrouch( void ) //----------------------------------------------------------------------------- void CNPC_Alyx::ModifyOrAppendCriteria( AI_CriteriaSet &set ) { +#ifdef MAPBASE + float fLengthOfLastCombat; +#else AIEnemiesIter_t iter; float fLengthOfLastCombat; int iNumEnemies; +#endif if ( GetState() == NPC_STATE_COMBAT ) { @@ -2989,6 +3081,7 @@ void CNPC_Alyx::ModifyOrAppendCriteria( AI_CriteriaSet &set ) set.AppendCriteria( "combat_length", UTIL_VarArgs( "%.3f", fLengthOfLastCombat ) ); +#ifndef MAPBASE // Moved to CNPC_PlayerCompanion iNumEnemies = 0; for ( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) { @@ -2998,6 +3091,7 @@ void CNPC_Alyx::ModifyOrAppendCriteria( AI_CriteriaSet &set ) } } set.AppendCriteria( "num_enemies", UTIL_VarArgs( "%d", iNumEnemies ) ); +#endif set.AppendCriteria( "darkness_mode", UTIL_VarArgs( "%d", HasCondition( COND_ALYX_IN_DARK ) ) ); set.AppendCriteria( "water_level", UTIL_VarArgs( "%d", GetWaterLevel() ) ); @@ -3149,7 +3243,12 @@ bool CNPC_Alyx::PlayerInSpread( const Vector &sourcePos, const Vector &targetPos { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); +#ifdef MAPBASE + // "> D_FR" means it isn't D_HT, D_FR, or D_ER (error disposition) + if ( pPlayer && ( !ignoreHatedPlayers || IRelationType( pPlayer ) > D_FR ) && !(pPlayer->GetFlags() & FL_NOTARGET) ) +#else if ( pPlayer && ( !ignoreHatedPlayers || IRelationType( pPlayer ) != D_HT ) ) +#endif { //If the player is being lifted by a barnacle then go ahead and ignore the player and shoot. #ifdef HL2_EPISODIC @@ -3324,8 +3423,10 @@ AI_BEGIN_CUSTOM_NPC( npc_alyx, CNPC_Alyx ) DECLARE_ANIMEVENT( AE_ALYX_EMPTOOL_ATTACHMENT ) DECLARE_ANIMEVENT( AE_ALYX_EMPTOOL_SEQUENCE ) DECLARE_ANIMEVENT( AE_ALYX_EMPTOOL_USE ) +#ifndef MAPBASE DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE ) DECLARE_ANIMEVENT( COMBINE_AE_ALTFIRE ) +#endif DECLARE_CONDITION( COND_ALYX_HAS_INTERACT_TARGET ) DECLARE_CONDITION( COND_ALYX_NO_INTERACT_TARGET ) diff --git a/mp/src/game/server/hl2/npc_alyx_episodic.h b/mp/src/game/server/hl2/npc_alyx_episodic.h index 5657dae7..9e72fd8d 100644 --- a/mp/src/game/server/hl2/npc_alyx_episodic.h +++ b/mp/src/game/server/hl2/npc_alyx_episodic.h @@ -54,6 +54,11 @@ public: bool OnBeginMoveAndShoot(); void SpeakAttacking( void ); +#ifdef MAPBASE + // This skips CAI_PlayerAlly's CanFlinch() function since Episodic Alyx can flinch to begin with. + virtual bool CanFlinch( void ) { return CAI_BaseActor::CanFlinch(); } +#endif + virtual float GetJumpGravity() const { return 1.8f; } // Crouching diff --git a/mp/src/game/server/hl2/npc_antlion.cpp b/mp/src/game/server/hl2/npc_antlion.cpp index 8cb7f789..d94c658d 100644 --- a/mp/src/game/server/hl2/npc_antlion.cpp +++ b/mp/src/game/server/hl2/npc_antlion.cpp @@ -247,6 +247,9 @@ BEGIN_DATADESC( CNPC_Antlion ) DEFINE_INPUTFUNC( FIELD_VOID, "IgnoreBugbait", InputIgnoreBugbait ), DEFINE_INPUTFUNC( FIELD_VOID, "HearBugbait", InputHearBugbait ), DEFINE_INPUTFUNC( FIELD_STRING, "JumpAtTarget", InputJumpAtTarget ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget", InputSetFollowTarget ), +#endif DEFINE_OUTPUT( m_OnReachFightGoal, "OnReachedFightGoal" ), DEFINE_OUTPUT( m_OnUnBurrowed, "OnUnBurrowed" ), @@ -361,6 +364,43 @@ void CNPC_Antlion::Spawn( void ) BaseClass::Spawn(); m_nSkin = random->RandomInt( 0, ANTLION_SKIN_COUNT-1 ); + +#if defined(MAPBASE) && defined(HL2_EPISODIC) + // Implement dynamic interactions here since we can't recompile the model + if (GetModelPtr()) + { + ScriptedNPCInteraction_t sInteraction01; + sInteraction01.iszInteractionName = AllocPooledString( "antlion_v_soldier_01" ); + sInteraction01.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( "antlion_soldier_DI_01" ); + + sInteraction01.vecRelativeOrigin = Vector(224, 0, 0); + sInteraction01.angRelativeAngles = QAngle(0, 180, 0); + //sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + sInteraction01.iFlags |= SCNPC_FLAG_TEST_END_POSITION; + sInteraction01.vecRelativeEndPos = Vector(312, -10, 0); + sInteraction01.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; + sInteraction01.flDelay = 15.0f; + sInteraction01.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; + sInteraction01.flDistSqr = (8 * 8); + + + ScriptedNPCInteraction_t sInteraction02; + sInteraction02.iszInteractionName = AllocPooledString( "antlion_v_soldier_02" ); + sInteraction02.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( "antlion_soldier_DI_02" ); + + sInteraction02.vecRelativeOrigin = Vector(64, 0, 0); + sInteraction02.angRelativeAngles = QAngle(0, 180, 0); + //sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + sInteraction02.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; + sInteraction02.flDelay = 7.5f; + sInteraction02.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; + sInteraction02.flDistSqr = (8 * 8); + + + AddScriptedNPCInteraction(&sInteraction01); + AddScriptedNPCInteraction(&sInteraction02); + } +#endif } //----------------------------------------------------------------------------- @@ -657,7 +697,11 @@ void CNPC_Antlion::MeleeAttack( float distance, float damage, QAngle &viewPunch, vecForceDir = ( pHurt->WorldSpaceCenter() - WorldSpaceCenter() ); //FIXME: Until the interaction is setup, kill combine soldiers in one hit -- jdw +#ifdef MAPBASE + if ( pHurt->Classify() == CLASS_COMBINE && FClassnameIs( pHurt, "npc_combine_s" ) && GlobalEntity_GetState("antlion_noinstakill") != GLOBAL_ON ) +#else if ( FClassnameIs( pHurt, "npc_combine_s" ) ) +#endif { CTakeDamageInfo dmgInfo( this, this, pHurt->m_iHealth+25, DMG_SLASH ); CalculateMeleeDamageForce( &dmgInfo, vecForceDir, pHurt->GetAbsOrigin() ); @@ -2465,6 +2509,38 @@ int CNPC_Antlion::SelectSchedule( void ) return SCHED_ANTLION_WORKER_RANGE_ATTACK1; } } + +#ifdef MAPBASE + // "Nemesis" is assigned to enemies we hate with 10+ priority. + // Since bugbait targets are given 99 priority, this means the AI here usually only applies + // when the antlion worker is following bugbait. + // + // This is just so antlion workers are more potent when commanded by bugbait, + // which wasn't explored in the official games, but may be used in HL2 mods. + if ( m_hFollowTarget && IsAllied() /*HasCondition( COND_SEE_NEMESIS )*/ ) + { + // Establish LOF if we can't see the enemy + if ( HasCondition( COND_ENEMY_OCCLUDED ) ) + return SCHED_ESTABLISH_LINE_OF_FIRE; + + // See if we need to destroy breakable cover + if ( HasCondition( COND_WEAPON_SIGHT_OCCLUDED ) ) + return SCHED_SHOOT_ENEMY_COVER; + + // Just face as usual if we're not too close to attack, + // otherwise fall back to base class and charge like any other antlion + if ( !HasCondition( COND_TOO_CLOSE_TO_ATTACK ) ) + { + // Run around randomly if our target is looking in our direction + if ( HasCondition( COND_BEHIND_ENEMY ) == false ) + return SCHED_ANTLION_WORKER_RUN_RANDOM; + + return SCHED_COMBAT_FACE; + } + } + else + { +#endif // Back up, we're too near an enemy or can't see them if ( HasCondition( COND_TOO_CLOSE_TO_ATTACK ) || HasCondition( COND_ENEMY_OCCLUDED ) ) @@ -2480,6 +2556,9 @@ int CNPC_Antlion::SelectSchedule( void ) // Face our target and continue to fire return SCHED_COMBAT_FACE; +#ifdef MAPBASE + } +#endif } else { @@ -2887,7 +2966,11 @@ int CNPC_Antlion::MeleeAttack1Conditions( float flDot, float flDist ) AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); // If the hit entity isn't our target and we don't hate it, don't hit it +#ifdef MAPBASE + if ( tr.m_pEnt != GetEnemy() && tr.fraction < 1.0f && IRelationType( tr.m_pEnt ) > D_FR ) +#else if ( tr.m_pEnt != GetEnemy() && tr.fraction < 1.0f && IRelationType( tr.m_pEnt ) != D_HT ) +#endif return 0; #else @@ -4224,6 +4307,25 @@ void CNPC_Antlion::SetFollowTarget( CBaseEntity *pTarget ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_Antlion::InputSetFollowTarget( inputdata_t &inputdata ) +{ + if ( IsAlive() == false ) + return; + + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller ); + + if ( pEntity != NULL ) + { + SetFollowTarget( pEntity ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. @@ -4438,7 +4540,15 @@ bool CNPC_Antlion::IsHeavyDamage( const CTakeDamageInfo &info ) bool CNPC_Antlion::CanRunAScriptedNPCInteraction( bool bForced /*= false*/ ) { // Workers shouldn't do DSS's because they explode +#ifdef MAPBASE + // Now that antlions have their DI restored, one might want workers to use them as well. + // Forced interactions are allowed now, but I went a step further and enabling dynamic interactions + // will disregard this check. This will allow vortigaunts to use dangerous melee interactions with workers, + // but if you're concerned about that just turn the antlion's interactions off. + if ( IsWorker() && !bForced && m_iDynamicInteractionsAllowed != TRS_TRUE ) +#else if ( IsWorker() ) +#endif return false; return BaseClass::CanRunAScriptedNPCInteraction( bForced ); diff --git a/mp/src/game/server/hl2/npc_antlion.h b/mp/src/game/server/hl2/npc_antlion.h index 1de36066..dd26ec2a 100644 --- a/mp/src/game/server/hl2/npc_antlion.h +++ b/mp/src/game/server/hl2/npc_antlion.h @@ -143,6 +143,9 @@ public: void InputJumpAtTarget( inputdata_t &inputdata ); void SetFollowTarget( CBaseEntity *pTarget ); +#ifdef MAPBASE + void InputSetFollowTarget( inputdata_t &inputdata ); +#endif int TranslateSchedule( int scheduleType ); virtual Activity NPC_TranslateActivity( Activity baseAct ); diff --git a/mp/src/game/server/hl2/npc_antliongrub.cpp b/mp/src/game/server/hl2/npc_antliongrub.cpp index 16ee2ed5..07444e9a 100644 --- a/mp/src/game/server/hl2/npc_antliongrub.cpp +++ b/mp/src/game/server/hl2/npc_antliongrub.cpp @@ -15,6 +15,9 @@ #include "items.h" #include "item_dynamic_resupply.h" #include "npc_vortigaunt_episodic.h" +#ifdef MAPBASE +#include "filters.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -659,6 +662,16 @@ void CAntlionGrub::GrubTouch( CBaseEntity *pOther ) IPhysicsObject *pPhysOther = pOther->VPhysicsGetObject(); // bool bThrown = ( pTarget->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_WAS_THROWN ) != 0; if ( pOther->IsPlayer() || FClassnameIs(pOther,"npc_vortigaunt") || ( pPhysOther && (pPhysOther->GetGameFlags() & FVPHYSICS_WAS_THROWN )) ) { +#ifdef MAPBASE + if (m_hDamageFilter) + { + // Don't squash if they don't pass our damage filter + CBaseFilter *pFilter = static_cast(m_hDamageFilter.Get()); + if (pFilter && !pFilter->PassesFilter(this, pOther)) + return; + } +#endif + m_OnAgitated.FireOutput( pOther, pOther ); Squash( pOther, true, true ); } @@ -844,6 +857,13 @@ void CGrubNugget::Spawn( void ) { Precache(); +#ifdef MAPBASE + if ( GetModelName() != NULL_STRING ) + { + SetModel( STRING(GetModelName()) ); + } + else +#endif if ( m_nDenomination == NUGGET_LARGE ) { SetModel( "models/grub_nugget_large.mdl" ); @@ -875,6 +895,10 @@ void CGrubNugget::Precache( void ) PrecacheModel("models/grub_nugget_small.mdl"); PrecacheModel("models/grub_nugget_medium.mdl"); PrecacheModel("models/grub_nugget_large.mdl"); +#ifdef MAPBASE + if (GetModelName() != NULL_STRING) + PrecacheModel( STRING(GetModelName()) ); +#endif PrecacheScriptSound( "GrubNugget.Touch" ); PrecacheScriptSound( "NPC_Antlion_Grub.Explode" ); diff --git a/mp/src/game/server/hl2/npc_antlionguard.cpp b/mp/src/game/server/hl2/npc_antlionguard.cpp index 9ed2eff4..efc5883d 100644 --- a/mp/src/game/server/hl2/npc_antlionguard.cpp +++ b/mp/src/game/server/hl2/npc_antlionguard.cpp @@ -190,6 +190,17 @@ Activity ACT_ANTLIONGUARD_CHARGE_STOP; Activity ACT_ANTLIONGUARD_CHARGE_HIT; Activity ACT_ANTLIONGUARD_CHARGE_ANTICIPATION; +#ifdef MAPBASE +// Unused activities +Activity ACT_ANTLIONGUARD_COVER_ENTER; +Activity ACT_ANTLIONGUARD_COVER_LOOP; +Activity ACT_ANTLIONGUARD_COVER_EXIT; +Activity ACT_ANTLIONGUARD_COVER_ADVANCE; +Activity ACT_ANTLIONGUARD_COVER_FLINCH; +Activity ACT_ANTLIONGUARD_SNEAK; +Activity ACT_ANTLIONGUARD_RUN_FULL; +#endif + // Anim events int AE_ANTLIONGUARD_CHARGE_HIT; int AE_ANTLIONGUARD_SHOVE_PHYSOBJECT; @@ -1573,7 +1584,11 @@ public: if ( pVictimBCC ) { // Can only damage other NPCs that we hate +#ifdef MAPBASE + if ( m_pAttacker->IRelationType( pEntity ) <= D_FR ) +#else if ( m_pAttacker->IRelationType( pEntity ) == D_HT ) +#endif { pEntity->TakeDamage( info ); return true; @@ -2612,8 +2627,15 @@ public: if ( !pEntity->IsNPC() && pEntity->GetMoveType() == MOVETYPE_VPHYSICS ) { IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject(); +#ifdef MAPBASE + // A MOVETYPE_VPHYSICS object without a VPhysics object is an odd edge case, but it's evidently possible + // since my game crashed after an antlion guard tried to see me through an EP2 jalopy. + // Perhaps that's a sign of an underlying issue? + if ( pPhysics && pPhysics->IsMoveable() && pPhysics->GetMass() < m_minMass ) +#else Assert(pPhysics); if ( pPhysics->IsMoveable() && pPhysics->GetMass() < m_minMass ) +#endif return false; } @@ -2729,7 +2751,11 @@ bool CNPC_AntlionGuard::HandleChargeImpact( Vector vecImpact, CBaseEntity *pEnti } // Hit anything we don't like +#ifdef MAPBASE + if ( IRelationType( pEntity ) <= D_FR && ( GetNextAttack() < gpGlobals->curtime ) ) +#else if ( IRelationType( pEntity ) == D_HT && ( GetNextAttack() < gpGlobals->curtime ) ) +#endif { EmitSound( "NPC_AntlionGuard.Shove" ); @@ -3227,6 +3253,9 @@ void CNPC_AntlionGuard::SummonAntlions( void ) // Make the antlion fire my input when he dies pAntlion->KeyValue( "OnDeath", UTIL_VarArgs("%s,SummonedAntlionDied,,0,-1", STRING(GetEntityName())) ); +#ifdef MAPBASE + pAntlion->KeyValue( "OnKilled", UTIL_VarArgs("%s,SummonedAntlionDied,,0,-1", STRING(GetEntityName())) ); +#endif // Start the antlion burrowed, and tell him to come up pAntlion->m_bStartBurrowed = true; @@ -3366,6 +3395,11 @@ void CNPC_AntlionGuard::InputClearChargeTarget( inputdata_t &inputdata ) //----------------------------------------------------------------------------- Activity CNPC_AntlionGuard::NPC_TranslateActivity( Activity baseAct ) { +#ifdef MAPBASE + // Needed for VScript NPC_TranslateActiviy hook + baseAct = BaseClass::NPC_TranslateActivity( baseAct ); +#endif + //See which run to use if ( ( baseAct == ACT_RUN ) && IsCurSchedule( SCHED_ANTLIONGUARD_CHARGE ) ) return (Activity) ACT_ANTLIONGUARD_CHARGE_RUN; @@ -4676,6 +4710,15 @@ AI_BEGIN_CUSTOM_NPC( npc_antlionguard, CNPC_AntlionGuard ) DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_FL ) DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_RR ) DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_RL ) +#ifdef MAPBASE + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_COVER_ENTER ) + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_COVER_LOOP ) + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_COVER_EXIT ) + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_COVER_ADVANCE ) + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_COVER_FLINCH ) + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_SNEAK ) + DECLARE_ACTIVITY( ACT_ANTLIONGUARD_RUN_FULL ) +#endif //Adrian: events go here DECLARE_ANIMEVENT( AE_ANTLIONGUARD_CHARGE_HIT ) diff --git a/mp/src/game/server/hl2/npc_attackchopper.cpp b/mp/src/game/server/hl2/npc_attackchopper.cpp index 0687c915..ddb9752a 100644 --- a/mp/src/game/server/hl2/npc_attackchopper.cpp +++ b/mp/src/game/server/hl2/npc_attackchopper.cpp @@ -115,6 +115,9 @@ static const char *s_pChunkModelName[CHOPPER_MAX_CHUNKS] = #define SF_HELICOPTER_IGNORE_AVOID_FORCES 0x00080000 #define SF_HELICOPTER_AGGRESSIVE 0x00100000 #define SF_HELICOPTER_LONG_SHADOW 0x00200000 +#ifdef MAPBASE +#define SF_HELICOPTER_AIM_WITH_GUN_OFF 0x00400000 +#endif #define CHOPPER_SLOW_BOMB_SPEED 250 @@ -347,6 +350,10 @@ protected: void CollisionCallback( CHelicopterChunk *pCaller ); +#ifdef MAPBASE + void InputFallApart( inputdata_t &inputdata ); +#endif + void FallThink( void ); bool m_bLanded; @@ -760,8 +767,13 @@ private: CSoundPatch *m_pGunFiringSound; // Outputs +#ifndef MAPBASE COutputInt m_OnHealthChanged; +#endif COutputEvent m_OnShotDown; +#ifdef MAPBASE + COutputEHANDLE m_OutBomb; +#endif // Crashing EHANDLE m_hCrashPoint; @@ -842,6 +854,10 @@ BEGIN_DATADESC( CNPC_AttackHelicopter ) DEFINE_KEYFIELD( m_flMaxSpeed, FIELD_FLOAT, "PatrolSpeed" ), DEFINE_KEYFIELD( m_bNonCombat, FIELD_BOOLEAN, "NonCombat" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ), +#endif + DEFINE_FIELD( m_hCrashPoint, FIELD_EHANDLE ), DEFINE_INPUTFUNC( FIELD_VOID, "ResetIdleTime", InputResetIdleTime ), @@ -866,7 +882,9 @@ BEGIN_DATADESC( CNPC_AttackHelicopter ) DEFINE_INPUTFUNC( FIELD_VOID, "StartContinuousShooting", InputStartContinuousShooting ), DEFINE_INPUTFUNC( FIELD_VOID, "StartFastShooting", InputStartFastShooting ), DEFINE_INPUTFUNC( FIELD_VOID, "GunOff", InputGunOff ), +#ifndef MAPBASE // This has been added to all NPCs. npc_helicopter overrides it with its original function, but the datadesc entry isn't needed anymore. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthFraction", InputSetHealthFraction ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "StartBombExplodeOnContact", InputStartBombExplodeOnContact ), DEFINE_INPUTFUNC( FIELD_VOID, "StopBombExplodeOnContact", InputStopBombExplodeOnContact ), @@ -878,8 +896,13 @@ BEGIN_DATADESC( CNPC_AttackHelicopter ) DEFINE_THINKFUNC( BlinkLightsThink ), DEFINE_THINKFUNC( SpotlightThink ), +#ifndef MAPBASE DEFINE_OUTPUT( m_OnHealthChanged, "OnHealthChanged" ), +#endif DEFINE_OUTPUT( m_OnShotDown, "OnShotDown" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OutBomb, "OutBomb" ), +#endif END_DATADESC() @@ -893,6 +916,9 @@ CNPC_AttackHelicopter::CNPC_AttackHelicopter() : m_bBombsExplodeOnContact( false ) { m_flMaxSpeed = 0; +#ifdef MAPBASE + m_flFieldOfView = -1.0; // 360 degrees +#endif } CNPC_AttackHelicopter::~CNPC_AttackHelicopter(void) @@ -937,6 +963,13 @@ void CNPC_AttackHelicopter::Precache( void ) { BaseClass::Precache(); +#ifdef MAPBASE + if ( GetModelName() != NULL_STRING ) + { + PrecacheModel( STRING(GetModelName()) ); + } + else +#endif if ( !HasSpawnFlags(SF_HELICOPTER_ELECTRICAL_DRONE) ) { PrecacheModel( CHOPPER_MODEL_NAME ); @@ -1038,6 +1071,13 @@ void CNPC_AttackHelicopter::Spawn( void ) m_bBombingSuppressed = false; m_bIgnorePathVisibilityTests = false; +#ifdef MAPBASE + if ( GetModelName() != NULL_STRING ) + { + SetModel( STRING(GetModelName()) ); + } + else +#endif if ( !HasSpawnFlags(SF_HELICOPTER_ELECTRICAL_DRONE) ) { SetModel( CHOPPER_MODEL_NAME ); @@ -1070,6 +1110,11 @@ void CNPC_AttackHelicopter::Spawn( void ) SetPauseState( PAUSE_NO_PAUSE ); +#ifdef MAPBASE + if (m_iHealth != 0) + m_iMaxHealth = m_iHealth; + else +#endif m_iMaxHealth = m_iHealth = sk_helicopter_health.GetInt(); m_flMaxSpeed = flLoadedSpeed; @@ -1081,7 +1126,9 @@ void CNPC_AttackHelicopter::Spawn( void ) m_nGrenadeCount = CHOPPER_BOMB_DROP_COUNT; +#ifndef MAPBASE // Moved to constructor because this is a keyvalue now m_flFieldOfView = -1.0; // 360 degrees +#endif m_flIdleTimeDelay = 0.0f; m_iAmmoType = GetAmmoDef()->Index("HelicopterGun"); @@ -2790,6 +2837,10 @@ CGrenadeHelicopter *CNPC_AttackHelicopter::SpawnBombEntity( const Vector &vecPos } #endif // HL2_EPISODIC +#ifdef MAPBASE + m_OutBomb.Set(pGrenade, pGrenade, this); +#endif + return pGrenade; } @@ -3475,9 +3526,16 @@ void CNPC_AttackHelicopter::TraceAttack( const CTakeDamageInfo &info, const Vect // Take no damage from trace attacks unless it's blast damage. RadiusDamage() sometimes calls // TraceAttack() as a means for delivering blast damage. Usually when the explosive penetrates // the target. (RPG missiles do this sometimes). +#ifdef MAPBASE + if ( ( info.GetDamageType() & DMG_AIRBOAT ) || + ( info.GetInflictor()->Classify() == CLASS_MISSILE ) || + ( info.GetAttacker()->Classify() == CLASS_MISSILE ) || + m_bAllowAnyDamage ) +#else if ( ( info.GetDamageType() & DMG_AIRBOAT ) || ( info.GetInflictor()->Classify() == CLASS_MISSILE ) || ( info.GetAttacker()->Classify() == CLASS_MISSILE ) ) +#endif { BaseClass::BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); } @@ -3490,7 +3548,11 @@ void CNPC_AttackHelicopter::TraceAttack( const CTakeDamageInfo &info, const Vect int CNPC_AttackHelicopter::OnTakeDamage( const CTakeDamageInfo &info ) { // We don't take blast damage from anything but the airboat or missiles (or myself!) +#ifdef MAPBASE + if( info.GetInflictor() != this && !m_bAllowAnyDamage ) +#else if( info.GetInflictor() != this ) +#endif { if ( ( ( info.GetDamageType() & DMG_AIRBOAT ) == 0 ) && ( info.GetInflictor()->Classify() != CLASS_MISSILE ) && @@ -3605,12 +3667,14 @@ int CNPC_AttackHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info ) ExplodeAndThrowChunk( info.GetDamagePosition() ); } +#ifndef MAPBASE // We need to make sure the base OnHealthChanged works with helicopters int nPrevPercent = (int)(100.0f * nPrevHealth / GetMaxHealth()); int nCurrPercent = (int)(100.0f * GetHealth() / GetMaxHealth()); if (( (nPrevPercent + 9) / 10 ) != ( (nCurrPercent + 9) / 10 )) { m_OnHealthChanged.Set( nCurrPercent, this, this ); } +#endif } return nRetVal; @@ -4795,6 +4859,26 @@ void CNPC_AttackHelicopter::Hunt( void ) { BullrushBombs(); } +#ifdef MAPBASE + // Some may want the hunter-chopper to aim at different positions searching for its target + // without actually firing at anything. Gun aiming is only handled in FireGun(), which is + // disabled when the gun is disabled. point_posecontroller doesn't seem to work well for this either, + // so a new spawnflag is handled here to allow the chopper to aim at its enemy even when the gun is off. + else if ( HasSpawnFlags( SF_HELICOPTER_AIM_WITH_GUN_OFF ) && GetEnemy() ) + { + // Get gun attachment points + Vector vBasePos; + GetAttachment( m_nGunBaseAttachment, vBasePos ); + + Vector vecFireAtPosition; + ComputeFireAtPosition( &vecFireAtPosition ); + + Vector vTargetDir = vecFireAtPosition - vBasePos; + VectorNormalize( vTargetDir ); + + PoseGunTowardTargetDirection( vTargetDir ); + } +#endif } #ifdef HL2_EPISODIC @@ -5939,6 +6023,10 @@ BEGIN_DATADESC( CHelicopterChunk ) DEFINE_PHYSPTR( m_pTailConstraint ), DEFINE_PHYSPTR( m_pCockpitConstraint ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "FallApart", InputFallApart ), +#endif + END_DATADESC() //----------------------------------------------------------------------------- @@ -6040,6 +6128,17 @@ void CHelicopterChunk::CollisionCallback( CHelicopterChunk *pCaller ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pCaller - +//----------------------------------------------------------------------------- +void CHelicopterChunk::InputFallApart( inputdata_t &inputdata ) +{ + CollisionCallback(this); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : &vecPos - diff --git a/mp/src/game/server/hl2/npc_barnacle.cpp b/mp/src/game/server/hl2/npc_barnacle.cpp index 1c195b90..68b7064c 100644 --- a/mp/src/game/server/hl2/npc_barnacle.cpp +++ b/mp/src/game/server/hl2/npc_barnacle.cpp @@ -40,6 +40,10 @@ ConVar sk_barnacle_health( "sk_barnacle_health","0"); static ConVar npc_barnacle_swallow( "npc_barnacle_swallow", "0", 0, "Use prototype swallow code." ); +#ifdef MAPBASE +ConVar npc_barnacle_ignite( "npc_barnacle_ignite", "0", FCVAR_NONE, "Allows barnacles to be ignited by flares and beyond." ); +#endif + const char *CNPC_Barnacle::m_szGibNames[NUM_BARNACLE_GIBS] = { "models/gibs/hgibs.mdl", @@ -69,6 +73,9 @@ int g_interactionBarnacleVictimDangle = 0; int g_interactionBarnacleVictimReleased = 0; int g_interactionBarnacleVictimGrab = 0; int g_interactionBarnacleVictimBite = 0; +#ifdef MAPBASE +int g_interactionBarnacleVictimFinalBite = 0; +#endif LINK_ENTITY_TO_CLASS( npc_barnacle, CNPC_Barnacle ); @@ -177,7 +184,7 @@ BEGIN_DATADESC( CNPC_Barnacle ) DEFINE_INPUTFUNC( FIELD_VOID, "DropTongue", InputDropTongue ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDropTongueSpeed", InputSetDropTongueSpeed ), -#ifdef HL2_EPISODIC +#if HL2_EPISODIC || MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "LetGo", InputLetGo ), DEFINE_OUTPUT( m_OnGrab, "OnGrab" ), DEFINE_OUTPUT( m_OnRelease, "OnRelease" ), @@ -187,7 +194,9 @@ BEGIN_DATADESC( CNPC_Barnacle ) DEFINE_THINKFUNC( BarnacleThink ), DEFINE_THINKFUNC( WaitTillDead ), +#ifndef MAPBASE DEFINE_FIELD( m_bSwallowingBomb, FIELD_BOOLEAN ), +#endif END_DATADESC() @@ -275,7 +284,9 @@ void CNPC_Barnacle::Spawn() m_cGibs = 0; m_bLiftingPrey = false; m_bSwallowingPrey = false; +#ifndef MAPBASE m_bSwallowingBomb = false; +#endif m_flDigestFinish = 0; m_takedamage = DAMAGE_YES; m_pConstraint = NULL; @@ -406,6 +417,16 @@ void CNPC_Barnacle::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Barnacle::AllowedToIgnite( void ) +{ + return npc_barnacle_ignite.GetBool(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Initialize tongue position when first spawned // Input : @@ -509,7 +530,11 @@ void CNPC_Barnacle::BarnacleThink ( void ) } else if ( GetEnemy() ) { +#ifdef MAPBASE + if ( m_bLiftingPrey ) +#else if ( m_bLiftingPrey || m_bSwallowingBomb == true ) +#endif { LiftPrey(); } @@ -1145,6 +1170,24 @@ void CNPC_Barnacle::LiftPhysicsObject( float flBiteZOffset ) // If we got a physics prop, wait until the thing has settled down m_bLiftingPrey = false; +#ifdef MAPBASE + Vector tipPos = m_vecTip.Get(); + Activity curAct = GetActivity(); + + // Other, non-character entities use this now + if (pVictim->DispatchInteraction( g_interactionBarnacleVictimBite, &tipPos, this )) + { + // Make sure the interaction isn't making us use an irregular activity + // (e.g. biting) + if (GetActivity() == curAct) + SetActivity( (Activity)ACT_BARNACLE_TASTE_SPIT ); + } + else + { + // Start the spit animation. + SetActivity( (Activity)ACT_BARNACLE_TASTE_SPIT ); + } +#else if ( hl2_episodic.GetBool() ) { CBounceBomb *pBounce = dynamic_cast( pVictim ); @@ -1181,6 +1224,7 @@ void CNPC_Barnacle::LiftPhysicsObject( float flBiteZOffset ) pBCC->DispatchInteraction( g_interactionBarnacleVictimBite, &tipPos, this ); } +#endif #endif } else @@ -1355,7 +1399,7 @@ void CNPC_Barnacle::InputDropTongue( inputdata_t &inputdata ) void CNPC_Barnacle::AttachTongueToTarget( CBaseEntity *pTouchEnt, Vector vecGrabPos ) { -#if HL2_EPISODIC +#if HL2_EPISODIC || MAPBASE m_OnGrab.Set( pTouchEnt, this, this ); #endif @@ -1577,6 +1621,18 @@ void CNPC_Barnacle::BitePrey( void ) CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer(); +#ifdef MAPBASE + if ( pVictim == NULL ) + { + if ( GetEnemy() ) + { + Vector tipPos = m_vecTip.Get(); + GetEnemy()->DispatchInteraction( g_interactionBarnacleVictimFinalBite, &tipPos, this ); + } + + return; + } +#else #ifdef HL2_EPISODIC if ( pVictim == NULL ) { @@ -1614,6 +1670,7 @@ void CNPC_Barnacle::BitePrey( void ) { return; } +#endif EmitSound( "NPC_Barnacle.FinalBite" ); @@ -1701,6 +1758,12 @@ void CNPC_Barnacle::BitePrey( void ) #endif +#ifdef MAPBASE + Vector tipPos = m_vecTip.Get(); + if (pVictim->DispatchInteraction( g_interactionBarnacleVictimFinalBite, &tipPos, this )) + return; +#endif + // Players are never swallowed, nor is anything we don't have a ragdoll for if ( !m_hRagdoll || pVictim->IsPlayer() ) { @@ -1873,7 +1936,7 @@ void CNPC_Barnacle::RemoveRagdoll( bool bDestroyRagdoll ) void CNPC_Barnacle::LostPrey( bool bRemoveRagdoll ) { -#if HL2_EPISODIC +#if HL2_EPISODIC || MAPBASE m_OnRelease.Set( GetEnemy(), this, this ); #endif @@ -1885,13 +1948,20 @@ void CNPC_Barnacle::LostPrey( bool bRemoveRagdoll ) PhysEnableEntityCollisions( this, pEnemy ); #endif +#ifdef MAPBASE + // These can be CBaseEntity-based now + pEnemy->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this ); +#endif + //No one survives being snatched by a barnacle anymore, so leave // this flag set so that their entity gets removed. //GetEnemy()->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE ); CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer(); if ( pVictim ) { +#ifndef MAPBASE pVictim->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this ); +#endif pVictim->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE ); if ( m_hRagdoll ) @@ -2213,6 +2283,11 @@ bool CNPC_Barnacle::IsPoisonous( CBaseEntity *pVictim ) if ( FClassnameIs(pVictim,"npc_headcrab_black") ) return true; +#ifdef MAPBASE + if (FClassnameIs( pVictim, "npc_poisonzombie" )) + return true; +#endif + if ( FClassnameIs(pVictim,"npc_antlion") && static_cast(pVictim)->IsWorker() ) @@ -2220,10 +2295,12 @@ bool CNPC_Barnacle::IsPoisonous( CBaseEntity *pVictim ) return false; } +#endif +#if HL2_EPISODIC || MAPBASE //========================================================= // script input to immediately abandon whatever I am lifting //========================================================= @@ -2240,8 +2317,10 @@ void CNPC_Barnacle::InputLetGo( inputdata_t &inputdata ) LostPrey( false ); } } +#endif +#if HL2_EPISODIC // Barnacle has custom impact damage tables, so it can take grave damage from sawblades. static impactentry_t barnacleLinearTable[] = { @@ -2712,6 +2791,9 @@ AI_BEGIN_CUSTOM_NPC( npc_barnacle, CNPC_Barnacle ) DECLARE_INTERACTION( g_interactionBarnacleVictimReleased ) DECLARE_INTERACTION( g_interactionBarnacleVictimGrab ) DECLARE_INTERACTION( g_interactionBarnacleVictimBite ) +#ifdef MAPBASE + DECLARE_INTERACTION( g_interactionBarnacleVictimFinalBite ) +#endif // Conditions diff --git a/mp/src/game/server/hl2/npc_barnacle.h b/mp/src/game/server/hl2/npc_barnacle.h index bcb191cf..373be24b 100644 --- a/mp/src/game/server/hl2/npc_barnacle.h +++ b/mp/src/game/server/hl2/npc_barnacle.h @@ -84,6 +84,10 @@ public: int OnTakeDamage_Alive( const CTakeDamageInfo &info ); void PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot ); +#ifdef MAPBASE + bool AllowedToIgnite( void ); +#endif + // The tongue's vphysics updated void OnTongueTipUpdated(); @@ -145,6 +149,19 @@ private: +#ifdef MAPBASE + +#if HL2_EPISODIC + /// Decides whether something should poison the barnacle upon eating + static bool IsPoisonous( CBaseEntity *pVictim ); + const impactdamagetable_t &GetPhysicsImpactDamageTable( void ); +#endif + + // Regular HL2 DLL has these now + void InputLetGo( inputdata_t &inputdata ); + COutputEHANDLE m_OnGrab, m_OnRelease; + +#else #if HL2_EPISODIC /// Decides whether something should poison the barnacle upon eating static bool IsPoisonous( CBaseEntity *pVictim ); @@ -153,6 +170,7 @@ private: COutputEHANDLE m_OnGrab, m_OnRelease; const impactdamagetable_t &GetPhysicsImpactDamageTable( void ); +#endif #endif CNetworkVar( float, m_flAltitude ); @@ -195,7 +213,9 @@ private: Vector m_vLastEnemyPos; float m_flLastPull; CSimpleSimTimer m_StuckTimer; +#ifndef MAPBASE // Handled by interactions now bool m_bSwallowingBomb; +#endif #ifdef HL2_EPISODIC bool m_bSwallowingPoison; #endif diff --git a/mp/src/game/server/hl2/npc_barney.cpp b/mp/src/game/server/hl2/npc_barney.cpp index 337d0eb6..02afce18 100644 --- a/mp/src/game/server/hl2/npc_barney.cpp +++ b/mp/src/game/server/hl2/npc_barney.cpp @@ -111,6 +111,9 @@ END_DATADESC() //----------------------------------------------------------------------------- void CNPC_Barney::SelectModel() { +#ifdef MAPBASE + if (GetModelName() == NULL_STRING) +#endif SetModelName( AllocPooledString( BARNEY_MODEL ) ); } @@ -121,7 +124,11 @@ void CNPC_Barney::Spawn( void ) { Precache(); +#ifdef MAPBASE + m_iHealth = sk_barney_health.GetInt(); +#else m_iHealth = 80; +#endif m_iszIdleExpression = MAKE_STRING("scenes/Expressions/BarneyIdle.vcd"); m_iszAlertExpression = MAKE_STRING("scenes/Expressions/BarneyAlert.vcd"); diff --git a/mp/src/game/server/hl2/npc_basescanner.cpp b/mp/src/game/server/hl2/npc_basescanner.cpp index b05441db..a94c77b6 100644 --- a/mp/src/game/server/hl2/npc_basescanner.cpp +++ b/mp/src/game/server/hl2/npc_basescanner.cpp @@ -41,6 +41,10 @@ BEGIN_DATADESC( CNPC_BaseScanner ) DEFINE_FIELD( m_flAttackFarDist, FIELD_FLOAT ), DEFINE_FIELD( m_flAttackRange, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flCustomMaxSpeed, FIELD_FLOAT, "CustomFlightSpeed" ), +#endif + DEFINE_FIELD( m_nPoseTail, FIELD_INTEGER ), DEFINE_FIELD( m_nPoseDynamo, FIELD_INTEGER ), DEFINE_FIELD( m_nPoseFlare, FIELD_INTEGER ), @@ -52,7 +56,11 @@ BEGIN_DATADESC( CNPC_BaseScanner ) DEFINE_FIELD( m_pSmokeTrail, FIELD_CLASSPTR ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDistanceOverride", InputSetDistanceOverride ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFlightSpeed", InputSetFlightSpeed ), +#else DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFlightSpeed", InputSetFlightSpeed ), +#endif DEFINE_THINKFUNC( DiveBombSoundThink ), END_DATADESC() @@ -861,12 +869,16 @@ void CNPC_BaseScanner::SpeakSentence( int sentenceType ) //----------------------------------------------------------------------------- void CNPC_BaseScanner::InputSetFlightSpeed(inputdata_t &inputdata) { +#ifdef MAPBASE + m_flCustomMaxSpeed = inputdata.value.Float(); +#else //FIXME: Currently unsupported /* m_flFlightSpeed = inputdata.value.Int(); m_bFlightSpeedOverridden = (m_flFlightSpeed > 0); */ +#endif } //----------------------------------------------------------------------------- @@ -1655,6 +1667,11 @@ void CNPC_BaseScanner::PainSound( const CTakeDamageInfo &info ) //----------------------------------------------------------------------------- float CNPC_BaseScanner::GetMaxSpeed() { +#ifdef MAPBASE + if (m_flCustomMaxSpeed > 0.0f) + return m_flCustomMaxSpeed; +#endif + return SCANNER_MAX_SPEED; } diff --git a/mp/src/game/server/hl2/npc_basescanner.h b/mp/src/game/server/hl2/npc_basescanner.h index ab69c81c..1a286b81 100644 --- a/mp/src/game/server/hl2/npc_basescanner.h +++ b/mp/src/game/server/hl2/npc_basescanner.h @@ -200,6 +200,11 @@ protected: float m_flAttackFarDist; float m_flAttackRange; +#ifdef MAPBASE + // Custom max speed for mappers to control + float m_flCustomMaxSpeed; +#endif + private: CSoundPatch *m_pEngineSound; diff --git a/mp/src/game/server/hl2/npc_bullsquid.cpp b/mp/src/game/server/hl2/npc_bullsquid.cpp index 2c0706da..954ea868 100644 --- a/mp/src/game/server/hl2/npc_bullsquid.cpp +++ b/mp/src/game/server/hl2/npc_bullsquid.cpp @@ -7,11 +7,11 @@ #include "cbase.h" #include "game.h" -#include "AI_Default.h" -#include "AI_Schedule.h" -#include "AI_Hull.h" -#include "AI_Navigator.h" -#include "AI_Motor.h" +#include "ai_default.h" +#include "ai_schedule.h" +#include "ai_hull.h" +#include "ai_navigator.h" +#include "ai_motor.h" #include "ai_squad.h" #include "npc_bullsquid.h" #include "npcevent.h" @@ -30,8 +30,8 @@ #include "engine/IEngineSound.h" #include "movevars_shared.h" -#include "AI_Hint.h" -#include "AI_Senses.h" +#include "ai_hint.h" +#include "ai_senses.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" diff --git a/mp/src/game/server/hl2/npc_citizen17.cpp b/mp/src/game/server/hl2/npc_citizen17.cpp index adb9eb8c..7b6d6ba8 100644 --- a/mp/src/game/server/hl2/npc_citizen17.cpp +++ b/mp/src/game/server/hl2/npc_citizen17.cpp @@ -25,6 +25,12 @@ #include "eventqueue.h" +#ifdef MAPBASE +#include "hl2_gamerules.h" +#include "mapbase/GlobalStrings.h" +#include "collisionutils.h" +#endif + #include "ai_squad.h" #include "ai_pathfinder.h" #include "ai_route.h" @@ -69,6 +75,10 @@ ConVar npc_citizen_squad_marker( "npc_citizen_squad_marker", "0" ); ConVar npc_citizen_explosive_resist( "npc_citizen_explosive_resist", "0" ); ConVar npc_citizen_auto_player_squad( "npc_citizen_auto_player_squad", "1" ); ConVar npc_citizen_auto_player_squad_allow_use( "npc_citizen_auto_player_squad_allow_use", "0" ); +#ifdef MAPBASE +ConVar npc_citizen_squad_secondary_toggle_use_button("npc_citizen_squad_toggle_use_button", "262144"); // IN_WALK by default +ConVar npc_citizen_squad_secondary_toggle_use_always( "npc_citizen_squad_secondary_toggle_use_always", "0", FCVAR_NONE, "Allows all citizens not strictly stuck to the player's squad to be toggled via Alt + E." ); +#endif ConVar npc_citizen_dont_precache_all( "npc_citizen_dont_precache_all", "0" ); @@ -84,15 +94,29 @@ ConVar sk_citizen_heal_toss_player_delay("sk_citizen_heal_toss_player_delay", "2 #define MEDIC_THROW_SPEED npc_citizen_medic_throw_speed.GetFloat() +#ifdef MAPBASE +// We use a boolean now, so NameMatches("griggs") is handled in CNPC_Citizen::Spawn(). +#define USE_EXPERIMENTAL_MEDIC_CODE() (npc_citizen_heal_chuck_medkit.GetBool() && m_bTossesMedkits) +#else #define USE_EXPERIMENTAL_MEDIC_CODE() (npc_citizen_heal_chuck_medkit.GetBool() && NameMatches("griggs")) #endif +#endif +#ifdef MAPBASE +ConVar player_squad_autosummon_enabled( "player_squad_autosummon_enabled", "1" ); +#endif ConVar player_squad_autosummon_time( "player_squad_autosummon_time", "5" ); ConVar player_squad_autosummon_move_tolerance( "player_squad_autosummon_move_tolerance", "20" ); ConVar player_squad_autosummon_player_tolerance( "player_squad_autosummon_player_tolerance", "10" ); ConVar player_squad_autosummon_time_after_combat( "player_squad_autosummon_time_after_combat", "8" ); ConVar player_squad_autosummon_debug( "player_squad_autosummon_debug", "0" ); +#ifdef MAPBASE +ConVar npc_citizen_resupplier_adjust_ammo("npc_citizen_resupplier_adjust_ammo", "1", FCVAR_NONE, "If what ammo we give to the player would go over their max, should we adjust what we give accordingly (1) or cancel it altogether? (0)" ); + +ConVar npc_citizen_nocollide_player( "npc_citizen_nocollide_player", "0" ); +#endif + #define ShouldAutosquad() (npc_citizen_auto_player_squad.GetBool()) enum SquadSlot_T @@ -247,6 +271,10 @@ class CMattsPipe : public CWeaponCrowbar void SetPickupTouch( void ) { /* do nothing */ } }; +#ifdef MAPBASE +LINK_ENTITY_TO_CLASS(weapon_mattpipe, CMattsPipe); +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -335,6 +363,10 @@ BEGIN_DATADESC( CNPC_Citizen ) DEFINE_KEYFIELD( m_bNotifyNavFailBlocked, FIELD_BOOLEAN, "notifynavfailblocked" ), DEFINE_KEYFIELD( m_bNeverLeavePlayerSquad, FIELD_BOOLEAN, "neverleaveplayersquad" ), DEFINE_KEYFIELD( m_iszDenyCommandConcept, FIELD_STRING, "denycommandconcept" ), +#ifdef MAPBASE + DEFINE_INPUT( m_bTossesMedkits, FIELD_BOOLEAN, "SetTossMedkits" ), + DEFINE_KEYFIELD( m_bAlternateAiming, FIELD_BOOLEAN, "AlternateAiming" ), +#endif DEFINE_OUTPUT( m_OnJoinedPlayerSquad, "OnJoinedPlayerSquad" ), DEFINE_OUTPUT( m_OnLeftPlayerSquad, "OnLeftPlayerSquad" ), @@ -342,11 +374,20 @@ BEGIN_DATADESC( CNPC_Citizen ) DEFINE_OUTPUT( m_OnStationOrder, "OnStationOrder" ), DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), DEFINE_OUTPUT( m_OnNavFailBlocked, "OnNavFailBlocked" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnHealedNPC, "OnHealedNPC" ), + DEFINE_OUTPUT( m_OnHealedPlayer, "OnHealedPlayer" ), + DEFINE_OUTPUT( m_OnThrowMedkit, "OnTossMedkit" ), + DEFINE_OUTPUT( m_OnGiveAmmo, "OnGiveAmmo" ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "RemoveFromPlayerSquad", InputRemoveFromPlayerSquad ), DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrolling", InputStartPatrolling ), DEFINE_INPUTFUNC( FIELD_VOID, "StopPatrolling", InputStopPatrolling ), DEFINE_INPUTFUNC( FIELD_VOID, "SetCommandable", InputSetCommandable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "SetUnCommandable", InputSetUnCommandable ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "SetMedicOn", InputSetMedicOn ), DEFINE_INPUTFUNC( FIELD_VOID, "SetMedicOff", InputSetMedicOff ), DEFINE_INPUTFUNC( FIELD_VOID, "SetAmmoResupplierOn", InputSetAmmoResupplierOn ), @@ -357,6 +398,10 @@ BEGIN_DATADESC( CNPC_Citizen ) DEFINE_INPUTFUNC( FIELD_VOID, "ThrowHealthKit", InputForceHealthKitToss ), #endif +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ), +#endif + DEFINE_USEFUNC( CommanderUse ), DEFINE_USEFUNC( SimpleUse ), @@ -374,6 +419,10 @@ bool CNPC_Citizen::CreateBehaviors() { BaseClass::CreateBehaviors(); AddBehavior( &m_FuncTankBehavior ); +#ifdef MAPBASE + AddBehavior( &m_RappelBehavior ); + AddBehavior( &m_PolicingBehavior ); +#endif return true; } @@ -481,6 +530,26 @@ void CNPC_Citizen::Spawn() m_bShouldPatrol = false; m_iHealth = sk_citizen_health.GetFloat(); +#ifdef MAPBASE + // Now only gets citizen_trains. + if ( GetMoveParent() && FClassnameIs( GetMoveParent(), "func_tracktrain" ) ) + { + if ( NameMatches("citizen_train_2") ) + { + CapabilitiesRemove( bits_CAP_MOVE_GROUND ); + SetMoveType( MOVETYPE_NONE ); + SetSequenceByName( "d1_t01_TrainRide_Sit_Idle" ); + SetIdealActivity( ACT_DO_NOT_DISTURB ); + } + else if ( NameMatches("citizen_train_1") ) + { + CapabilitiesRemove( bits_CAP_MOVE_GROUND ); + SetMoveType( MOVETYPE_NONE ); + SetSequenceByName( "d1_t01_TrainRide_Stand" ); + SetIdealActivity( ACT_DO_NOT_DISTURB ); + } + } +#else // Are we on a train? Used in trainstation to have NPCs on trains. if ( GetMoveParent() && FClassnameIs( GetMoveParent(), "func_tracktrain" ) ) { @@ -497,6 +566,7 @@ void CNPC_Citizen::Spawn() SetIdealActivity( ACT_DO_NOT_DISTURB ); } } +#endif m_flStopManhackFlinch = -1; @@ -528,6 +598,13 @@ void CNPC_Citizen::Spawn() // Use render bounds instead of human hull for guys sitting in chairs, etc. m_ActBusyBehavior.SetUseRenderBounds( HasSpawnFlags( SF_CITIZEN_USE_RENDER_BOUNDS ) ); + +#ifdef MAPBASE + if (NameMatches("griggs")) + { + m_bTossesMedkits = true; + } +#endif } //----------------------------------------------------------------------------- @@ -589,6 +666,14 @@ void CNPC_Citizen::SelectModel() if ( m_Type == CT_DEFAULT ) { +#ifdef MAPBASE + if (HL2GameRules()->GetDefaultCitizenType() != CT_DEFAULT) + { + m_Type = static_cast(HL2GameRules()->GetDefaultCitizenType()); + } + else + { +#endif struct CitizenTypeMapping { const char *pszMapTag; @@ -621,6 +706,9 @@ void CNPC_Citizen::SelectModel() if ( m_Type == CT_DEFAULT ) m_Type = CT_DOWNTRODDEN; +#ifdef MAPBASE + } +#endif } if( HasSpawnFlags( SF_CITIZEN_RANDOM_HEAD | SF_CITIZEN_RANDOM_HEAD_MALE | SF_CITIZEN_RANDOM_HEAD_FEMALE ) || GetModelName() == NULL_STRING ) @@ -751,7 +839,11 @@ void CNPC_Citizen::SelectExpressionType() void CNPC_Citizen::FixupMattWeapon() { CBaseCombatWeapon *pWeapon = GetActiveWeapon(); +#ifdef MAPBASE + if ( pWeapon && EntIsClass( pWeapon, gm_isz_class_Crowbar ) && NameMatches( "matt" ) ) +#else if ( pWeapon && pWeapon->ClassMatches( "weapon_crowbar" ) && NameMatches( "matt" ) ) +#endif { Weapon_Drop( pWeapon ); UTIL_Remove( pWeapon ); @@ -1165,6 +1257,19 @@ int CNPC_Citizen::SelectFailSchedule( int failedSchedule, int failedTask, AI_Tas //----------------------------------------------------------------------------- int CNPC_Citizen::SelectSchedule() { +#ifdef MAPBASE + if ( IsWaitingToRappel() && BehaviorSelectSchedule() ) + { + return BaseClass::SelectSchedule(); + } + + if ( GetMoveType() == MOVETYPE_NONE && !Q_strncmp(STRING(GetEntityName()), "citizen_train_", 14) ) + { + // Only "sit on train" if we're a citizen_train_ + Assert( GetMoveParent() && FClassnameIs( GetMoveParent(), "func_tracktrain" ) ); + return SCHED_CITIZEN_SIT_ON_TRAIN; + } +#else // If we can't move, we're on a train, and should be sitting. if ( GetMoveType() == MOVETYPE_NONE ) { @@ -1173,13 +1278,23 @@ int CNPC_Citizen::SelectSchedule() Assert( GetMoveParent() && FClassnameIs( GetMoveParent(), "func_tracktrain" ) ); return SCHED_CITIZEN_SIT_ON_TRAIN; } +#endif +#ifdef MAPBASE + if ( GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_RPG) ) + { + CWeaponRPG *pRPG = static_cast(GetActiveWeapon()); +#else CWeaponRPG *pRPG = dynamic_cast(GetActiveWeapon()); +#endif if ( pRPG && pRPG->IsGuiding() ) { DevMsg( "Citizen in select schedule but RPG is guiding?\n"); pRPG->StopGuiding(); } +#ifdef MAPBASE + } +#endif return BaseClass::SelectSchedule(); } @@ -1479,9 +1594,15 @@ int CNPC_Citizen::TranslateSchedule( int scheduleType ) case SCHED_RANGE_ATTACK1: // If we have an RPG, we use a custom schedule for it +#ifdef MAPBASE + if ( !IsMortar( GetEnemy() ) && GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_RPG) ) + { + if ( GetEnemy() && EntIsClass(GetEnemy(), gm_isz_class_Strider) ) +#else if ( !IsMortar( GetEnemy() ) && GetActiveWeapon() && FClassnameIs( GetActiveWeapon(), "weapon_rpg" ) ) { if ( GetEnemy() && GetEnemy()->ClassMatches( "npc_strider" ) ) +#endif { if (OccupyStrategySlotRange( SQUAD_SLOT_CITIZEN_RPG1, SQUAD_SLOT_CITIZEN_RPG2 ) ) { @@ -1494,14 +1615,21 @@ int CNPC_Citizen::TranslateSchedule( int scheduleType ) } else { +#ifndef MAPBASE // This has been disabled for now. CBasePlayer *pPlayer = AI_GetSinglePlayer(); +#ifdef MAPBASE + // Don't avoid player if notarget is on + if ( pPlayer && GetEnemy() && !(pPlayer->GetFlags() & FL_NOTARGET) && ( ( GetEnemy()->GetAbsOrigin() - +#else if ( pPlayer && GetEnemy() && ( ( GetEnemy()->GetAbsOrigin() - +#endif pPlayer->GetAbsOrigin() ).LengthSqr() < RPG_SAFE_DISTANCE * RPG_SAFE_DISTANCE ) ) { // Don't fire our RPG at an enemy too close to the player return SCHED_STANDOFF; } else +#endif { return SCHED_CITIZEN_RANGE_ATTACK1_RPG; } @@ -1767,13 +1895,20 @@ void CNPC_Citizen::RunTask( const Task_t *pTask ) } Vector vecEnemyPos = GetEnemy()->BodyTarget(GetAbsOrigin(), false); +#ifndef MAPBASE // This has been disabled for now. CBasePlayer *pPlayer = AI_GetSinglePlayer(); +#ifdef MAPBASE + // Don't avoid player if notarget is on + if ( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) && ( ( vecEnemyPos - pPlayer->GetAbsOrigin() ).LengthSqr() < RPG_SAFE_DISTANCE * RPG_SAFE_DISTANCE ) ) +#else if ( pPlayer && ( ( vecEnemyPos - pPlayer->GetAbsOrigin() ).LengthSqr() < RPG_SAFE_DISTANCE * RPG_SAFE_DISTANCE ) ) +#endif { m_bRPGAvoidPlayer = true; Speak( TLK_WATCHOUT ); } else +#endif { // Pull the laserdot towards the target Vector vecToTarget = (vecEnemyPos - vecLaserPos); @@ -1797,7 +1932,12 @@ void CNPC_Citizen::RunTask( const Task_t *pTask ) return; } // Add imprecision to avoid obvious robotic perfection stationary targets +#ifdef MAPBASE + // More imprecision with low-accuracy citizens + float imprecision = 18*sin(gpGlobals->curtime) + cosh(GetCurrentWeaponProficiency() - 4); +#else float imprecision = 18*sin(gpGlobals->curtime); +#endif vecLaserPos.x += imprecision; vecLaserPos.y += imprecision; vecLaserPos.z += imprecision; @@ -1838,9 +1978,18 @@ Activity CNPC_Citizen::NPC_TranslateActivity( Activity activity ) { if ( activity == ACT_MELEE_ATTACK1 ) { +#ifdef MAPBASE + // It could be the new weapon punt activity. + if (GetActiveWeapon() && GetActiveWeapon()->IsMeleeWeapon()) + { + return ACT_MELEE_ATTACK_SWING; + } +#else return ACT_MELEE_ATTACK_SWING; +#endif } +#ifndef MAPBASE // Covered by the new backup activity system // !!!HACK - Citizens don't have the required animations for shotguns, // so trick them into using the rifle counterparts for now (sjb) if ( activity == ACT_RUN_AIM_SHOTGUN ) @@ -1851,6 +2000,22 @@ Activity CNPC_Citizen::NPC_TranslateActivity( Activity activity ) return ACT_IDLE_ANGRY_SMG1; if ( activity == ACT_RANGE_ATTACK_SHOTGUN_LOW ) return ACT_RANGE_ATTACK_SMG1_LOW; +#endif + +#ifdef MAPBASE + if (m_bAlternateAiming) + { + if (activity == ACT_RUN_AIM_RIFLE) + return ACT_RUN_AIM_RIFLE_STIMULATED; + if (activity == ACT_WALK_AIM_RIFLE) + return ACT_WALK_AIM_RIFLE_STIMULATED; + + if (activity == ACT_RUN_AIM_AR2) + return ACT_RUN_AIM_AR2_STIMULATED; + if (activity == ACT_WALK_AIM_AR2) + return ACT_WALK_AIM_AR2_STIMULATED; + } +#endif return BaseClass::NPC_TranslateActivity( activity ); } @@ -1878,7 +2043,12 @@ void CNPC_Citizen::HandleAnimEvent( animevent_t *pEvent ) { // Heal my target (if within range) #if HL2_EPISODIC +#ifdef MAPBASE + // Don't throw medkits at NPCs, that's not how it works + if ( USE_EXPERIMENTAL_MEDIC_CODE() && IsMedic() && GetTarget() && !GetTarget()->IsNPC() ) +#else if ( USE_EXPERIMENTAL_MEDIC_CODE() && IsMedic() ) +#endif { CBaseCombatCharacter *pTarget = dynamic_cast( GetTarget() ); Assert(pTarget); @@ -1918,6 +2088,7 @@ void CNPC_Citizen::HandleAnimEvent( animevent_t *pEvent ) } } +#ifndef MAPBASE // Moved to CAI_BaseNPC //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void CNPC_Citizen::PickupItem( CBaseEntity *pItem ) @@ -1944,6 +2115,7 @@ void CNPC_Citizen::PickupItem( CBaseEntity *pItem ) DevMsg("Citizen doesn't know how to pick up %s!\n", pItem->GetClassname() ); } } +#endif //----------------------------------------------------------------------------- // Purpose: @@ -2092,7 +2264,12 @@ bool CNPC_Citizen::IsManhackMeleeCombatant() { CBaseCombatWeapon *pWeapon = GetActiveWeapon(); CBaseEntity *pEnemy = GetEnemy(); +#ifdef MAPBASE + // Any melee weapon passes + return ( pEnemy && pWeapon && pEnemy->Classify() == CLASS_MANHACK && pWeapon->IsMeleeWeapon() ); +#else return ( pEnemy && pWeapon && pEnemy->Classify() == CLASS_MANHACK && pWeapon->ClassMatches( "weapon_crowbar" ) ); +#endif } //----------------------------------------------------------------------------- @@ -2101,13 +2278,25 @@ bool CNPC_Citizen::IsManhackMeleeCombatant() //----------------------------------------------------------------------------- Vector CNPC_Citizen::GetActualShootPosition( const Vector &shootOrigin ) { +#ifdef MAPBASE + // The code below is probably broken. If not, it definitely isn't very effective. + return BaseClass::GetActualShootPosition( shootOrigin ); +#else Vector vecTarget = BaseClass::GetActualShootPosition( shootOrigin ); +#ifdef MAPBASE + // If we're firing an RPG at a gunship, aim off to it's side, because we'll auger towards it. + if ( GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_RPG) && GetEnemy() ) + { + CWeaponRPG *pRPG = static_cast(GetActiveWeapon()); + if ( EntIsClass( GetEnemy(), gm_isz_class_Gunship ) ) +#else CWeaponRPG *pRPG = dynamic_cast(GetActiveWeapon()); // If we're firing an RPG at a gunship, aim off to it's side, because we'll auger towards it. if ( pRPG && GetEnemy() ) { if ( FClassnameIs( GetEnemy(), "npc_combinegunship" ) ) +#endif { Vector vecRight; GetVectors( NULL, &vecRight, NULL ); @@ -2146,6 +2335,7 @@ Vector CNPC_Citizen::GetActualShootPosition( const Vector &shootOrigin ) } return vecTarget; +#endif } //----------------------------------------------------------------------------- @@ -2194,19 +2384,31 @@ bool CNPC_Citizen::ShouldLookForBetterWeapon() { bool bDefer = false; +#ifdef MAPBASE + if ( EntIsClass(pWeapon, gm_isz_class_AR2) ) +#else if( FClassnameIs( pWeapon, "weapon_ar2" ) ) +#endif { // Content to keep this weapon forever m_flNextWeaponSearchTime = OTHER_DEFER_SEARCH_TIME; bDefer = true; } +#ifdef MAPBASE + else if( EntIsClass(pWeapon, gm_isz_class_RPG) ) +#else else if( FClassnameIs( pWeapon, "weapon_rpg" ) ) +#endif { // Content to keep this weapon forever m_flNextWeaponSearchTime = OTHER_DEFER_SEARCH_TIME; bDefer = true; } +#ifdef MAPBASE + else if ( EntIsClass(pWeapon, gm_isz_class_Shotgun) ) +#else else if( FClassnameIs( pWeapon, "weapon_shotgun" ) ) +#endif { // Shotgunners do not defer their weapon search indefinitely. // If more than one citizen in the squad has a shotgun, we force @@ -2294,6 +2496,20 @@ int CNPC_Citizen::OnTakeDamage_Alive( const CTakeDamageInfo &info ) return BaseClass::OnTakeDamage_Alive( newInfo ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_Citizen::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + + // No need to tell me. + set.AppendCriteria("medic", IsMedic() ? "1" : "0"); + + set.AppendCriteria("citizentype", UTIL_VarArgs("%i", m_Type)); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CNPC_Citizen::IsCommandable() @@ -2337,6 +2553,11 @@ bool CNPC_Citizen::CanJoinPlayerSquad() if ( IRelationType( UTIL_GetLocalPlayer() ) != D_LI ) return false; +#ifdef MAPBASE + if ( IsWaitingToRappel() ) + return false; +#endif + return true; } @@ -2511,6 +2732,64 @@ bool CNPC_Citizen::SpeakCommandResponse( AIConcept_t concept, const char *modifi ( modifiers ) ? CFmtStr(",%s", modifiers).operator const char *() : "" ) ); } +#ifdef MAPBASE +extern ConVar ai_debug_avoidancebounds; + +//----------------------------------------------------------------------------- +// Purpose: Implements player nocollide. +//----------------------------------------------------------------------------- +void CNPC_Citizen::SetPlayerAvoidState( void ) +{ + bool bShouldPlayerAvoid = false; + Vector vNothing; + + GetSequenceLinearMotion( GetSequence(), &vNothing ); + bool bIsMoving = ( IsMoving() || ( vNothing != vec3_origin ) ); + + m_bPlayerAvoidState = ShouldPlayerAvoid(); + bool bSquadNoCollide = (IsInPlayerSquad() && npc_citizen_nocollide_player.GetBool()); + + // If we are coming out of a script, check if we are stuck inside the player. + if ( m_bPerformAvoidance || ( m_bPlayerAvoidState && bIsMoving ) || bSquadNoCollide ) + { + trace_t trace; + Vector vMins, vMaxs; + + GetPlayerAvoidBounds( &vMins, &vMaxs ); + + CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); + + if ( pLocalPlayer ) + { + bShouldPlayerAvoid = (!bSquadNoCollide || !pLocalPlayer->IsMoving()) && IsBoxIntersectingBox( GetAbsOrigin() + vMins, GetAbsOrigin() + vMaxs, + pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMins(), pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMaxs() ); + } + + if ( ai_debug_avoidancebounds.GetBool() ) + { + int iRed = ( bShouldPlayerAvoid == true ) ? 255 : 0; + + NDebugOverlay::Box( GetAbsOrigin(), vMins, vMaxs, iRed, 0, 255, 64, 0.1 ); + } + } + + m_bPerformAvoidance = bShouldPlayerAvoid; + + if ( GetCollisionGroup() == COLLISION_GROUP_NPC || GetCollisionGroup() == COLLISION_GROUP_NPC_ACTOR ) + { + if ( m_bPerformAvoidance == true || + (bSquadNoCollide && !m_bPlayerAvoidState)) + { + SetCollisionGroup( COLLISION_GROUP_NPC_ACTOR ); + } + else + { + SetCollisionGroup( COLLISION_GROUP_NPC ); + } + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: return TRUE if the commander mode should try to give this order // to more people. return FALSE otherwise. For instance, we don't @@ -2553,7 +2832,11 @@ void CNPC_Citizen::MoveOrder( const Vector &vecDest, CAI_BaseNPC **Allies, int n if ( !AI_IsSinglePlayer() ) return; +#ifdef MAPBASE + if ( m_iszDenyCommandConcept != NULL_STRING ) +#else if( hl2_episodic.GetBool() && m_iszDenyCommandConcept != NULL_STRING ) +#endif { SpeakCommandResponse( STRING(m_iszDenyCommandConcept) ); return; @@ -2635,6 +2918,28 @@ void CNPC_Citizen::OnMoveOrder() BaseClass::OnMoveOrder(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline bool CNPC_Citizen::ShouldAllowSquadToggleUse( CBasePlayer *pPlayer ) +{ + if (HasSpawnFlags( SF_CITIZEN_NOT_COMMANDABLE )) + return false; + + //if (!HL2GameRules() || !HL2GameRules()->AllowSquadToggleUse()) + if (!HasSpawnFlags( SF_CITIZEN_PLAYER_TOGGLE_SQUAD )) + { + if (!npc_citizen_squad_secondary_toggle_use_always.GetBool() || m_bNeverLeavePlayerSquad) + return false; + + // npc_citizen_squad_secondary_toggle_use_always was invoked + AddSpawnFlags( SF_CITIZEN_PLAYER_TOGGLE_SQUAD ); + } + + return (pPlayer->m_nButtons & npc_citizen_squad_secondary_toggle_use_button.GetInt()) != 0; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_Citizen::CommanderUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) @@ -2654,6 +2959,31 @@ void CNPC_Citizen::CommanderUse( CBaseEntity *pActivator, CBaseEntity *pCaller, // Don't say hi after you've been addressed by the player SetSpokeConcept( TLK_HELLO, NULL ); +#ifdef MAPBASE + if ( ShouldAllowSquadToggleUse(UTIL_GetLocalPlayer()) || npc_citizen_auto_player_squad_allow_use.GetBool() ) + { + // Version of TogglePlayerSquadState() that has "used" as a modifier + static const char *szSquadUseModifier = "used:1"; + if ( !IsInPlayerSquad() ) + { + AddToPlayerSquad(); + + if ( HaveCommandGoal() ) + { + SpeakCommandResponse( TLK_COMMANDED, szSquadUseModifier ); + } + else if ( m_FollowBehavior.GetFollowTarget() == UTIL_GetLocalPlayer() ) + { + SpeakCommandResponse( TLK_STARTFOLLOW, szSquadUseModifier ); + } + } + else + { + SpeakCommandResponse( TLK_STOPFOLLOW, szSquadUseModifier ); + RemoveFromPlayerSquad(); + } + } +#else if ( npc_citizen_auto_player_squad_allow_use.GetBool() ) { if ( !ShouldAutosquad() ) @@ -2661,8 +2991,37 @@ void CNPC_Citizen::CommanderUse( CBaseEntity *pActivator, CBaseEntity *pCaller, else if ( !IsInPlayerSquad() && npc_citizen_auto_player_squad_allow_use.GetBool() ) AddToPlayerSquad(); } +#endif else if ( GetCurSchedule() && ConditionInterruptsCurSchedule( COND_IDLE_INTERRUPT ) ) { +#ifdef MAPBASE + // Just do regular idle question behavior so question groups, etc. work on +USE. + if ( IsAllowedToSpeak( TLK_QUESTION, true ) ) + { + // 1 = Old "SpeakIdleResponse" behavior + // 2, 3 = AskQuestion() for QA groups, etc. + // 4 = Just speak + int iRandom = random->RandomInt(1, 4); + if ( iRandom == 1 ) + { + CBaseEntity *pRespondant = FindSpeechTarget( AIST_NPCS ); + if ( pRespondant ) + { + g_EventQueue.AddEvent( pRespondant, "SpeakIdleResponse", ( GetTimeSpeechComplete() - gpGlobals->curtime ) + .2, this, this ); + } + } + if ( iRandom < 4 ) + { + // Ask someone else + AskQuestionNow(); + } + else + { + // Just speak + Speak( TLK_QUESTION ); + } + } +#else if ( SpeakIfAllowed( TLK_QUESTION, NULL, true ) ) { if ( random->RandomInt( 1, 4 ) < 4 ) @@ -2674,6 +3033,7 @@ void CNPC_Citizen::CommanderUse( CBaseEntity *pActivator, CBaseEntity *pCaller, } } } +#endif } } } @@ -2883,6 +3243,11 @@ void CNPC_Citizen::UpdatePlayerSquad() if ( !pCitizen->CanJoinPlayerSquad() ) continue; +#ifdef MAPBASE + if ( pCitizen->HasSpawnFlags(SF_CITIZEN_PLAYER_TOGGLE_SQUAD) ) + continue; +#endif + bool bShouldAdd = false; if ( pCitizen->HasCondition( COND_SEE_PLAYER ) ) @@ -2940,7 +3305,11 @@ void CNPC_Citizen::UpdatePlayerSquad() if ( ppAIs[j]->GetClassname() != GetClassname() ) continue; +#ifdef MAPBASE + if ( ppAIs[j]->HasSpawnFlags( SF_CITIZEN_NOT_COMMANDABLE | SF_CITIZEN_PLAYER_TOGGLE_SQUAD ) ) +#else if ( ppAIs[j]->HasSpawnFlags( SF_CITIZEN_NOT_COMMANDABLE ) ) +#endif continue; CNPC_Citizen *pCitizen = assert_cast(ppAIs[j]); @@ -3415,7 +3784,11 @@ bool CNPC_Citizen::ShouldHealTarget( CBaseEntity *pTarget, bool bActiveUse ) { Disposition_t disposition; +#ifdef MAPBASE + if ( pTarget && ( ( disposition = IRelationType( pTarget ) ) != D_LI && disposition != D_NU ) ) +#else if ( !pTarget && ( ( disposition = IRelationType( pTarget ) ) != D_LI && disposition != D_NU ) ) +#endif return false; // Don't heal if I'm in the middle of talking @@ -3497,6 +3870,14 @@ bool CNPC_Citizen::ShouldHealTarget( CBaseEntity *pTarget, bool bActiveUse ) if ( ((CBasePlayer*)pTarget)->Weapon_GetWpnForAmmo( iAmmoType ) ) return true; } +#ifdef MAPBASE + else if ( (iMax - iCount) < m_iAmmoAmount && (iMax - iCount) != 0 ) + { + // If we're allowed to adjust our ammo, the amount of ammo we give may be reduced, but that's better than not giving any at all! + if (npc_citizen_resupplier_adjust_ammo.GetBool() == true && ((CBasePlayer*)pTarget)->Weapon_GetWpnForAmmo( iAmmoType )) + return true; + } +#endif } } } @@ -3515,14 +3896,25 @@ bool CNPC_Citizen::ShouldHealTossTarget( CBaseEntity *pTarget, bool bActiveUse ) if ( !IsMedic() ) return false; +#ifdef MAPBASE + if ( pTarget && ( ( disposition = IRelationType( pTarget ) ) != D_LI && disposition != D_NU ) ) +#else if ( !pTarget && ( ( disposition = IRelationType( pTarget ) ) != D_LI && disposition != D_NU ) ) +#endif return false; // Don't heal if I'm in the middle of talking if ( IsSpeaking() ) return false; +#ifdef MAPBASE + // NPCs cannot be healed by throwing medkits at them. + // I don't think NPCs even pass through this function anyway, it's just the actual heal event that's the problem. + if (!pTarget->IsPlayer()) + return false; +#else bool bTargetIsPlayer = pTarget->IsPlayer(); +#endif // Don't heal or give ammo to targets in vehicles CBaseCombatCharacter *pCCTarget = pTarget->MyCombatCharacterPointer(); @@ -3550,18 +3942,26 @@ bool CNPC_Citizen::ShouldHealTossTarget( CBaseEntity *pTarget, bool bActiveUse ) } // Are we ready to heal again? +#ifdef MAPBASE + bool bReadyToHeal = m_flPlayerHealTime <= gpGlobals->curtime; +#else bool bReadyToHeal = ( ( bTargetIsPlayer && m_flPlayerHealTime <= gpGlobals->curtime ) || ( !bTargetIsPlayer && m_flAllyHealTime <= gpGlobals->curtime ) ); +#endif // Only heal if we're ready if ( bReadyToHeal ) { int requiredHealth; +#ifdef MAPBASE + requiredHealth = pTarget->GetMaxHealth() - sk_citizen_heal_player.GetFloat(); +#else if ( bTargetIsPlayer ) requiredHealth = pTarget->GetMaxHealth() - sk_citizen_heal_player.GetFloat(); else requiredHealth = pTarget->GetMaxHealth() * sk_citizen_heal_player_min_pct.GetFloat(); +#endif if ( ( pTarget->m_iHealth <= requiredHealth ) && IRelationType( pTarget ) == D_LI ) return true; @@ -3583,6 +3983,11 @@ void CNPC_Citizen::Heal() CBaseEntity *pTarget = GetTarget(); +#ifdef MAPBASE + if ( !pTarget ) + return; +#endif + Vector target = pTarget->GetAbsOrigin() - GetAbsOrigin(); if ( target.Length() > HEAL_TARGET_RANGE * 2 ) return; @@ -3625,6 +4030,10 @@ void CNPC_Citizen::Heal() EmitSound( filter, pTarget->entindex(), "HealthKit.Touch" ); } +#ifdef MAPBASE + pTarget->IsPlayer() ? m_OnHealedPlayer.FireOutput(pTarget, this) : m_OnHealedNPC.FireOutput(pTarget, this); +#endif + pTarget->TakeHealth( healAmt, DMG_GENERIC ); pTarget->RemoveAllDecals(); } @@ -3643,6 +4052,10 @@ void CNPC_Citizen::Heal() else { ((CBasePlayer*)pTarget)->GiveAmmo( m_iAmmoAmount, iAmmoType, false ); + +#ifdef MAPBASE + m_OnGiveAmmo.FireOutput(pTarget, this); +#endif } m_flPlayerGiveAmmoTime = gpGlobals->curtime + sk_citizen_giveammo_player_delay.GetFloat(); @@ -3715,6 +4128,10 @@ void CNPC_Citizen::TossHealthKit(CBaseCombatCharacter *pThrowAt, const Vector &o pPhysicsObject->SetVelocity( &tossVelocity, &angDummy ); } } + +#ifdef MAPBASE + m_OnThrowMedkit.Set(pHealthKit, pHealthKit, this); +#endif } else { @@ -3790,6 +4207,16 @@ void CNPC_Citizen::InputSetCommandable( inputdata_t &inputdata ) gm_PlayerSquadEvaluateTimer.Force(); } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CNPC_Citizen::InputSetUnCommandable( inputdata_t &inputdata ) +{ + AddSpawnFlags( SF_CITIZEN_NOT_COMMANDABLE ); + RemoveFromPlayerSquad(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -3833,6 +4260,39 @@ void CNPC_Citizen::InputSpeakIdleResponse( inputdata_t &inputdata ) SpeakIfAllowed( TLK_ANSWER, NULL, true ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_Citizen::InputSetPoliceGoal( inputdata_t &inputdata ) +{ + if (/*!inputdata.value.String() ||*/ inputdata.value.String()[0] == 0) + { + m_PolicingBehavior.Disable(); + return; + } + + CBaseEntity *pGoal = gEntList.FindEntityByName( NULL, inputdata.value.String() ); + + if ( pGoal == NULL ) + { + DevMsg( "SetPoliceGoal: %s (%s) unable to find ai_goal_police: %s\n", GetClassname(), GetDebugName(), inputdata.value.String() ); + return; + } + + CAI_PoliceGoal *pPoliceGoal = dynamic_cast(pGoal); + + if ( pPoliceGoal == NULL ) + { + DevMsg( "SetPoliceGoal: %s (%s)'s target %s is not an ai_goal_police entity!\n", GetClassname(), GetDebugName(), inputdata.value.String() ); + return; + } + + m_PolicingBehavior.Enable( pPoliceGoal ); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_Citizen::DeathSound( const CTakeDamageInfo &info ) @@ -3840,7 +4300,13 @@ void CNPC_Citizen::DeathSound( const CTakeDamageInfo &info ) // Sentences don't play on dead NPCs SentenceStop(); +#ifdef MAPBASE + AI_CriteriaSet set; + ModifyOrAppendDamageCriteria(set, info); + Speak( TLK_DEATH, set ); +#else EmitSound( "NPC_Citizen.Die" ); +#endif } //------------------------------------------------------------------------------ diff --git a/mp/src/game/server/hl2/npc_citizen17.h b/mp/src/game/server/hl2/npc_citizen17.h index 49efbc7a..35f39a05 100644 --- a/mp/src/game/server/hl2/npc_citizen17.h +++ b/mp/src/game/server/hl2/npc_citizen17.h @@ -11,6 +11,10 @@ #include "npc_playercompanion.h" #include "ai_behavior_functank.h" +#ifdef MAPBASE +#include "ai_behavior_rappel.h" +#include "ai_behavior_police.h" +#endif struct SquadCandidate_t; @@ -33,6 +37,9 @@ struct SquadCandidate_t; #define SF_CITIZEN_RANDOM_HEAD_MALE ( 1 << 22 ) //4194304 #define SF_CITIZEN_RANDOM_HEAD_FEMALE ( 1 << 23 )//8388608 #define SF_CITIZEN_USE_RENDER_BOUNDS ( 1 << 24 )//16777216 +#ifdef MAPBASE +#define SF_CITIZEN_PLAYER_TOGGLE_SQUAD ( 1 << 25 ) //33554432 Prevents the citizen from joining the squad automatically, but still being commandable if the player toggles it +#endif //------------------------------------- // Animation events @@ -130,7 +137,9 @@ public: void HandleAnimEvent( animevent_t *pEvent ); void TaskFail( AI_TaskFailureCode_t code ); +#ifndef MAPBASE // Moved to CAI_BaseNPC void PickupItem( CBaseEntity *pItem ); +#endif void SimpleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -161,6 +170,11 @@ public: // Damage handling //--------------------------------- int OnTakeDamage_Alive( const CTakeDamageInfo &info ); + +#ifdef MAPBASE + //--------------------------------- + void ModifyOrAppendCriteria( AI_CriteriaSet& set ); +#endif //--------------------------------- // Commander mode @@ -179,6 +193,9 @@ public: void MoveOrder( const Vector &vecDest, CAI_BaseNPC **Allies, int numAllies ); void OnMoveOrder(); void CommanderUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +#ifdef MAPBASE + bool ShouldAllowSquadToggleUse( CBasePlayer *pPlayer ); +#endif bool ShouldSpeakRadio( CBaseEntity *pListener ); void OnMoveToCommandGoalFailed(); void AddToPlayerSquad(); @@ -195,6 +212,10 @@ public: void AddInsignia(); void RemoveInsignia(); bool SpeakCommandResponse( AIConcept_t concept, const char *modifiers = NULL ); + +#ifdef MAPBASE + virtual void SetPlayerAvoidState( void ); +#endif //--------------------------------- // Scanner interaction @@ -235,11 +256,17 @@ public: void InputStartPatrolling( inputdata_t &inputdata ); void InputStopPatrolling( inputdata_t &inputdata ); void InputSetCommandable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetUnCommandable( inputdata_t &inputdata ); +#endif void InputSetMedicOn( inputdata_t &inputdata ); void InputSetMedicOff( inputdata_t &inputdata ); void InputSetAmmoResupplierOn( inputdata_t &inputdata ); void InputSetAmmoResupplierOff( inputdata_t &inputdata ); void InputSpeakIdleResponse( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetPoliceGoal( inputdata_t &inputdata ); +#endif //--------------------------------- // Sounds & speech @@ -303,6 +330,10 @@ private: bool m_bWasInPlayerSquad; float m_flTimeLastCloseToPlayer; string_t m_iszDenyCommandConcept; +#ifdef MAPBASE + bool m_bTossesMedkits; + bool m_bAlternateAiming; +#endif CSimpleSimTimer m_AutoSummonTimer; Vector m_vAutoSummonAnchor; @@ -326,9 +357,23 @@ private: COutputEvent m_OnStationOrder; COutputEvent m_OnPlayerUse; COutputEvent m_OnNavFailBlocked; +#ifdef MAPBASE + COutputEvent m_OnHealedNPC; + COutputEvent m_OnHealedPlayer; + COutputEHANDLE m_OnThrowMedkit; + COutputEvent m_OnGiveAmmo; +#endif //----------------------------------------------------- CAI_FuncTankBehavior m_FuncTankBehavior; +#ifdef MAPBASE + CAI_RappelBehavior m_RappelBehavior; + CAI_PolicingBehavior m_PolicingBehavior; + + // Rappel + virtual bool IsWaitingToRappel( void ) { return m_RappelBehavior.IsWaitingToRappel(); } + void BeginRappel() { m_RappelBehavior.BeginRappel(); } +#endif CHandle m_hSavedFollowGoalEnt; diff --git a/mp/src/game/server/hl2/npc_combine.cpp b/mp/src/game/server/hl2/npc_combine.cpp index 09cae88a..7599a6ac 100644 --- a/mp/src/game/server/hl2/npc_combine.cpp +++ b/mp/src/game/server/hl2/npc_combine.cpp @@ -29,23 +29,40 @@ #include "weapon_physcannon.h" #include "SoundEmitterSystem/isoundemittersystembase.h" #include "npc_headcrab.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#include "globalstate.h" +#include "sceneentity.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared when someone answers. YUCK old global from grunt code +#ifdef MAPBASE +ConVar npc_combine_idle_walk_easy( "npc_combine_idle_walk_easy", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use ACT_WALK_EASY as a walking animation when idle." ); +ConVar npc_combine_unarmed_anims( "npc_combine_unarmed_anims", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use unarmed idle/walk animations when they have no weapon." ); +ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only", "1", FCVAR_NONE, "Mapbase: Elites are normally only allowed to fire their alt-fire attack at the player and the player's allies; This allows elites to alt-fire at other enemies too." ); + +ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." ); +#endif + #define COMBINE_SKIN_DEFAULT 0 #define COMBINE_SKIN_SHOTGUNNER 1 +#ifndef MAPBASE #define COMBINE_GRENADE_THROW_SPEED 650 #define COMBINE_GRENADE_TIMER 3.5 #define COMBINE_GRENADE_FLUSH_TIME 3.0 // Don't try to flush an enemy who has been out of sight for longer than this. #define COMBINE_GRENADE_FLUSH_DIST 256.0 // Don't try to flush an enemy who has moved farther than this distance from the last place I saw him. +#endif #define COMBINE_LIMP_HEALTH 20 +#ifndef MAPBASE #define COMBINE_MIN_GRENADE_CLEAR_DIST 250 +#endif #define COMBINE_EYE_STANDING_POSITION Vector( 0, 0, 66 ) #define COMBINE_GUN_STANDING_POSITION Vector( 0, 0, 57 ) @@ -60,7 +77,11 @@ int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared wh //----------------------------------------------------------------------------- // This is the index to the name of the shotgun's classname in the string pool // so that we can get away with an integer compare rather than a string compare. +#ifdef MAPBASE +#define s_iszShotgunClassname gm_isz_class_Shotgun +#else string_t s_iszShotgunClassname; +#endif //----------------------------------------------------------------------------- // Interactions @@ -73,13 +94,17 @@ int g_interactionCombineBash = 0; // melee bash attack #define COMBINE_AE_RELOAD ( 2 ) #define COMBINE_AE_KICK ( 3 ) #define COMBINE_AE_AIM ( 4 ) +#ifndef MAPBASE #define COMBINE_AE_GREN_TOSS ( 7 ) +#endif #define COMBINE_AE_GREN_LAUNCH ( 8 ) #define COMBINE_AE_GREN_DROP ( 9 ) #define COMBINE_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad. +#ifndef MAPBASE int COMBINE_AE_BEGIN_ALTFIRE; int COMBINE_AE_ALTFIRE; +#endif //========================================================= // Combine activities @@ -91,12 +116,20 @@ int COMBINE_AE_ALTFIRE; //Activity ACT_COMBINE_WALKING_AR2; //Activity ACT_COMBINE_STANDING_SHOTGUN; //Activity ACT_COMBINE_CROUCHING_SHOTGUN; +#ifndef SHARED_COMBINE_ACTIVITIES Activity ACT_COMBINE_THROW_GRENADE; +#endif Activity ACT_COMBINE_LAUNCH_GRENADE; Activity ACT_COMBINE_BUGBAIT; +#ifndef SHARED_COMBINE_ACTIVITIES Activity ACT_COMBINE_AR2_ALTFIRE; +#endif Activity ACT_WALK_EASY; Activity ACT_WALK_MARCH; +#ifdef MAPBASE +Activity ACT_IDLE_UNARMED; +Activity ACT_WALK_UNARMED; +#endif // ----------------------------------------------- // > Squad slots @@ -114,6 +147,9 @@ enum TacticalVariant_T TACTICAL_VARIANT_DEFAULT = 0, TACTICAL_VARIANT_PRESSURE_ENEMY, // Always try to close in on the player. TACTICAL_VARIANT_PRESSURE_ENEMY_UNTIL_CLOSE, // Act like VARIANT_PRESSURE_ENEMY, but go to VARIANT_DEFAULT once within 30 feet +#ifdef MAPBASE + TACTICAL_VARIANT_GRENADE_HAPPY, // Throw grenades as if you're fighting a turret +#endif }; enum PathfindingVariant_T @@ -135,20 +171,33 @@ BEGIN_DATADESC( CNPC_Combine ) DEFINE_FIELD( m_nKickDamage, FIELD_INTEGER ), DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), +#ifndef MAPBASE DEFINE_FIELD( m_hForcedGrenadeTarget, FIELD_EHANDLE ), +#endif DEFINE_FIELD( m_bShouldPatrol, FIELD_BOOLEAN ), DEFINE_FIELD( m_bFirstEncounter, FIELD_BOOLEAN ), DEFINE_FIELD( m_flNextPainSoundTime, FIELD_TIME ), DEFINE_FIELD( m_flNextAlertSoundTime, FIELD_TIME ), +#ifndef MAPBASE DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ), +#endif DEFINE_FIELD( m_flNextLostSoundTime, FIELD_TIME ), DEFINE_FIELD( m_flAlertPatrolTime, FIELD_TIME ), +#ifndef MAPBASE DEFINE_FIELD( m_flNextAltFireTime, FIELD_TIME ), +#endif DEFINE_FIELD( m_nShots, FIELD_INTEGER ), DEFINE_FIELD( m_flShotDelay, FIELD_FLOAT ), DEFINE_FIELD( m_flStopMoveShootTime, FIELD_TIME ), +#ifndef MAPBASE // See ai_grenade.h DEFINE_KEYFIELD( m_iNumGrenades, FIELD_INTEGER, "NumGrenades" ), +#else +DEFINE_INPUT( m_bUnderthrow, FIELD_BOOLEAN, "UnderthrowGrenades" ), +DEFINE_INPUT( m_bAlternateCapable, FIELD_BOOLEAN, "SetAlternateCapable" ), +#endif +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM DEFINE_EMBEDDED( m_Sentences ), +#endif // m_AssaultBehavior (auto saved by AI) // m_StandoffBehavior (auto saved by AI) @@ -167,11 +216,25 @@ DEFINE_INPUTFUNC( FIELD_STRING, "Assault", InputAssault ), DEFINE_INPUTFUNC( FIELD_VOID, "HitByBugbait", InputHitByBugbait ), +#ifndef MAPBASE DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), +#else +DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetElite", InputSetElite ), + +DEFINE_INPUTFUNC( FIELD_VOID, "DropGrenade", InputDropGrenade ), + +DEFINE_INPUTFUNC( FIELD_INTEGER, "SetTacticalVariant", InputSetTacticalVariant ), + +DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ), + +DEFINE_AIGRENADE_DATADESC() +#endif DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), DEFINE_FIELD( m_fIsElite, FIELD_BOOLEAN ), +#ifndef MAPBASE DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ), +#endif DEFINE_KEYFIELD( m_iTacticalVariant, FIELD_INTEGER, "tacticalvariant" ), DEFINE_KEYFIELD( m_iPathfindingVariant, FIELD_INTEGER, "pathfindingvariant" ), @@ -196,7 +259,9 @@ bool CNPC_Combine::CreateComponents() if ( !BaseClass::CreateComponents() ) return false; +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM m_Sentences.Init( this, "NPC_Combine.SentenceParameters" ); +#endif return true; } @@ -249,6 +314,7 @@ void CNPC_Combine::InputHitByBugbait( inputdata_t &inputdata ) SetCondition( COND_COMBINE_HIT_BY_BUGBAIT ); } +#ifndef MAPBASE //----------------------------------------------------------------------------- // Purpose: Force the combine soldier to throw a grenade at the target // If I'm a combine elite, fire my combine ball at the target instead. @@ -260,7 +326,11 @@ void CNPC_Combine::InputThrowGrenadeAtTarget( inputdata_t &inputdata ) if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) return; +#ifdef MAPBASE + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); +#else CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller ); +#endif if ( !pEntity ) { DevMsg("%s (%s) received ThrowGrenadeAtTarget input, but couldn't find target entity '%s'\n", GetClassname(), GetDebugName(), inputdata.value.String() ); @@ -272,6 +342,66 @@ void CNPC_Combine::InputThrowGrenadeAtTarget( inputdata_t &inputdata ) ClearSchedule( "Told to throw grenade via input" ); } +#endif + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Instant transformation of arsenal from grenades to energy balls, or vice versa +//----------------------------------------------------------------------------- +void CNPC_Combine::InputSetElite( inputdata_t &inputdata ) +{ + m_fIsElite = inputdata.value.Bool(); +} + +//----------------------------------------------------------------------------- +// We were told to drop a grenade +//----------------------------------------------------------------------------- +void CNPC_Combine::InputDropGrenade( inputdata_t &inputdata ) +{ + SetCondition( COND_COMBINE_DROP_GRENADE ); + + ClearSchedule( "Told to drop grenade via input" ); +} + +//----------------------------------------------------------------------------- +// Changes our tactical variant easily +//----------------------------------------------------------------------------- +void CNPC_Combine::InputSetTacticalVariant( inputdata_t &inputdata ) +{ + m_iTacticalVariant = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_Combine::InputSetPoliceGoal( inputdata_t &inputdata ) +{ + if (/*!inputdata.value.String() ||*/ inputdata.value.String()[0] == 0) + { + m_PolicingBehavior.Disable(); + return; + } + + CBaseEntity *pGoal = gEntList.FindEntityByName( NULL, inputdata.value.String() ); + + if ( pGoal == NULL ) + { + DevMsg( "SetPoliceGoal: %s (%s) unable to find ai_goal_police: %s\n", GetClassname(), GetDebugName(), inputdata.value.String() ); + return; + } + + CAI_PoliceGoal *pPoliceGoal = dynamic_cast(pGoal); + + if ( pPoliceGoal == NULL ) + { + DevMsg( "SetPoliceGoal: %s (%s)'s target %s is not an ai_goal_police entity!\n", GetClassname(), GetDebugName(), inputdata.value.String() ); + return; + } + + m_PolicingBehavior.Enable( pPoliceGoal ); +} +#endif //----------------------------------------------------------------------------- // Purpose: @@ -283,7 +413,9 @@ void CNPC_Combine::Precache() PrecacheScriptSound( "NPC_Combine.GrenadeLaunch" ); PrecacheScriptSound( "NPC_Combine.WeaponBash" ); +#ifndef MAPBASE // Now that we use WeaponSound(SPECIAL1), this isn't necessary PrecacheScriptSound( "Weapon_CombineGuard.Special1" ); +#endif BaseClass::Precache(); } @@ -292,7 +424,9 @@ void CNPC_Combine::Precache() //----------------------------------------------------------------------------- void CNPC_Combine::Activate() { +#ifndef MAPBASE s_iszShotgunClassname = FindPooledString( "weapon_shotgun" ); +#endif BaseClass::Activate(); } @@ -350,6 +484,14 @@ void CNPC_Combine::Spawn( void ) m_flNextAltFireTime = gpGlobals->curtime; NPCInit(); + +#ifdef MAPBASE + // This was moved from CalcWeaponProficiency() so soldiers don't change skin unnaturally and uncontrollably + if ( GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_Shotgun) && m_nSkin != COMBINE_SKIN_SHOTGUNNER ) + { + m_nSkin = COMBINE_SKIN_SHOTGUNNER; + } +#endif } //----------------------------------------------------------------------------- @@ -364,6 +506,9 @@ bool CNPC_Combine::CreateBehaviors() AddBehavior( &m_StandoffBehavior ); AddBehavior( &m_FollowBehavior ); AddBehavior( &m_FuncTankBehavior ); +#ifdef MAPBASE + AddBehavior( &m_PolicingBehavior ); +#endif return BaseClass::CreateBehaviors(); } @@ -372,6 +517,7 @@ bool CNPC_Combine::CreateBehaviors() //----------------------------------------------------------------------------- void CNPC_Combine::PostNPCInit() { +#ifndef MAPBASE if( IsElite() ) { // Give a warning if a Combine Soldier is equipped with anything other than @@ -381,6 +527,7 @@ void CNPC_Combine::PostNPCInit() DevWarning("**Combine Elite Soldier MUST be equipped with AR2\n"); } } +#endif BaseClass::PostNPCInit(); } @@ -428,7 +575,9 @@ void CNPC_Combine::PrescheduleThink() BaseClass::PrescheduleThink(); // Speak any queued sentences +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM m_Sentences.UpdateSentenceQueue(); +#endif if ( IsOnFire() ) { @@ -467,7 +616,7 @@ void CNPC_Combine::PrescheduleThink() } } - +#ifndef MAPBASE //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_Combine::DelayAltFireAttack( float flDelay ) @@ -494,7 +643,11 @@ void CNPC_Combine::DelaySquadAltFireAttack( float flDelay ) { CNPC_Combine *pCombine = dynamic_cast(pSquadmate); +#ifdef MAPBASE + if( pCombine && pCombine->IsAltFireCapable() ) +#else if( pCombine && pCombine->IsElite() ) +#endif { pCombine->DelayAltFireAttack( flDelay ); } @@ -502,6 +655,7 @@ void CNPC_Combine::DelaySquadAltFireAttack( float flDelay ) pSquadmate = m_pSquad->GetNextMember( &iter ); } } +#endif //----------------------------------------------------------------------------- // Purpose: degrees to turn in 0.1 seconds @@ -586,6 +740,24 @@ Class_T CNPC_Combine::Classify ( void ) return CLASS_COMBINE; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Function for gauging whether we're capable of alt-firing. +//----------------------------------------------------------------------------- +bool CNPC_Combine::IsAltFireCapable( void ) +{ + return IsElite() || m_bAlternateCapable; +} + +//----------------------------------------------------------------------------- +// Purpose: Function for gauging whether we're capable of throwing grenades. +//----------------------------------------------------------------------------- +bool CNPC_Combine::IsGrenadeCapable( void ) +{ + return !IsElite() || m_bAlternateCapable; +} +#endif + //----------------------------------------------------------------------------- // Continuous movement tasks @@ -719,7 +891,6 @@ void CNPC_Combine::RunTaskChaseEnemyContinuously( const Task_t *pTask ) m_vSavePosition = pEnemy->WorldSpaceCenter(); } - //========================================================= // start task //========================================================= @@ -813,7 +984,11 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) { m_flLastAttackTime = gpGlobals->curtime; +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_ANNOUNCE, SENTENCE_PRIORITY_HIGH ); +#else m_Sentences.Speak( "COMBINE_ANNOUNCE", SENTENCE_PRIORITY_HIGH ); +#endif // Wait two seconds SetWait( 2.0 ); @@ -837,7 +1012,11 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) } else { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_THROWGRENADE, SENTENCE_PRIORITY_MEDIUM ); +#else m_Sentences.Speak( "COMBINE_THROW_GRENADE", SENTENCE_PRIORITY_MEDIUM ); +#endif SetActivity(ACT_IDLE); // Wait two seconds @@ -857,6 +1036,9 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) break; case TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS: +#ifdef MAPBASE + StartTask_GetPathToForced(pTask); +#else { if ( !m_hForcedGrenadeTarget ) { @@ -895,6 +1077,7 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) m_vInterruptSavePosition = posLos; } } +#endif break; case TASK_COMBINE_IGNORE_ATTACKS: @@ -912,6 +1095,9 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) case TASK_COMBINE_DEFER_SQUAD_GRENADES: { +#ifdef MAPBASE + StartTask_DeferSquad(pTask); +#else if ( m_pSquad ) { // iterate my squad and stop everyone from throwing grenades for a little while. @@ -920,18 +1106,23 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) CAI_BaseNPC *pSquadmate = m_pSquad ? m_pSquad->GetFirstMember( &iter ) : NULL; while ( pSquadmate ) { +#ifdef MAPBASE + pSquadmate->DelayGrenadeCheck(5); +#else CNPC_Combine *pCombine = dynamic_cast(pSquadmate); if( pCombine ) { pCombine->m_flNextGrenadeCheck = gpGlobals->curtime + 5; } +#endif pSquadmate = m_pSquad->GetNextMember( &iter ); } } TaskComplete(); +#endif break; } @@ -973,7 +1164,11 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) m_pSquad->SquadRemember(bits_MEMORY_PLAYER_HURT); } +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_PLAYERHIT, SENTENCE_PRIORITY_INVALID ); +#else m_Sentences.Speak( "COMBINE_PLAYERHIT", SENTENCE_PRIORITY_INVALID ); +#endif JustMadeSound( SENTENCE_PRIORITY_HIGH ); } if ( pEntity->MyNPCPointer() ) @@ -992,6 +1187,15 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) break; case TASK_RANGE_ATTACK1: { +#ifdef MAPBASE + // The game can crash if a soldier's weapon is removed while they're shooting + if (!GetActiveWeapon()) + { + TaskFail( "No weapon" ); + break; + } +#endif + m_nShots = GetActiveWeapon()->GetRandomBurst(); m_flShotDelay = GetActiveWeapon()->GetFireRate(); @@ -1079,6 +1283,19 @@ void CNPC_Combine::RunTask( const Task_t *pTask ) } break; +#ifdef MAPBASE + case TASK_COMBINE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET: + RunTask_FaceAltFireTarget(pTask); + break; + + case TASK_COMBINE_FACE_TOSS_DIR: + RunTask_FaceTossDir(pTask); + break; + + case TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS: + RunTask_GetPathToForced(pTask); + break; +#else case TASK_COMBINE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET: GetMotor()->SetIdealYawToTargetAndUpdate( m_vecAltFireTarget, AI_KEEP_YAW_SPEED ); @@ -1124,6 +1341,7 @@ void CNPC_Combine::RunTask( const Task_t *pTask ) } } break; +#endif case TASK_RANGE_ATTACK1: { @@ -1239,7 +1457,11 @@ void CNPC_Combine::Event_Killed( const CTakeDamageInfo &info ) } // In the Citadel we need to dissolve this +#ifdef MAPBASE + if ( PlayerHasMegaPhysCannon() && GlobalEntity_GetCounter("super_phys_gun") != 1 ) +#else if ( PlayerHasMegaPhysCannon() ) +#endif { CBaseCombatWeapon *pWeapon = static_cast(pItem); @@ -1288,9 +1510,57 @@ void CNPC_Combine::BuildScheduleTestBits( void ) { SetCustomInterruptCondition( COND_COMBINE_ON_FIRE ); } + +#ifdef MAPBASE + if (npc_combine_new_cover_behavior.GetBool()) + { + if ( IsCurSchedule( SCHED_COMBINE_COMBAT_FAIL ) ) + { + SetCustomInterruptCondition( COND_NEW_ENEMY ); + SetCustomInterruptCondition( COND_LIGHT_DAMAGE ); + SetCustomInterruptCondition( COND_HEAVY_DAMAGE ); + } + else if ( IsCurSchedule( SCHED_COMBINE_MOVE_TO_MELEE ) ) + { + SetCustomInterruptCondition( COND_HEAR_DANGER ); + SetCustomInterruptCondition( COND_HEAR_MOVE_AWAY ); + } + } +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : eNewActivity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pRequired ) +{ + // We have differing low animations and ACT_CROUCHIDLE is not friendly to weapon translation. + // ACT_CROUCHIDLE is pretty much deprecated at this point anyway. + if (eNewActivity == ACT_CROUCHIDLE) + eNewActivity = ACT_RANGE_AIM_LOW; + + return BaseClass::Weapon_TranslateActivity(eNewActivity, pRequired); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CNPC_Combine::NPC_BackupActivity( Activity eNewActivity ) +{ + // Otherwise we move around, T-posing. + if (eNewActivity == ACT_WALK) + return ACT_WALK_UNARMED; + else if (eNewActivity == ACT_RUN) + return ACT_RUN_RIFLE; + + return BaseClass::NPC_BackupActivity( eNewActivity ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Translate base class activities into combot activites //----------------------------------------------------------------------------- @@ -1302,6 +1572,7 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) if (eNewActivity == ACT_RANGE_ATTACK2) { +#ifndef MAPBASE // grunt is going to a secondary long range attack. This may be a thrown // grenade or fired grenade, we must determine which and pick proper sequence if (Weapon_OwnsThisType( "weapon_grenadelauncher" ) ) @@ -1309,8 +1580,19 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) return ( Activity )ACT_COMBINE_LAUNCH_GRENADE; } else +#else + if (m_bUnderthrow) { + return ACT_SPECIAL_ATTACK1; + } + else +#endif + { +#ifdef SHARED_COMBINE_ACTIVITIES + return ACT_COMBINE_THROW_GRENADE; +#else return ( Activity )ACT_COMBINE_THROW_GRENADE; +#endif } } else if (eNewActivity == ACT_IDLE) @@ -1338,6 +1620,19 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) break; } } +#ifdef MAPBASE + else if (!GetActiveWeapon() && npc_combine_unarmed_anims.GetBool() && HaveSequenceForActivity(ACT_IDLE_UNARMED)) + { + if (eNewActivity == ACT_IDLE || eNewActivity == ACT_IDLE_ANGRY) + eNewActivity = ACT_IDLE_UNARMED; + else if (eNewActivity == ACT_WALK) + eNewActivity = ACT_WALK_UNARMED; + } + else if (eNewActivity == ACT_WALK && m_NPCState == NPC_STATE_IDLE && npc_combine_idle_walk_easy.GetBool() && HaveSequenceForActivity(ACT_WALK_EASY)) + { + eNewActivity = ACT_WALK_EASY; + } +#endif return BaseClass::NPC_TranslateActivity( eNewActivity ); } @@ -1399,13 +1694,20 @@ void CNPC_Combine::AnnounceAssault(void) // Make sure player can see me if ( FVisible( pBCC ) ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_ASSAULT ); +#else m_Sentences.Speak( "COMBINE_ASSAULT" ); +#endif } } void CNPC_Combine::AnnounceEnemyType( CBaseEntity *pEnemy ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_ENEMY, SENTENCE_PRIORITY_HIGH ); +#else const char *pSentenceName = "COMBINE_MONST"; switch ( pEnemy->Classify() ) { @@ -1439,6 +1741,7 @@ void CNPC_Combine::AnnounceEnemyType( CBaseEntity *pEnemy ) } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); +#endif } void CNPC_Combine::AnnounceEnemyKill( CBaseEntity *pEnemy ) @@ -1446,6 +1749,11 @@ void CNPC_Combine::AnnounceEnemyKill( CBaseEntity *pEnemy ) if (!pEnemy ) return; +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendEnemyCriteria(set, pEnemy); + SpeakIfAllowed( TLK_CMB_KILLENEMY, set, SENTENCE_PRIORITY_HIGH ); +#else const char *pSentenceName = "COMBINE_KILL_MONST"; switch ( pEnemy->Classify() ) { @@ -1475,6 +1783,7 @@ void CNPC_Combine::AnnounceEnemyKill( CBaseEntity *pEnemy ) } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); +#endif } //----------------------------------------------------------------------------- @@ -1705,6 +2014,9 @@ int CNPC_Combine::SelectSchedule( void ) { Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); +#ifdef MAPBASE + // I switched this to IsAltFireCapable() before, but m_bAlternateCapable makes it necessary to use IsElite() again. +#endif if ( IsElite() ) { if ( FVisible( m_hForcedGrenadeTarget ) ) @@ -1733,6 +2045,12 @@ int CNPC_Combine::SelectSchedule( void ) } } +#ifdef MAPBASE + // Drop a grenade? + if ( HasCondition( COND_COMBINE_DROP_GRENADE ) ) + return SCHED_COMBINE_DROP_GRENADE; +#endif + if ( m_NPCState != NPC_STATE_SCRIPT) { // If we're hit by bugbait, thrash around @@ -1776,7 +2094,11 @@ int CNPC_Combine::SelectSchedule( void ) { // I hear something dangerous, probably need to take cover. // dangerous sound nearby!, call it out +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM const char *pSentenceName = "COMBINE_DANGER"; +#else + bool bGrenade = false; +#endif CBaseEntity *pSoundOwner = pSound->m_hOwner; if ( pSoundOwner ) @@ -1787,12 +2109,20 @@ int CNPC_Combine::SelectSchedule( void ) if ( IRelationType( pGrenade->GetThrower() ) != D_LI ) { // special case call out for enemy grenades +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM pSentenceName = "COMBINE_GREN"; +#else + bGrenade = true; +#endif } } } +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_DANGER, UTIL_VarArgs( "grenade:%d", bGrenade ) ); +#else m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); +#endif // If the sound is approaching danger, I have no enemy, and I don't see it, turn to face. if( !GetEnemy() && pSound->IsSoundType(SOUND_CONTEXT_DANGER_APPROACH) && pSound->m_hOwner && !FInViewCone(pSound->GetSoundReactOrigin()) ) @@ -1881,7 +2211,12 @@ int CNPC_Combine::SelectFailSchedule( int failedSchedule, int failedTask, AI_Tas { if( failedSchedule == SCHED_COMBINE_TAKE_COVER1 ) { +#ifdef MAPBASE + if( IsInSquad() && IsStrategySlotRangeOccupied(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2) && HasCondition(COND_SEE_ENEMY) + && ( !npc_combine_new_cover_behavior.GetBool() || (taskFailCode == FAIL_NO_COVER) ) ) +#else if( IsInSquad() && IsStrategySlotRangeOccupied(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2) && HasCondition(COND_SEE_ENEMY) ) +#endif { // This eases the effects of an unfortunate bug that usually plagues shotgunners. Since their rate of fire is low, // they spend relatively long periods of time without an attack squad slot. If you corner a shotgunner, usually @@ -1912,9 +2247,11 @@ bool CNPC_Combine::ShouldChargePlayer() int CNPC_Combine::SelectScheduleAttack() { +#ifndef MAPBASE // Moved to SelectSchedule() // Drop a grenade? if ( HasCondition( COND_COMBINE_DROP_GRENADE ) ) return SCHED_COMBINE_DROP_GRENADE; +#endif // Kick attack? if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) @@ -1924,7 +2261,11 @@ int CNPC_Combine::SelectScheduleAttack() // If I'm fighting a combine turret (it's been hacked to attack me), I can't really // hurt it with bullets, so become grenade happy. +#ifdef MAPBASE + if ( GetEnemy() && ( (IsUsingTacticalVariant(TACTICAL_VARIANT_GRENADE_HAPPY)) || GetEnemy()->ClassMatches(gm_isz_class_FloorTurret) ) ) +#else if ( GetEnemy() && GetEnemy()->Classify() == CLASS_COMBINE && FClassnameIs(GetEnemy(), "npc_turret_floor") ) +#endif { // Don't do this until I've been fighting the turret for a few seconds float flTimeAtFirstHand = GetEnemies()->TimeAtFirstHand(GetEnemy()); @@ -1940,7 +2281,12 @@ int CNPC_Combine::SelectScheduleAttack() // If we're not in the viewcone of the turret, run up and hit it. Do this a bit later to // give other squadmembers a chance to throw a grenade before I run in. +#ifdef MAPBASE + // Don't do turret charging of we're just grenade happy. + if ( !IsUsingTacticalVariant(TACTICAL_VARIANT_GRENADE_HAPPY) && !GetEnemy()->MyNPCPointer()->FInViewCone( this ) && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) ) +#else if ( !GetEnemy()->MyNPCPointer()->FInViewCone( this ) && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) ) +#endif return SCHED_COMBINE_CHARGE_TURRET; } @@ -2058,7 +2404,11 @@ int CNPC_Combine::TranslateSchedule( int scheduleType ) HasCondition(COND_CAN_RANGE_ATTACK2) && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_THROWGRENADE ); +#else m_Sentences.Speak( "COMBINE_THROW_GRENADE" ); +#endif return SCHED_COMBINE_TOSS_GRENADE_COVER1; } else @@ -2097,6 +2447,13 @@ int CNPC_Combine::TranslateSchedule( int scheduleType ) return TranslateSchedule( SCHED_RANGE_ATTACK1 ); } +#ifdef MAPBASE + if ( npc_combine_new_cover_behavior.GetBool() && HasCondition( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) ) + { + return TranslateSchedule( SCHED_RANGE_ATTACK2 ); + } +#endif + // Run somewhere randomly return TranslateSchedule( SCHED_FAIL ); break; @@ -2237,6 +2594,13 @@ int CNPC_Combine::TranslateSchedule( int scheduleType ) Stand(); } +#ifdef MAPBASE + // SCHED_COMBINE_WAIT_IN_COVER uses INCOVER, but only gets out of it when the soldier moves. + // That seems to mess up shooting, so this Forget() attempts to fix that. + // I don't know if there's a better workaround. + Forget( bits_MEMORY_INCOVER ); +#endif + return SCHED_COMBINE_RANGE_ATTACK1; } case SCHED_RANGE_ATTACK2: @@ -2320,12 +2684,24 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) { if ( pEvent->event == COMBINE_AE_BEGIN_ALTFIRE ) { +#ifdef MAPBASE + if (GetActiveWeapon()) + GetActiveWeapon()->WeaponSound(SPECIAL1); +#else EmitSound( "Weapon_CombineGuard.Special1" ); +#endif +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_THROWGRENADE, "altfire:1", SENTENCE_PRIORITY_MEDIUM ); +#endif handledEvent = true; } else if ( pEvent->event == COMBINE_AE_ALTFIRE ) { - if( IsElite() ) +#ifdef MAPBASE + if ( IsAltFireCapable() ) +#else + if ( IsElite() ) +#endif { animevent_t fakeEvent; @@ -2343,7 +2719,15 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) // that makes sure the elite has grenades in order to fire a combine ball, we // preserve the legacy behavior while making it possible for a designer to prevent // elites from shooting combine balls by setting grenades to '0' in hammer. (sjb) EP2_OUTLAND_10 +#ifdef MAPBASE + // + // Here's a tip: In Mapbase, "OnThrowGrenade" is fired during alt-fire as well, fired by the weapon so it could pass its alt-fire projectile. + // So if you want elites to decrement on each grenade again, you could fire "!self > AddGrenades -1" every time an elite fires OnThrowGrenade. + // + // AddGrenades(-1); +#else // m_iNumGrenades--; +#endif } handledEvent = true; @@ -2367,8 +2751,12 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) // We never actually run out of ammo, just need to refill the clip if (GetActiveWeapon()) { +#ifdef MAPBASE + GetActiveWeapon()->Reload_NPC(); +#else GetActiveWeapon()->WeaponSound( RELOAD_NPC ); GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); +#endif GetActiveWeapon()->m_iClip2 = GetActiveWeapon()->GetMaxClip2(); } ClearCondition(COND_LOW_PRIMARY_AMMO); @@ -2395,13 +2783,24 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) GetVectors( &forward, NULL, &up ); vecThrow = forward * 750 + up * 175; +#ifdef MAPBASE + CBaseEntity *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vecThrow, vecSpin, this, COMBINE_GRENADE_TIMER, true ); + m_OnThrowGrenade.Set(pGrenade, pGrenade, this); +#else Fraggrenade_Create( vecStart, vec3_angle, vecThrow, vecSpin, this, COMBINE_GRENADE_TIMER, true ); +#endif } else { // Use the Velocity that AI gave us. +#ifdef MAPBASE + CBaseEntity *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vecSpin, this, COMBINE_GRENADE_TIMER, true ); + m_OnThrowGrenade.Set(pGrenade, pGrenade, this); + AddGrenades(-1, pGrenade); +#else Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vecSpin, this, COMBINE_GRENADE_TIMER, true ); m_iNumGrenades--; +#endif } // wait six seconds before even looking again to see if a grenade can be thrown. @@ -2429,10 +2828,34 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) case COMBINE_AE_GREN_DROP: { Vector vecStart; +#ifdef MAPBASE + QAngle angStart; + m_vecTossVelocity.x = 15; + m_vecTossVelocity.y = 0; + m_vecTossVelocity.z = 0; + + GetAttachment( "lefthand", vecStart, angStart ); + + CBaseEntity *pGrenade = NULL; + if (m_NPCState == NPC_STATE_SCRIPT) + { + // While scripting, have the grenade face upwards like it was originally and also don't decrement grenade count. + pGrenade = Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vec3_origin, this, COMBINE_GRENADE_TIMER, true ); + } + else + { + pGrenade = Fraggrenade_Create( vecStart, angStart, m_vecTossVelocity, vec3_origin, this, COMBINE_GRENADE_TIMER, true ); + AddGrenades(-1); + } + + // Well, technically we're not throwing, but...still. + m_OnThrowGrenade.Set(pGrenade, pGrenade, this); +#else GetAttachment( "lefthand", vecStart ); Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vec3_origin, this, COMBINE_GRENADE_TIMER, true ); m_iNumGrenades--; +#endif } handledEvent = true; break; @@ -2463,13 +2886,21 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) } } +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_KICK ); +#else m_Sentences.Speak( "COMBINE_KICK" ); +#endif handledEvent = true; break; } case COMBINE_AE_CAUGHT_ENEMY: +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_ENEMY ); //SpeakIfAllowed( "TLK_CMB_ALERT" ); +#else m_Sentences.Speak( "COMBINE_ALERT" ); +#endif handledEvent = true; break; @@ -2556,16 +2987,78 @@ void CNPC_Combine::SpeakSentence( int sentenceType ) // If I'm moving more than 20ft, I need to talk about it if ( GetNavigator()->GetPath()->GetPathLength() > 20 * 12.0f ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_FLANK ); +#else m_Sentences.Speak( "COMBINE_FLANK" ); +#endif } break; } } +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM +//========================================================= +bool CNPC_Combine::SpeakIfAllowed( const char *concept, const char *modifiers, SentencePriority_t sentencepriority, SentenceCriteria_t sentencecriteria ) +{ + AI_CriteriaSet set; + if (modifiers) + { + GetExpresser()->MergeModifiers(set, modifiers); + } + return SpeakIfAllowed( concept, set, sentencepriority, sentencecriteria ); +} + +//========================================================= +//========================================================= +bool CNPC_Combine::SpeakIfAllowed( const char *concept, AI_CriteriaSet& modifiers, SentencePriority_t sentencepriority, SentenceCriteria_t sentencecriteria ) +{ + if ( sentencepriority != SENTENCE_PRIORITY_INVALID && !FOkToMakeSound( sentencepriority ) ) + return false; + + if ( !GetExpresser()->CanSpeakConcept( concept ) ) + return false; + + // Don't interrupt scripted VCD dialogue + if ( IsRunningScriptedSceneWithSpeechAndNotPaused( this, true ) ) + return false; + + if ( Speak( concept, modifiers ) ) + { + JustMadeSound( sentencepriority, 2.0f /*GetTimeSpeechComplete()*/ ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_Combine::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + + set.AppendCriteria( "numgrenades", UTIL_VarArgs("%d", m_iNumGrenades) ); + + if (IsElite()) + { + set.AppendCriteria( "elite", "1" ); + } + else + { + set.AppendCriteria( "elite", "0" ); + } +} +#endif + //========================================================= // PainSound //========================================================= +#ifdef MAPBASE +void CNPC_Combine::PainSound ( const CTakeDamageInfo &info ) +#else void CNPC_Combine::PainSound ( void ) +#endif { // NOTE: The response system deals with this at the moment if ( GetFlags() & FL_DISSOLVING ) @@ -2573,6 +3066,11 @@ void CNPC_Combine::PainSound ( void ) if ( gpGlobals->curtime > m_flNextPainSoundTime ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendDamageCriteria(set, info); + SpeakIfAllowed( TLK_CMB_PAIN, set, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#else const char *pSentenceName = "COMBINE_PAIN"; float healthRatio = (float)GetHealth() / (float)GetMaxHealth(); if ( !HasMemory(bits_MEMORY_PAIN_LIGHT_SOUND) && healthRatio > 0.9 ) @@ -2587,6 +3085,7 @@ void CNPC_Combine::PainSound ( void ) } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif m_flNextPainSoundTime = gpGlobals->curtime + 1; } } @@ -2602,6 +3101,12 @@ void CNPC_Combine::LostEnemySound( void) if ( gpGlobals->curtime <= m_flNextLostSoundTime ) return; +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + if (SpeakIfAllowed( TLK_CMB_LOSTENEMY, UTIL_VarArgs("lastseenenemy:%d", GetEnemyLastTimeSeen()) )) + { + m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); + } +#else const char *pSentence; if (!(CBaseEntity*)GetEnemy() || gpGlobals->curtime - GetEnemyLastTimeSeen() > 10) { @@ -2616,6 +3121,7 @@ void CNPC_Combine::LostEnemySound( void) { m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); } +#endif } //----------------------------------------------------------------------------- @@ -2626,7 +3132,11 @@ void CNPC_Combine::LostEnemySound( void) //----------------------------------------------------------------------------- void CNPC_Combine::FoundEnemySound( void) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_REFINDENEMY, SENTENCE_PRIORITY_HIGH ); +#else m_Sentences.Speak( "COMBINE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH ); +#endif } //----------------------------------------------------------------------------- @@ -2641,7 +3151,11 @@ void CNPC_Combine::AlertSound( void) { if ( gpGlobals->curtime > m_flNextAlertSoundTime ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_GOALERT, SENTENCE_PRIORITY_HIGH ); +#else m_Sentences.Speak( "COMBINE_GO_ALERT", SENTENCE_PRIORITY_HIGH ); +#endif m_flNextAlertSoundTime = gpGlobals->curtime + 10.0f; } } @@ -2651,16 +3165,22 @@ void CNPC_Combine::AlertSound( void) //========================================================= void CNPC_Combine::NotifyDeadFriend ( CBaseEntity* pFriend ) { +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM if ( GetSquad()->NumMembers() < 2 ) { m_Sentences.Speak( "COMBINE_LAST_OF_SQUAD", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_NORMAL ); JustMadeSound(); return; } +#endif // relaxed visibility test so that guys say this more often //if( FInViewCone( pFriend ) && FVisible( pFriend ) ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_MANDOWN ); +#else m_Sentences.Speak( "COMBINE_MAN_DOWN" ); +#endif } BaseClass::NotifyDeadFriend(pFriend); } @@ -2668,13 +3188,23 @@ void CNPC_Combine::NotifyDeadFriend ( CBaseEntity* pFriend ) //========================================================= // DeathSound //========================================================= +#ifdef MAPBASE +void CNPC_Combine::DeathSound ( const CTakeDamageInfo &info ) +#else void CNPC_Combine::DeathSound ( void ) +#endif { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendDamageCriteria(set, info); + SpeakIfAllowed(TLK_CMB_DIE, set, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS); +#else // NOTE: The response system deals with this at the moment if ( GetFlags() & FL_DISSOLVING ) return; m_Sentences.Speak( "COMBINE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif } //========================================================= @@ -2686,6 +3216,11 @@ void CNPC_Combine::IdleSound( void ) { if (!g_fCombineQuestion) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + int iRandom = random->RandomInt(0, 2); + SpeakIfAllowed( TLK_CMB_QUESTION, UTIL_VarArgs("combinequestion:%d", iRandom) ); + g_fCombineQuestion = iRandom + 1; +#else // ask question or make statement switch (random->RandomInt(0,2)) { @@ -2707,9 +3242,14 @@ void CNPC_Combine::IdleSound( void ) m_Sentences.Speak( "COMBINE_IDLE" ); break; } +#endif } else { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_CMB_ANSWER, UTIL_VarArgs("combinequestion:%d", g_fCombineQuestion) ); + g_fCombineQuestion = 0; +#else switch (g_fCombineQuestion) { case 1: // check in @@ -2725,6 +3265,7 @@ void CNPC_Combine::IdleSound( void ) } break; } +#endif } } } @@ -2744,6 +3285,7 @@ int CNPC_Combine::RangeAttack2Conditions( float flDot, float flDist ) return COND_NONE; } +#ifndef MAPBASE //----------------------------------------------------------------------------- // Purpose: Return true if the combine has grenades, hasn't checked lately, and // can throw a grenade at the target point. @@ -2858,12 +3400,17 @@ bool CNPC_Combine::CheckCanThrowGrenade( const Vector &vecTarget ) return false; } } +#endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CNPC_Combine::CanAltFireEnemy( bool bUseFreeKnowledge ) { - if (!IsElite() ) +#ifdef MAPBASE + if ( !IsAltFireCapable() ) +#else + if ( !IsElite() ) +#endif return false; if (IsCrouching()) @@ -2884,7 +3431,14 @@ bool CNPC_Combine::CanAltFireEnemy( bool bUseFreeKnowledge ) CBaseEntity *pEnemy = GetEnemy(); +#ifdef MAPBASE + // "Our weapons alone cannot take down the antlion guard!" + // "Wait, you're an elite, don't you have, like, disintegration balls or somethi--" + // "SHUT UP!" + if ( !npc_combine_altfire_not_allies_only.GetBool() && !pEnemy->IsPlayer() && (!pEnemy->IsNPC() || !pEnemy->MyNPCPointer()->IsPlayerAlly()) ) +#else if( !pEnemy->IsPlayer() && (!pEnemy->IsNPC() || !pEnemy->MyNPCPointer()->IsPlayerAlly()) ) +#endif return false; Vector vecTarget; @@ -2913,14 +3467,23 @@ bool CNPC_Combine::CanAltFireEnemy( bool bUseFreeKnowledge ) } // Trace a hull about the size of the combine ball. +#ifdef MAPBASE + UTIL_TraceHull( vShootPosition, vecTarget, mins, maxs, MASK_COMBINE_BALL_LOS, this, COLLISION_GROUP_NONE, &tr ); +#else UTIL_TraceHull( vShootPosition, vecTarget, mins, maxs, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); +#endif float flLength = (vShootPosition - vecTarget).Length(); flLength *= tr.fraction; //If the ball can travel at least 65% of the distance to the player then let the NPC shoot it. +#ifdef MAPBASE + // (unless it hit the world) + if( tr.fraction >= 0.65 && (!tr.m_pEnt || !tr.m_pEnt->IsWorld()) && flLength > 128.0f ) +#else if( tr.fraction >= 0.65 && flLength > 128.0f ) +#endif { // Target is valid m_vecAltFireTarget = vecTarget; @@ -2938,7 +3501,11 @@ bool CNPC_Combine::CanAltFireEnemy( bool bUseFreeKnowledge ) //----------------------------------------------------------------------------- bool CNPC_Combine::CanGrenadeEnemy( bool bUseFreeKnowledge ) { - if( IsElite() ) +#ifdef MAPBASE + if ( !IsGrenadeCapable() ) +#else + if ( IsElite() ) +#endif return false; CBaseEntity *pEnemy = GetEnemy(); @@ -3029,6 +3596,7 @@ Vector CNPC_Combine::EyePosition( void ) */ } +#ifndef MAPBASE //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- Vector CNPC_Combine::GetAltFireTarget() @@ -3037,6 +3605,7 @@ Vector CNPC_Combine::GetAltFireTarget() return m_vecAltFireTarget; } +#endif //----------------------------------------------------------------------------- // Purpose: @@ -3070,6 +3639,37 @@ Vector CNPC_Combine::GetCrouchEyeOffset( void ) return COMBINE_EYE_CROUCHING_POSITION; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Combine::IsCrouchedActivity( Activity activity ) +{ + if (BaseClass::IsCrouchedActivity( activity )) + return true; + + Activity realActivity = TranslateActivity(activity); + + // Soldiers need to consider these crouched activities, but not all NPCs should. + switch ( realActivity ) + { + case ACT_RANGE_AIM_LOW: + case ACT_RANGE_AIM_AR2_LOW: + case ACT_RANGE_AIM_SMG1_LOW: + case ACT_RANGE_AIM_PISTOL_LOW: + case ACT_RANGE_ATTACK1_LOW: + case ACT_RANGE_ATTACK_AR2_LOW: + case ACT_RANGE_ATTACK_SMG1_LOW: + case ACT_RANGE_ATTACK_SHOTGUN_LOW: + case ACT_RANGE_ATTACK_PISTOL_LOW: + case ACT_RANGE_ATTACK2_LOW: + return true; + } + + return false; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_Combine::SetActivity( Activity NewActivity ) @@ -3138,7 +3738,11 @@ void CNPC_Combine::OnEndMoveAndShoot() //----------------------------------------------------------------------------- WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) { +#ifdef MAPBASE + if( pWeapon->ClassMatches( gm_isz_class_AR2 ) ) +#else if( FClassnameIs( pWeapon, "weapon_ar2" ) ) +#endif { if( hl2_episodic.GetBool() ) { @@ -3149,16 +3753,26 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea return WEAPON_PROFICIENCY_GOOD; } } +#ifdef MAPBASE + else if( pWeapon->ClassMatches( gm_isz_class_Shotgun ) ) +#else else if( FClassnameIs( pWeapon, "weapon_shotgun" ) ) +#endif { +#ifndef MAPBASE // Moved so soldiers don't change skin unnaturally and uncontrollably if( m_nSkin != COMBINE_SKIN_SHOTGUNNER ) { m_nSkin = COMBINE_SKIN_SHOTGUNNER; } +#endif return WEAPON_PROFICIENCY_PERFECT; } +#ifdef MAPBASE + else if( pWeapon->ClassMatches( gm_isz_class_SMG1 ) ) +#else else if( FClassnameIs( pWeapon, "weapon_smg1" ) ) +#endif { return WEAPON_PROFICIENCY_GOOD; } @@ -3300,12 +3914,20 @@ DECLARE_TASK( TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS ) DECLARE_TASK( TASK_COMBINE_SET_STANDING ) //Activities +#ifndef SHARED_COMBINE_ACTIVITIES DECLARE_ACTIVITY( ACT_COMBINE_THROW_GRENADE ) +#endif DECLARE_ACTIVITY( ACT_COMBINE_LAUNCH_GRENADE ) DECLARE_ACTIVITY( ACT_COMBINE_BUGBAIT ) +#ifndef SHARED_COMBINE_ACTIVITIES DECLARE_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ) +#endif DECLARE_ACTIVITY( ACT_WALK_EASY ) DECLARE_ACTIVITY( ACT_WALK_MARCH ) +#ifdef MAPBASE +DECLARE_ACTIVITY( ACT_IDLE_UNARMED ) +DECLARE_ACTIVITY( ACT_WALK_UNARMED ) +#endif DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE ) DECLARE_ANIMEVENT( COMBINE_AE_ALTFIRE ) diff --git a/mp/src/game/server/hl2/npc_combine.h b/mp/src/game/server/hl2/npc_combine.h index ab166723c..ec541cca 100644 --- a/mp/src/game/server/hl2/npc_combine.h +++ b/mp/src/game/server/hl2/npc_combine.h @@ -21,6 +21,15 @@ #include "ai_behavior_actbusy.h" #include "ai_sentence.h" #include "ai_baseactor.h" +#ifdef MAPBASE +#include "mapbase/ai_grenade.h" +#include "ai_behavior_police.h" +#endif +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE +#include "mapbase/expandedrs_combine.h" +//#define CAI_Sentence CAI_SentenceTalker +#define COMBINE_SOLDIER_USES_RESPONSE_SYSTEM 1 +#endif // Used when only what combine to react to what the spotlight sees #define SF_COMBINE_NO_LOOK (1 << 16) @@ -30,11 +39,19 @@ //========================================================= // >> CNPC_Combine //========================================================= +#ifdef MAPBASE +class CNPC_Combine : public CAI_GrenadeUser +{ + DECLARE_DATADESC(); + DEFINE_CUSTOM_AI; + DECLARE_CLASS( CNPC_Combine, CAI_GrenadeUser ); +#else class CNPC_Combine : public CAI_BaseActor { DECLARE_DATADESC(); DEFINE_CUSTOM_AI; DECLARE_CLASS( CNPC_Combine, CAI_BaseActor ); +#endif public: CNPC_Combine(); @@ -42,8 +59,10 @@ public: // Create components virtual bool CreateComponents(); +#ifndef MAPBASE bool CanThrowGrenade( const Vector &vecTarget ); bool CheckCanThrowGrenade( const Vector &vecTarget ); +#endif virtual bool CanGrenadeEnemy( bool bUseFreeKnowledge = true ); virtual bool CanAltFireEnemy( bool bUseFreeKnowledge ); int GetGrenadeConditions( float flDot, float flDist ); @@ -56,6 +75,10 @@ public: virtual Vector GetCrouchEyeOffset( void ); +#ifdef MAPBASE + virtual bool IsCrouchedActivity( Activity activity ); +#endif + void Event_Killed( const CTakeDamageInfo &info ); @@ -69,7 +92,17 @@ public: void InputStopPatrolling( inputdata_t &inputdata ); void InputAssault( inputdata_t &inputdata ); void InputHitByBugbait( inputdata_t &inputdata ); +#ifndef MAPBASE void InputThrowGrenadeAtTarget( inputdata_t &inputdata ); +#else + void InputSetElite( inputdata_t &inputdata ); + + void InputDropGrenade( inputdata_t &inputdata ); + + void InputSetTacticalVariant( inputdata_t &inputdata ); + + void InputSetPoliceGoal( inputdata_t &inputdata ); +#endif bool UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer = NULL ); @@ -79,8 +112,16 @@ public: Class_T Classify( void ); bool IsElite() { return m_fIsElite; } +#ifdef MAPBASE + bool IsAltFireCapable(); + bool IsGrenadeCapable(); + const char* GetGrenadeAttachment() { return "lefthand"; } +#else +#endif +#ifndef MAPBASE void DelayAltFireAttack( float flDelay ); void DelaySquadAltFireAttack( float flDelay ); +#endif float MaxYawSpeed( void ); bool ShouldMoveAndShoot(); bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval );; @@ -90,7 +131,9 @@ public: Vector EyeOffset( Activity nActivity ); Vector EyePosition( void ); Vector BodyTarget( const Vector &posSrc, bool bNoisy = true ); +#ifndef MAPBASE Vector GetAltFireTarget(); +#endif void StartTask( const Task_t *pTask ); void RunTask( const Task_t *pTask ); @@ -98,6 +141,10 @@ public: void GatherConditions(); virtual void PrescheduleThink(); +#ifdef MAPBASE + Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); + Activity NPC_BackupActivity( Activity eNewActivity ); +#endif Activity NPC_TranslateActivity( Activity eNewActivity ); void BuildScheduleTestBits( void ); virtual int SelectSchedule( void ); @@ -125,8 +172,13 @@ public: // ------------- // Sounds // ------------- +#ifdef MAPBASE + void DeathSound( const CTakeDamageInfo &info ); + void PainSound( const CTakeDamageInfo &info ); +#else void DeathSound( void ); void PainSound( void ); +#endif void IdleSound( void ); void AlertSound( void ); void LostEnemySound( void ); @@ -143,6 +195,15 @@ public: // Speaking void SpeakSentence( int sentType ); +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + bool SpeakIfAllowed( const char *concept, SentencePriority_t sentencepriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t sentencecriteria = SENTENCE_CRITERIA_IN_SQUAD ) + { + return SpeakIfAllowed( concept, NULL, sentencepriority, sentencecriteria ); + } + bool SpeakIfAllowed( const char *concept, const char *modifiers, SentencePriority_t sentencepriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t sentencecriteria = SENTENCE_CRITERIA_IN_SQUAD ); + bool SpeakIfAllowed( const char *concept, AI_CriteriaSet& modifiers, SentencePriority_t sentencepriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t sentencecriteria = SENTENCE_CRITERIA_IN_SQUAD ); + void ModifyOrAppendCriteria( AI_CriteriaSet& set ); +#endif virtual int TranslateSchedule( int scheduleType ); void OnStartSchedule( int scheduleType ); @@ -151,7 +212,9 @@ public: protected: void SetKickDamage( int nDamage ) { m_nKickDamage = nDamage; } +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM CAI_Sentence< CNPC_Combine > *GetSentences() { return &m_Sentences; } +#endif private: //========================================================= @@ -257,37 +320,56 @@ private: private: int m_nKickDamage; +#ifndef MAPBASE Vector m_vecTossVelocity; EHANDLE m_hForcedGrenadeTarget; +#else + // Underthrow grenade at target + bool m_bUnderthrow; + bool m_bAlternateCapable; +#endif bool m_bShouldPatrol; bool m_bFirstEncounter;// only put on the handsign show in the squad's first encounter. // Time Variables float m_flNextPainSoundTime; float m_flNextAlertSoundTime; +#ifndef MAPBASE float m_flNextGrenadeCheck; +#endif float m_flNextLostSoundTime; float m_flAlertPatrolTime; // When to stop doing alert patrol +#ifndef MAPBASE float m_flNextAltFireTime; // Elites only. Next time to begin considering alt-fire attack. +#endif int m_nShots; float m_flShotDelay; float m_flStopMoveShootTime; +#ifndef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM CAI_Sentence< CNPC_Combine > m_Sentences; +#endif +#ifndef MAPBASE int m_iNumGrenades; +#endif CAI_AssaultBehavior m_AssaultBehavior; CCombineStandoffBehavior m_StandoffBehavior; CAI_FollowBehavior m_FollowBehavior; CAI_FuncTankBehavior m_FuncTankBehavior; CAI_RappelBehavior m_RappelBehavior; CAI_ActBusyBehavior m_ActBusyBehavior; +#ifdef MAPBASE + CAI_PolicingBehavior m_PolicingBehavior; +#endif public: int m_iLastAnimEventHandled; bool m_fIsElite; +#ifndef MAPBASE Vector m_vecAltFireTarget; +#endif int m_iTacticalVariant; int m_iPathfindingVariant; diff --git a/mp/src/game/server/hl2/npc_combinecamera.cpp b/mp/src/game/server/hl2/npc_combinecamera.cpp index 26d1f07e..13bbba1b 100644 --- a/mp/src/game/server/hl2/npc_combinecamera.cpp +++ b/mp/src/game/server/hl2/npc_combinecamera.cpp @@ -547,7 +547,11 @@ bool CNPC_CombineCamera::FVisible(CBaseEntity *pEntity, int traceMask, CBaseEnti // If we hit something that's okay to hit anyway, still fire if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() ) { +#ifdef MAPBASE + if (IRelationType(pHitEntity) <= D_FR) +#else if (IRelationType(pHitEntity) == D_HT) +#endif return true; } @@ -623,6 +627,14 @@ void CNPC_CombineCamera::ActiveThink() if ( !pTarget ) { // Nobody suspicious. Go back to being idle. +#ifdef MAPBASE + if (m_hEnemyTarget) + { + m_OnLostEnemy.FireOutput( m_hEnemyTarget, this ); + if (m_hEnemyTarget->IsPlayer()) + m_OnLostPlayer.FireOutput( m_hEnemyTarget, this ); + } +#endif m_hEnemyTarget = NULL; EmitSound("NPC_CombineCamera.BecomeIdle"); SetAngry(false); diff --git a/mp/src/game/server/hl2/npc_combinedropship.cpp b/mp/src/game/server/hl2/npc_combinedropship.cpp index ffe1befd..fa40fd76 100644 --- a/mp/src/game/server/hl2/npc_combinedropship.cpp +++ b/mp/src/game/server/hl2/npc_combinedropship.cpp @@ -175,6 +175,11 @@ public: virtual int OnTakeDamage( const CTakeDamageInfo &info ); virtual void Event_Killed( const CTakeDamageInfo &info ); +#ifdef MAPBASE + // NOTE: This function is shared across containers and dropships; this is the container's version + bool AllowsAnyDamage( const CTakeDamageInfo &info ); +#endif + private: enum { @@ -247,6 +252,11 @@ public: void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ); int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ); +#ifdef MAPBASE + // NOTE: This function is shared across containers and dropships; this is the dropship's version + bool AllowsAnyDamage() { return m_bAllowAnyDamage; } +#endif + // Input handlers. void InputLandLeave( inputdata_t &inputdata ); void InputLandTake( inputdata_t &inputdata ); @@ -254,6 +264,9 @@ public: void InputDropMines( inputdata_t &inputdata ); void InputDropStrider( inputdata_t &inputdata ); void InputDropAPC( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDropCargo( inputdata_t &inputdata ); +#endif void InputPickup( inputdata_t &inputdata ); void InputSetGunRange( inputdata_t &inputdata ); @@ -317,6 +330,9 @@ private: EHANDLE m_hPickupTarget; int m_iContainerMoveType; bool m_bWaitForDropoffInput; +#ifdef MAPBASE + bool m_bDontEmitDanger; +#endif DECLARE_DATADESC(); DEFINE_CUSTOM_AI; @@ -357,6 +373,10 @@ private: COutputFloat m_OnContainerShotDownBeforeDropoff; COutputEvent m_OnContainerShotDownAfterDropoff; +#ifdef MAPBASE + COutputEHANDLE m_OnSpawnNPC; +#endif + protected: // Because the combine dropship is a leaf class, we can use // static variables to store this information, and save some memory. @@ -614,6 +634,23 @@ void CCombineDropshipContainer::Event_Killed( const CTakeDamageInfo &info ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCombineDropshipContainer::AllowsAnyDamage( const CTakeDamageInfo &info ) +{ + if (GetOwnerEntity()) + { + CNPC_CombineDropship *pDropship = assert_cast(GetOwnerEntity()); + return pDropship->AllowsAnyDamage() && pDropship->PassesDamageFilter(info); + } + + return false; +} +#endif + + //----------------------------------------------------------------------------- // Damage effects //----------------------------------------------------------------------------- @@ -623,7 +660,11 @@ int CCombineDropshipContainer::OnTakeDamage( const CTakeDamageInfo &info ) return 0; // Airboat guns + explosive damage is all that can hurt it +#ifdef MAPBASE + if (( info.GetDamageType() & (DMG_BLAST | DMG_AIRBOAT) ) == 0 && !AllowsAnyDamage(info) ) +#else if (( info.GetDamageType() & (DMG_BLAST | DMG_AIRBOAT) ) == 0 ) +#endif return 0; CTakeDamageInfo dmgInfo = info; @@ -764,6 +805,9 @@ BEGIN_DATADESC( CNPC_CombineDropship ) DEFINE_FIELD( m_hPickupTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_iContainerMoveType, FIELD_INTEGER ), DEFINE_FIELD( m_bWaitForDropoffInput, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bDontEmitDanger, FIELD_BOOLEAN, "DontEmitDanger" ), +#endif DEFINE_FIELD( m_hLandTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_bHasDroppedOff, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_bInvulnerable, FIELD_BOOLEAN, "Invulnerable" ), @@ -810,6 +854,9 @@ BEGIN_DATADESC( CNPC_CombineDropship ) DEFINE_INPUTFUNC( FIELD_INTEGER, "DropMines", InputDropMines ), DEFINE_INPUTFUNC( FIELD_VOID, "DropStrider", InputDropStrider ), DEFINE_INPUTFUNC( FIELD_VOID, "DropAPC", InputDropAPC ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "DropCargo", InputDropCargo ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "Pickup", InputPickup ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetGunRange", InputSetGunRange ), DEFINE_INPUTFUNC( FIELD_STRING, "NPCFinishDustoff", InputNPCFinishDustoff ), @@ -823,6 +870,10 @@ BEGIN_DATADESC( CNPC_CombineDropship ) DEFINE_OUTPUT( m_OnContainerShotDownBeforeDropoff, "OnCrateShotDownBeforeDropoff" ), DEFINE_OUTPUT( m_OnContainerShotDownAfterDropoff, "OnCrateShotDownAfterDropoff" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnSpawnNPC, "OnSpawnNPC" ), +#endif + END_DATADESC() @@ -896,6 +947,11 @@ void CNPC_CombineDropship::Spawn( void ) m_iMachineGunBaseAttachment = m_hContainer->LookupAttachment( "gun_base" ); // NOTE: gun_ref must have the same position as gun_base, but rotates with the gun m_iMachineGunRefAttachment = m_hContainer->LookupAttachment( "gun_ref" ); + +#ifdef MAPBASE + m_poseWeapon_Pitch = m_hContainer->LookupPoseParameter("weapon_pitch"); //added these two lines + m_poseWeapon_Yaw = m_hContainer->LookupPoseParameter("weapon_yaw"); +#endif } break; @@ -907,6 +963,9 @@ void CNPC_CombineDropship::Spawn( void ) m_hContainer->SetOwnerEntity(this); m_hContainer->Spawn(); m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector( 0, 0 , 100 ) ); +#ifdef MAPBASE + m_OnSpawnNPC.Set( m_hContainer, m_hContainer, this ); +#endif break; case CRATE_APC: @@ -1399,7 +1458,11 @@ int CNPC_CombineDropship::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) // code above to see how to do it. if ( m_hContainer && !m_bInvulnerable ) { +#ifdef MAPBASE + if ( (inputInfo.GetDamageType() & DMG_AIRBOAT) || (m_iCrateType == CRATE_SOLDIER) || m_bAllowAnyDamage ) +#else if ( (inputInfo.GetDamageType() & DMG_AIRBOAT) || (m_iCrateType == CRATE_SOLDIER) ) +#endif { m_hContainer->TakeDamage( inputInfo ); } @@ -1754,12 +1817,60 @@ void CNPC_CombineDropship::InputDropAPC( inputdata_t &inputdata ) UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX ); +#ifdef MAPBASE + m_OnFinishedDropoff.FireOutput( m_hContainer, this ); + m_hContainer = NULL; +#else m_hContainer = NULL; m_OnFinishedDropoff.FireOutput( this, this ); +#endif SetLandingState( LANDING_NO ); m_hLandTarget = NULL; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_CombineDropship::InputDropCargo( inputdata_t &inputdata ) +{ + if ( !m_hContainer ) + { + Warning("npc_combinedropship %s was told to drop cargo, but isn't carrying any!\n", STRING(GetEntityName()) ); + return; + } + + m_hContainer->SetParent(NULL, 0); +// m_hContainer->SetOwnerEntity(NULL); + + Vector vecAbsVelocity = GetAbsVelocity(); + if ( vecAbsVelocity.z > 0 ) + { + vecAbsVelocity.z = 0.0f; + } + if ( m_hContainer->GetHealth() > 0 ) + { + vecAbsVelocity = vec3_origin; + } + + m_hContainer->SetAbsVelocity( vecAbsVelocity ); + m_hContainer->SetMoveType( (MoveType_t)m_iContainerMoveType ); + + // If the container has a physics object, remove it's shadow + IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject(); + if ( pPhysicsObject ) + { + pPhysicsObject->RemoveShadowController(); + } + + UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX ); + + m_OnFinishedDropoff.FireOutput( m_hContainer, this ); + m_hContainer = NULL; + SetLandingState( LANDING_NO ); + m_hLandTarget = NULL; +} +#endif + //----------------------------------------------------------------------------- // Drop the soldier container @@ -1796,13 +1907,19 @@ void CNPC_CombineDropship::DropSoldierContainer( ) UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX ); +#ifndef MAPBASE m_hContainer = NULL; +#endif SetLandingState( LANDING_NO ); m_hLandTarget = NULL; if ( m_bHasDroppedOff ) { +#ifdef MAPBASE + m_OnContainerShotDownAfterDropoff.FireOutput( m_hContainer, this ); +#else m_OnContainerShotDownAfterDropoff.FireOutput( this, this ); +#endif } else { @@ -1812,8 +1929,16 @@ void CNPC_CombineDropship::DropSoldierContainer( ) Msg("Dropship died, troops not unloaded: %d\n", iTroopsNotUnloaded ); } +#ifdef MAPBASE + m_OnContainerShotDownBeforeDropoff.Set( iTroopsNotUnloaded, m_hContainer, this ); +#else m_OnContainerShotDownBeforeDropoff.Set( iTroopsNotUnloaded, this, this ); +#endif } + +#ifdef MAPBASE + m_hContainer = NULL; +#endif } @@ -2082,10 +2207,17 @@ void CNPC_CombineDropship::PrescheduleThink( void ) // place danger sounds 1 foot above ground to get troops to scatter if they are below dropship Vector vecBottom = GetAbsOrigin(); vecBottom.z += WorldAlignMins().z; +#ifdef MAPBASE + if (!m_bDontEmitDanger) + { +#endif Vector vecSpot = vecBottom + Vector(0, 0, -1) * (flAltitude - 12 ); CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this, 0 ); CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, 400, 0.1, this, 1 ); // NDebugOverlay::Cross3D( vecSpot, -Vector(4,4,4), Vector(4,4,4), 255, 0, 255, false, 10.0f ); +#ifdef MAPBASE + } +#endif // now check to see if player is below us, if so, cause heat damage to them (i.e. get them to move) trace_t tr; @@ -2257,7 +2389,11 @@ void CNPC_CombineDropship::PrescheduleThink( void ) SetLandingState( LANDING_NO ); m_hLandTarget = NULL; m_bHasDroppedOff = true; +#ifdef MAPBASE + m_OnFinishedDropoff.FireOutput( m_hContainer, this ); +#else m_OnFinishedDropoff.FireOutput( this, this ); +#endif } if ( m_hContainer ) @@ -2317,7 +2453,11 @@ void CNPC_CombineDropship::PrescheduleThink( void ) m_hContainer->SetMoveType( MOVETYPE_PUSH ); m_hContainer->SetGroundEntity( NULL ); +#ifdef MAPBASE + m_OnFinishedPickup.FireOutput( m_hContainer, this ); +#else m_OnFinishedPickup.FireOutput( this, this ); +#endif SetLandingState( LANDING_NO ); } } @@ -2395,6 +2535,9 @@ void CNPC_CombineDropship::SpawnTroop( void ) QAngle vecDeployEndAngles; m_hContainer->GetAttachment( m_iAttachmentTroopDeploy, vecDeployEndPoint, vecDeployEndAngles ); vecDeployEndPoint = GetDropoffFinishPosition( vecDeployEndPoint, NULL, vecNPCMins, vecNPCMaxs ); +#ifdef MAPBASE + if (!m_bDontEmitDanger) +#endif CSoundEnt::InsertSound( SOUND_DANGER, vecDeployEndPoint, 120.0f, 2.0f, this ); // Make sure there are no NPCs on the spot @@ -2470,6 +2613,10 @@ void CNPC_CombineDropship::SpawnTroop( void ) pSequence->AcceptInput( "BeginSequence", this, this, emptyVariant, 0 ); m_hLastTroopToLeave = pNPC; + +#ifdef MAPBASE + m_OnSpawnNPC.Set( pNPC, pNPC, this ); +#endif } //----------------------------------------------------------------------------- @@ -2633,7 +2780,12 @@ float CNPC_CombineDropship::GetAltitude( void ) //----------------------------------------------------------------------------- void CNPC_CombineDropship::DropMine( void ) { +#ifdef MAPBASE + CBaseEntity *pMine = NPC_Rollermine_DropFromPoint( GetAbsOrigin(), this, STRING( m_sRollermineTemplateData ) ); + m_OnSpawnNPC.Set( pMine, pMine, this ); +#else NPC_Rollermine_DropFromPoint( GetAbsOrigin(), this, STRING(m_sRollermineTemplateData) ); +#endif } //------------------------------------------------------------------------------ diff --git a/mp/src/game/server/hl2/npc_combinegunship.cpp b/mp/src/game/server/hl2/npc_combinegunship.cpp index 25831053..dfe0aa20 100644 --- a/mp/src/game/server/hl2/npc_combinegunship.cpp +++ b/mp/src/game/server/hl2/npc_combinegunship.cpp @@ -51,6 +51,10 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +#define BELLYBLAST +#endif + #define GUNSHIP_MSG_BIG_SHOT 1 #define GUNSHIP_MSG_STREAKS 2 @@ -370,6 +374,10 @@ private: int m_iDoSmokePuff; int m_iAmmoType; int m_iBurstSize; + +#ifdef MAPBASE + int m_iHealthIncrements; +#endif bool m_fBlindfire; bool m_fOmniscient; @@ -420,7 +428,11 @@ BEGIN_DATADESC( CNPC_CombineGunship ) DEFINE_FIELD( m_flNextGroundAttack,FIELD_TIME ), DEFINE_FIELD( m_bIsGroundAttacking,FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bCanGroundAttack, FIELD_BOOLEAN, "CanGroundAttack" ), +#else DEFINE_FIELD( m_bCanGroundAttack, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_flGroundAttackTime,FIELD_TIME ), DEFINE_FIELD( m_pRotorWashModel, FIELD_CLASSPTR ), DEFINE_FIELD( m_pSmokeTrail, FIELD_EHANDLE ), @@ -437,6 +449,9 @@ BEGIN_DATADESC( CNPC_CombineGunship ) DEFINE_FIELD( m_iDoSmokePuff, FIELD_INTEGER ), DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), DEFINE_FIELD( m_iBurstSize, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iHealthIncrements, FIELD_INTEGER, "HealthIncrements" ), +#endif DEFINE_FIELD( m_flBurstDelay, FIELD_FLOAT ), DEFINE_FIELD( m_fBlindfire, FIELD_BOOLEAN ), DEFINE_FIELD( m_fOmniscient, FIELD_BOOLEAN ), @@ -552,6 +567,11 @@ void CNPC_CombineGunship::Spawn( void ) m_iMaxHealth = m_iHealth = 100; +#ifdef MAPBASE + if (m_iHealthIncrements == 0) + m_iHealthIncrements = sk_gunship_health_increments.GetInt(); +#endif + m_flFieldOfView = -0.707; // 270 degrees m_fHelicopterFlags |= BITS_HELICOPTER_GUN_ON; @@ -601,8 +621,10 @@ void CNPC_CombineGunship::Spawn( void ) m_bPreFire = false; m_bInvulnerable = false; +#ifndef MAPBASE // Spawnflag has been replaced with KV // See if we should start being able to attack m_bCanGroundAttack = ( m_spawnflags & SF_GUNSHIP_NO_GROUND_ATTACK ) ? false : true; +#endif m_flEndDestructTime = 0; @@ -2831,7 +2853,13 @@ void CNPC_CombineGunship::MakeTracer( const Vector &vecTracerSrc, const trace_t void CNPC_CombineGunship::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) { // Reflect bullets +#ifdef MAPBASE + // There's a keyvalue that allows any damage, but still reflect if the bullets wouldn't pass our damage filter. + if ( info.GetDamageType() & DMG_BULLET && + (!m_bAllowAnyDamage || !PassesDamageFilter(info)) ) +#else if ( info.GetDamageType() & DMG_BULLET ) +#endif { if ( random->RandomInt( 0, 2 ) == 0 ) { @@ -2912,7 +2940,11 @@ void CNPC_CombineGunship::FireDamageOutputsUpto( int iDamageNumber ) int CNPC_CombineGunship::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) { // Allow npc_kill to kill me +#ifdef MAPBASE + if ( inputInfo.GetDamageType() != DMG_GENERIC && !m_bAllowAnyDamage ) +#else if ( inputInfo.GetDamageType() != DMG_GENERIC ) +#endif { // Ignore mundane bullet damage. if ( ( inputInfo.GetDamageType() & DMG_BLAST ) == false ) @@ -2945,7 +2977,11 @@ int CNPC_CombineGunship::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) { // Take a percentage of our health away // Adjust health for damage +#ifdef MAPBASE + int iHealthIncrements = m_iHealthIncrements; +#else int iHealthIncrements = sk_gunship_health_increments.GetInt(); +#endif if ( g_pGameRules->IsSkillLevel( SKILL_EASY ) ) { iHealthIncrements = ceil( iHealthIncrements * 0.5 ); diff --git a/mp/src/game/server/hl2/npc_combines.cpp b/mp/src/game/server/hl2/npc_combines.cpp index 9addfbeb..be85d895 100644 --- a/mp/src/game/server/hl2/npc_combines.cpp +++ b/mp/src/game/server/hl2/npc_combines.cpp @@ -96,7 +96,12 @@ void CNPC_CombineS::Precache() { const char *pModelName = STRING( GetModelName() ); +#ifdef MAPBASE + // Need to do this for dirt variant + if( !Q_strnicmp( pModelName, "models/combine_super_sold", 25 ) ) +#else if( !Q_stricmp( pModelName, "models/combine_super_soldier.mdl" ) ) +#endif { m_fIsElite = true; } @@ -122,14 +127,21 @@ void CNPC_CombineS::Precache() void CNPC_CombineS::DeathSound( const CTakeDamageInfo &info ) { +#ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendDamageCriteria(set, info); + SpeakIfAllowed( TLK_CMB_DIE, set, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#else // NOTE: The response system deals with this at the moment if ( GetFlags() & FL_DISSOLVING ) return; GetSentences()->Speak( "COMBINE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif } +#ifndef MAPBASE // Moved to CAI_GrenadeUser //----------------------------------------------------------------------------- // Purpose: Soldiers use CAN_RANGE_ATTACK2 to indicate whether they can throw // a grenade. Because they check only every half-second or so, this @@ -151,6 +163,7 @@ void CNPC_CombineS::ClearAttackConditions( ) SetCondition( COND_CAN_RANGE_ATTACK2 ); } } +#endif void CNPC_CombineS::PrescheduleThink( void ) { @@ -307,7 +320,15 @@ void CNPC_CombineS::Event_Killed( const CTakeDamageInfo &info ) if ( HasSpawnFlags( SF_COMBINE_NO_AR2DROP ) == false ) #endif { +#ifdef MAPBASE + CBaseEntity *pItem; + if (GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), "weapon_smg1")) + pItem = DropItem( "item_ammo_smg1_grenade", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + else + pItem = DropItem( "item_ammo_ar2_altfire", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); +#else CBaseEntity *pItem = DropItem( "item_ammo_ar2_altfire", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); +#endif if ( pItem ) { diff --git a/mp/src/game/server/hl2/npc_combines.h b/mp/src/game/server/hl2/npc_combines.h index e9dc75b2..7c7d8fbe 100644 --- a/mp/src/game/server/hl2/npc_combines.h +++ b/mp/src/game/server/hl2/npc_combines.h @@ -35,7 +35,9 @@ public: void Event_Killed( const CTakeDamageInfo &info ); void OnListened(); +#ifndef MAPBASE // Moved to CAI_GrenadeUser void ClearAttackConditions( void ); +#endif bool m_fIsBlocking; diff --git a/mp/src/game/server/hl2/npc_enemyfinder.cpp b/mp/src/game/server/hl2/npc_enemyfinder.cpp index 02afc08b..f8fc21bd 100644 --- a/mp/src/game/server/hl2/npc_enemyfinder.cpp +++ b/mp/src/game/server/hl2/npc_enemyfinder.cpp @@ -69,6 +69,10 @@ private: bool m_bEnemyStatus; +#ifdef MAPBASE + Class_T m_iClassify = CLASS_NONE; +#endif + COutputEvent m_OnLostEnemies; COutputEvent m_OnAcquireEnemies; @@ -103,6 +107,10 @@ BEGIN_DATADESC( CNPC_EnemyFinder ) DEFINE_FIELD( m_bEnemyStatus, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_INPUT( m_iClassify, FIELD_INTEGER, "SetClassify" ), +#endif + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), @@ -417,7 +425,11 @@ bool CNPC_EnemyFinder::ShouldAlwaysThink() return true; CBasePlayer *pPlayer = AI_GetSinglePlayer(); +#ifdef MAPBASE + if ( pPlayer && IRelationType( pPlayer ) <= D_FR ) +#else if ( pPlayer && IRelationType( pPlayer ) == D_HT ) +#endif { float playerDistSqr = GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ); @@ -454,6 +466,11 @@ void CNPC_EnemyFinder::GatherConditions() //----------------------------------------------------------------------------- Class_T CNPC_EnemyFinder::Classify( void ) { +#ifdef MAPBASE + if (m_iClassify != CLASS_NONE) + return m_iClassify; +#endif + if ( GetSquad() ) { AISquadIter_t iter; diff --git a/mp/src/game/server/hl2/npc_fastzombie.cpp b/mp/src/game/server/hl2/npc_fastzombie.cpp index 0ecd7155..0e15e2a8 100644 --- a/mp/src/game/server/hl2/npc_fastzombie.cpp +++ b/mp/src/game/server/hl2/npc_fastzombie.cpp @@ -61,6 +61,12 @@ enum COND_FASTZOMBIE_CLIMB_TOUCH = LAST_BASE_ZOMBIE_CONDITION, }; +#ifdef MAPBASE +ConVar sk_zombie_fast_health( "sk_zombie_fast_health", "50"); +ConVar sk_zombie_fast_dmg_one_slash( "sk_zombie_fast_dmg_claw","3"); +ConVar sk_zombie_fast_dmg_both_slash( "sk_zombie_fast_dmg_leap","5"); +#endif + envelopePoint_t envFastZombieVolumeJump[] = { { 1.0f, 1.0f, @@ -255,7 +261,9 @@ public: void OnChangeActivity( Activity NewActivity ); void OnStateChange( NPC_STATE OldState, NPC_STATE NewState ); void Event_Killed( const CTakeDamageInfo &info ); +#ifndef MAPBASE bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold ); +#endif virtual Vector GetAutoAimCenter() { return WorldSpaceCenter() - Vector( 0, 0, 12.0f ); } @@ -652,7 +660,9 @@ void CFastZombie::Spawn( void ) m_fJustJumped = false; +#ifndef MAPBASE // Controlled by KV m_fIsTorso = m_fIsHeadless = false; +#endif if( FClassnameIs( this, "npc_fastzombie" ) ) { @@ -670,7 +680,11 @@ void CFastZombie::Spawn( void ) SetBloodColor( BLOOD_COLOR_YELLOW ); #endif // HL2_EPISODIC +#ifdef MAPBASE + m_iHealth = sk_zombie_fast_health.GetInt(); +#else m_iHealth = 50; +#endif m_flFieldOfView = 0.2; CapabilitiesClear(); @@ -1084,7 +1098,11 @@ void CFastZombie::HandleAnimEvent( animevent_t *pEvent ) right = right * -50; QAngle angle( -3, -5, -3 ); +#ifdef MAPBASE + ClawAttack( GetClawAttackRange(), sk_zombie_fast_dmg_one_slash.GetInt(), angle, right, ZOMBIE_BLOOD_RIGHT_HAND ); +#else ClawAttack( GetClawAttackRange(), 3, angle, right, ZOMBIE_BLOOD_RIGHT_HAND ); +#endif return; } @@ -1094,7 +1112,11 @@ void CFastZombie::HandleAnimEvent( animevent_t *pEvent ) AngleVectors( GetLocalAngles(), NULL, &right, NULL ); right = right * 50; QAngle angle( -3, 5, -3 ); +#ifdef MAPBASE + ClawAttack( GetClawAttackRange(), sk_zombie_fast_dmg_one_slash.GetInt(), angle, right, ZOMBIE_BLOOD_LEFT_HAND ); +#else ClawAttack( GetClawAttackRange(), 3, angle, right, ZOMBIE_BLOOD_LEFT_HAND ); +#endif return; } @@ -1480,7 +1502,11 @@ void CFastZombie::LeapAttackTouch( CBaseEntity *pOther ) forward *= 500; QAngle qaPunch( 15, random->RandomInt(-5,5), random->RandomInt(-5,5) ); +#ifdef MAPBASE + ClawAttack( GetClawAttackRange(), sk_zombie_fast_dmg_both_slash.GetInt(), qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS ); +#else ClawAttack( GetClawAttackRange(), 5, qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS ); +#endif SetTouch( NULL ); } @@ -1848,6 +1874,7 @@ void CFastZombie::Event_Killed( const CTakeDamageInfo &info ) BaseClass::Event_Killed( dInfo ); } +#ifndef MAPBASE //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CFastZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold ) @@ -1868,6 +1895,7 @@ bool CFastZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamage return false; } +#endif //============================================================================= #ifdef HL2_EPISODIC diff --git a/mp/src/game/server/hl2/npc_headcrab.cpp b/mp/src/game/server/hl2/npc_headcrab.cpp index 6e7d3e71..22f44d3d 100644 --- a/mp/src/game/server/hl2/npc_headcrab.cpp +++ b/mp/src/game/server/hl2/npc_headcrab.cpp @@ -72,6 +72,10 @@ ConVar g_debug_headcrab( "g_debug_headcrab", "0", FCVAR_CHEAT ); //------------------------------------ #define SF_HEADCRAB_START_HIDDEN (1 << 16) #define SF_HEADCRAB_START_HANGING (1 << 17) +#ifdef MAPBASE +#define SF_HEADCRAB_DONT_DROWN (1 << 18) +#define SF_HEADCRAB_NO_MELEE_INSTAKILL (1 << 19) +#endif //----------------------------------------------------------------------------- @@ -218,6 +222,10 @@ BEGIN_DATADESC( CBaseHeadcrab ) DEFINE_INPUTFUNC( FIELD_VOID, "StartHangingFromCeiling", InputStartHangingFromCeiling ), DEFINE_INPUTFUNC( FIELD_VOID, "DropFromCeiling", InputDropFromCeiling ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnLeap, "OnLeap" ), +#endif + // Function Pointers DEFINE_THINKFUNC( EliminateRollAndPitch ), DEFINE_THINKFUNC( ThrowThink ), @@ -482,6 +490,11 @@ void CBaseHeadcrab::Leap( const Vector &vecVel ) m_bMidJump = true; SetThink( &CBaseHeadcrab::ThrowThink ); SetNextThink( gpGlobals->curtime ); + +#ifdef MAPBASE + // We usually leap at an enemy, so use that as the activator + m_OnLeap.FireOutput(GetEnemy(), this); +#endif } @@ -926,7 +939,11 @@ void CBaseHeadcrab::LeapTouch( CBaseEntity *pOther ) { m_bMidJump = false; +#ifdef MAPBASE + if ( IRelationType( pOther ) <= D_FR ) +#else if ( IRelationType( pOther ) == D_HT ) +#endif { // Don't hit if back on ground if ( !( GetFlags() & FL_ONGROUND ) ) @@ -1032,7 +1049,11 @@ void CBaseHeadcrab::GatherConditions( void ) BaseClass::GatherConditions(); +#ifdef MAPBASE + if (GetWaterLevel() > 1 && m_lifeState == LIFE_ALIVE && !HasSpawnFlags( SF_HEADCRAB_DONT_DROWN )) +#else if( m_lifeState == LIFE_ALIVE && GetWaterLevel() > 1 ) +#endif { // Start Drowning! SetCondition( COND_HEADCRAB_IN_WATER ); @@ -1712,7 +1733,12 @@ int CBaseHeadcrab::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) // // Certain death from melee bludgeon weapons! // +#ifdef MAPBASE + // (unless the mapper said no) + if ( info.GetDamageType() & DMG_CLUB && !HasSpawnFlags( SF_HEADCRAB_NO_MELEE_INSTAKILL ) ) +#else if ( info.GetDamageType() & DMG_CLUB ) +#endif { info.SetDamage( m_iHealth ); } @@ -2230,6 +2256,9 @@ void CBaseHeadcrab::GrabHintNode( CAI_Hint *pHint ) { SetHintNode( pHint ); pHint->Lock( this ); +#ifdef MAPBASE + pHint->NPCStartedUsing( this ); +#endif } } diff --git a/mp/src/game/server/hl2/npc_headcrab.h b/mp/src/game/server/hl2/npc_headcrab.h index daf79b46..8c4103ad 100644 --- a/mp/src/game/server/hl2/npc_headcrab.h +++ b/mp/src/game/server/hl2/npc_headcrab.h @@ -149,6 +149,10 @@ protected: bool m_bHangingFromCeiling; float m_flIlluminatedTime; + +#ifdef MAPBASE + COutputEvent m_OnLeap; +#endif }; diff --git a/mp/src/game/server/hl2/npc_launcher.cpp b/mp/src/game/server/hl2/npc_launcher.cpp index 9e942862..817bab23 100644 --- a/mp/src/game/server/hl2/npc_launcher.cpp +++ b/mp/src/game/server/hl2/npc_launcher.cpp @@ -62,6 +62,9 @@ public: // Outputs // ---------------- COutputEvent m_OnLaunch; // Triggered when missile is launched. +#ifdef MAPBASE + COutputEHANDLE m_OutMissile; // Passes the missile. +#endif // ---------------- // Inputs @@ -126,6 +129,9 @@ BEGIN_DATADESC( CNPC_Launcher ) DEFINE_INPUTFUNC( FIELD_VOID, "ClearEnemyEntity", InputClearEnemy ), DEFINE_OUTPUT( m_OnLaunch, "OnLaunch" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OutMissile, "OutMissile" ), +#endif // Function Pointers DEFINE_THINKFUNC( LauncherThink ), @@ -278,6 +284,13 @@ void CNPC_Launcher::LaunchGrenade( CBaseEntity* pEnemy ) pGrenade->SetDamage(m_flDamage); pGrenade->SetDamageRadius(m_flDamageRadius); pGrenade->Launch(m_flLaunchSpeed,m_sPathCornerName); + +#ifdef MAPBASE + if (GetOwnerEntity()) + pGrenade->SetOwnerEntity(GetOwnerEntity()); + + m_OutMissile.Set(pGrenade, pGrenade, this); +#endif } else { @@ -292,6 +305,13 @@ void CNPC_Launcher::LaunchGrenade( CBaseEntity* pEnemy ) pGrenade->SetDamage(m_flDamage); pGrenade->SetDamageRadius(m_flDamageRadius); pGrenade->Launch(this,pEnemy,vLaunchVelocity,m_flHomingSpeed,GetGravity(),m_nSmokeTrail); + +#ifdef MAPBASE + if (GetOwnerEntity()) + pGrenade->SetOwnerEntity(GetOwnerEntity()); + + m_OutMissile.Set(pGrenade, pGrenade, this); +#endif } CPASAttenuationFilter filter( this, 0.3 ); diff --git a/mp/src/game/server/hl2/npc_manhack.cpp b/mp/src/game/server/hl2/npc_manhack.cpp index feef84f8..27dcde85 100644 --- a/mp/src/game/server/hl2/npc_manhack.cpp +++ b/mp/src/game/server/hl2/npc_manhack.cpp @@ -78,6 +78,10 @@ #define MANHACK_CHARGE_MIN_DIST 200 +#if defined(MAPBASE) && defined(HL2_EPISODIC) +extern ConVar npc_alyx_interact_manhacks; +#endif + ConVar sk_manhack_health( "sk_manhack_health","0"); ConVar sk_manhack_melee_dmg( "sk_manhack_melee_dmg","0"); ConVar sk_manhack_v2( "sk_manhack_v2","1"); @@ -149,7 +153,11 @@ BEGIN_DATADESC( CNPC_Manhack ) DEFINE_FIELD( m_bGib, FIELD_BOOLEAN), DEFINE_FIELD( m_bHeld, FIELD_BOOLEAN), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bHackedByAlyx, FIELD_BOOLEAN, "Hacked" ), +#else DEFINE_FIELD( m_bHackedByAlyx, FIELD_BOOLEAN), +#endif DEFINE_FIELD( m_vecLoiterPosition, FIELD_POSITION_VECTOR), DEFINE_FIELD( m_fTimeNextLoiterPulse, FIELD_TIME), @@ -159,6 +167,10 @@ BEGIN_DATADESC( CNPC_Manhack ) DEFINE_FIELD( m_flBladeSpeed, FIELD_FLOAT), DEFINE_KEYFIELD( m_bIgnoreClipbrushes, FIELD_BOOLEAN, "ignoreclipbrushes" ), DEFINE_FIELD( m_hSmokeTrail, FIELD_EHANDLE), +#ifdef MAPBASE + DEFINE_FIELD( m_hPrevOwner, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_bNoSprites, FIELD_BOOLEAN, "NoSprites" ), +#endif // DEFINE_FIELD( m_pLightGlow, FIELD_CLASSPTR ), // DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ), @@ -187,6 +199,10 @@ BEGIN_DATADESC( CNPC_Manhack ) // Function Pointers DEFINE_INPUTFUNC( FIELD_VOID, "DisableSwarm", InputDisableSwarm ), DEFINE_INPUTFUNC( FIELD_VOID, "Unpack", InputUnpack ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnableSprites", InputEnableSprites ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableSprites", InputDisableSprites ), +#endif DEFINE_ENTITYFUNC( CrashTouch ), @@ -1519,7 +1535,11 @@ void CNPC_Manhack::Slice( CBaseEntity *pHitEntity, float flInterval, trace_t &tr pHitEntity->TakeDamage( info ); // Spawn some extra blood where we hit +#ifdef MAPBASE + if ( pHitEntity->BloodColor() == DONT_BLEED || (IRelationType(pHitEntity) > D_FR && !pHitEntity->PassesDamageFilter(info)) ) +#else if ( pHitEntity->BloodColor() == DONT_BLEED ) +#endif { CEffectData data; Vector velocity = GetCurrentVelocity(); @@ -2453,7 +2473,9 @@ void CNPC_Manhack::Spawn(void) SetCollisionGroup( COLLISION_GROUP_NONE ); m_bHeld = false; +#ifndef MAPBASE m_bHackedByAlyx = false; +#endif StopLoitering(); } @@ -2462,6 +2484,11 @@ void CNPC_Manhack::Spawn(void) //----------------------------------------------------------------------------- void CNPC_Manhack::StartEye( void ) { +#ifdef MAPBASE + if (m_bNoSprites) + return; +#endif + //Create our Eye sprite if ( m_pEyeGlow == NULL ) { @@ -2981,6 +3008,26 @@ void CNPC_Manhack::InputUnpack( inputdata_t &inputdata ) SetCondition( COND_LIGHT_DAMAGE ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Creates the sprite if it has been destroyed +//----------------------------------------------------------------------------- +void CNPC_Manhack::InputEnableSprites( inputdata_t &inputdata ) +{ + m_bNoSprites = false; + StartEye(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroys the sprite +//----------------------------------------------------------------------------- +void CNPC_Manhack::InputDisableSprites( inputdata_t &inputdata ) +{ + KillSprites( 0.0 ); + m_bNoSprites = true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pPhysGunUser - @@ -3007,6 +3054,11 @@ void CNPC_Manhack::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r } else { +#ifdef MAPBASE + // Store the previous owner in case of npc_maker + m_hPrevOwner.Set(GetOwnerEntity()); +#endif + // Suppress collisions between the manhack and the player; we're currently bumping // almost certainly because it's not purely a physics object. SetOwnerEntity( pPhysGunUser ); @@ -3023,7 +3075,13 @@ void CNPC_Manhack::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r void CNPC_Manhack::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason ) { // Stop suppressing collisions between the manhack and the player +#ifndef MAPBASE SetOwnerEntity( NULL ); +#else + SetOwnerEntity( m_hPrevOwner ); + + m_hPrevOwner = NULL; +#endif m_bHeld = false; @@ -3088,6 +3146,20 @@ float CNPC_Manhack::GetMaxEnginePower() return 1.0f; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Option to restore Alyx's interactions with non-rollermines +//----------------------------------------------------------------------------- +bool CNPC_Manhack::CanInteractWith( CAI_BaseNPC *pUser ) +{ +#ifdef HL2_EPISODIC + return npc_alyx_interact_manhacks.GetBool(); +#else + return false; +#endif +} +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -3199,16 +3271,43 @@ void CNPC_Manhack::SetEyeState( int state ) if ( m_pEyeGlow ) { //Toggle our state +#ifdef MAPBASE + // Makes it easier to distinguish between hostile and friendly manhacks. + if( m_bHackedByAlyx ) + { + m_pEyeGlow->SetColor( 0, 0, 255 ); + m_pEyeGlow->SetScale( 0.35f, 0.6f ); + } + else + { + m_pEyeGlow->SetColor( 255, 128, 0 ); + m_pEyeGlow->SetScale( 0.15f, 0.1f ); + } +#else m_pEyeGlow->SetColor( 255, 128, 0 ); m_pEyeGlow->SetScale( 0.15f, 0.1f ); +#endif m_pEyeGlow->SetBrightness( 164, 0.1f ); m_pEyeGlow->m_nRenderFX = kRenderFxStrobeFast; } if ( m_pLightGlow ) { +#ifdef MAPBASE + if( m_bHackedByAlyx ) + { + m_pLightGlow->SetColor( 0, 0, 255 ); + m_pLightGlow->SetScale( 0.35f, 0.6f ); + } + else + { + m_pLightGlow->SetColor( 255, 128, 0 ); + m_pLightGlow->SetScale( 0.15f, 0.1f ); + } +#else m_pLightGlow->SetColor( 255, 128, 0 ); m_pLightGlow->SetScale( 0.15f, 0.1f ); +#endif m_pLightGlow->SetBrightness( 164, 0.1f ); m_pLightGlow->m_nRenderFX = kRenderFxStrobeFast; } diff --git a/mp/src/game/server/hl2/npc_manhack.h b/mp/src/game/server/hl2/npc_manhack.h index 17a3cedb..a080f0eb 100644 --- a/mp/src/game/server/hl2/npc_manhack.h +++ b/mp/src/game/server/hl2/npc_manhack.h @@ -145,6 +145,10 @@ public: void InputDisableSwarm( inputdata_t &inputdata ); void InputUnpack( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputEnableSprites( inputdata_t &inputdata ); + void InputDisableSprites( inputdata_t &inputdata ); +#endif // CDefaultPlayerPickupVPhysics virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); @@ -155,7 +159,11 @@ public: float GetMaxEnginePower(); // INPCInteractive Functions +#ifdef MAPBASE + virtual bool CanInteractWith( CAI_BaseNPC *pUser ); +#else virtual bool CanInteractWith( CAI_BaseNPC *pUser ) { return false; } // Disabled for now (sjb) +#endif virtual bool HasBeenInteractedWith() { return m_bHackedByAlyx; } virtual void NotifyInteraction( CAI_BaseNPC *pUser ) { @@ -163,6 +171,9 @@ public: KillSprites(0.0f); m_bHackedByAlyx = true; StartEye(); +#ifdef MAPBASE + m_OnHacked.FireOutput(pUser, this); +#endif } virtual void InputPowerdown( inputdata_t &inputdata ) @@ -254,6 +265,11 @@ private: CSprite *m_pLightGlow; CHandle m_hSmokeTrail; +#ifdef MAPBASE + EHANDLE m_hPrevOwner; + + bool m_bNoSprites; +#endif int m_iPanel1; int m_iPanel2; diff --git a/mp/src/game/server/hl2/npc_metropolice.cpp b/mp/src/game/server/hl2/npc_metropolice.cpp index 833318d4..174a832d 100644 --- a/mp/src/game/server/hl2/npc_metropolice.cpp +++ b/mp/src/game/server/hl2/npc_metropolice.cpp @@ -19,6 +19,10 @@ #include "iservervehicle.h" #include "items.h" #include "hl2_gamerules.h" +#ifdef MAPBASE +#include "grenade_frag.h" +#include "mapbase/GlobalStrings.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -119,6 +123,10 @@ ConVar metropolice_chase_use_follow( "metropolice_chase_use_follow", "0" ); ConVar metropolice_move_and_melee("metropolice_move_and_melee", "1" ); ConVar metropolice_charge("metropolice_charge", "1" ); +#ifdef MAPBASE +ConVar metropolice_new_component_behavior("metropolice_new_component_behavior", "1"); +#endif + // How many clips of pistol ammo a metropolice carries. #define METROPOLICE_NUM_CLIPS 5 #define METROPOLICE_BURST_RELOAD_COUNT 20 @@ -172,7 +180,9 @@ BEGIN_DATADESC( CNPC_MetroPolice ) DEFINE_KEYFIELD( m_fWeaponDrawn, FIELD_BOOLEAN, "weapondrawn" ), DEFINE_FIELD( m_LastShootSlot, FIELD_INTEGER ), DEFINE_EMBEDDED( m_TimeYieldShootSlot ), +#ifndef METROPOLICE_USES_RESPONSE_SYSTEM DEFINE_EMBEDDED( m_Sentences ), +#endif DEFINE_FIELD( m_bPlayerIsNear, FIELD_BOOLEAN ), DEFINE_FIELD( m_vecBurstTargetPos, FIELD_POSITION_VECTOR ), @@ -225,13 +235,33 @@ BEGIN_DATADESC( CNPC_MetroPolice ) DEFINE_KEYFIELD( m_iManhacks, FIELD_INTEGER, "manhacks" ), DEFINE_INPUTFUNC( FIELD_VOID, "EnableManhackToss", InputEnableManhackToss ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "DisableManhackToss", InputDisableManhackToss ), + DEFINE_INPUTFUNC( FIELD_VOID, "DeployManhack", InputDeployManhack ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddManhacks", InputAddManhacks ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetManhacks", InputSetManhacks ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ), DEFINE_INPUTFUNC( FIELD_VOID, "ActivateBaton", InputActivateBaton ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "AdministerJustice", InputAdministerJustice ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddWarnings", InputAddWarnings ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetWarnings", InputSetWarnings ), +#endif DEFINE_USEFUNC( PrecriminalUse ), DEFINE_OUTPUT( m_OnStunnedPlayer, "OnStunnedPlayer" ), DEFINE_OUTPUT( m_OnCupCopped, "OnCupCopped" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnHitByPhysicsObject, "OnHitByPhysicsObject" ), + DEFINE_OUTPUT( m_OutManhack, "OutManhack" ), +#endif + +#ifdef MAPBASE + DEFINE_AIGRENADE_DATADESC() + DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ), +#endif END_DATADESC() @@ -333,7 +363,11 @@ public: if ( pBCC && pVictimBCC ) { // Can only damage other NPCs that we hate +#ifdef MAPBASE + if ( m_bDamageAnyNPC || pBCC->IRelationType( pEntity ) <= D_FR || pEntity->IsPlayer() ) +#else if ( m_bDamageAnyNPC || pBCC->IRelationType( pEntity ) == D_HT || pEntity->IsPlayer() ) +#endif { if ( info.GetDamage() ) { @@ -434,9 +468,20 @@ void CNPC_MetroPolice::NotifyDeadFriend( CBaseEntity* pFriend ) { BaseClass::NotifyDeadFriend(pFriend); +#ifdef MAPBASE + // m_hManhack is set to NULL after it's finished deploying, which means this has no chance of playing unless the manhack + // was still being deployed. This is thought to be an unintended oversight. + // Mapbase stores the metrocop thrower as the manhack's owner entity, so this code is now able to check that instead. + if ( pFriend->GetOwnerEntity() == this ) +#else if ( pFriend == m_hManhack ) +#endif { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_MANHACKKILLED, "my_manhack:1", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL); +#else m_Sentences.Speak( "METROPOLICE_MANHACK_KILLED", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); +#endif DevMsg("My manhack died!\n"); m_hManhack = NULL; return; @@ -452,6 +497,9 @@ void CNPC_MetroPolice::NotifyDeadFriend( CBaseEntity* pFriend ) m_nIdleChatterType = METROPOLICE_CHATTER_ASK_QUESTION; } +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_MANDOWN, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL); +#else if ( GetSquad()->NumMembers() < 2 ) { m_Sentences.Speak( "METROPOLICE_LAST_OF_SQUAD", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); @@ -459,6 +507,7 @@ void CNPC_MetroPolice::NotifyDeadFriend( CBaseEntity* pFriend ) } m_Sentences.Speak( "METROPOLICE_MAN_DOWN", SENTENCE_PRIORITY_MEDIUM ); +#endif } @@ -466,6 +515,9 @@ void CNPC_MetroPolice::NotifyDeadFriend( CBaseEntity* pFriend ) //----------------------------------------------------------------------------- CNPC_MetroPolice::CNPC_MetroPolice() { +#ifdef MAPBASE + m_iGrenadeCapabilities = GRENCAP_GRENADE; +#endif } @@ -490,8 +542,10 @@ void CNPC_MetroPolice::PrescheduleThink( void ) { BaseClass::PrescheduleThink(); +#ifndef METROPOLICE_USES_RESPONSE_SYSTEM // Speak any queued sentences m_Sentences.UpdateSentenceQueue(); +#endif // Look at near players, always m_bPlayerIsNear = false; @@ -572,6 +626,11 @@ bool CNPC_MetroPolice::OverrideMoveFacing( const AILocalMoveGoal_t &move, float //----------------------------------------------------------------------------- void CNPC_MetroPolice::Precache( void ) { +#ifdef MAPBASE + // It doesn't matter if we can't find models/police.mdl in the string pool because then we know this NPC doesn't have that model. + if ( GetModelName() == NULL_STRING || GetModelName() == FindPooledString("models/police.mdl") ) + { +#endif if ( HasSpawnFlags( SF_NPC_START_EFFICIENT ) ) { SetModelName( AllocPooledString("models/police_cheaple.mdl" ) ); @@ -580,6 +639,9 @@ void CNPC_MetroPolice::Precache( void ) { SetModelName( AllocPooledString("models/police.mdl") ); } +#ifdef MAPBASE + } +#endif PrecacheModel( STRING( GetModelName() ) ); @@ -602,7 +664,9 @@ bool CNPC_MetroPolice::CreateComponents() if ( !BaseClass::CreateComponents() ) return false; +#ifndef METROPOLICE_USES_RESPONSE_SYSTEM m_Sentences.Init( this, "NPC_Metropolice.SentenceParameters" ); +#endif return true; } @@ -681,7 +745,11 @@ void CNPC_MetroPolice::Spawn( void ) pWeapon = GetActiveWeapon(); +#ifdef MAPBASE + if (!EntIsClass(pWeapon, gm_isz_class_Pistol) && !EntIsClass(pWeapon, gm_isz_class_357)) +#else if( !FClassnameIs( pWeapon, "weapon_pistol" ) ) +#endif { m_fWeaponDrawn = true; } @@ -706,7 +774,11 @@ void CNPC_MetroPolice::Spawn( void ) // Clear out spawnflag if we're missing the smg1 if( HasSpawnFlags( SF_METROPOLICE_ALWAYS_STITCH ) ) { +#ifdef MAPBASE + if ( !Weapon_OwnsThisType( STRING(gm_isz_class_SMG1) ) ) +#else if ( !Weapon_OwnsThisType( "weapon_smg1" ) ) +#endif { Warning( "Warning! Metrocop is trying to use the stitch behavior but he has no smg1!\n" ); RemoveSpawnFlags( SF_METROPOLICE_ALWAYS_STITCH ); @@ -752,6 +824,23 @@ void CNPC_MetroPolice::SpeakFuncTankSentence( int nSentenceType ) { switch ( nSentenceType ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + case FUNCTANK_SENTENCE_MOVE_TO_MOUNT: + SpeakIfAllowed( TLK_COP_FT_APPROACH, SENTENCE_PRIORITY_MEDIUM ); + break; + + case FUNCTANK_SENTENCE_JUST_MOUNTED: + SpeakIfAllowed( TLK_COP_FT_MOUNT, SENTENCE_PRIORITY_HIGH ); + break; + + case FUNCTANK_SENTENCE_SCAN_FOR_ENEMIES: + SpeakIfAllowed( TLK_COP_FT_SCAN, SENTENCE_PRIORITY_NORMAL ); + break; + + case FUNCTANK_SENTENCE_DISMOUNTING: + SpeakIfAllowed( TLK_COP_FT_DISMOUNT, SENTENCE_PRIORITY_HIGH ); + break; +#else case FUNCTANK_SENTENCE_MOVE_TO_MOUNT: m_Sentences.Speak( "METROPOLICE_FT_APPROACH", SENTENCE_PRIORITY_MEDIUM ); break; @@ -767,6 +856,7 @@ void CNPC_MetroPolice::SpeakFuncTankSentence( int nSentenceType ) case FUNCTANK_SENTENCE_DISMOUNTING: m_Sentences.Speak( "METROPOLICE_FT_DISMOUNT", SENTENCE_PRIORITY_HIGH ); break; +#endif } } @@ -778,6 +868,31 @@ void CNPC_MetroPolice::SpeakStandoffSentence( int nSentenceType ) { switch ( nSentenceType ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + case STANDOFF_SENTENCE_BEGIN_STANDOFF: + SpeakIfAllowed( TLK_COP_SO_BEGIN, SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_SQUAD_LEADER ); + break; + + case STANDOFF_SENTENCE_END_STANDOFF: + SpeakIfAllowed( TLK_COP_SO_END, SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_SQUAD_LEADER ); + break; + + case STANDOFF_SENTENCE_OUT_OF_AMMO: + AnnounceOutOfAmmo( ); + break; + + case STANDOFF_SENTENCE_FORCED_TAKE_COVER: + SpeakIfAllowed( TLK_COP_SO_END ); + break; + + case STANDOFF_SENTENCE_STAND_CHECK_TARGET: + if ( gm_flTimeLastSpokePeek != 0 && gpGlobals->curtime - gm_flTimeLastSpokePeek > 20 ) + { + SpeakIfAllowed( TLK_COP_SO_PEEK ); + gm_flTimeLastSpokePeek = gpGlobals->curtime; + } + break; +#else case STANDOFF_SENTENCE_BEGIN_STANDOFF: m_Sentences.Speak( "METROPOLICE_SO_BEGIN", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_SQUAD_LEADER ); break; @@ -801,6 +916,7 @@ void CNPC_MetroPolice::SpeakStandoffSentence( int nSentenceType ) gm_flTimeLastSpokePeek = gpGlobals->curtime; } break; +#endif } } @@ -811,6 +927,37 @@ void CNPC_MetroPolice::SpeakAssaultSentence( int nSentenceType ) { switch ( nSentenceType ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + case ASSAULT_SENTENCE_HIT_RALLY_POINT: + SpeakIfAllowed( TLK_COP_AS_HIT_RALLY, SENTENCE_PRIORITY_NORMAL ); + break; + + case ASSAULT_SENTENCE_HIT_ASSAULT_POINT: + SpeakIfAllowed( TLK_COP_AS_HIT_ASSAULT, SENTENCE_PRIORITY_NORMAL ); + break; + + case ASSAULT_SENTENCE_SQUAD_ADVANCE_TO_RALLY: + if ( SpeakIfAllowed( TLK_COP_AS_ADV_RALLY, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_SQUAD_LEADER ) ) + { + GetSquad()->BroadcastInteraction( g_interactionMetrocopClearSentenceQueues, NULL ); + } + break; + + case ASSAULT_SENTENCE_SQUAD_ADVANCE_TO_ASSAULT: + if ( SpeakIfAllowed( TLK_COP_AS_ADV_ASSAULT, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_SQUAD_LEADER ) ) + { + GetSquad()->BroadcastInteraction( g_interactionMetrocopClearSentenceQueues, NULL ); + } + break; + + case ASSAULT_SENTENCE_COVER_NO_AMMO: + AnnounceOutOfAmmo( ); + break; + + case ASSAULT_SENTENCE_UNDER_ATTACK: + SpeakIfAllowed( TLK_COP_GO_ALERT ); + break; +#else case ASSAULT_SENTENCE_HIT_RALLY_POINT: m_Sentences.SpeakQueued( "METROPOLICE_AS_HIT_RALLY", SENTENCE_PRIORITY_NORMAL ); break; @@ -840,6 +987,7 @@ void CNPC_MetroPolice::SpeakAssaultSentence( int nSentenceType ) case ASSAULT_SENTENCE_UNDER_ATTACK: m_Sentences.Speak( "METROPOLICE_GO_ALERT" ); break; +#endif } } @@ -875,6 +1023,70 @@ void CNPC_MetroPolice::SpeakSentence( int nSentenceType ) switch ( nSentenceType ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + case METROPOLICE_SENTENCE_FREEZE: + SpeakIfAllowed( TLK_COP_FREEZE, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); + break; + + case METROPOLICE_SENTENCE_HES_OVER_HERE: + SpeakIfAllowed( TLK_COP_OVER_HERE, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); + break; + + case METROPOLICE_SENTENCE_HES_RUNNING: + SpeakIfAllowed( TLK_COP_HES_RUNNING, SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL ); + break; + + case METROPOLICE_SENTENCE_TAKE_HIM_DOWN: + SpeakIfAllowed( TLK_COP_TAKE_HIM_DOWN, SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL ); + break; + + case METROPOLICE_SENTENCE_ARREST_IN_POSITION: + SpeakIfAllowed( TLK_COP_ARREST_IN_POS, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); + break; + + case METROPOLICE_SENTENCE_DEPLOY_MANHACK: + SpeakIfAllowed( TLK_COP_DEPLOY_MANHACK ); + break; + + case METROPOLICE_SENTENCE_MOVE_INTO_POSITION: + { + CBaseEntity *pEntity = GetEnemy(); + + // NOTE: This is a good time to check to see if the player is hurt. + // Have the cops notice this and call out + if ( pEntity && !HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) ) + { + if ( pEntity->IsPlayer() && (pEntity->GetHealth() <= 20) ) + { + if ( !HasMemory(bits_MEMORY_PLAYER_HURT) ) + { + if ( SpeakIfAllowed( TLK_COP_PLAYERHIT, SENTENCE_PRIORITY_HIGH ) ) + { +#ifdef MAPBASE + if (GetSquad()) + GetSquad()->SquadRemember(bits_MEMORY_PLAYER_HURT); +#else + m_pSquad->SquadRemember(bits_MEMORY_PLAYER_HURT); +#endif + } + } + } + + if ( GetNavigator()->GetPath()->GetPathLength() > 20 * 12.0f ) + { + SpeakIfAllowed( TLK_COP_FLANK ); + } + } + } + break; + + case METROPOLICE_SENTENCE_HEARD_SOMETHING: + if ( ( GetState() == NPC_STATE_ALERT ) || ( GetState() == NPC_STATE_IDLE ) ) + { + SpeakIfAllowed( TLK_COP_HEARD_SOMETHING, SENTENCE_PRIORITY_MEDIUM ); + } + break; +#else case METROPOLICE_SENTENCE_FREEZE: m_Sentences.Speak( "METROPOLICE_FREEZE", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); break; @@ -932,9 +1144,52 @@ void CNPC_MetroPolice::SpeakSentence( int nSentenceType ) m_Sentences.Speak( "METROPOLICE_HEARD_SOMETHING", SENTENCE_PRIORITY_MEDIUM ); } break; +#endif } } +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM +//========================================================= +//========================================================= +bool CNPC_MetroPolice::SpeakIfAllowed( const char *concept, const char *modifiers, SentencePriority_t sentencepriority, SentenceCriteria_t sentencecriteria ) +{ + AI_CriteriaSet set; + if (modifiers) + { + GetExpresser()->MergeModifiers(set, modifiers); + } + return SpeakIfAllowed( concept, set, sentencepriority, sentencecriteria ); +} + +//========================================================= +//========================================================= +bool CNPC_MetroPolice::SpeakIfAllowed( const char *concept, AI_CriteriaSet& modifiers, SentencePriority_t sentencepriority, SentenceCriteria_t sentencecriteria ) +{ + if ( sentencepriority != SENTENCE_PRIORITY_INVALID && !FOkToMakeSound( sentencepriority ) ) + return false; + + if ( !GetExpresser()->CanSpeakConcept( concept ) ) + return false; + + if ( Speak( concept, modifiers ) ) + { + JustMadeSound( sentencepriority, 2.0f /*GetTimeSpeechComplete()*/ ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_MetroPolice::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + + set.AppendCriteria( "numwarnings", UTIL_VarArgs("%d", m_nNumWarnings) ); +} +#endif + //----------------------------------------------------------------------------- // Speaking @@ -954,6 +1209,24 @@ void CNPC_MetroPolice::AnnounceEnemyType( CBaseEntity *pEnemy ) if ( m_pSquad->IsLeader( this ) || ( m_pSquad->GetLeader() && m_pSquad->GetLeader()->GetEnemy() != GetEnemy() ) ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + // First contact, and I'm the squad leader. + bool bEnemyInVehicle = false; + switch ( pEnemy->Classify() ) + { + case CLASS_PLAYER: + { + CBasePlayer *pPlayer = assert_cast( pEnemy ); + if ( pPlayer && pPlayer->IsInAVehicle() ) + { + bEnemyInVehicle = true; + } + } + break; + } + + SpeakIfAllowed( TLK_COP_ENEMY, UTIL_VarArgs("enemy_in_vehicle:%d", bEnemyInVehicle), SENTENCE_PRIORITY_HIGH ); +#else // First contact, and I'm the squad leader. const char *pSentenceName = "METROPOLICE_MONST"; switch ( pEnemy->Classify() ) @@ -998,6 +1271,7 @@ void CNPC_MetroPolice::AnnounceEnemyType( CBaseEntity *pEnemy ) } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); +#endif } else { @@ -1019,6 +1293,11 @@ void CNPC_MetroPolice::AnnounceEnemyKill( CBaseEntity *pEnemy ) if ( !pEnemy ) return; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendEnemyCriteria(set, pEnemy); + SpeakIfAllowed( TLK_COP_KILLENEMY, set, SENTENCE_PRIORITY_HIGH ); +#else const char *pSentenceName = "METROPOLICE_KILL_MONST"; switch ( pEnemy->Classify() ) { @@ -1053,6 +1332,7 @@ void CNPC_MetroPolice::AnnounceEnemyKill( CBaseEntity *pEnemy ) } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); +#endif } @@ -1061,6 +1341,16 @@ void CNPC_MetroPolice::AnnounceEnemyKill( CBaseEntity *pEnemy ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::AnnounceOutOfAmmo( ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + if ( HasCondition( COND_NO_PRIMARY_AMMO ) ) + { + SpeakIfAllowed( TLK_COP_NOAMMO ); + } + else + { + SpeakIfAllowed( TLK_COP_LOWAMMO ); + } +#else if ( HasCondition( COND_NO_PRIMARY_AMMO ) ) { m_Sentences.Speak( "METROPOLICE_COVER_NO_AMMO" ); @@ -1069,6 +1359,7 @@ void CNPC_MetroPolice::AnnounceOutOfAmmo( ) { m_Sentences.Speak( "METROPOLICE_COVER_LOW_AMMO" ); } +#endif } //----------------------------------------------------------------------------- @@ -1076,6 +1367,38 @@ void CNPC_MetroPolice::AnnounceOutOfAmmo( ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::AnnounceTakeCoverFromDanger( CSound *pSound ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + bool bGrenade = false; + bool bVehicle = false; + bool bManhack = false; + + CBaseEntity *pSoundOwner = pSound->m_hOwner; + if ( pSoundOwner ) + { + CBaseGrenade *pGrenade = dynamic_cast(pSoundOwner); + if ( pGrenade ) + { + if ( IRelationType( pGrenade->GetThrower() ) != D_LI ) + { + // special case call out for enemy grenades + bGrenade = true; + } + } + else if ( pSoundOwner->GetServerVehicle() ) + { + bVehicle = true; + } + else if ( FClassnameIs( pSoundOwner, "npc_manhack" ) ) + { + if ( pSoundOwner->HasPhysicsAttacker( 1.0f ) ) + { + bManhack = true; + } + } + } + + SpeakIfAllowed(TLK_COP_DANGER, UTIL_VarArgs("grenade:%d,vehicle:%d,manhack:%d", bGrenade, bVehicle, bManhack), SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL); +#else CBaseEntity *pSoundOwner = pSound->m_hOwner; if ( pSoundOwner ) { @@ -1110,6 +1433,7 @@ void CNPC_MetroPolice::AnnounceTakeCoverFromDanger( CSound *pSound ) // dangerous sound nearby!, call it out const char *pSentenceName = "METROPOLICE_DANGER"; m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL ); +#endif } @@ -2444,12 +2768,54 @@ void CNPC_MetroPolice::InputEnableManhackToss( inputdata_t &inputdata ) } } +#ifdef MAPBASE +void CNPC_MetroPolice::InputDisableManhackToss( inputdata_t &inputdata ) +{ + if ( !HasSpawnFlags( SF_METROPOLICE_NO_MANHACK_DEPLOY ) ) + { + AddSpawnFlags( SF_METROPOLICE_NO_MANHACK_DEPLOY ); + } +} + +void CNPC_MetroPolice::InputDeployManhack( inputdata_t &inputdata ) +{ + // I am aware this bypasses regular deployment conditions, but the mapper wants us to deploy a manhack, damn it! + // We do have to have one, though. + if ( m_iManhacks > 0 ) + { + SetSchedule(SCHED_METROPOLICE_DEPLOY_MANHACK); + } +} + +void CNPC_MetroPolice::InputAddManhacks( inputdata_t &inputdata ) +{ + m_iManhacks += inputdata.value.Int(); + + SetBodygroup( METROPOLICE_BODYGROUP_MANHACK, (m_iManhacks > 0) ); +} + +void CNPC_MetroPolice::InputSetManhacks( inputdata_t &inputdata ) +{ + m_iManhacks = inputdata.value.Int(); + + SetBodygroup( METROPOLICE_BODYGROUP_MANHACK, (m_iManhacks > 0) ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CNPC_MetroPolice::InputSetPoliceGoal( inputdata_t &inputdata ) { +#ifdef MAPBASE + if (/*!inputdata.value.String() ||*/ inputdata.value.String()[0] == 0) + { + m_PolicingBehavior.Disable(); + return; + } +#endif + CBaseEntity *pGoal = gEntList.FindEntityByName( NULL, inputdata.value.String() ); if ( pGoal == NULL ) @@ -2478,6 +2844,35 @@ void CNPC_MetroPolice::InputActivateBaton( inputdata_t &inputdata ) SetBatonState( inputdata.value.Bool() ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_MetroPolice::InputAdministerJustice( inputdata_t &inputdata ) +{ + AdministerJustice(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_MetroPolice::InputAddWarnings( inputdata_t &inputdata ) +{ + m_nNumWarnings += inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_MetroPolice::InputSetWarnings( inputdata_t &inputdata ) +{ + m_nNumWarnings = inputdata.value.Int(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -2485,7 +2880,11 @@ void CNPC_MetroPolice::InputActivateBaton( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::AlertSound( void ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_COP_GO_ALERT ); +#else m_Sentences.Speak( "METROPOLICE_GO_ALERT" ); +#endif } @@ -2498,7 +2897,13 @@ void CNPC_MetroPolice::DeathSound( const CTakeDamageInfo &info ) if ( IsOnFire() ) return; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendDamageCriteria(set, info); + SpeakIfAllowed( TLK_COP_DIE, set, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#else m_Sentences.Speak( "METROPOLICE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif } @@ -2517,6 +2922,12 @@ void CNPC_MetroPolice::LostEnemySound( void) if ( gpGlobals->curtime <= m_flNextLostSoundTime ) return; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + if (SpeakIfAllowed(TLK_COP_LOSTENEMY)) + { + m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); + } +#else const char *pSentence; if (!(CBaseEntity*)GetEnemy() || gpGlobals->curtime - GetEnemyLastTimeSeen() > 10) { @@ -2531,6 +2942,7 @@ void CNPC_MetroPolice::LostEnemySound( void) { m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); } +#endif } @@ -2546,7 +2958,11 @@ void CNPC_MetroPolice::FoundEnemySound( void) if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) ) return; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_COP_REFINDENEMY, SENTENCE_PRIORITY_HIGH ); +#else m_Sentences.Speak( "METROPOLICE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH ); +#endif } @@ -2571,6 +2987,59 @@ bool CNPC_MetroPolice::ShouldPlayIdleSound( void ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::IdleSound( void ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + // This happens when the NPC is waiting for his buddies to respond to him + switch( m_nIdleChatterType ) + { + case METROPOLICE_CHATTER_WAIT_FOR_RESPONSE: + break; + + case METROPOLICE_CHATTER_ASK_QUESTION: + { + if ( m_bPlayerIsNear && !HasMemory(bits_MEMORY_PLAYER_HARASSED) ) + { + if ( SpeakIfAllowed( TLK_COP_HARASS, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ) ) + { + Remember( bits_MEMORY_PLAYER_HARASSED ); + if ( GetSquad() ) + { + GetSquad()->SquadRemember(bits_MEMORY_PLAYER_HARASSED); + } + } + return; + } + + if ( !random->RandomInt(0,1) ) + break; + + int nQuestionType = random->RandomInt( 0, METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT ); + if ( !IsInSquad() || ( nQuestionType == METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT ) ) + { + SpeakIfAllowed(TLK_COP_IDLE); + break; + } + + if ( SpeakIfAllowed( TLK_COP_QUESTION, UTIL_VarArgs("combinequestion:%d", nQuestionType) ) ) + { + GetSquad()->BroadcastInteraction( g_interactionMetrocopIdleChatter, (void*)(METROPOLICE_CHATTER_RESPONSE + nQuestionType), this ); + m_nIdleChatterType = METROPOLICE_CHATTER_WAIT_FOR_RESPONSE; + } + } + break; + + default: + { + int nResponseType = m_nIdleChatterType - METROPOLICE_CHATTER_RESPONSE; + + if ( SpeakIfAllowed( TLK_COP_ANSWER, UTIL_VarArgs("combinequestion:%d", nResponseType) ) ) + { + GetSquad()->BroadcastInteraction( g_interactionMetrocopIdleChatter, (void*)(METROPOLICE_CHATTER_ASK_QUESTION), this ); + m_nIdleChatterType = METROPOLICE_CHATTER_ASK_QUESTION; + } + } + break; + } +#else bool bIsCriminal = PlayerIsCriminal(); // This happens when the NPC is waiting for his buddies to respond to him @@ -2636,6 +3105,7 @@ void CNPC_MetroPolice::IdleSound( void ) } break; } +#endif } @@ -2651,6 +3121,12 @@ void CNPC_MetroPolice::PainSound( const CTakeDamageInfo &info ) if ( IsOnFire() ) return; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + AI_CriteriaSet set; + ModifyOrAppendDamageCriteria(set, info); + SpeakIfAllowed(TLK_COP_PAIN, set, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS); + m_flNextPainSoundTime = gpGlobals->curtime + 1; +#else float healthRatio = (float)GetHealth() / (float)GetMaxHealth(); if ( healthRatio > 0.0f ) { @@ -2670,6 +3146,7 @@ void CNPC_MetroPolice::PainSound( const CTakeDamageInfo &info ) m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); m_flNextPainSoundTime = gpGlobals->curtime + 1; } +#endif } //----------------------------------------------------------------------------- @@ -2769,7 +3246,7 @@ void CNPC_MetroPolice::OnAnimEventStartDeployManhack( void ) if ( m_iManhacks <= 0 ) { - DevMsg( "Error: Throwing manhack but out of manhacks!\n" ); + CGMsg( 1, CON_GROUP_NPC_AI, "Error: Throwing manhack but out of manhacks!\n" ); return; } @@ -2806,6 +3283,10 @@ void CNPC_MetroPolice::OnAnimEventStartDeployManhack( void ) pManhack->SetParent( this, handAttachment ); m_hManhack = pManhack; + +#ifdef MAPBASE + m_OutManhack.Set(m_hManhack, pManhack, this); +#endif } //----------------------------------------------------------------------------- @@ -2883,7 +3364,7 @@ void CNPC_MetroPolice::OnAnimEventShove( void ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::OnAnimEventBatonOn( void ) { -#ifndef HL2MP +#if !defined(HL2MP) || defined(MAPBASE) CWeaponStunStick *pStick = dynamic_cast(GetActiveWeapon()); @@ -2900,7 +3381,7 @@ void CNPC_MetroPolice::OnAnimEventBatonOn( void ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::OnAnimEventBatonOff( void ) { -#ifndef HL2MP +#if !defined(HL2MP) || defined(MAPBASE) CWeaponStunStick *pStick = dynamic_cast(GetActiveWeapon()); @@ -2989,7 +3470,9 @@ bool CNPC_MetroPolice::HandleInteraction(int interactionType, void *data, CBaseC if ( interactionType == g_interactionMetrocopClearSentenceQueues ) { +#ifndef METROPOLICE_USES_RESPONSE_SYSTEM m_Sentences.ClearQueue(); +#endif return true; } @@ -3010,8 +3493,15 @@ bool CNPC_MetroPolice::HandleInteraction(int interactionType, void *data, CBaseC CBaseProp *pProp = (CBaseProp*)data; if( pProp != NULL ) { +#ifdef MAPBASE + if( pProp->NameMatches("cupcop_can") ) + m_OnCupCopped.FireOutput( sourceEnt, this ); + + m_OnHitByPhysicsObject.Set(pProp, sourceEnt, this); +#else if( pProp->NameMatches("cupcop_can") ) m_OnCupCopped.FireOutput( this, NULL ); +#endif } return true; @@ -3046,9 +3536,71 @@ Activity CNPC_MetroPolice::NPC_TranslateActivity( Activity newActivity ) newActivity = ACT_IDLE_ANGRY; } +#ifdef MAPBASE + if (newActivity == ACT_RANGE_ATTACK2) + { + return ACT_COMBINE_THROW_GRENADE; + } +#endif + return newActivity; } +#ifdef MAPBASE +Activity CNPC_MetroPolice::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) +{ + Activity translated = BaseClass::Weapon_TranslateActivity(baseAct, pRequired); + + if (!m_fWeaponDrawn) + { + // If our pistol is holstered, don't act like we have one in our hands. + switch (translated) + { + case ACT_WALK_PISTOL: return ACT_WALK; + case ACT_RUN_PISTOL: return ACT_RUN; + case ACT_IDLE_PISTOL: return ACT_IDLE; + } + } + + return translated; +} + +int CNPC_MetroPolice::UnholsterWeapon() +{ +#if 0 + if (!m_fWeaponDrawn && (!IsCurSchedule(SCHED_METROPOLICE_DRAW_PISTOL))) + SetSchedule(SCHED_METROPOLICE_DRAW_PISTOL); + + return -1; +#else + // Remain compatible with the original behavior + if (IsCurSchedule(SCHED_METROPOLICE_DRAW_PISTOL)) + return -1; + else if (!m_fWeaponDrawn) + { + SetSchedule(SCHED_METROPOLICE_DRAW_PISTOL); + return -1; + } + + return BaseClass::UnholsterWeapon(); +#endif +} + +void CNPC_MetroPolice::OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior ) +{ + BaseClass::OnChangeRunningBehavior( pOldBehavior, pNewBehavior ); + + // Fix the npc_metropolice using an invisible gun + if (!m_fWeaponDrawn) + { + // We can't just stop and draw, so fall back to gesture unholstering. + // Our implementation of UnholsterWeapon() just handles stopping and drawing, which we can skip in this case. + m_fWeaponDrawn = true; + BaseClass::UnholsterWeapon(); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Makes the held manhack solid //----------------------------------------------------------------------------- @@ -3068,6 +3620,12 @@ void CNPC_MetroPolice::ReleaseManhack( void ) // Make us active m_hManhack->RemoveSpawnFlags( SF_NPC_WAIT_FOR_SCRIPT ); m_hManhack->ClearSchedule( "Manhack released by metropolice" ); + +#ifdef MAPBASE + // FSOLID_COLLIDE_WITH_OWNER allows us to be remembered as the manhack's owner without making us invulnerable to it + m_hManhack->SetOwnerEntity( this ); + m_hManhack->AddSolidFlags( FSOLID_COLLIDE_WITH_OWNER ); +#endif // Start him with knowledge of our current enemy if ( GetEnemy() ) @@ -3160,6 +3718,17 @@ int CNPC_MetroPolice::SelectRangeAttackSchedule() return SCHED_METROPOLICE_DEPLOY_MANHACK; } +#ifdef MAPBASE + // Throw a grenade if not allowed to engage with weapon. + if ( CanGrenadeEnemy() ) + { + if ( OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + return SCHED_METROPOLICE_RANGE_ATTACK2; + } + } +#endif + return SCHED_METROPOLICE_ADVANCE; } @@ -3333,7 +3902,11 @@ int CNPC_MetroPolice::SelectCombatSchedule() { m_nRecentDamage = 0; m_flRecentDamageTime = 0; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_COVER_HEAVY_DAMAGE, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL); +#else m_Sentences.Speak( "METROPOLICE_COVER_HEAVY_DAMAGE", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); +#endif return SCHED_TAKE_COVER_FROM_ENEMY; } @@ -3376,7 +3949,11 @@ int CNPC_MetroPolice::SelectCombatSchedule() CBaseEntity *pBlocker = GetEnemyOccluder(); if ( pBlocker && pBlocker->GetHealth() > 0 && OccupyStrategySlotRange( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1, SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2 ) ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_SHOOTCOVER); +#else m_Sentences.Speak( "METROPOLICE_SHOOT_COVER" ); +#endif return SCHED_SHOOT_ENEMY_COVER; } } @@ -3385,6 +3962,27 @@ int CNPC_MetroPolice::SelectCombatSchedule() { if ( GetEnemy() && !(GetEnemy()->GetFlags() & FL_NOTARGET) ) { +#ifdef MAPBASE + if ( HasGrenades() ) + { + // We don't see our enemy. If it hasn't been long since I last saw him, + // and he's pretty close to the last place I saw him, throw a grenade in + // to flush him out. A wee bit of cheating here... + + float flTime; + float flDist; + + flTime = gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() ); + flDist = ( GetEnemy()->GetAbsOrigin() - GetEnemies()->LastSeenPosition( GetEnemy() ) ).Length(); + + //Msg("Time: %f Dist: %f\n", flTime, flDist ); + if ( flTime <= COMBINE_GRENADE_FLUSH_TIME && flDist <= COMBINE_GRENADE_FLUSH_DIST && CanGrenadeEnemy( false ) && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + return SCHED_METROPOLICE_RANGE_ATTACK2; + } + } +#endif + // Charge in and break the enemy's cover! return SCHED_ESTABLISH_LINE_OF_FIRE; } @@ -3800,6 +4398,148 @@ int CNPC_MetroPolice::SelectAirboatCombatSchedule() } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Standoff schedule selection +//----------------------------------------------------------------------------- +int CNPC_MetroPolice::SelectBehaviorOverrideSchedule() +{ + // Announce a new enemy + if ( HasCondition( COND_NEW_ENEMY ) ) + { + AnnounceEnemyType( GetEnemy() ); + } + + int nResult = SelectScheduleNewEnemy(); + if ( nResult != SCHED_NONE ) + return nResult; + + if (!HasBaton() && ((float)m_nRecentDamage / (float)GetMaxHealth()) > RECENT_DAMAGE_THRESHOLD) + { + m_nRecentDamage = 0; + m_flRecentDamageTime = 0; +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_COVER_HEAVY_DAMAGE, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL); +#else + m_Sentences.Speak( "METROPOLICE_COVER_HEAVY_DAMAGE", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); +#endif + + return SCHED_TAKE_COVER_FROM_ENEMY; + } + + if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) + { + nResult = SelectRangeAttackSchedule(); + if ( !GetShotRegulator()->IsInRestInterval() && nResult != SCHED_METROPOLICE_ADVANCE && nResult != SCHED_RANGE_ATTACK1 ) + return nResult; + } + + if ( HasCondition( COND_TOO_CLOSE_TO_ATTACK ) ) + { + return SCHED_BACK_AWAY_FROM_ENEMY; + } + + if ( HasCondition( COND_LOW_PRIMARY_AMMO ) || HasCondition( COND_NO_PRIMARY_AMMO ) ) + { + AnnounceOutOfAmmo( ); + return SCHED_HIDE_AND_RELOAD; + } + + if ( HasCondition(COND_WEAPON_SIGHT_OCCLUDED) && !HasBaton() ) + { + // If they are hiding behind something that we can destroy, start shooting at it. + CBaseEntity *pBlocker = GetEnemyOccluder(); + if ( pBlocker && pBlocker->GetHealth() > 0 && OccupyStrategySlotRange( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1, SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2 ) ) + { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_SHOOTCOVER); +#else + m_Sentences.Speak( "METROPOLICE_SHOOT_COVER" ); +#endif + return SCHED_SHOOT_ENEMY_COVER; + } + } + + if (HasCondition(COND_ENEMY_OCCLUDED)) + { + if ( GetEnemy() && !(GetEnemy()->GetFlags() & FL_NOTARGET) ) + { + if ( HasGrenades() ) + { + // We don't see our enemy. If it hasn't been long since I last saw him, + // and he's pretty close to the last place I saw him, throw a grenade in + // to flush him out. A wee bit of cheating here... + + float flTime; + float flDist; + + flTime = gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() ); + flDist = ( GetEnemy()->GetAbsOrigin() - GetEnemies()->LastSeenPosition( GetEnemy() ) ).Length(); + + //Msg("Time: %f Dist: %f\n", flTime, flDist ); + if ( flTime <= COMBINE_GRENADE_FLUSH_TIME && flDist <= COMBINE_GRENADE_FLUSH_DIST && CanGrenadeEnemy( false ) && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + return SCHED_METROPOLICE_RANGE_ATTACK2; + } + } + } + } + + // If you can't attack, but you can deploy a manhack, do it! + if( CanDeployManhack() && OccupyStrategySlot( SQUAD_SLOT_POLICE_DEPLOY_MANHACK ) ) + return SCHED_METROPOLICE_DEPLOY_MANHACK; + + return SCHED_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_MetroPolice::IsCrouchedActivity( Activity activity ) +{ + Activity realActivity = TranslateActivity(activity); + + switch ( realActivity ) + { + case ACT_RELOAD_LOW: + case ACT_COVER_LOW: + case ACT_COVER_PISTOL_LOW: + case ACT_COVER_SMG1_LOW: + case ACT_RELOAD_SMG1_LOW: + //case ACT_RELOAD_AR2_LOW: + case ACT_RELOAD_PISTOL_LOW: + case ACT_RELOAD_SHOTGUN_LOW: + + // These animations aren't actually "low" on metrocops + //case ACT_RANGE_AIM_LOW: + //case ACT_RANGE_AIM_AR2_LOW: + //case ACT_RANGE_AIM_SMG1_LOW: + //case ACT_RANGE_AIM_PISTOL_LOW: + + //case ACT_RANGE_ATTACK1_LOW: + //case ACT_RANGE_ATTACK_AR2_LOW: + //case ACT_RANGE_ATTACK_SMG1_LOW: + //case ACT_RANGE_ATTACK_PISTOL_LOW: + //case ACT_RANGE_ATTACK2_LOW: + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Standoff schedule selection +//----------------------------------------------------------------------------- +int CNPC_MetroPolice::CMetroPoliceStandoffBehavior::SelectScheduleAttack() +{ + int result = metropolice_new_component_behavior.GetBool() ? GetOuter()->SelectBehaviorOverrideSchedule() : SCHED_NONE; + if (result == SCHED_NONE) + result = BaseClass::SelectScheduleAttack(); + return result; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: // Input : &info - @@ -3884,6 +4624,9 @@ void CNPC_MetroPolice::PlayFlinchGesture( void ) //----------------------------------------------------------------------------- void CNPC_MetroPolice::AnnounceHarrassment( void ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_BACK_UP, SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL); +#else static const char *pWarnings[3] = { "METROPOLICE_BACK_UP_A", @@ -3892,6 +4635,7 @@ void CNPC_MetroPolice::AnnounceHarrassment( void ) }; m_Sentences.Speak( pWarnings[ random->RandomInt( 0, ARRAYSIZE(pWarnings)-1 ) ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL ); +#endif } //----------------------------------------------------------------------------- @@ -4013,7 +4757,11 @@ int CNPC_MetroPolice::SelectSchedule( void ) if ( HasCondition(COND_METROPOLICE_ON_FIRE) ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_ON_FIRE, "hurt_by_fire:1", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS); +#else m_Sentences.Speak( "METROPOLICE_ON_FIRE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif return SCHED_METROPOLICE_BURNING_STAND; } @@ -4025,17 +4773,66 @@ int CNPC_MetroPolice::SelectSchedule( void ) // See which state our player relationship is in if ( PlayerIsCriminal() == false ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_COP_HIT_BY_PHYSOBJ, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#else m_Sentences.Speak( "METROPOLICE_HIT_BY_PHYSOBJECT", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif m_nNumWarnings = METROPOLICE_MAX_WARNINGS; AdministerJustice(); } else if ( GlobalEntity_GetState( "gordon_precriminal" ) == GLOBAL_ON ) { // We're not allowed to respond, but warn them +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_COP_HARASS, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#else m_Sentences.Speak( "METROPOLICE_IDLE_HARASS_PLAYER", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +#endif } } +#ifdef MAPBASE + if ( m_hForcedGrenadeTarget ) + { + if ( m_flNextGrenadeCheck < gpGlobals->curtime ) + { + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + + // The fact we have a forced grenade target overrides whether we're marked as "capable". + // If we're *only* alt-fire capable, use an energy ball. If not, throw a grenade. + if (!IsAltFireCapable() || IsGrenadeCapable()) + { + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + { + // If we can, throw a grenade at the target. + // Ignore grenade count / distance / etc + if ( CheckCanThrowGrenade( vecTarget ) ) + { + m_hForcedGrenadeTarget = NULL; + return SCHED_METROPOLICE_FORCED_GRENADE_THROW; + } + } + } + else + { + if ( FVisible( m_hForcedGrenadeTarget ) ) + { + m_vecAltFireTarget = vecTarget; + m_hForcedGrenadeTarget = NULL; + return SCHED_METROPOLICE_AR2_ALTFIRE; + } + } + } + + // Can't throw at the target, so lets try moving to somewhere where I can see it + if ( !FVisible( m_hForcedGrenadeTarget ) ) + { + return SCHED_METROPOLICE_MOVE_TO_FORCED_GREN_LOS; + } + } +#endif + int nSched = SelectFlinchSchedule(); if ( nSched != SCHED_NONE ) return nSched; @@ -4125,7 +4922,11 @@ int CNPC_MetroPolice::SelectSchedule( void ) { if ( GetActiveWeapon() && (GetActiveWeapon()->m_iClip1 <= 5) ) { +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_COP_LOWAMMO ); +#else m_Sentences.Speak( "METROPOLICE_COVER_LOW_AMMO" ); +#endif return SCHED_HIDE_AND_RELOAD; } } @@ -4168,7 +4969,11 @@ int CNPC_MetroPolice::SelectSchedule( void ) break; case NPC_STATE_COMBAT: +#ifdef MAPBASE + if (!IsEnemyInAnAirboat() || !GetActiveWeapon() || !EntIsClass(GetActiveWeapon(), gm_isz_class_SMG1)) +#else if (!IsEnemyInAnAirboat() || !Weapon_OwnsThisType( "weapon_smg1" ) ) +#endif { int nResult = SelectCombatSchedule(); if ( nResult != SCHED_NONE ) @@ -4244,6 +5049,14 @@ int CNPC_MetroPolice::TranslateSchedule( int scheduleType ) if ( nSched != SCHED_NONE ) return nSched; } +#ifdef MAPBASE + if ( CanAltFireEnemy(false) && OccupyStrategySlot(SQUAD_SLOT_SPECIAL_ATTACK) ) + { + // If this metrocop has the balls to alt-fire the enemy's last known position, + // do so! + return SCHED_METROPOLICE_AR2_ALTFIRE; + } +#endif return SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE; case SCHED_WAKE_ANGRY: @@ -4270,7 +5083,21 @@ int CNPC_MetroPolice::TranslateSchedule( int scheduleType ) return SCHED_METROPOLICE_DRAW_PISTOL; } +#ifdef MAPBASE + if (CanAltFireEnemy( true ) && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK )) + { + // Since I'm holding this squadslot, no one else can try right now. If I die before the shot + // goes off, I won't have affected anyone else's ability to use this attack at their nearest + // convenience. + return SCHED_METROPOLICE_AR2_ALTFIRE; + } +#endif + +#ifdef MAPBASE + if (GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_SMG1)) +#else if( Weapon_OwnsThisType( "weapon_smg1" ) ) +#endif { if ( IsEnemyInAnAirboat() ) { @@ -4289,10 +5116,44 @@ int CNPC_MetroPolice::TranslateSchedule( int scheduleType ) } } break; +#ifdef MAPBASE + case SCHED_TAKE_COVER_FROM_ENEMY: + { + if ( m_pSquad ) + { + // Have to explicitly check innate range attack condition as may have weapon with range attack 2 + if ( g_pGameRules->IsSkillLevel( SKILL_HARD ) && + HasCondition(COND_CAN_RANGE_ATTACK2) && + OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + #ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed( TLK_COP_THROWGRENADE ); + #else + m_Sentences.Speak( "COMBINE_THROW_GRENADE" ); + #endif + return SCHED_METROPOLICE_RANGE_ATTACK2; + } + } + } + break; + case SCHED_HIDE_AND_RELOAD: + { + if( CanGrenadeEnemy() && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) && random->RandomInt( 0, 100 ) < 20 ) + { + // If I COULD throw a grenade and I need to reload, 20% chance I'll throw a grenade before I hide to reload. + return SCHED_METROPOLICE_RANGE_ATTACK2; + } + } + break; +#endif case SCHED_METROPOLICE_ADVANCE: if ( m_NextChargeTimer.Expired() && metropolice_charge.GetBool() ) { +#ifdef MAPBASE + if (GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_Pistol)) +#else if ( Weapon_OwnsThisType( "weapon_pistol" ) ) +#endif { if ( GetEnemy() && GetEnemy()->GetAbsOrigin().DistToSqr( GetAbsOrigin() ) > 300*300 ) { @@ -4417,7 +5278,11 @@ void CNPC_MetroPolice::StartTask( const Task_t *pTask ) break; } +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_ACTIVATE_BATON, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL); +#else m_Sentences.Speak( "METROPOLICE_ACTIVATE_BATON", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); +#endif SetIdealActivity( (Activity) ACT_ACTIVATE_BATON ); } else @@ -4428,7 +5293,11 @@ void CNPC_MetroPolice::StartTask( const Task_t *pTask ) break; } +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + SpeakIfAllowed(TLK_COP_DEACTIVATE_BATON, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL); +#else m_Sentences.Speak( "METROPOLICE_DEACTIVATE_BATON", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); +#endif SetIdealActivity( (Activity) ACT_DEACTIVATE_BATON ); } } @@ -4465,6 +5334,23 @@ void CNPC_MetroPolice::StartTask( const Task_t *pTask ) TaskComplete(); break; +#ifdef MAPBASE + case TASK_METROPOLICE_GET_PATH_TO_FORCED_GREN_LOS: + StartTask_GetPathToForced( pTask ); + break; + + case TASK_METROPOLICE_DEFER_SQUAD_GRENADES: + StartTask_DeferSquad( pTask ); + break; + + case TASK_METROPOLICE_FACE_TOSS_DIR: + break; + + case TASK_METROPOLICE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET: + StartTask_FaceAltFireTarget( pTask ); + break; +#endif + case TASK_METROPOLICE_GET_PATH_TO_STITCH: { if ( !ShouldAttemptToStitch() ) @@ -4828,6 +5714,20 @@ void CNPC_MetroPolice::RunTask( const Task_t *pTask ) } break; +#ifdef MAPBASE + case TASK_METROPOLICE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET: + RunTask_FaceAltFireTarget( pTask ); + break; + + case TASK_METROPOLICE_GET_PATH_TO_FORCED_GREN_LOS: + RunTask_GetPathToForced( pTask ); + break; + + case TASK_METROPOLICE_FACE_TOSS_DIR: + RunTask_FaceTossDir( pTask ); + break; +#endif + default: BaseClass::RunTask( pTask ); break; @@ -4947,18 +5847,34 @@ void CNPC_MetroPolice::BuildScheduleTestBits( void ) { ClearCustomInterruptCondition( COND_CAN_MELEE_ATTACK1 ); } + +#ifdef MAPBASE + if (gpGlobals->curtime < m_flNextAttack) + { + ClearCustomInterruptCondition( COND_CAN_RANGE_ATTACK1 ); + ClearCustomInterruptCondition( COND_CAN_RANGE_ATTACK2 ); + } +#endif } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- WeaponProficiency_t CNPC_MetroPolice::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) { +#ifdef MAPBASE + if (EntIsClass(pWeapon, gm_isz_class_Pistol)) +#else if( FClassnameIs( pWeapon, "weapon_pistol" ) ) +#endif { return WEAPON_PROFICIENCY_POOR; } +#ifdef MAPBASE + if (EntIsClass(pWeapon, gm_isz_class_SMG1)) +#else if( FClassnameIs( pWeapon, "weapon_smg1" ) ) +#endif { return WEAPON_PROFICIENCY_VERY_GOOD; } @@ -5040,7 +5956,11 @@ bool CNPC_MetroPolice::HasBaton( void ) CBaseCombatWeapon *pWeapon = GetActiveWeapon(); if ( pWeapon ) +#ifdef MAPBASE + return EntIsClass(pWeapon, gm_isz_class_Stunstick); +#else return FClassnameIs( pWeapon, "weapon_stunstick" ); +#endif return false; } @@ -5051,7 +5971,7 @@ bool CNPC_MetroPolice::HasBaton( void ) //----------------------------------------------------------------------------- bool CNPC_MetroPolice::BatonActive( void ) { -#ifndef HL2MP +#if !defined(HL2MP) || defined(MAPBASE) CWeaponStunStick *pStick = dynamic_cast(GetActiveWeapon()); @@ -5180,12 +6100,20 @@ AI_BEGIN_CUSTOM_NPC( npc_metropolice, CNPC_MetroPolice ) DECLARE_ANIMEVENT( AE_METROPOLICE_START_DEPLOY ); DECLARE_ANIMEVENT( AE_METROPOLICE_DRAW_PISTOL ); DECLARE_ANIMEVENT( AE_METROPOLICE_DEPLOY_MANHACK ); +#ifdef MAPBASE + DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE ) + DECLARE_ANIMEVENT( COMBINE_AE_ALTFIRE ) +#endif DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_CHARGE_ENEMY ); DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_HARASS ); DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_DEPLOY_MANHACK ); DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1 ); DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2 ); +#ifdef MAPBASE + DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_COVERING_FIRE1 ); + DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_COVERING_FIRE2 ); +#endif DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_ARREST_ENEMY ); DECLARE_ACTIVITY( ACT_METROPOLICE_DRAW_PISTOL ); @@ -5223,6 +6151,12 @@ AI_BEGIN_CUSTOM_NPC( npc_metropolice, CNPC_MetroPolice ) DECLARE_TASK( TASK_METROPOLICE_WAIT_FOR_SENTENCE ); DECLARE_TASK( TASK_METROPOLICE_GET_PATH_TO_PRECHASE ); DECLARE_TASK( TASK_METROPOLICE_CLEAR_PRECHASE ); +#ifdef MAPBASE + DECLARE_TASK( TASK_METROPOLICE_GET_PATH_TO_FORCED_GREN_LOS ) + DECLARE_TASK( TASK_METROPOLICE_DEFER_SQUAD_GRENADES ) + DECLARE_TASK( TASK_METROPOLICE_FACE_TOSS_DIR ) + DECLARE_TASK( TASK_METROPOLICE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET ) +#endif DECLARE_CONDITION( COND_METROPOLICE_ON_FIRE ); DECLARE_CONDITION( COND_METROPOLICE_ENEMY_RESISTING_ARREST ); @@ -5842,5 +6776,85 @@ DEFINE_SCHEDULE " COND_ENEMY_DEAD" ); +#ifdef MAPBASE +//========================================================= + // Mapmaker forced grenade throw + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_METROPOLICE_FORCED_GRENADE_THROW, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_METROPOLICE_FACE_TOSS_DIR 0" + " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade + " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2" + " TASK_METROPOLICE_DEFER_SQUAD_GRENADES 0" + "" + " Interrupts" + ) + + //========================================================= + // Move to LOS of the mapmaker's forced grenade throw target + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_METROPOLICE_MOVE_TO_FORCED_GREN_LOS, + + " Tasks " + " TASK_SET_TOLERANCE_DISTANCE 48" + " TASK_METROPOLICE_GET_PATH_TO_FORCED_GREN_LOS 0" + " TASK_SPEAK_SENTENCE 1" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + " " + " Interrupts " + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_CAN_MELEE_ATTACK1" + " COND_CAN_MELEE_ATTACK2" + " COND_HEAR_DANGER" + " COND_HEAR_MOVE_AWAY" + " COND_HEAVY_DAMAGE" + ) + + //========================================================= + // SCHED_METROPOLICE_RANGE_ATTACK2 + // + // secondary range attack. Overriden because base class stops attacking when the enemy is occluded. + // combines's grenade toss requires the enemy be occluded. + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_METROPOLICE_RANGE_ATTACK2, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_METROPOLICE_FACE_TOSS_DIR 0" + " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade + " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2" + " TASK_METROPOLICE_DEFER_SQUAD_GRENADES 0" + " TASK_SET_SCHEDULE SCHEDULE:SCHED_HIDE_AND_RELOAD" // don't run immediately after throwing grenade. + "" + " Interrupts" + ) + + //========================================================= + // AR2 Alt Fire Attack + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_METROPOLICE_AR2_ALTFIRE, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_ANNOUNCE_ATTACK 1" + " TASK_METROPOLICE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET ACTIVITY:ACT_COMBINE_AR2_ALTFIRE" + "" + " Interrupts" + " COND_TOO_CLOSE_TO_ATTACK" + ) +#endif + AI_END_CUSTOM_NPC() diff --git a/mp/src/game/server/hl2/npc_metropolice.h b/mp/src/game/server/hl2/npc_metropolice.h index 544fc68b..1f3f9517 100644 --- a/mp/src/game/server/hl2/npc_metropolice.h +++ b/mp/src/game/server/hl2/npc_metropolice.h @@ -24,13 +24,26 @@ #include "ai_behavior_police.h" #include "ai_behavior_follow.h" #include "ai_sentence.h" +#ifdef MAPBASE +#include "mapbase/ai_grenade.h" +#endif #include "props.h" +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE +#include "mapbase/expandedrs_combine.h" +#define METROPOLICE_USES_RESPONSE_SYSTEM 1 +#endif class CNPC_MetroPolice; +#ifdef MAPBASE +class CNPC_MetroPolice : public CAI_GrenadeUser +{ + DECLARE_CLASS( CNPC_MetroPolice, CAI_GrenadeUser ); +#else class CNPC_MetroPolice : public CAI_BaseActor { DECLARE_CLASS( CNPC_MetroPolice, CAI_BaseActor ); +#endif DECLARE_DATADESC(); public: @@ -46,6 +59,17 @@ public: float MaxYawSpeed( void ); void HandleAnimEvent( animevent_t *pEvent ); Activity NPC_TranslateActivity( Activity newActivity ); +#ifdef MAPBASE + Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired ); + + virtual int UnholsterWeapon( void ); + virtual void OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior ); + + const char* GetGrenadeAttachment() { return "LHand"; } + + virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; } + virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; } +#endif Vector EyeDirection3D( void ) { return CAI_BaseHumanoid::EyeDirection3D(); } // cops don't have eyes @@ -87,6 +111,15 @@ public: // Speaking virtual void SpeakSentence( int nSentenceType ); +#ifdef METROPOLICE_USES_RESPONSE_SYSTEM + bool SpeakIfAllowed( const char *concept, SentencePriority_t sentencepriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t sentencecriteria = SENTENCE_CRITERIA_IN_SQUAD ) + { + return SpeakIfAllowed( concept, NULL, sentencepriority, sentencecriteria ); + } + bool SpeakIfAllowed( const char *concept, const char *modifiers, SentencePriority_t sentencepriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t sentencecriteria = SENTENCE_CRITERIA_IN_SQUAD ); + bool SpeakIfAllowed( const char *concept, AI_CriteriaSet& modifiers, SentencePriority_t sentencepriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t sentencecriteria = SENTENCE_CRITERIA_IN_SQUAD ); + void ModifyOrAppendCriteria( AI_CriteriaSet& set ); +#endif // Set up the shot regulator based on the equipped weapon virtual void OnUpdateShotRegulator( ); @@ -101,7 +134,9 @@ public: void SetBatonState( bool state ); bool BatonActive( void ); +#ifndef METROPOLICE_USES_RESPONSE_SYSTEM CAI_Sentence< CNPC_MetroPolice > *GetSentences() { return &m_Sentences; } +#endif virtual bool AllowedToIgnite( void ) { return true; } @@ -164,8 +199,19 @@ private: // Inputs void InputEnableManhackToss( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDisableManhackToss( inputdata_t &inputdata ); + void InputDeployManhack( inputdata_t &inputdata ); + void InputAddManhacks( inputdata_t &inputdata ); + void InputSetManhacks( inputdata_t &inputdata ); +#endif void InputSetPoliceGoal( inputdata_t &inputdata ); void InputActivateBaton( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputAdministerJustice( inputdata_t &inputdata ); + void InputAddWarnings( inputdata_t &inputdata ); + void InputSetWarnings( inputdata_t &inputdata ); +#endif void NotifyDeadFriend ( CBaseEntity* pFriend ); @@ -202,6 +248,21 @@ private: int SelectAirboatCombatSchedule(); int SelectAirboatRangeAttackSchedule(); +#ifdef MAPBASE + int SelectBehaviorOverrideSchedule(); + + bool IsCrouchedActivity( Activity activity ); + + // This is something Valve did with Combine soldiers so they would throw grenades during standoffs. + // We're using a similar thing here so metrocops deploy manhacks. + class CMetroPoliceStandoffBehavior : public CAI_ComponentWithOuter + { + typedef CAI_ComponentWithOuter BaseClass; + + virtual int SelectScheduleAttack(); + }; +#endif + // Handle flinching bool IsHeavyDamage( const CTakeDamageInfo &info ); @@ -361,6 +422,12 @@ private: SCHED_METROPOLICE_ALERT_FACE_BESTSOUND, SCHED_METROPOLICE_RETURN_TO_PRECHASE, SCHED_METROPOLICE_SMASH_PROP, +#ifdef MAPBASE + SCHED_METROPOLICE_FORCED_GRENADE_THROW, + SCHED_METROPOLICE_MOVE_TO_FORCED_GREN_LOS, + SCHED_METROPOLICE_RANGE_ATTACK2, + SCHED_METROPOLICE_AR2_ALTFIRE, +#endif }; enum @@ -387,6 +454,12 @@ private: TASK_METROPOLICE_WAIT_FOR_SENTENCE, TASK_METROPOLICE_GET_PATH_TO_PRECHASE, TASK_METROPOLICE_CLEAR_PRECHASE, +#ifdef MAPBASE + TASK_METROPOLICE_GET_PATH_TO_FORCED_GREN_LOS, + TASK_METROPOLICE_DEFER_SQUAD_GRENADES, + TASK_METROPOLICE_FACE_TOSS_DIR, + TASK_METROPOLICE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET, +#endif }; private: @@ -441,19 +514,32 @@ private: // Outputs COutputEvent m_OnStunnedPlayer; COutputEvent m_OnCupCopped; +#ifdef MAPBASE + COutputEHANDLE m_OnHitByPhysicsObject; + COutputEHANDLE m_OutManhack; + + // Determines whether this NPC is allowed to use grenades or alt-fire stuff. + eGrenadeCapabilities m_iGrenadeCapabilities; +#endif AIHANDLE m_hManhack; CHandle m_hBlockingProp; CAI_ActBusyBehavior m_ActBusyBehavior; +#ifdef MAPBASE + CMetroPoliceStandoffBehavior m_StandoffBehavior; +#else CAI_StandoffBehavior m_StandoffBehavior; +#endif CAI_AssaultBehavior m_AssaultBehavior; CAI_FuncTankBehavior m_FuncTankBehavior; CAI_RappelBehavior m_RappelBehavior; CAI_PolicingBehavior m_PolicingBehavior; CAI_FollowBehavior m_FollowBehavior; +#ifndef METROPOLICE_USES_RESPONSE_SYSTEM CAI_Sentence< CNPC_MetroPolice > m_Sentences; +#endif int m_nRecentDamage; float m_flRecentDamageTime; diff --git a/mp/src/game/server/hl2/npc_monk.cpp b/mp/src/game/server/hl2/npc_monk.cpp index c20f558c..63a4ee6d 100644 --- a/mp/src/game/server/hl2/npc_monk.cpp +++ b/mp/src/game/server/hl2/npc_monk.cpp @@ -16,6 +16,9 @@ #include "ai_behavior.h" #include "ai_behavior_assault.h" #include "ai_behavior_lead.h" +#ifdef MAPBASE +#include "ai_behavior_functank.h" +#endif #include "npcevent.h" #include "ai_playerally.h" #include "ai_senses.h" @@ -103,6 +106,10 @@ private: CAI_AssaultBehavior m_AssaultBehavior; CAI_LeadBehavior m_LeadBehavior; +#ifdef MAPBASE + CAI_FuncTankBehavior m_FuncTankBehavior; +#endif + int m_iNumZombies; int m_iDangerousZombies; bool m_bPerfectAccuracy; @@ -113,6 +120,9 @@ private: BEGIN_DATADESC( CNPC_Monk ) // m_AssaultBehavior // m_LeadBehavior +#ifdef MAPBASE +// m_FuncTankBehavior +#endif DEFINE_FIELD( m_iNumZombies, FIELD_INTEGER ), DEFINE_FIELD( m_iDangerousZombies, FIELD_INTEGER ), DEFINE_FIELD( m_bPerfectAccuracy, FIELD_BOOLEAN ), @@ -132,6 +142,9 @@ bool CNPC_Monk::CreateBehaviors() { AddBehavior( &m_LeadBehavior ); AddBehavior( &m_AssaultBehavior ); +#ifdef MAPBASE + AddBehavior( &m_FuncTankBehavior ); +#endif return BaseClass::CreateBehaviors(); } @@ -665,6 +678,17 @@ int CNPC_Monk::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFa { if( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) { +#ifdef MAPBASE + // I thought it would be a nice touch. + if (RandomInt(1, 2) == 1 && CanRunAScriptedNPCInteraction(false)) + { + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + m_ScriptedInteractions[i].flNextAttemptTime = gpGlobals->curtime; + } + } +#endif + // Most likely backed into a corner. Just blaze away. return SCHED_MONK_RANGE_ATTACK1; } diff --git a/mp/src/game/server/hl2/npc_playercompanion.cpp b/mp/src/game/server/hl2/npc_playercompanion.cpp index a03a752f..7cfc3a24 100644 --- a/mp/src/game/server/hl2/npc_playercompanion.cpp +++ b/mp/src/game/server/hl2/npc_playercompanion.cpp @@ -31,12 +31,20 @@ #include "grenade_frag.h" #include #include "physics_npc_solver.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#include "world.h" +#endif ConVar ai_debug_readiness("ai_debug_readiness", "0" ); ConVar ai_use_readiness("ai_use_readiness", "1" ); // 0 = off, 1 = on, 2 = on for player squad only ConVar ai_readiness_decay( "ai_readiness_decay", "120" );// How many seconds it takes to relax completely ConVar ai_new_aiming( "ai_new_aiming", "1" ); +#ifdef COMPANION_MELEE_ATTACK +ConVar sk_companion_melee_damage("sk_companion_melee_damage", "25"); +#endif + #define GetReadinessUse() ai_use_readiness.GetInt() extern ConVar g_debug_transitions; @@ -46,6 +54,11 @@ extern ConVar g_debug_transitions; int AE_COMPANION_PRODUCE_FLARE; int AE_COMPANION_LIGHT_FLARE; int AE_COMPANION_RELEASE_FLARE; +#if COMPANION_MELEE_ATTACK +#define AE_PC_MELEE 3 + +#define COMPANION_MELEE_DIST 64.0 +#endif #define MAX_TIME_BETWEEN_BARRELS_EXPLODING 5.0f #define MAX_TIME_BETWEEN_CONSECUTIVE_PLAYER_KILLS 3.0f @@ -97,7 +110,9 @@ BEGIN_DATADESC( CNPC_PlayerCompanion ) #endif // HL2_EPISODIC //------------------------------------------------------------------------------ +#ifndef MAPBASE DEFINE_INPUTFUNC( FIELD_STRING, "GiveWeapon", InputGiveWeapon ), +#endif DEFINE_FIELD( m_flReadiness, FIELD_FLOAT ), DEFINE_FIELD( m_flReadinessSensitivity, FIELD_FLOAT ), @@ -130,6 +145,11 @@ BEGIN_DATADESC( CNPC_PlayerCompanion ) DEFINE_OUTPUT( m_OnWeaponPickup, "OnWeaponPickup" ), +#ifdef MAPBASE + DEFINE_AIGRENADE_DATADESC() + DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ), +#endif + END_DATADESC() //----------------------------------------------------------------------------- @@ -137,11 +157,20 @@ END_DATADESC() CNPC_PlayerCompanion::eCoverType CNPC_PlayerCompanion::gm_fCoverSearchType; bool CNPC_PlayerCompanion::gm_bFindingCoverFromAllEnemies; +#ifdef MAPBASE +string_t CNPC_PlayerCompanion::gm_iszMortarClassname; +string_t CNPC_PlayerCompanion::gm_iszGroundTurretClassname; +#else string_t CNPC_PlayerCompanion::gm_iszMortarClassname; string_t CNPC_PlayerCompanion::gm_iszFloorTurretClassname; string_t CNPC_PlayerCompanion::gm_iszGroundTurretClassname; string_t CNPC_PlayerCompanion::gm_iszShotgunClassname; string_t CNPC_PlayerCompanion::gm_iszRollerMineClassname; +#ifdef MAPBASE +string_t CNPC_PlayerCompanion::gm_iszSMG1Classname; +string_t CNPC_PlayerCompanion::gm_iszAR2Classname; +#endif +#endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -175,11 +204,20 @@ bool CNPC_PlayerCompanion::CreateBehaviors() //----------------------------------------------------------------------------- void CNPC_PlayerCompanion::Precache() { +#ifdef MAPBASE + gm_iszMortarClassname = AllocPooledString( "func_tankmortar" ); + gm_iszGroundTurretClassname = AllocPooledString( "npc_turret_ground" ); +#else gm_iszMortarClassname = AllocPooledString( "func_tankmortar" ); gm_iszFloorTurretClassname = AllocPooledString( "npc_turret_floor" ); gm_iszGroundTurretClassname = AllocPooledString( "npc_turret_ground" ); gm_iszShotgunClassname = AllocPooledString( "weapon_shotgun" ); gm_iszRollerMineClassname = AllocPooledString( "npc_rollermine" ); +#ifdef MAPBASE + gm_iszSMG1Classname = AllocPooledString( "weapon_smg1" ); + gm_iszAR2Classname = AllocPooledString( "weapon_ar2" ); +#endif +#endif PrecacheModel( STRING( GetModelName() ) ); @@ -188,6 +226,10 @@ void CNPC_PlayerCompanion::Precache() PrecacheModel( "models/props_junk/flare.mdl" ); #endif // HL2_EPISODIC +#ifdef MAPBASE + PrecacheScriptSound( "Weapon_CombineGuard.Special1" ); +#endif + BaseClass::Precache(); } @@ -234,7 +276,7 @@ void CNPC_PlayerCompanion::Spawn() m_AnnounceAttackTimer.Set( 10, 30 ); -#ifdef HL2_EPISODIC +#if HL2_EPISODIC && !MAPBASE // Mapbase permits this flag since the warning can be distracting and stripping the flag might break some HL2 maps in Episodic mods // We strip this flag because it's been made obsolete by the StartScripting behavior if ( HasSpawnFlags( SF_NPC_ALTCOLLISION ) ) { @@ -245,6 +287,10 @@ void CNPC_PlayerCompanion::Spawn() m_hFlare = NULL; #endif // HL2_EPISODIC +#if COMPANION_MELEE_ATTACK + m_nMeleeDamage = sk_companion_melee_damage.GetInt(); +#endif + BaseClass::Spawn(); } @@ -260,7 +306,7 @@ int CNPC_PlayerCompanion::Restore( IRestore &restore ) m_StandoffBehavior.SetActive( false ); } -#ifdef HL2_EPISODIC +#if HL2_EPISODIC && !MAPBASE // Mapbase permits this flag since the warning can be distracting and stripping the flag might break some HL2 maps in Episodic mods // We strip this flag because it's been made obsolete by the StartScripting behavior if ( HasSpawnFlags( SF_NPC_ALTCOLLISION ) ) { @@ -315,8 +361,13 @@ Disposition_t CNPC_PlayerCompanion::IRelationType( CBaseEntity *pTarget ) else if ( baseRelationship == D_HT && pTarget->IsNPC() && ((CAI_BaseNPC *)pTarget)->GetActiveWeapon() && +#ifdef MAPBASE + (EntIsClass( ((CAI_BaseNPC *)pTarget)->GetActiveWeapon(), gm_iszShotgunClassname ) && + ( !GetActiveWeapon() || !EntIsClass( GetActiveWeapon(), gm_iszShotgunClassname ) ) ) ) +#else ((CAI_BaseNPC *)pTarget)->GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname ) && ( !GetActiveWeapon() || !GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname ) ) ) +#endif { if ( (pTarget->GetAbsOrigin() - GetAbsOrigin()).LengthSqr() < Square( 25 * 12 ) ) { @@ -496,6 +547,14 @@ void CNPC_PlayerCompanion::GatherConditions() DoCustomSpeechAI(); } +#ifdef MAPBASE + // Alyx's custom combat AI copied to CNPC_PlayerCompanion for reasons specified in said function. + if ( m_NPCState == NPC_STATE_COMBAT ) + { + DoCustomCombatAI(); + } +#endif + if ( AI_IsSinglePlayer() && hl2_episodic.GetBool() && !GetEnemy() && HasCondition( COND_HEAR_PLAYER ) ) { Vector los = ( UTIL_GetLocalPlayer()->EyePosition() - EyePosition() ); @@ -538,7 +597,12 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void ) } // Mention the player is dead +#ifdef MAPBASE + // (unless we hate them) + if ( HasCondition( COND_TALKER_PLAYER_DEAD ) && (!pPlayer || IRelationType(pPlayer) > D_FR) ) +#else if ( HasCondition( COND_TALKER_PLAYER_DEAD ) ) +#endif { SpeakIfAllowed( TLK_PLDEAD ); } @@ -573,6 +637,15 @@ void CNPC_PlayerCompanion::BuildScheduleTestBits() SetCustomInterruptCondition( COND_PLAYER_PUSHING ); } +#if COMPANION_MELEE_ATTACK + if (IsCurSchedule(SCHED_RANGE_ATTACK1) || + IsCurSchedule(SCHED_BACK_AWAY_FROM_ENEMY) || + IsCurSchedule(SCHED_RUN_FROM_ENEMY)) + { + SetCustomInterruptCondition( COND_CAN_MELEE_ATTACK1 ); + } +#endif + if ( ( ConditionInterruptsCurSchedule( COND_GIVE_WAY ) || IsCurSchedule(SCHED_HIDE_AND_RELOAD ) || IsCurSchedule(SCHED_RELOAD ) || @@ -715,6 +788,46 @@ int CNPC_PlayerCompanion::SelectSchedule() } } +#ifdef MAPBASE + if ( m_hForcedGrenadeTarget ) + { + // Can't throw at the target, so lets try moving to somewhere where I can see it + if ( !FVisible( m_hForcedGrenadeTarget ) ) + { + return SCHED_PC_MOVE_TO_FORCED_GREN_LOS; + } + else if ( m_flNextGrenadeCheck < gpGlobals->curtime ) + { + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + + // The fact we have a forced grenade target overrides whether we're marked as "capable". + // If we're *only* alt-fire capable, use an energy ball. If not, throw a grenade. + if (!IsAltFireCapable() || IsGrenadeCapable()) + { + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + { + // If we can, throw a grenade at the target. + // Ignore grenade count / distance / etc + if ( CheckCanThrowGrenade( vecTarget ) ) + { + m_hForcedGrenadeTarget = NULL; + return SCHED_PC_FORCED_GRENADE_THROW; + } + } + } + else + { + if ( FVisible( m_hForcedGrenadeTarget ) ) + { + m_vecAltFireTarget = vecTarget; + m_hForcedGrenadeTarget = NULL; + return SCHED_PC_AR2_ALTFIRE; + } + } + } + } +#endif + int nSched = SelectFlinchSchedule(); if ( nSched != SCHED_NONE ) return nSched; @@ -873,10 +986,39 @@ bool CNPC_PlayerCompanion::IgnorePlayerPushing( void ) //----------------------------------------------------------------------------- int CNPC_PlayerCompanion::SelectScheduleCombat() { +#if COMPANION_MELEE_ATTACK + if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) + { + DevMsg("Returning melee attack schedule\n"); + return SCHED_MELEE_ATTACK1; + } +#endif + if ( CanReload() && (HasCondition ( COND_NO_PRIMARY_AMMO ) || HasCondition(COND_LOW_PRIMARY_AMMO)) ) { return SCHED_HIDE_AND_RELOAD; } + +#ifdef MAPBASE + if ( HasGrenades() && GetEnemy() && !HasCondition(COND_SEE_ENEMY) ) + { + // We don't see our enemy. If it hasn't been long since I last saw him, + // and he's pretty close to the last place I saw him, throw a grenade in + // to flush him out. A wee bit of cheating here... + + float flTime; + float flDist; + + flTime = gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() ); + flDist = ( GetEnemy()->GetAbsOrigin() - GetEnemies()->LastSeenPosition( GetEnemy() ) ).Length(); + + //Msg("Time: %f Dist: %f\n", flTime, flDist ); + if ( flTime <= COMBINE_GRENADE_FLUSH_TIME && flDist <= COMBINE_GRENADE_FLUSH_DIST && CanGrenadeEnemy( false ) && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + return SCHED_PC_RANGE_ATTACK2; + } + } +#endif return SCHED_NONE; } @@ -908,6 +1050,14 @@ bool CNPC_PlayerCompanion::ShouldDeferToFollowBehavior() return false; } +#if COMPANION_MELEE_ATTACK + if (HasCondition(COND_CAN_MELEE_ATTACK1) /*&& !GetFollowBehavior().IsActive()*/) + { + // We should only get melee condition if we're not moving + return false; + } +#endif + // Even though assault and act busy are placed ahead of the follow behavior in precedence, the below // code is necessary because we call ShouldDeferToFollowBehavior BEFORE we call the generic // BehaviorSelectSchedule, which tries the behaviors in priority order. @@ -943,6 +1093,12 @@ bool CNPC_PlayerCompanion::IsValidReasonableFacing( const Vector &vecSightDir, f if( ai_new_aiming.GetBool() ) { +#ifdef MAPBASE + // Hint node facing should still be obeyed + if (GetHintNode() && GetHintNode()->GetIgnoreFacing() != HIF_YES) + return true; +#endif + Vector vecEyePositionCentered = GetAbsOrigin(); vecEyePositionCentered.z = EyePosition().z; @@ -978,6 +1134,10 @@ int CNPC_PlayerCompanion::TranslateSchedule( int scheduleType ) pWeapon->Clip1() < ( pWeapon->GetMaxClip1() * .75 ) && pPlayer->GetAmmoCount( pWeapon->GetPrimaryAmmoType() ) ) { +#ifdef MAPBASE + // Less annoying + if ( !pWeapon->m_bInReload && (gpGlobals->curtime - GetLastEnemyTime()) > 5.0f ) +#endif SpeakIfAllowed( TLK_PLRELOAD ); } } @@ -1009,6 +1169,14 @@ int CNPC_PlayerCompanion::TranslateSchedule( int scheduleType ) return SCHED_PC_FLEE_FROM_BEST_SOUND; case SCHED_ESTABLISH_LINE_OF_FIRE: +#ifdef MAPBASE + if ( CanAltFireEnemy(false) && OccupyStrategySlot(SQUAD_SLOT_SPECIAL_ATTACK) ) + { + // If this companion has the balls to alt-fire the enemy's last known position, + // do so! + return SCHED_PC_AR2_ALTFIRE; + } +#endif case SCHED_MOVE_TO_WEAPON_RANGE: if ( IsMortar( GetEnemy() ) ) return SCHED_TAKE_COVER_FROM_ENEMY; @@ -1017,13 +1185,21 @@ int CNPC_PlayerCompanion::TranslateSchedule( int scheduleType ) case SCHED_CHASE_ENEMY: if ( IsMortar( GetEnemy() ) ) return SCHED_TAKE_COVER_FROM_ENEMY; +#ifdef MAPBASE + if ( GetEnemy() && EntIsClass( GetEnemy(), gm_isz_class_Gunship ) ) +#else if ( GetEnemy() && FClassnameIs( GetEnemy(), "npc_combinegunship" ) ) +#endif return SCHED_ESTABLISH_LINE_OF_FIRE; break; case SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK: // If we're fighting a gunship, try again +#ifdef MAPBASE + if ( GetEnemy() && EntIsClass( GetEnemy(), gm_isz_class_Gunship ) ) +#else if ( GetEnemy() && FClassnameIs( GetEnemy(), "npc_combinegunship" ) ) +#endif return SCHED_ESTABLISH_LINE_OF_FIRE; break; @@ -1034,10 +1210,44 @@ int CNPC_PlayerCompanion::TranslateSchedule( int scheduleType ) if ( GetShotRegulator()->IsInRestInterval() ) return SCHED_STANDOFF; +#ifdef MAPBASE + if (CanAltFireEnemy( true ) && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK )) + { + // Since I'm holding this squadslot, no one else can try right now. If I die before the shot + // goes off, I won't have affected anyone else's ability to use this attack at their nearest + // convenience. + return SCHED_PC_AR2_ALTFIRE; + } + + if ( !OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) ) + { + // Throw a grenade if not allowed to engage with weapon. + if ( CanGrenadeEnemy() ) + { + if ( OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + return SCHED_PC_RANGE_ATTACK2; + } + } + + return SCHED_STANDOFF; + } +#else if( !OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) ) return SCHED_STANDOFF; +#endif break; +#if COMPANION_MELEE_ATTACK + //case SCHED_BACK_AWAY_FROM_ENEMY: + // if (HasCondition(COND_CAN_MELEE_ATTACK1)) + // return SCHED_MELEE_ATTACK1; + // break; + + case SCHED_MELEE_ATTACK1: + return SCHED_PC_MELEE_AND_MOVE_AWAY; +#endif + case SCHED_FAIL_TAKE_COVER: if ( IsEnemyTurret() ) { @@ -1046,17 +1256,53 @@ int CNPC_PlayerCompanion::TranslateSchedule( int scheduleType ) break; case SCHED_RUN_FROM_ENEMY_FALLBACK: { +#if COMPANION_MELEE_ATTACK + if (HasCondition(COND_CAN_MELEE_ATTACK1) && !HasCondition(COND_HEAVY_DAMAGE)) + { + return SCHED_MELEE_ATTACK1; + } +#endif if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) { return SCHED_RANGE_ATTACK1; } break; } + +#ifdef MAPBASE + case SCHED_TAKE_COVER_FROM_ENEMY: + { + if ( m_pSquad ) + { + // Have to explicitly check innate range attack condition as may have weapon with range attack 2 + if ( HasCondition(COND_CAN_RANGE_ATTACK2) && + OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) ) + { + SpeakIfAllowed("TLK_THROWGRENADE"); + return SCHED_PC_RANGE_ATTACK2; + } + } + } + break; + case SCHED_HIDE_AND_RELOAD: + { + if( CanGrenadeEnemy() && OccupyStrategySlot( SQUAD_SLOT_SPECIAL_ATTACK ) && random->RandomInt( 0, 100 ) < 20 ) + { + // If I COULD throw a grenade and I need to reload, 20% chance I'll throw a grenade before I hide to reload. + return SCHED_PC_RANGE_ATTACK2; + } + } + break; +#endif } return BaseClass::TranslateSchedule( scheduleType ); } +#ifdef MAPBASE +//extern float GetCurrentGravity( void ); +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_PlayerCompanion::StartTask( const Task_t *pTask ) @@ -1114,6 +1360,23 @@ void CNPC_PlayerCompanion::StartTask( const Task_t *pTask ) } break; +#ifdef MAPBASE + case TASK_PC_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET: + StartTask_FaceAltFireTarget( pTask ); + break; + + case TASK_PC_GET_PATH_TO_FORCED_GREN_LOS: + StartTask_GetPathToForced( pTask ); + break; + + case TASK_PC_DEFER_SQUAD_GRENADES: + StartTask_DeferSquad( pTask ); + break; + + case TASK_PC_FACE_TOSS_DIR: + break; +#endif + default: BaseClass::StartTask( pTask ); break; @@ -1161,6 +1424,20 @@ void CNPC_PlayerCompanion::RunTask( const Task_t *pTask ) } break; +#ifdef MAPBASE + case TASK_PC_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET: + RunTask_FaceAltFireTarget( pTask ); + break; + + case TASK_PC_GET_PATH_TO_FORCED_GREN_LOS: + RunTask_GetPathToForced( pTask ); + break; + + case TASK_PC_FACE_TOSS_DIR: + RunTask_FaceTossDir( pTask ); + break; +#endif + default: BaseClass::RunTask( pTask ); break; @@ -1366,6 +1643,21 @@ Activity CNPC_PlayerCompanion::NPC_TranslateActivity( Activity activity ) activity = ACT_RUN_PROTECTED; } +#ifdef COMPANION_HOLSTER_WORKAROUND + if (activity == ACT_DISARM || activity == ACT_ARM) + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon() ? GetActiveWeapon() : m_hWeapons[m_iLastHolsteredWeapon]; + if (pWeapon && pWeapon->WeaponClassify() != WEPCLASS_HANDGUN) + { + switch (activity) + { + case ACT_DISARM: return ACT_DISARM_RIFLE; + case ACT_ARM: return ACT_ARM_RIFLE; + } + } + } +#endif + activity = BaseClass::NPC_TranslateActivity( activity ); if ( activity == ACT_IDLE ) @@ -1376,6 +1668,15 @@ Activity CNPC_PlayerCompanion::NPC_TranslateActivity( Activity activity ) } } +#ifdef MAPBASE + // Vorts use ACT_RANGE_ATTACK2, but they should translate to ACT_VORTIGAUNT_DISPEL + // before that reaches this code... + if (activity == ACT_RANGE_ATTACK2) + { + activity = ACT_COMBINE_THROW_GRENADE; + } +#endif + return TranslateActivityReadiness( activity ); } @@ -1449,14 +1750,44 @@ void CNPC_PlayerCompanion::HandleAnimEvent( animevent_t *pEvent ) case EVENT_WEAPON_RELOAD: if ( GetActiveWeapon() ) { +#ifdef MAPBASE + GetActiveWeapon()->Reload_NPC(); +#else GetActiveWeapon()->WeaponSound( RELOAD_NPC ); GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); +#endif ClearCondition(COND_LOW_PRIMARY_AMMO); ClearCondition(COND_NO_PRIMARY_AMMO); ClearCondition(COND_NO_SECONDARY_AMMO); } break; +#if COMPANION_MELEE_ATTACK + case AE_PC_MELEE: + { + CBaseEntity *pHurt = CheckTraceHullAttack(COMPANION_MELEE_DIST, -Vector(16, 16, 18), Vector(16, 16, 18), 0, DMG_CLUB); + CBaseCombatCharacter* pBCC = ToBaseCombatCharacter(pHurt); + if (pBCC) + { + Vector forward, up; + AngleVectors(GetLocalAngles(), &forward, NULL, &up); + + if (pBCC->IsPlayer()) + { + pBCC->ViewPunch(QAngle(-12, -7, 0)); + pHurt->ApplyAbsVelocityImpulse(forward * 100 + up * 50); + } + + CTakeDamageInfo info(this, this, m_nMeleeDamage, DMG_CLUB); + CalculateMeleeDamageForce(&info, forward, pBCC->GetAbsOrigin()); + pBCC->TakeDamage(info); + + EmitSound("NPC_Combine.WeaponBash"); + } + break; + } +#endif + default: BaseClass::HandleAnimEvent( pEvent ); break; @@ -1533,6 +1864,20 @@ void CNPC_PlayerCompanion::ModifyOrAppendCriteria( AI_CriteriaSet& set ) set.AppendCriteria( "hurt_by_fire", "1" ); } +#ifdef MAPBASE + // Ported from Alyx. + AIEnemiesIter_t iter; + int iNumEnemies = 0; + for ( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) + { + if ( pEMemory->hEnemy->IsAlive() && ( pEMemory->hEnemy->Classify() != CLASS_BULLSEYE ) ) + { + iNumEnemies++; + } + } + set.AppendCriteria( "num_enemies", UTIL_VarArgs( "%d", iNumEnemies ) ); +#endif + if ( m_bReadinessCapable ) { switch( GetReadinessLevel() ) @@ -1574,11 +1919,34 @@ bool CNPC_PlayerCompanion::IsReadinessCapable() return false; #endif +#ifdef MAPBASE +#ifdef HL2_EPISODIC + if (GetActiveWeapon()) +#else + // We already know we have a weapon due to the check above +#endif + { + // Rather than looking up the activity string, we just make sure our weapon accepts a few basic readiness activity overrides. + // This lets us make sure our weapon is readiness-capable to begin with. + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ( pWeapon->ActivityOverride(ACT_IDLE_RELAXED, NULL) == ACT_IDLE_RELAXED && + pWeapon->ActivityOverride( ACT_IDLE_STIMULATED, NULL ) == ACT_IDLE_STIMULATED && + pWeapon->ActivityOverride( ACT_IDLE_AGITATED, NULL ) == ACT_IDLE_AGITATED ) + return false; + + if (LookupActivity( "ACT_IDLE_AIM_RIFLE_STIMULATED" ) == ACT_INVALID) + return false; + + if (EntIsClass(GetActiveWeapon(), gm_isz_class_RPG)) + return false; + } +#else if( GetActiveWeapon() && LookupActivity("ACT_IDLE_AIM_RIFLE_STIMULATED") == ACT_INVALID ) return false; if( GetActiveWeapon() && FClassnameIs( GetActiveWeapon(), "weapon_rpg" ) ) return false; +#endif return true; } @@ -2329,7 +2697,11 @@ Vector CNPC_PlayerCompanion::GetActualShootPosition( const Vector &shootOrigin ) //------------------------------------------------------------------------------ WeaponProficiency_t CNPC_PlayerCompanion::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) { +#ifdef MAPBASE + if ( EntIsClass(pWeapon, gm_iszAR2Classname) ) +#else if( FClassnameIs( pWeapon, "weapon_ar2" ) ) +#endif { return WEAPON_PROFICIENCY_VERY_GOOD; } @@ -2346,7 +2718,11 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) // If this weapon is a shotgun, take measures to control how many // are being used in this squad. Don't allow a companion to pick up // a shotgun if a squadmate already has one. +#ifdef MAPBASE + if (EntIsClass(pWeapon, gm_iszShotgunClassname)) +#else if( pWeapon->ClassMatches( gm_iszShotgunClassname ) ) +#endif { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); } @@ -2366,6 +2742,15 @@ bool CNPC_PlayerCompanion::ShouldLookForBetterWeapon() if ( m_bDontPickupWeapons ) return false; +#ifdef MAPBASE + // Now that citizens can holster weapons, they might look for a new one while unarmed. + // Since that could already be worked around with OnHolster > DisableWeaponPickup, I decided to keep it that way in case it's desirable. + + // Don't look for a new weapon if we have secondary ammo for our current one. + if (m_iNumGrenades > 0 && IsAltFireCapable() && GetActiveWeapon() && GetActiveWeapon()->UsesSecondaryAmmo()) + return false; +#endif + return BaseClass::ShouldLookForBetterWeapon(); } @@ -2382,10 +2767,94 @@ void CNPC_PlayerCompanion::Weapon_Equip( CBaseCombatWeapon *pWeapon ) void CNPC_PlayerCompanion::PickupWeapon( CBaseCombatWeapon *pWeapon ) { BaseClass::PickupWeapon( pWeapon ); +#ifdef MAPBASE + SetPotentialSpeechTarget( pWeapon ); + SetSpeechTarget(pWeapon); + SpeakIfAllowed( TLK_NEWWEAPON ); + m_OnWeaponPickup.FireOutput( pWeapon, this ); +#else SpeakIfAllowed( TLK_NEWWEAPON ); m_OnWeaponPickup.FireOutput( this, this ); +#endif } +#if COMPANION_MELEE_ATTACK +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CNPC_PlayerCompanion::KeyValue( const char *szKeyName, const char *szValue ) +{ + // MeleeAttack01 restoration, see CNPC_PlayerCompanion::MeleeAttack1Conditions + if (FStrEq(szKeyName, "EnableMeleeAttack")) + { + if (!FStrEq(szValue, "0")) + CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 ); + else + CapabilitiesRemove( bits_CAP_INNATE_MELEE_ATTACK1 ); + + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: For unused citizen melee attack (vorts might use this too) +// Input : +// Output : +//----------------------------------------------------------------------------- +int CNPC_PlayerCompanion::MeleeAttack1Conditions ( float flDot, float flDist ) +{ + if (!GetActiveWeapon()) + return COND_NONE; + + if (IsMoving()) + { + // Is moving, cond_none + return COND_NONE; + } + + if (flDist > COMPANION_MELEE_DIST) + { + return COND_NONE; // COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.7) + { + return COND_NONE; // COND_NOT_FACING_ATTACK; + } + + if (GetEnemy()) + { + // Check Z + if ( fabs(GetEnemy()->GetAbsOrigin().z - GetAbsOrigin().z) > 64 ) + return COND_NONE; + + if ( GetEnemy()->MyCombatCharacterPointer() && GetEnemy()->MyCombatCharacterPointer()->GetHullType() == HULL_TINY ) + { + return COND_NONE; + } + } + + // Make sure not trying to kick through a window or something. + trace_t tr; + Vector vecSrc, vecEnd; + + vecSrc = WorldSpaceCenter(); + vecEnd = GetEnemy()->WorldSpaceCenter(); + + AI_TraceLine(vecSrc, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); + if( tr.m_pEnt != GetEnemy() ) + { + return COND_NONE; + } + + return COND_CAN_MELEE_ATTACK1; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -2880,11 +3349,19 @@ bool CNPC_PlayerCompanion::OverrideMove( float flInterval ) if ( !overrode && GetNavigator()->GetGoalType() != GOALTYPE_NONE ) { +#ifdef MAPBASE + #define iszEnvFire gm_isz_class_EnvFire +#else string_t iszEnvFire = AllocPooledString( "env_fire" ); +#endif string_t iszBounceBomb = AllocPooledString( "combine_mine" ); #ifdef HL2_EPISODIC +#ifdef MAPBASE + #define iszNPCTurretFloor gm_isz_class_FloorTurret +#else string_t iszNPCTurretFloor = AllocPooledString( "npc_turret_floor" ); +#endif string_t iszEntityFlame = AllocPooledString( "entityflame" ); #endif // HL2_EPISODIC @@ -3503,6 +3980,7 @@ void CNPC_PlayerCompanion::InputDisableWeaponPickup( inputdata_t &inputdata ) m_bDontPickupWeapons = true; } +#ifndef MAPBASE // See CAI_BaseNPC::InputGiveWeapon() //------------------------------------------------------------------------------ // Purpose: Give the NPC in question the weapon specified //------------------------------------------------------------------------------ @@ -3522,6 +4000,7 @@ void CNPC_PlayerCompanion::InputGiveWeapon( inputdata_t &inputdata ) } } } +#endif #if HL2_EPISODIC //------------------------------------------------------------------------------ @@ -3570,6 +4049,9 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak } CBaseEntity *pInflictor = info.GetInflictor(); +#ifdef MAPBASE + AI_CriteriaSet modifiers; +#else int iNumBarrels = 0; int iConsecutivePlayerKills = 0; bool bPuntedGrenade = false; @@ -3578,6 +4060,7 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak bool bVictimWasAttacker = false; bool bHeadshot = false; bool bOneShot = false; +#endif if ( dynamic_cast( pInflictor ) && ( info.GetDamageType() & DMG_BLAST ) ) { @@ -3590,7 +4073,11 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak m_iNumConsecutiveBarrelsExploded++; m_fLastBarrelExploded = gpGlobals->curtime; +#ifdef MAPBASE + modifiers.AppendCriteria( "num_barrels", UTIL_VarArgs("%i", m_iNumConsecutiveBarrelsExploded) ); +#else iNumBarrels = m_iNumConsecutiveBarrelsExploded; +#endif } else { @@ -3602,7 +4089,11 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak } m_iNumConsecutivePlayerKills++; m_fLastPlayerKill = gpGlobals->curtime; +#ifdef MAPBASE + modifiers.AppendCriteria( "consecutive_player_kills", UTIL_VarArgs("%i", m_iNumConsecutivePlayerKills) ); +#else iConsecutivePlayerKills = m_iNumConsecutivePlayerKills; +#endif } // don't comment on kills when she can't see the victim @@ -3612,29 +4103,53 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak } // check if the player killed an enemy by punting a grenade +#ifdef MAPBASE + modifiers.AppendCriteria( "punted_grenade", ( pInflictor && Fraggrenade_WasPunted( pInflictor ) && Fraggrenade_WasCreatedByCombine( pInflictor ) ) ? "1" : "0" ); +#else if ( pInflictor && Fraggrenade_WasPunted( pInflictor ) && Fraggrenade_WasCreatedByCombine( pInflictor ) ) { bPuntedGrenade = true; } +#endif // check if the victim was Alyx's enemy +#ifdef MAPBASE + modifiers.AppendCriteria( "victim_was_enemy", GetEnemy() == pVictim ? "1" : "0" ); +#else if ( GetEnemy() == pVictim ) { bVictimWasEnemy = true; } +#endif AI_EnemyInfo_t *pEMemory = GetEnemies()->Find( pVictim ); if ( pEMemory != NULL ) { // was Alyx being mobbed by this enemy? +#ifdef MAPBASE + modifiers.AppendCriteria( "victim_was_mob", pEMemory->bMobbedMe ? "1" : "0" ); + modifiers.AppendCriteria( "victim_was_attacker", pEMemory->timeLastReceivedDamageFrom > 0 ? "1" : "0" ); +#else bVictimWasMob = pEMemory->bMobbedMe; // has Alyx recieved damage from this enemy? if ( pEMemory->timeLastReceivedDamageFrom > 0 ) { bVictimWasAttacker = true; } +#endif } +#ifdef MAPBASE + else + { + modifiers.AppendCriteria( "victim_was_mob", "0" ); + modifiers.AppendCriteria( "victim_was_attacker", "0" ); + } +#endif +#ifdef MAPBASE + modifiers.AppendCriteria( "headshot", ((pCombatVictim->LastHitGroup() == HITGROUP_HEAD) && (info.GetDamageType() & DMG_BULLET)) ? "1" : "0" ); + modifiers.AppendCriteria( "oneshot", ((pCombatVictim->GetDamageCount() == 1) && (info.GetDamageType() & DMG_BULLET)) ? "1" : "0" ); +#else // Was it a headshot? if ( ( pCombatVictim->LastHitGroup() == HITGROUP_HEAD ) && ( info.GetDamageType() & DMG_BULLET ) ) { @@ -3646,18 +4161,145 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak { bOneShot = true; } +#endif +#ifdef MAPBASE + ModifyOrAppendEnemyCriteria(modifiers, pVictim); +#else // set up the speech modifiers CFmtStrN<512> modifiers( "num_barrels:%d,distancetoplayerenemy:%f,playerAmmo:%s,consecutive_player_kills:%d," "punted_grenade:%d,victim_was_enemy:%d,victim_was_mob:%d,victim_was_attacker:%d,headshot:%d,oneshot:%d", iNumBarrels, EnemyDistance( pVictim ), info.GetAmmoName(), iConsecutivePlayerKills, bPuntedGrenade, bVictimWasEnemy, bVictimWasMob, bVictimWasAttacker, bHeadshot, bOneShot ); +#endif SpeakIfAllowed( TLK_PLAYER_KILLED_NPC, modifiers ); BaseClass::OnPlayerKilledOther( pVictim, info ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ + if ( pVictim ) + { + if (pVictim->IsPlayer() || (pVictim->IsNPC() && + ( pVictim->MyNPCPointer()->GetLastPlayerDamageTime() == 0 || + gpGlobals->curtime - pVictim->MyNPCPointer()->GetLastPlayerDamageTime() > 5 )) ) + { + AI_CriteriaSet modifiers; + + AI_EnemyInfo_t *pEMemory = GetEnemies()->Find( pVictim ); + if ( pEMemory != NULL ) + { + modifiers.AppendCriteria( "victim_was_mob", pEMemory->bMobbedMe ? "1" : "0" ); + modifiers.AppendCriteria( "victim_was_attacker", pEMemory->timeLastReceivedDamageFrom > 0 ? "1" : "0" ); + } + else + { + modifiers.AppendCriteria( "victim_was_mob", "0" ); + modifiers.AppendCriteria( "victim_was_attacker", "0" ); + } + + CBaseCombatCharacter *pCombatVictim = pVictim->MyCombatCharacterPointer(); + if (pCombatVictim) + { + modifiers.AppendCriteria( "headshot", ((pCombatVictim->LastHitGroup() == HITGROUP_HEAD) && (info.GetDamageType() & DMG_BULLET)) ? "1" : "0" ); + modifiers.AppendCriteria( "oneshot", ((pCombatVictim->GetDamageCount() == 1) && (info.GetDamageType() & DMG_BULLET)) ? "1" : "0" ); + } + else + { + modifiers.AppendCriteria( "headshot", "0" ); + modifiers.AppendCriteria( "oneshot", "0" ); + } + + SetPotentialSpeechTarget( pVictim ); + SetSpeechTarget( pVictim ); + SpeakIfAllowed( TLK_ENEMY_DEAD, modifiers ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles stuff ported from Alyx. +// +// For some reason, I thought Alyx's mobbed AI was used to measure enemy count for criteria stuff, which I wanted citizens to use. +// Now that I realize enemy counting for criteria is elsewhere and this is used for just mobbing in general, I deactivated it +// since it would barely be used and I don't know what kind of an impact it has on performance. +// +// If you want to use it, feel free to re-activate. +//----------------------------------------------------------------------------- +void CNPC_PlayerCompanion::DoCustomCombatAI( void ) +{ + /* + #define COMPANION_MIN_MOB_DIST_SQR Square(120) // Any enemy closer than this adds to the 'mob' + #define COMPANION_MIN_CONSIDER_DIST Square(1200) // Only enemies within this range are counted and considered to generate AI speech + + AIEnemiesIter_t iter; + + float visibleEnemiesScore = 0.0f; + float closeEnemiesScore = 0.0f; + + for ( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) + { + if ( IRelationType( pEMemory->hEnemy ) != D_NU && IRelationType( pEMemory->hEnemy ) != D_LI && pEMemory->hEnemy->GetAbsOrigin().DistToSqr(GetAbsOrigin()) <= COMPANION_MIN_CONSIDER_DIST ) + { + if( pEMemory->hEnemy && pEMemory->hEnemy->IsAlive() && gpGlobals->curtime - pEMemory->timeLastSeen <= 0.5f && pEMemory->hEnemy->Classify() != CLASS_BULLSEYE ) + { + if( pEMemory->hEnemy->GetAbsOrigin().DistToSqr(GetAbsOrigin()) <= COMPANION_MIN_MOB_DIST_SQR ) + { + closeEnemiesScore += 1.0f; + } + else + { + visibleEnemiesScore += 1.0f; + } + } + } + } + + if( closeEnemiesScore > 2 ) + { + SetCondition( COND_MOBBED_BY_ENEMIES ); + + // mark anyone in the mob as having mobbed me + for ( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) + { + if ( pEMemory->bMobbedMe ) + continue; + + if ( IRelationType( pEMemory->hEnemy ) != D_NU && IRelationType( pEMemory->hEnemy ) != D_LI && pEMemory->hEnemy->GetAbsOrigin().DistToSqr(GetAbsOrigin()) <= COMPANION_MIN_CONSIDER_DIST ) + { + if( pEMemory->hEnemy && pEMemory->hEnemy->IsAlive() && gpGlobals->curtime - pEMemory->timeLastSeen <= 0.5f && pEMemory->hEnemy->Classify() != CLASS_BULLSEYE ) + { + if( pEMemory->hEnemy->GetAbsOrigin().DistToSqr(GetAbsOrigin()) <= COMPANION_MIN_MOB_DIST_SQR ) + { + pEMemory->bMobbedMe = true; + } + } + } + } + } + else + { + ClearCondition( COND_MOBBED_BY_ENEMIES ); + } + + // Say a combat thing + if( HasCondition( COND_MOBBED_BY_ENEMIES ) ) + { + SpeakIfAllowed( TLK_MOBBED ); + } + else if( visibleEnemiesScore > 4 ) + { + SpeakIfAllowed( TLK_MANY_ENEMIES ); + } + */ +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CNPC_PlayerCompanion::IsNavigationUrgent( void ) @@ -3712,10 +4354,20 @@ AI_BEGIN_CUSTOM_NPC( player_companion_base, CNPC_PlayerCompanion ) DECLARE_TASK( TASK_PC_WAITOUT_MORTAR ) DECLARE_TASK( TASK_PC_GET_PATH_OFF_COMPANION ) +#ifdef MAPBASE + DECLARE_TASK( TASK_PC_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET ) + DECLARE_TASK( TASK_PC_GET_PATH_TO_FORCED_GREN_LOS ) + DECLARE_TASK( TASK_PC_DEFER_SQUAD_GRENADES ) + DECLARE_TASK( TASK_PC_FACE_TOSS_DIR ) +#endif DECLARE_ANIMEVENT( AE_COMPANION_PRODUCE_FLARE ) DECLARE_ANIMEVENT( AE_COMPANION_LIGHT_FLARE ) DECLARE_ANIMEVENT( AE_COMPANION_RELEASE_FLARE ) +#ifdef MAPBASE + DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE ) + DECLARE_ANIMEVENT( COMBINE_AE_ALTFIRE ) +#endif //========================================================= // > TakeCoverFromBestSound @@ -3845,6 +4497,107 @@ AI_BEGIN_CUSTOM_NPC( player_companion_base, CNPC_PlayerCompanion ) "" ) +#ifdef COMPANION_MELEE_ATTACK + DEFINE_SCHEDULE + ( + SCHED_PC_MELEE_AND_MOVE_AWAY, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_FACE_ENEMY 0" + " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack + " TASK_MELEE_ATTACK1 0" + " TASK_SET_SCHEDULE SCHEDULE:SCHED_MOVE_AWAY_FROM_ENEMY" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + //" COND_LIGHT_DAMAGE" + " COND_HEAVY_DAMAGE" + " COND_ENEMY_OCCLUDED" + ) +#endif + +#ifdef MAPBASE + //========================================================= + // AR2 Alt Fire Attack + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_PC_AR2_ALTFIRE, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_ANNOUNCE_ATTACK 1" + " TASK_PC_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET ACTIVITY:ACT_COMBINE_AR2_ALTFIRE" + "" + " Interrupts" + " COND_TOO_CLOSE_TO_ATTACK" + ) + + //========================================================= + // Move to LOS of the mapmaker's forced grenade throw target + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_PC_MOVE_TO_FORCED_GREN_LOS, + + " Tasks " + " TASK_SET_TOLERANCE_DISTANCE 48" + " TASK_PC_GET_PATH_TO_FORCED_GREN_LOS 0" + " TASK_SPEAK_SENTENCE 1" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + " " + " Interrupts " + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_CAN_MELEE_ATTACK1" + " COND_CAN_MELEE_ATTACK2" + " COND_HEAR_DANGER" + " COND_HEAR_MOVE_AWAY" + " COND_HEAVY_DAMAGE" + ) + + //========================================================= + // Mapmaker forced grenade throw + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_PC_FORCED_GRENADE_THROW, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_PC_FACE_TOSS_DIR 0" + " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade + " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2" + " TASK_PC_DEFER_SQUAD_GRENADES 0" + "" + " Interrupts" + ) + + //========================================================= + // SCHED_PC_RANGE_ATTACK2 + // + // secondary range attack. Overriden because base class stops attacking when the enemy is occluded. + // combines's grenade toss requires the enemy be occluded. + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_PC_RANGE_ATTACK2, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_PC_FACE_TOSS_DIR 0" + " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade + " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2" + " TASK_PC_DEFER_SQUAD_GRENADES 0" + " TASK_SET_SCHEDULE SCHEDULE:SCHED_HIDE_AND_RELOAD" // don't run immediately after throwing grenade. + "" + " Interrupts" + ) +#endif + AI_END_CUSTOM_NPC() diff --git a/mp/src/game/server/hl2/npc_playercompanion.h b/mp/src/game/server/hl2/npc_playercompanion.h index f1c3b1f3..81ba080c 100644 --- a/mp/src/game/server/hl2/npc_playercompanion.h +++ b/mp/src/game/server/hl2/npc_playercompanion.h @@ -22,6 +22,10 @@ #include "ai_behavior_passenger_companion.h" #endif +#ifdef MAPBASE +#include "mapbase/ai_grenade.h" +#endif + #if defined( _WIN32 ) #pragma once #endif @@ -85,15 +89,25 @@ public: class CPhysicsProp; +#ifdef MAPBASE +// If you think about it, this is really unnecessary. +//#define COMPANION_MELEE_ATTACK 1 +#endif + //----------------------------------------------------------------------------- // // CLASS: CNPC_PlayerCompanion // //----------------------------------------------------------------------------- - +#ifdef MAPBASE +class CNPC_PlayerCompanion : public CAI_GrenadeUser +{ + DECLARE_CLASS( CNPC_PlayerCompanion, CAI_GrenadeUser ); +#else class CNPC_PlayerCompanion : public CAI_PlayerAlly { DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly ); +#endif public: //--------------------------------- @@ -189,7 +203,9 @@ public: virtual void ReadinessLevelChanged( int iPriorLevel ) { } +#ifndef MAPBASE void InputGiveWeapon( inputdata_t &inputdata ); +#endif #ifdef HL2_EPISODIC //--------------------------------- @@ -217,6 +233,13 @@ public: public: virtual void OnPlayerKilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); +#ifdef MAPBASE + // This is just here to overwrite ai_playerally's TLK_ENEMY_DEAD + virtual void OnKilledNPC(CBaseCombatCharacter *pKilled) {} + + virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); + virtual void DoCustomCombatAI( void ); +#endif //--------------------------------- //--------------------------------- @@ -256,6 +279,11 @@ public: bool Weapon_CanUse( CBaseCombatWeapon *pWeapon ); void Weapon_Equip( CBaseCombatWeapon *pWeapon ); void PickupWeapon( CBaseCombatWeapon *pWeapon ); + +#if COMPANION_MELEE_ATTACK + bool KeyValue( const char *szKeyName, const char *szValue ); + int MeleeAttack1Conditions( float flDot, float flDist ); +#endif bool FindCoverPos( CBaseEntity *pEntity, Vector *pResult); bool FindCoverPosInRadius( CBaseEntity *pEntity, const Vector &goalPos, float coverRadius, Vector *pResult ); @@ -308,6 +336,16 @@ public: bool AllowReadinessValueChange( void ); +#ifdef MAPBASE + virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; } + virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; } + +private: + + // Determines whether this NPC is allowed to use grenades or alt-fire stuff. + eGrenadeCapabilities m_iGrenadeCapabilities; +#endif + protected: //----------------------------------------------------- // Conditions, Schedules, Tasks @@ -326,10 +364,25 @@ protected: SCHED_PC_FAIL_TAKE_COVER_TURRET, SCHED_PC_FAKEOUT_MORTAR, SCHED_PC_GET_OFF_COMPANION, +#ifdef COMPANION_MELEE_ATTACK + SCHED_PC_MELEE_AND_MOVE_AWAY, +#endif +#ifdef MAPBASE + SCHED_PC_AR2_ALTFIRE, + SCHED_PC_MOVE_TO_FORCED_GREN_LOS, + SCHED_PC_FORCED_GRENADE_THROW, + SCHED_PC_RANGE_ATTACK2, // Grenade throw +#endif NEXT_SCHEDULE, TASK_PC_WAITOUT_MORTAR = BaseClass::NEXT_TASK, TASK_PC_GET_PATH_OFF_COMPANION, +#ifdef MAPBASE + TASK_PC_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET, + TASK_PC_GET_PATH_TO_FORCED_GREN_LOS, + TASK_PC_DEFER_SQUAD_GRENADES, + TASK_PC_FACE_TOSS_DIR, +#endif NEXT_TASK, }; @@ -406,11 +459,25 @@ protected: //----------------------------------------------------- +#ifdef MAPBASE + static string_t gm_iszMortarClassname; + #define gm_iszFloorTurretClassname gm_isz_class_FloorTurret + static string_t gm_iszGroundTurretClassname; + #define gm_iszShotgunClassname gm_isz_class_Shotgun + #define gm_iszRollerMineClassname gm_isz_class_Rollermine + #define gm_iszSMG1Classname gm_isz_class_SMG1 + #define gm_iszAR2Classname gm_isz_class_AR2 +#else static string_t gm_iszMortarClassname; static string_t gm_iszFloorTurretClassname; static string_t gm_iszGroundTurretClassname; static string_t gm_iszShotgunClassname; static string_t gm_iszRollerMineClassname; +#ifdef MAPBASE + static string_t gm_iszSMG1Classname; + static string_t gm_iszAR2Classname; +#endif +#endif //----------------------------------------------------- @@ -424,6 +491,10 @@ protected: COutputEvent m_OnWeaponPickup; +#if COMPANION_MELEE_ATTACK + int m_nMeleeDamage; +#endif + CStopwatch m_SpeechWatch_PlayerLooking; DECLARE_DATADESC(); diff --git a/mp/src/game/server/hl2/npc_rollermine.cpp b/mp/src/game/server/hl2/npc_rollermine.cpp index d850fe2c..ec9be919 100644 --- a/mp/src/game/server/hl2/npc_rollermine.cpp +++ b/mp/src/game/server/hl2/npc_rollermine.cpp @@ -388,7 +388,11 @@ BEGIN_DATADESC( CNPC_RollerMine ) DEFINE_FIELD( m_bBuried, FIELD_BOOLEAN ), DEFINE_FIELD( m_wakeUp, FIELD_BOOLEAN ), DEFINE_FIELD( m_bEmbedOnGroundImpact, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bHackedByAlyx, FIELD_BOOLEAN, "Hacked" ), +#else DEFINE_FIELD( m_bHackedByAlyx, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_bPowerDown, FIELD_BOOLEAN ), DEFINE_FIELD( m_flPowerDownTime, FIELD_TIME ), @@ -543,6 +547,9 @@ void CNPC_RollerMine::Spawn( void ) BaseClass::Spawn(); AddEFlags( EFL_NO_DISSOLVE ); +#ifdef MAPBASE + AddEFlags( EFL_NO_MEGAPHYSCANNON_RAGDOLL ); +#endif CapabilitiesClear(); CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_SQUAD ); @@ -1957,6 +1964,10 @@ void CNPC_RollerMine::NotifyInteraction( CAI_BaseNPC *pUser ) // Force the rollermine open here. At very least, this ensures that the // correct, smaller bounding box is recomputed around it. Open(); + +#ifdef MAPBASE + m_OnHacked.FireOutput(pUser, this); +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/npc_scanner.cpp b/mp/src/game/server/hl2/npc_scanner.cpp index 86cfcad2..fb49ff95 100644 --- a/mp/src/game/server/hl2/npc_scanner.cpp +++ b/mp/src/game/server/hl2/npc_scanner.cpp @@ -179,6 +179,11 @@ BEGIN_DATADESC( CNPC_CScanner ) DEFINE_INPUTFUNC( FIELD_STRING, "DeployMine", InputDeployMine ), DEFINE_INPUTFUNC( FIELD_STRING, "EquipMine", InputEquipMine ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "DisablePhotos", InputDisablePhotos ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnablePhotos", InputEnablePhotos ), +#endif + DEFINE_OUTPUT( m_OnPhotographPlayer, "OnPhotographPlayer" ), DEFINE_OUTPUT( m_OnPhotographNPC, "OnPhotographNPC" ), @@ -217,6 +222,10 @@ CNPC_CScanner::CNPC_CScanner() { m_bIsClawScanner = false; } + +#ifdef MAPBASE + CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 ); +#endif } //----------------------------------------------------------------------------- @@ -289,7 +298,9 @@ void CNPC_CScanner::Spawn(void) // -------------------------------------------- +#ifndef MAPBASE // Moved to constructor so keyvalue works CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 ); +#endif m_bPhotoTaken = false; @@ -319,6 +330,27 @@ void CNPC_CScanner::Activate() m_pEyeFlash->SetScale( 1.4 ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CNPC_CScanner::KeyValue( const char *szKeyName, const char *szValue ) +{ + // Any "Counter" outputs are changed to "OutCounter" before spawning. + if (FStrEq(szKeyName, "DisablePhotos") && FStrEq(szValue, "1")) + { + CapabilitiesRemove(bits_CAP_INNATE_MELEE_ATTACK1); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} +#endif + //------------------------------------------------------------------------------ // Purpose: Override to split in two when attacked //------------------------------------------------------------------------------ @@ -958,7 +990,12 @@ void CNPC_CScanner::DeployMine() //----------------------------------------------------------------------------- float CNPC_CScanner::GetMaxSpeed() { +#ifdef MAPBASE + // Don't stomp custom max speed in base class + if( IsStriderScout() && m_flCustomMaxSpeed <= 0.0f ) +#else if( IsStriderScout() ) +#endif { return SCANNER_SCOUT_MAX_SPEED; } @@ -1034,6 +1071,24 @@ void CNPC_CScanner::InputEquipMine(inputdata_t &inputdata) pEnt->Spawn(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputDisablePhotos( inputdata_t &inputdata ) +{ + CapabilitiesRemove(bits_CAP_INNATE_MELEE_ATTACK1); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputEnablePhotos( inputdata_t &inputdata ) +{ + CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Tells the scanner to go photograph an entity. @@ -1450,6 +1505,10 @@ int CNPC_CScanner::SelectSchedule(void) return SCHED_CSCANNER_SPOTLIGHT_HOVER; // Melee attack if possible +#ifdef MAPBASE + if ( CapabilitiesGet() & bits_CAP_INNATE_MELEE_ATTACK1 ) + { +#endif if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) { if ( random->RandomInt(0,1) ) @@ -1458,6 +1517,13 @@ int CNPC_CScanner::SelectSchedule(void) // TODO: a schedule where he makes an alarm sound? return SCHED_SCANNER_CHASE_ENEMY; } +#ifdef MAPBASE + } + else + { + return SCHED_CSCANNER_SPOTLIGHT_HOVER; + } +#endif // If I'm far from the enemy, stay up high and approach in spotlight mode float fAttack2DDist = ( GetEnemyLKP() - GetAbsOrigin() ).Length2D(); @@ -1866,6 +1932,15 @@ void CNPC_CScanner::UpdateOnRemove( void ) //------------------------------------------------------------------------------ void CNPC_CScanner::TakePhoto(void) { +#ifdef MAPBASE + // Only take photos if we're allowed + if (!(CapabilitiesGet() & bits_CAP_INNATE_MELEE_ATTACK1)) + { + m_bPhotoTaken = true; + return; + } +#endif + ScannerEmitSound( "TakePhoto" ); m_pEyeFlash->SetScale( 1.4 ); diff --git a/mp/src/game/server/hl2/npc_scanner.h b/mp/src/game/server/hl2/npc_scanner.h index f34eba35..109b2a7d 100644 --- a/mp/src/game/server/hl2/npc_scanner.h +++ b/mp/src/game/server/hl2/npc_scanner.h @@ -51,6 +51,9 @@ public: virtual char *GetScannerSoundPrefix( void ); void Spawn(void); void Activate(); +#ifdef MAPBASE + bool KeyValue( const char *szKeyName, const char *szValue ); +#endif void StartTask( const Task_t *pTask ); void UpdateOnRemove( void ); void DeployMine(); @@ -67,6 +70,10 @@ public: void InputInspectTargetSpotlight( inputdata_t &inputdata ); void InputDeployMine( inputdata_t &inputdata ); void InputEquipMine( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDisablePhotos( inputdata_t &inputdata ); + void InputEnablePhotos( inputdata_t &inputdata ); +#endif void InputShouldInspect( inputdata_t &inputdata ); void InspectTarget( inputdata_t &inputdata, ScannerFlyMode_t eFlyMode ); diff --git a/mp/src/game/server/hl2/npc_stalker.cpp b/mp/src/game/server/hl2/npc_stalker.cpp index 204d7a55..0165c29d 100644 --- a/mp/src/game/server/hl2/npc_stalker.cpp +++ b/mp/src/game/server/hl2/npc_stalker.cpp @@ -126,7 +126,14 @@ BEGIN_DATADESC( CNPC_Stalker ) DEFINE_FIELD( m_flNextBreatheSoundTime, FIELD_TIME ), DEFINE_FIELD( m_flNextScrambleSoundTime, FIELD_TIME ), DEFINE_FIELD( m_nextSmokeTime, FIELD_TIME ), +#ifdef MAPBASE + // Funnily enough, we can very easily treat this as a boolean value in Hammer + // since "0" means don't attack and "1" means attack. There is no unique behavior beyond 1. + DEFINE_KEYFIELD( m_iPlayerAggression, FIELD_INTEGER, "Aggression" ), + DEFINE_KEYFIELD( m_bBleed, FIELD_BOOLEAN, "Bleed" ), +#else DEFINE_FIELD( m_iPlayerAggression, FIELD_INTEGER ), +#endif DEFINE_FIELD( m_flNextScreamTime, FIELD_TIME ), // Function Pointers @@ -300,7 +307,9 @@ void CNPC_Stalker::Spawn( void ) m_flDistTooFar = MAX_STALKER_FIRE_RANGE; +#ifndef MAPBASE m_iPlayerAggression = 0; +#endif GetSenses()->SetDistLook(MAX_STALKER_FIRE_RANGE - 1); } @@ -329,6 +338,11 @@ void CNPC_Stalker::Precache( void ) PrecacheScriptSound( "NPC_Stalker.Pain" ); PrecacheScriptSound( "NPC_Stalker.Die" ); +#ifdef MAPBASE + if (m_bBleed) + PrecacheParticleSystem( "blood_impact_synth_01" ); +#endif + BaseClass::Precache(); } @@ -389,6 +403,33 @@ void CNPC_Stalker::Event_Killed( const CTakeDamageInfo &info ) BaseClass::Event_Killed( info ); } +#ifdef MAPBASE +extern void DispatchParticleEffect( const char *pszParticleName, Vector vecOrigin, QAngle vecAngles, CBaseEntity *pEntity = NULL ); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_Stalker::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) +{ + // Is it insane to desire for stalkers to bleed? No. + // Is it insane to port the hunter's blood particle system because + // it fits with the fact stalkers probably don't run on regular blood anymore? Maybe. + if (m_bBleed) + { + if ( ( inputInfo.GetDamageType() & DMG_BULLET ) || + ( inputInfo.GetDamageType() & DMG_BUCKSHOT ) || + ( inputInfo.GetDamageType() & DMG_CLUB ) || + ( inputInfo.GetDamageType() & DMG_NEVERGIB ) ) + { + QAngle vecAngles; + VectorAngles( ptr->plane.normal, vecAngles ); + DispatchParticleEffect( "blood_impact_synth_01", ptr->endpos, vecAngles ); + } + } + + BaseClass::TraceAttack( inputInfo, vecDir, ptr, pAccumulator ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : @@ -1106,6 +1147,30 @@ void CNPC_Stalker::DrawAttackBeam(void) */ } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: Fixes stalker beam cleanup not working correctly +//------------------------------------------------------------------------------ +void CNPC_Stalker::UpdateOnRemove( void ) +{ + if (m_pBeam) + { + StopSound(m_pBeam->entindex(), "NPC_Stalker.BurnWall" ); + StopSound(m_pBeam->entindex(), "NPC_Stalker.BurnFlesh" ); + + UTIL_Remove( m_pLightGlow ); + UTIL_Remove( m_pBeam); + m_pBeam = NULL; + m_bPlayingHitWall = false; + m_bPlayingHitFlesh = false; + + SetThink(NULL); + } + + BaseClass::UpdateOnRemove(); +} +#endif + //------------------------------------------------------------------------------ // Purpose : Draw attack beam and do damage / decals // Input : @@ -1168,7 +1233,11 @@ bool CNPC_Stalker::InnateWeaponLOSCondition( const Vector &ownerPos, const Vecto } else if (pBCC) { +#ifdef MAPBASE + if (IRelationType( pBCC ) <= D_FR) +#else if (IRelationType( pBCC ) == D_HT) +#endif { return true; } diff --git a/mp/src/game/server/hl2/npc_stalker.h b/mp/src/game/server/hl2/npc_stalker.h index dc2c08e5..206d199d 100644 --- a/mp/src/game/server/hl2/npc_stalker.h +++ b/mp/src/game/server/hl2/npc_stalker.h @@ -46,9 +46,19 @@ public: float m_bPlayingHitFlesh; CBeam* m_pBeam; CSprite* m_pLightGlow; +#ifdef MAPBASE + // This is a keyvalue now, so we have to initialize the value through somewhere that isn't Spawn() + int m_iPlayerAggression = 0; + bool m_bBleed; +#else int m_iPlayerAggression; +#endif float m_flNextScreamTime; +#ifdef MAPBASE + void UpdateOnRemove( void ); +#endif + void KillAttackBeam(void); void DrawAttackBeam(void); void CalcBeamPosition(void); @@ -97,6 +107,9 @@ public: void PainSound( const CTakeDamageInfo &info ); void Event_Killed( const CTakeDamageInfo &info ); +#ifdef MAPBASE + void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); +#endif void DoSmokeEffect( const Vector &position ); void AddZigZagToPath(void); diff --git a/mp/src/game/server/hl2/npc_strider.cpp b/mp/src/game/server/hl2/npc_strider.cpp index 60d37cdf..5be578f3 100644 --- a/mp/src/game/server/hl2/npc_strider.cpp +++ b/mp/src/game/server/hl2/npc_strider.cpp @@ -54,6 +54,10 @@ #include "filters.h" #include "saverestore_utlvector.h" #include "eventqueue.h" +#ifdef MAPBASE +#include "npc_basescanner.h" +#include "mapbase/GlobalStrings.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -328,6 +332,11 @@ BEGIN_DATADESC( CNPC_Strider ) DEFINE_FIELD( m_hCannonTarget, FIELD_EHANDLE ), DEFINE_EMBEDDED( m_AttemptCannonLOSTimer ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_strStompFilter, FIELD_STRING, "stompfilter" ), + DEFINE_FIELD( m_hStompFilter, FIELD_EHANDLE ), +#endif + DEFINE_FIELD( m_flSpeedScale, FIELD_FLOAT ), DEFINE_FIELD( m_flTargetSpeedScale, FIELD_FLOAT ), @@ -392,6 +401,10 @@ BEGIN_DATADESC( CNPC_Strider ) DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ), DEFINE_INPUTFUNC( FIELD_FLOAT, "ScaleGroundSpeed", InputScaleGroundSpeed ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetStompFilter", InputSetStompFilter ), +#endif + // Function Pointers // DEFINE_FUNCTION( JumpTouch ), DEFINE_THINKFUNC( CarriedThink ), @@ -429,6 +442,11 @@ CNPC_Strider::CNPC_Strider() //--------------------------------------------------------- CNPC_Strider::~CNPC_Strider() { +#ifdef MAPBASE + if (m_hFocus) + UTIL_Remove( m_hFocus ); +#endif + delete m_pMinigun; } @@ -1105,7 +1123,11 @@ void CNPC_Strider::GatherConditions() // Don't switch targets if shooting at a bullseye! Level designers depend on bullseyes. if( GetEnemy() && m_pMinigun->IsShooting() && GetTimeEnemyAcquired() != gpGlobals->curtime ) { +#ifdef MAPBASE + if( m_pMinigun->IsOnTarget( 3 ) && !EntIsClass( GetEnemy(), gm_isz_class_Bullseye ) ) +#else if( m_pMinigun->IsOnTarget( 3 ) && !FClassnameIs( GetEnemy(), "npc_bullseye" ) ) +#endif { if( m_iVisibleEnemies > 1 ) { @@ -1993,7 +2015,11 @@ Disposition_t CNPC_Strider::IRelationType( CBaseEntity *pTarget ) //--------------------------------------------------------- void CNPC_Strider::AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ) { +#ifdef MAPBASE + if ( nDisposition == D_HT && EntIsClass(pEntity, gm_isz_class_Bullseye) ) +#else if ( nDisposition == D_HT && pEntity->ClassMatches("npc_bullseye") ) +#endif UpdateEnemyMemory( pEntity, pEntity->GetAbsOrigin() ); BaseClass::AddEntityRelationship( pEntity, nDisposition, nPriority ); } @@ -2361,6 +2387,16 @@ void CNPC_Strider::InputScaleGroundSpeed( inputdata_t &inputdata ) m_flTargetSpeedScale = inputdata.value.Float(); } +#ifdef MAPBASE +//--------------------------------------------------------- +//--------------------------------------------------------- +void CNPC_Strider::InputSetStompFilter( inputdata_t &inputdata ) +{ + m_strStompFilter = inputdata.value.StringID(); + m_hStompFilter = NULL; +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- bool CNPC_Strider::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) @@ -2426,7 +2462,11 @@ bool CNPC_Strider::IsValidEnemy( CBaseEntity *pTarget ) //--------------------------------------------------------- bool CNPC_Strider::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer ) { +#ifdef MAPBASE + if (dynamic_cast(pInformer)) +#else if( pInformer && FClassnameIs( pInformer, "npc_cscanner" ) ) +#endif { EmitSound( "NPC_Strider.Alert" ); // Move Strider's focus to this location and make strider mad at it @@ -2557,6 +2597,14 @@ int CNPC_Strider::MeleeAttack1Conditions( float flDot, float flDist ) return COND_NONE; } +#ifdef MAPBASE + if (GetStompFilter()) + { + if (!GetStompFilter()->PassesFilter(this, pEnemy)) + return COND_NONE; + } +#endif + // recompute this because the base class function does not work for the strider flDist = StriderEnemyDistance( pEnemy ); @@ -4378,6 +4426,35 @@ void CNPC_Strider::StompHit( int followerBoneIndex ) } } +#ifdef MAPBASE +//--------------------------------------------------------- +//--------------------------------------------------------- +CBaseFilter *CNPC_Strider::GetStompFilter() +{ + if (m_hStompFilter) + return m_hStompFilter; + + if (m_strStompFilter != NULL_STRING) + { + CBaseEntity *pEntity = gEntList.FindEntityByName(NULL, m_strStompFilter); + if (pEntity) + { + m_hStompFilter = dynamic_cast(pEntity); + if (m_hStompFilter) + return m_hStompFilter; + else + Warning("%s stomp filter %s not a filter!", GetDebugName(), STRING(m_strStompFilter)); + } + else + { + Warning("%s stomp filter %s not found!", GetDebugName(), STRING(m_strStompFilter)); + } + } + + return NULL; +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CNPC_Strider::FootFX( const Vector &origin ) diff --git a/mp/src/game/server/hl2/npc_strider.h b/mp/src/game/server/hl2/npc_strider.h index dd645781..a9074599 100644 --- a/mp/src/game/server/hl2/npc_strider.h +++ b/mp/src/game/server/hl2/npc_strider.h @@ -14,6 +14,9 @@ #include "smoke_trail.h" #include "physics_bone_follower.h" #include "physics_prop_ragdoll.h" +#ifdef MAPBASE +#include "filters.h" +#endif #if defined( _WIN32 ) #pragma once @@ -172,6 +175,10 @@ public: void InputExplode( inputdata_t &inputdata ); void InputScaleGroundSpeed( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetStompFilter( inputdata_t &inputdata ); +#endif + //--------------------------------- // Combat //--------------------------------- @@ -356,6 +363,10 @@ public: Vector BackFootHit( float eventtime ); void StompHit( int followerBoneIndex ); +#ifdef MAPBASE + CBaseFilter *GetStompFilter(); +#endif + void FootFX( const Vector &origin ); Vector CalculateStompHitPosition( CBaseEntity *pEnemy ); bool IsLegBoneFollower( CBoneFollower *pFollower ); @@ -452,6 +463,11 @@ private: EHANDLE m_hCannonTarget; CSimpleSimTimer m_AttemptCannonLOSTimer; +#ifdef MAPBASE + string_t m_strStompFilter; + CHandle m_hStompFilter; +#endif + float m_flSpeedScale; float m_flTargetSpeedScale; diff --git a/mp/src/game/server/hl2/npc_turret_ceiling.cpp b/mp/src/game/server/hl2/npc_turret_ceiling.cpp index ec8b4207..bc4f6821 100644 --- a/mp/src/game/server/hl2/npc_turret_ceiling.cpp +++ b/mp/src/game/server/hl2/npc_turret_ceiling.cpp @@ -26,7 +26,11 @@ //Debug visualization ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" ); +#ifdef MAPBASE +#define CEILING_TURRET_MODEL GetTurretModel() +#else #define CEILING_TURRET_MODEL "models/combine_turrets/ceiling_turret.mdl" +#endif #define CEILING_TURRET_GLOW_SPRITE "sprites/glow1.vmt" /* // we now inherit these from the ai_basenpc baseclass #define CEILING_TURRET_BC_YAW "aim_yaw" @@ -49,6 +53,9 @@ ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" ); #define SF_CEILING_TURRET_STARTINACTIVE 0x00000040 #define SF_CEILING_TURRET_NEVERRETIRE 0x00000080 #define SF_CEILING_TURRET_OUT_OF_AMMO 0x00000100 +#ifdef MAPBASE +#define SF_CEILING_TURRET_NO_SPRITE 0x00000400 +#endif //Heights #define CEILING_TURRET_RETRACT_HEIGHT 24 @@ -110,6 +117,14 @@ public: void InputToggle( inputdata_t &inputdata ); void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDepleteAmmo( inputdata_t &inputdata ); + void InputRestoreAmmo( inputdata_t &inputdata ); + void InputCreateSprite( inputdata_t &inputdata ); + void InputDestroySprite( inputdata_t &inputdata ); + + virtual void StopLoopingSounds( void ) { StopSound(GetMoveSound()); } +#endif void SetLastSightTime(); @@ -147,8 +162,31 @@ public: } protected: + +#ifdef MAPBASE + virtual const char *GetTurretModel() { return "models/combine_turrets/ceiling_turret.mdl"; } + + virtual const char *GetRetireSound() { return "NPC_CeilingTurret.Retire"; } + virtual const char *GetDeploySound() { return "NPC_CeilingTurret.Deploy"; } + virtual const char *GetMoveSound() { return "NPC_CeilingTurret.Move"; } + virtual const char *GetActiveSound() { return "NPC_CeilingTurret.Active"; } + virtual const char *GetAlertSound() { return "NPC_CeilingTurret.Alert"; } + virtual const char *GetShootSound() { return "NPC_CeilingTurret.ShotSounds"; } + virtual const char *GetPingSound() { return "NPC_CeilingTurret.Ping"; } + virtual const char *GetDieSound() { return "NPC_CeilingTurret.Die"; } + + virtual float GetFireRate(bool bFightingPlayer = false) { return bFightingPlayer ? 0.5f : 0.1f; } + + virtual void SetIdleGoalAngles() { m_vecGoalAngles = GetAbsAngles(); } +#endif +#ifdef MAPBASE + virtual bool PreThink( turretState_e state ); + void DryFire( void ); + const char *GetTracerType( void ) { return "AR2Tracer"; } +#else bool PreThink( turretState_e state ); +#endif void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy ); void SetEyeState( eyeState_t state ); void Ping( void ); @@ -157,8 +195,14 @@ protected: void Disable( void ); void SpinUp( void ); void SpinDown( void ); +#ifdef MAPBASE + virtual +#endif void SetHeight( float height ); +#ifdef MAPBASE + virtual +#endif bool UpdateFacing( void ); int m_iAmmoType; @@ -179,7 +223,9 @@ protected: COutputEvent m_OnDeploy; COutputEvent m_OnRetire; +#ifndef MAPBASE COutputEvent m_OnTipped; +#endif DECLARE_DATADESC(); }; @@ -198,6 +244,9 @@ BEGIN_DATADESC( CNPC_CeilingTurret ) DEFINE_FIELD( m_flPingTime, FIELD_TIME ), DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ), DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ), +#ifdef MAPBASE + DEFINE_INPUT( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ), +#endif DEFINE_THINKFUNC( Retire ), DEFINE_THINKFUNC( Deploy ), @@ -210,10 +259,18 @@ BEGIN_DATADESC( CNPC_CeilingTurret ) DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "DepleteAmmo", InputDepleteAmmo ), + DEFINE_INPUTFUNC( FIELD_VOID, "RestoreAmmo", InputRestoreAmmo ), + DEFINE_INPUTFUNC( FIELD_VOID, "CreateSprite", InputCreateSprite ), + DEFINE_INPUTFUNC( FIELD_VOID, "DestroySprite", InputDestroySprite ), +#endif DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ), DEFINE_OUTPUT( m_OnRetire, "OnRetire" ), +#ifndef MAPBASE DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), +#endif END_DATADESC() @@ -258,6 +315,16 @@ void CNPC_CeilingTurret::Precache( void ) ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_FIRE ); ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_DRYFIRE ); +#ifdef MAPBASE + PrecacheScriptSound( GetRetireSound() ); + PrecacheScriptSound( GetDeploySound() ); + PrecacheScriptSound( GetMoveSound() ); + PrecacheScriptSound( GetActiveSound() ); + PrecacheScriptSound( GetAlertSound() ); + PrecacheScriptSound( GetShootSound() ); + PrecacheScriptSound( GetPingSound() ); + PrecacheScriptSound( GetDieSound() ); +#else PrecacheScriptSound( "NPC_CeilingTurret.Retire" ); PrecacheScriptSound( "NPC_CeilingTurret.Deploy" ); PrecacheScriptSound( "NPC_CeilingTurret.Move" ); @@ -266,6 +333,7 @@ void CNPC_CeilingTurret::Precache( void ) PrecacheScriptSound( "NPC_CeilingTurret.ShotSounds" ); PrecacheScriptSound( "NPC_CeilingTurret.Ping" ); PrecacheScriptSound( "NPC_CeilingTurret.Die" ); +#endif PrecacheScriptSound( "NPC_FloorTurret.DryFire" ); @@ -285,9 +353,16 @@ void CNPC_CeilingTurret::Spawn( void ) m_HackedGunPos = Vector( 0, 0, 12.75 ); SetViewOffset( EyeOffset( ACT_IDLE ) ); +#ifndef MAPBASE // We use this as a keyvalue now. m_flFieldOfView = 0.0f; +#endif m_takedamage = DAMAGE_YES; +#ifdef MAPBASE + if (m_iHealth == 0) + m_iHealth = 1000; +#else m_iHealth = 1000; +#endif m_bloodColor = BLOOD_COLOR_MECH; SetSolid( SOLID_BBOX ); @@ -304,9 +379,16 @@ void CNPC_CeilingTurret::Spawn( void ) m_iAmmoType = GetAmmoDef()->Index( "AR2" ); //Create our eye sprite +#ifdef MAPBASE + if (!HasSpawnFlags(SF_CEILING_TURRET_NO_SPRITE)) + { +#endif m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false ); m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation ); m_pEyeGlow->SetAttachment( this, 2 ); +#ifdef MAPBASE + } +#endif //Set our autostart state m_bAutoStart = !!( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE ); @@ -361,7 +443,11 @@ int CNPC_CeilingTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo ) ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false ); SetThink( &CNPC_CeilingTurret::DeathThink ); +#ifdef MAPBASE + StopSound( GetAlertSound() ); +#else StopSound( "NPC_CeilingTurret.Alert" ); +#endif m_OnDamaged.FireOutput( info.GetInflictor(), this ); @@ -397,7 +483,11 @@ void CNPC_CeilingTurret::Retire( void ) if ( UpdateFacing() == false ) { SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE ); +#ifdef MAPBASE + EmitSound( GetRetireSound() ); +#else EmitSound( "NPC_CeilingTurret.Retire" ); +#endif //Notify of the retraction m_OnRetire.FireOutput( NULL, this ); @@ -424,6 +514,10 @@ void CNPC_CeilingTurret::Retire( void ) SetEyeState( TURRET_EYE_DISABLED ); SetThink( &CNPC_CeilingTurret::SUB_DoNothing ); } + +#ifdef MAPBASE + StopLoopingSounds(); +#endif } } @@ -447,10 +541,18 @@ void CNPC_CeilingTurret::Deploy( void ) { m_bActive = true; SetActivity( (Activity) ACT_CEILING_TURRET_OPEN ); +#ifdef MAPBASE + EmitSound( GetDeploySound() ); +#else EmitSound( "NPC_CeilingTurret.Deploy" ); +#endif //Notify we're deploying +#ifdef MAPBASE + m_OnDeploy.FireOutput( GetEnemy(), this ); +#else m_OnDeploy.FireOutput( NULL, this ); +#endif } //If we're done, then start searching @@ -465,7 +567,11 @@ void CNPC_CeilingTurret::Deploy( void ) m_flPlaybackRate = 0; SetThink( &CNPC_CeilingTurret::SearchThink ); +#ifdef MAPBASE + EmitSound( GetMoveSound() ); +#else EmitSound( "NPC_CeilingTurret.Move" ); +#endif } SetLastSightTime(); @@ -569,7 +675,11 @@ bool CNPC_CeilingTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEnt // If we hit something that's okay to hit anyway, still fire if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() ) { +#ifdef MAPBASE + if (IRelationType(pHitEntity) <= D_FR) +#else if (IRelationType(pHitEntity) == D_HT) +#endif return true; } @@ -671,6 +781,20 @@ void CNPC_CeilingTurret::ActiveThink( void ) //Fire the gun if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop { +#ifdef MAPBASE + if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) + { + ResetActivity(); + SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE ); + DryFire(); + } + else + { + ResetActivity(); + SetActivity( (Activity) ACT_CEILING_TURRET_FIRE ); + Shoot( vecMuzzle, vecMuzzleDir ); + } +#else if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) { SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE ); @@ -682,6 +806,7 @@ void CNPC_CeilingTurret::ActiveThink( void ) //Fire the weapon Shoot( vecMuzzle, vecMuzzleDir ); +#endif } } else @@ -733,6 +858,9 @@ void CNPC_CeilingTurret::SearchThink( void ) //If we've found a target, spin up the barrel and start to attack if ( GetEnemy() != NULL ) { +#ifdef MAPBASE + m_flShotTime = gpGlobals->curtime + GetFireRate( GetEnemy()->IsPlayer() ); +#else //Give players a grace period if ( GetEnemy()->IsPlayer() ) { @@ -742,13 +870,18 @@ void CNPC_CeilingTurret::SearchThink( void ) { m_flShotTime = gpGlobals->curtime + 0.1f; } +#endif m_flLastSight = 0; SetThink( &CNPC_CeilingTurret::ActiveThink ); SetEyeState( TURRET_EYE_SEE_TARGET ); SpinUp(); +#ifdef MAPBASE + EmitSound( GetActiveSound() ); +#else EmitSound( "NPC_CeilingTurret.Active" ); +#endif return; } @@ -799,10 +932,76 @@ void CNPC_CeilingTurret::AutoSearchThink( void ) if ( GetEnemy() != NULL ) { SetThink( &CNPC_CeilingTurret::Deploy ); +#ifdef MAPBASE + EmitSound( GetAlertSound() ); +#else EmitSound( "NPC_CeilingTurret.Alert" ); +#endif } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// I decided to move dry firing to its own function instead of keeping it in Shoot, similar to npc_turret_floor +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::DryFire( void ) +{ + EmitSound( "NPC_FloorTurret.DryFire"); + EmitSound( GetActiveSound() ); + + if ( RandomFloat( 0, 1 ) > 0.7 ) + { + m_flShotTime = gpGlobals->curtime + random->RandomFloat( 0.5, 1.5 ); + } + else + { + m_flShotTime = gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Fire! +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy ) +{ + FireBulletsInfo_t info; + + if ( GetEnemy() != NULL ) + { + Vector vecDir = GetActualShootTrajectory( vecSrc ); + + info.m_vecSrc = vecSrc; + info.m_vecDirShooting = vecDir; + info.m_iTracerFreq = 1; + info.m_iShots = 1; + info.m_pAttacker = this; + info.m_vecSpread = VECTOR_CONE_PRECALCULATED; + info.m_flDistance = MAX_COORD_RANGE; + info.m_iAmmoType = m_iAmmoType; + } + else + { + // Just shoot where you're facing! + + info.m_vecSrc = vecSrc; + info.m_vecDirShooting = vecDirToEnemy; + info.m_iTracerFreq = 1; + info.m_iShots = 1; + info.m_pAttacker = this; + info.m_vecSpread = GetAttackSpread( NULL, NULL ); + info.m_flDistance = MAX_COORD_RANGE; + info.m_iAmmoType = m_iAmmoType; + } + + FireBullets( info ); +#ifdef MAPBASE + EmitSound( GetShootSound() ); +#else + EmitSound( "NPC_CeilingTurret.ShotSounds" ); +#endif + DoMuzzleFlash(); +} +#else //----------------------------------------------------------------------------- // Purpose: Fire! //----------------------------------------------------------------------------- @@ -859,6 +1058,7 @@ void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnem EmitSound( "NPC_CeilingTurret.ShotSounds" ); DoMuzzleFlash(); } +#endif //----------------------------------------------------------------------------- // Purpose: Allows a generic think function before the others are called @@ -946,7 +1146,11 @@ void CNPC_CeilingTurret::Ping( void ) return; //Ping! +#ifdef MAPBASE + EmitSound( GetPingSound() ); +#else EmitSound( "NPC_CeilingTurret.Ping" ); +#endif SetEyeState( TURRET_EYE_SEEKING_TARGET ); @@ -1023,6 +1227,52 @@ void CNPC_CeilingTurret::InputDisable( inputdata_t &inputdata ) Disable(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Stops the turret from firing live rounds (still attempts to though) +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputDepleteAmmo( inputdata_t &inputdata ) +{ + AddSpawnFlags( SF_CEILING_TURRET_OUT_OF_AMMO ); +} + +//----------------------------------------------------------------------------- +// Purpose: Allows the turret to fire live rounds again +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputRestoreAmmo( inputdata_t &inputdata ) +{ + RemoveSpawnFlags( SF_CEILING_TURRET_OUT_OF_AMMO ); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates the sprite if it has been destroyed +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputCreateSprite( inputdata_t &inputdata ) +{ + if (m_pEyeGlow) + return; + + m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false ); + m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation ); + m_pEyeGlow->SetAttachment( this, 2 ); + + RemoveSpawnFlags(SF_CEILING_TURRET_NO_SPRITE); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroys the sprite +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputDestroySprite( inputdata_t &inputdata ) +{ + if (!m_pEyeGlow) + return; + + UTIL_Remove(m_pEyeGlow); + m_pEyeGlow = NULL; + AddSpawnFlags(SF_CEILING_TURRET_NO_SPRITE); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1048,14 +1298,26 @@ void CNPC_CeilingTurret::DeathThink( void ) return; //Level out our angles +#ifdef MAPBASE + SetIdleGoalAngles(); +#else m_vecGoalAngles = GetAbsAngles(); +#endif SetNextThink( gpGlobals->curtime ); if ( m_lifeState != LIFE_DEAD ) { m_lifeState = LIFE_DEAD; +#ifdef MAPBASE + StopLoopingSounds(); +#endif + +#ifdef MAPBASE + EmitSound( GetDieSound() ); +#else EmitSound( "NPC_CeilingTurret.Die" ); +#endif SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE ); } @@ -1124,4 +1386,367 @@ bool CNPC_CeilingTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy ) } return BaseClass::CanBeAnEnemyOf( pEnemy ); -} \ No newline at end of file +} + + + +#ifdef MAPBASE +// +// Eli's Lab Turret +// + +//#define LAB_TURRET_MANUAL_YAW 1 + +int ACT_CEILING_TURRET_MIRROR_CLOSED_IDLE; +int ACT_CEILING_TURRET_MIRROR_OPEN; +int ACT_CEILING_TURRET_MIRROR_CLOSE; + +class CNPC_LabTurret : public CNPC_CeilingTurret +{ + DECLARE_CLASS( CNPC_LabTurret, CNPC_CeilingTurret ); +public: + CNPC_LabTurret(); + + Class_T Classify( void ) + { + if( m_bEnabled ) + return CLASS_PLAYER_ALLY; + + return CLASS_NONE; + } + + const char *GetTracerType( void ) { return "Tracer"; } + bool PreThink( turretState_e state ); + + void SetIdleGoalAngles(); + + void Precache( void ); + void Spawn(); + Activity NPC_TranslateActivity( Activity activity ); + +#ifdef LAB_TURRET_MANUAL_YAW + void SetArmYaw( float flYaw ); + void ArmYawThink(); + + void InputSetArmYaw( inputdata_t &inputdata ); +#else + inline void SetArmYaw( float flYaw ) { SetPoseParameter( m_poseMove_Yaw, flYaw ); } +#endif + + void StopLoopingSounds( void ) { StopSound(GetMoveSound()); } + +protected: + + const char *GetTurretModel() { return "models/props_lab/labturret_npc.mdl"; } + + const char *GetRetireSound() { return "NPC_LabTurret.Retire"; } + const char *GetDeploySound() { return "NPC_LabTurret.Deploy"; } + const char *GetMoveSound() { return "NPC_LabTurret.Move"; } + const char *GetActiveSound() { return "NPC_LabTurret.Active"; } + const char *GetAlertSound() { return "NPC_LabTurret.Alert"; } + const char *GetShootSound() { return "NPC_LabTurret.ShotSounds"; } + const char *GetPingSound() { return "NPC_LabTurret.Ping"; } + const char *GetDieSound() { return "NPC_LabTurret.Die"; } + + float GetFireRate(bool bFightingPlayer = false) { return 1.0f; } + + void SetHeight( float height ); + + bool UpdateFacing( void ); + + bool m_bManualArmYaw; + +#ifdef LAB_TURRET_MANUAL_YAW + // The arm must reset each retire and be restored each deploy + float m_flStoredArmYaw; + float m_flGoalArmYaw; +#endif + + bool m_bMirrored; + + DECLARE_DATADESC(); +}; + +BEGIN_DATADESC( CNPC_LabTurret ) + + DEFINE_KEYFIELD( m_bManualArmYaw, FIELD_BOOLEAN, "ManualArmYaw" ), +#ifdef LAB_TURRET_MANUAL_YAW + DEFINE_KEYFIELD( m_flStoredArmYaw, FIELD_FLOAT, "ArmYaw" ), + DEFINE_FIELD( m_flGoalArmYaw, FIELD_FLOAT ), +#endif + DEFINE_KEYFIELD( m_bMirrored, FIELD_BOOLEAN, "Mirrored" ), + +#ifdef LAB_TURRET_MANUAL_YAW + DEFINE_THINKFUNC( ArmYawThink ), +#endif + + // Inputs +#ifdef LAB_TURRET_MANUAL_YAW + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetArmYaw", InputSetArmYaw ), +#endif + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( npc_turret_lab, CNPC_LabTurret ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNPC_LabTurret::CNPC_LabTurret() +{ + // Lab turrets can rotate much more, so they can see from more angles than regular turrets. + m_flFieldOfView = -0.5; +} + +//----------------------------------------------------------------------------- +// Purpose: Precache +//----------------------------------------------------------------------------- +void CNPC_LabTurret::Precache( void ) +{ + BaseClass::Precache(); + + // Activities + ADD_CUSTOM_ACTIVITY( CNPC_LabTurret, ACT_CEILING_TURRET_MIRROR_CLOSED_IDLE ); + ADD_CUSTOM_ACTIVITY( CNPC_LabTurret, ACT_CEILING_TURRET_MIRROR_OPEN ); + ADD_CUSTOM_ACTIVITY( CNPC_LabTurret, ACT_CEILING_TURRET_MIRROR_CLOSE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_LabTurret::Spawn( void ) +{ + BaseClass::Spawn(); + + m_iAmmoType = GetAmmoDef()->Index( "SMG1" ); + + if (m_bMirrored) + SetActivity((Activity)ACT_CEILING_TURRET_MIRROR_CLOSED_IDLE); + + SetPoseParameter( m_poseMove_Yaw, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Override base class activiites +//----------------------------------------------------------------------------- +Activity CNPC_LabTurret::NPC_TranslateActivity( Activity activity ) +{ + if (m_bMirrored) + { + if (activity == ACT_CEILING_TURRET_CLOSED_IDLE) + return (Activity)ACT_CEILING_TURRET_MIRROR_CLOSED_IDLE; + if (activity == ACT_CEILING_TURRET_OPEN) + return (Activity)ACT_CEILING_TURRET_MIRROR_OPEN; + if (activity == ACT_CEILING_TURRET_CLOSE) + return (Activity)ACT_CEILING_TURRET_MIRROR_CLOSE; + } + + return BaseClass::NPC_TranslateActivity(activity); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_LabTurret::PreThink( turretState_e state ) +{ + if (state == TURRET_RETIRING || state == TURRET_DEAD) + { +#ifdef LAB_TURRET_MANUAL_YAW + if (m_bManualArmYaw) + m_flStoredArmYaw = GetPoseParameter(m_poseMove_Yaw); +#endif + + SetArmYaw( 0 ); + } +#ifdef LAB_TURRET_MANUAL_YAW + else if (state == TURRET_DEPLOYING) + { + if (m_bManualArmYaw) + SetArmYaw( m_flStoredArmYaw ); + } +#endif + + return BaseClass::PreThink(state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_LabTurret::SetIdleGoalAngles( void ) +{ + m_vecGoalAngles = GetAbsAngles(); + + if (m_bMirrored) + { + //m_vecGoalAngles.x = AngleNormalize( m_vecGoalAngles.x + 90 ); + m_vecGoalAngles.y = AngleNormalize( m_vecGoalAngles.y + 270 ); + } + else + { + //m_vecGoalAngles.x = AngleNormalize( m_vecGoalAngles.x + 270 ); + m_vecGoalAngles.y = AngleNormalize( m_vecGoalAngles.y + 90 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : height - +//----------------------------------------------------------------------------- +void CNPC_LabTurret::SetHeight( float height ) +{ + Vector forward, right, up; + AngleVectors( GetLocalAngles(), &forward, &right, &up ); + + height /= 2; + + Vector mins = ( forward * -8.0f ) + ( right * -height ) + ( up * -8.0f ); + Vector maxs = ( forward * height ) + ( right * height ) + ( up * CEILING_TURRET_RETRACT_HEIGHT ); + + if ( mins.x > maxs.x ) + { + V_swap( mins.x, maxs.x ); + } + + if ( mins.y > maxs.y ) + { + V_swap( mins.y, maxs.y ); + } + + if ( mins.z > maxs.z ) + { + V_swap( mins.z, maxs.z ); + } + + SetCollisionBounds( mins, maxs ); +} + +//----------------------------------------------------------------------------- +// Purpose: Causes the turret to face its desired angles +//----------------------------------------------------------------------------- +bool CNPC_LabTurret::UpdateFacing( void ) +{ + bool bMoved = false; + + matrix3x4_t localToWorld; + + GetAttachment( LookupAttachment( "eyes" ), localToWorld ); + + Vector vecGoalDir; + AngleVectors( m_vecGoalAngles, &vecGoalDir ); + + Vector vecGoalLocalDir; + VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir ); + + if ( g_debug_turret_ceiling.GetBool() ) + { + Vector vecMuzzle, vecMuzzleDir; + QAngle vecMuzzleAng; + + GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); + AngleVectors( vecMuzzleAng, &vecMuzzleDir ); + + NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); + NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); + NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 ); + + NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); + NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); + NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 ); + } + + QAngle vecGoalLocalAngles; + VectorAngles( vecGoalLocalDir, vecGoalLocalAngles ); + +#ifdef LAB_TURRET_MANUAL_YAW + if ( !m_bManualArmYaw && m_flGoalArmYaw == 0.0f ) +#else + if ( !m_bManualArmYaw ) +#endif + { + // Update yaw + float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) ); + + SetPoseParameter( m_poseMove_Yaw, GetPoseParameter( m_poseMove_Yaw ) + ( flDiff / 1.5f ) * 0.5f ); + + // Recalculate muzzle + GetAttachment( LookupAttachment( "eyes" ), localToWorld ); + VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir ); + VectorAngles( vecGoalLocalDir, vecGoalLocalAngles ); + + if ( fabs( flDiff ) > 0.1f ) + { + bMoved = true; + } + } + + // Update pitch + // Pitch is faster than the others, but it also kind of jiggles when targetting. + float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, (GetActivity() != ACT_CEILING_TURRET_CLOSE ? 0.15f : 0.1f) * MaxYawSpeed() ) ); + + SetPoseParameter( m_poseAim_Pitch, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff * 2.0f ) ); + + if ( fabs( flDiff ) > 0.1f ) + { + bMoved = true; + } + + // Update yaw + flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) ); + + SetPoseParameter( m_poseAim_Yaw, GetPoseParameter( m_poseAim_Yaw ) + ( flDiff / 1.5f ) ); + + if ( fabs( flDiff ) > 0.1f ) + { + bMoved = true; + } + + InvalidateBoneCache(); + + return bMoved; +} + +#ifdef LAB_TURRET_MANUAL_YAW +//----------------------------------------------------------------------------- +// Purpose: Change move yaw +//----------------------------------------------------------------------------- +void CNPC_LabTurret::SetArmYaw( float flYaw ) +{ + m_flGoalArmYaw = flYaw; + + SetContextThink(&CNPC_LabTurret::ArmYawThink, gpGlobals->curtime, "SetArmYaw"); +} + +//----------------------------------------------------------------------------- +// Purpose: Change move yaw +//----------------------------------------------------------------------------- +void CNPC_LabTurret::ArmYawThink() +{ + //GetBoneTransform( LookupBone( "eyes" ), localToWorld ); + + // Update yaw + float flDiff = AngleNormalize( UTIL_ApproachAngle( m_flGoalArmYaw, GetPoseParameter( m_poseMove_Yaw ), 0.05f * MaxYawSpeed() ) ); + + SetPoseParameter( m_poseMove_Yaw, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff / 1.5f ) ); + + InvalidateBoneCache(); + + if ( fabs( flDiff ) > 0.1f ) + { + SetNextThink(gpGlobals->curtime, "SetArmYaw"); + } + else + { + m_flGoalArmYaw = 0.0f; + SetContextThink(NULL, gpGlobals->curtime, "SetArmYaw"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Change move yaw +//----------------------------------------------------------------------------- +void CNPC_LabTurret::InputSetArmYaw( inputdata_t &inputdata ) +{ + SetArmYaw( inputdata.value.Float() ); +} +#endif +#endif diff --git a/mp/src/game/server/hl2/npc_turret_floor.cpp b/mp/src/game/server/hl2/npc_turret_floor.cpp index d934e72d..8698e742 100644 --- a/mp/src/game/server/hl2/npc_turret_floor.cpp +++ b/mp/src/game/server/hl2/npc_turret_floor.cpp @@ -40,6 +40,15 @@ ConVar g_debug_turret( "g_debug_turret", "0" ); extern ConVar physcannon_tracelength; +#if defined(MAPBASE) && defined(HL2_EPISODIC) +extern ConVar npc_alyx_interact_turrets; +#endif + +#ifdef MAPBASE +// m_iKeySkin has been replaced with the original m_nSkin so we can make it show up in Hammer, etc. +#define m_iKeySkin m_nSkin +#endif + // Interactions int g_interactionTurretStillStanding = 0; @@ -132,6 +141,10 @@ BEGIN_DATADESC( CNPC_FloorTurret ) DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), DEFINE_INPUTFUNC( FIELD_VOID, "DepleteAmmo", InputDepleteAmmo ), DEFINE_INPUTFUNC( FIELD_VOID, "RestoreAmmo", InputRestoreAmmo ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "CreateSprite", InputCreateSprite ), + DEFINE_INPUTFUNC( FIELD_VOID, "DestroySprite", InputDestroySprite ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestruct", InputSelfDestruct ), DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ), @@ -288,10 +301,12 @@ void CNPC_FloorTurret::Spawn( void ) // add one mod 4 nextSkin = (nextSkin + 1) & 0x03; } +#ifndef MAPBASE else { // at least make sure that it's in the right range m_nSkin = clamp(m_iKeySkin,1,4); } +#endif } BaseClass::Spawn(); @@ -1545,7 +1560,11 @@ bool CNPC_FloorTurret::PreThink( turretState_e state ) void CNPC_FloorTurret::SetEyeState( eyeState_t state ) { // Must have a valid eye to affect +#ifdef MAPBASE + if ( !m_hEyeGlow && !HasSpawnFlags(SF_FLOOR_TURRET_NO_SPRITE) ) +#else if ( !m_hEyeGlow ) +#endif { // Create our eye sprite m_hEyeGlow = CSprite::SpriteCreate( FLOOR_TURRET_GLOW_SPRITE, GetLocalOrigin(), false ); @@ -1775,6 +1794,39 @@ void CNPC_FloorTurret::InputRestoreAmmo( inputdata_t &inputdata ) RemoveSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Creates the sprite if it has been destroyed +//----------------------------------------------------------------------------- +void CNPC_FloorTurret::InputCreateSprite( inputdata_t &inputdata ) +{ + if (m_hEyeGlow) + return; + + m_hEyeGlow = CSprite::SpriteCreate( FLOOR_TURRET_GLOW_SPRITE, GetLocalOrigin(), false ); + if ( !m_hEyeGlow ) + return; + + m_hEyeGlow->SetTransparency( kRenderWorldGlow, 255, 0, 0, 128, kRenderFxNoDissipation ); + m_hEyeGlow->SetAttachment( this, m_iEyeAttachment ); + + RemoveSpawnFlags(SF_FLOOR_TURRET_NO_SPRITE); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroys the sprite +//----------------------------------------------------------------------------- +void CNPC_FloorTurret::InputDestroySprite( inputdata_t &inputdata ) +{ + if (!m_hEyeGlow) + return; + + UTIL_Remove(m_hEyeGlow); + m_hEyeGlow = NULL; + AddSpawnFlags(SF_FLOOR_TURRET_NO_SPRITE); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Allow players and npc's to turn the turret on and off //----------------------------------------------------------------------------- @@ -1994,6 +2046,20 @@ int CNPC_FloorTurret::DrawDebugTextOverlays( void ) return text_offset; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Option to restore Alyx's interactions with non-rollermines +//----------------------------------------------------------------------------- +bool CNPC_FloorTurret::CanInteractWith( CAI_BaseNPC *pUser ) +{ +#ifdef HL2_EPISODIC + return npc_alyx_interact_turrets.GetBool(); +#else + return false; +#endif +} +#endif + void CNPC_FloorTurret::UpdateMuzzleMatrix() { if ( gpGlobals->tickcount != m_muzzleToWorldTick ) diff --git a/mp/src/game/server/hl2/npc_turret_floor.h b/mp/src/game/server/hl2/npc_turret_floor.h index 53c54b0c..8600e13a 100644 --- a/mp/src/game/server/hl2/npc_turret_floor.h +++ b/mp/src/game/server/hl2/npc_turret_floor.h @@ -43,6 +43,9 @@ enum eyeState_t #define SF_FLOOR_TURRET_FASTRETIRE 0x00000080 #define SF_FLOOR_TURRET_OUT_OF_AMMO 0x00000100 #define SF_FLOOR_TURRET_CITIZEN 0x00000200 // Citizen modified turret +#ifdef MAPBASE +#define SF_FLOOR_TURRET_NO_SPRITE 0x00000400 +#endif class CTurretTipController; class CBeam; @@ -131,6 +134,11 @@ public: void InputDepleteAmmo( inputdata_t &inputdata ); void InputRestoreAmmo( inputdata_t &inputdata ); void InputSelfDestruct( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputCreateSprite( inputdata_t &inputdata ); + void InputDestroySprite( inputdata_t &inputdata ); + void InputPowerdown( inputdata_t &inputdata ) { InputSelfDestruct(inputdata); } +#endif virtual bool IsValidEnemy( CBaseEntity *pEnemy ); bool CanBeAnEnemyOf( CBaseEntity *pEnemy ); @@ -168,13 +176,20 @@ public: int DrawDebugTextOverlays( void ); // INPCInteractive Functions +#ifdef MAPBASE + virtual bool CanInteractWith( CAI_BaseNPC *pUser ); +#else virtual bool CanInteractWith( CAI_BaseNPC *pUser ) { return false; } // Disabled for now (sjb) +#endif virtual bool HasBeenInteractedWith() { return m_bHackedByAlyx; } virtual void NotifyInteraction( CAI_BaseNPC *pUser ) { // For now, turn green so we can tell who is hacked. SetRenderColor( 0, 255, 0 ); m_bHackedByAlyx = true; +#ifdef MAPBASE + m_OnHacked.FireOutput(pUser, this); +#endif } static float fMaxTipControllerVelocity; @@ -220,7 +235,9 @@ protected: bool m_bCarriedByPlayer; bool m_bUseCarryAngles; float m_flPlayerDropTime; +#ifndef MAPBASE // Replaced with m_nSkin. int m_iKeySkin; +#endif CHandle m_hLastNPCToKickMe; // Stores the last NPC who tried to knock me over float m_flKnockOverFailedTime; // Time at which we should tell the NPC that he failed to knock me over diff --git a/mp/src/game/server/hl2/npc_turret_ground.cpp b/mp/src/game/server/hl2/npc_turret_ground.cpp index bacaac0a..8ffd37df 100644 --- a/mp/src/game/server/hl2/npc_turret_ground.cpp +++ b/mp/src/game/server/hl2/npc_turret_ground.cpp @@ -130,8 +130,10 @@ void CNPC_GroundTurret::Spawn( void ) if( !GetParent() ) { DevMsg("ERROR! npc_ground_turret with no parent!\n"); +#ifndef MAPBASE UTIL_Remove(this); return; +#endif } m_flTimeNextShoot = gpGlobals->curtime; diff --git a/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index 3337a65b..8dffd6db 100644 --- a/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -581,6 +581,28 @@ bool CNPC_Vortigaunt::InnateWeaponLOSCondition( const Vector &ownerPos, const Ve UTIL_PredictedPosition( this, flTimeDelta, &vecNewOwnerPos ); UTIL_PredictedPosition( GetEnemy(), flTimeDelta, &vecNewTargetPos ); +#ifdef MAPBASE + // This fix was created by DKY. + // His original comment is below. + + /* + + Fix for LOS test failures when the Vort is attempting to find a viable + shoot position-- Valve's "predict where we'll be in a moment" hack (as + described above) completely breaks SCHED_ESTABLISH_LINE_OF_FIRE because it + causes all LOS checks to fail for every node if the Vort's current position + is not a valid shooting position. + - Thank you, dky.tehkingd.u! + + */ + + // Determine the Vort's predicted position delta + Vector ownerDelta = vecNewOwnerPos - GetAbsOrigin(); + + // Offset our requested LOS check location by the predicted delta. + vecNewOwnerPos = ownerPos + ownerDelta; +#endif + Vector vecDelta = vecNewTargetPos - GetEnemy()->GetAbsOrigin(); Vector vecFinalTargetPos = GetEnemy()->BodyTarget( vecNewOwnerPos ) + vecDelta; @@ -2776,7 +2798,12 @@ bool CNPC_Vortigaunt::CanFlinch( void ) if ( IsCurSchedule( SCHED_VORTIGAUNT_DISPEL_ANTLIONS ) || IsCurSchedule( SCHED_RANGE_ATTACK1 ) ) return false; +#ifdef MAPBASE + // This skips CAI_PlayerAlly's CanFlinch() function since Episodic vorts can flinch to begin with. + return CAI_BaseActor::CanFlinch(); +#else return BaseClass::CanFlinch(); +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/npc_zombie.cpp b/mp/src/game/server/hl2/npc_zombie.cpp index 6762c45c..47e8923b 100644 --- a/mp/src/game/server/hl2/npc_zombie.cpp +++ b/mp/src/game/server/hl2/npc_zombie.cpp @@ -17,6 +17,10 @@ #include "soundenvelope.h" #include "engine/IEngineSound.h" #include "ammodef.h" +#ifdef MAPBASE +#include "AI_ResponseSystem.h" +#include "ai_speech.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -75,6 +79,18 @@ envelopePoint_t envZombieMoanIgnited[] = }, }; +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Move these to CNPC_BaseZombie if other zombies end up using the response system +//------------------------------------------------------------------------------ +#define TLK_ZOMBIE_PAIN "TLK_WOUND" +#define TLK_ZOMBIE_DEATH "TLK_DEATH" +#define TLK_ZOMBIE_ALERT "TLK_STARTCOMBAT" +#define TLK_ZOMBIE_IDLE "TLK_QUESTION" +#define TLK_ZOMBIE_ATTACK "TLK_MELEE" +#define TLK_ZOMBIE_MOAN "TLK_MOAN" +#endif + //============================================================================= //============================================================================= @@ -263,6 +279,17 @@ void CZombie::Spawn( void ) { Precache(); +#ifdef MAPBASE + if( Q_strstr( GetClassname(), "torso" ) ) + { + // This was placed as an npc_zombie_torso + m_fIsTorso = true; + } + else + { + m_fIsTorso = false; + } +#else if( FClassnameIs( this, "npc_zombie" ) ) { m_fIsTorso = false; @@ -274,6 +301,7 @@ void CZombie::Spawn( void ) } m_fIsHeadless = false; +#endif #ifdef HL2_EPISODIC SetBloodColor( BLOOD_COLOR_ZOMBIE ); @@ -999,3 +1027,266 @@ AI_BEGIN_CUSTOM_NPC( npc_zombie, CZombie ) AI_END_CUSTOM_NPC() //============================================================================= + +#ifdef MAPBASE +class CZombieCustom : public CAI_ExpresserHost +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CZombieCustom, CAI_ExpresserHost ); + +public: + CZombieCustom(); + + void Spawn( void ); + void Precache( void ); + + void SpeakIfAllowed( const char *concept, AI_CriteriaSet *modifiers = NULL ); + void ModifyOrAppendCriteria( AI_CriteriaSet& set ); + virtual CAI_Expresser *CreateExpresser( void ); + virtual CAI_Expresser *GetExpresser() { return m_pExpresser; } + virtual void PostConstructor( const char *szClassname ); + + void PainSound( const CTakeDamageInfo &info ); + void DeathSound( const CTakeDamageInfo &info ); + void AlertSound( void ); + void IdleSound( void ); + void AttackSound( void ); + + const char *GetMoanSound( int nSound ); + + void SetZombieModel( void ); + + virtual const char *GetLegsModel( void ) { return STRING(m_iszLegsModel); } + virtual const char *GetTorsoModel( void ) { return STRING(m_iszTorsoModel); } + virtual const char *GetHeadcrabClassname( void ) { return STRING(m_iszHeadcrabClassname); } + virtual const char *GetHeadcrabModel( void ) { return STRING(m_iszHeadcrabModel); } + + string_t m_iszLegsModel; + string_t m_iszTorsoModel; + string_t m_iszHeadcrabClassname; + string_t m_iszHeadcrabModel; + + CAI_Expresser *m_pExpresser; +}; + +BEGIN_DATADESC( CZombieCustom ) + + DEFINE_KEYFIELD( m_iszLegsModel, FIELD_STRING, "LegsModel" ), + DEFINE_KEYFIELD( m_iszTorsoModel, FIELD_STRING, "TorsoModel" ), + DEFINE_KEYFIELD( m_iszHeadcrabClassname, FIELD_STRING, "HeadcrabClassname" ), + DEFINE_KEYFIELD( m_iszHeadcrabModel, FIELD_STRING, "HeadcrabModel" ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( npc_zombie_custom, CZombieCustom ); +LINK_ENTITY_TO_CLASS( npc_zombie_custom_torso, CZombieCustom ); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CZombieCustom::CZombieCustom() +{ + m_iszLegsModel = AllocPooledString( CZombie::GetLegsModel() ); + m_iszTorsoModel = AllocPooledString( CZombie::GetTorsoModel() ); + m_iszHeadcrabClassname = AllocPooledString( CZombie::GetHeadcrabClassname() ); + m_iszHeadcrabModel = AllocPooledString( CZombie::GetHeadcrabModel() ); + + SetModelName( AllocPooledString("models/zombie/classic.mdl") ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::Spawn( void ) +{ + int iHealth = m_iHealth; + + BaseClass::Spawn(); + + if (iHealth > 0) + { + m_iMaxHealth = iHealth; + m_iHealth = iHealth; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheModel(STRING(GetModelName())); + + if (m_iszLegsModel != NULL_STRING) + PrecacheModel( STRING(m_iszLegsModel) ); + + if (m_iszTorsoModel != NULL_STRING) + PrecacheModel( STRING(m_iszTorsoModel) ); + + if (m_iszHeadcrabClassname != NULL_STRING) + UTIL_PrecacheOther( STRING(m_iszHeadcrabClassname) ); + + if (m_iszHeadcrabModel != NULL_STRING) + PrecacheModel( STRING(m_iszHeadcrabModel) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::SetZombieModel( void ) +{ + Hull_t lastHull = GetHullType(); + + if ( m_fIsTorso ) + { + SetModel( GetTorsoModel() ); + SetHullType( HULL_TINY ); + } + else + { + SetModel( STRING(GetModelName()) ); + SetHullType( HULL_HUMAN ); + } + + SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless ); + + SetHullSizeNormal( true ); + SetDefaultEyeOffset(); + SetActivity( ACT_IDLE ); + + // hull changed size, notify vphysics + // UNDONE: Solve this generally, systematically so other + // NPCs can change size + if ( lastHull != GetHullType() ) + { + if ( VPhysicsGetObject() ) + { + SetupVPhysicsHull(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::PainSound( const CTakeDamageInfo &info ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendDamageCriteria( modifiers, info ); + SpeakIfAllowed( TLK_ZOMBIE_PAIN, &modifiers ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::DeathSound( const CTakeDamageInfo &info ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendDamageCriteria( modifiers, info ); + SpeakIfAllowed( TLK_ZOMBIE_DEATH, &modifiers ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::AlertSound( void ) +{ + SpeakIfAllowed( TLK_ZOMBIE_ALERT ); + + // Don't let a moan sound cut off the alert sound. + m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a moan sound for this class of zombie. +//----------------------------------------------------------------------------- +const char *CZombieCustom::GetMoanSound( int nSound ) +{ + AI_CriteriaSet modifiers; + + // We could probably do this through the response system alone now, but whatever. + modifiers.AppendCriteria( "moansound", UTIL_VarArgs("%i", nSound & 4) ); + + AI_Response response; + if ( !SpeakFindResponse(response, TLK_ZOMBIE_MOAN, modifiers) ) + return "NPC_BaseZombie.Moan1"; + + // Must be static so it could be returned + static char szSound[128]; + Q_strncpy( szSound, response.GetResponsePtr(), AI_Response::MAX_RESPONSE_NAME ); + + return szSound; +} + +//----------------------------------------------------------------------------- +// Purpose: Play a random idle sound. +//----------------------------------------------------------------------------- +void CZombieCustom::IdleSound( void ) +{ + if( GetState() == NPC_STATE_IDLE && random->RandomFloat( 0, 1 ) == 0 ) + { + // Moan infrequently in IDLE state. + return; + } + + SpeakIfAllowed( TLK_ZOMBIE_IDLE ); + MakeAISpookySound( 360.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Play a random attack sound. +//----------------------------------------------------------------------------- +void CZombieCustom::AttackSound( void ) +{ + SpeakIfAllowed( TLK_ZOMBIE_ATTACK ); +} + +//----------------------------------------------------------------------------- +// Purpose: Speak concept +//----------------------------------------------------------------------------- +void CZombieCustom::SpeakIfAllowed(const char *concept, AI_CriteriaSet *modifiers) +{ + Speak( concept, modifiers ? *modifiers : AI_CriteriaSet() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CZombieCustom::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + + set.AppendCriteria( "slumped", IsSlumped() ? "1" : "0" ); + + // Does this or a name already exist? + set.AppendCriteria( "onfire", IsOnFire() ? "1" : "0" ); + + // Custom zombies (and zombie torsos) must make zombie sounds. + // This can be overridden with response contexts. + set.AppendCriteria( "classname", "npc_zombie" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAI_Expresser *CZombieCustom::CreateExpresser( void ) +{ + m_pExpresser = new CAI_Expresser(this); + if (!m_pExpresser) + return NULL; + + m_pExpresser->Connect(this); + return m_pExpresser; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CZombieCustom::PostConstructor(const char *szClassname) +{ + BaseClass::PostConstructor(szClassname); + CreateExpresser(); +} +#endif diff --git a/mp/src/game/server/hl2/npc_zombine.cpp b/mp/src/game/server/hl2/npc_zombine.cpp index 479d220b..c25104d2 100644 --- a/mp/src/game/server/hl2/npc_zombine.cpp +++ b/mp/src/game/server/hl2/npc_zombine.cpp @@ -167,8 +167,15 @@ private: float m_flSuperFastAttackTime; float m_flGrenadePullTime; +#ifdef MAPBASE + int m_iGrenadeCount = ZOMBINE_MAX_GRENADES; +#else int m_iGrenadeCount; +#endif +#ifdef MAPBASE + COutputEHANDLE m_OnGrenade; +#endif EHANDLE m_hGrenade; protected: @@ -184,7 +191,12 @@ BEGIN_DATADESC( CNPC_Zombine ) DEFINE_FIELD( m_flSuperFastAttackTime, FIELD_TIME ), DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ), DEFINE_FIELD( m_flGrenadePullTime, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iGrenadeCount, FIELD_INTEGER, "NumGrenades" ), + DEFINE_OUTPUT( m_OnGrenade, "OnPullGrenade" ), +#else DEFINE_FIELD( m_iGrenadeCount, FIELD_INTEGER ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "StartSprint", InputStartSprint ), DEFINE_INPUTFUNC( FIELD_VOID, "PullGrenade", InputPullGrenade ), END_DATADESC() @@ -201,7 +213,9 @@ void CNPC_Zombine::Spawn( void ) Precache(); m_fIsTorso = false; +#ifndef MAPBASE // Controlled by KV m_fIsHeadless = false; +#endif #ifdef HL2_EPISODIC SetBloodColor( BLOOD_COLOR_ZOMBIE ); @@ -226,7 +240,9 @@ void CNPC_Zombine::Spawn( void ) g_flZombineGrenadeTimes = gpGlobals->curtime; m_flGrenadePullTime = gpGlobals->curtime; +#ifndef MAPBASE m_iGrenadeCount = ZOMBINE_MAX_GRENADES; +#endif } void CNPC_Zombine::Precache( void ) @@ -560,6 +576,9 @@ void CNPC_Zombine::HandleAnimEvent( animevent_t *pEvent ) pGrenade->SetParent( this, iAttachment ); pGrenade->SetDamage( 200.0f ); +#ifdef MAPBASE + m_OnGrenade.Set(pGrenade, pGrenade, this); +#endif m_hGrenade = pGrenade; EmitSound( "Zombine.ReadyGrenade" ); diff --git a/mp/src/game/server/hl2/prop_combine_ball.cpp b/mp/src/game/server/hl2/prop_combine_ball.cpp index b9370349..7a6f287b 100644 --- a/mp/src/game/server/hl2/prop_combine_ball.cpp +++ b/mp/src/game/server/hl2/prop_combine_ball.cpp @@ -227,6 +227,10 @@ BEGIN_DATADESC( CPropCombineBall ) DEFINE_INPUTFUNC( FIELD_VOID, "FadeAndRespawn", InputFadeAndRespawn ), DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ), DEFINE_INPUTFUNC( FIELD_VOID, "Socketed", InputSocketed ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLifetime", InputSetLifetime ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "AddLifetime", InputAddLifetime ), +#endif END_DATADESC() @@ -566,6 +570,10 @@ void CPropCombineBall::InputKill( inputdata_t &inputdata ) SetOwnerEntity( NULL ); } +#ifdef MAPBASE + m_OnKilled.FireOutput( inputdata.pActivator, this ); +#endif + UTIL_Remove( this ); NotifySpawnerOfRemoval(); @@ -596,6 +604,86 @@ void CPropCombineBall::InputSocketed( inputdata_t &inputdata ) NotifySpawnerOfRemoval(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropCombineBall::InputSetLifetime( inputdata_t &inputdata ) +{ + if (m_bHeld) + { + // Special handling when held + float dt = inputdata.value.Float(); + float flSoundRampTime = GetBallHoldDissolveTime() - GetBallHoldSoundRampTime(); + + if (dt > flSoundRampTime) + { + if ( m_pHoldingSound ) + { + // Reset holding sound to regular pitch + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + controller.SoundChangePitch( m_pHoldingSound, 100, flSoundRampTime ); + } + + SetContextThink( &CPropCombineBall::DissolveRampSoundThink, gpGlobals->curtime + dt - flSoundRampTime, s_pHoldDissolveContext ); + } + else + { + if ( m_pHoldingSound ) + { + // Do pitch ramp based on our custom time, which is less than the normal pitch ramp time + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + controller.SoundChangePitch( m_pHoldingSound, 150, dt ); + } + SetContextThink( &CPropCombineBall::DissolveThink, gpGlobals->curtime + dt, s_pHoldDissolveContext ); + } + } + else + { + SetContextThink( &CPropCombineBall::ExplodeThink, gpGlobals->curtime + inputdata.value.Float(), s_pExplodeTimerContext ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropCombineBall::InputAddLifetime( inputdata_t &inputdata ) +{ + if (m_bHeld) + { + // Special handling when held + float dt = (GetNextThink( s_pHoldDissolveContext ) - gpGlobals->curtime + inputdata.value.Float()); + float flSoundRampTime = GetBallHoldDissolveTime() - GetBallHoldSoundRampTime(); + + if (dt > flSoundRampTime) + { + if ( m_pHoldingSound ) + { + // Reset holding sound to regular pitch + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + controller.SoundChangePitch( m_pHoldingSound, 100, flSoundRampTime ); + } + + SetContextThink( &CPropCombineBall::DissolveRampSoundThink, gpGlobals->curtime + dt - flSoundRampTime, s_pHoldDissolveContext ); + } + else + { + if ( m_pHoldingSound ) + { + // Do pitch ramp based on our custom time, which is less than the normal pitch ramp time + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + controller.SoundChangePitch( m_pHoldingSound, 150, dt ); + } + SetContextThink( &CPropCombineBall::DissolveThink, gpGlobals->curtime + dt, s_pHoldDissolveContext ); + } + } + else + { + SetContextThink( &CPropCombineBall::ExplodeThink, gpGlobals->curtime + (GetNextThink( s_pExplodeTimerContext ) + inputdata.value.Float()), s_pExplodeTimerContext ); + } +} +#endif + //----------------------------------------------------------------------------- // Cleanup. //----------------------------------------------------------------------------- @@ -706,9 +794,63 @@ void CPropCombineBall::WhizSoundThink() pPhysicsObject->GetPosition( &vecPosition, NULL ); pPhysicsObject->GetVelocity( &vecVelocity, NULL ); - if ( gpGlobals->maxClients == 1 ) + // Multiplayer equivelent, loops through players and decides if it should go or not, like SP. + if ( gpGlobals->maxClients > 1 ) + { + CBasePlayer *pPlayer = NULL; + + for (int i = 1;i <= gpGlobals->maxClients; i++) + { + pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer ) + { + Vector vecDelta; + VectorSubtract( pPlayer->GetAbsOrigin(), vecPosition, vecDelta ); + VectorNormalize( vecDelta ); + if ( DotProduct( vecDelta, vecVelocity ) > 0.5f ) + { + Vector vecEndPoint; + VectorMA( vecPosition, 2.0f * TICK_INTERVAL, vecVelocity, vecEndPoint ); + float flDist = CalcDistanceToLineSegment( pPlayer->GetAbsOrigin(), vecPosition, vecEndPoint ); + if ( flDist < 200.0f ) + { + // We're basically doing what CPASAttenuationFilter does, on a per-user basis, if it passes we create the filter and send off the sound + // if it doesn't, we skip the player. + float distance, maxAudible; + Vector vecRelative; + + VectorSubtract( pPlayer->EarPosition(), vecPosition, vecRelative ); + distance = VectorLength( vecRelative ); + maxAudible = ( 2 * SOUND_NORMAL_CLIP_DIST ) / ATTN_NORM; + if ( distance <= maxAudible ) + continue; + + // Set the recipient to the player it checked against so multiple sounds don't play. + CSingleUserRecipientFilter filter( pPlayer ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_STATIC; + if ( hl2_episodic.GetBool() ) + { + ep.m_pSoundName = "NPC_CombineBall_Episodic.WhizFlyby"; + } + else + { + ep.m_pSoundName = "NPC_CombineBall.WhizFlyby"; + } + ep.m_flVolume = 1.0f; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + } + } + } + } + } + else { CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer ) { Vector vecDelta; @@ -743,6 +885,7 @@ void CPropCombineBall::WhizSoundThink() } } } + } SetContextThink( &CPropCombineBall::WhizSoundThink, gpGlobals->curtime + 2.0f * TICK_INTERVAL, s_pWhizThinkContext ); @@ -1245,6 +1388,12 @@ void CPropCombineBall::OnHitEntity( CBaseEntity *pHitEntity, float flSpeed, int m_flNextDamageTime = gpGlobals->curtime + 0.1f; } +#ifdef MAPBASE + // Damage forces for NPC balls. + info.SetDamagePosition( GetAbsOrigin() ); + info.SetDamageForce( GetAbsVelocity() ); +#endif + pHitEntity->TakeDamage( info ); } } diff --git a/mp/src/game/server/hl2/prop_combine_ball.h b/mp/src/game/server/hl2/prop_combine_ball.h index 1de4390d..d750d6ad 100644 --- a/mp/src/game/server/hl2/prop_combine_ball.h +++ b/mp/src/game/server/hl2/prop_combine_ball.h @@ -65,6 +65,10 @@ public: void InputFadeAndRespawn( inputdata_t &inputdata ); void InputKill( inputdata_t &inputdata ); void InputSocketed( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetLifetime( inputdata_t &inputdata ); + void InputAddLifetime( inputdata_t &inputdata ); +#endif enum { diff --git a/mp/src/game/server/hl2/proto_sniper.cpp b/mp/src/game/server/hl2/proto_sniper.cpp index fe9844eb..5427850a 100644 --- a/mp/src/game/server/hl2/proto_sniper.cpp +++ b/mp/src/game/server/hl2/proto_sniper.cpp @@ -33,6 +33,13 @@ #include "effect_color_tables.h" #include "npc_rollermine.h" #include "eventqueue.h" +#ifdef MAPBASE +#include "CRagdollMagnet.h" +#endif +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE +#include "mapbase/expandedrs_combine.h" +#include "ai_speech.h" +#endif #include "effect_dispatch_data.h" #include "te_effect_dispatch.h" @@ -62,6 +69,9 @@ extern ConVar sk_dmg_sniper_penetrate_npc; #define SF_SNIPER_STARTDISABLED (1 << 19) #define SF_SNIPER_FAST (1 << 20) ///< This is faster-shooting sniper. Paint time is decreased 25%. Bullet speed increases 150%. #define SF_SNIPER_NOSWEEP (1 << 21) ///< This sniper doesn't sweep to the target or use decoys. +#ifdef MAPBASE +#define SF_SNIPER_DIE_ON_FIRE (1 << 22) // This sniper dies on fire. +#endif // If the last time I fired at someone was between 0 and this many seconds, draw // a bead on them much faster. (use subsequent paint time) @@ -194,9 +204,15 @@ private: //========================================================= //========================================================= +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE +class CProtoSniper : public CAI_ExpresserHost +{ + DECLARE_CLASS( CProtoSniper, CAI_ExpresserHost ); +#else class CProtoSniper : public CAI_BaseNPC { DECLARE_CLASS( CProtoSniper, CAI_BaseNPC ); +#endif public: CProtoSniper( void ); @@ -262,7 +278,21 @@ public: void NotifyShotMissedTarget(); +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE + //DeclareResponseSystem() + bool SpeakIfAllowed(const char *concept, const char *modifiers = NULL); + void ModifyOrAppendCriteria( AI_CriteriaSet& set ); + + virtual CAI_Expresser *CreateExpresser( void ); + virtual CAI_Expresser *GetExpresser() { return m_pExpresser; } + virtual void PostConstructor( const char *szClassname ); +#endif + private: + +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE + CAI_Expresser * m_pExpresser; +#endif bool ShouldSnapShot( void ); void ClearTargetGroup( void ); @@ -363,6 +393,11 @@ private: bool m_bKilledPlayer; bool m_bShootZombiesInChest; ///< if true, do not try to shoot zombies in the headcrab +#ifdef MAPBASE + string_t m_iszBeamName; // Custom beam texture + color32 m_BeamColor; // Custom beam color +#endif + COutputEvent m_OnShotFired; DEFINE_CUSTOM_AI; @@ -440,6 +475,11 @@ BEGIN_DATADESC( CProtoSniper ) DEFINE_FIELD( m_bWarnedTargetEntity, FIELD_BOOLEAN ), DEFINE_FIELD( m_flTimeLastShotMissed, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszBeamName, FIELD_STRING, "BeamName" ), + DEFINE_KEYFIELD( m_BeamColor, FIELD_COLOR32, "BeamColor" ), +#endif + // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "EnableSniper", InputEnableSniper ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableSniper", InputDisableSniper ), @@ -496,6 +536,10 @@ enum Sniper_Conds COND_SNIPER_FRUSTRATED, COND_SNIPER_SWEEP_TARGET, COND_SNIPER_NO_SHOT, +#ifdef MAPBASE + // Using COND_ENEMY_DEAD made us take credit for other people's kills + COND_SNIPER_KILLED_ENEMY, +#endif }; @@ -557,7 +601,11 @@ CProtoSniper::CProtoSniper( void ) : m_flKeyfieldPaintTime(SNIPER_DEFAULT_PAINT_ bool CProtoSniper::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC ) { Disposition_t disp = IRelationType(pEntity); +#ifdef MAPBASE + if( disp > D_FR ) +#else if( disp != D_HT ) +#endif { // Don't bother with anything I wouldn't shoot. return false; @@ -643,8 +691,13 @@ void CProtoSniper::LaserOn( const Vector &vecTarget, const Vector &vecDeviance ) { if (!m_pBeam) { +#ifdef MAPBASE + m_pBeam = CBeam::BeamCreate( STRING(m_iszBeamName), 1.0f ); + m_pBeam->SetColor( m_BeamColor.r, m_BeamColor.g, m_BeamColor.b ); +#else m_pBeam = CBeam::BeamCreate( "effects/bluelaser1.vmt", 1.0f ); m_pBeam->SetColor( 0, 100, 255 ); +#endif } else { @@ -904,10 +957,40 @@ LINK_ENTITY_TO_CLASS( sniperbullet, CSniperBullet ); //----------------------------------------------------------------------------- void CProtoSniper::Precache( void ) { +#ifdef MAPBASE + if (GetModelName() == NULL_STRING) + SetModelName(AllocPooledString("models/combine_soldier.mdl")); + + PrecacheModel(STRING(GetModelName())); + + // Should we bother to make these customizable? These are static, too. + sHaloSprite = PrecacheModel("sprites/light_glow03.vmt"); + sFlashSprite = PrecacheModel( "sprites/muzzleflash1.vmt" ); + + if (m_iszBeamName == NULL_STRING) + { + m_iszBeamName = AllocPooledString("effects/bluelaser1.vmt"); + m_BeamColor.r = 0; + m_BeamColor.g = 100; + m_BeamColor.b = 255; + } + else if (Q_GetFileExtension(STRING(m_iszBeamName)) == NULL) + { + // The path doesn't have a .vmt. Fix this or we crash! + // + // I know I warn against using .vmt even though this code ultimately ensures there is no consequence other than a warning, + // but it's bad practice and remember: That old string without the .vmt would likely be floating around somewhere doing nothing. + Warning("%s beam name \"%s\" lacks .vmt!\n", GetDebugName(), STRING(m_iszBeamName)); + m_iszBeamName = AllocPooledString(UTIL_VarArgs("%s.vmt", STRING(m_iszBeamName))); + } + + PrecacheModel(STRING(m_iszBeamName)); +#else PrecacheModel("models/combine_soldier.mdl"); sHaloSprite = PrecacheModel("sprites/light_glow03.vmt"); sFlashSprite = PrecacheModel( "sprites/muzzleflash1.vmt" ); PrecacheModel("effects/bluelaser1.vmt"); +#endif UTIL_PrecacheOther( "sniperbullet" ); @@ -931,8 +1014,12 @@ void CProtoSniper::Spawn( void ) { Precache(); +#ifdef MAPBASE + SetModel( STRING(GetModelName()) ); +#else /// HACK: SetModel( "models/combine_soldier.mdl" ); +#endif //m_hBullet = (CSniperBullet *)Create( "sniperbullet", GetBulletOrigin(), GetLocalAngles(), NULL ); @@ -985,7 +1072,11 @@ void CProtoSniper::Spawn( void ) // Point the cursor straight ahead so that the sniper's // first sweep of the laser doesn't look weird. Vector vecForward; +#ifdef MAPBASE + AngleVectors( GetAbsAngles(), &vecForward ); +#else AngleVectors( GetLocalAngles(), &vecForward ); +#endif m_vecPaintCursor = GetBulletOrigin() + vecForward * 1024; m_fWeaponLoaded = true; @@ -1200,7 +1291,11 @@ Vector CProtoSniper::GetBulletOrigin( void ) else { Vector vecForward; +#ifdef MAPBASE + AngleVectors( GetAbsAngles(), &vecForward ); +#else AngleVectors( GetLocalAngles(), &vecForward ); +#endif return WorldSpaceCenter() + vecForward * 20; } } @@ -1327,11 +1422,32 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info ) { if( !(m_spawnflags & SF_SNIPER_NOCORPSE) ) { +#ifdef MAPBASE + Vector vecForce; + + // See if there's a ragdoll magnet that should influence our force. + // However, to avoid conflicts with existing maps, only allow magnets that have us as their target. + CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this ); + if( pMagnet && pMagnet->m_target == GetEntityName() ) + { + vecForce = pMagnet->GetForceVector( this ); + pMagnet->m_OnUsed.Set(vecForce, this, pMagnet); + } + else + { + Vector vecForward; + AngleVectors( GetAbsAngles(), &vecForward ); + float flForce = random->RandomFloat( 500, 700 ) * 10; + + vecForce = (vecForward * flForce) + Vector(0, 0, 600); + } +#else Vector vecForward; float flForce = random->RandomFloat( 500, 700 ) * 10; AngleVectors( GetLocalAngles(), &vecForward ); +#endif float flFadeTime = 0.0; @@ -1341,8 +1457,13 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info ) } CBaseEntity *pGib; +#ifdef MAPBASE + bool bShouldIgnite = IsOnFire() || HasSpawnFlags(SF_SNIPER_DIE_ON_FIRE); + pGib = CreateRagGib( STRING(GetModelName()), GetAbsOrigin(), GetAbsAngles(), vecForce, flFadeTime, bShouldIgnite ); +#else bool bShouldIgnite = IsOnFire() || hl2_episodic.GetBool(); pGib = CreateRagGib( "models/combine_soldier.mdl", GetLocalOrigin(), GetLocalAngles(), (vecForward * flForce) + Vector(0, 0, 600), flFadeTime, bShouldIgnite ); +#endif } @@ -1357,7 +1478,11 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info ) LaserOff(); +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE + SpeakIfAllowed( TLK_SNIPER_DIE ); +#else EmitSound( "NPC_Sniper.Die" ); +#endif UTIL_Remove( this ); } @@ -1366,6 +1491,11 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info ) //--------------------------------------------------------- void CProtoSniper::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) { +#ifdef MAPBASE + if (pVictim == GetEnemy()) + SetCondition(COND_SNIPER_KILLED_ENEMY); +#endif + if( pVictim && pVictim->IsPlayer() ) { m_bKilledPlayer = true; @@ -1384,10 +1514,18 @@ void CProtoSniper::UpdateOnRemove( void ) //--------------------------------------------------------- int CProtoSniper::SelectSchedule ( void ) { +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE + if (HasCondition(COND_SNIPER_KILLED_ENEMY)) + { + SpeakIfAllowed(TLK_SNIPER_TARGETDESTROYED); + ClearCondition(COND_SNIPER_KILLED_ENEMY); + } +#else if( HasCondition(COND_ENEMY_DEAD) && sniperspeak.GetBool() ) { EmitSound( "NPC_Sniper.TargetDestroyed" ); } +#endif if( !m_fWeaponLoaded ) { @@ -1420,10 +1558,14 @@ int CProtoSniper::SelectSchedule ( void ) // probably won't harm him. // Also, don't play the sound effect if we're an ally. +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE + SpeakIfAllowed(TLK_SNIPER_DANGER); +#else if ( IsPlayerAllySniper() == false ) { EmitSound( "NPC_Sniper.HearDanger" ); } +#endif } return SCHED_PSNIPER_SUPPRESSED; @@ -1795,7 +1937,11 @@ int CProtoSniper::RangeAttack1Conditions ( float flDot, float flDist ) float flDist; +#ifdef MAPBASE + flDist = ( GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).Length2D(); +#else flDist = ( GetLocalOrigin() - GetEnemy()->GetLocalOrigin() ).Length2D(); +#endif if( flDist <= m_flPatience ) { @@ -1893,7 +2039,11 @@ bool CProtoSniper::FireBullet( const Vector &vecTarget, bool bDirectShot ) vecBulletOrigin = GetBulletOrigin(); +#ifdef MAPBASE + pBullet = (CSniperBullet *)Create( "sniperbullet", GetBulletOrigin(), GetAbsAngles(), NULL ); +#else pBullet = (CSniperBullet *)Create( "sniperbullet", GetBulletOrigin(), GetLocalAngles(), NULL ); +#endif Assert( pBullet != NULL ); @@ -1994,10 +2144,18 @@ void CProtoSniper::StartTask( const Task_t *pTask ) // Otherwise, sweep from wherever the cursor was. if( m_hSweepTarget->HasSpawnFlags( SF_SNIPERTARGET_SNAPTO ) ) { +#ifdef MAPBASE + m_vecPaintCursor = m_hSweepTarget->GetAbsOrigin(); +#else m_vecPaintCursor = m_hSweepTarget->GetLocalOrigin(); +#endif } +#ifdef MAPBASE + LaserOn( m_hSweepTarget->GetAbsOrigin(), vec3_origin ); +#else LaserOn( m_hSweepTarget->GetLocalOrigin(), vec3_origin ); +#endif break; case TASK_SNIPER_PAINT_ENEMY: @@ -2066,7 +2224,11 @@ void CProtoSniper::StartTask( const Task_t *pTask ) else { // Try to start the laser where the player can't miss seeing it! +#ifdef MAPBASE + AngleVectors( GetEnemy()->GetAbsAngles(), &vecCursor ); +#else AngleVectors( GetEnemy()->GetLocalAngles(), &vecCursor ); +#endif vecCursor = vecCursor * 300; vecCursor += GetEnemy()->EyePosition(); LaserOn( vecCursor, Vector( 16, 16, 16 ) ); @@ -2200,7 +2362,11 @@ void CProtoSniper::RunTask( const Task_t *pTask ) if ( m_hSweepTarget->HasSpawnFlags( SF_SNIPERTARGET_SHOOTME ) ) { +#ifdef MAPBASE + FireBullet( m_hSweepTarget->GetAbsOrigin(), false ); +#else FireBullet( m_hSweepTarget->GetLocalOrigin(), false ); +#endif TaskComplete(); // Force a reload. } @@ -2209,7 +2375,11 @@ void CProtoSniper::RunTask( const Task_t *pTask ) // Bump the timer up, update the cursor, paint the new target! // This is done regardless of whether we just fired at the current target. +#ifdef MAPBASE + m_vecPaintCursor = m_hSweepTarget->GetAbsOrigin(); +#else m_vecPaintCursor = m_hSweepTarget->GetLocalOrigin(); +#endif if( IsSweepingRandomly() ) { // If sweeping randomly, just pick another target. @@ -2399,7 +2569,11 @@ Vector CProtoSniper::EyePosition( void ) { if( m_spawnflags & SF_SNIPER_HIDDEN ) { +#ifdef MAPBASE + return GetAbsOrigin(); +#else return GetLocalOrigin(); +#endif } else { @@ -2538,7 +2712,11 @@ Vector CProtoSniper::LeadTarget( CBaseEntity *pTarget ) vecAngle.x = 0; vecAngle.z = 0; +#ifdef MAPBASE + vecAngle.y += pTarget->GetAbsAngles().y; +#else vecAngle.y += pTarget->GetLocalAngles().y; +#endif AngleVectors( vecAngle, &vecVelocity ); @@ -2573,7 +2751,11 @@ Vector CProtoSniper::LeadTarget( CBaseEntity *pTarget ) { Vector vecBulletOrigin; vecBulletOrigin = GetBulletOrigin(); +#ifdef MAPBASE + CPVSFilter filter( GetAbsOrigin() ); +#else CPVSFilter filter( GetLocalOrigin() ); +#endif te->ShowLine( filter, 0.0, &vecBulletOrigin, &vecAdjustedShot ); } @@ -2772,7 +2954,11 @@ bool CProtoSniper::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity ** vecVerticalOffset = SNIPER_TARGET_VERTICAL_OFFSET; } +#ifdef MAPBASE + AngleVectors( pEntity->GetAbsAngles(), NULL, &vecRight, NULL ); +#else AngleVectors( pEntity->GetLocalAngles(), NULL, &vecRight, NULL ); +#endif vecEye = vecRight * SNIPER_EYE_DIST - vecVerticalOffset; UTIL_TraceLine( EyePosition(), pEntity->EyePosition() + vecEye, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr ); @@ -2901,6 +3087,52 @@ void CProtoSniper::NotifyShotMissedTarget() // in these NPCs' walk and run animations. } +#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE +//----------------------------------------------------------------------------- +// Purpose: Speak concept +//----------------------------------------------------------------------------- +bool CProtoSniper::SpeakIfAllowed(const char *concept, const char *modifiers) +{ + if (!GetExpresser()->CanSpeakConcept(concept)) + return false; + + return Speak(concept, modifiers); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CProtoSniper::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + + // We still need this + set.AppendCriteria( "sniperspeak", UTIL_VarArgs("%i", sniperspeak.GetInt()) ); + set.AppendCriteria( "playerally", UTIL_VarArgs("%d", IsPlayerAllySniper()) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAI_Expresser *CProtoSniper::CreateExpresser( void ) +{ + m_pExpresser = new CAI_Expresser(this); + if (!m_pExpresser) + return NULL; + + m_pExpresser->Connect(this); + return m_pExpresser; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CProtoSniper::PostConstructor(const char *szClassname) +{ + BaseClass::PostConstructor(szClassname); + CreateExpresser(); +} +#endif + //----------------------------------------------------------------------------- // // Schedules @@ -2916,6 +3148,9 @@ AI_BEGIN_CUSTOM_NPC( proto_sniper, CProtoSniper ) DECLARE_CONDITION( COND_SNIPER_FRUSTRATED ); DECLARE_CONDITION( COND_SNIPER_SWEEP_TARGET ); DECLARE_CONDITION( COND_SNIPER_NO_SHOT ); +#ifdef MAPBASE + DECLARE_CONDITION( COND_SNIPER_KILLED_ENEMY ); +#endif DECLARE_TASK( TASK_SNIPER_FRUSTRATED_ATTACK ); DECLARE_TASK( TASK_SNIPER_PAINT_ENEMY ); diff --git a/mp/src/game/server/hl2/script_intro.cpp b/mp/src/game/server/hl2/script_intro.cpp index 2798af2c..4c13da44 100644 --- a/mp/src/game/server/hl2/script_intro.cpp +++ b/mp/src/game/server/hl2/script_intro.cpp @@ -46,6 +46,13 @@ BEGIN_DATADESC(CScriptIntro) DEFINE_KEYFIELD( m_bAlternateFOV, FIELD_BOOLEAN, "alternatefovchange" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bDrawSky, FIELD_BOOLEAN, "DrawSky" ), + DEFINE_KEYFIELD( m_bDrawSky2, FIELD_BOOLEAN, "DrawSky2" ), + + DEFINE_KEYFIELD( m_bUseEyePosition, FIELD_BOOLEAN, "UseEyePosition" ), +#endif + // Inputs DEFINE_INPUTFUNC(FIELD_STRING, "SetCameraViewEntity", InputSetCameraViewEntity ), DEFINE_INPUTFUNC(FIELD_INTEGER, "SetBlendMode", InputSetBlendMode ), @@ -58,6 +65,10 @@ BEGIN_DATADESC(CScriptIntro) DEFINE_INPUTFUNC(FIELD_VOID, "Deactivate", InputDeactivate ), DEFINE_INPUTFUNC(FIELD_STRING, "FadeTo", InputFadeTo ), DEFINE_INPUTFUNC(FIELD_STRING, "SetFadeColor", InputSetFadeColor ), +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetDrawSky", InputSetDrawSky ), + DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetDrawSky2", InputSetDrawSky2 ), +#endif DEFINE_THINKFUNC( BlendComplete ), @@ -71,7 +82,11 @@ IMPLEMENT_SERVERCLASS_ST( CScriptIntro, DT_ScriptIntro ) SendPropFloat( SENDINFO( m_flNextBlendTime ), 10 ), SendPropFloat( SENDINFO( m_flBlendStartTime ), 10 ), SendPropBool( SENDINFO( m_bActive ) ), - +#ifdef MAPBASE + SendPropBool( SENDINFO( m_bDrawSky ) ), + SendPropBool( SENDINFO( m_bDrawSky2 ) ), + SendPropBool( SENDINFO( m_bUseEyePosition ) ), +#endif // Fov & fov blends SendPropInt( SENDINFO( m_iFOV ), 9 ), @@ -394,3 +409,256 @@ void CScriptIntro::InputSetFadeColor( inputdata_t &inputdata ) m_flFadeColor.Set( 1, flG ); m_flFadeColor.Set( 2, flB ); } + +#ifdef MAPBASE +class CPlayerViewProxy : public CBaseEntity +{ +public: + DECLARE_CLASS( CPlayerViewProxy, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CPlayerViewProxy(); + + void Spawn( void ); + int UpdateTransmitState( void ); + + void ActivateEnt( CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); + void DeactivateEnt(); + void MeasureThink(); + + Vector EyePosition( void ); // position of eyes + const QAngle &EyeAngles( void ); // Direction of eyes in world space + const QAngle &LocalEyeAngles( void ); // Direction of eyes + Vector EarPosition( void ); // position of ears + + // Inputs + void InputActivate( inputdata_t &inputdata ); + void InputDeactivate( inputdata_t &inputdata ); + +#ifdef MAPBASE_MP + // TODO: Mapbase MP should use reception filter or something to determine which player's eye position is offset + CNetworkVar( CHandle, m_hPlayer ); +#else + CHandle m_hPlayer; +#endif + + string_t m_iszMeasureReference; + EHANDLE m_hMeasureReference; + string_t m_iszTargetReference; + EHANDLE m_hTargetReference; + + CNetworkVar( float, m_flScale ); + + CNetworkVar( bool, m_bEnabled ); +}; + +LINK_ENTITY_TO_CLASS( info_player_view_proxy, CPlayerViewProxy ); + +BEGIN_DATADESC( CPlayerViewProxy ) + + // Keys + DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iszMeasureReference, FIELD_STRING, "MeasureReference" ), + DEFINE_FIELD( m_hMeasureReference, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iszTargetReference, FIELD_STRING, "TargetReference" ), + DEFINE_FIELD( m_hTargetReference, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "TargetScale" ), + DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "Enabled" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), + + DEFINE_THINKFUNC( MeasureThink ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CPlayerViewProxy, DT_PlayerViewProxy ) +#ifdef MAPBASE_MP + SendPropEHandle( SENDINFO( m_hPlayer ) ), +#endif + SendPropBool( SENDINFO( m_bEnabled ) ), +END_SEND_TABLE() + +CPlayerViewProxy::CPlayerViewProxy() +{ + m_flScale = 1.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerViewProxy::Spawn( void ) +{ + if (m_bEnabled) + ActivateEnt(); +} + +//------------------------------------------------------------------------------ +// Purpose : Send even though we don't have a model. +//------------------------------------------------------------------------------ +int CPlayerViewProxy::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerViewProxy::MeasureThink( void ) +{ + if (m_hPlayer.Get() == NULL) + { + // Player has disappeared! Stopping measure + return; + } + + if (m_bEnabled && m_hMeasureReference.Get() && m_hTargetReference.Get()) + { + matrix3x4_t matRefToMeasure, matWorldToMeasure; + MatrixInvert( m_hPlayer.Get()->EntityToWorldTransform(), matWorldToMeasure ); + ConcatTransforms( matWorldToMeasure, m_hMeasureReference.Get()->EntityToWorldTransform(), matRefToMeasure ); + + // Apply the scale factor + if ( ( m_flScale != 0.0f ) && ( m_flScale != 1.0f ) ) + { + Vector vecTranslation; + MatrixGetColumn( matRefToMeasure, 3, vecTranslation ); + vecTranslation /= m_flScale; + MatrixSetColumn( vecTranslation, 3, matRefToMeasure ); + } + + // Now apply the new matrix to the new reference point + matrix3x4_t matMeasureToRef, matNewTargetToWorld; + MatrixInvert( matRefToMeasure, matMeasureToRef ); + ConcatTransforms( m_hTargetReference.Get()->EntityToWorldTransform(), matMeasureToRef, matNewTargetToWorld ); + + Vector vecOrigin; + QAngle angAngles; + MatrixAngles( matNewTargetToWorld, angAngles, vecOrigin ); + Teleport( &vecOrigin, &angAngles, NULL ); + + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); + } + //else + //{ + // SetAbsOrigin( m_hPlayer.Get()->GetAbsOrigin() ); + // SetAbsAngles( m_hPlayer.Get()->GetAbsAngles() ); + //} +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Vector CPlayerViewProxy::EyePosition( void ) +{ + if (m_hPlayer.Get()) + { + //Vector vecPlayerOffset = m_hPlayer.Get()->EyePosition() - m_hPlayer.Get()->GetAbsOrigin(); + //return GetAbsOrigin() + vecPlayerOffset; + + Vector vecOrigin; + QAngle angAngles; + float fldummy; + m_hPlayer.Get()->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy ); + + return GetAbsOrigin() + (vecOrigin - m_hPlayer.Get()->GetAbsOrigin()); + } + else + return BaseClass::EyePosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const QAngle &CPlayerViewProxy::EyeAngles( void ) +{ + if (m_hPlayer.Get()) + { + Vector vecOrigin; + static QAngle angAngles; + float fldummy; + m_hPlayer.Get()->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy ); + + angAngles = GetAbsAngles() + (angAngles - m_hPlayer.Get()->GetAbsAngles()); + return angAngles; + + //return m_hPlayer.Get()->EyeAngles(); + } + else + return BaseClass::EyeAngles(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const QAngle &CPlayerViewProxy::LocalEyeAngles( void ) +{ + if (m_hPlayer.Get()) { + static QAngle angAngles = GetAbsAngles() + (m_hPlayer.Get()->LocalEyeAngles() - m_hPlayer.Get()->GetAbsAngles()); + return angAngles; + } else + return BaseClass::LocalEyeAngles(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Vector CPlayerViewProxy::EarPosition( void ) +{ + if (m_hPlayer.Get()) + { + Vector vecPlayerOffset = m_hPlayer.Get()->EarPosition() - m_hPlayer.Get()->GetAbsOrigin(); + return GetAbsOrigin() + vecPlayerOffset; + } + else + return BaseClass::EarPosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerViewProxy::ActivateEnt( CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + m_bEnabled = true; + + m_hMeasureReference = gEntList.FindEntityByName( NULL, m_iszMeasureReference, this, pActivator, pCaller ); + m_hTargetReference = gEntList.FindEntityByName( NULL, m_iszTargetReference, this, pActivator, pCaller ); + + // Do something else in Mapbase MP + m_hPlayer = UTIL_GetLocalPlayer(); + + SetThink( &CPlayerViewProxy::MeasureThink ); + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerViewProxy::DeactivateEnt( void ) +{ + m_bEnabled = false; + + m_hMeasureReference = NULL; + m_hTargetReference = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPlayerViewProxy::InputActivate( inputdata_t &inputdata ) +{ + ActivateEnt(inputdata.pActivator, inputdata.pCaller); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPlayerViewProxy::InputDeactivate( inputdata_t &inputdata ) +{ + DeactivateEnt(); +} +#endif diff --git a/mp/src/game/server/hl2/script_intro.h b/mp/src/game/server/hl2/script_intro.h index 5e59fa33..552c6188 100644 --- a/mp/src/game/server/hl2/script_intro.h +++ b/mp/src/game/server/hl2/script_intro.h @@ -44,6 +44,11 @@ public: void InputFadeTo( inputdata_t &inputdata ); void InputSetFadeColor( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetDrawSky( inputdata_t &inputdata ) { m_bDrawSky = inputdata.value.Bool(); } + void InputSetDrawSky2( inputdata_t &inputdata ) { m_bDrawSky2 = inputdata.value.Bool(); } +#endif + bool GetIncludedPVSOrigin( Vector *pOrigin, CBaseEntity **ppCamera ); private: @@ -61,6 +66,11 @@ private: CNetworkVar( float, m_flBlendStartTime ); CNetworkVar( int, m_iStartFOV ); CNetworkVar( bool, m_bActive ); +#ifdef MAPBASE + CNetworkVar( bool, m_bDrawSky ); + CNetworkVar( bool, m_bDrawSky2 ); + CNetworkVar( bool, m_bUseEyePosition ); +#endif // Fov & fov blends CNetworkVar( int, m_iNextFOV ); diff --git a/mp/src/game/server/hl2/vehicle_airboat.cpp b/mp/src/game/server/hl2/vehicle_airboat.cpp index c8a49ddb..120ffcaf 100644 --- a/mp/src/game/server/hl2/vehicle_airboat.cpp +++ b/mp/src/game/server/hl2/vehicle_airboat.cpp @@ -1573,6 +1573,8 @@ void CPropAirboat::FireGun( ) Vector vecForward; GetAttachment( m_nGunBarrelAttachment, vecGunPosition, &vecForward ); + CDisablePredictionFiltering disabler; + // NOTE: For the airboat, unable to fire really means the aim is clamped Vector vecAimPoint; if ( !m_bUnableToFire ) diff --git a/mp/src/game/server/hl2/vehicle_jeep.cpp b/mp/src/game/server/hl2/vehicle_jeep.cpp index 80eb1368..0b47f3cb 100644 --- a/mp/src/game/server/hl2/vehicle_jeep.cpp +++ b/mp/src/game/server/hl2/vehicle_jeep.cpp @@ -135,6 +135,10 @@ BEGIN_DATADESC( CPropJeep ) DEFINE_INPUTFUNC( FIELD_VOID, "ShowHudHint", InputShowHudHint ), DEFINE_INPUTFUNC( FIELD_VOID, "StartRemoveTauCannon", InputStartRemoveTauCannon ), DEFINE_INPUTFUNC( FIELD_VOID, "FinishRemoveTauCannon", InputFinishRemoveTauCannon ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "DisablePhysGun", InputDisablePhysGun ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnablePhysGun", InputEnablePhysGun ), +#endif DEFINE_THINKFUNC( JeepSeagullThink ), END_DATADESC() @@ -148,6 +152,11 @@ END_SEND_TABLE(); LINK_ENTITY_TO_CLASS( prop_vehicle_jeep, CPropJeep ); #endif +#ifdef MAPBASE +// Shortcut to old jeep for those who want to use the scout car in Episodic +LINK_ENTITY_TO_CLASS( prop_vehicle_jeep_old, CPropJeep ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -898,6 +907,8 @@ void CPropJeep::FireCannon( void ) if ( m_bUnableToFire ) return; + CDisablePredictionFiltering disabler; + m_flCannonTime = gpGlobals->curtime + 0.2f; m_bCannonCharging = false; @@ -936,6 +947,8 @@ void CPropJeep::FireCannon( void ) //----------------------------------------------------------------------------- void CPropJeep::FireChargedCannon( void ) { + CDisablePredictionFiltering disabler; + bool penetrated = false; m_bCannonCharging = false; @@ -1674,6 +1687,23 @@ void CPropJeep::InputFinishRemoveTauCannon( inputdata_t &inputdata ) m_bHasGun = false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Stop players punting the car around. +//----------------------------------------------------------------------------- +void CPropJeep::InputDisablePhysGun( inputdata_t &data ) +{ + AddEFlags( EFL_NO_PHYSCANNON_INTERACTION ); +} +//----------------------------------------------------------------------------- +// Purpose: Return to normal +//----------------------------------------------------------------------------- +void CPropJeep::InputEnablePhysGun( inputdata_t &data ) +{ + RemoveEFlags( EFL_NO_PHYSCANNON_INTERACTION ); +} +#endif + //======================================================================================================================================== // JEEP FOUR WHEEL PHYSICS VEHICLE SERVER VEHICLE //======================================================================================================================================== diff --git a/mp/src/game/server/hl2/vehicle_jeep.h b/mp/src/game/server/hl2/vehicle_jeep.h index ba08c2c4..982ee6db 100644 --- a/mp/src/game/server/hl2/vehicle_jeep.h +++ b/mp/src/game/server/hl2/vehicle_jeep.h @@ -118,6 +118,10 @@ private: void InputShowHudHint( inputdata_t &inputdata ); void InputStartRemoveTauCannon( inputdata_t &inputdata ); void InputFinishRemoveTauCannon( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDisablePhysGun( inputdata_t &data ); + void InputEnablePhysGun( inputdata_t &data ); +#endif protected: diff --git a/mp/src/game/server/hl2/weapon_357.cpp b/mp/src/game/server/hl2/weapon_357.cpp index 2b3a3a12..3fa3fab8 100644 --- a/mp/src/game/server/hl2/weapon_357.cpp +++ b/mp/src/game/server/hl2/weapon_357.cpp @@ -39,8 +39,43 @@ public: float WeaponAutoAimScale() { return 0.6f; } +#ifdef MAPBASE + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + + virtual int GetMinBurst() { return 1; } + virtual int GetMaxBurst() { return 1; } + virtual float GetMinRestTime( void ) { return 1.0f; } + virtual float GetMaxRestTime( void ) { return 2.5f; } + + virtual float GetFireRate( void ) { return 1.0f; } + + virtual const Vector& GetBulletSpread( void ) + { + static Vector cone = VECTOR_CONE_15DEGREES; + if (!GetOwner() || !GetOwner()->IsNPC()) + return cone; + + static Vector AllyCone = VECTOR_CONE_2DEGREES; + static Vector NPCCone = VECTOR_CONE_5DEGREES; + + if( GetOwner()->MyNPCPointer()->IsPlayerAlly() ) + { + // 357 allies should be cooler + return AllyCone; + } + + return NPCCone; + } + + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); +#endif + DECLARE_SERVERCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE + DECLARE_ACTTABLE(); +#endif }; LINK_ENTITY_TO_CLASS( weapon_357, CWeapon357 ); @@ -53,6 +88,29 @@ END_SEND_TABLE() BEGIN_DATADESC( CWeapon357 ) END_DATADESC() +#ifdef MAPBASE +acttable_t CWeapon357::m_acttable[] = +{ + { ACT_IDLE, ACT_IDLE_PISTOL, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true }, + { ACT_RELOAD, ACT_RELOAD_PISTOL, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_PISTOL, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_PISTOL, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_PISTOL,true }, + { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_PISTOL_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_PISTOL_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, + { ACT_WALK, ACT_WALK_PISTOL, false }, + { ACT_RUN, ACT_RUN_PISTOL, false }, +}; + + +IMPLEMENT_ACTTABLE( CWeapon357 ); +#endif + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- @@ -60,6 +118,13 @@ CWeapon357::CWeapon357( void ) { m_bReloadsSingly = false; m_bFiresUnderwater = false; + +#ifdef MAPBASE + m_fMinRange1 = 24; + m_fMaxRange1 = 1000; + m_fMinRange2 = 24; + m_fMaxRange2 = 200; +#endif } //----------------------------------------------------------------------------- @@ -87,9 +152,57 @@ void CWeapon357::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatChara break; } +#ifdef MAPBASE + case EVENT_WEAPON_PISTOL_FIRE: + { + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + ASSERT( npc != NULL ); + + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); + } + break; + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; +#endif } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeapon357::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + WeaponSound( SINGLE_NPC ); + pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 1 ); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Some things need this. (e.g. the new Force(X)Fire inputs or blindfire actbusy) +//----------------------------------------------------------------------------- +void CWeapon357::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_annabelle.cpp b/mp/src/game/server/hl2/weapon_annabelle.cpp index 94a8a1a8..47ee9e39 100644 --- a/mp/src/game/server/hl2/weapon_annabelle.cpp +++ b/mp/src/game/server/hl2/weapon_annabelle.cpp @@ -82,6 +82,9 @@ END_DATADESC() acttable_t CWeaponAnnabelle::m_acttable[] = { +#ifdef MAPBASE + { ACT_IDLE, ACT_IDLE_SMG1, false }, +#endif { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true }, { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, { ACT_RELOAD, ACT_RELOAD_SMG1, true }, diff --git a/mp/src/game/server/hl2/weapon_ar2.cpp b/mp/src/game/server/hl2/weapon_ar2.cpp index d9c7ec25..89a16700 100644 --- a/mp/src/game/server/hl2/weapon_ar2.cpp +++ b/mp/src/game/server/hl2/weapon_ar2.cpp @@ -27,6 +27,9 @@ #include "npc_combine.h" #include "rumble_shared.h" #include "gamestats.h" +#ifdef MAPBASE +#include "npc_playercompanion.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -35,6 +38,10 @@ ConVar sk_weapon_ar2_alt_fire_radius( "sk_weapon_ar2_alt_fire_radius", "10" ); ConVar sk_weapon_ar2_alt_fire_duration( "sk_weapon_ar2_alt_fire_duration", "2" ); ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" ); +#ifdef MAPBASE +extern acttable_t *GetSMG1Acttable(); +#endif + //========================================================= //========================================================= @@ -54,6 +61,56 @@ PRECACHE_WEAPON_REGISTER(weapon_ar2); acttable_t CWeaponAR2::m_acttable[] = { +#if AR2_ACTIVITY_FIX == 1 + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, true }, + { ACT_RELOAD, ACT_RELOAD_AR2, true }, + { ACT_IDLE, ACT_IDLE_AR2, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_AR2, false }, + + { ACT_WALK, ACT_WALK_AR2, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_AR2_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_AR2_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_AR2, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_AR2_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR2_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR2_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR2, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR2_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_AR2, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_AR2, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_AR2, false }, + { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR2_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR2_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR2, true }, +// { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, +#else { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, true }, { ACT_RELOAD, ACT_RELOAD_SMG1, true }, // FIXME: hook to AR2 unique { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to AR2 unique @@ -102,6 +159,7 @@ acttable_t CWeaponAR2::m_acttable[] = { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponAR2); @@ -382,6 +440,10 @@ void CWeaponAR2::FireNPCSecondaryAttack( CBaseCombatCharacter *pOperator, bool b Vector vecTarget; +#ifdef MAPBASE + // It's shared across all NPCs now that it's available on more than just soldiers on more than just the AR2. + vecTarget = pNPC->GetAltFireTarget(); +#else CNPC_Combine *pSoldier = dynamic_cast( pNPC ); if ( pSoldier ) { @@ -389,6 +451,13 @@ void CWeaponAR2::FireNPCSecondaryAttack( CBaseCombatCharacter *pOperator, bool b // Therefore, we must ask them specifically what direction they are shooting. vecTarget = pSoldier->GetAltFireTarget(); } +#ifdef MAPBASE + else if ( CNPC_PlayerCompanion *pCompanion = dynamic_cast( pNPC ) ) + { + // Companions can use energy balls now. Isn't that lovely? + vecTarget = pCompanion->GetAltFireTarget(); + } +#endif else { // All other users of the AR2 alt-fire shoot directly at their enemy. @@ -397,6 +466,7 @@ void CWeaponAR2::FireNPCSecondaryAttack( CBaseCombatCharacter *pOperator, bool b vecTarget = pNPC->GetEnemy()->BodyTarget( vecSrc ); } +#endif vecAiming = vecTarget - vecSrc; VectorNormalize( vecAiming ); @@ -412,12 +482,25 @@ void CWeaponAR2::FireNPCSecondaryAttack( CBaseCombatCharacter *pOperator, bool b Vector vecVelocity = vecAiming * 1000.0f; // Fire the combine ball +#ifdef MAPBASE + CBaseEntity *pBall = CreateCombineBall( vecSrc, + vecVelocity, + flRadius, + sk_weapon_ar2_alt_fire_mass.GetFloat(), + flDuration, + pNPC ); + + variant_t var; + var.SetEntity(pBall); + pNPC->FireNamedOutput("OnThrowGrenade", var, pBall, pNPC); +#else CreateCombineBall( vecSrc, vecVelocity, flRadius, sk_weapon_ar2_alt_fire_mass.GetFloat(), flDuration, pNPC ); +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_bugbait.cpp b/mp/src/game/server/hl2/weapon_bugbait.cpp index 38c02239..612fe142 100644 --- a/mp/src/game/server/hl2/weapon_bugbait.cpp +++ b/mp/src/game/server/hl2/weapon_bugbait.cpp @@ -349,6 +349,14 @@ void CWeaponBugBait::ItemPostFrame( void ) if ( pOwner == NULL ) return; +#ifdef MAPBASE + if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) + { + WeaponIdle(); + return; + } +#endif + // See if we're cocked and ready to throw if ( m_bDrawBackFinished ) { diff --git a/mp/src/game/server/hl2/weapon_cguard.cpp b/mp/src/game/server/hl2/weapon_cguard.cpp index 572a90c5..4b8a67d4 100644 --- a/mp/src/game/server/hl2/weapon_cguard.cpp +++ b/mp/src/game/server/hl2/weapon_cguard.cpp @@ -86,6 +86,10 @@ void TE_ConcussiveExplosion( IRecipientFilter& filter, float delay, //Temp ent for the blast +#ifdef MAPBASE +#define SF_CONCUSSIVEBLAST_REPEATABLE 0x00000001 +#endif + class CConcussiveBlast : public CBaseEntity { DECLARE_DATADESC(); @@ -94,6 +98,13 @@ public: int m_spriteTexture; +#ifdef MAPBASE + float m_flDamage = 200; + float m_flRadius = 256; + float m_flMagnitude = 1.0; + string_t m_iszSoundName; +#endif + CConcussiveBlast( void ) {} //----------------------------------------------------------------------------- @@ -104,6 +115,11 @@ public: { m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" ); +#ifdef MAPBASE + if (m_iszSoundName != NULL_STRING) + PrecacheScriptSound(STRING(m_iszSoundName)); +#endif + BaseClass::Precache(); } @@ -150,10 +166,32 @@ public: ); //Do the radius damage +#ifdef MAPBASE + RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), m_flDamage, DMG_BLAST|DMG_DISSOLVE ), GetAbsOrigin(), m_flRadius, CLASS_NONE, NULL ); +#else RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), 200, DMG_BLAST|DMG_DISSOLVE ), GetAbsOrigin(), 256, CLASS_NONE, NULL ); +#endif +#ifdef MAPBASE + if (m_iszSoundName != NULL_STRING) + EmitSound(STRING(m_iszSoundName)); + + if (!HasSpawnFlags(SF_CONCUSSIVEBLAST_REPEATABLE)) +#endif UTIL_Remove( this ); } + +#ifdef MAPBASE + void InputExplode( inputdata_t &inputdata ) + { + Explode(m_flMagnitude); + } + + void InputExplodeWithMagnitude( inputdata_t &inputdata ) + { + Explode(inputdata.value.Int()); + } +#endif }; LINK_ENTITY_TO_CLASS( concussiveblast, CConcussiveBlast ); @@ -165,6 +203,16 @@ BEGIN_DATADESC( CConcussiveBlast ) // DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "damage" ), + DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ), + DEFINE_KEYFIELD( m_flMagnitude, FIELD_FLOAT, "magnitude" ), + DEFINE_KEYFIELD( m_iszSoundName, FIELD_SOUNDNAME, "soundname" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "ExplodeWithMagnitude", InputExplodeWithMagnitude ), +#endif + END_DATADESC() diff --git a/mp/src/game/server/hl2/weapon_crossbow.cpp b/mp/src/game/server/hl2/weapon_crossbow.cpp index badd50d8..b86b4c05 100644 --- a/mp/src/game/server/hl2/weapon_crossbow.cpp +++ b/mp/src/game/server/hl2/weapon_crossbow.cpp @@ -41,6 +41,10 @@ extern ConVar sk_plr_dmg_crossbow; extern ConVar sk_npc_dmg_crossbow; +#ifdef MAPBASE +ConVar weapon_crossbow_new_hit_locations( "weapon_crossbow_new_hit_locations", "1", FCVAR_NONE, "Toggles new crossbow knockback that properly pushes back the correct limbs." ); +#endif + void TE_StickyBolt( IRecipientFilter& filter, float delay, Vector vecDirection, const Vector *origin ); #define BOLT_SKIN_NORMAL 0 @@ -54,7 +58,11 @@ class CCrossbowBolt : public CBaseCombatCharacter DECLARE_CLASS( CCrossbowBolt, CBaseCombatCharacter ); public: +#ifdef MAPBASE + CCrossbowBolt(); +#else CCrossbowBolt() { }; +#endif ~CCrossbowBolt(); Class_T Classify( void ) { return CLASS_NONE; } @@ -66,7 +74,16 @@ public: void BoltTouch( CBaseEntity *pOther ); bool CreateVPhysics( void ); unsigned int PhysicsSolidMaskForEntity() const; +#ifdef MAPBASE + static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseCombatCharacter *pentOwner = NULL ); + + void InputSetDamage( inputdata_t &inputdata ); + float m_flDamage; + + virtual void SetDamage(float flDamage) { m_flDamage = flDamage; } +#else static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner = NULL ); +#endif protected: @@ -89,12 +106,23 @@ BEGIN_DATADESC( CCrossbowBolt ) DEFINE_FIELD( m_pGlowSprite, FIELD_EHANDLE ), //DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamage", InputSetDamage ), +#endif + END_DATADESC() IMPLEMENT_SERVERCLASS_ST( CCrossbowBolt, DT_CrossbowBolt ) END_SEND_TABLE() +#ifdef MAPBASE +CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseCombatCharacter *pentOwner ) +#else CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner ) +#endif { // Create a new entity with CCrossbowBolt private data CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName( "crossbow_bolt" ); @@ -102,10 +130,27 @@ CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle pBolt->SetAbsAngles( angAngles ); pBolt->Spawn(); pBolt->SetOwnerEntity( pentOwner ); +#ifdef MAPBASE + if (pentOwner && pentOwner->IsNPC()) + pBolt->m_flDamage = sk_npc_dmg_crossbow.GetFloat(); + //else + // pBolt->m_flDamage = sk_plr_dmg_crossbow.GetFloat(); +#endif return pBolt; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCrossbowBolt::CCrossbowBolt( void ) +{ + // Independent bolts without m_flDamage set need damage + m_flDamage = sk_plr_dmg_crossbow.GetFloat(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -194,6 +239,16 @@ void CCrossbowBolt::Precache( void ) PrecacheModel( "sprites/light_glow02_noz.vmt" ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrossbowBolt::InputSetDamage( inputdata_t &inputdata ) +{ + m_flDamage = inputdata.value.Float(); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) @@ -201,7 +256,19 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) ) { // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. +#ifdef MAPBASE + // But some physics objects that are also triggers (like weapons) shouldn't go through this check. + // + // Note: rpg_missile has the same code, except it properly accounts for weapons in a different way. + // This was discovered after I implemented this and both work fine, but if this ever causes problems, + // use rpg_missile's implementation: + // + // if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON ) + // + if ( pOther->GetMoveType() == MOVETYPE_NONE && (( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY )) ) +#else if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) ) +#endif return; } @@ -226,9 +293,45 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) } #endif//HL2_EPISODIC +#ifdef MAPBASE + if (weapon_crossbow_new_hit_locations.GetInt() > 0) + { + // A very experimental and weird way of getting a crossbow bolt to deal accurate knockback. + CBaseAnimating *pOtherAnimating = pOther->GetBaseAnimating(); + if (pOtherAnimating && pOtherAnimating->GetModelPtr() && pOtherAnimating->GetModelPtr()->numbones() > 1) + { + int iClosestBone = -1; + float flCurDistSqr = Square(128.0f); + matrix3x4_t bonetoworld; + Vector vecBonePos; + for (int i = 0; i < pOtherAnimating->GetModelPtr()->numbones(); i++) + { + pOtherAnimating->GetBoneTransform( i, bonetoworld ); + MatrixPosition( bonetoworld, vecBonePos ); + + float flDist = vecBonePos.DistToSqr(GetLocalOrigin()); + if (flDist < flCurDistSqr) + { + iClosestBone = i; + flCurDistSqr = flDist; + } + } + + if (iClosestBone != -1) + { + tr.physicsbone = pOtherAnimating->GetPhysicsBone(iClosestBone); + } + } + } +#endif + if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC() ) { +#ifdef MAPBASE + CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_flDamage, DMG_NEVERGIB ); +#else CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_NEVERGIB ); +#endif dmgInfo.AdjustPlayerDamageInflictedForSkillLevel(); CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f ); dmgInfo.SetDamagePosition( tr.endpos ); @@ -243,7 +346,11 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) } else { +#ifdef MAPBASE + CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_flDamage, DMG_BULLET | DMG_NEVERGIB ); +#else CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_BULLET | DMG_NEVERGIB ); +#endif CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f ); dmgInfo.SetDamagePosition( tr.endpos ); pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr ); @@ -434,17 +541,48 @@ public: virtual void Drop( const Vector &vecVelocity ); virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); virtual bool Reload( void ); +#ifdef MAPBASE + virtual void Reload_NPC( void ); +#endif virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + virtual void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); +#endif virtual bool SendWeaponAnim( int iActivity ); virtual bool IsWeaponZoomed() { return m_bInZoom; } bool ShouldDisplayHUDHint() { return true; } +#ifdef MAPBASE + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + + virtual int GetMinBurst() { return 1; } + virtual int GetMaxBurst() { return 1; } + + virtual float GetMinRestTime( void ) { return 3.0f; } // 1.5f + virtual float GetMaxRestTime( void ) { return 3.0f; } // 2.0f + + virtual float GetFireRate( void ) { return 5.0f; } + + virtual const Vector& GetBulletSpread( void ) + { + static Vector cone = VECTOR_CONE_15DEGREES; + if (!GetOwner() || !GetOwner()->IsNPC()) + return cone; + + static Vector NPCCone = VECTOR_CONE_5DEGREES; + + return NPCCone; + } +#endif DECLARE_SERVERCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE + DECLARE_ACTTABLE(); +#endif private: @@ -452,6 +590,9 @@ private: void SetSkin( int skinNum ); void CheckZoomToggle( void ); void FireBolt( void ); +#ifdef MAPBASE + void FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, Vector &vecShootDir ); +#endif void ToggleZoom( void ); // Various states for the crossbow's charger @@ -494,6 +635,62 @@ BEGIN_DATADESC( CWeaponCrossbow ) END_DATADESC() +#ifdef MAPBASE +acttable_t CWeaponCrossbow::m_acttable[] = +{ + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true }, + { ACT_RELOAD, ACT_RELOAD_SMG1, true }, + { ACT_IDLE, ACT_IDLE_SMG1, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true }, + + { ACT_WALK, ACT_WALK_RIFLE, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SMG1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SMG1, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG1_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, +}; + +IMPLEMENT_ACTTABLE(CWeaponCrossbow); +#endif + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- @@ -504,6 +701,13 @@ CWeaponCrossbow::CWeaponCrossbow( void ) m_bAltFiresUnderwater = true; m_bInZoom = false; m_bMustReload = false; + +#ifdef MAPBASE + m_fMinRange1 = 24; + m_fMaxRange1 = 5000; + m_fMinRange2 = 24; + m_fMaxRange2 = 5000; +#endif } #define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt" @@ -577,6 +781,18 @@ bool CWeaponCrossbow::Reload( void ) return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::Reload_NPC( void ) +{ + BaseClass::Reload_NPC(); + + m_nSkin = 0; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -699,6 +915,44 @@ void CWeaponCrossbow::FireBolt( void ) SetChargerState( CHARGER_STATE_DISCHARGE ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + Assert(pOwner); + + QAngle angAiming; + VectorAngles( vecShootDir, angAiming ); + + CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecShootOrigin, angAiming, pOwner ); + + if ( pOwner->GetWaterLevel() == 3 ) + { + pBolt->SetAbsVelocity( vecShootDir * BOLT_WATER_VELOCITY ); + } + else + { + pBolt->SetAbsVelocity( vecShootDir * BOLT_AIR_VELOCITY ); + } + + m_iClip1--; + + m_nSkin = 1; + + WeaponSound( SINGLE_NPC ); + WeaponSound( SPECIAL2 ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 ); + + m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 2.5f; + + SetSkin( BOLT_SKIN_GLOW ); + SetChargerState( CHARGER_STATE_DISCHARGE ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. @@ -761,7 +1015,11 @@ void CWeaponCrossbow::CreateChargerEffects( void ) { CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); +#ifdef MAPBASE + if ( m_hChargerSprite != NULL || pOwner == NULL ) +#else if ( m_hChargerSprite != NULL ) +#endif return; m_hChargerSprite = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false ); @@ -933,12 +1191,46 @@ void CWeaponCrossbow::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombat SetChargerState( CHARGER_STATE_READY ); break; +#ifdef MAPBASE + case EVENT_WEAPON_SMG1: + { + CAI_BaseNPC *pNPC = pOperator->MyNPCPointer(); + Assert(pNPC); + + Vector vecSrc = pNPC->Weapon_ShootPosition(); + Vector vecAiming = pNPC->GetActualShootTrajectory( vecSrc ); + + FireNPCBolt( pNPC, vecSrc, vecAiming ); + //m_bMustReload = true; + } + break; +#endif + default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); break; } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCBolt( pOperator->MyNPCPointer(), vecShootOrigin, vecShootDir ); + + //m_bMustReload = true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Set the desired activity for the weapon and its viewmodel counterpart // Input : iActivity - activity to play diff --git a/mp/src/game/server/hl2/weapon_flaregun.cpp b/mp/src/game/server/hl2/weapon_flaregun.cpp index d3fd6b15..110f5c66 100644 --- a/mp/src/game/server/hl2/weapon_flaregun.cpp +++ b/mp/src/game/server/hl2/weapon_flaregun.cpp @@ -19,6 +19,7 @@ #include "tier0/memdbgon.h" +#ifndef MAPBASE /******************************************************************** NOTE: if you are looking at this file becase you would like flares to be considered as fires (and thereby trigger gas traps), be aware @@ -37,6 +38,13 @@ For some partial work towards this end, see changelist 192474. ********************************************************************/ +#else +// ================================================================ // +// I've fixed this...more or less. env_firesensor detects flares now. +// I tried to integrate it with the greater fire system, but I found that too difficult. +// I probably didn't try hard enough. You could fix this yourself if you think it's a big issue. +// ================================================================ // +#endif #define FLARE_LAUNCH_SPEED 1500 @@ -120,6 +128,21 @@ void KillFlare( CBaseEntity *pOwnerEntity, CBaseEntity *pEntity, float flKillTim } } +#ifdef MAPBASE +// For prop_flare debugging. +float GetEnvFlareLifetime( CBaseEntity *pEntity ) +{ + CFlare *pFlare = static_cast< CFlare *>( pEntity ); + + if ( pFlare ) + { + return pFlare->m_flTimeBurnOut - gpGlobals->curtime; + } + + return 0.0f; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_physcannon.cpp b/mp/src/game/server/hl2/weapon_physcannon.cpp index 7e49b7f4..87fddf4e 100644 --- a/mp/src/game/server/hl2/weapon_physcannon.cpp +++ b/mp/src/game/server/hl2/weapon_physcannon.cpp @@ -40,6 +40,9 @@ #include "ai_interactions.h" #include "rumble_shared.h" #include "gamestats.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#endif // NVNT haptic utils #include "haptics/haptic_utils.h" @@ -141,6 +144,10 @@ public: // Handle grate entities differently if ( HasContentsGrate( pEntity ) ) { +#ifdef MAPBASE + if (pEntity->CanBePickedUpByPhyscannon()) + return true; +#else // See if it's a grabbable physics prop CPhysicsProp *pPhysProp = dynamic_cast(pEntity); if ( pPhysProp != NULL ) @@ -166,6 +173,7 @@ public: // Somehow had a classname that didn't match the class! Assert(0); } +#endif // Don't bother with any other sort of grated entity return false; @@ -438,10 +446,12 @@ static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out ) // Purpose: //----------------------------------------------------------------------------- // derive from this so we can add save/load data to it +#ifndef MAPBASE // Moved to weapon_physcannon.h for point_physics_control struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t { DECLARE_SIMPLE_DATADESC(); }; +#endif BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t ) @@ -808,7 +818,14 @@ void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, CPhysicsProp *pProp = dynamic_cast(pEntity); if ( pProp ) { +#ifdef MAPBASE + // If the prop has custom carry angles, don't override them + // (regular PreferredCarryAngles() code should cover it) + if (!pProp->m_bUsesCustomCarryAngles) + m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles ); +#else m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles ); +#endif m_flDistanceOffset = pProp->GetCarryDistanceOffset(); } else @@ -1146,7 +1163,11 @@ void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller Vector vecLaunch; m_pPlayer->EyeVectors( &vecLaunch ); // JAY: Scale this with mass because some small objects really go flying +#ifdef MAPBASE + float massFactor = pPhys ? clamp( pPhys->GetMass(), 0.5, 15 ) : 7.5; +#else float massFactor = clamp( pPhys->GetMass(), 0.5, 15 ); +#endif massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 ); vecLaunch *= player_throwforce.GetFloat() * massFactor; @@ -2084,6 +2105,10 @@ bool CWeaponPhysCannon::EntityAllowsPunts( CBaseEntity *pEntity ) if ( pEntity->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) ) { +#ifdef MAPBASE + if (pEntity->IsBaseCombatWeapon() || pEntity->IsCombatItem()) + return false; +#else CBaseCombatWeapon *pWeapon = dynamic_cast(pEntity); if ( pWeapon != NULL ) @@ -2093,6 +2118,7 @@ bool CWeaponPhysCannon::EntityAllowsPunts( CBaseEntity *pEntity ) return false; } } +#endif } return true; @@ -2448,7 +2474,11 @@ bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosit } #if defined(HL2_DLL) +#ifdef MAPBASE + if( physcannon_right_turrets.GetBool() && EntIsClass(pObject, gm_isz_class_FloorTurret) ) +#else if( physcannon_right_turrets.GetBool() && pObject->ClassMatches("npc_turret_floor") ) +#endif { // We just picked up a turret. Is it already upright? Vector vecUp; @@ -3287,6 +3317,15 @@ void CWeaponPhysCannon::ItemPostFrame() return; } +#ifdef MAPBASE + if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) + { + m_nAttack2Debounce = 0; + WeaponIdle(); + return; + } +#endif + //Check for object in pickup range if ( m_bActive == false ) { @@ -3459,6 +3498,12 @@ bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget ) if ( pOwner && pOwner->GetGroundEntity() == pTarget ) return false; +#ifdef MAPBASE + // The gravity gun can't pick up vehicles. + if ( pTarget->GetServerVehicle() ) + return false; +#endif + if ( !IsMegaPhysCannon() ) { if ( pTarget->VPhysicsIsFlesh( ) ) diff --git a/mp/src/game/server/hl2/weapon_physcannon.h b/mp/src/game/server/hl2/weapon_physcannon.h index d7ce48ee..01356d26 100644 --- a/mp/src/game/server/hl2/weapon_physcannon.h +++ b/mp/src/game/server/hl2/weapon_physcannon.h @@ -30,4 +30,11 @@ CBaseEntity *GetPlayerHeldEntity( CBasePlayer *pPlayer ); bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject ); +#ifdef MAPBASE // Moved here so point_physics_control can access, datadesc is still in weapon_physcannon.cpp +struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t +{ + DECLARE_SIMPLE_DATADESC(); +}; +#endif + #endif // WEAPON_PHYSCANNON_H diff --git a/mp/src/game/server/hl2/weapon_pistol.cpp b/mp/src/game/server/hl2/weapon_pistol.cpp index d0a25412..80bb5090 100644 --- a/mp/src/game/server/hl2/weapon_pistol.cpp +++ b/mp/src/game/server/hl2/weapon_pistol.cpp @@ -52,6 +52,10 @@ public: void AddViewKick( void ); void DryFire( void ); void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); +#endif void UpdatePenaltyTime( void ); @@ -193,12 +197,16 @@ void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCh vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); +#ifdef MAPBASE + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +#else CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); WeaponSound( SINGLE_NPC ); pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 ); pOperator->DoMuzzleFlash(); m_iClip1 = m_iClip1 - 1; +#endif } break; default: @@ -207,6 +215,36 @@ void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCh } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPistol::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + WeaponSound( SINGLE_NPC ); + pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 ); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Some things need this. (e.g. the new Force(X)Fire inputs or blindfire actbusy) +//----------------------------------------------------------------------------- +void CWeaponPistol::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_rpg.cpp b/mp/src/game/server/hl2/weapon_rpg.cpp index e6278593..3ae41e28 100644 --- a/mp/src/game/server/hl2/weapon_rpg.cpp +++ b/mp/src/game/server/hl2/weapon_rpg.cpp @@ -43,6 +43,10 @@ static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15"); ConVar rpg_missle_use_custom_detonators( "rpg_missle_use_custom_detonators", "1" ); +#ifdef MAPBASE +ConVar weapon_rpg_use_old_behavior( "weapon_rpg_use_old_behavior", "0" ); +ConVar weapon_rpg_fire_rate( "weapon_rpg_fire_rate", "4.0" ); +#endif #define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat() @@ -2039,7 +2043,20 @@ bool CWeaponRPG::WeaponLOSCondition( const Vector &ownerPos, const Vector &targe Vector vecShootDir = npcOwner->GetActualShootTrajectory( vecMuzzle ); // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out. +#ifdef MAPBASE + // Oh, and don't collide with ourselves or our owner. That would be stupid. + if (!weapon_rpg_use_old_behavior.GetBool()) + { + CTraceFilterSkipTwoEntities pTraceFilter( this, GetOwner(), COLLISION_GROUP_NONE ); + AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, &pTraceFilter, &tr ); + } + else + { +#endif AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); +#ifdef MAPBASE + } +#endif if( tr.fraction != 1.0f ) bResult = false; @@ -2089,7 +2106,20 @@ int CWeaponRPG::WeaponRangeAttack1Condition( float flDot, float flDist ) Vector vecShootDir = pOwner->GetActualShootTrajectory( vecMuzzle ); // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out. +#ifdef MAPBASE + // Oh, and don't collide with ourselves or our owner. That would be stupid. + if (!weapon_rpg_use_old_behavior.GetBool()) + { + CTraceFilterSkipTwoEntities pTraceFilter( this, GetOwner(), COLLISION_GROUP_NONE ); + AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, &pTraceFilter, &tr ); + } + else + { +#endif AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); +#ifdef MAPBASE + } +#endif if( tr.fraction != 1.0 ) { @@ -2213,6 +2243,23 @@ void CWeaponRPG::UpdateLaserEffects( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponRPG::SupportsBackupActivity(Activity activity) +{ + // NPCs shouldn't use their SMG activities to aim and fire RPGs while running. + if (activity == ACT_RUN_AIM || + activity == ACT_WALK_AIM || + activity == ACT_RUN_CROUCH_AIM || + activity == ACT_WALK_CROUCH_AIM) + return false; + + return true; +} +#endif + //============================================================================= // Laser Dot //============================================================================= diff --git a/mp/src/game/server/hl2/weapon_rpg.h b/mp/src/game/server/hl2/weapon_rpg.h index b8ca99af..3c9dae5f 100644 --- a/mp/src/game/server/hl2/weapon_rpg.h +++ b/mp/src/game/server/hl2/weapon_rpg.h @@ -165,6 +165,9 @@ private: //----------------------------------------------------------------------------- CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle ); +#ifdef MAPBASE +extern ConVar weapon_rpg_fire_rate; +#endif //----------------------------------------------------------------------------- // RPG @@ -182,7 +185,11 @@ public: void Precache( void ); void PrimaryAttack( void ); +#ifdef MAPBASE + virtual float GetFireRate( void ) { return weapon_rpg_fire_rate.GetFloat(); }; +#else virtual float GetFireRate( void ) { return 1; }; +#endif void ItemPostFrame( void ); void Activate( void ); @@ -196,6 +203,10 @@ public: virtual void Drop( const Vector &vecVelocity ); +#ifdef MAPBASE + bool SupportsBackupActivity(Activity activity); +#endif + int GetMinBurst() { return 1; } int GetMaxBurst() { return 1; } float GetMinRestTime() { return 4.0; } diff --git a/mp/src/game/server/hl2/weapon_shotgun.cpp b/mp/src/game/server/hl2/weapon_shotgun.cpp index a832a3d6..fb9ec215 100644 --- a/mp/src/game/server/hl2/weapon_shotgun.cpp +++ b/mp/src/game/server/hl2/weapon_shotgun.cpp @@ -24,6 +24,10 @@ extern ConVar sk_auto_reload_time; extern ConVar sk_plr_num_shotgun_pellets; +#ifdef MAPBASE +extern ConVar sk_plr_num_shotgun_pellets_double; +extern ConVar sk_npc_num_shotgun_pellets; +#endif class CWeaponShotgun : public CBaseHLCombatWeapon { @@ -183,7 +187,11 @@ void CWeaponShotgun::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); } +#ifdef MAPBASE + pOperator->FireBullets( sk_npc_num_shotgun_pellets.GetInt(), vecShootOrigin, vecShootDir, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 ); +#else pOperator->FireBullets( 8, vecShootOrigin, vecShootDir, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 ); +#endif } //----------------------------------------------------------------------------- @@ -526,7 +534,11 @@ void CWeaponShotgun::SecondaryAttack( void ) Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT ); // Fire the bullets +#ifdef MAPBASE + pPlayer->FireBullets( sk_plr_num_shotgun_pellets_double.GetInt(), vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, false, false ); +#else pPlayer->FireBullets( 12, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, false, false ); +#endif pPlayer->ViewPunch( QAngle(random->RandomFloat( -5, 5 ),0,0) ); pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 ); diff --git a/mp/src/game/server/hl2/weapon_smg1.cpp b/mp/src/game/server/hl2/weapon_smg1.cpp index cc9934a4..97b0e89e 100644 --- a/mp/src/game/server/hl2/weapon_smg1.cpp +++ b/mp/src/game/server/hl2/weapon_smg1.cpp @@ -22,6 +22,9 @@ #include "tier0/memdbgon.h" extern ConVar sk_plr_dmg_smg1_grenade; +#ifdef MAPBASE +extern ConVar sk_npc_dmg_smg1_grenade; +#endif class CWeaponSMG1 : public CHLSelectFireMachineGun { @@ -135,6 +138,19 @@ acttable_t CWeaponSMG1::m_acttable[] = IMPLEMENT_ACTTABLE(CWeaponSMG1); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the SMG1's activity table. +acttable_t *GetSMG1Acttable() +{ + return CWeaponSMG1::m_acttable; +} + +int GetSMG1ActtableCount() +{ + return ARRAYSIZE(CWeaponSMG1::m_acttable); +} +#endif + //========================================================= CWeaponSMG1::CWeaponSMG1( ) { @@ -202,6 +218,10 @@ void CWeaponSMG1::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool b FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); } +#ifdef MAPBASE +float GetCurrentGravity( void ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -228,6 +248,56 @@ void CWeaponSMG1::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatChar } break; +#ifdef MAPBASE + case EVENT_WEAPON_AR2_ALTFIRE: + { + WeaponSound( WPN_DOUBLE ); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + if (!npc) + return; + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecTarget = npc->GetAltFireTarget(); + Vector vecThrow; + if (vecTarget == vec3_origin) + AngleVectors( npc->EyeAngles(), &vecThrow ); // Not much else to do, unfortunately + else + { + // Because this is happening right now, we can't "VecCheckThrow" and can only "VecDoThrow", you know what I mean? + // ...Anyway, this borrows from that so we'll never return vec3_origin. + //vecThrow = VecCheckThrow( this, vecShootOrigin, vecTarget, 600.0, 0.5 ); + + vecThrow = (vecTarget - vecShootOrigin); + + // throw at a constant time + float time = vecThrow.Length() / 600.0; + vecThrow = vecThrow * (1.0 / time); + + // adjust upward toss to compensate for gravity loss + vecThrow.z += (GetCurrentGravity() * 0.5) * time * 0.5; + } + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + + pGrenade->SetThrower( npc ); + + pGrenade->SetGravity(0.5); // lower gravity since grenade is aerodynamic and engine doesn't know it. + + pGrenade->SetDamage(sk_npc_dmg_smg1_grenade.GetFloat()); + + variant_t var; + var.SetEntity(pGrenade); + npc->FireNamedOutput("OnThrowGrenade", var, pGrenade, npc); + } + break; +#else /*//FIXME: Re-enable case EVENT_WEAPON_AR2_GRENADE: { @@ -254,6 +324,7 @@ void CWeaponSMG1::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatChar } break; */ +#endif default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); diff --git a/mp/src/game/server/hl2/weapon_stunstick.h b/mp/src/game/server/hl2/weapon_stunstick.h index 901b4721..9d3942c8 100644 --- a/mp/src/game/server/hl2/weapon_stunstick.h +++ b/mp/src/game/server/hl2/weapon_stunstick.h @@ -4,6 +4,13 @@ // //=============================================================================// +#ifdef MAPBASE + +// Redirect to HL2:DM's stunstick. +// It has NPC support now. +#include "hl2mp/weapon_stunstick.h" + +#else #ifndef WEAPON_STUNSTICK_H #define WEAPON_STUNSTICK_H #ifdef _WIN32 @@ -13,7 +20,12 @@ #include "basebludgeonweapon.h" #define STUNSTICK_RANGE 75.0f +#ifdef MAPBASE +// MP refire +#define STUNSTICK_REFIRE 0.8f +#else #define STUNSTICK_REFIRE 0.6f +#endif class CWeaponStunStick : public CBaseHLBludgeonWeapon { @@ -56,3 +68,4 @@ private: }; #endif // WEAPON_STUNSTICK_H +#endif diff --git a/mp/src/game/server/hl2mp/grenade_tripmine.cpp b/mp/src/game/server/hl2mp/grenade_tripmine.cpp index e14a929d..39918869 100644 --- a/mp/src/game/server/hl2mp/grenade_tripmine.cpp +++ b/mp/src/game/server/hl2mp/grenade_tripmine.cpp @@ -34,6 +34,18 @@ BEGIN_DATADESC( CTripmineGrenade ) DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ), DEFINE_FIELD( m_posOwner, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_angleOwner, FIELD_VECTOR ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flPowerUpTime, FIELD_FLOAT, "PowerUpTime" ), + DEFINE_FIELD( m_hAttacker, FIELD_EHANDLE ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetOwner", InputSetOwner ), + + // Outputs + DEFINE_OUTPUT( m_OnExplode, "OnExplode" ), +#endif // Function Pointers DEFINE_THINKFUNC( WarningThink ), @@ -49,6 +61,10 @@ CTripmineGrenade::CTripmineGrenade() m_vecEnd.Init(); m_posOwner.Init(); m_angleOwner.Init(); + +#ifdef MAPBASE + m_flPowerUpTime = 2.0; +#endif } void CTripmineGrenade::Spawn( void ) @@ -65,18 +81,46 @@ void CTripmineGrenade::Spawn( void ) SetCycle( 0.0f ); m_nBody = 3; +#ifdef MAPBASE + if (m_flDamage == 0) + m_flDamage = sk_plr_dmg_tripmine.GetFloat(); + if (m_DmgRadius == 0) + m_DmgRadius = sk_tripmine_radius.GetFloat(); +#else m_flDamage = sk_plr_dmg_tripmine.GetFloat(); m_DmgRadius = sk_tripmine_radius.GetFloat(); +#endif ResetSequenceInfo( ); m_flPlaybackRate = 0; UTIL_SetSize(this, Vector( -4, -4, -2), Vector(4, 4, 2)); +#ifdef MAPBASE + if (!HasSpawnFlags(SF_TRIPMINE_START_INACTIVE)) + { + if (m_flPowerUpTime > 0) + { + m_flPowerUp = gpGlobals->curtime + m_flPowerUpTime; + + SetThink( &CTripmineGrenade::PowerupThink ); + SetNextThink( gpGlobals->curtime + 0.2 ); + } + else + { + MakeBeam( ); + RemoveSolidFlags( FSOLID_NOT_SOLID ); + m_bIsLive = true; + + //EmitSound( "TripmineGrenade.Activate" ); + } + } +#else m_flPowerUp = gpGlobals->curtime + 2.0; SetThink( &CTripmineGrenade::PowerupThink ); SetNextThink( gpGlobals->curtime + 0.2 ); +#endif m_takedamage = DAMAGE_YES; @@ -222,7 +266,11 @@ void CTripmineGrenade::BeamBreakThink( void ) if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001) { m_iHealth = 0; +#ifdef MAPBASE + Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, pEntity, 100, GIB_NORMAL ) ); +#else Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, this, 100, GIB_NORMAL ) ); +#endif return; } @@ -254,6 +302,10 @@ void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info ) { m_takedamage = DAMAGE_NO; +#ifdef MAPBASE + m_hAttacker = info.GetAttacker(); +#endif + SetThink( &CTripmineGrenade::DelayDeathThink ); SetNextThink( gpGlobals->curtime + 0.25 ); @@ -271,6 +323,48 @@ void CTripmineGrenade::DelayDeathThink( void ) ExplosionCreate( GetAbsOrigin() + m_vecDir * 8, GetAbsAngles(), m_hOwner, GetDamage(), 200, SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); +#ifdef MAPBASE + m_OnExplode.FireOutput(m_hAttacker.Get(), this); +#endif + UTIL_Remove( this ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CTripmineGrenade::InputActivate( inputdata_t &inputdata ) +{ + if (m_flPowerUpTime > 0) + { + m_flPowerUp = gpGlobals->curtime + m_flPowerUpTime; + + SetThink( &CTripmineGrenade::PowerupThink ); + SetNextThink( gpGlobals->curtime + 0.2 ); + } + else + { + MakeBeam( ); + RemoveSolidFlags( FSOLID_NOT_SOLID ); + m_bIsLive = true; + + //EmitSound( "TripmineGrenade.Activate" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CTripmineGrenade::InputDeactivate( inputdata_t &inputdata ) +{ + KillBeam( ); + //AddSolidFlags( FSOLID_NOT_SOLID ); + m_bIsLive = false; +} +#endif + diff --git a/mp/src/game/server/hl2mp/grenade_tripmine.h b/mp/src/game/server/hl2mp/grenade_tripmine.h index e1887096..9ec9e726 100644 --- a/mp/src/game/server/hl2mp/grenade_tripmine.h +++ b/mp/src/game/server/hl2mp/grenade_tripmine.h @@ -15,6 +15,9 @@ class CBeam; +#ifdef MAPBASE +#define SF_TRIPMINE_START_INACTIVE (1 << 0) +#endif class CTripmineGrenade : public CBaseGrenade { @@ -37,9 +40,24 @@ public: void MakeBeam( void ); void KillBeam( void ); +#ifdef MAPBASE + void PowerUp(); + + void InputActivate( inputdata_t &inputdata ); + void InputDeactivate( inputdata_t &inputdata ); + void InputSetOwner( inputdata_t &inputdata ) { m_hOwner = inputdata.value.Entity(); } + + COutputEvent m_OnExplode; +#endif + public: EHANDLE m_hOwner; +#ifdef MAPBASE + float m_flPowerUpTime; + EHANDLE m_hAttacker; +#endif + private: float m_flPowerUp; Vector m_vecDir; diff --git a/mp/src/game/server/item_world.cpp b/mp/src/game/server/item_world.cpp index e4233172..6f124280 100644 --- a/mp/src/game/server/item_world.cpp +++ b/mp/src/game/server/item_world.cpp @@ -103,6 +103,14 @@ BEGIN_DATADESC( CItem ) DEFINE_THINKFUNC( FallThink ), #endif +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnablePlayerPickup", InputEnablePlayerPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisablePlayerPickup", InputDisablePlayerPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableNPCPickup", InputEnableNPCPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableNPCPickup", InputDisableNPCPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "BreakConstraint", InputBreakConstraint ), +#endif + // Outputs DEFINE_OUTPUT( m_OnPlayerTouch, "OnPlayerTouch" ), DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ), @@ -344,6 +352,19 @@ bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer ) if ( pItem == NULL || pPlayer == NULL ) return false; +#ifdef MAPBASE + // Weapons go through this, but this is identical to SF_WEAPON_NO_PLAYER_PICKUP and that would be a convenient coincidence, + // but OnCacheInteraction worked with "No player pickup" before and SF_WEAPON_NO_PLAYER_PICKUP is often checked after this, + // so we have to make sure we're not dealing with a weapon for this check after all. + if (pItem->HasSpawnFlags(SF_ITEM_NO_PLAYER_PICKUP) && !pItem->IsBaseCombatWeapon()) + return false; + + // Fortunately, unlike the above code, this flag is identical in between weapons and items + // and can safely be used without identifying the entity. + if (pItem->HasSpawnFlags(SF_ITEM_ALWAYS_TOUCHABLE)) + return true; +#endif + // For now, always allow a vehicle riding player to pick up things they're driving over if ( pPlayer->IsInAVehicle() ) return true; @@ -545,3 +566,49 @@ void CItem::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason ) // Restore the pickup box to the original CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT ); } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItem::InputEnablePlayerPickup( inputdata_t &inputdata ) +{ + RemoveSpawnFlags(SF_ITEM_NO_PLAYER_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItem::InputDisablePlayerPickup( inputdata_t &inputdata ) +{ + AddSpawnFlags(SF_ITEM_NO_PLAYER_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItem::InputEnableNPCPickup( inputdata_t &inputdata ) +{ + RemoveSpawnFlags(SF_ITEM_NO_NPC_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItem::InputDisableNPCPickup( inputdata_t &inputdata ) +{ + AddSpawnFlags(SF_ITEM_NO_NPC_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItem::InputBreakConstraint( inputdata_t &inputdata ) +{ + if ( m_pConstraint != NULL ) + { + physenv->DestroyConstraint( m_pConstraint ); + m_pConstraint = NULL; + } +} +#endif diff --git a/mp/src/game/server/items.h b/mp/src/game/server/items.h index bc1bb77e..ed40fa59 100644 --- a/mp/src/game/server/items.h +++ b/mp/src/game/server/items.h @@ -36,6 +36,15 @@ #define SIZE_AMMO_AR2_ALTFIRE 1 #define SF_ITEM_START_CONSTRAINED 0x00000001 +#ifdef MAPBASE +// Copied from CBaseCombatWeapon's flags, including any additions we made to those. +// I really, REALLY hope no item uses their own spawnflags either. +#define SF_ITEM_NO_PLAYER_PICKUP (1<<1) +#define SF_ITEM_NO_PHYSCANNON_PUNT (1<<2) +#define SF_ITEM_NO_NPC_PICKUP (1<<3) + +#define SF_ITEM_ALWAYS_TOUCHABLE (1<<6) // This needs to stay synced with the weapon spawnflag +#endif class CItem : public CBaseAnimating, public CDefaultPlayerPickupVPhysics @@ -79,6 +88,18 @@ public: float m_flNextResetCheckTime; #endif +#ifdef MAPBASE + // This is in CBaseEntity, but I can't find a use for it anywhere. + // Must not have been fully implemented. Please remove this if it turns out to be something important. + virtual bool IsCombatItem() { return true; } + + void InputEnablePlayerPickup( inputdata_t &inputdata ); + void InputDisablePlayerPickup( inputdata_t &inputdata ); + void InputEnableNPCPickup( inputdata_t &inputdata ); + void InputDisableNPCPickup( inputdata_t &inputdata ); + void InputBreakConstraint( inputdata_t &inputdata ); +#endif + DECLARE_DATADESC(); protected: virtual void ComeToRest( void ); diff --git a/mp/src/game/server/lightglow.cpp b/mp/src/game/server/lightglow.cpp index 59d0505d..d941f033 100644 --- a/mp/src/game/server/lightglow.cpp +++ b/mp/src/game/server/lightglow.cpp @@ -33,6 +33,10 @@ public: virtual int UpdateTransmitState( void ); void InputColor(inputdata_t &data); +#ifdef MAPBASE + void InputEnable( inputdata_t &data ) { m_bDisabled = false; } + void InputDisable( inputdata_t &data ) { m_bDisabled = true; } +#endif public: CNetworkVar( int, m_nHorizontalSize ); @@ -43,6 +47,10 @@ public: CNetworkVar( float, m_flGlowProxySize ); CNetworkVar( float, m_flHDRColorScale ); + +#ifdef MAPBASE + CNetworkVar( bool, m_bDisabled ); +#endif }; extern void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); @@ -60,6 +68,9 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CLightGlow, DT_LightGlow ) SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)), SendPropFloat( SENDINFO(m_flGlowProxySize ), 6, SPROP_ROUNDUP, 0.0f, 64.0f ), SendPropFloat( SENDINFO_NAME( m_flHDRColorScale, HDRColorScale ), 0, SPROP_NOSCALE, 0.0f, 100.0f ), +#ifdef MAPBASE + SendPropBool( SENDINFO( m_bDisabled ) ), +#endif END_SEND_TABLE() LINK_ENTITY_TO_CLASS( env_lightglow, CLightGlow ); @@ -73,6 +84,11 @@ BEGIN_DATADESC( CLightGlow ) DEFINE_KEYFIELD( m_nOuterMaxDist, FIELD_INTEGER, "OuterMaxDist" ), DEFINE_KEYFIELD( m_flGlowProxySize, FIELD_FLOAT, "GlowProxySize" ), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#endif DEFINE_INPUTFUNC( FIELD_COLOR32, "Color", InputColor ), END_DATADESC() diff --git a/mp/src/game/server/logic_measure_movement.cpp b/mp/src/game/server/logic_measure_movement.cpp index bf074bd0..06c3da45 100644 --- a/mp/src/game/server/logic_measure_movement.cpp +++ b/mp/src/game/server/logic_measure_movement.cpp @@ -7,10 +7,31 @@ #include "cbase.h" #include "baseentity.h" +#ifdef MAPBASE +#include "filters.h" +#include "ai_basenpc.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +// These spawnflags were originally only on logic_measure_direction. +#define SF_LOGIC_MEASURE_MOVEMENT_IGNORE_X ( 1 << 0 ) +#define SF_LOGIC_MEASURE_MOVEMENT_IGNORE_Y ( 1 << 1 ) +#define SF_LOGIC_MEASURE_MOVEMENT_IGNORE_Z ( 1 << 2 ) + +// Uses the "Ignore X/Y/Z" flags for the origin instead of the angles. +// logic_measure_direction uses this flag to control trace direction. +#define SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN ( 1 << 3 ) + +// Uses "Teleport" instead of "SetAbsOrigin" for smoother movement +#define SF_LOGIC_MEASURE_MOVEMENT_TELEPORT ( 1 << 4 ) + +// Specifically refuse to set the target's angles, rather than just turning them to 0 +#define SF_LOGIC_MEASURE_MOVEMENT_DONT_SET_ANGLES ( 1 << 5 ) +#endif + //----------------------------------------------------------------------------- // This will measure the movement of a target entity and move // another entity to match the movement of the first. @@ -23,7 +44,11 @@ class CLogicMeasureMovement : public CLogicalEntity public: virtual void Activate(); +#ifdef MAPBASE +public: +#else private: +#endif void SetMeasureTarget( const char *pName ); void SetMeasureReference( const char *pName ); void SetTarget( const char *pName ); @@ -37,13 +62,30 @@ private: void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + // Allows for derived class trickery + void MeasureThink(); //{ DoMeasure(); } + + // Allows for InputGetPosition(), etc. + virtual void DoMeasure(Vector &vecOrigin, QAngle &angAngles); + void HandleIgnoreFlags( float *vec ); + + void InputSetMeasureAttachment( inputdata_t &inputdata ); + void InputSetMeasureType( inputdata_t &inputdata ) { m_nMeasureType = inputdata.value.Int(); } + void InputGetPosition( inputdata_t &inputdata ); +#else void MeasureThink(); private: +#endif enum { MEASURE_POSITION = 0, MEASURE_EYE_POSITION, +#ifdef MAPBASE + MEASURE_ATTACHMENT, + //MEASURE_BARREL_POSITION, +#endif }; string_t m_strMeasureTarget; @@ -55,6 +97,16 @@ private: EHANDLE m_hTarget; EHANDLE m_hTargetReference; +#ifdef MAPBASE + string_t m_strAttachment; + int m_iAttachment; + + bool m_bOutputPosition; + + COutputVector m_OutPosition; + COutputVector m_OutAngles; +#endif + float m_flScale; int m_nMeasureType; }; @@ -70,6 +122,17 @@ BEGIN_DATADESC( CLogicMeasureMovement ) DEFINE_KEYFIELD( m_strTargetReference, FIELD_STRING, "TargetReference" ), DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "TargetScale" ), DEFINE_KEYFIELD( m_nMeasureType, FIELD_INTEGER, "MeasureType" ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMeasureType", InputSetMeasureType ), + + DEFINE_KEYFIELD( m_strAttachment, FIELD_STRING, "MeasureAttachment" ), + DEFINE_FIELD( m_iAttachment, FIELD_EHANDLE ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetMeasureAttachment", InputSetMeasureAttachment ), + + DEFINE_INPUT( m_bOutputPosition, FIELD_BOOLEAN, "ShouldOutputPosition" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "GetPosition", InputGetPosition ), +#endif DEFINE_FIELD( m_hMeasureTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_hMeasureReference, FIELD_EHANDLE ), @@ -79,12 +142,20 @@ BEGIN_DATADESC( CLogicMeasureMovement ) DEFINE_INPUTFUNC( FIELD_STRING, "SetMeasureTarget", InputSetMeasureTarget ), DEFINE_INPUTFUNC( FIELD_STRING, "SetMeasureReference", InputSetMeasureReference ), DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "Target", InputSetTarget ), // For legacy support...even though that name was broken before. +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetTargetReference", InputSetTargetReference ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTargetScale", InputSetTargetScale ), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OutPosition, "OutPosition" ), + DEFINE_OUTPUT( m_OutAngles, "OutAngles" ), +#endif + DEFINE_THINKFUNC( MeasureThink ), END_DATADESC() @@ -112,40 +183,75 @@ void CLogicMeasureMovement::Activate() //----------------------------------------------------------------------------- void CLogicMeasureMovement::SetMeasureTarget( const char *pName ) { +#ifdef MAPBASE + m_hMeasureTarget = gEntList.FindEntityByName( NULL, pName, this ); +#else m_hMeasureTarget = gEntList.FindEntityByName( NULL, pName ); +#endif if ( !m_hMeasureTarget ) { if ( Q_strnicmp( STRING(m_strMeasureTarget), "!player", 8 ) ) { +#ifdef MAPBASE + Warning( "%s: Unable to find measure target entity %s\n", GetDebugName(), pName ); +#else Warning("logic_measure_movement: Unable to find measure target entity %s\n", pName ); +#endif } } +#ifdef MAPBASE + m_iAttachment = 0; +#endif } void CLogicMeasureMovement::SetMeasureReference( const char *pName ) { +#ifdef MAPBASE + m_hMeasureReference = gEntList.FindEntityByName( NULL, pName, this ); +#else m_hMeasureReference = gEntList.FindEntityByName( NULL, pName ); +#endif if ( !m_hMeasureReference ) { +#ifdef MAPBASE + Warning( "%s: Unable to find measure reference entity %s\n", GetDebugName(), pName ); +#else Warning("logic_measure_movement: Unable to find measure reference entity %s\n", pName ); +#endif } } void CLogicMeasureMovement::SetTarget( const char *pName ) { +#ifdef MAPBASE + m_hTarget = gEntList.FindEntityByName( NULL, pName, this ); +#else m_hTarget = gEntList.FindEntityByName( NULL, pName ); +#endif if ( !m_hTarget ) { +#ifdef MAPBASE + Warning( "%s: Unable to find movement target entity %s\n", GetDebugName(), pName ); +#else Warning("logic_measure_movement: Unable to find movement target entity %s\n", pName ); +#endif } } void CLogicMeasureMovement::SetTargetReference( const char *pName ) { +#ifdef MAPBASE + m_hTargetReference = gEntList.FindEntityByName( NULL, pName, this ); +#else m_hTargetReference = gEntList.FindEntityByName( NULL, pName ); +#endif if ( !m_hTargetReference ) { +#ifdef MAPBASE + Warning( "%s: Unable to find movement reference entity %s\n", GetDebugName(), pName ); +#else Warning("logic_measure_movement: Unable to find movement reference entity %s\n", pName ); +#endif } } @@ -165,6 +271,29 @@ void CLogicMeasureMovement::MeasureThink( ) // Make sure all entities are valid if ( m_hMeasureTarget.Get() && m_hMeasureReference.Get() && m_hTarget.Get() && m_hTargetReference.Get() ) { +#ifdef MAPBASE + Vector vecNewOrigin; + QAngle vecNewAngles; + DoMeasure(vecNewOrigin, vecNewAngles); + + if (m_bOutputPosition) + { + m_OutPosition.Set(vecNewOrigin, m_hTarget.Get(), this); + m_OutAngles.Set(vecNewAngles, m_hTarget.Get(), this); + } + + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_TELEPORT )) + { + m_hTarget->Teleport( &vecNewOrigin, !HasSpawnFlags(SF_LOGIC_MEASURE_MOVEMENT_DONT_SET_ANGLES) ? &vecNewAngles : NULL, NULL ); + } + else + { + m_hTarget->SetAbsOrigin( vecNewOrigin ); + + if (!HasSpawnFlags(SF_LOGIC_MEASURE_MOVEMENT_DONT_SET_ANGLES)) + m_hTarget->SetAbsAngles( vecNewAngles ); + } +#else matrix3x4_t matRefToMeasure, matWorldToMeasure; switch( m_nMeasureType ) { @@ -175,7 +304,6 @@ void CLogicMeasureMovement::MeasureThink( ) case MEASURE_EYE_POSITION: AngleIMatrix( m_hMeasureTarget->EyeAngles(), m_hMeasureTarget->EyePosition(), matWorldToMeasure ); break; - // FIXME: Could add attachment point measurement here easily } @@ -201,11 +329,103 @@ void CLogicMeasureMovement::MeasureThink( ) MatrixAngles( matNewTargetToWorld, vecNewAngles, vecNewOrigin ); m_hTarget->SetAbsOrigin( vecNewOrigin ); m_hTarget->SetAbsAngles( vecNewAngles ); +#endif } SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Moves logic_measure_movement's movement measurements to its own function, +// primarily to allow for the GetPosition input without any hacks. +// Also helps with derivative entities that would otherwise have to find a way to re-define the think function. +// Warning: Doesn't account for whether these handles are null! +//----------------------------------------------------------------------------- +void CLogicMeasureMovement::DoMeasure( Vector &vecOrigin, QAngle &angAngles ) +{ + matrix3x4_t matRefToMeasure, matWorldToMeasure; + switch( m_nMeasureType ) + { + case MEASURE_POSITION: + MatrixInvert( m_hMeasureTarget->EntityToWorldTransform(), matWorldToMeasure ); + break; + + case MEASURE_EYE_POSITION: + AngleIMatrix( m_hMeasureTarget->EyeAngles(), m_hMeasureTarget->EyePosition(), matWorldToMeasure ); + break; + + case MEASURE_ATTACHMENT: + if (CBaseAnimating *pAnimating = m_hMeasureTarget->GetBaseAnimating()) + { + if (m_iAttachment <= 0) + m_iAttachment = m_hMeasureTarget->GetBaseAnimating()->LookupAttachment(STRING(m_strAttachment)); + + if (m_iAttachment == -1) + Warning("WARNING: %s requesting invalid attachment %s on %s!\n", GetDebugName(), STRING(m_strAttachment), m_hMeasureTarget->GetDebugName()); + else + pAnimating->GetAttachment(m_iAttachment, matWorldToMeasure); + } + else + { + Warning("WARNING: %s requesting attachment point on non-animating entity %s!\n", GetDebugName(), m_hMeasureTarget->GetDebugName()); + } + break; + } + + ConcatTransforms( matWorldToMeasure, m_hMeasureReference->EntityToWorldTransform(), matRefToMeasure ); + + // Apply the scale factor + if ( ( m_flScale != 0.0f ) && ( m_flScale != 1.0f ) ) + { + Vector vecTranslation; + MatrixGetColumn( matRefToMeasure, 3, vecTranslation ); + vecTranslation /= m_flScale; + MatrixSetColumn( vecTranslation, 3, matRefToMeasure ); + } + + // Now apply the new matrix to the new reference point + matrix3x4_t matMeasureToRef, matNewTargetToWorld; + MatrixInvert( matRefToMeasure, matMeasureToRef ); + + // Handle origin ignorance + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN )) + { + // Get the position from the matrix's column directly and re-assign it + Vector vecPosition; + MatrixGetColumn( matMeasureToRef, 3, vecPosition ); + + HandleIgnoreFlags( vecPosition.Base() ); + + MatrixSetColumn( vecPosition, 3, matMeasureToRef ); + } + + ConcatTransforms( m_hTargetReference->EntityToWorldTransform(), matMeasureToRef, matNewTargetToWorld ); + + MatrixAngles( matNewTargetToWorld, angAngles, vecOrigin ); + + // If our spawnflags are greater than 0 (and don't just contain our default "TELEPORT" flag), we might need to ignore one of our angles. + if (GetSpawnFlags() && GetSpawnFlags() != SF_LOGIC_MEASURE_MOVEMENT_TELEPORT && !HasSpawnFlags(SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN)) + { + HandleIgnoreFlags( angAngles.Base() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles logic_measure_movement's ignore flags on the specified Vector/QAngle +//----------------------------------------------------------------------------- +FORCEINLINE void CLogicMeasureMovement::HandleIgnoreFlags( float *vec ) +{ + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_IGNORE_X )) + vec[0] = 0.0f; + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_IGNORE_Y )) + vec[1] = 0.0f; + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_IGNORE_Z )) + vec[2] = 0.0f; +} +#endif + //----------------------------------------------------------------------------- // Enable, disable @@ -225,6 +445,84 @@ void CLogicMeasureMovement::InputDisable( inputdata_t &inputdata ) //----------------------------------------------------------------------------- // Methods to change various targets //----------------------------------------------------------------------------- +#ifdef MAPBASE + +// +// Inputs work differently now so they could take !activator, etc. +// + +void CLogicMeasureMovement::InputSetMeasureTarget( inputdata_t &inputdata ) +{ + m_strMeasureTarget = inputdata.value.StringID(); + m_hMeasureTarget = gEntList.FindEntityByName( NULL, STRING(m_strMeasureTarget), this, inputdata.pActivator, inputdata.pCaller ); + if ( !m_hMeasureTarget ) + { + if ( Q_strnicmp( STRING(m_strMeasureTarget), "!player", 8 ) ) + { + Warning( "%s: Unable to find measure target entity %s\n", GetDebugName(), STRING(m_strMeasureTarget) ); + } + } + + m_iAttachment = 0; + + if (!m_hTarget) + SetTarget( STRING(m_target) ); + if (!m_hTargetReference) + SetTargetReference( STRING(m_strTargetReference) ); +} + +void CLogicMeasureMovement::InputSetMeasureReference( inputdata_t &inputdata ) +{ + m_strMeasureReference = inputdata.value.StringID(); + m_hMeasureReference = gEntList.FindEntityByName( NULL, STRING(m_strMeasureReference), this, inputdata.pActivator, inputdata.pCaller ); + if ( !m_hMeasureReference ) + { + Warning( "%s: Unable to find measure reference entity %s\n", GetDebugName(), STRING(m_strMeasureReference) ); + } +} + +void CLogicMeasureMovement::InputSetTarget( inputdata_t &inputdata ) +{ + m_target = inputdata.value.StringID(); + m_hTarget = gEntList.FindEntityByName( NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller ); + if ( !m_hTarget ) + { + Warning( "%s: Unable to find movement target entity %s\n", GetDebugName(), STRING(m_target) ); + } +} + +void CLogicMeasureMovement::InputSetTargetReference( inputdata_t &inputdata ) +{ + m_strTargetReference = inputdata.value.StringID(); + m_hTargetReference = gEntList.FindEntityByName( NULL, STRING(m_strTargetReference), this, inputdata.pActivator, inputdata.pCaller ); + if ( !m_hTargetReference ) + { + Warning( "%s: Unable to find movement reference entity %s\n", GetDebugName(), STRING(m_strTargetReference) ); + } +} + +void CLogicMeasureMovement::InputSetMeasureAttachment( inputdata_t &inputdata ) +{ + m_strAttachment = inputdata.value.StringID(); + m_iAttachment = 0; +} + +// Just gets the position once and fires outputs without moving anything. +// We don't even need a target for this. +void CLogicMeasureMovement::InputGetPosition( inputdata_t &inputdata ) +{ + if ( !m_hMeasureTarget.Get() || !m_hMeasureReference.Get() || !m_hTargetReference.Get() ) + return; + + Vector vecNewOrigin; + QAngle vecNewAngles; + DoMeasure(vecNewOrigin, vecNewAngles); + + // m_bOutputPosition has been repurposed here to toggle between using the target or the input activator as the activator. + m_OutPosition.Set(vecNewOrigin, m_bOutputPosition ? m_hTarget.Get() : inputdata.pActivator, this); + m_OutAngles.Set(Vector(vecNewAngles.x, vecNewAngles.y, vecNewAngles.z), m_bOutputPosition ? m_hTarget.Get() : inputdata.pActivator, this); +} +#else void CLogicMeasureMovement::InputSetMeasureTarget( inputdata_t &inputdata ) { m_strMeasureTarget = MAKE_STRING( inputdata.value.String() ); @@ -250,8 +548,354 @@ void CLogicMeasureMovement::InputSetTargetReference( inputdata_t &inputdata ) m_strTargetReference = MAKE_STRING( inputdata.value.String() ); SetTargetReference( inputdata.value.String() ); } +#endif void CLogicMeasureMovement::InputSetTargetScale( inputdata_t &inputdata ) { m_flScale = inputdata.value.Float(); } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// This will measure the direction of a target entity and move +// another entity to where the target entity is facing. +// +// m_hMeasureTarget; // Whose direction is measured +// m_hMeasureReference; // Position where direction is measured +// m_hTarget; // Target whose origin is applied +// m_hTargetReference; // From where the target's origin is applied +//----------------------------------------------------------------------------- +class CLogicMeasureDirection : public CLogicMeasureMovement +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CLogicMeasureDirection, CLogicMeasureMovement ); + +public: + + virtual void DoMeasure(Vector &vecOrigin, QAngle &angAngles); + + CBaseFilter *GetTraceFilter(); + //void InputSetTraceFilter( inputdata_t &inputdata ) { InputSetDamageFilter(inputdata); } + +private: + + float m_flTraceDistance; + int m_iMask; + int m_iCollisionGroup; + bool m_bHitIfPassed; + //string_t m_iszTraceFilter; + //CHandle m_hTraceFilter; + + bool m_bTraceTargetReference; + +}; + + +LINK_ENTITY_TO_CLASS( logic_measure_direction, CLogicMeasureDirection ); + + +BEGIN_DATADESC( CLogicMeasureDirection ) + + DEFINE_KEYFIELD( m_flTraceDistance, FIELD_FLOAT, "TraceDistance" ), + DEFINE_KEYFIELD( m_iMask, FIELD_INTEGER, "Mask" ), + DEFINE_KEYFIELD( m_iCollisionGroup, FIELD_INTEGER, "CollisionGroup" ), + DEFINE_KEYFIELD( m_bHitIfPassed, FIELD_BOOLEAN, "HitIfPassed" ), + DEFINE_KEYFIELD( m_bTraceTargetReference, FIELD_BOOLEAN, "TraceTargetReference" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetTraceFilter", InputSetDamageFilter ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Gets our "trace filter". +//----------------------------------------------------------------------------- +inline CBaseFilter *CLogicMeasureDirection::GetTraceFilter() +{ + return static_cast(m_hDamageFilter.Get()); // pranked +} + +//----------------------------------------------------------------------------- +// Purpose: Does measure. +//----------------------------------------------------------------------------- +void CLogicMeasureDirection::DoMeasure( Vector &vecOrigin, QAngle &angAngles ) +{ + trace_t tr; + Vector vecStart, vecDir; + QAngle angStart; + switch( m_nMeasureType ) + { + case MEASURE_POSITION: + vecStart = m_hMeasureReference->GetAbsOrigin(); + angStart = m_hMeasureTarget->GetAbsAngles(); + break; + + case MEASURE_EYE_POSITION: + vecStart = m_hMeasureReference->EyePosition(); + angStart = m_hMeasureTarget->EyeAngles(); + break; + + case MEASURE_ATTACHMENT: + CBaseAnimating *pAnimating = m_hMeasureTarget->GetBaseAnimating(); + if (pAnimating) + { + if (m_iAttachment <= 0) + m_iAttachment = m_hMeasureTarget->GetBaseAnimating()->LookupAttachment(STRING(m_strAttachment)); + + if (m_iAttachment == -1) + Warning("WARNING: %s requesting invalid attachment %s on %s!\n", GetDebugName(), STRING(m_strAttachment), m_hMeasureTarget->GetDebugName()); + else + { + pAnimating->GetAttachment(m_iAttachment, vecStart, angStart); + } + } + else + { + Warning("WARNING: %s requesting attachment point on non-animating entity %s!\n", GetDebugName(), m_hMeasureTarget->GetDebugName()); + } + break; + } + + // If we have spawn flags, we might be supposed to ignore something + if (GetSpawnFlags() > 0) + { + if (!HasSpawnFlags(SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN)) + AngleVectors(angStart, &vecDir); + + HandleIgnoreFlags( angStart.Base() ); + + if (HasSpawnFlags(SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN)) + AngleVectors(angStart, &vecDir); + } + else + { + AngleVectors(angStart, &vecDir); + } + + CTraceFilterEntityFilter traceFilter(m_hMeasureReference, m_iCollisionGroup); + traceFilter.m_pFilter = GetTraceFilter(); + traceFilter.m_bHitIfPassed = m_bHitIfPassed; + UTIL_TraceLine( vecStart, vecStart + vecDir * (m_flTraceDistance != 0 ? m_flTraceDistance : MAX_TRACE_LENGTH), m_iMask, &traceFilter, &tr ); //MASK_BLOCKLOS_AND_NPCS + + Vector vecEnd = tr.endpos; + + // Apply the scale factor + float flScale = m_flScale; + if ( ( flScale != 0.0f ) && ( flScale != 1.0f ) ) + { + vecEnd = (vecStart + ((vecEnd - vecStart) / flScale)); + } + + Vector refPos = m_hTargetReference->GetAbsOrigin(); + Vector vecPos = refPos + (vecEnd - vecStart); + + if (m_bTraceTargetReference) + { + // Make sure we can go the whole distance there + UTIL_TraceLine( refPos, vecPos, m_iMask, &traceFilter, &tr ); + vecPos = tr.endpos; + } + + vecOrigin = vecPos; + angAngles = angStart; +} + + + +//----------------------------------------------------------------------------- +// The unused, "forgotten" entity brought back to life. +// Mirrors an entity's movement across a reference. +// It derives from logic_measure_movement now so it could use its features. +// This is unfinished and I'm still figuring out how it works. +// +// m_hMeasureTarget; // Whose position is mirrored (m_hRemoteTarget) +// m_hMeasureReference; // Position where position is mirrored (m_hMirrorRelative) +// m_hTarget; // Target whose origin is mirrored (m_hMovementTarget) +// m_hTargetReference; // From where the target's origin is mirrored (m_hMirrorTarget) +//----------------------------------------------------------------------------- +class CLogicMirrorMovement : public CLogicMeasureMovement +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CLogicMirrorMovement, CLogicMeasureMovement ); + +public: + virtual void DoMeasure(Vector &vecOrigin, QAngle &angAngles); +}; + + +LINK_ENTITY_TO_CLASS( logic_mirror_movement, CLogicMirrorMovement ); + +BEGIN_DATADESC( CLogicMirrorMovement ) +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Does measure. +//----------------------------------------------------------------------------- +void CLogicMirrorMovement::DoMeasure( Vector &vecOrigin, QAngle &angAngles ) +{ + + matrix3x4_t matRefToMeasure, matWorldToMeasure; + switch( m_nMeasureType ) + { + case MEASURE_POSITION: + MatrixInvert( m_hMeasureTarget->EntityToWorldTransform(), matWorldToMeasure ); + break; + + case MEASURE_EYE_POSITION: + AngleIMatrix( m_hMeasureTarget->EyeAngles(), m_hMeasureTarget->EyePosition(), matWorldToMeasure ); + break; + + case MEASURE_ATTACHMENT: + if (CBaseAnimating *pAnimating = m_hMeasureTarget->GetBaseAnimating()) + { + if (m_iAttachment <= 0) + m_iAttachment = m_hMeasureTarget->GetBaseAnimating()->LookupAttachment(STRING(m_strAttachment)); + + if (m_iAttachment == -1) + Warning("WARNING: %s requesting invalid attachment %s on %s!\n", GetDebugName(), STRING(m_strAttachment), m_hMeasureTarget->GetDebugName()); + else + pAnimating->GetAttachment(m_iAttachment, matWorldToMeasure); + } + else + { + Warning("WARNING: %s requesting attachment point on non-animating entity %s!\n", GetDebugName(), m_hMeasureTarget->GetDebugName()); + } + break; + } + + ConcatTransforms( matWorldToMeasure, m_hMeasureReference->EntityToWorldTransform(), matRefToMeasure ); + + // Apply the scale factor + if ( ( m_flScale != 0.0f ) && ( m_flScale != 1.0f ) ) + { + Vector vecTranslation; + MatrixGetColumn( matRefToMeasure, 3, vecTranslation ); + vecTranslation /= m_flScale; + MatrixSetColumn( vecTranslation, 3, matRefToMeasure ); + } + + MatrixScaleBy( -1.0f, matRefToMeasure ); + + QAngle angRot; + Vector vecPos; + MatrixAngles( matRefToMeasure, angRot ); + MatrixPosition( matRefToMeasure, vecPos ); + angRot.z *= -1.0f; + vecPos.z *= -1.0f; + AngleMatrix( angRot, vecPos, matWorldToMeasure ); + + // Now apply the new matrix to the new reference point + matrix3x4_t matMeasureToRef, matNewTargetToWorld; + MatrixInvert( matRefToMeasure, matMeasureToRef ); + + // Handle origin ignorance + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN )) + { + // Get the position from the matrix's column directly and re-assign it + Vector vecPosition; + MatrixGetColumn( matRefToMeasure, 3, vecPosition ); + + HandleIgnoreFlags( vecPosition.Base() ); + + MatrixSetColumn( vecPosition, 3, matRefToMeasure ); + } + + ConcatTransforms( m_hTargetReference->EntityToWorldTransform(), matMeasureToRef, matNewTargetToWorld ); + + MatrixAngles( matNewTargetToWorld, angAngles, vecOrigin ); + + // If our spawnflags are greater than 0 (and don't just contain our default "TELEPORT" flag), we might need to ignore one of our angles. + if (GetSpawnFlags() && GetSpawnFlags() != SF_LOGIC_MEASURE_MOVEMENT_TELEPORT && !HasSpawnFlags(SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN)) + { + HandleIgnoreFlags( angAngles.Base() ); + } + + /* + VMatrix matPortal1ToWorldInv, matPortal2ToWorld; + MatrixInverseGeneral( m_hMeasureReference->EntityToWorldTransform(), matPortal1ToWorldInv ); + switch( m_nMeasureType ) + { + case MEASURE_POSITION: + matPortal2ToWorld = m_hMeasureTarget->EntityToWorldTransform(); + break; + + case MEASURE_EYE_POSITION: + matPortal2ToWorld.SetupMatrixOrgAngles( m_hMeasureTarget->EyePosition(), m_hMeasureTarget->EyeAngles() ); + break; + + case MEASURE_ATTACHMENT: + CBaseAnimating *pAnimating = m_hMeasureTarget->GetBaseAnimating(); + if (pAnimating) + { + if (m_iAttachment <= 0) + m_iAttachment = m_hMeasureTarget->GetBaseAnimating()->LookupAttachment(STRING(m_strAttachment)); + + if (m_iAttachment == -1) + Warning("WARNING: %s requesting invalid attachment %s on %s!\n", GetDebugName(), STRING(m_strAttachment), m_hMeasureTarget->GetDebugName()); + else + { + pAnimating->GetAttachment( m_iAttachment, matPortal2ToWorld.As3x4() ); + } + } + else + { + Warning("WARNING: %s requesting attachment point on non-animating entity %s!\n", GetDebugName(), m_hMeasureTarget->GetDebugName()); + } + break; + } + + // If we have spawn flags, we might be supposed to ignore something + if (GetSpawnFlags() > 0) + { + if (HasSpawnFlags( SF_LOGIC_MEASURE_MOVEMENT_USE_IGNORE_FLAGS_FOR_ORIGIN )) + { + // Get the position from the matrix's column directly and re-assign it + Vector vecPosition; + MatrixGetColumn( matPortal2ToWorld, 3, &vecPosition ); + + HandleIgnoreFlags( vecPosition.Base() ); + + MatrixSetColumn( matPortal2ToWorld, 3, vecPosition ); + } + else + { + // Get the angles from the matrix and re-assign it + QAngle angAngles; + MatrixToAngles( matPortal2ToWorld, angAngles ); + + HandleIgnoreFlags( angAngles.Base() ); + + matPortal2ToWorld.SetupMatrixAngles( angAngles ); + } + } + + // Apply the scale factor + if ( ( m_flScale != 0.0f ) && ( m_flScale != 1.0f ) ) + { + Vector vecTranslation; + MatrixGetColumn( matPortal2ToWorld.As3x4(), 3, vecTranslation ); + vecTranslation /= m_flScale; + MatrixSetColumn( vecTranslation, 3, matPortal2ToWorld.As3x4() ); + } + + // Get our scene camera's current orientation + Vector ptCameraPosition, vCameraLook, vCameraRight, vCameraUp; + ptCameraPosition = m_hTargetReference->EyePosition(); + m_hTargetReference->GetVectors( &vCameraLook, &vCameraRight, &vCameraUp ); + + // map this position and orientation to the remote portal, mirrored (invert the result) + Vector ptNewPosition, vNewLook; + ptNewPosition = matPortal1ToWorldInv * ptCameraPosition; + ptNewPosition = matPortal2ToWorld*(Vector( -ptNewPosition.x, -ptNewPosition.y, ptNewPosition.z )); + + vNewLook = matPortal1ToWorldInv.ApplyRotation( vCameraLook ); + vNewLook = matPortal2ToWorld.ApplyRotation( Vector( -vNewLook.x, -vNewLook.y, vNewLook.z ) ); + + // Set the point camera to the new location/orientation + QAngle qNewAngles; + VectorAngles( vNewLook, qNewAngles ); + + vecOrigin = ptNewPosition; + angAngles = qNewAngles; + */ +} +#endif diff --git a/mp/src/game/server/logic_random_outputs.cpp b/mp/src/game/server/logic_random_outputs.cpp new file mode 100644 index 00000000..b5c9f64b --- /dev/null +++ b/mp/src/game/server/logic_random_outputs.cpp @@ -0,0 +1,221 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ==== +// +// When triggered, will attempt to fire off each of its outputs. Each output +// has its own chance of firing. +// +//============================================================================= + +#include "cbase.h" +#include "entityinput.h" +#include "entityoutput.h" +#include "eventqueue.h" +#include "soundent.h" +#include "logic_random_outputs.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +const int SF_REMOVE_ON_FIRE = 0x001; // Relay will remove itself after being triggered. +const int SF_ALLOW_FAST_RETRIGGER = 0x002; // Unless set, entity will disable itself until the last output is sent. + +LINK_ENTITY_TO_CLASS(logic_random_outputs, CLogicRandomOutputs); + + +BEGIN_DATADESC( CLogicRandomOutputs ) + + DEFINE_FIELD(m_bWaitForRefire, FIELD_BOOLEAN), + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"), + + DEFINE_AUTO_ARRAY( m_flOnTriggerChance, FIELD_FLOAT ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), + DEFINE_INPUTFUNC(FIELD_VOID, "EnableRefire", InputEnableRefire), + DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + DEFINE_INPUTFUNC(FIELD_VOID, "Trigger", InputTrigger), + DEFINE_INPUTFUNC(FIELD_VOID, "CancelPending", InputCancelPending), + + // Outputs + DEFINE_OUTPUT(m_OnSpawn, "OnSpawn"), + DEFINE_OUTPUT(m_Output[0], "OnTrigger1"), + DEFINE_OUTPUT(m_Output[1], "OnTrigger2"), + DEFINE_OUTPUT(m_Output[2], "OnTrigger3"), + DEFINE_OUTPUT(m_Output[3], "OnTrigger4"), + DEFINE_OUTPUT(m_Output[4], "OnTrigger5"), + DEFINE_OUTPUT(m_Output[5], "OnTrigger6"), + DEFINE_OUTPUT(m_Output[6], "OnTrigger7"), + DEFINE_OUTPUT(m_Output[7], "OnTrigger8"), + +END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CLogicRandomOutputs::CLogicRandomOutputs(void) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Read in the chance of firing each output +//----------------------------------------------------------------------------- +bool CLogicRandomOutputs::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( szValue && szValue[0] ) + { + for ( int i=0; i < NUM_RANDOM_OUTPUTS; i++ ) + { + if ( FStrEq( szKeyName, UTIL_VarArgs( "OnTriggerChance%d", i ) ) ) + { + m_flOnTriggerChance[i] = atof( szValue ); + return true; + } + } + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Give out the chance of firing each output +//----------------------------------------------------------------------------- +bool CLogicRandomOutputs::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if ( !Q_strnicmp(szKeyName, "OnTriggerChance", 15) ) + { + for ( int i=0; i < NUM_RANDOM_OUTPUTS; i++ ) + { + if ( FStrEq( szKeyName, UTIL_VarArgs( "OnTriggerChance%d", i ) ) ) + { + Q_snprintf( szValue, iMaxLen, "%f", m_flOnTriggerChance[i] ); + return true; + } + } + } + + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); +} +#endif + +//------------------------------------------------------------------------------ +// Kickstarts a think if we have OnSpawn connections. +//------------------------------------------------------------------------------ +void CLogicRandomOutputs::Activate() +{ + BaseClass::Activate(); + + if ( m_OnSpawn.NumberOfElements() > 0) + { + SetNextThink( gpGlobals->curtime + 0.01 ); + } +} + + +//----------------------------------------------------------------------------- +// If we have OnSpawn connections, this is called shortly after spawning to +// fire the OnSpawn output. +//----------------------------------------------------------------------------- +void CLogicRandomOutputs::Think() +{ + // Fire an output when we spawn. This is used for self-starting an entity + // template -- since the logic_random_outputs is inside the template, it gets all the + // name and I/O connection fixup, so can target other entities in the template. + m_OnSpawn.FireOutput( this, this ); + + // We only get here if we had OnSpawn connections, so this is safe. + if ( m_spawnflags & SF_REMOVE_ON_FIRE ) + { + UTIL_Remove(this); + } +} + + +//------------------------------------------------------------------------------ +// Purpose: Turns on the entity, allowing it to fire outputs. +//------------------------------------------------------------------------------ +void CLogicRandomOutputs::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//------------------------------------------------------------------------------ +// Purpose: Enables us to fire again. This input is only posted from our Trigger +// function to prevent rapid refire. +//------------------------------------------------------------------------------ +void CLogicRandomOutputs::InputEnableRefire( inputdata_t &inputdata ) +{ + Msg(" now enabling refire\n" ); + m_bWaitForRefire = false; +} + + +//------------------------------------------------------------------------------ +// Purpose: Cancels any I/O events in the queue that were fired by us. +//------------------------------------------------------------------------------ +void CLogicRandomOutputs::InputCancelPending( inputdata_t &inputdata ) +{ + g_EventQueue.CancelEvents( this ); + + // Stop waiting; allow another Trigger. + m_bWaitForRefire = false; +} + + +//------------------------------------------------------------------------------ +// Purpose: Turns off the entity, preventing it from firing outputs. +//------------------------------------------------------------------------------ +void CLogicRandomOutputs::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + + +//------------------------------------------------------------------------------ +// Purpose: Toggles the enabled/disabled state of the entity. +//------------------------------------------------------------------------------ +void CLogicRandomOutputs::InputToggle( inputdata_t &inputdata ) +{ + m_bDisabled = !m_bDisabled; +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler that triggers the logic_random_outputs. +//----------------------------------------------------------------------------- +void CLogicRandomOutputs::InputTrigger( inputdata_t &inputdata ) +{ + if ((!m_bDisabled) && (!m_bWaitForRefire)) + { + for ( int i=0 ; i < NUM_RANDOM_OUTPUTS ; i++ ) + { + if ( RandomFloat() <= m_flOnTriggerChance[i] ) + { + m_Output[i].FireOutput( inputdata.pActivator, this ); + } + } + + if (m_spawnflags & SF_REMOVE_ON_FIRE) + { + UTIL_Remove(this); + } + else if (!(m_spawnflags & SF_ALLOW_FAST_RETRIGGER)) + { + // find the max delay from all our outputs + float fMaxDelay = 0; + for ( int i=0 ; i < NUM_RANDOM_OUTPUTS ; i++ ) + { + fMaxDelay = MAX( fMaxDelay, m_Output[i].GetMaxDelay() ); + } + if ( fMaxDelay > 0 ) + { + // Disable the relay so that it cannot be refired until after the last output + // has been fired and post an input to re-enable ourselves. + m_bWaitForRefire = true; + g_EventQueue.AddEvent(this, "EnableRefire", fMaxDelay + 0.001, this, this); + } + } + } +} diff --git a/mp/src/game/server/logic_random_outputs.h b/mp/src/game/server/logic_random_outputs.h new file mode 100644 index 00000000..aeb60bff --- /dev/null +++ b/mp/src/game/server/logic_random_outputs.h @@ -0,0 +1,54 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef LOGICRANDOMOUTPUTS_H +#define LOGICRANDOMOUTPUTS_H + +#include "cbase.h" +#include "entityinput.h" +#include "entityoutput.h" +#include "eventqueue.h" + +#define NUM_RANDOM_OUTPUTS 8 + +class CLogicRandomOutputs : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicRandomOutputs, CLogicalEntity ); + + CLogicRandomOutputs(); + + void Activate(); + void Think(); + virtual bool KeyValue( const char *szKeyName, const char *szValue ); +#ifdef MAPBASE + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); +#endif + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputEnableRefire( inputdata_t &inputdata ); // Private input handler, not in FGD + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + void InputTrigger( inputdata_t &inputdata ); + void InputCancelPending( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + + // Outputs + COutputEvent m_Output[ NUM_RANDOM_OUTPUTS ]; + COutputEvent m_OnSpawn; + + float m_flOnTriggerChance[ NUM_RANDOM_OUTPUTS ]; + +private: + + bool m_bDisabled; + bool m_bWaitForRefire; // Set to disallow a refire while we are waiting for our outputs to finish firing. +}; + +#endif //LOGICRANDOMOUTPUTS_H diff --git a/mp/src/game/server/logicentities.cpp b/mp/src/game/server/logicentities.cpp index cc8e6b73..2197baff 100644 --- a/mp/src/game/server/logicentities.cpp +++ b/mp/src/game/server/logicentities.cpp @@ -14,6 +14,12 @@ #include "saverestore_utlvector.h" #include "vstdlib/random.h" #include "gameinterface.h" +#ifdef MAPBASE +#include "mapbase/variant_tools.h" +#include "mapbase/matchers.h" +#include "mapbase/datadesc_mod.h" +#include "activitylist.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -21,6 +27,126 @@ extern CServerGameDLL g_ServerGameDLL; +//----------------------------------------------------------------------------- +// Purpose: An entity that acts as a container for game scripts. +//----------------------------------------------------------------------------- + +#define MAX_SCRIPT_GROUP 16 + +class CLogicScript : public CPointEntity +{ +public: + DECLARE_CLASS( CLogicScript, CPointEntity ); + DECLARE_DATADESC(); + + void RunVScripts() + { + /* + EntityGroup <- []; + function __AppendToScriptGroup( name ) + { + if ( name.len() == 0 ) + { + EntityGroup.append( null ); + } + else + { + local ent = Entities.FindByName( null, name ); + EntityGroup.append( ent ); + if ( ent != null ) + { + ent.ValidateScriptScope(); + ent.GetScriptScope().EntityGroup <- EntityGroup; + } + } + } + */ + + static const char szAddCode[] = + { + 0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x20,0x3c,0x2d,0x20,0x5b,0x5d,0x3b,0x0d,0x0a, + 0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x5f,0x5f,0x41,0x70,0x70,0x65,0x6e,0x64,0x54,0x6f,0x53, + 0x63,0x72,0x69,0x70,0x74,0x47,0x72,0x6f,0x75,0x70,0x28,0x20,0x6e,0x61,0x6d,0x65,0x20,0x29,0x20,0x0d, + 0x0a,0x7b,0x0d,0x0a,0x09,0x69,0x66,0x20,0x28,0x20,0x6e,0x61,0x6d,0x65,0x2e,0x6c,0x65,0x6e,0x28,0x29, + 0x20,0x3d,0x3d,0x20,0x30,0x20,0x29,0x20,0x0d,0x0a,0x09,0x7b,0x20,0x0d,0x0a,0x09,0x09,0x45,0x6e,0x74, + 0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x2e,0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x20,0x6e,0x75,0x6c, + 0x6c,0x20,0x29,0x3b,0x20,0x0d,0x0a,0x09,0x7d,0x20,0x0d,0x0a,0x09,0x65,0x6c,0x73,0x65,0x0d,0x0a,0x09, + 0x7b,0x20,0x0d,0x0a,0x09,0x09,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x65,0x6e,0x74,0x20,0x3d,0x20,0x45,0x6e, + 0x74,0x69,0x74,0x69,0x65,0x73,0x2e,0x46,0x69,0x6e,0x64,0x42,0x79,0x4e,0x61,0x6d,0x65,0x28,0x20,0x6e, + 0x75,0x6c,0x6c,0x2c,0x20,0x6e,0x61,0x6d,0x65,0x20,0x29,0x3b,0x0d,0x0a,0x09,0x09,0x45,0x6e,0x74,0x69, + 0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x2e,0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x20,0x65,0x6e,0x74,0x20, + 0x29,0x3b,0x0d,0x0a,0x09,0x09,0x69,0x66,0x20,0x28,0x20,0x65,0x6e,0x74,0x20,0x21,0x3d,0x20,0x6e,0x75, + 0x6c,0x6c,0x20,0x29,0x0d,0x0a,0x09,0x09,0x7b,0x0d,0x0a,0x09,0x09,0x09,0x65,0x6e,0x74,0x2e,0x56,0x61, + 0x6c,0x69,0x64,0x61,0x74,0x65,0x53,0x63,0x72,0x69,0x70,0x74,0x53,0x63,0x6f,0x70,0x65,0x28,0x29,0x3b, + 0x0d,0x0a,0x09,0x09,0x09,0x65,0x6e,0x74,0x2e,0x47,0x65,0x74,0x53,0x63,0x72,0x69,0x70,0x74,0x53,0x63, + 0x6f,0x70,0x65,0x28,0x29,0x2e,0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x20,0x3c,0x2d, + 0x20,0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x3b,0x0d,0x0a,0x09,0x09,0x7d,0x0d,0x0a, + 0x09,0x7d,0x0d,0x0a,0x7d,0x0d,0x0a,0x00 + }; + + int iLastMember; + for ( iLastMember = MAX_SCRIPT_GROUP - 1; iLastMember >= 0; iLastMember-- ) + { + if ( m_iszGroupMembers[iLastMember] != NULL_STRING ) + { + break; + } + } + + if ( iLastMember >= 0 ) + { + HSCRIPT hAddScript = g_pScriptVM->CompileScript( szAddCode ); + if ( hAddScript ) + { + ValidateScriptScope(); + m_ScriptScope.Run( hAddScript ); + HSCRIPT hAddFunc = m_ScriptScope.LookupFunction( "__AppendToScriptGroup" ); + if ( hAddFunc ) + { + for ( int i = 0; i <= iLastMember; i++ ) + { + m_ScriptScope.Call( hAddFunc, NULL, STRING(m_iszGroupMembers[i]) ); + } + g_pScriptVM->ReleaseFunction( hAddFunc ); + m_ScriptScope.ClearValue( "__AppendToScriptGroup" ); + } + + g_pScriptVM->ReleaseScript( hAddScript ); + } + } + BaseClass::RunVScripts(); + } + + string_t m_iszGroupMembers[MAX_SCRIPT_GROUP]; + +}; + +LINK_ENTITY_TO_CLASS( logic_script, CLogicScript ); + +BEGIN_DATADESC( CLogicScript ) + // Silence, Classcheck! + // DEFINE_ARRAY( m_iszGroupMembers, FIELD_STRING, MAX_NUM_TEMPLATES ), + + DEFINE_KEYFIELD( m_iszGroupMembers[0], FIELD_STRING, "Group00"), + DEFINE_KEYFIELD( m_iszGroupMembers[1], FIELD_STRING, "Group01"), + DEFINE_KEYFIELD( m_iszGroupMembers[2], FIELD_STRING, "Group02"), + DEFINE_KEYFIELD( m_iszGroupMembers[3], FIELD_STRING, "Group03"), + DEFINE_KEYFIELD( m_iszGroupMembers[4], FIELD_STRING, "Group04"), + DEFINE_KEYFIELD( m_iszGroupMembers[5], FIELD_STRING, "Group05"), + DEFINE_KEYFIELD( m_iszGroupMembers[6], FIELD_STRING, "Group06"), + DEFINE_KEYFIELD( m_iszGroupMembers[7], FIELD_STRING, "Group07"), + DEFINE_KEYFIELD( m_iszGroupMembers[8], FIELD_STRING, "Group08"), + DEFINE_KEYFIELD( m_iszGroupMembers[9], FIELD_STRING, "Group09"), + DEFINE_KEYFIELD( m_iszGroupMembers[10], FIELD_STRING, "Group10"), + DEFINE_KEYFIELD( m_iszGroupMembers[11], FIELD_STRING, "Group11"), + DEFINE_KEYFIELD( m_iszGroupMembers[12], FIELD_STRING, "Group12"), + DEFINE_KEYFIELD( m_iszGroupMembers[13], FIELD_STRING, "Group13"), + DEFINE_KEYFIELD( m_iszGroupMembers[14], FIELD_STRING, "Group14"), + DEFINE_KEYFIELD( m_iszGroupMembers[15], FIELD_STRING, "Group15"), + +END_DATADESC() + + //----------------------------------------------------------------------------- // Purpose: Compares a set of integer inputs to the one main input @@ -32,12 +158,24 @@ public: DECLARE_CLASS( CLogicCompareInteger, CLogicalEntity ); // outputs +#ifdef MAPBASE + COutputVariant m_OnEqual; + COutputVariant m_OnNotEqual; +#else COutputEvent m_OnEqual; COutputEvent m_OnNotEqual; +#endif // data +#ifdef MAPBASE + variant_t m_iValue; + bool m_iShouldCompareToValue; + bool m_bStrLenAllowed = true; + int DrawDebugTextOverlays(void); +#else int m_iIntegerValue; int m_iShouldCompareToValue; +#endif DECLARE_DATADESC(); @@ -45,6 +183,10 @@ public: // Input handlers void InputValue( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputValueNoFire( inputdata_t &inputdata ); + void InputSetIntegerValue( inputdata_t &inputdata ); +#endif void InputCompareValues( inputdata_t &inputdata ); }; @@ -57,12 +199,22 @@ BEGIN_DATADESC( CLogicCompareInteger ) DEFINE_OUTPUT( m_OnEqual, "OnEqual" ), DEFINE_OUTPUT( m_OnNotEqual, "OnNotEqual" ), +#ifdef MAPBASE + DEFINE_KEYVARIANT( m_iValue, "IntegerValue" ), + DEFINE_KEYFIELD( m_iShouldCompareToValue, FIELD_BOOLEAN, "ShouldComparetoValue" ), + DEFINE_KEYFIELD( m_bStrLenAllowed, FIELD_BOOLEAN, "StrLenAllowed" ), +#else DEFINE_KEYFIELD( m_iIntegerValue, FIELD_INTEGER, "IntegerValue" ), DEFINE_KEYFIELD( m_iShouldCompareToValue, FIELD_INTEGER, "ShouldComparetoValue" ), +#endif DEFINE_FIELD( m_AllIntCompares, FIELD_INPUT ), DEFINE_INPUTFUNC( FIELD_INPUT, "InputValue", InputValue ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INPUT, "InputValueNoFire", InputValueNoFire ), + DEFINE_INPUTFUNC( FIELD_INPUT, "SetReferenceValue", InputSetIntegerValue ), +#endif DEFINE_INPUTFUNC( FIELD_INPUT, "CompareValues", InputCompareValues ), END_DATADESC() @@ -75,9 +227,14 @@ END_DATADESC() //----------------------------------------------------------------------------- void CLogicCompareInteger::InputValue( inputdata_t &inputdata ) { +#ifdef MAPBASE + // Parse the input value, regardless of field type + inputdata.value = Variant_ParseInput(inputdata); +#else // make sure it's an int, if it can't be converted just throw it away if ( !inputdata.value.Convert(FIELD_INTEGER) ) return; +#endif // update the value list with the new value m_AllIntCompares.AddValue( inputdata.value, inputdata.nOutputID ); @@ -85,12 +242,38 @@ void CLogicCompareInteger::InputValue( inputdata_t &inputdata ) // if we haven't already this frame, send a message to ourself to update and fire if ( !m_AllIntCompares.m_bUpdatedThisFrame ) { +#ifdef MAPBASE + // Need to wait for all inputs to arrive + g_EventQueue.AddEvent( this, "CompareValues", 0.01, inputdata.pActivator, this, inputdata.nOutputID ); +#else // TODO: need to add this event with a lower priority, so it gets called after all inputs have arrived g_EventQueue.AddEvent( this, "CompareValues", 0, inputdata.pActivator, this, inputdata.nOutputID ); +#endif m_AllIntCompares.m_bUpdatedThisFrame = TRUE; } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Adds to the list of compared values without firing +//----------------------------------------------------------------------------- +void CLogicCompareInteger::InputValueNoFire( inputdata_t &inputdata ) +{ + // Parse the input value, regardless of field type + inputdata.value = Variant_ParseInput(inputdata); + + // update the value list with the new value + m_AllIntCompares.AddValue( inputdata.value, inputdata.nOutputID ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets our reference value +//----------------------------------------------------------------------------- +void CLogicCompareInteger::InputSetIntegerValue( inputdata_t &inputdata ) +{ + m_iValue = Variant_ParseInput(inputdata); +} +#endif //----------------------------------------------------------------------------- // Purpose: Forces a recompare @@ -100,6 +283,30 @@ void CLogicCompareInteger::InputCompareValues( inputdata_t &inputdata ) m_AllIntCompares.m_bUpdatedThisFrame = FALSE; // loop through all the values comparing them +#ifdef MAPBASE + variant_t value = m_iValue; + CMultiInputVar::inputitem_t *input = m_AllIntCompares.m_InputList; + + if ( !m_iShouldCompareToValue && input ) + { + value = input->value; + } + + while ( input ) + { + if ( !Variant_Equal(value, input->value, m_bStrLenAllowed) ) + { + // false + m_OnNotEqual.Set( input->value, inputdata.pActivator, this ); + return; + } + + input = input->next; + } + + // true! all values equal + m_OnEqual.Set( value, inputdata.pActivator, this ); +#else int value = m_iIntegerValue; CMultiInputVar::inputitem_t *input = m_AllIntCompares.m_InputList; @@ -122,8 +329,42 @@ void CLogicCompareInteger::InputCompareValues( inputdata_t &inputdata ) // true! all values equal m_OnEqual.FireOutput( inputdata.pActivator, this ); +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CLogicCompareInteger::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + Q_snprintf(tempstr, sizeof(tempstr), " Reference Value: %s", m_iValue.GetDebug()); + EntityText(text_offset, tempstr, 0); + text_offset++; + + int count = 1; + CMultiInputVar::inputitem_t *input = m_AllIntCompares.m_InputList; + while ( input ) + { + Q_snprintf(tempstr, sizeof(tempstr), " Value %i: %s", count, input->value.GetDebug()); + EntityText(text_offset, tempstr, 0); + text_offset++; + + count++; + input = input->next; + } + } + return text_offset; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Timer entity. Fires an output at regular or random intervals. @@ -171,6 +412,9 @@ public: int m_iUseRandomTime; float m_flLowerRandomBound; float m_flUpperRandomBound; +#ifdef MAPBASE + bool m_bUseBoundsForTimerInputs; +#endif // methods void ResetTimer( void ); @@ -189,6 +433,10 @@ BEGIN_DATADESC( CTimerEntity ) DEFINE_FIELD( m_bUpDownState, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bUseBoundsForTimerInputs, FIELD_BOOLEAN, "UseBoundsForTimerInputs" ), +#endif + // Inputs DEFINE_INPUTFUNC( FIELD_FLOAT, "RefireTime", InputRefireTime ), DEFINE_INPUTFUNC( FIELD_VOID, "FireTimer", InputFireTimer ), @@ -409,7 +657,24 @@ void CTimerEntity::InputAddToTimer( inputdata_t &inputdata ) // Add time to timer float flNextThink = GetNextThink(); +#ifdef MAPBASE + if (m_bUseBoundsForTimerInputs) + { + // Make sure it's not above our min or max bounds + if (flNextThink - gpGlobals->curtime > m_flUpperRandomBound) + return; + + flNextThink += inputdata.value.Float(); + flNextThink = clamp( flNextThink - gpGlobals->curtime, m_flLowerRandomBound, m_flUpperRandomBound ); + SetNextThink( gpGlobals->curtime + flNextThink ); + } + else + { + SetNextThink( flNextThink + inputdata.value.Float() ); + } +#else SetNextThink( flNextThink += inputdata.value.Float() ); +#endif } //----------------------------------------------------------------------------- @@ -424,6 +689,23 @@ void CTimerEntity::InputSubtractFromTimer( inputdata_t &inputdata ) // Subtract time from the timer but don't let the timer go negative float flNextThink = GetNextThink(); +#ifdef MAPBASE + if (m_bUseBoundsForTimerInputs) + { + // Make sure it's not above our min or max bounds + if (flNextThink - gpGlobals->curtime < m_flLowerRandomBound) + return; + + flNextThink -= inputdata.value.Float(); + flNextThink = clamp( flNextThink - gpGlobals->curtime, m_flLowerRandomBound, m_flUpperRandomBound ); + SetNextThink( gpGlobals->curtime + flNextThink ); + } + else + { + flNextThink -= inputdata.value.Float(); + SetNextThink( (flNextThink <= gpGlobals->curtime) ? gpGlobals->curtime : flNextThink ); + } +#else if ( ( flNextThink - gpGlobals->curtime ) <= inputdata.value.Float() ) { SetNextThink( gpGlobals->curtime ); @@ -432,6 +714,7 @@ void CTimerEntity::InputSubtractFromTimer( inputdata_t &inputdata ) { SetNextThink( flNextThink -= inputdata.value.Float() ); } +#endif } //----------------------------------------------------------------------------- @@ -844,6 +1127,39 @@ void CC_Global_Set( const CCommand &args ) static ConCommand global_set( "global_set", CC_Global_Set, "global_set : Sets the state of the given env_global (0 = OFF, 1 = ON, 2 = DEAD).", FCVAR_CHEAT ); +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Console command to set the counter of a global +//----------------------------------------------------------------------------- +void CC_Global_Counter( const CCommand &args ) +{ + const char *szGlobal = args[1]; + const char *szCounter = args[2]; + + if ( szGlobal == NULL || szCounter == NULL ) + { + Msg( "Usage: global_counter : Sets the counter of the given env_global.\n" ); + return; + } + + int nCounter = atoi( szCounter ); + + int nIndex = GlobalEntity_GetIndex( szGlobal ); + + if ( nIndex >= 0 ) + { + GlobalEntity_SetCounter( nIndex, nCounter ); + } + else + { + nIndex = GlobalEntity_Add( szGlobal, STRING( gpGlobals->mapname ), GLOBAL_ON ); + GlobalEntity_SetCounter( nIndex, nCounter ); + } +} + +static ConCommand global_counter( "global_counter", CC_Global_Counter, "global_counter : Sets the counter of the given env_global.", FCVAR_CHEAT ); +#endif + //----------------------------------------------------------------------------- // Purpose: Holds a global state that can be queried by other entities to change @@ -858,6 +1174,10 @@ public: void Spawn( void ); +#ifdef MAPBASE + bool KeyValue( const char *szKeyName, const char *szValue ); +#endif + // Input handlers void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); @@ -897,7 +1217,11 @@ BEGIN_DATADESC( CEnvGlobal ) DEFINE_INPUTFUNC( FIELD_INTEGER, "AddToCounter", InputAddToCounter ), DEFINE_INPUTFUNC( FIELD_VOID, "GetCounter", InputGetCounter ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_outCounter, "OutCounter" ), +#else DEFINE_OUTPUT( m_outCounter, "Counter" ), +#endif END_DATADESC() @@ -938,6 +1262,24 @@ void CEnvGlobal::Spawn( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CEnvGlobal::KeyValue( const char *szKeyName, const char *szValue ) +{ + // Any "Counter" outputs are changed to "OutCounter" before spawning. + if (FStrEq(szKeyName, "Counter") && strchr(szValue, ',')) + { + return BaseClass::KeyValue( "OutCounter", szValue ); + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} +#endif //------------------------------------------------------------------------------ // Purpose: @@ -1297,7 +1639,11 @@ void CMultiSource::Register(void) class CMathCounter : public CLogicalEntity { DECLARE_CLASS( CMathCounter, CLogicalEntity ); +#ifdef MAPBASE +protected: +#else private: +#endif float m_flMin; // Minimum clamp value. If min and max are BOTH zero, no clamping is done. float m_flMax; // Maximum clamp value. bool m_bHitMin; // Set when we reach or go below our minimum value, cleared if we go above it again. @@ -1310,6 +1656,9 @@ private: int DrawDebugTextOverlays(void); +#ifdef MAPBASE + virtual +#endif void UpdateOutValue(CBaseEntity *pActivator, float fNewValue); // Inputs @@ -1324,12 +1673,20 @@ private: void InputGetValue( inputdata_t &inputdata ); void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetMinValueNoFire( inputdata_t &inputdata ); + void InputSetMaxValueNoFire( inputdata_t &inputdata ); +#endif // Outputs COutputFloat m_OutValue; COutputFloat m_OnGetValue; // Used for polling the counter value. COutputEvent m_OnHitMin; COutputEvent m_OnHitMax; +#ifdef MAPBASE + COutputEvent m_OnChangedFromMin; + COutputEvent m_OnChangedFromMax; +#endif DECLARE_DATADESC(); }; @@ -1360,12 +1717,20 @@ BEGIN_DATADESC( CMathCounter ) DEFINE_INPUTFUNC(FIELD_VOID, "GetValue", InputGetValue), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxValueNoFire", InputSetMaxValueNoFire ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMinValueNoFire", InputSetMinValueNoFire ), +#endif // Outputs DEFINE_OUTPUT(m_OutValue, "OutValue"), DEFINE_OUTPUT(m_OnHitMin, "OnHitMin"), DEFINE_OUTPUT(m_OnHitMax, "OnHitMax"), DEFINE_OUTPUT(m_OnGetValue, "OnGetValue"), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnChangedFromMin, "OnChangedFromMin" ), + DEFINE_OUTPUT( m_OnChangedFromMax, "OnChangedFromMax" ), +#endif END_DATADESC() @@ -1382,7 +1747,11 @@ bool CMathCounter::KeyValue(const char *szKeyName, const char *szValue) // if (!stricmp(szKeyName, "startvalue")) { +#ifdef MAPBASE + m_OutValue.Init(atof(szValue)); +#else m_OutValue.Init(atoi(szValue)); +#endif return(true); } @@ -1478,6 +1847,29 @@ void CMathCounter::InputSetHitMin( inputdata_t &inputdata ) UpdateOutValue( inputdata.pActivator, m_OutValue.Get() ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Change min/max +//----------------------------------------------------------------------------- +void CMathCounter::InputSetMaxValueNoFire( inputdata_t &inputdata ) +{ + m_flMax = inputdata.value.Float(); + if ( m_flMax < m_flMin ) + { + m_flMin = m_flMax; + } +} + +void CMathCounter::InputSetMinValueNoFire( inputdata_t &inputdata ) +{ + m_flMin = inputdata.value.Float(); + if ( m_flMax < m_flMin ) + { + m_flMax = m_flMin; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Input handler for adding to the accumulator value. @@ -1636,6 +2028,14 @@ void CMathCounter::UpdateOutValue(CBaseEntity *pActivator, float fNewValue) } else { +#ifdef MAPBASE + // Fire an output if we just changed from the maximum value + if ( m_OutValue.Get() == m_flMax ) + { + m_OnChangedFromMax.FireOutput( pActivator, this ); + } +#endif + m_bHitMax = false; } @@ -1652,6 +2052,13 @@ void CMathCounter::UpdateOutValue(CBaseEntity *pActivator, float fNewValue) } else { +#ifdef MAPBASE + // Fire an output if we just changed from the maximum value + if ( m_OutValue.Get() == m_flMin ) + { + m_OnChangedFromMin.FireOutput( pActivator, this ); + } +#endif m_bHitMin = false; } @@ -1662,6 +2069,430 @@ void CMathCounter::UpdateOutValue(CBaseEntity *pActivator, float fNewValue) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Advanced math_counter with advanced calculation capabilities. +//----------------------------------------------------------------------------- +class CMathCounterAdvanced : public CMathCounter +{ + DECLARE_CLASS( CMathCounterAdvanced, CMathCounter ); +private: + + bool m_bPreserveValue; + bool m_bAlwaysOutputAsInt; + float m_flLerpPercent; + + void UpdateOutValue(CBaseEntity *pActivator, float fNewValue); + + void InputSetValueToPi( inputdata_t &inputdata ); + + void InputPower( inputdata_t &inputdata ); + void InputSquareRoot( inputdata_t &inputdata ); + + void InputRound( inputdata_t &inputdata ); + void InputFloor( inputdata_t &inputdata ); + void InputCeiling( inputdata_t &inputdata ); + void InputTrunc( inputdata_t &inputdata ); + + void InputSine( inputdata_t &inputdata ); + void InputCosine( inputdata_t &inputdata ); + void InputTangent( inputdata_t &inputdata ); + + void InputRandomInt( inputdata_t &inputdata ); + void InputRandomFloat( inputdata_t &inputdata ); + + void InputLerpTo( inputdata_t &inputdata ); + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_counter_advanced, CMathCounterAdvanced); + + +BEGIN_DATADESC( CMathCounterAdvanced ) + + // Keys + DEFINE_INPUT(m_bPreserveValue, FIELD_BOOLEAN, "PreserveValue"), + DEFINE_INPUT(m_bAlwaysOutputAsInt, FIELD_BOOLEAN, "AlwaysOutputAsInt"), + DEFINE_INPUT(m_flLerpPercent, FIELD_FLOAT, "SetLerpPercent"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "SetValueToPi", InputSetValueToPi), + + DEFINE_INPUTFUNC(FIELD_VOID, "SquareRoot", InputSquareRoot), + DEFINE_INPUTFUNC(FIELD_INTEGER, "Power", InputPower), + + DEFINE_INPUTFUNC(FIELD_INTEGER, "Round", InputRound), + DEFINE_INPUTFUNC(FIELD_INTEGER, "Floor", InputFloor), + DEFINE_INPUTFUNC(FIELD_INTEGER, "Ceil", InputCeiling), + DEFINE_INPUTFUNC(FIELD_INTEGER, "Trunc", InputTrunc), + + DEFINE_INPUTFUNC(FIELD_VOID, "Sin", InputSine), + DEFINE_INPUTFUNC(FIELD_VOID, "Cos", InputCosine), + DEFINE_INPUTFUNC(FIELD_VOID, "Tan", InputTangent), + + DEFINE_INPUTFUNC(FIELD_STRING, "RandomInt", InputRandomInt), + DEFINE_INPUTFUNC(FIELD_STRING, "RandomFloat", InputRandomFloat), + + DEFINE_INPUTFUNC(FIELD_FLOAT, "LerpTo", InputLerpTo), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Input handler for setting the current value to pi. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputSetValueToPi( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SET TO PI because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = M_PI; + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for calculating the square root of the current value. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputSquareRoot( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SQUARE ROOT because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = sqrt(m_OutValue.Get()); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for exponentiation of the current value. Use 2 to square it. +// Input : Integer value to raise the current value's power. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputPower( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring POWER!!! because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = pow(m_OutValue.Get(), inputdata.value.Int()); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +// +// For some reason, I had trouble finding the original math functions at first. +// Then I just randomly stumbled upon them, bright as day. +// Oh well. These might be faster anyway. +// +FORCEINLINE int RoundToNumber(int input, int number) +{ + (input < 0 && number > 0) ? number *= -1 : 0; + int result = (input + (number / 2)); + result -= (result % number); + return result; +} + +// Warning: Negative numbers should be ceiled +FORCEINLINE int FloorToNumber(int input, int number) +{ + return (input - (input % number)); +} + +FORCEINLINE int CeilToNumber(int input, int number) +{ + (input < 0 && number > 0) ? number *= -1 : 0; + int result = (input - (input % number)); + return result != input ? result + number : result; +} + +FORCEINLINE int TruncToNumber(int input, int number) +{ + //(input < 0 && number > 0) ? number *= -1 : 0; + return (input - (input % number)); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for rounding an integer to the specified number. (e.g. 126 rounding to 10 = 130, 1523 rounding to 5 = 1525) +// Input : Integer value to round the current value to. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputRound( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring ROUND because it is disabled\n", GetDebugName() ); + return; + } + + int iMultiple = inputdata.value.Int(); + int iNewValue; + if (iMultiple != 0) + { + // Round to the nearest input number. + iNewValue = RoundToNumber(m_OutValue.Get(), iMultiple); + } + else + { + // 0 just rounds floats. + iNewValue = static_cast(m_OutValue.Get() + 0.5f); + } + + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for flooring an integer to the specified number. (e.g. 126 flooring to 10 = 120, 1528 flooring to 5 = 1525) +// Input : Integer value to floor the current value to. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputFloor( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring FLOOR because it is disabled\n", GetDebugName() ); + return; + } + + int iMultiple = inputdata.value.Int(); + int iNewValue; + if (iMultiple != 0) + { + iNewValue = m_OutValue.Get(); + if (iNewValue >= 0) + { + // Floor to the nearest input number. + iNewValue = FloorToNumber(m_OutValue.Get(), iMultiple); + } + else + { + // We have to do it differently for negatives. + iNewValue = CeilToNumber(m_OutValue.Get(), iMultiple); + } + } + else + { + // 0 just floors floats. + iNewValue = static_cast(m_OutValue.Get()); + } + + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for ceiling an integer to the specified number. (e.g. 126 ceiling to 10 = 130, 1523 ceiling to 50 = 1550) +// Input : Integer value to ceil the current value to. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputCeiling( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring CEIL because it is disabled\n", GetDebugName() ); + return; + } + + int iMultiple = inputdata.value.Int(); + int iNewValue; + if (iMultiple != 0) + { + // Ceil to the nearest input number. + iNewValue = CeilToNumber(m_OutValue.Get(), iMultiple); + } + else + { + // 0 just ceils floats. + iNewValue = static_cast(m_OutValue.Get()) + (m_OutValue.Get() != 0 ? 1 : 0); + } + + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for truncating an integer to the specified number. (e.g. 126 rounding to 10 = 120, -1523 rounding to 5 = 1520) +// Input : Integer value to truncate the current value to. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputTrunc( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring TRUNC because it is disabled\n", GetDebugName() ); + return; + } + + int iMultiple = inputdata.value.Int(); + int iNewValue; + if (iMultiple != 0) + { + // Floor always truncates negative numbers if we don't tell it not to + iNewValue = FloorToNumber(m_OutValue.Get(), iMultiple); + } + else + { + // 0 just ceils floats. + iNewValue = static_cast(m_OutValue.Get()); + if (iNewValue < 0) + iNewValue += 1; + } + + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for applying sine to the current value. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputSine( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SINE because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = sin(m_OutValue.Get()); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for applying cosine to the current value. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputCosine( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SINE because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = cos(m_OutValue.Get()); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for applying tangent to the current value. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputTangent( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SINE because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = tan(m_OutValue.Get()); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for random int generation. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputRandomInt( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring RANDOMINT because it is disabled\n", GetDebugName() ); + return; + } + + int i1 = 0; + int i2 = 0; + + char szInput[128]; + Q_strncpy( szInput, inputdata.value.String(), sizeof(szInput) ); + char *sSpace = strchr( szInput, ' ' ); + if ( sSpace ) + { + i1 = atoi(szInput); + i2 = atoi(sSpace+1); + } + else + { + // No space, assume anything from 0 to X + i2 = atoi(szInput); + } + + float fNewValue = RandomInt(i1, i2); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for random float generation. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputRandomFloat( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring RANDOMFLOAT because it is disabled\n", GetDebugName() ); + return; + } + + float f1 = 0; + float f2 = 0; + + char szInput[128]; + Q_strncpy( szInput, inputdata.value.String(), sizeof(szInput) ); + char *sSpace = strchr( szInput, ' ' ); + if ( sSpace ) + { + f1 = atof(szInput); + f2 = atof(sSpace+1); + } + else + { + // No space, assume anything from 0 to X + f2 = atof(szInput); + } + + float fNewValue = RandomFloat(f1, f2); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for random float generation. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::InputLerpTo( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring LERPTO because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = m_OutValue.Get() + (inputdata.value.Float() - m_OutValue.Get()) * m_flLerpPercent; + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value to the new value, clamping and firing the output value. +// Input : fNewValue - Value to set. +//----------------------------------------------------------------------------- +void CMathCounterAdvanced::UpdateOutValue(CBaseEntity *pActivator, float fNewValue) +{ + if (m_bAlwaysOutputAsInt) + fNewValue = roundf(fNewValue); + + if (m_bPreserveValue) + { + //float fOriginal = m_OutValue.Get(); + //DevMsg("Preserve Before: %f\n", fOriginal); + //BaseClass::UpdateOutValue(pActivator, fNewValue); + //DevMsg("Preserve After: %f\n", fOriginal); + //m_OutValue.Init(fOriginal); + + variant_t var; + var.SetFloat(fNewValue); + m_OutValue.FireOutput( var, pActivator, this ); + } + else + { + BaseClass::UpdateOutValue(pActivator, fNewValue); + } +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Compares a single string input to up to 16 case values, firing an @@ -1678,6 +2509,10 @@ class CLogicCase : public CLogicalEntity private: string_t m_nCase[MAX_LOGIC_CASES]; +#ifdef MAPBASE + bool m_bMultipleCasesAllowed; +#endif + int m_nShuffleCases; int m_nLastShuffleCase; unsigned char m_uchShuffleCaseMap[MAX_LOGIC_CASES]; @@ -1694,6 +2529,9 @@ private: // Outputs COutputEvent m_OnCase[MAX_LOGIC_CASES]; // Fired when the input value matches one of the case values. COutputVariant m_OnDefault; // Fired when no match was found. +#ifdef MAPBASE + COutputVariant m_OnUsed; // Fired when this entity receives any input at all. +#endif DECLARE_DATADESC(); }; @@ -1723,6 +2561,10 @@ BEGIN_DATADESC( CLogicCase ) DEFINE_KEYFIELD(m_nCase[13], FIELD_STRING, "Case14"), DEFINE_KEYFIELD(m_nCase[14], FIELD_STRING, "Case15"), DEFINE_KEYFIELD(m_nCase[15], FIELD_STRING, "Case16"), + +#ifdef MAPBASE + DEFINE_KEYFIELD(m_bMultipleCasesAllowed, FIELD_BOOLEAN, "MultipleCasesAllowed"), +#endif DEFINE_FIELD( m_nShuffleCases, FIELD_INTEGER ), DEFINE_FIELD( m_nLastShuffleCase, FIELD_INTEGER ), @@ -1752,6 +2594,9 @@ BEGIN_DATADESC( CLogicCase ) DEFINE_OUTPUT(m_OnCase[15], "OnCase16"), DEFINE_OUTPUT(m_OnDefault, "OnDefault"), +#ifdef MAPBASE + DEFINE_OUTPUT(m_OnUsed, "OnUsed"), +#endif END_DATADESC() @@ -1775,16 +2620,35 @@ void CLogicCase::Spawn( void ) //----------------------------------------------------------------------------- void CLogicCase::InputValue( inputdata_t &inputdata ) { +#ifdef MAPBASE + m_OnUsed.Set(inputdata.value, inputdata.pActivator, this); + bool bFoundCase = false; +#endif const char *pszValue = inputdata.value.String(); for (int i = 0; i < MAX_LOGIC_CASES; i++) { +#ifdef MAPBASE + if ((m_nCase[i] != NULL_STRING) && Matcher_Match(STRING(m_nCase[i]), pszValue)) + { + m_OnCase[i].FireOutput( inputdata.pActivator, this ); + + if (!m_bMultipleCasesAllowed) + return; + else if (!bFoundCase) + bFoundCase = true; + } +#else if ((m_nCase[i] != NULL_STRING) && !stricmp(STRING(m_nCase[i]), pszValue)) { m_OnCase[i].FireOutput( inputdata.pActivator, this ); return; } +#endif } +#ifdef MAPBASE + if (!bFoundCase) +#endif m_OnDefault.Set( inputdata.value, inputdata.pActivator, this ); } @@ -1916,23 +2780,50 @@ class CLogicCompare : public CLogicalEntity public: int DrawDebugTextOverlays(void); +#ifdef MAPBASE + void Spawn(); +#endif + private: // Inputs void InputSetValue( inputdata_t &inputdata ); void InputSetValueCompare( inputdata_t &inputdata ); void InputSetCompareValue( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetCompareValueCompare( inputdata_t &inputdata ); +#endif void InputCompare( inputdata_t &inputdata ); +#ifdef MAPBASE + void DoCompare(CBaseEntity *pActivator, variant_t value); +#else void DoCompare(CBaseEntity *pActivator, float flInValue); +#endif +#ifdef MAPBASE + bool m_bStrLenAllowed = true; + bool m_bGreaterThanOrEqual; + variant_t m_InValue; // Place to hold the last input value for a recomparison. + variant_t m_CompareValue; // The value to compare the input value against. +#else float m_flInValue; // Place to hold the last input value for a recomparison. float m_flCompareValue; // The value to compare the input value against. +#endif // Outputs +#ifdef MAPBASE + COutputVariant m_OnLessThan; // Fired when the input value is less than the compare value. + COutputVariant m_OnEqualTo; // Fired when the input value is equal to the compare value. + COutputVariant m_OnNotEqualTo; // Fired when the input value is not equal to the compare value. + COutputVariant m_OnGreaterThan; // Fired when the input value is greater than the compare value. + COutputVariant m_OnLessThanOrEqualTo; // Fired when the input value is less than or equal to the compare value. + COutputVariant m_OnGreaterThanOrEqualTo; // Fired when the input value is greater than or equal to the compare value. +#else COutputFloat m_OnLessThan; // Fired when the input value is less than the compare value. COutputFloat m_OnEqualTo; // Fired when the input value is equal to the compare value. COutputFloat m_OnNotEqualTo; // Fired when the input value is not equal to the compare value. COutputFloat m_OnGreaterThan; // Fired when the input value is greater than the compare value. +#endif DECLARE_DATADESC(); }; @@ -1943,13 +2834,26 @@ LINK_ENTITY_TO_CLASS(logic_compare, CLogicCompare); BEGIN_DATADESC( CLogicCompare ) // Keys +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bStrLenAllowed, FIELD_BOOLEAN, "StrLenAllowed" ), + DEFINE_KEYVARIANT(m_CompareValue, "CompareValue"), + DEFINE_KEYVARIANT(m_InValue, "InitialValue"), +#else DEFINE_KEYFIELD(m_flCompareValue, FIELD_FLOAT, "CompareValue"), DEFINE_KEYFIELD(m_flInValue, FIELD_FLOAT, "InitialValue"), +#endif // Inputs +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_INPUT, "SetValue", InputSetValue), + DEFINE_INPUTFUNC(FIELD_INPUT, "SetValueCompare", InputSetValueCompare), + DEFINE_INPUTFUNC(FIELD_INPUT, "SetCompareValue", InputSetCompareValue), + DEFINE_INPUTFUNC(FIELD_INPUT, "SetCompareValueCompare", InputSetCompareValueCompare), +#else DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue), DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueCompare", InputSetValueCompare), DEFINE_INPUTFUNC(FIELD_FLOAT, "SetCompareValue", InputSetCompareValue), +#endif DEFINE_INPUTFUNC(FIELD_VOID, "Compare", InputCompare), // Outputs @@ -1957,18 +2861,40 @@ BEGIN_DATADESC( CLogicCompare ) DEFINE_OUTPUT(m_OnNotEqualTo, "OnNotEqualTo"), DEFINE_OUTPUT(m_OnGreaterThan, "OnGreaterThan"), DEFINE_OUTPUT(m_OnLessThan, "OnLessThan"), +#ifdef MAPBASE + DEFINE_OUTPUT(m_OnGreaterThanOrEqualTo, "OnGreaterThanOrEqualTo"), + DEFINE_OUTPUT(m_OnLessThanOrEqualTo, "OnLessThanOrEqualTo"), +#endif END_DATADESC() +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for a new input value without performing a comparison. +//----------------------------------------------------------------------------- +void CLogicCompare::Spawn() +{ + // Empty initial values are equivalent to 0 + if (m_InValue.FieldType() == FIELD_STRING && m_InValue.String()[0] == '\0') + m_InValue.SetInt( 0 ); + + BaseClass::Spawn(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Input handler for a new input value without performing a comparison. //----------------------------------------------------------------------------- void CLogicCompare::InputSetValue( inputdata_t &inputdata ) { +#ifdef MAPBASE + m_InValue = Variant_ParseInput(inputdata); +#else m_flInValue = inputdata.value.Float(); +#endif } @@ -1977,8 +2903,13 @@ void CLogicCompare::InputSetValue( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CLogicCompare::InputSetValueCompare( inputdata_t &inputdata ) { +#ifdef MAPBASE + m_InValue = Variant_ParseInput(inputdata); + DoCompare( inputdata.pActivator, m_InValue ); +#else m_flInValue = inputdata.value.Float(); DoCompare( inputdata.pActivator, m_flInValue ); +#endif } //----------------------------------------------------------------------------- @@ -1986,15 +2917,34 @@ void CLogicCompare::InputSetValueCompare( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CLogicCompare::InputSetCompareValue( inputdata_t &inputdata ) { +#ifdef MAPBASE + m_CompareValue = Variant_ParseInput(inputdata); +#else m_flCompareValue = inputdata.value.Float(); +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for a new input value and doing the comparison. +//----------------------------------------------------------------------------- +void CLogicCompare::InputSetCompareValueCompare( inputdata_t &inputdata ) +{ + m_CompareValue = Variant_ParseInput(inputdata); + DoCompare( inputdata.pActivator, m_InValue ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Input handler for forcing a recompare of the last input value. //----------------------------------------------------------------------------- void CLogicCompare::InputCompare( inputdata_t &inputdata ) { +#ifdef MAPBASE + DoCompare( inputdata.pActivator, m_InValue ); +#else DoCompare( inputdata.pActivator, m_flInValue ); +#endif } @@ -2003,6 +2953,33 @@ void CLogicCompare::InputCompare( inputdata_t &inputdata ) // output(s) based on the comparison result. // Input : flInValue - Value to compare against the comparison value. //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CLogicCompare::DoCompare(CBaseEntity *pActivator, variant_t value) +{ + if (Variant_Equal(value, m_CompareValue, m_bStrLenAllowed)) + { + m_OnEqualTo.Set(value, pActivator, this); + + m_OnLessThanOrEqualTo.Set(value, pActivator, this); + m_OnGreaterThanOrEqualTo.Set(value, pActivator, this); + } + else + { + m_OnNotEqualTo.Set(value, pActivator, this); + + if (Variant_Greater(m_InValue, m_CompareValue, m_bStrLenAllowed)) + { + m_OnGreaterThan.Set(value, pActivator, this); + m_OnGreaterThanOrEqualTo.Set(value, pActivator, this); + } + else + { + m_OnLessThan.Set(value, pActivator, this); + m_OnLessThanOrEqualTo.Set(value, pActivator, this); + } + } +} +#else void CLogicCompare::DoCompare(CBaseEntity *pActivator, float flInValue) { if (flInValue == m_flCompareValue) @@ -2023,6 +3000,7 @@ void CLogicCompare::DoCompare(CBaseEntity *pActivator, float flInValue) } } } +#endif //----------------------------------------------------------------------------- // Purpose: Draw any debug text overlays @@ -2037,12 +3015,20 @@ int CLogicCompare::DrawDebugTextOverlays( void ) char tempstr[512]; // print duration +#ifdef MAPBASE + Q_snprintf(tempstr,sizeof(tempstr)," Initial Value: %s", m_InValue.GetDebug()); +#else Q_snprintf(tempstr,sizeof(tempstr)," Initial Value: %f", m_flInValue); +#endif EntityText(text_offset,tempstr,0); text_offset++; // print hold time +#ifdef MAPBASE + Q_snprintf(tempstr,sizeof(tempstr)," Compare Value: %s", m_CompareValue.GetDebug()); +#else Q_snprintf(tempstr,sizeof(tempstr)," Compare Value: %f", m_flCompareValue); +#endif EntityText(text_offset,tempstr,0); text_offset++; } @@ -2256,6 +3242,11 @@ int CLogicBranch::DrawDebugTextOverlays( void ) return text_offset; } +#ifdef MAPBASE +extern void MapbaseGameLog_Record( const char *szContext ); +extern ConVar mapbase_game_log_on_autosave; +#endif + //----------------------------------------------------------------------------- // Purpose: Autosaves when triggered //----------------------------------------------------------------------------- @@ -2292,6 +3283,13 @@ END_DATADESC() //----------------------------------------------------------------------------- void CLogicAutosave::InputSave( inputdata_t &inputdata ) { +#ifdef MAPBASE + if (mapbase_game_log_on_autosave.GetBool()) + { + MapbaseGameLog_Record( "autosave" ); + } +#endif + if ( m_bForceNewLevelUnit ) { engine->ClearSaveDir(); @@ -2305,6 +3303,13 @@ void CLogicAutosave::InputSave( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CLogicAutosave::InputSaveDangerous( inputdata_t &inputdata ) { +#ifdef MAPBASE + if (mapbase_game_log_on_autosave.GetBool()) + { + MapbaseGameLog_Record( "autosave_dangerous" ); + } +#endif + CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); if ( g_ServerGameDLL.m_fAutoSaveDangerousTime != 0.0f && g_ServerGameDLL.m_fAutoSaveDangerousTime >= gpGlobals->curtime ) @@ -2443,10 +3448,26 @@ class CLogicCollisionPair : public CLogicalEntity DECLARE_CLASS( CLogicCollisionPair, CLogicalEntity ); public: +#ifdef MAPBASE + // !activator, !caller, etc. support + void EnableCollisions( bool bEnable, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ) + { + IPhysicsObject *pPhysics0 = NULL; + IPhysicsObject *pPhysics1 = NULL; + + CBaseEntity *pEntity0 = gEntList.FindEntityByName( NULL, m_nameAttach1, this, pActivator, pCaller ); + if (pEntity0) + pPhysics0 = pEntity0->VPhysicsGetObject(); + + CBaseEntity *pEntity1 = gEntList.FindEntityByName( NULL, m_nameAttach2, this, pActivator, pCaller ); + if (pEntity1) + pPhysics1 = pEntity1->VPhysicsGetObject(); +#else void EnableCollisions( bool bEnable ) { IPhysicsObject *pPhysics0 = FindPhysicsObjectByNameOrWorld( m_nameAttach1, this ); IPhysicsObject *pPhysics1 = FindPhysicsObjectByNameOrWorld( m_nameAttach2, this ); +#endif // need two different objects to do anything if ( pPhysics0 && pPhysics1 && pPhysics0 != pPhysics1 ) @@ -2481,14 +3502,22 @@ public: { if ( m_succeeded && m_disabled ) return; +#ifdef MAPBASE + EnableCollisions( false, inputdata.pActivator, inputdata.pCaller ); +#else EnableCollisions( false ); +#endif } void InputEnableCollisions( inputdata_t &inputdata ) { if ( m_succeeded && !m_disabled ) return; +#ifdef MAPBASE + EnableCollisions( true, inputdata.pActivator, inputdata.pCaller ); +#else EnableCollisions( true ); +#endif } // If Activate() becomes PostSpawn() //void OnRestore() { Activate(); } @@ -2746,3 +3775,3404 @@ int CLogicBranchList::DrawDebugTextOverlays( void ) return text_offset; } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Prints messages to the console. +//----------------------------------------------------------------------------- +class CLogicConsole : public CLogicalEntity +{ +public: + + DECLARE_CLASS( CLogicConsole, CLogicalEntity ); + + // Keys + int m_iDevLevel = 1; + Color m_MsgColor = Color(210, 250, 255, 255); + Color m_WarningColor = Color(255, 210, 210, 255); + bool m_bNewLineNotAuto = false; + + // TODO: Replace "append" with variable arguments? + inline void LCMsg(const char *msg, const char *append = NULL) { ConColorMsg(m_MsgColor, msg, append); } + inline void LCDevMsg(int lvl, const char *msg, const char *append = NULL) { developer.GetInt() >= lvl ? ConColorMsg(m_MsgColor, msg, append) : (void)0; } + inline void LCWarning(const char *msg, const char *append = NULL) { ConColorMsg(m_WarningColor, msg, append); } + inline void LCDevWarning(int lvl, const char *msg, const char *append = NULL) { developer.GetInt() >= lvl ? ConColorMsg(m_WarningColor, msg, append) : (void)0; } + + //inline void LCMsg(const char *msg, const char *append = NULL) { ColorSpewMessage(SPEW_MESSAGE, &m_MsgColor, msg, append); } + //inline void LCDevMsg(int lvl, const char *msg, const char *append = NULL) { developer.GetInt() >= lvl ? ColorSpewMessage(SPEW_MESSAGE, &m_MsgColor, msg, append) : (void)0; } + //inline void LCWarning(const char *msg, const char *append = NULL) { ColorSpewMessage(SPEW_MESSAGE, &m_WarningColor, msg, append); } + //inline void LCDevWarning(int lvl, const char *msg, const char *append = NULL) { developer.GetInt() >= lvl ? ColorSpewMessage(SPEW_MESSAGE, &m_WarningColor, msg, append) : (void)0; } + + // Inputs + void InputSendMsg( inputdata_t &inputdata ) { !m_bNewLineNotAuto ? LCMsg("%s\n", inputdata.value.String()) : LCMsg("%s", inputdata.value.String()); } + void InputSendWarning( inputdata_t &inputdata ) { !m_bNewLineNotAuto ? LCWarning("%s\n", inputdata.value.String()) : LCWarning("%s", inputdata.value.String()); } + void InputSendDevMsg( inputdata_t &inputdata ) { !m_bNewLineNotAuto ? LCDevMsg(m_iDevLevel, "%s\n", inputdata.value.String()) : LCDevMsg(m_iDevLevel, "%s", inputdata.value.String()); } + void InputSendDevWarning( inputdata_t &inputdata ) { !m_bNewLineNotAuto ? LCDevWarning(m_iDevLevel, "%s\n", inputdata.value.String()) : LCDevWarning(m_iDevLevel, "%s", inputdata.value.String()); } + + void InputNewLine( inputdata_t &inputdata ) { LCMsg("\n"); } + void InputDevNewLine( inputdata_t &inputdata ) { LCDevMsg(m_iDevLevel, "\n"); } + + // MAPBASE MP TODO: "ClearConsoleOnTarget" + // (and make this input broadcast to all players) + void InputClearConsole( inputdata_t &inputdata ) { UTIL_GetLocalPlayer() ? engine->ClientCommand(UTIL_GetLocalPlayer()->edict(), "clear") : (void)0; } + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_console, CLogicConsole); + + +BEGIN_DATADESC( CLogicConsole ) + + DEFINE_INPUT( m_iDevLevel, FIELD_INTEGER, "SetDevLvl" ), + DEFINE_INPUT( m_MsgColor, FIELD_COLOR32, "SetMsgColor" ), + DEFINE_INPUT( m_WarningColor, FIELD_COLOR32, "SetWarningColor" ), + DEFINE_INPUT( m_bNewLineNotAuto, FIELD_BOOLEAN, "SetNewLineNotAuto" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SendMsg", InputSendMsg ), + DEFINE_INPUTFUNC( FIELD_STRING, "SendWarning", InputSendWarning ), + DEFINE_INPUTFUNC( FIELD_STRING, "SendDevMsg", InputSendDevMsg ), + DEFINE_INPUTFUNC( FIELD_STRING, "SendDevWarning", InputSendDevWarning ), + + DEFINE_INPUTFUNC( FIELD_VOID, "NewLine", InputNewLine ), + DEFINE_INPUTFUNC( FIELD_VOID, "DevNewLine", InputDevNewLine ), + + DEFINE_INPUTFUNC( FIELD_VOID, "ClearConsole", InputClearConsole ), + +END_DATADESC() + +ConVar sv_allow_logic_convar( "sv_allow_logic_convar", "1", FCVAR_NOT_CONNECTED ); + +//----------------------------------------------------------------------------- +// Purpose: Gets console variables for the evil mapper. +//----------------------------------------------------------------------------- +class CLogicConvar : public CLogicalEntity +{ +public: + + DECLARE_CLASS( CLogicConvar, CLogicalEntity ); + + // Keys + string_t m_iszConVar; + string_t m_iszCompareValue; + + const char *GetConVarString( inputdata_t &inputdata ); + + // Inputs + void InputGetValue( inputdata_t &inputdata ); + void InputTest( inputdata_t &inputdata ); + + // Outputs + COutputEvent m_OnTrue; + COutputEvent m_OnFalse; + COutputString m_OutValue; + COutputEvent m_OnDenied; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_convar, CLogicConvar); + + +BEGIN_DATADESC( CLogicConvar ) + + DEFINE_INPUT( m_iszConVar, FIELD_STRING, "SetConVar" ), + DEFINE_INPUT( m_iszCompareValue, FIELD_STRING, "SetTestValue" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "GetValue", InputGetValue ), + DEFINE_INPUTFUNC( FIELD_VOID, "Test", InputTest ), + + DEFINE_OUTPUT(m_OnTrue, "OnTrue"), + DEFINE_OUTPUT(m_OnFalse, "OnFalse"), + DEFINE_OUTPUT(m_OutValue, "OutValue"), + DEFINE_OUTPUT(m_OnDenied, "OnDenied"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CLogicConvar::GetConVarString( inputdata_t &inputdata ) +{ + if (!sv_allow_logic_convar.GetBool()) + { + m_OnDenied.FireOutput(this, this); + + //return ConVarRef("", true); + return NULL; + } + + ConVarRef pCVar = ConVarRef(STRING(m_iszConVar), true); + if (!pCVar.IsValid()) + { + const char *pszCVar = STRING( m_iszConVar ); + CBasePlayer *pPlayer = ToBasePlayer( inputdata.pActivator ); + if (!pPlayer && AI_IsSinglePlayer()) + pPlayer = UTIL_PlayerByIndex( 1 ); + + if (pPlayer) + { + // Check if it's a common cheat command a player might be using + if (FStrEq( pszCVar, "god" )) + return (pPlayer->GetFlags() & FL_GODMODE) ? "1" : "0"; + if (FStrEq( pszCVar, "notarget" )) + return (pPlayer->GetFlags() & FL_NOTARGET) ? "1" : "0"; + if (FStrEq( pszCVar, "noclip" )) + return (pPlayer->IsEFlagSet(EFL_NOCLIP_ACTIVE)) ? "1" : "0"; + + // It might be a client convar + // This function returns a blank string if the convar doesn't exist, so we have to put this at the end + const char *pszClientValue = engine->GetClientConVarValue( pPlayer->GetClientIndex(), pszCVar ); + if (pszClientValue) + { + return pszClientValue; + } + } + + //Warning("Warning: %s has invalid convar \"%s\"\n", GetDebugName(), STRING(m_iszConVar)); + } + + return pCVar.GetString(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicConvar::InputGetValue( inputdata_t &inputdata ) +{ + const char *pCVarString = GetConVarString(inputdata); + if (pCVarString != NULL) + m_OutValue.Set( AllocPooledString( pCVarString ), inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicConvar::InputTest( inputdata_t &inputdata ) +{ + const char *pCVarString = GetConVarString(inputdata); + if (pCVarString) + { + if (Matcher_Match( STRING( m_iszCompareValue ), pCVarString )) + { + m_OnTrue.FireOutput(inputdata.pActivator, this); + } + else + { + m_OnFalse.FireOutput(inputdata.pActivator, this); + } + } +} + +#define MAX_LOGIC_FORMAT_PARAMETERS 8 +//----------------------------------------------------------------------------- +// Purpose: Takes a string and a bunch of parameters and spits out a formatted string. +//----------------------------------------------------------------------------- +class CLogicFormat : public CLogicalEntity +{ +public: + + DECLARE_CLASS( CLogicFormat, CLogicalEntity ); + + // Keys + string_t m_iszInput; + string_t m_iszParameter[MAX_LOGIC_FORMAT_PARAMETERS]; + string_t m_iszBackupParameter; + + void FormatString(const char *szStringToFormat, char *szOutput, int outputlen); + + // Inputs + void InputGetFormattedString( inputdata_t &inputdata ); + + // Outputs + COutputString m_OutFormattedString; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_format, CLogicFormat); + + +BEGIN_DATADESC( CLogicFormat ) + + DEFINE_INPUT( m_iszInput, FIELD_STRING, "SetInputValue" ), + DEFINE_INPUT( m_iszParameter[0], FIELD_STRING, "SetParameter0" ), + DEFINE_INPUT( m_iszParameter[1], FIELD_STRING, "SetParameter1" ), + DEFINE_INPUT( m_iszParameter[2], FIELD_STRING, "SetParameter2" ), + DEFINE_INPUT( m_iszParameter[3], FIELD_STRING, "SetParameter3" ), + DEFINE_INPUT( m_iszParameter[4], FIELD_STRING, "SetParameter4" ), + DEFINE_INPUT( m_iszParameter[5], FIELD_STRING, "SetParameter5" ), + DEFINE_INPUT( m_iszParameter[6], FIELD_STRING, "SetParameter6" ), + DEFINE_INPUT( m_iszParameter[7], FIELD_STRING, "SetParameter7" ), + DEFINE_INPUT( m_iszBackupParameter, FIELD_STRING, "SetBackupParameter" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "GetFormattedValue", InputGetFormattedString ), + + DEFINE_OUTPUT(m_OutFormattedString, "OutFormattedValue"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicFormat::InputGetFormattedString( inputdata_t &inputdata ) +{ + char szFormatted[256]; + if (m_iszInput != NULL_STRING) + { + FormatString(STRING(m_iszInput), szFormatted, sizeof(szFormatted)); + m_OutFormattedString.Set(AllocPooledString(szFormatted), inputdata.pActivator, this); + } +} + +//----------------------------------------------------------------------------- +// I'm bad at coding. +//----------------------------------------------------------------------------- +void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, int outputlen) +{ + const char *szParameters[MAX_LOGIC_FORMAT_PARAMETERS]; + for (int i = 0; i < MAX_LOGIC_FORMAT_PARAMETERS; i++) + { + if (m_iszParameter[i] != NULL_STRING) + { + szParameters[i] = STRING(m_iszParameter[i]); + } + else if (m_iszBackupParameter != NULL_STRING) + { + szParameters[i] = STRING(m_iszBackupParameter); + } + else + { + szParameters[i] = ""; + } + } + + char szFormatted[256] = { 0 }; // Needed so garbage isn't spewed at the beginning + //Q_snprintf(szFormatted, sizeof(szFormatted), szInput, szParameters); + + char szInput[256]; + Q_strncpy(szInput, szStringToFormat, sizeof(szInput)); + + bool inparam = (szInput[0] == '{'); + int curparam = 0; + char *szToken = strtok(szInput, "{"); + while (szToken != NULL) + { + if (inparam) + { + curparam = atoi(szToken); + if (curparam < MAX_LOGIC_FORMAT_PARAMETERS /*&& szParameters[curparam] != NULL*/) //if (curparam < MAX_FORMAT_PARAMETERS) + { + Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, szParameters[curparam]); + } + else + { + Warning("Warning: Parameter %i out of bounds in \"%s\"\n", curparam, szStringToFormat); + + // This might not be the best way to do this, but + // reaching it is supposed to be the result of a mistake anyway. + m_iszBackupParameter != NULL_STRING ? + Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, STRING(m_iszBackupParameter)) : + Q_snprintf(szFormatted, sizeof(szFormatted), "%s", szFormatted); + } + + inparam = false; + szToken = strtok(NULL, "{"); + } + else + { + Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, szToken); + + inparam = true; + szToken = strtok(NULL, "}"); + } + } + + Q_strncpy(szOutput, szFormatted, outputlen); +} + + +//----------------------------------------------------------------------------- +// Purpose: Accesses a keyvalue from a specific entity +// Mostly ported from Half-Laugh. +//----------------------------------------------------------------------------- +class CLogicKeyfieldAccessor : public CLogicalEntity +{ + DECLARE_CLASS(CLogicKeyfieldAccessor, CLogicalEntity); + +protected: + CBaseEntity *GetTarget(CBaseEntity *pCaller, CBaseEntity *pActivator); + + virtual bool TestKey(CBaseEntity *pTarget, const char *szKeyName); + virtual bool SetKeyValue(CBaseEntity *pTarget, const char *szKeyName, const char *szValue); + virtual bool SetKeyValueBits(CBaseEntity *pTarget, const char *szKeyName, int iValue, bool bRemove = false); + + // Inputs + void InputTest(inputdata_t &inputdata); + void InputTestKey(inputdata_t &inputdata); + void InputTestTarget(inputdata_t &inputdata); + + void InputSetKey(inputdata_t &inputdata); + + void InputSetValue(inputdata_t &inputdata); + void InputAddBits(inputdata_t &inputdata); + void InputRemoveBits(inputdata_t &inputdata); + + //bool ReadUnregisteredKeyfields(CBaseEntity *pTarget, const char *szKeyName, variant_t *variant); + + COutputVariant m_OutValue; + COutputEvent m_OnFailed; + + string_t m_iszKey; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_keyfield, CLogicKeyfieldAccessor); + + +BEGIN_DATADESC(CLogicKeyfieldAccessor) + +DEFINE_KEYFIELD( m_iszKey, FIELD_STRING, "keyname" ), + +// Inputs +DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest), +DEFINE_INPUTFUNC(FIELD_STRING, "TestKey", InputTestKey), +DEFINE_INPUTFUNC(FIELD_STRING, "TestTarget", InputTestTarget), +DEFINE_INPUTFUNC(FIELD_STRING, "SetKey", InputSetKey), + +DEFINE_INPUTFUNC(FIELD_STRING, "SetValue", InputSetValue), +DEFINE_INPUTFUNC(FIELD_INTEGER, "AddBits", InputAddBits), +DEFINE_INPUTFUNC(FIELD_INTEGER, "RemoveBits", InputRemoveBits), + +DEFINE_OUTPUT( m_OutValue, "OutValue" ), +DEFINE_OUTPUT( m_OnFailed, "OnFailed" ), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline CBaseEntity *CLogicKeyfieldAccessor::GetTarget(CBaseEntity *pCaller, CBaseEntity *pActivator) +{ + return gEntList.FindEntityByName(NULL, m_target, this, pActivator, pCaller); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicKeyfieldAccessor::TestKey(CBaseEntity *pTarget, const char *szKeyName) +{ + variant_t variant; + if (pTarget->ReadKeyField(szKeyName, &variant) || ReadUnregisteredKeyfields(pTarget, szKeyName, &variant)) + { + m_OutValue.Set(variant, pTarget, this); + return true; + } + else + { + m_OnFailed.FireOutput(pTarget, this); + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicKeyfieldAccessor::SetKeyValue(CBaseEntity *pTarget, const char *szKeyName, const char *szValue) +{ + if (pTarget->KeyValue(szKeyName, szValue)) + { + // We'll still fire OutValue + variant_t variant; + if (!pTarget->ReadKeyField(szKeyName, &variant)) + ReadUnregisteredKeyfields(pTarget, szKeyName, &variant); + + m_OutValue.Set(variant, pTarget, this); + return true; + } + else + { + m_OnFailed.FireOutput(pTarget, this); + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicKeyfieldAccessor::SetKeyValueBits(CBaseEntity *pTarget, const char *szKeyName, int iValue, bool bRemove) +{ + variant_t variant; + if ((pTarget->ReadKeyField(szKeyName, &variant) || ReadUnregisteredKeyfields(pTarget, szKeyName, &variant)) && variant.FieldType() == FIELD_INTEGER) + { + if (bRemove) + variant.SetInt(variant.Int() & ~iValue); + else + variant.SetInt(variant.Int() | iValue); + + pTarget->KeyValue(szKeyName, UTIL_VarArgs("%i", variant.Int())); + + m_OutValue.Set(variant, pTarget, this); + return true; + } + else + { + m_OnFailed.FireOutput(pTarget, this); + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputTest(inputdata_t &inputdata) +{ + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (pTarget && m_iszKey != NULL_STRING) + { + TestKey(pTarget, STRING(m_iszKey)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputTestKey(inputdata_t &inputdata) +{ + const char *input = inputdata.value.String(); + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (input && pTarget) + { + TestKey(pTarget, input); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputTestTarget(inputdata_t &inputdata) +{ + m_target = inputdata.value.StringID(); + CBaseEntity *pTarget = gEntList.FindEntityByName(NULL, inputdata.value.StringID(), this, inputdata.pCaller, inputdata.pActivator); + if (pTarget && m_iszKey != NULL_STRING) + { + TestKey(pTarget, STRING(m_iszKey)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputSetKey(inputdata_t &inputdata) +{ + m_iszKey = inputdata.value.StringID(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputSetValue(inputdata_t &inputdata) +{ + const char *input = inputdata.value.String(); + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (input && pTarget) + { + SetKeyValue(pTarget, STRING(m_iszKey), input); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputAddBits(inputdata_t &inputdata) +{ + int input = inputdata.value.Int(); + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (input && pTarget) + { + SetKeyValueBits(pTarget, STRING(m_iszKey), input, false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicKeyfieldAccessor::InputRemoveBits(inputdata_t &inputdata) +{ + int input = inputdata.value.Int(); + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (input && pTarget) + { + SetKeyValueBits(pTarget, STRING(m_iszKey), input, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Clamps the input value between two values +//----------------------------------------------------------------------------- +class CMathClamp : public CLogicalEntity +{ +public: + + DECLARE_CLASS( CMathClamp, CLogicalEntity ); + + // Keys + variant_t m_Max; + variant_t m_Min; + + // Inputs + void InputClampValue( inputdata_t &inputdata ); + + float ClampValue(float input, float min, float max, int *bounds); + void ClampValue(variant_t var, inputdata_t *inputdata); + + // Outputs + COutputVariant m_OutValue; + COutputVariant m_OnBeforeMin; + COutputVariant m_OnBeyondMax; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_clamp, CMathClamp); + + +BEGIN_DATADESC( CMathClamp ) + + DEFINE_INPUT( m_Max, FIELD_INPUT, "SetMax" ), + DEFINE_INPUT( m_Min, FIELD_INPUT, "SetMin" ), + + DEFINE_INPUTFUNC( FIELD_INPUT, "ClampValue", InputClampValue ), + + DEFINE_OUTPUT(m_OutValue, "OutValue"), + DEFINE_OUTPUT(m_OnBeforeMin, "OnBeforeMin"), + DEFINE_OUTPUT(m_OnBeyondMax, "OnBeyondMax"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathClamp::InputClampValue( inputdata_t &inputdata ) +{ + ClampValue(inputdata.value, &inputdata); +} + +//----------------------------------------------------------------------------- +// "bounds" returns 1 if the number was less than min, 2 if more than max. Must not be NULL +//----------------------------------------------------------------------------- +inline float CMathClamp::ClampValue(float input, float min, float max, int *bounds) +{ + if ( max < min ) + { + Warning("WARNING: Max value (%f) less than min value (%f) in %s!\n", max, min, GetDebugName()); + return max; + } + else if( input < min ) + { + *bounds = 1; + return min; + } + else if( input > max ) + { + *bounds = 2; + return max; + } + else + return input; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathClamp::ClampValue(variant_t var, inputdata_t *inputdata) +{ + // Don't convert up here in case of invalid type + + int nBounds; + + switch (var.FieldType()) + { + case FIELD_FLOAT: + { + m_Max.Convert(var.FieldType()); + m_Min.Convert(var.FieldType()); + + var.SetFloat(ClampValue(var.Float(), m_Max.Float(), m_Min.Float(), &nBounds)); + } break; + case FIELD_INTEGER: + { + m_Max.Convert(var.FieldType()); + m_Min.Convert(var.FieldType()); + + var.SetInt(ClampValue(var.Int(), m_Max.Int(), m_Min.Int(), &nBounds)); + } break; + case FIELD_VECTOR: + { + m_Max.Convert(var.FieldType()); + m_Min.Convert(var.FieldType()); + + Vector min; + Vector max; + m_Min.Vector3D(min); + m_Max.Vector3D(max); + + Vector vec; + var.Vector3D(vec); + + vec.x = ClampValue(vec.x, min.x, max.x, &nBounds); + vec.y = ClampValue(vec.y, min.y, max.y, &nBounds); + vec.z = ClampValue(vec.z, min.z, max.z, &nBounds); + + var.SetVector3D(vec); + } break; + default: + { + Warning("Error: Unsupported value %s in math_clamp %s\n", var.GetDebug(), STRING(GetEntityName())); + return; + } + } + + if (inputdata) + { + m_OutValue.Set(var, inputdata->pActivator, this); + if (nBounds == 1) + m_OnBeforeMin.Set(var, inputdata->pActivator, this); + else if (nBounds == 2) + m_OnBeyondMax.Set(var, inputdata->pActivator, this); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Bits calculations. +//----------------------------------------------------------------------------- +class CMathBits : public CLogicalEntity +{ + DECLARE_CLASS( CMathBits, CLogicalEntity ); +private: + + bool m_bDisabled; + + bool KeyValue(const char *szKeyName, const char *szValue); + + void UpdateOutValue(CBaseEntity *pActivator, int iNewValue); + + int DrawDebugTextOverlays(void); + + // Inputs + void InputAdd( inputdata_t &inputdata ); + void InputSubtract( inputdata_t &inputdata ); + void InputShiftLeft( inputdata_t &inputdata ); + void InputShiftRight( inputdata_t &inputdata ); + void InputApplyAnd( inputdata_t &inputdata ); + void InputApplyOr( inputdata_t &inputdata ); + void InputSetValue( inputdata_t &inputdata ); + void InputSetValueNoFire( inputdata_t &inputdata ); + void InputGetValue( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputContainsBits( inputdata_t &inputdata ); + void InputContainsAllBits( inputdata_t &inputdata ); + + // Outputs + COutputInt m_OutValue; + COutputInt m_OnGetValue; + COutputEvent m_OnTrue; + COutputEvent m_OnFalse; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_bits, CMathBits); + + +BEGIN_DATADESC( CMathBits ) + + // Keys + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_INTEGER, "Add", InputAdd), + DEFINE_INPUTFUNC(FIELD_INTEGER, "Subtract", InputSubtract), + DEFINE_INPUTFUNC(FIELD_INTEGER, "ShiftLeft", InputShiftLeft), + DEFINE_INPUTFUNC(FIELD_INTEGER, "ShiftRight", InputShiftRight), + DEFINE_INPUTFUNC(FIELD_INTEGER, "ApplyAnd", InputApplyAnd), + DEFINE_INPUTFUNC(FIELD_INTEGER, "ApplyOr", InputApplyOr), + DEFINE_INPUTFUNC(FIELD_INTEGER, "SetValue", InputSetValue), + DEFINE_INPUTFUNC(FIELD_INTEGER, "SetValueNoFire", InputSetValueNoFire), + DEFINE_INPUTFUNC(FIELD_VOID, "GetValue", InputGetValue), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC(FIELD_INTEGER, "ContainsBits", InputContainsBits), + DEFINE_INPUTFUNC(FIELD_INTEGER, "ContainsAllBits", InputContainsAllBits), + + // Outputs + DEFINE_OUTPUT(m_OutValue, "OutValue"), + DEFINE_OUTPUT(m_OnGetValue, "OnGetValue"), + DEFINE_OUTPUT(m_OnTrue, "OnTrue"), + DEFINE_OUTPUT(m_OnFalse, "OnFalse"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CMathBits::KeyValue(const char *szKeyName, const char *szValue) +{ + // + // Set the initial value of the counter. + // + if (!stricmp(szKeyName, "startvalue")) + { + m_OutValue.Init(atoi(szValue)); + return(true); + } + + return(BaseClass::KeyValue(szKeyName, szValue)); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for adding to the accumulator value. +// Input : Bit value to add. +//----------------------------------------------------------------------------- +void CMathBits::InputAdd( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring ADD because it is disabled\n", GetDebugName() ); + return; + } + + int iNewValue = m_OutValue.Get() | inputdata.value.Int(); + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for subtracting from the current value. +// Input : Bit value to subtract. +//----------------------------------------------------------------------------- +void CMathBits::InputSubtract( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring SUBTRACT because it is disabled\n", GetDebugName() ); + return; + } + + int iNewValue = m_OutValue.Get() & ~inputdata.value.Int(); + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for shifting from the current value. +// Input : Bit value to shift by. +//----------------------------------------------------------------------------- +void CMathBits::InputShiftLeft( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring SHIFTLEFT because it is disabled\n", GetDebugName() ); + return; + } + + int iNewValue = m_OutValue.Get() << inputdata.value.Int(); + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for shifting from the current value. +// Input : Bit value to shift by. +//----------------------------------------------------------------------------- +void CMathBits::InputShiftRight( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring SHIFTRIGHT because it is disabled\n", GetDebugName() ); + return; + } + + int iNewValue = m_OutValue.Get() >> inputdata.value.Int(); + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for applying & to the current value. +// Input : Bit value to shift by. +//----------------------------------------------------------------------------- +void CMathBits::InputApplyAnd( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring APPLYAND because it is disabled\n", GetDebugName() ); + return; + } + + int iNewValue = m_OutValue.Get() & inputdata.value.Int(); + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for applying | to the current value. +// Input : Bit value to shift by. +//----------------------------------------------------------------------------- +void CMathBits::InputApplyOr( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring APPLYOR because it is disabled\n", GetDebugName() ); + return; + } + + int iNewValue = m_OutValue.Get() | inputdata.value.Int(); + UpdateOutValue( inputdata.pActivator, iNewValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for updating the value. +// Input : Bit value to set. +//----------------------------------------------------------------------------- +void CMathBits::InputSetValue( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring SETVALUE because it is disabled\n", GetDebugName() ); + return; + } + + UpdateOutValue( inputdata.pActivator, inputdata.value.Int() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for updating the value. +// Input : Bit value to set. +//----------------------------------------------------------------------------- +void CMathBits::InputSetValueNoFire( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring SETVALUENOFIRE because it is disabled\n", GetDebugName() ); + return; + } + + m_OutValue.Init( inputdata.value.Int() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathBits::InputGetValue( inputdata_t &inputdata ) +{ + int iOutValue = m_OutValue.Get(); + m_OnGetValue.Set( iOutValue, inputdata.pActivator, inputdata.pCaller ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for checking whether a bit is stored. +// Input : Bit value to check. +//----------------------------------------------------------------------------- +void CMathBits::InputContainsBits( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring CONTAINS BITS because it is disabled\n", GetDebugName() ); + return; + } + + if (m_OutValue.Get() & inputdata.value.Int()) + m_OnTrue.FireOutput(inputdata.pActivator, this); + else + m_OnFalse.FireOutput(inputdata.pActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for checking whether all of the specified bits are stored. +// Input : Bit value to check. +//----------------------------------------------------------------------------- +void CMathBits::InputContainsAllBits( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Bits %s ignoring CONTAINS ALL BITS because it is disabled\n", GetDebugName() ); + return; + } + + bool bResult = false; + int iInput = inputdata.value.Int(); + int iValue = m_OutValue.Get(); + + for (int i = 1, n = 0; n < 32; (i <<= 1), n++) + { + DevMsg("%i\n", i); + if (iInput & i) + { + if (!(iValue & i)) + { + DevMsg("%i does not go into %i\n", i, iValue); + bResult = false; + break; + } + else if (!bResult) + { + bResult = true; + } + } + } + + if (bResult) + m_OnTrue.FireOutput(inputdata.pActivator, this); + else + m_OnFalse.FireOutput(inputdata.pActivator, this); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathBits::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathBits::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value to the new value, firing the output value. +// Input : iNewValue - Value to set. +//----------------------------------------------------------------------------- +void CMathBits::UpdateOutValue(CBaseEntity *pActivator, int iNewValue) +{ + m_OutValue.Set(iNewValue, pActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CMathBits::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + Q_snprintf(tempstr,sizeof(tempstr),"current value: %i", m_OutValue.Get()); + EntityText(text_offset,tempstr,0); + text_offset++; + + if( m_bDisabled ) + { + Q_snprintf(tempstr,sizeof(tempstr),"*DISABLED*"); + } + else + { + Q_snprintf(tempstr,sizeof(tempstr),"Enabled."); + } + EntityText(text_offset,tempstr,0); + text_offset++; + + } + return text_offset; +} + +// These spawnflags control math_vector dimensions. +#define SF_MATH_VECTOR_DISABLE_X ( 1 << 0 ) +#define SF_MATH_VECTOR_DISABLE_Y ( 1 << 1 ) +#define SF_MATH_VECTOR_DISABLE_Z ( 1 << 2 ) + +//----------------------------------------------------------------------------- +// Purpose: Vector calculations. +//----------------------------------------------------------------------------- +class CMathVector : public CLogicalEntity +{ + DECLARE_CLASS( CMathVector, CLogicalEntity ); +private: + + bool m_bDisabled; + + bool KeyValue(const char *szKeyName, const char *szValue); + bool KeyValue( const char *szKeyName, const Vector &vecValue ); + + void UpdateOutValue(CBaseEntity *pActivator, Vector vecNewValue); + + int DrawDebugTextOverlays(void); + + // Inputs + void InputAdd( inputdata_t &inputdata ); + void InputSubtract( inputdata_t &inputdata ); + void InputDivide( inputdata_t &inputdata ); + void InputMultiply( inputdata_t &inputdata ); + void InputSetValue( inputdata_t &inputdata ); + void InputSetValueNoFire( inputdata_t &inputdata ); + void InputGetValue( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + void PointAt( Vector &origin, Vector &target, Vector &out ); + void InputPointAtLocation( inputdata_t &inputdata ); + void InputPointAtEntity( inputdata_t &inputdata ); + + void InputNormalize( inputdata_t &inputdata ); + void InputNormalizeAngles( inputdata_t &inputdata ); + void InputVectorAngles( inputdata_t &inputdata ); + void InputAngleVectorForward( inputdata_t &inputdata ); + void InputAngleVectorRight( inputdata_t &inputdata ); + void InputAngleVectorUp( inputdata_t &inputdata ); + + void SetCoordinate(float value, char coord, CBaseEntity *pActivator); + void GetCoordinate(char coord, CBaseEntity *pActivator); + void AddCoordinate(float value, char coord, CBaseEntity *pActivator); + void SubtractCoordinate(float value, char coord, CBaseEntity *pActivator); + + void InputSetX( inputdata_t &inputdata ) { SetCoordinate(inputdata.value.Float(), 'X', inputdata.pActivator); } + void InputSetY( inputdata_t &inputdata ) { SetCoordinate(inputdata.value.Float(), 'Y', inputdata.pActivator); } + void InputSetZ( inputdata_t &inputdata ) { SetCoordinate(inputdata.value.Float(), 'Z', inputdata.pActivator); } + void InputGetX( inputdata_t &inputdata ) { GetCoordinate('X', inputdata.pActivator); } + void InputGetY( inputdata_t &inputdata ) { GetCoordinate('Y', inputdata.pActivator); } + void InputGetZ( inputdata_t &inputdata ) { GetCoordinate('Z', inputdata.pActivator); } + void InputAddX( inputdata_t &inputdata ) { AddCoordinate(inputdata.value.Float(), 'X', inputdata.pActivator); } + void InputAddY( inputdata_t &inputdata ) { AddCoordinate(inputdata.value.Float(), 'Y', inputdata.pActivator); } + void InputAddZ( inputdata_t &inputdata ) { AddCoordinate(inputdata.value.Float(), 'Z', inputdata.pActivator); } + void InputSubtractX( inputdata_t &inputdata ) { SubtractCoordinate(inputdata.value.Float(), 'X', inputdata.pActivator); } + void InputSubtractY( inputdata_t &inputdata ) { SubtractCoordinate(inputdata.value.Float(), 'Y', inputdata.pActivator); } + void InputSubtractZ( inputdata_t &inputdata ) { SubtractCoordinate(inputdata.value.Float(), 'Z', inputdata.pActivator); } + + // Outputs + COutputVector m_OutValue; + COutputFloat m_OutX; + COutputFloat m_OutY; + COutputFloat m_OutZ; + + COutputVector m_OnGetValue; + COutputFloat m_OnGetX; + COutputFloat m_OnGetY; + COutputFloat m_OnGetZ; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_vector, CMathVector); + + +BEGIN_DATADESC( CMathVector ) + + // Keys + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VECTOR, "Add", InputAdd), + DEFINE_INPUTFUNC(FIELD_VECTOR, "Subtract", InputSubtract), + DEFINE_INPUTFUNC(FIELD_VECTOR, "Divide", InputDivide), + DEFINE_INPUTFUNC(FIELD_VECTOR, "Multiply", InputMultiply), + DEFINE_INPUTFUNC(FIELD_VECTOR, "SetValue", InputSetValue), + DEFINE_INPUTFUNC(FIELD_VECTOR, "SetValueNoFire", InputSetValueNoFire), + DEFINE_INPUTFUNC(FIELD_VOID, "GetValue", InputGetValue), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_INPUTFUNC( FIELD_VECTOR, "PointAtLocation", InputPointAtLocation ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "PointAtEntity", InputPointAtEntity ), + + DEFINE_INPUTFUNC(FIELD_VOID, "Normalize", InputNormalize), + DEFINE_INPUTFUNC(FIELD_VOID, "NormalizeAngles", InputNormalizeAngles), + DEFINE_INPUTFUNC(FIELD_VOID, "VectorAngles", InputVectorAngles), + DEFINE_INPUTFUNC(FIELD_VOID, "AngleVectorForward", InputAngleVectorForward), + DEFINE_INPUTFUNC(FIELD_VOID, "AngleVectorRight", InputAngleVectorRight), + DEFINE_INPUTFUNC(FIELD_VOID, "AngleVectorUp", InputAngleVectorUp), + + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetX", InputSetX), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetY", InputSetY), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetZ", InputSetZ), + DEFINE_INPUTFUNC(FIELD_VOID, "GetX", InputGetX), + DEFINE_INPUTFUNC(FIELD_VOID, "GetY", InputGetY), + DEFINE_INPUTFUNC(FIELD_VOID, "GetZ", InputGetZ), + DEFINE_INPUTFUNC(FIELD_FLOAT, "AddX", InputAddX), + DEFINE_INPUTFUNC(FIELD_FLOAT, "AddY", InputAddY), + DEFINE_INPUTFUNC(FIELD_FLOAT, "AddZ", InputAddZ), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SubtractX", InputSubtractX), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SubtractY", InputSubtractY), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SubtractZ", InputSubtractZ), + + // Outputs + DEFINE_OUTPUT(m_OutValue, "OutValue"), + DEFINE_OUTPUT(m_OutX, "OutX"), + DEFINE_OUTPUT(m_OutY, "OutY"), + DEFINE_OUTPUT(m_OutZ, "OutZ"), + + DEFINE_OUTPUT(m_OnGetValue, "OnGetValue"), + DEFINE_OUTPUT(m_OnGetX, "OnGetX"), + DEFINE_OUTPUT(m_OnGetY, "OnGetY"), + DEFINE_OUTPUT(m_OnGetZ, "OnGetZ"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CMathVector::KeyValue(const char *szKeyName, const char *szValue) +{ + // + // Set the initial value of the counter. + // + if (!stricmp(szKeyName, "startvalue")) + { + Vector vec; + UTIL_StringToVector( vec.Base(), szValue ); + m_OutValue.Init(vec); + return(true); + } + + return(BaseClass::KeyValue(szKeyName, szValue)); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CMathVector::KeyValue( const char *szKeyName, const Vector &vecValue ) +{ + // + // Set the initial value of the counter. + // + if (!stricmp(szKeyName, "startvalue")) + { + m_OutValue.Init(vecValue); + return true; + } + + // So, CLogicalEntity descends from CBaseEntity... + // Yup. + // ...and CBaseEntity has a version of KeyValue that takes vectors. + // Yup. + // Since it's virtual, I could easily override it just like I could with a KeyValue that takes strings, right? + // Sounds right to me. + // So let me override it. + // *No suitable function exists* + return CBaseEntity::KeyValue(szKeyName, vecValue); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for adding to the accumulator value. +// Input : Bit value to add. +//----------------------------------------------------------------------------- +void CMathVector::InputAdd( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring ADD because it is disabled\n", GetDebugName() ); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + Vector cur; + m_OutValue.Get(cur); + UpdateOutValue( inputdata.pActivator, cur + vec ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for subtracting from the current value. +// Input : Bit value to subtract. +//----------------------------------------------------------------------------- +void CMathVector::InputSubtract( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring SUBTRACT because it is disabled\n", GetDebugName() ); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + Vector cur; + m_OutValue.Get(cur); + UpdateOutValue( inputdata.pActivator, cur - vec ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for multiplying the current value. +// Input : Float value to multiply the value by. +//----------------------------------------------------------------------------- +void CMathVector::InputDivide( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring DIVIDE because it is disabled\n", GetDebugName() ); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + Vector cur; + m_OutValue.Get(cur); + + if (vec.x != 0) + cur.x /= vec.x; + if (vec.y != 0) + cur.y /= vec.y; + if (vec.z != 0) + cur.z /= vec.z; + + UpdateOutValue( inputdata.pActivator, cur ); + + //if (vec.x != 0 && vec.y != 0 && vec.z != 0) + //{ + // UpdateOutValue( inputdata.pActivator, cur / vec ); + //} + //else + //{ + // DevMsg( 1, "LEVEL DESIGN ERROR: Divide by zero in math_vector\n" ); + // UpdateOutValue( inputdata.pActivator, cur ); + //} +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for multiplying the current value. +// Input : Float value to multiply the value by. +//----------------------------------------------------------------------------- +void CMathVector::InputMultiply( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring MULTIPLY because it is disabled\n", GetDebugName() ); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + Vector cur; + m_OutValue.Get(cur); + UpdateOutValue( inputdata.pActivator, cur * vec ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for updating the value. +// Input : Bit value to set. +//----------------------------------------------------------------------------- +void CMathVector::InputSetValue( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring SETVALUE because it is disabled\n", GetDebugName() ); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + UpdateOutValue( inputdata.pActivator, vec ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for updating the value. +// Input : Bit value to set. +//----------------------------------------------------------------------------- +void CMathVector::InputSetValueNoFire( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring SETVALUENOFIRE because it is disabled\n", GetDebugName() ); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + m_OutValue.Init( vec ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputGetValue( inputdata_t &inputdata ) +{ + Vector cur; + m_OutValue.Get(cur); + m_OnGetValue.Set( cur, inputdata.pActivator, inputdata.pCaller ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::PointAt( Vector &origin, Vector &target, Vector &out ) +{ + out = origin - target; + VectorNormalize( out ); + + QAngle ang; + VectorAngles( out, ang ); + + out[0] = ang[0]; + out[1] = ang[1]; + out[2] = ang[2]; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputPointAtLocation( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring POINTATLOCATION because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + m_OutValue.Get(cur); + + Vector location; + inputdata.value.Vector3D( location ); + + PointAt( cur, location, cur ); + + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputPointAtEntity( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring POINTATENTITY because it is disabled\n", GetDebugName() ); + return; + } + + if (!inputdata.value.Entity()) + { + Warning("%s received no entity to point at\n", GetDebugName()); + return; + } + + Vector cur; + m_OutValue.Get(cur); + + Vector location = inputdata.value.Entity()->GetAbsOrigin(); + + PointAt( cur, location, cur ); + + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputNormalize( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring NORMALIZE because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + m_OutValue.Get(cur); + VectorNormalize(cur); + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputNormalizeAngles( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring NORMALIZEANGLES because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + m_OutValue.Get(cur); + cur.x = AngleNormalize(cur.x); + cur.y = AngleNormalize(cur.y); + cur.z = AngleNormalize(cur.z); + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputVectorAngles( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring VECTORANGLES because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + QAngle ang; + m_OutValue.Get(cur); + VectorAngles(cur, ang); + UpdateOutValue( inputdata.pActivator, Vector(ang.x, ang.y, ang.z) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputAngleVectorForward( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring ANGLEVECTORFORWARD because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + m_OutValue.Get(cur); + AngleVectors(QAngle(cur.x, cur.y, cur.z), &cur); + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputAngleVectorRight( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring ANGLEVECTORRIGHT because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + m_OutValue.Get(cur); + AngleVectors(QAngle(cur.x, cur.y, cur.z), NULL, &cur, NULL); + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::InputAngleVectorUp( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring ANGLEVECTORUP because it is disabled\n", GetDebugName() ); + return; + } + + Vector cur; + m_OutValue.Get(cur); + AngleVectors(QAngle(cur.x, cur.y, cur.z), NULL, NULL, &cur); + UpdateOutValue( inputdata.pActivator, cur ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::SetCoordinate(float value, char coord, CBaseEntity *pActivator) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring SET%c because it is disabled\n", GetDebugName(), coord ); + return; + } + + Vector vec; + m_OutValue.Get(vec); + switch (coord) + { + case 'X': vec.x = value; break; + case 'Y': vec.y = value; break; + case 'Z': vec.z = value; break; + } + UpdateOutValue( pActivator, vec ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::GetCoordinate(char coord, CBaseEntity *pActivator) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring SET%c because it is disabled\n", GetDebugName(), coord ); + return; + } + + Vector vec; + m_OutValue.Get(vec); + switch (coord) + { + case 'X': m_OnGetX.Set(vec.x, pActivator, this); break; + case 'Y': m_OnGetY.Set(vec.y, pActivator, this); break; + case 'Z': m_OnGetZ.Set(vec.z, pActivator, this); break; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::AddCoordinate(float value, char coord, CBaseEntity *pActivator) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring ADD%c because it is disabled\n", GetDebugName(), coord ); + return; + } + + Vector vec; + m_OutValue.Get(vec); + switch (coord) + { + case 'X': vec.x += value; break; + case 'Y': vec.y += value; break; + case 'Z': vec.z += value; break; + } + UpdateOutValue( pActivator, vec ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathVector::SubtractCoordinate(float value, char coord, CBaseEntity *pActivator) +{ + if( m_bDisabled ) + { + DevMsg("Math Vector %s ignoring SUBTRACT%c because it is disabled\n", GetDebugName(), coord ); + return; + } + + Vector vec; + m_OutValue.Get(vec); + switch (coord) + { + case 'X': vec.x += value; break; + case 'Y': vec.y += value; break; + case 'Z': vec.z += value; break; + } + UpdateOutValue( pActivator, vec ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value to the new value, firing the output value. +// Input : vecNewValue - Value to set. +//----------------------------------------------------------------------------- +void CMathVector::UpdateOutValue(CBaseEntity *pActivator, Vector vecNewValue) +{ + if (HasSpawnFlags( SF_MATH_VECTOR_DISABLE_X )) + vecNewValue.x = 0; + if (HasSpawnFlags( SF_MATH_VECTOR_DISABLE_Y )) + vecNewValue.y = 0; + if (HasSpawnFlags( SF_MATH_VECTOR_DISABLE_Z )) + vecNewValue.z = 0; + + m_OutValue.Set(vecNewValue, pActivator, this); + + m_OutX.Set(vecNewValue.x, pActivator, this); + m_OutY.Set(vecNewValue.y, pActivator, this); + m_OutZ.Set(vecNewValue.z, pActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CMathVector::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + Vector cur; + m_OutValue.Get(cur); + + Q_snprintf(tempstr, sizeof(tempstr), "current value: [%g %g %g]", (double)cur[0], (double)cur[1], (double)cur[2]); + EntityText(text_offset,tempstr,0); + text_offset++; + + if( m_bDisabled ) + { + Q_snprintf(tempstr,sizeof(tempstr),"*DISABLED*"); + } + else + { + Q_snprintf(tempstr,sizeof(tempstr),"Enabled."); + } + EntityText(text_offset,tempstr,0); + text_offset++; + + } + return text_offset; +} + +//----------------------------------------------------------------------------- +// Purpose: Accesses/modifies any field in a datadesc based on its internal name. +// Oh boy. +//----------------------------------------------------------------------------- +class CLogicFieldAccessor : public CLogicKeyfieldAccessor +{ + DECLARE_CLASS(CLogicFieldAccessor, CLogicKeyfieldAccessor); + +private: + bool TestKey(CBaseEntity *pTarget, const char *szKeyName); + bool SetKeyValue(CBaseEntity *pTarget, const char *szKeyName, const char *szValue); + bool SetKeyValueBits(CBaseEntity *pTarget, const char *szKeyName, int iValue, bool bRemove = false); + + //DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_datadesc_accessor, CLogicFieldAccessor); + + +//BEGIN_DATADESC(CLogicFieldAccessor) + +//END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicFieldAccessor::TestKey(CBaseEntity *pTarget, const char *szKeyName) +{ + variant_t var; + for ( datamap_t *dmap = pTarget->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_SAVE | FTYPEDESC_KEY) ) + { + DevMsg("Field Name: %s,\n", dmap->dataDesc[i].fieldName); + if ( Matcher_NamesMatch(szKeyName, dmap->dataDesc[i].fieldName) ) + { + fieldtype_t fieldtype = dmap->dataDesc[i].fieldType; + switch (fieldtype) + { + case FIELD_TIME: fieldtype = FIELD_FLOAT; break; + case FIELD_MODELNAME: fieldtype = FIELD_STRING; break; + case FIELD_SOUNDNAME: fieldtype = FIELD_STRING; break; + // There's definitely more of them. Add when demand becomes prevalent + } + + var.Set( fieldtype, ((char*)pTarget) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ] ); + DevMsg("FIELD TYPE: %i\n", fieldtype); + m_OutValue.Set(var, pTarget, this); + return true; + } + } + } + } + + m_OnFailed.FireOutput(pTarget, this); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicFieldAccessor::SetKeyValue(CBaseEntity *pTarget, const char *szKeyName, const char *szValue) +{ + for ( datamap_t *dmap = pTarget->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_SAVE | FTYPEDESC_KEY) ) + { + DevMsg("Field Name: %s,\n", dmap->dataDesc[i].fieldName); + if ( Matcher_NamesMatch(szKeyName, dmap->dataDesc[i].fieldName) ) + { + // Copied from ::ParseKeyvalue... + fieldtype_t fieldtype = FIELD_VOID; + typedescription_t *pField = &dmap->dataDesc[i]; + char *data = Datadesc_SetFieldString( szValue, pTarget, pField, &fieldtype ); + + if (!data) + { + Warning( "%s cannot set field of type %i.\n", GetDebugName(), dmap->dataDesc[i].fieldType ); + } + else if (fieldtype != FIELD_VOID) + { + variant_t var; + var.Set(fieldtype, data); + m_OutValue.Set(var, pTarget, this); + return true; + } + } + } + } + } + + m_OnFailed.FireOutput(pTarget, this); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicFieldAccessor::SetKeyValueBits(CBaseEntity *pTarget, const char *szKeyName, int iValue, bool bRemove) +{ + variant_t var; + for ( datamap_t *dmap = pTarget->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_SAVE | FTYPEDESC_KEY) ) + { + DevMsg("Field Name: %s,\n", dmap->dataDesc[i].fieldName); + if ( Matcher_NamesMatch(szKeyName, dmap->dataDesc[i].fieldName) ) + { + fieldtype_t fieldtype = dmap->dataDesc[i].fieldType; + if (fieldtype != FIELD_INTEGER) + break; + + var.Set( fieldtype, ((char*)pTarget) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ] ); + + if (bRemove) + var.SetInt(var.Int() & ~iValue); + else + var.SetInt(var.Int() | iValue); + + DevMsg("FIELD TYPE: %i\n", fieldtype); + m_OutValue.Set(var, pTarget, this); + return true; + } + } + } + } + + m_OnFailed.FireOutput(pTarget, this); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Passes global variables, like curtime. +//----------------------------------------------------------------------------- +class CGameGlobalVars : public CLogicalEntity +{ + DECLARE_CLASS( CGameGlobalVars, CLogicalEntity ); +private: + + // Inputs + void InputGetCurtime( inputdata_t &inputdata ) { m_OutCurtime.Set(gpGlobals->curtime, inputdata.pActivator, this); } + void InputGetFrameCount( inputdata_t &inputdata ) { m_OutFrameCount.Set(gpGlobals->framecount, inputdata.pActivator, this); } + void InputGetFrametime( inputdata_t &inputdata ) { m_OutFrametime.Set(gpGlobals->frametime, inputdata.pActivator, this); } + void InputGetTickCount( inputdata_t &inputdata ) { m_OutTickCount.Set(gpGlobals->tickcount, inputdata.pActivator, this); } + void InputGetIntervalPerTick( inputdata_t &inputdata ) { m_OutIntervalPerTick.Set(gpGlobals->interval_per_tick, inputdata.pActivator, this); } + + // Outputs + COutputFloat m_OutCurtime; + COutputInt m_OutFrameCount; + COutputFloat m_OutFrametime; + COutputInt m_OutTickCount; + COutputInt m_OutIntervalPerTick; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(game_globalvars, CGameGlobalVars); + + +BEGIN_DATADESC( CGameGlobalVars ) + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "GetCurtime", InputGetCurtime ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetFrameCount", InputGetFrameCount ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetFrametime", InputGetFrametime ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetTickCount", InputGetTickCount ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetIntervalPerTick", InputGetIntervalPerTick ), + + // Outputs + DEFINE_OUTPUT(m_OutCurtime, "OutCurtime"), + DEFINE_OUTPUT(m_OutFrameCount, "OutFrameCount"), + DEFINE_OUTPUT(m_OutFrametime, "OutFrametime"), + DEFINE_OUTPUT(m_OutTickCount, "OutTickCount"), + DEFINE_OUTPUT(m_OutIntervalPerTick, "OutIntervalPerTick"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//void CGameGlobalVars::InputGetCurtime( inputdata_t &inputdata ) +//{ +// m_OutCurtime.Set(gpGlobals->curtime, inputdata.pActivator, this); +//} + + +#define MathModCalc(val1, val2, op) \ + switch (op) \ + { \ + case '+': val1 += val2; break; \ + case '-': val1 -= val2; break; \ + case '*': val1 *= val2; break; \ + case '/': val1 /= val2; break; \ + } \ + +//----------------------------------------------------------------------------- +// Purpose: Modifies values on the fly. +//----------------------------------------------------------------------------- +class CMathMod : public CLogicalEntity +{ + DECLARE_CLASS( CMathMod, CLogicalEntity ); +private: + + bool KeyValue(const char *szKeyName, const char *szValue); + + // Inputs + void InputSetMod( inputdata_t &inputdata ); + void InputSetOperator( inputdata_t &inputdata ); + + void InputModInt( inputdata_t &inputdata ); + void InputModFloat( inputdata_t &inputdata ); + void InputModVector( inputdata_t &inputdata ); + + // Outputs + COutputInt m_OutInt; + COutputFloat m_OutFloat; + COutputVector m_OutVector; + + int m_Operator; + + variant_t m_Mod; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_mod, CMathMod); + + +BEGIN_DATADESC( CMathMod ) + + DEFINE_KEYFIELD( m_Operator, FIELD_INTEGER, "SetOperator" ), + + DEFINE_VARIANT( m_Mod ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_INPUT, "SetMod", InputSetMod ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetOperator", InputSetOperator ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "ModInt", InputModInt ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "ModFloat", InputModFloat ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "ModVector", InputModVector ), + + // Outputs + DEFINE_OUTPUT(m_OutInt, "OutInt"), + DEFINE_OUTPUT(m_OutFloat, "OutFloat"), + DEFINE_OUTPUT(m_OutVector, "OutVector"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CMathMod::KeyValue(const char *szKeyName, const char *szValue) +{ + if (!stricmp(szKeyName, "startvalue")) + { + // It converts later anyway + m_Mod.SetString(AllocPooledString(szValue)); + return true; + } + + return BaseClass::KeyValue(szKeyName, szValue); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathMod::InputSetMod( inputdata_t &inputdata ) +{ + m_Mod = inputdata.value; + //if (inputdata.value.FieldType() == FIELD_STRING) + // m_Mod = Variant_Parse(inputdata.value.String()); + //else + // m_Mod = inputdata.value; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathMod::InputSetOperator( inputdata_t &inputdata ) +{ + m_Operator = inputdata.value.String()[0]; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathMod::InputModInt( inputdata_t &inputdata ) +{ + m_Mod.Convert(FIELD_INTEGER); + + DevMsg("Operator is %c you see\n", m_Operator); + + int out = inputdata.value.Int(); + MathModCalc(out, m_Mod.Int(), m_Operator); + + m_OutInt.Set( out, inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathMod::InputModFloat( inputdata_t &inputdata ) +{ + m_Mod.Convert(FIELD_FLOAT); + + float out = inputdata.value.Float(); + MathModCalc(out, m_Mod.Float(), m_Operator); + + m_OutFloat.Set( out, inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathMod::InputModVector( inputdata_t &inputdata ) +{ + m_Mod.Convert(FIELD_VECTOR); + + Vector out; + inputdata.value.Vector3D(out); + Vector mod; + m_Mod.Vector3D(mod); + MathModCalc(out, mod, m_Operator); + + m_OutVector.Set( out, inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets an entity's model information. +//----------------------------------------------------------------------------- +class CLogicModelInfo : public CLogicalEntity +{ + DECLARE_CLASS( CLogicModelInfo, CLogicalEntity ); +private: + + CBaseAnimating *GetTarget(inputdata_t &inputdata); + int GetPoseParameterIndex(CBaseAnimating *pTarget); + + // Inputs + //void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); m_hTarget = NULL; } + void InputGetNumSkins( inputdata_t &inputdata ); + void InputLookupSequence( inputdata_t &inputdata ); + void InputLookupActivity( inputdata_t &inputdata ); + + void InputSetPoseParameterName( inputdata_t &inputdata ); + void InputSetPoseParameterValue( inputdata_t &inputdata ); + void InputGetPoseParameter( inputdata_t &inputdata ); + + // Outputs + COutputInt m_OutNumSkins; + COutputInt m_OnHasSequence; + COutputEvent m_OnLacksSequence; + + COutputFloat m_OutPoseParameterValue; + + // KeyValues + + string_t m_iszPoseParameterName; + int m_iPoseParameterIndex = -1; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_modelinfo, CLogicModelInfo); + + +BEGIN_DATADESC( CLogicModelInfo ) + + DEFINE_KEYFIELD( m_iszPoseParameterName, FIELD_STRING, "PoseParameterName" ), + DEFINE_FIELD( m_iPoseParameterIndex, FIELD_INTEGER ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "GetNumSkins", InputGetNumSkins ), + DEFINE_INPUTFUNC( FIELD_STRING, "LookupSequence", InputLookupSequence ), + DEFINE_INPUTFUNC( FIELD_STRING, "LookupActivity", InputLookupActivity ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetPoseParameterName", InputSetPoseParameterName ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPoseParameterValue", InputSetPoseParameterValue ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetPoseParameter", InputGetPoseParameter ), + + // Outputs + DEFINE_OUTPUT(m_OutNumSkins, "OutNumSkins"), + DEFINE_OUTPUT(m_OnHasSequence, "OnHasSequence"), + DEFINE_OUTPUT(m_OnLacksSequence, "OnLacksSequence"), + DEFINE_OUTPUT(m_OutPoseParameterValue, "OutPoseParameterValue"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline CBaseAnimating *CLogicModelInfo::GetTarget(inputdata_t &inputdata) +{ + CBaseEntity *pEntity = gEntList.FindEntityByName(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + if (!pEntity || !pEntity->GetBaseAnimating()) + return NULL; + return pEntity->GetBaseAnimating(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CLogicModelInfo::GetPoseParameterIndex(CBaseAnimating *pTarget) +{ + if (m_iPoseParameterIndex == -1) + m_iPoseParameterIndex = pTarget->LookupPoseParameter(STRING(m_iszPoseParameterName)); + return m_iPoseParameterIndex; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicModelInfo::InputGetNumSkins( inputdata_t &inputdata ) +{ + CBaseAnimating *pAnimating = GetTarget(inputdata); + if (pAnimating && pAnimating->GetModelPtr()) + { + m_OutNumSkins.Set(pAnimating->GetModelPtr()->numskinfamilies(), pAnimating, this); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicModelInfo::InputLookupSequence( inputdata_t &inputdata ) +{ + CBaseAnimating *pAnimating = GetTarget(inputdata); + if (pAnimating && pAnimating->GetModelPtr()) + { + int index = pAnimating->LookupSequence(inputdata.value.String()); + + if (index != ACT_INVALID) + m_OnHasSequence.Set(index, pAnimating, this); + else + m_OnLacksSequence.FireOutput(pAnimating, this); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicModelInfo::InputLookupActivity( inputdata_t &inputdata ) +{ + CBaseAnimating *pAnimating = GetTarget(inputdata); + if (pAnimating && pAnimating->GetModelPtr()) + { + int iActivity = ActivityList_IndexForName(inputdata.value.String()); + if (iActivity == -1) + { + // Check if it's a raw activity ID + iActivity = atoi(inputdata.value.String()); + if (!ActivityList_NameForIndex(iActivity)) + { + Msg("%s received invalid LookupActivity %s\n", GetDebugName(), inputdata.value.String()); + return; + } + } + + int index = pAnimating->SelectWeightedSequence((Activity)iActivity); + + if (index != ACT_INVALID) + m_OnHasSequence.Set(index, pAnimating, this); + else + m_OnLacksSequence.FireOutput(pAnimating, this); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicModelInfo::InputSetPoseParameterName( inputdata_t &inputdata ) +{ + m_iszPoseParameterName = inputdata.value.StringID(); + m_iPoseParameterIndex = -1; + + CBaseAnimating *pAnimating = GetTarget(inputdata); + if (pAnimating && pAnimating->GetModelPtr()) + { + if (GetPoseParameterIndex(pAnimating) == -1) + Warning("%s: Pose parameter \"%s\" does not exist on %s\n", GetDebugName(), inputdata.value.String(), STRING(pAnimating->GetModelName())); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicModelInfo::InputSetPoseParameterValue( inputdata_t &inputdata ) +{ + CBaseAnimating *pAnimating = GetTarget(inputdata); + if (pAnimating && pAnimating->GetModelPtr()) + { + int index = GetPoseParameterIndex(pAnimating); + if (index != -1) + { + pAnimating->SetPoseParameter( index, inputdata.value.Float() ); + } + else + Warning("%s: Pose parameter \"%s\" does not exist on %s\n", GetDebugName(), STRING(m_iszPoseParameterName), STRING(pAnimating->GetModelName())); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicModelInfo::InputGetPoseParameter( inputdata_t &inputdata ) +{ + CBaseAnimating *pAnimating = GetTarget(inputdata); + if (pAnimating && pAnimating->GetModelPtr()) + { + int index = GetPoseParameterIndex(pAnimating); + if (index != -1) + { + m_OutPoseParameterValue.Set( pAnimating->GetPoseParameter( index ), pAnimating, this ); + } + else + Warning("%s: Pose parameter \"%s\" does not exist on %s\n", GetDebugName(), inputdata.value.String(), STRING(pAnimating->GetModelName())); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Checks and calculates an entity's position. +//----------------------------------------------------------------------------- +class CLogicEntityPosition : public CLogicalEntity +{ + DECLARE_CLASS( CLogicEntityPosition, CLogicalEntity ); +private: + EHANDLE m_hTarget; + + int m_iPositionType; + enum + { + POSITION_ORIGIN = 0, + POSITION_LOCAL, + POSITION_BBOX, + POSITION_EYES, + POSITION_EARS, + POSITION_ATTACHMENT, + }; + + // Something that accompanies the position type, like an attachment name. + string_t m_iszPositionParameter; + + CBaseEntity *GetTarget(CBaseEntity *pActivator, CBaseEntity *pCaller); + + Vector GetPosition(CBaseEntity *pEntity); + QAngle GetAngles(CBaseEntity *pEntity); + + // Inputs + void InputGetPosition( inputdata_t &inputdata ); + void InputSetPosition( inputdata_t &inputdata ); + void InputPredictPosition( inputdata_t &inputdata ); + + void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); m_hTarget = NULL; } + + // Outputs + COutputVector m_OutPosition; + COutputVector m_OutAngles; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_entity_position, CLogicEntityPosition); + +BEGIN_DATADESC( CLogicEntityPosition ) + + // Keys + DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iPositionType, FIELD_INTEGER, "PositionType" ), + DEFINE_KEYFIELD( m_iszPositionParameter, FIELD_STRING, "PositionParameter" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "GetPosition", InputGetPosition ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetPosition", InputSetPosition ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "PredictPosition", InputPredictPosition ), + + // Outputs + DEFINE_OUTPUT(m_OutPosition, "OutPosition"), + DEFINE_OUTPUT(m_OutAngles, "OutAngles"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline CBaseEntity *CLogicEntityPosition::GetTarget(CBaseEntity *pActivator, CBaseEntity *pCaller) +{ + // Always reset with procedurals + if (!m_hTarget || STRING(m_target)[0] == '!') + m_hTarget = gEntList.FindEntityByName(NULL, STRING(m_target), this, pActivator, pCaller); + return m_hTarget.Get(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Vector CLogicEntityPosition::GetPosition(CBaseEntity *pEntity) +{ + switch (m_iPositionType) + { + case POSITION_ORIGIN: return pEntity->GetAbsOrigin(); + case POSITION_LOCAL: return pEntity->GetLocalOrigin(); + case POSITION_BBOX: return pEntity->WorldSpaceCenter(); + case POSITION_EYES: return pEntity->EyePosition(); + case POSITION_EARS: return pEntity->EarPosition(); + case POSITION_ATTACHMENT: + { + CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); + if (!pAnimating) + { + Warning("%s wants to measure one of %s's attachments, but %s doesn't support them!\n", GetDebugName(), pEntity->GetDebugName(), pEntity->GetDebugName()); + break; + } + + Vector vecPosition; + pAnimating->GetAttachment(STRING(m_iszPositionParameter), vecPosition); + return vecPosition; + } + } + + return vec3_origin; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +QAngle CLogicEntityPosition::GetAngles(CBaseEntity *pEntity) +{ + switch (m_iPositionType) + { + case POSITION_BBOX: + case POSITION_EARS: + case POSITION_ORIGIN: return pEntity->GetAbsAngles(); break; + case POSITION_LOCAL: return pEntity->GetLocalAngles(); break; + case POSITION_EYES: return pEntity->EyeAngles(); break; + case POSITION_ATTACHMENT: + { + CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); + if (!pAnimating) + { + Warning("%s wants to measure one of %s's attachments, but %s doesn't support them!\n", GetDebugName(), pEntity->GetDebugName(), pEntity->GetDebugName()); + break; + } + + QAngle AttachmentAngles; + matrix3x4_t attachmentToWorld; + pAnimating->GetAttachment( pAnimating->LookupAttachment( STRING( m_iszPositionParameter ) ), attachmentToWorld ); + MatrixAngles( attachmentToWorld, AttachmentAngles ); + return AttachmentAngles; + } break; + } + + return vec3_angle; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicEntityPosition::InputGetPosition( inputdata_t &inputdata ) +{ + CBaseEntity *pEntity = GetTarget(inputdata.pActivator, inputdata.pCaller); + if (!pEntity) + { + m_OutPosition.Set( vec3_origin, NULL, this ); + m_OutAngles.Set( vec3_angle, NULL, this ); + return; + } + + m_OutPosition.Set( GetPosition(pEntity), pEntity, this ); + m_OutAngles.Set( GetAngles(pEntity), pEntity, this ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicEntityPosition::InputSetPosition( inputdata_t &inputdata ) +{ + CBaseEntity *pEntity = GetTarget(inputdata.pActivator, inputdata.pCaller); + if (!pEntity) + { + Warning("%s can't find entity %s for SetPosition!\n", GetDebugName(), STRING(m_target)); + return; + } + + Vector vec; + inputdata.value.Vector3D(vec); + + // If the position is local, they might want to move local origin instead + if (m_iPositionType == POSITION_LOCAL) + pEntity->SetLocalOrigin(vec); + else + pEntity->SetAbsOrigin(vec); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicEntityPosition::InputPredictPosition( inputdata_t &inputdata ) +{ + CBaseEntity *pEntity = GetTarget(inputdata.pActivator, inputdata.pCaller); + if (!pEntity) + { + m_OutPosition.Set( vec3_origin, NULL, this ); + m_OutAngles.Set( vec3_angle, NULL, this ); + return; + } + + Vector vecPosition; + UTIL_PredictedPosition(pEntity, GetPosition(pEntity), inputdata.value.Float(), &vecPosition); + + QAngle angAngles; + UTIL_PredictedAngles(pEntity, GetAngles(pEntity), inputdata.value.Float(), &angAngles); + + m_OutPosition.Set( vecPosition, pEntity, this ); + m_OutAngles.Set( angAngles, pEntity, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Accesses context values +//----------------------------------------------------------------------------- +class CLogicContextAccessor : public CLogicalEntity +{ + DECLARE_CLASS(CLogicContextAccessor, CLogicalEntity); + +public: + CBaseEntity *GetTarget(CBaseEntity *pCaller, CBaseEntity *pActivator); + + bool TestContext(CBaseEntity *pTarget, const char *szKeyName); + void SetContext(CBaseEntity *pTarget, const char *szKeyName, string_t szValue); + + // Inputs + void InputTest(inputdata_t &inputdata); + void InputTestContext(inputdata_t &inputdata); + void InputTestTarget(inputdata_t &inputdata); + + void InputSetContext(inputdata_t &inputdata); + + void InputSetValue(inputdata_t &inputdata); + + //bool ReadUnregisteredKeyfields(CBaseEntity *pTarget, const char *szKeyName, variant_t *variant); + + COutputString m_OutValue; + COutputEvent m_OnFailed; + + string_t m_iszContext; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_context_accessor, CLogicContextAccessor); + + +BEGIN_DATADESC(CLogicContextAccessor) + +DEFINE_KEYFIELD( m_iszContext, FIELD_STRING, "context" ), + +// Inputs +DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest), +DEFINE_INPUTFUNC(FIELD_STRING, "TestContext", InputTestContext), +DEFINE_INPUTFUNC(FIELD_STRING, "TestTarget", InputTestTarget), +DEFINE_INPUTFUNC(FIELD_STRING, "SetContext", InputSetContext), +DEFINE_INPUTFUNC(FIELD_STRING, "SetValue", InputSetValue), + +DEFINE_OUTPUT( m_OutValue, "OutValue" ), +DEFINE_OUTPUT( m_OnFailed, "OnFailed" ), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline CBaseEntity *CLogicContextAccessor::GetTarget(CBaseEntity *pCaller, CBaseEntity *pActivator) +{ + return gEntList.FindEntityByName(NULL, m_target, this, pActivator, pCaller); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicContextAccessor::TestContext(CBaseEntity *pTarget, const char *szKeyName) +{ + int idx = pTarget->FindContextByName( szKeyName ); + if ( idx != -1 ) + { + m_OutValue.Set(FindPooledString(pTarget->GetContextValue(idx)), pTarget, this); + return true; + } + else + { + m_OnFailed.FireOutput(pTarget, this); + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicContextAccessor::SetContext(CBaseEntity *pTarget, const char *szKeyName, string_t szValue) +{ + pTarget->AddContext(szKeyName, STRING(szValue)); + + m_OutValue.Set(szValue, pTarget, this); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicContextAccessor::InputTest(inputdata_t &inputdata) +{ + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (pTarget && m_iszContext != NULL_STRING) + { + TestContext(pTarget, STRING(m_iszContext)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicContextAccessor::InputTestContext(inputdata_t &inputdata) +{ + const char *input = inputdata.value.String(); + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (input && pTarget) + { + TestContext(pTarget, input); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicContextAccessor::InputTestTarget(inputdata_t &inputdata) +{ + m_target = inputdata.value.StringID(); + CBaseEntity *pTarget = gEntList.FindEntityByName(NULL, inputdata.value.StringID(), this, inputdata.pCaller, inputdata.pActivator); + if (pTarget && m_iszContext != NULL_STRING) + { + TestContext(pTarget, STRING(m_iszContext)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicContextAccessor::InputSetContext(inputdata_t &inputdata) +{ + m_iszContext = inputdata.value.StringID(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicContextAccessor::InputSetValue(inputdata_t &inputdata) +{ + CBaseEntity *pTarget = GetTarget(inputdata.pCaller, inputdata.pActivator); + if (pTarget) + { + SetContext(pTarget, STRING(m_iszContext), inputdata.value.StringID()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Replicates light pattern functionality. +//----------------------------------------------------------------------------- +class CMathLightPattern : public CLogicalEntity +{ + DECLARE_CLASS( CMathLightPattern, CLogicalEntity ); +private: + + + string_t m_iszPattern; + + bool m_bDisabled; + + void Spawn(); + bool KeyValue( const char *szKeyName, const char *szValue ); + + void OutputCurPattern(); + + void StartPatternThink(); + void PatternThink(); + unsigned char m_NextLetter = 0; + + // How fast the pattern should be + float m_flPatternSpeed = 0.1f; + + inline bool VerifyPatternValid() { return (m_iszPattern != NULL_STRING && STRING( m_iszPattern )[0] != '\0'); } + + // Inputs + void InputSetStyle( inputdata_t &inputdata ); + void InputSetPattern( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + // Outputs + COutputFloat m_OutValue; + COutputString m_OutLetter; + COutputEvent m_OnLightOn; + COutputEvent m_OnLightOff; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( math_lightpattern, CMathLightPattern ); + +BEGIN_DATADESC( CMathLightPattern ) + + // Keys + DEFINE_KEYFIELD(m_iszPattern, FIELD_STRING, "pattern"), + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_KEYFIELD(m_flPatternSpeed, FIELD_FLOAT, "PatternSpeed"), + + // Inputs + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetStyle", InputSetStyle ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetPattern", InputSetPattern ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + + // Outputs + DEFINE_OUTPUT(m_OutValue, "OutValue"), + DEFINE_OUTPUT(m_OutLetter, "OutLetter"), + DEFINE_OUTPUT(m_OnLightOn, "OnLightOn"), + DEFINE_OUTPUT(m_OnLightOff, "OnLightOff"), + + DEFINE_THINKFUNC( PatternThink ), + DEFINE_FIELD( m_NextLetter, FIELD_CHARACTER ), + +END_DATADESC() + +extern const char *GetDefaultLightstyleString( int styleIndex ); + +static const char *s_pLightPatternContext = "PatternContext"; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::Spawn() +{ + BaseClass::Spawn(); + + if (!m_bDisabled && VerifyPatternValid()) + StartPatternThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::OutputCurPattern() +{ + // This code looks messy, but it does what it's supposed to and is safe enough. + // First, we get the next letter in the pattern sequence. + // Next, we calculate its integral proximity to the character 'a' (fully dark) + // and calculate its approximate brightness by dividing it by the number of letters in the alphabet other than a. + // We output that brightness value for things like projected textures and other custom intensity values + // so they could replicate the patterns of their corresponding vrad lights. + char cLetter = STRING(m_iszPattern)[m_NextLetter]; + int iValue = (cLetter - 'a'); + float flResult = iValue != 0 ? ((float)iValue / 25.0f) : 0.0f; + m_OutValue.Set(flResult, this, this); + + // User-friendly "Light on, light off" outputs + if (flResult > 0) + m_OnLightOn.FireOutput(this, this); + else + m_OnLightOff.FireOutput(this, this); + + // Create a string with cLetter and a null terminator. + char szLetter[2] = { cLetter, '\0' }; + m_OutLetter.Set( AllocPooledString(szLetter), this, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::StartPatternThink() +{ + // Output our current/next one immediately. + OutputCurPattern(); + + // Start thinking now. + SetContextThink( &CMathLightPattern::PatternThink, gpGlobals->curtime, s_pLightPatternContext ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::PatternThink() +{ + // Output our current/next one + OutputCurPattern(); + + // Increment + m_NextLetter++; + if (STRING(m_iszPattern)[m_NextLetter] == '\0') + m_NextLetter = 0; + + //m_OutLetter.Set(AllocPooledString(UTIL_VarArgs("%c", m_NextLetter)), this, this); + + SetNextThink( gpGlobals->curtime + m_flPatternSpeed, s_pLightPatternContext ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMathLightPattern::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "style" ) ) + { + m_iszPattern = AllocPooledString(GetDefaultLightstyleString(atoi(szValue))); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::InputSetStyle( inputdata_t &inputdata ) +{ + m_iszPattern = AllocPooledString(GetDefaultLightstyleString(inputdata.value.Int())); + m_NextLetter = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::InputSetPattern( inputdata_t &inputdata ) +{ + m_iszPattern = inputdata.value.StringID(); + m_NextLetter = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::InputEnable( inputdata_t &inputdata ) +{ + if (VerifyPatternValid()) + StartPatternThink(); + else + Warning("%s tried to enable without valid pattern\n", GetDebugName()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::InputDisable( inputdata_t &inputdata ) +{ + SetContextThink( NULL, TICK_NEVER_THINK, s_pLightPatternContext ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathLightPattern::InputToggle( inputdata_t &inputdata ) +{ + if (GetNextThink(s_pLightPatternContext) != TICK_NEVER_THINK) + InputDisable(inputdata); + else + InputEnable(inputdata); +} + +//----------------------------------------------------------------------------- +// Purpose: Sequences for keypads, etc. +//----------------------------------------------------------------------------- +#define MAX_SEQUENCE_CASES 16 + +class CLogicSequence : public CLogicalEntity +{ + DECLARE_CLASS( CLogicSequence, CLogicalEntity ); +public: + CLogicSequence(); + + void Activate(); + + bool KeyValue( const char *szKeyName, const char *szValue ); + + void TestCase( int iCase, string_t iszValue, CBaseEntity *pActivator ); + void SequenceComplete( string_t iszValue, CBaseEntity *pActivator ); + +private: + string_t m_iszCase[MAX_SEQUENCE_CASES]; + int m_iNumCases; + + bool m_bDisabled; + + bool m_bDontIncrementOnPass; + + // Inputs + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + void InputInValue( inputdata_t &inputdata ); + void InputSetCurrentCase( inputdata_t &inputdata ); + void InputSetCurrentCaseNoFire( inputdata_t &inputdata ); + void InputIncrementSequence( inputdata_t &inputdata ); + void InputResetSequence( inputdata_t &inputdata ); + + // Outputs + COutputInt m_CurCase; + COutputString m_OnCasePass; + COutputString m_OnCaseFail; + COutputString m_OnSequenceComplete; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( logic_sequence, CLogicSequence ); + + +BEGIN_DATADESC( CLogicSequence ) + + // Keys + DEFINE_KEYFIELD( m_iszCase[0], FIELD_STRING, "Case01" ), + DEFINE_KEYFIELD( m_iszCase[1], FIELD_STRING, "Case02" ), + DEFINE_KEYFIELD( m_iszCase[2], FIELD_STRING, "Case03" ), + DEFINE_KEYFIELD( m_iszCase[3], FIELD_STRING, "Case04" ), + DEFINE_KEYFIELD( m_iszCase[4], FIELD_STRING, "Case05" ), + DEFINE_KEYFIELD( m_iszCase[5], FIELD_STRING, "Case06" ), + DEFINE_KEYFIELD( m_iszCase[6], FIELD_STRING, "Case07" ), + DEFINE_KEYFIELD( m_iszCase[7], FIELD_STRING, "Case08" ), + DEFINE_KEYFIELD( m_iszCase[8], FIELD_STRING, "Case09" ), + DEFINE_KEYFIELD( m_iszCase[9], FIELD_STRING, "Case10" ), + DEFINE_KEYFIELD( m_iszCase[10], FIELD_STRING, "Case11" ), + DEFINE_KEYFIELD( m_iszCase[11], FIELD_STRING, "Case12" ), + DEFINE_KEYFIELD( m_iszCase[12], FIELD_STRING, "Case13" ), + DEFINE_KEYFIELD( m_iszCase[13], FIELD_STRING, "Case14" ), + DEFINE_KEYFIELD( m_iszCase[14], FIELD_STRING, "Case15" ), + DEFINE_KEYFIELD( m_iszCase[15], FIELD_STRING, "Case16" ), + + // This doesn't need to be saved, it can be assigned every Activate() + //DEFINE_FIELD( m_iNumCases, FIELD_INTEGER ), + + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + DEFINE_KEYFIELD( m_bDontIncrementOnPass, FIELD_BOOLEAN, "DontIncrementOnPass" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + DEFINE_INPUTFUNC( FIELD_STRING, "InValue", InputInValue ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCurrentCase", InputSetCurrentCase ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCurrentCaseNoFire", InputSetCurrentCaseNoFire ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "IncrementSequence", InputIncrementSequence ), + DEFINE_INPUTFUNC( FIELD_VOID, "ResetSequence", InputResetSequence ), + + // Outputs + DEFINE_OUTPUT( m_CurCase, "OutCurCase" ), + DEFINE_OUTPUT( m_OnCasePass, "OnCasePass" ), + DEFINE_OUTPUT( m_OnCaseFail, "OnCaseFail" ), + DEFINE_OUTPUT( m_OnSequenceComplete, "OnSequenceComplete" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CLogicSequence::CLogicSequence() +{ + m_CurCase.Init( 1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::Activate( void ) +{ + BaseClass::Activate(); + + // Count number of cases + for (m_iNumCases = 0; m_iNumCases < MAX_SEQUENCE_CASES; m_iNumCases++) + { + if (m_iszCase[m_iNumCases] == NULL_STRING) + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CLogicSequence::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq( szKeyName, "StartCase" )) + { + m_CurCase.Init( atoi(szValue) ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::TestCase( int iCase, string_t iszValue, CBaseEntity *pActivator ) +{ + if (m_bDisabled) + { + DevMsg("%s ignoring case test because it is disabled\n", GetDebugName()); + return; + } + + // Arrays are 0-based, so the index is (iCase - 1) + int iIndex = iCase - 1; + if (iIndex >= m_iNumCases) + { + DevMsg("%s ignoring case test because the current case %i is greater than or equal to the number of cases %i\n", GetDebugName(), iCase, m_iNumCases); + return; + } + + if (Matcher_Match( STRING( m_iszCase[iIndex] ), STRING(iszValue) )) + { + m_OnCasePass.Set( iszValue, pActivator, this ); + + if (!m_bDontIncrementOnPass) + { + m_CurCase.Set(iCase + 1, pActivator, this); + + if (m_CurCase.Get() > m_iNumCases) + { + // Sequence complete! + SequenceComplete(iszValue, pActivator); + } + } + else + { + m_CurCase.Set(iCase, pActivator, this); + } + } + else + { + m_OnCaseFail.Set( iszValue, pActivator, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::SequenceComplete( string_t iszValue, CBaseEntity *pActivator ) +{ + m_OnSequenceComplete.Set( iszValue, pActivator, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputToggle( inputdata_t &inputdata ) +{ + m_bDisabled = (m_bDisabled == false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputInValue( inputdata_t &inputdata ) +{ + TestCase( m_CurCase.Get(), inputdata.value.StringID(), inputdata.pActivator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputSetCurrentCase( inputdata_t &inputdata ) +{ + m_CurCase.Set( inputdata.value.Int(), inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputSetCurrentCaseNoFire( inputdata_t &inputdata ) +{ + m_CurCase.Init( inputdata.value.Int() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputIncrementSequence( inputdata_t &inputdata ) +{ + int iInc = inputdata.value.Int(); + m_CurCase.Set( m_CurCase.Get() + (iInc != 0 ? iInc : 1), inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSequence::InputResetSequence( inputdata_t &inputdata ) +{ + m_CurCase.Set( 1, inputdata.pActivator, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Generates various types of numbers based on existing material proxies +//----------------------------------------------------------------------------- +class CMathGenerate : public CLogicalEntity +{ +public: + DECLARE_CLASS( CMathGenerate, CLogicalEntity ); + CMathGenerate(); + + enum GenerateType_t + { + GENERATE_SINE_WAVE, + GENERATE_LINEAR_RAMP, + GENERATE_UNIFORM_NOISE, + GENERATE_GAUSSIAN_NOISE, + GENERATE_EXPONENTIAL, + }; + + // Keys + float m_flMax; + float m_flMin; + + float m_flParam1; + float m_flParam2; + + bool m_bDisabled; + + GenerateType_t m_iGenerateType; + + // Inputs + void InputSetValue( inputdata_t &inputdata ); + void InputSetValueNoFire( inputdata_t &inputdata ); + void InputGetValue( inputdata_t &inputdata ); + void InputSetGenerateType( inputdata_t &inputdata ); + + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + void UpdateOutValue( float fNewValue, CBaseEntity *pActivator = NULL ); + void UpdateOutValueSine( float fNewValue, CBaseEntity *pActivator = NULL ); + + // Basic functions + void Spawn(); + bool KeyValue( const char *szKeyName, const char *szValue ); + + void StartGenerating(); + void StopGenerating(); + + // Number generation functions + void GenerateSineWave(); + void GenerateLinearRamp(); + void GenerateUniformNoise(); + void GenerateGaussianNoise(); + void GenerateExponential(); + + // The gaussian stream normally only exists on the client, so we use our own. + static CGaussianRandomStream m_GaussianStream; + + bool m_bHitMin; // Set when we reach or go below our minimum value, cleared if we go above it again. + bool m_bHitMax; // Set when we reach or exceed our maximum value, cleared if we fall below it again. + + // Outputs + COutputFloat m_OutValue; + COutputFloat m_OnGetValue; // Used for polling the counter value. + COutputEvent m_OnHitMin; + COutputEvent m_OnHitMax; + COutputEvent m_OnChangedFromMin; + COutputEvent m_OnChangedFromMax; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( math_generate, CMathGenerate ); + + +BEGIN_DATADESC( CMathGenerate ) + + DEFINE_INPUT( m_flMax, FIELD_FLOAT, "SetHitMax" ), + DEFINE_INPUT( m_flMin, FIELD_FLOAT, "SetHitMin" ), + DEFINE_INPUT( m_flParam1, FIELD_FLOAT, "SetParam1" ), + DEFINE_INPUT( m_flParam2, FIELD_FLOAT, "SetParam2" ), + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + DEFINE_KEYFIELD( m_iGenerateType, FIELD_INTEGER, "GenerateType" ), + + DEFINE_FIELD( m_bHitMax, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bHitMin, FIELD_BOOLEAN ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetValue", InputSetValue ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetValueNoFire", InputSetValueNoFire ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetValue", InputGetValue ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGenerateType", InputSetGenerateType ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + + DEFINE_OUTPUT( m_OutValue, "OutValue" ), + DEFINE_OUTPUT( m_OnHitMin, "OnHitMin" ), + DEFINE_OUTPUT( m_OnHitMax, "OnHitMax" ), + DEFINE_OUTPUT( m_OnGetValue, "OnGetValue" ), + DEFINE_OUTPUT( m_OnChangedFromMin, "OnChangedFromMin" ), + DEFINE_OUTPUT( m_OnChangedFromMax, "OnChangedFromMax" ), + + DEFINE_THINKFUNC( GenerateSineWave ), + DEFINE_THINKFUNC( GenerateLinearRamp ), + DEFINE_THINKFUNC( GenerateUniformNoise ), + DEFINE_THINKFUNC( GenerateGaussianNoise ), + DEFINE_THINKFUNC( GenerateExponential ), + +END_DATADESC() + +CGaussianRandomStream CMathGenerate::m_GaussianStream; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMathGenerate::CMathGenerate() +{ + m_GaussianStream.AttachToStream( random ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathGenerate::Spawn() +{ + BaseClass::Spawn(); + + if (!m_bDisabled) + StartGenerating(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMathGenerate::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq( szKeyName, "InitialValue" )) + { + m_OutValue.Init( atof(szValue) ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputSetValue( inputdata_t &inputdata ) +{ + UpdateOutValue(inputdata.value.Float(), inputdata.pActivator); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputSetValueNoFire( inputdata_t &inputdata ) +{ + m_OutValue.Init(inputdata.value.Float()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputGetValue( inputdata_t &inputdata ) +{ + float flOutValue = m_OutValue.Get(); + m_OnGetValue.Set( flOutValue, inputdata.pActivator, inputdata.pCaller ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputSetGenerateType( inputdata_t &inputdata ) +{ + m_iGenerateType = (GenerateType_t)inputdata.value.Int(); + + if (GetNextThink() != TICK_NEVER_THINK) + { + // Change our generation function if we're already generating. + // StartGenerating() should set to the new function. + StartGenerating(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; + StartGenerating(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; + StopGenerating(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::InputToggle( inputdata_t &inputdata ) +{ + m_bDisabled ? InputEnable(inputdata) : InputDisable(inputdata); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value to the new value, clamping and firing the output value. +// Input : fNewValue - Value to set. +//----------------------------------------------------------------------------- +void CMathGenerate::UpdateOutValue( float fNewValue, CBaseEntity *pActivator ) +{ + if ((m_flMin != 0) || (m_flMax != 0)) + { + // + // Fire an output any time we reach or exceed our maximum value. + // + if ( fNewValue >= m_flMax || (m_iGenerateType == GENERATE_SINE_WAVE && fNewValue >= (m_flMax * 0.995f)) ) + { + if ( !m_bHitMax ) + { + m_bHitMax = true; + m_OnHitMax.FireOutput( pActivator, this ); + } + } + else + { + // Fire an output if we just changed from the maximum value + if ( m_OutValue.Get() == m_flMax ) + { + m_OnChangedFromMax.FireOutput( pActivator, this ); + } + + m_bHitMax = false; + } + + // + // Fire an output any time we reach or go below our minimum value. + // + if ( fNewValue <= m_flMin ) + { + if ( !m_bHitMin ) + { + m_bHitMin = true; + m_OnHitMin.FireOutput( pActivator, this ); + } + } + else + { + // Fire an output if we just changed from the maximum value + if ( m_OutValue.Get() == m_flMin ) + { + m_OnChangedFromMin.FireOutput( pActivator, this ); + } + + m_bHitMin = false; + } + + fNewValue = clamp(fNewValue, m_flMin, m_flMax); + } + + m_OutValue.Set(fNewValue, pActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value to the new value, clamping and firing the output value. +// Sine generation needs to use a different function to account for skips and imprecision. +// Input : fNewValue - Value to set. +//----------------------------------------------------------------------------- +void CMathGenerate::UpdateOutValueSine( float fNewValue, CBaseEntity *pActivator ) +{ + if ((m_flMin != 0) || (m_flMax != 0)) + { + // + // Fire an output any time we reach or exceed our maximum value. + // + if ( fNewValue >= (m_flMax * 0.995f) ) + { + if ( !m_bHitMax ) + { + m_bHitMax = true; + m_OnHitMax.FireOutput( pActivator, this ); + } + } + else + { + // Fire an output if we just changed from the maximum value + if ( m_bHitMax ) + { + m_OnChangedFromMax.FireOutput( pActivator, this ); + } + + m_bHitMax = false; + } + + // + // Fire an output any time we reach or go below our minimum value. + // + if ( fNewValue <= (m_flMin * 1.005f) ) + { + if ( !m_bHitMin ) + { + m_bHitMin = true; + m_OnHitMin.FireOutput( pActivator, this ); + } + } + else + { + // Fire an output if we just changed from the maximum value + if ( m_bHitMin ) + { + m_OnChangedFromMin.FireOutput( pActivator, this ); + } + + m_bHitMin = false; + } + + //fNewValue = clamp(fNewValue, m_flMin, m_flMax); + } + + m_OutValue.Set(fNewValue, pActivator, this); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::StartGenerating() +{ + // Correct any min/max quirks here + if (m_flMin > m_flMax) + { + float flTemp = m_flMin; + m_flMin = m_flMax; + m_flMax = flTemp; + } + + switch (m_iGenerateType) + { + case GENERATE_SINE_WAVE: + SetThink( &CMathGenerate::GenerateSineWave ); + break; + case GENERATE_LINEAR_RAMP: + SetThink( &CMathGenerate::GenerateLinearRamp ); + break; + case GENERATE_UNIFORM_NOISE: + SetThink( &CMathGenerate::GenerateUniformNoise ); + break; + case GENERATE_GAUSSIAN_NOISE: + SetThink( &CMathGenerate::GenerateGaussianNoise ); + break; + case GENERATE_EXPONENTIAL: + SetThink( &CMathGenerate::GenerateExponential ); + break; + + default: + Warning("%s is set to invalid generation type %i! It won't do anything now.\n", GetDebugName(), m_iGenerateType); + StopGenerating(); + return; + } + + // All valid types should fall through to this + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::StopGenerating() +{ + SetThink(NULL); + SetNextThink( TICK_NEVER_THINK ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::GenerateSineWave() +{ + // CSineProxy in mathproxy.cpp + float flSineTimeOffset = m_flParam2; + float flSinePeriod = m_flParam1; + float flValue; + + if (flSinePeriod == 0) + flSinePeriod = 1; + + // get a value in [0,1] + flValue = ( sin( 2.0f * M_PI * (gpGlobals->curtime - flSineTimeOffset) / flSinePeriod ) * 0.5f ) + 0.5f; + // get a value in [min,max] + flValue = ( m_flMax - m_flMin ) * flValue + m_flMin; + + UpdateOutValueSine( flValue ); + + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::GenerateLinearRamp() +{ + // CLinearRampProxy in mathproxy.cpp + + // Param1 = rate + float flVal = m_flParam1 * gpGlobals->curtime + m_OutValue.Get(); + + // clamp + if (flVal < m_flMin) + flVal = m_flMin; + else if (flVal > m_flMax) + flVal = m_flMax; + + UpdateOutValue( flVal ); + + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::GenerateUniformNoise() +{ + // CUniformNoiseProxy in mathproxy.cpp + + UpdateOutValue( random->RandomFloat( m_flMin, m_flMax ) ); + + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::GenerateGaussianNoise() +{ + // CGaussianNoiseProxy in mathproxy.cpp + + float flMean = m_flParam1; + float flStdDev = m_flParam2; + float flVal = m_GaussianStream.RandomFloat( flMean, flStdDev ); + + // clamp + if (flVal < m_flMin) + flVal = m_flMin; + else if (flVal > m_flMax) + flVal = m_flMax; + + UpdateOutValue( flVal ); + + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathGenerate::GenerateExponential() +{ + // CExponentialProxy in mathproxy.cpp + + // Param1 = scale + // Param2 = offset + float flVal = m_flParam1 * exp( m_OutValue.Get() + m_flParam2 ); + + // clamp + if (flVal < m_flMin) + flVal = m_flMin; + else if (flVal > m_flMax) + flVal = m_flMax; + + UpdateOutValue( flVal ); + + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); +} +#endif diff --git a/mp/src/game/server/logicrelay.cpp b/mp/src/game/server/logicrelay.cpp index b556ec04..6daa15f4 100644 --- a/mp/src/game/server/logicrelay.cpp +++ b/mp/src/game/server/logicrelay.cpp @@ -15,6 +15,10 @@ #include "eventqueue.h" #include "soundent.h" #include "logicrelay.h" +#ifdef MAPBASE +#include "mapbase/variant_tools.h" +#include "saverestore_utlvector.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -29,6 +33,11 @@ BEGIN_DATADESC( CLogicRelay ) DEFINE_FIELD(m_bWaitForRefire, FIELD_BOOLEAN), DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"), +#if RELAY_QUEUE_SYSTEM + DEFINE_KEYFIELD(m_bQueueTrigger, FIELD_BOOLEAN, "QueueDisabledTrigger"), + DEFINE_FIELD(m_bQueueWaiting, FIELD_BOOLEAN), + DEFINE_FIELD(m_flRefireTime, FIELD_TIME), +#endif // Inputs DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), @@ -36,10 +45,16 @@ BEGIN_DATADESC( CLogicRelay ) DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), DEFINE_INPUTFUNC(FIELD_VOID, "Trigger", InputTrigger), +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_INPUT, "TriggerWithParameter", InputTriggerWithParameter), +#endif DEFINE_INPUTFUNC(FIELD_VOID, "CancelPending", InputCancelPending), // Outputs DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"), +#ifdef MAPBASE + DEFINE_OUTPUT(m_OnTriggerParameter, "OnTriggerParameter"), +#endif DEFINE_OUTPUT(m_OnSpawn, "OnSpawn"), END_DATADESC() @@ -93,6 +108,11 @@ void CLogicRelay::Think() void CLogicRelay::InputEnable( inputdata_t &inputdata ) { m_bDisabled = false; + +#if RELAY_QUEUE_SYSTEM + if (m_bQueueWaiting) + m_OnTrigger.FireOutput( inputdata.pActivator, this ); +#endif } //------------------------------------------------------------------------------ @@ -132,6 +152,11 @@ void CLogicRelay::InputDisable( inputdata_t &inputdata ) void CLogicRelay::InputToggle( inputdata_t &inputdata ) { m_bDisabled = !m_bDisabled; + +#if RELAY_QUEUE_SYSTEM + if (m_bQueueWaiting) + m_OnTrigger.FireOutput( inputdata.pActivator, this ); +#endif } @@ -144,6 +169,45 @@ void CLogicRelay::InputTrigger( inputdata_t &inputdata ) { m_OnTrigger.FireOutput( inputdata.pActivator, this ); + if (m_spawnflags & SF_REMOVE_ON_FIRE) + { + UTIL_Remove(this); + } + else if (!(m_spawnflags & SF_ALLOW_FAST_RETRIGGER)) + { + // + // Disable the relay so that it cannot be refired until after the last output + // has been fired and post an input to re-enable ourselves. + // + m_bWaitForRefire = true; + g_EventQueue.AddEvent(this, "EnableRefire", m_OnTrigger.GetMaxDelay() + 0.001, this, this); +#if RELAY_QUEUE_SYSTEM + if (m_bQueueTrigger) + m_flRefireTime = gpGlobals->curtime + m_OnTrigger.GetMaxDelay() + 0.002; +#endif + } + } +#if RELAY_QUEUE_SYSTEM + else if (m_bQueueTrigger) + { + if (m_bDisabled) + m_bQueueWaiting = true; + else // m_bWaitForRefire + m_OnTrigger.FireOutput( inputdata.pActivator, this, (gpGlobals->curtime - m_flRefireTime) ); + } +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler that triggers the relay. +//----------------------------------------------------------------------------- +void CLogicRelay::InputTriggerWithParameter( inputdata_t &inputdata ) +{ + if ((!m_bDisabled) && (!m_bWaitForRefire)) + { + m_OnTriggerParameter.Set( inputdata.value, inputdata.pActivator, this ); + if (m_spawnflags & SF_REMOVE_ON_FIRE) { UTIL_Remove(this); @@ -159,4 +223,258 @@ void CLogicRelay::InputTrigger( inputdata_t &inputdata ) } } } +#endif + +#ifdef MAPBASE + +BEGIN_SIMPLE_DATADESC( LogicRelayQueueInfo_t ) + + DEFINE_FIELD( TriggerWithParameter, FIELD_BOOLEAN ), + DEFINE_FIELD( pActivator, FIELD_CLASSPTR ), + DEFINE_VARIANT( value ), + DEFINE_FIELD( outputID, FIELD_INTEGER ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS(logic_relay_queue, CLogicRelayQueue); + + +BEGIN_DATADESC( CLogicRelayQueue ) + + DEFINE_FIELD(m_bWaitForRefire, FIELD_BOOLEAN), + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"), + + DEFINE_INPUT(m_iMaxQueueItems, FIELD_INTEGER, "SetMaxQueueItems"), + DEFINE_KEYFIELD(m_bDontQueueWhenDisabled, FIELD_BOOLEAN, "DontQueueWhenDisabled"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), + DEFINE_INPUTFUNC(FIELD_VOID, "EnableRefire", InputEnableRefire), + DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + DEFINE_INPUTFUNC(FIELD_VOID, "Trigger", InputTrigger), + DEFINE_INPUTFUNC(FIELD_INPUT, "TriggerWithParameter", InputTriggerWithParameter), + DEFINE_INPUTFUNC(FIELD_VOID, "CancelPending", InputCancelPending), + DEFINE_INPUTFUNC(FIELD_VOID, "ClearQueue", InputClearQueue), + + // Outputs + DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"), + DEFINE_OUTPUT(m_OnTriggerParameter, "OnTriggerParameter"), + + DEFINE_UTLVECTOR(m_QueueItems, FIELD_EMBEDDED), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CLogicRelayQueue::CLogicRelayQueue(void) +{ +} + + +//------------------------------------------------------------------------------ +// Purpose: Turns on the relay, allowing it to fire outputs. +//------------------------------------------------------------------------------ +void CLogicRelayQueue::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; + + if (!m_bWaitForRefire && m_QueueItems.Count() > 0) + HandleNextQueueItem(); +} + +//------------------------------------------------------------------------------ +// Purpose: Enables us to fire again. This input is only posted from our Trigger +// function to prevent rapid refire. +//------------------------------------------------------------------------------ +void CLogicRelayQueue::InputEnableRefire( inputdata_t &inputdata ) +{ + m_bWaitForRefire = false; + + if (!m_bDisabled && m_QueueItems.Count() > 0) + HandleNextQueueItem(); +} + + +//------------------------------------------------------------------------------ +// Purpose: Cancels any I/O events in the queue that were fired by us. +//------------------------------------------------------------------------------ +void CLogicRelayQueue::InputCancelPending( inputdata_t &inputdata ) +{ + g_EventQueue.CancelEvents( this ); + + // Stop waiting; allow another Trigger. + m_bWaitForRefire = false; + + if (!m_bDisabled && m_QueueItems.Count() > 0) + HandleNextQueueItem(); +} + + +//------------------------------------------------------------------------------ +// Purpose: Clears the queue. +//------------------------------------------------------------------------------ +void CLogicRelayQueue::InputClearQueue( inputdata_t &inputdata ) +{ + m_QueueItems.RemoveAll(); +} + + +//------------------------------------------------------------------------------ +// Purpose: Turns off the relay, preventing it from firing outputs. +//------------------------------------------------------------------------------ +void CLogicRelayQueue::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + + +//------------------------------------------------------------------------------ +// Purpose: Toggles the enabled/disabled state of the relay. +//------------------------------------------------------------------------------ +void CLogicRelayQueue::InputToggle( inputdata_t &inputdata ) +{ + m_bDisabled = !m_bDisabled; + + if (!m_bDisabled && !m_bWaitForRefire && m_QueueItems.Count() > 0) + HandleNextQueueItem(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler that triggers the relay. +//----------------------------------------------------------------------------- +void CLogicRelayQueue::InputTrigger( inputdata_t &inputdata ) +{ + if ((!m_bDisabled) && (!m_bWaitForRefire)) + { + m_OnTrigger.FireOutput( inputdata.pActivator, this ); + + // + // Disable the relay so that it cannot be refired until after the last output + // has been fired and post an input to re-enable ourselves. + // + m_bWaitForRefire = true; + g_EventQueue.AddEvent(this, "EnableRefire", m_OnTrigger.GetMaxDelay() + 0.001, this, this); + } + else if ( (!m_bDisabled || !m_bDontQueueWhenDisabled) && (m_QueueItems.Count() < m_iMaxQueueItems) ) + { + AddQueueItem(inputdata.pActivator, inputdata.nOutputID); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that triggers the relay. +//----------------------------------------------------------------------------- +void CLogicRelayQueue::InputTriggerWithParameter( inputdata_t &inputdata ) +{ + if ((!m_bDisabled) && (!m_bWaitForRefire)) + { + m_OnTriggerParameter.Set( inputdata.value, inputdata.pActivator, this ); + + // + // Disable the relay so that it cannot be refired until after the last output + // has been fired and post an input to re-enable ourselves. + // + m_bWaitForRefire = true; + g_EventQueue.AddEvent(this, "EnableRefire", m_OnTrigger.GetMaxDelay() + 0.001, this, this); + } + else if ( (!m_bDisabled || !m_bDontQueueWhenDisabled) && (m_QueueItems.Count() < m_iMaxQueueItems) ) + { + AddQueueItem(inputdata.pActivator, inputdata.nOutputID, inputdata.value); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles next queue item. +//----------------------------------------------------------------------------- +void CLogicRelayQueue::HandleNextQueueItem() +{ + LogicRelayQueueInfo_t info = m_QueueItems.Element(0); + + //if (!info.TriggerWithParameter) + //{ + // m_OnTrigger.FireOutput(info.pActivator, this); + //} + //else + //{ + // m_OnTriggerParameter.Set(info.value, info.pActivator, this); + //} + + AcceptInput(info.TriggerWithParameter ? "TriggerWithParameter" : "Trigger", info.pActivator, this, info.value, info.outputID); + + m_QueueItems.Remove(0); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a queue item. +//----------------------------------------------------------------------------- +void CLogicRelayQueue::AddQueueItem(CBaseEntity *pActivator, int outputID, variant_t &value) +{ + LogicRelayQueueInfo_t info; + info.pActivator = pActivator; + info.outputID = outputID; + + info.value = value; + info.TriggerWithParameter = true; + + m_QueueItems.AddToTail(info); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a queue item without a parameter. +//----------------------------------------------------------------------------- +void CLogicRelayQueue::AddQueueItem(CBaseEntity *pActivator, int outputID) +{ + LogicRelayQueueInfo_t info; + info.pActivator = pActivator; + info.outputID = outputID; + + info.TriggerWithParameter = false; + + m_QueueItems.AddToTail(info); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CLogicRelayQueue::DrawDebugTextOverlays(void) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + // -------------- + // Print Target + // -------------- + char tempstr[255]; + + if (m_QueueItems.Count() > 0) + { + Q_snprintf(tempstr, sizeof(tempstr), "Queue Items: %i (%i)", m_QueueItems.Count(), m_iMaxQueueItems); + EntityText(text_offset, tempstr, 0); + text_offset++; + + for (int i = 0; i < m_QueueItems.Count(); i++) + { + Q_snprintf(tempstr, sizeof(tempstr), " Input: %s, Activator: %s, Output ID: %i", + m_QueueItems[i].TriggerWithParameter ? "TriggerWithParameter" : "Trigger", + m_QueueItems[i].pActivator ? m_QueueItems[i].pActivator->GetDebugName() : "None", + m_QueueItems[i].outputID); + EntityText(text_offset, tempstr, 0); + text_offset++; + } + } + else + { + Q_snprintf(tempstr, sizeof(tempstr), "Queue Items: 0 (%i)", m_iMaxQueueItems); + EntityText(text_offset, tempstr, 0); + text_offset++; + } + } + return text_offset; +} +#endif diff --git a/mp/src/game/server/logicrelay.h b/mp/src/game/server/logicrelay.h index 8d082acc..00791579 100644 --- a/mp/src/game/server/logicrelay.h +++ b/mp/src/game/server/logicrelay.h @@ -13,6 +13,13 @@ #include "entityoutput.h" #include "eventqueue.h" +#ifdef MAPBASE + +// I was originally going to add something similar to that queue thing to logic_relay directly, but I later decided to relegate it to a derivative entity. +#define RELAY_QUEUE_SYSTEM 0 + +#endif + class CLogicRelay : public CLogicalEntity { public: @@ -25,16 +32,26 @@ public: // Input handlers void InputEnable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputEnableRefire( inputdata_t &inputdata ); +#else void InputEnableRefire( inputdata_t &inputdata ); // Private input handler, not in FGD +#endif void InputDisable( inputdata_t &inputdata ); void InputToggle( inputdata_t &inputdata ); void InputTrigger( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputTriggerWithParameter( inputdata_t &inputdata ); +#endif void InputCancelPending( inputdata_t &inputdata ); DECLARE_DATADESC(); // Outputs COutputEvent m_OnTrigger; +#ifdef MAPBASE + COutputVariant m_OnTriggerParameter; +#endif COutputEvent m_OnSpawn; bool IsDisabled( void ){ return m_bDisabled; } @@ -43,6 +60,66 @@ private: bool m_bDisabled; bool m_bWaitForRefire; // Set to disallow a refire while we are waiting for our outputs to finish firing. +#if RELAY_QUEUE_SYSTEM + bool m_bQueueTrigger; + bool m_bQueueWaiting; + float m_flRefireTime; +#endif }; +#ifdef MAPBASE +struct LogicRelayQueueInfo_t +{ + DECLARE_SIMPLE_DATADESC(); + + bool TriggerWithParameter; + CBaseEntity *pActivator; + variant_t value; + int outputID; +}; + +//#define LOGIC_RELAY_QUEUE_LIMIT 16 + +class CLogicRelayQueue : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicRelayQueue, CLogicalEntity ); + + CLogicRelayQueue(); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputEnableRefire( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + void InputTrigger( inputdata_t &inputdata ); + void InputTriggerWithParameter( inputdata_t &inputdata ); + void InputCancelPending( inputdata_t &inputdata ); + void InputClearQueue( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + + // Outputs + COutputEvent m_OnTrigger; + COutputVariant m_OnTriggerParameter; + + bool IsDisabled( void ){ return m_bDisabled; } + + void HandleNextQueueItem(); + void AddQueueItem(CBaseEntity *pActivator, int outputID, variant_t &value); + void AddQueueItem(CBaseEntity *pActivator, int outputID); + + int DrawDebugTextOverlays( void ); + +private: + + bool m_bDisabled; + bool m_bWaitForRefire; // Set to disallow a refire while we are waiting for our outputs to finish firing. + + int m_iMaxQueueItems; + bool m_bDontQueueWhenDisabled; // Don't add to queue while disabled, only when waiting for refire + CUtlVector m_QueueItems; +}; +#endif + #endif //LOGICRELAY_H diff --git a/mp/src/game/server/mapbase/GlobalStrings.cpp b/mp/src/game/server/mapbase/GlobalStrings.cpp new file mode 100644 index 00000000..4abd6f63 --- /dev/null +++ b/mp/src/game/server/mapbase/GlobalStrings.cpp @@ -0,0 +1,100 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================== +// +// Purpose: See GlobalStrings.h for more information. +// +// $NoKeywords: $ +//============================================================================= + +#include "cbase.h" +#include "GlobalStrings.h" + + +// Global strings must be initially declared here. +// Be sure to sync them with the externs in GlobalStrings.h. + +// ------------------------------------------------------------- +// +// Classnames +// +// ------------------------------------------------------------- + +#ifdef HL2_DLL +string_t gm_isz_class_Shotgun; +string_t gm_isz_class_SMG1; +string_t gm_isz_class_AR2; +string_t gm_isz_class_Pistol; +string_t gm_isz_class_Stunstick; +string_t gm_isz_class_Crowbar; +string_t gm_isz_class_RPG; +string_t gm_isz_class_357; +string_t gm_isz_class_Grenade; +string_t gm_isz_class_Physcannon; +string_t gm_isz_class_Crossbow; + +string_t gm_isz_class_Strider; +string_t gm_isz_class_Gunship; +string_t gm_isz_class_Dropship; +string_t gm_isz_class_FloorTurret; +string_t gm_isz_class_CScanner; +string_t gm_isz_class_ClawScanner; +string_t gm_isz_class_Rollermine; +#endif + +string_t gm_isz_class_Bullseye; + +string_t gm_isz_class_PropPhysics; +string_t gm_isz_class_PropPhysicsOverride; +string_t gm_isz_class_FuncPhysbox; +string_t gm_isz_class_EnvFire; + +// ------------------------------------------------------------- + +string_t gm_isz_name_player; +string_t gm_isz_name_activator; + +// ------------------------------------------------------------- + +// ------------------------------------------------------------- + +// We know it hasn't been allocated yet +#define INITIALIZE_GLOBAL_STRING(string, text) string = AllocPooledString(text) //SetGlobalString(string, text) + +void InitGlobalStrings() +{ +#ifdef HL2_DLL + INITIALIZE_GLOBAL_STRING(gm_isz_class_Shotgun, "weapon_shotgun"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_SMG1, "weapon_smg1"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_AR2, "weapon_ar2"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Pistol, "weapon_pistol"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Stunstick, "weapon_stunstick"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Crowbar, "weapon_crowbar"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_RPG, "weapon_rpg"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_357, "weapon_357"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Grenade, "weapon_frag"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Physcannon, "weapon_physcannon"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Crossbow, "weapon_crossbow"); + + INITIALIZE_GLOBAL_STRING(gm_isz_class_Strider, "npc_strider"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Gunship, "npc_combinegunship"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Dropship, "npc_combinedropship"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_FloorTurret, "npc_turret_floor"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_CScanner, "npc_cscanner"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_ClawScanner, "npc_clawscanner"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_Rollermine, "npc_rollermine"); +#endif + + INITIALIZE_GLOBAL_STRING(gm_isz_class_Bullseye, "npc_bullseye"); + + INITIALIZE_GLOBAL_STRING(gm_isz_class_PropPhysics, "prop_physics"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_PropPhysicsOverride, "prop_physics_override"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_FuncPhysbox, "func_physbox"); + INITIALIZE_GLOBAL_STRING(gm_isz_class_EnvFire, "env_fire"); + + INITIALIZE_GLOBAL_STRING(gm_isz_name_player, "!player"); + INITIALIZE_GLOBAL_STRING(gm_isz_name_activator, "!activator"); +} + +// ------------------------------------------------------------- + + + diff --git a/mp/src/game/server/mapbase/GlobalStrings.h b/mp/src/game/server/mapbase/GlobalStrings.h new file mode 100644 index 00000000..336ff622 --- /dev/null +++ b/mp/src/game/server/mapbase/GlobalStrings.h @@ -0,0 +1,92 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================== +// +// Purpose: Shared global string library. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef MAPBASE_GLOBAL_STRINGS_H +#define MAPBASE_GLOBAL_STRINGS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "cbase.h" + +// ------------------------------------------------------------- +// +// Valve uses global pooled strings in various parts of the code, particularly Episodic code, +// so they could get away with integer/pointer comparisons instead of string comparisons. +// +// While developing Mapbase, I thought of what it would be like if that system was more uniform and more widely used. +// My OCD ended up taking me over and that thought became real, even though on today's machines these are (for the most part) micro-optimizations +// that just clutter the code. +// +// It was fun and satisfying to do this and I hope you are not judging me for my strange ways. +// I wanted to toggle them via a preprocessor so you could turn them off at will, but that just made things messier. +// I hope this doesn't cause problems for anyone. +// +// ------------------------------------------------------------- + +// ------------------------------------------------------------- +// +// Classnames +// +// ------------------------------------------------------------- + +#ifdef HL2_DLL +extern string_t gm_isz_class_Shotgun; +extern string_t gm_isz_class_SMG1; +extern string_t gm_isz_class_AR2; +extern string_t gm_isz_class_Pistol; +extern string_t gm_isz_class_Stunstick; +extern string_t gm_isz_class_Crowbar; +extern string_t gm_isz_class_RPG; +extern string_t gm_isz_class_357; +extern string_t gm_isz_class_Grenade; +extern string_t gm_isz_class_Physcannon; +extern string_t gm_isz_class_Crossbow; + +extern string_t gm_isz_class_Strider; +extern string_t gm_isz_class_Gunship; +extern string_t gm_isz_class_Dropship; +extern string_t gm_isz_class_FloorTurret; +extern string_t gm_isz_class_CScanner; +extern string_t gm_isz_class_ClawScanner; +extern string_t gm_isz_class_Rollermine; +#endif + +extern string_t gm_isz_class_Bullseye; + +extern string_t gm_isz_class_PropPhysics; +extern string_t gm_isz_class_PropPhysicsOverride; +extern string_t gm_isz_class_FuncPhysbox; +extern string_t gm_isz_class_EnvFire; + +// ------------------------------------------------------------- + +extern string_t gm_isz_name_player; +extern string_t gm_isz_name_activator; + +// ------------------------------------------------------------- + +// Does the classname of this entity match the string_t? +// +// This function is for comparing global strings and allows us to change how we compare them quickly. +inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) +{ + //return ent->ClassMatches(str2); + + // Since classnames are pooled, the global string and the entity's classname should point to the same string in memory. + // As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary. + // Feel free to correct me if I'm disastrously wrong. + return ent->m_iClassname == str2; +} + +// ------------------------------------------------------------- + +void InitGlobalStrings(); + +// ------------------------------------------------------------- + +#endif diff --git a/mp/src/game/server/mapbase/SystemConvarMod.cpp b/mp/src/game/server/mapbase/SystemConvarMod.cpp new file mode 100644 index 00000000..2ff4380e --- /dev/null +++ b/mp/src/game/server/mapbase/SystemConvarMod.cpp @@ -0,0 +1,352 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mostly just Mapbase's convar mod code. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "saverestore_utlvector.h" +#include "SystemConvarMod.h" +#include "fmtstr.h" + +ConVar g_debug_convarmod( "g_debug_convarmod", "0" ); + +BEGIN_SIMPLE_DATADESC( modifiedconvars_t ) + DEFINE_ARRAY( pszConvar, FIELD_CHARACTER, MAX_MODIFIED_CONVAR_STRING ), + DEFINE_ARRAY( pszCurrentValue, FIELD_CHARACTER, MAX_MODIFIED_CONVAR_STRING ), + DEFINE_ARRAY( pszOrgValue, FIELD_CHARACTER, MAX_MODIFIED_CONVAR_STRING ), +END_DATADESC() + + +// ====================================================================== +// +// Everything below here is for the Mapbase convar mod system. +// ...which is based off of the Commentary convar mod system. +// +// ====================================================================== + +#define MAX_CONVARMOD_STRING_SIZE 512 + +CHandle m_hConvarsChanging; +CMapbaseCVarModEntity *ChangingCVars( void ) { return m_hConvarsChanging.Get(); } +void SetChangingCVars( CMapbaseCVarModEntity *hEnt ) { m_hConvarsChanging = hEnt; } + +CUtlVector< CHandle > m_ModEntities; +bool m_bModActive; + +void ConvarChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + ConVarRef var( pConVar ); + CMapbaseCVarModEntity *modent = ChangingCVars(); + + for ( int i = 0; i < m_ModEntities.Count(); i++ ) + { + if (!m_ModEntities[i]) + { + Warning("NULL mod entity at %i\n", i); + continue; + } + + if (m_ModEntities[i].Get()->NewCVar(&var, pOldString, modent)) + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CV_GlobalChange_Mapbase( IConVar *var, const char *pOldString, float flOldValue ) +{ + if ( !ChangingCVars() ) + { + // A convar has changed, but not due to Mapbase systems. Ignore it. + return; + } + + ConvarChanged( var, pOldString, flOldValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CC_MapbaseNotChangingCVars( void ) +{ + SetChangingCVars( NULL ); +} +static ConCommand mapbase_cvarsnotchanging("mapbase_cvarsnotchanging", CC_MapbaseNotChangingCVars, "An internal command used for ConVar modification.", FCVAR_HIDDEN); + +// ------------------------------------------------------------------------ + +void CV_InitMod() +{ + if (!m_bModActive) + { + cvar->InstallGlobalChangeCallback( CV_GlobalChange_Mapbase ); + m_bModActive = true; + } +} + +// ------------------------------------------------------------------------ + +void CVEnt_Precache(CMapbaseCVarModEntity *modent) +{ + // Now protected by FCVAR_NOT_CONNECTED + //if (Q_strstr(STRING(modent->m_target), "sv_allow_logic_convar")) + // return; + +#ifdef MAPBASE_MP + if (gpGlobals->maxClients > 1 && !modent->m_bUseServer) + { + Warning("WARNING: %s is using the local player in a multiplayer game and will not function.\n", modent->GetDebugName()); + } +#endif + + CV_InitMod(); +} +void CVEnt_Activate(CMapbaseCVarModEntity *modent) +{ + const char *pszCommands = STRING( modent->m_target ); + if ( Q_strnchr(pszCommands, '^', MAX_CONVARMOD_STRING_SIZE) ) + { + // Just like the commentary system, we convert ^s to "s here. + char szTmp[MAX_CONVARMOD_STRING_SIZE]; + Q_strncpy( szTmp, pszCommands, MAX_CONVARMOD_STRING_SIZE ); + int len = Q_strlen( szTmp ); + for ( int i = 0; i < len; i++ ) + { + if ( szTmp[i] == '^' ) + { + szTmp[i] = '"'; + } + } + + pszCommands = szTmp; + } + + CV_InitMod(); + + if (m_ModEntities.Find(modent) == m_ModEntities.InvalidIndex()) + m_ModEntities.AddToTail( modent ); + + if (modent->m_bUseServer) + { + SetChangingCVars( modent ); + + engine->ServerCommand( CFmtStr("%s\n", pszCommands) ); + engine->ServerCommand( "mapbase_cvarsnotchanging\n" ); + } + else + { + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + edict_t *edict = pPlayer ? pPlayer->edict() : NULL; + if (edict) + { + SetChangingCVars( modent ); + + engine->ClientCommand( edict, pszCommands ); + engine->ClientCommand( edict, "mapbase_cvarsnotchanging" ); + } + else + { + Warning("%s unable to find local player edict\n", modent->GetDebugName()); + } + } +} +void CVEnt_Deactivate(CMapbaseCVarModEntity *modent) +{ + // Remove our global convar callback + cvar->RemoveGlobalChangeCallback( CV_GlobalChange_Mapbase ); + + // Reset any convars that have been changed by the commentary + for ( int i = 0; i < modent->m_ModifiedConvars.Count(); i++ ) + { + ConVar *pConVar = (ConVar *)cvar->FindVar( modent->m_ModifiedConvars[i].pszConvar ); + if ( pConVar ) + { + pConVar->SetValue( modent->m_ModifiedConvars[i].pszOrgValue ); + } + } + + modent->m_ModifiedConvars.Purge(); + + if (m_bModActive) + { + m_ModEntities.FindAndRemove(modent); + + if (m_ModEntities.Count() == 0) + { + // No more mod entities + m_bModActive = false; + } + else + { + // We're done and we're still active, install our callback again + cvar->InstallGlobalChangeCallback( CV_GlobalChange_Mapbase ); + } + } +} +void CVEnt_Restore(CMapbaseCVarModEntity *modent) +{ + m_ModEntities.AddToTail(modent); +} + +// ------------------------------------------------------------------------ + + +LINK_ENTITY_TO_CLASS( game_convar_mod, CMapbaseCVarModEntity ); + +// This classname should be phased out after beta +LINK_ENTITY_TO_CLASS( mapbase_convar_mod, CMapbaseCVarModEntity ); + +BEGIN_DATADESC( CMapbaseCVarModEntity ) + + DEFINE_UTLVECTOR( m_ModifiedConvars, FIELD_EMBEDDED ), + DEFINE_KEYFIELD( m_bUseServer, FIELD_BOOLEAN, "UseServer" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), + + DEFINE_THINKFUNC( CvarModActivate ), + +END_DATADESC() + + +void CMapbaseCVarModEntity::UpdateOnRemove() +{ + BaseClass::UpdateOnRemove(); + + CVEnt_Deactivate(this); +} + +void CMapbaseCVarModEntity::Precache( void ) +{ + BaseClass::Precache(); + + CVEnt_Precache(this); +} + +void CMapbaseCVarModEntity::Spawn( void ) +{ + BaseClass::Spawn(); + + if (m_target == NULL_STRING) + { + Warning("WARNING: %s has no cvars specified! Removing...\n", GetDebugName()); + UTIL_Remove(this); + return; + } + + Precache(); + + if (HasSpawnFlags(SF_CVARMOD_START_ACTIVATED)) + { + if (!m_bUseServer && !UTIL_GetLocalPlayer()) + { + // The local player doesn't exist yet, so we should wait until they do + SetThink( &CMapbaseCVarModEntity::CvarModActivate ); + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); + } + else + { + CVEnt_Activate( this ); + } + } +} + +void CMapbaseCVarModEntity::CvarModActivate() +{ + if (UTIL_GetLocalPlayer()) + { + CVEnt_Activate( this ); + } + else + { + SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); + } +} + +void CMapbaseCVarModEntity::OnRestore( void ) +{ + BaseClass::OnRestore(); + + // Set any convars that have already been changed by the mapper before the save + if (m_ModifiedConvars.Count() > 0) + { + for ( int i = 0; i < m_ModifiedConvars.Count(); i++ ) + { + ConVar *pConVar = (ConVar *)cvar->FindVar( m_ModifiedConvars[i].pszConvar ); + if ( pConVar ) + { + if (g_debug_convarmod.GetBool()) + Msg(" %s Restoring Convar %s: value %s (org %s)\n", GetDebugName(), m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue ); + pConVar->SetValue( m_ModifiedConvars[i].pszCurrentValue ); + } + } + + CVEnt_Restore(this); + } +} + +void CMapbaseCVarModEntity::InputActivate(inputdata_t &inputdata) +{ + CVEnt_Activate(this); +} + +void CMapbaseCVarModEntity::InputDeactivate(inputdata_t &inputdata) +{ + CVEnt_Deactivate(this); +} + +bool CMapbaseCVarModEntity::NewCVar( ConVarRef *var, const char *pOldString, CBaseEntity *modent ) +{ + for (int i = 0; i < m_ModifiedConvars.Count(); i++) + { + // If we find it, just update the current value + modifiedconvars_t modvar = m_ModifiedConvars[i]; + if ( !Q_strncmp( var->GetName(), modvar.pszConvar, MAX_MODIFIED_CONVAR_STRING ) ) + { + if (modent == this) + { + Q_strncpy( modvar.pszCurrentValue, var->GetString(), MAX_MODIFIED_CONVAR_STRING ); + if (g_debug_convarmod.GetBool()) + Msg(" %s Updating Convar %s: value %s (org %s)\n", GetDebugName(), modvar.pszConvar, modvar.pszCurrentValue, modvar.pszOrgValue ); + return true; + } + else + { + // A different entity is using this CVar now, remove ours + m_ModifiedConvars.Remove(i); + if (g_debug_convarmod.GetBool()) + Msg(" %s Removing Convar %s: value %s (org %s)\n", GetDebugName(), modvar.pszConvar, modvar.pszCurrentValue, modvar.pszOrgValue ); + return false; + } + } + } + + // Did I do that? + if (modent == this) + { + // Add the CVar to our list. + modifiedconvars_t newConvar; + Q_strncpy( newConvar.pszConvar, var->GetName(), MAX_MODIFIED_CONVAR_STRING ); + Q_strncpy( newConvar.pszCurrentValue, var->GetString(), MAX_MODIFIED_CONVAR_STRING ); + Q_strncpy( newConvar.pszOrgValue, pOldString, MAX_MODIFIED_CONVAR_STRING ); + m_ModifiedConvars.AddToTail( newConvar ); + + if (g_debug_convarmod.GetBool()) + { + Msg(" %s changed '%s' to '%s' (was '%s')\n", GetDebugName(), var->GetName(), var->GetString(), pOldString ); + Msg(" Convars stored: %d\n", m_ModifiedConvars.Count() ); + for ( int i = 0; i < m_ModifiedConvars.Count(); i++ ) + { + Msg(" Convar %d: %s, value %s (org %s)\n", i, m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue ); + } + } + + return true; + } + + return false; +} diff --git a/mp/src/game/server/mapbase/SystemConvarMod.h b/mp/src/game/server/mapbase/SystemConvarMod.h new file mode 100644 index 00000000..faaafc27 --- /dev/null +++ b/mp/src/game/server/mapbase/SystemConvarMod.h @@ -0,0 +1,49 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: modifiedconvarts_t from CommentarySystem.cpp moved to a header file so Mapbase can use it. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + + +// Convar restoration save/restore +#define MAX_MODIFIED_CONVAR_STRING 128 +struct modifiedconvars_t +{ + DECLARE_SIMPLE_DATADESC(); + + char pszConvar[MAX_MODIFIED_CONVAR_STRING]; + char pszCurrentValue[MAX_MODIFIED_CONVAR_STRING]; + char pszOrgValue[MAX_MODIFIED_CONVAR_STRING]; +}; + +// --------------------------------------------------------------------- + +#define SF_CVARMOD_START_ACTIVATED (1 << 0) + +class CMapbaseCVarModEntity : public CPointEntity +{ +public: + DECLARE_CLASS( CMapbaseCVarModEntity, CPointEntity ); + + void UpdateOnRemove(); + + void Precache( void ); + + void Spawn( void ); + void CvarModActivate(); + + void OnRestore( void ); + + void InputActivate(inputdata_t &inputdata); + void InputDeactivate(inputdata_t &inputdata); + + bool NewCVar( ConVarRef *var, const char *pOldString, CBaseEntity *modent ); + + CUtlVector< modifiedconvars_t > m_ModifiedConvars; + bool m_bUseServer; + + DECLARE_DATADESC(); +}; diff --git a/mp/src/game/server/mapbase/ai_grenade.cpp b/mp/src/game/server/mapbase/ai_grenade.cpp new file mode 100644 index 00000000..e14b47a3 --- /dev/null +++ b/mp/src/game/server/mapbase/ai_grenade.cpp @@ -0,0 +1,13 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" + +#include "ai_grenade.h" + + +int COMBINE_AE_BEGIN_ALTFIRE; +int COMBINE_AE_ALTFIRE; diff --git a/mp/src/game/server/mapbase/ai_grenade.h b/mp/src/game/server/mapbase/ai_grenade.h new file mode 100644 index 00000000..7f1ea8c1 --- /dev/null +++ b/mp/src/game/server/mapbase/ai_grenade.h @@ -0,0 +1,662 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef AI_GRENADE_H +#define AI_GRENADE_H +#ifdef _WIN32 +#pragma once +#endif + +// I wish I didn't have to #include all this, but I don't really have a choice. +// I guess something similar to CAI_BehaviorHost's backbridges could be tried. +#include "cbase.h" +#include "ai_basenpc.h" +#include "npcevent.h" +#include "grenade_frag.h" +#include "basegrenade_shared.h" +#include "ai_squad.h" +#include "GlobalStrings.h" + +#define COMBINE_AE_GREN_TOSS ( 7 ) + +#define COMBINE_GRENADE_THROW_SPEED 650 +#define COMBINE_GRENADE_TIMER 3.5 +#define COMBINE_GRENADE_FLUSH_TIME 3.0 // Don't try to flush an enemy who has been out of sight for longer than this. +#define COMBINE_GRENADE_FLUSH_DIST 256.0 // Don't try to flush an enemy who has moved farther than this distance from the last place I saw him. + +#define COMBINE_MIN_GRENADE_CLEAR_DIST 250 + +#define DEFINE_AIGRENADE_DATADESC() \ + DEFINE_KEYFIELD( m_iNumGrenades, FIELD_INTEGER, "NumGrenades" ), \ + DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ), \ + DEFINE_FIELD( m_hForcedGrenadeTarget, FIELD_EHANDLE ), \ + DEFINE_FIELD( m_flNextAltFireTime, FIELD_TIME ), \ + DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ), \ + DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), \ + DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), \ + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGrenades", InputSetGrenades ), \ + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddGrenades", InputAddGrenades ), \ + DEFINE_OUTPUT(m_OnThrowGrenade, "OnThrowGrenade"), \ + DEFINE_OUTPUT(m_OnOutOfGrenades, "OnOutOfGrenades"), \ + +// Use extern float GetCurrentGravity( void ); +#define SMGGrenadeArc(shootpos, targetpos) \ + Vector vecShootPos = shootpos; \ + Vector vecThrow = (targetpos - vecShootPos); \ + float time = vecThrow.Length() / 600.0; \ + vecThrow = vecThrow * (1.0 / time); \ + vecThrow.z += (GetCurrentGravity() * 0.5) * time * 0.5; \ + Vector vecFace = vecShootPos + (vecThrow * 0.5); \ + AddFacingTarget(vecFace, 1.0, 0.5); \ + +// Mask used for Combine ball hull traces. +// This used MASK_SHOT before, but this has been changed to MASK_SHOT_HULL. +// This fixes the existing problem of soldiers trying to fire energy balls through grates, +// but it's also important to prevent soldiers from blowing themselves up with their newfound SMG grenades. +#define MASK_COMBINE_BALL_LOS MASK_SHOT_HULL + +extern int COMBINE_AE_BEGIN_ALTFIRE; +extern int COMBINE_AE_ALTFIRE; + +enum eGrenadeCapabilities +{ + GRENCAP_GRENADE = (1 << 0), + GRENCAP_ALTFIRE = (1 << 1), +}; + +//----------------------------------------------------------------------------- +// Other classes can use this and access some CAI_GrenadeUser functions. +//----------------------------------------------------------------------------- +class CAI_GrenadeUserSink +{ +public: + CAI_GrenadeUserSink() { } + + virtual bool UsingOnThrowGrenade() { return false; } +}; + +//----------------------------------------------------------------------------- +// +// Template class for NPCs using grenades or weapon alt-fire stuff. +// You'll still have to use DEFINE_AIGRENADE_DATADESC() in your derived class's datadesc. +// +// I wanted to have these functions defined in a CPP file, but template class definitions must be in the header. +// Please excuse the bloat below the class definition. +// +//----------------------------------------------------------------------------- +template +class CAI_GrenadeUser : public BASE_NPC, public CAI_GrenadeUserSink +{ + DECLARE_CLASS_NOFRIEND( CAI_GrenadeUser, BASE_NPC ); + +public: + CAI_GrenadeUser() : CAI_GrenadeUserSink() { } + + void AddGrenades( int inc, CBaseEntity *pLastGrenade = NULL ) + { + m_iNumGrenades += inc; + if (m_iNumGrenades <= 0) + m_OnOutOfGrenades.Set( pLastGrenade, pLastGrenade, this ); + } + + virtual bool IsAltFireCapable() { return false; } + virtual bool IsGrenadeCapable() { return true; } + inline bool HasGrenades() { return m_iNumGrenades > 0; } + + void InputSetGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() - m_iNumGrenades ); } + void InputAddGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() ); } + void InputThrowGrenadeAtTarget( inputdata_t &inputdata ); + + virtual void DelayGrenadeCheck( float delay ) { m_flNextGrenadeCheck = gpGlobals->curtime + delay; } + + void HandleAnimEvent( animevent_t *pEvent ); + + // Soldiers use "lefthand", cops use "LHand", and citizens use "anim_attachment_LH" + virtual const char* GetGrenadeAttachment() { return "anim_attachment_LH"; } + + void ClearAttackConditions( void ); + + Vector GetAltFireTarget() { return m_vecAltFireTarget; } + virtual bool CanAltFireEnemy( bool bUseFreeKnowledge ); + void DelayAltFireAttack( float flDelay ); + void DelaySquadAltFireAttack( float flDelay ); + + virtual bool CanGrenadeEnemy( bool bUseFreeKnowledge = true ); + bool CanThrowGrenade( const Vector &vecTarget ); + bool CheckCanThrowGrenade( const Vector &vecTarget ); + + // For OnThrowGrenade + point_entity_replace, see grenade_frag.cpp + bool UsingOnThrowGrenade() { return m_OnThrowGrenade.NumberOfElements() > 0; } + +protected: + + void StartTask_FaceAltFireTarget( const Task_t *pTask ); + void StartTask_GetPathToForced( const Task_t *pTask ); + void StartTask_DeferSquad( const Task_t *pTask ); + + void RunTask_FaceAltFireTarget( const Task_t *pTask ); + void RunTask_GetPathToForced( const Task_t *pTask ); + void RunTask_FaceTossDir( const Task_t *pTask ); + +protected: // We can't have any private saved variables because only derived classes use the datadescs + + int m_iNumGrenades; + float m_flNextGrenadeCheck; + EHANDLE m_hForcedGrenadeTarget; + + float m_flNextAltFireTime; + Vector m_vecAltFireTarget; + Vector m_vecTossVelocity; + + COutputEHANDLE m_OnThrowGrenade; + COutputEHANDLE m_OnOutOfGrenades; +}; + +//------------------------------------------------------------------------------ +// Purpose: Handle animation events +//------------------------------------------------------------------------------ +template +void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) +{ + if ( pEvent->event == COMBINE_AE_BEGIN_ALTFIRE ) + { + if (this->GetActiveWeapon()) + this->GetActiveWeapon()->WeaponSound( SPECIAL1 ); + + //SpeakIfAllowed( TLK_CMB_THROWGRENADE, "altfire:1" ); + return; + } + if ( pEvent->event == COMBINE_AE_ALTFIRE ) + { + animevent_t fakeEvent; + + fakeEvent.pSource = this; + fakeEvent.event = EVENT_WEAPON_AR2_ALTFIRE; + + // Weapon could've been dropped while playing animation + if (this->GetActiveWeapon()) + this->GetActiveWeapon()->Operator_HandleAnimEvent( &fakeEvent, this ); + + // Stop other squad members from combine balling for a while. + DelaySquadAltFireAttack( 10.0f ); + + AddGrenades(-1); + + return; + } + + if ( pEvent->event == COMBINE_AE_GREN_TOSS ) + { + Vector vecSpin; + vecSpin.x = random->RandomFloat( -1000.0, 1000.0 ); + vecSpin.y = random->RandomFloat( -1000.0, 1000.0 ); + vecSpin.z = random->RandomFloat( -1000.0, 1000.0 ); + + Vector vecStart; + this->GetAttachment( GetGrenadeAttachment(), vecStart ); + + if( this->GetState() == NPC_STATE_SCRIPT ) + { + // Use a fixed velocity for grenades thrown in scripted state. + // Grenades thrown from a script do not count against grenades remaining for the AI to use. + Vector forward, up, vecThrow; + + this->GetVectors( &forward, NULL, &up ); + vecThrow = forward * 750 + up * 175; + + // This code is used by player allies now, so it's only "combine spawned" if the thrower isn't allied with the player. + CBaseEntity *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vecThrow, vecSpin, this, COMBINE_GRENADE_TIMER, !this->IsPlayerAlly() ); + m_OnThrowGrenade.Set(pGrenade, pGrenade, this); + } + else + { + // Use the Velocity that AI gave us. + CBaseEntity *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vecSpin, this, COMBINE_GRENADE_TIMER, !this->IsPlayerAlly() ); + m_OnThrowGrenade.Set(pGrenade, pGrenade, this); + AddGrenades(-1, pGrenade); + } + + // wait six seconds before even looking again to see if a grenade can be thrown. + m_flNextGrenadeCheck = gpGlobals->curtime + 6; + return; + } + + BaseClass::HandleAnimEvent( pEvent ); +} + +//----------------------------------------------------------------------------- +// Purpose: Force the combine soldier to throw a grenade at the target +// If I'm a combine elite, fire my combine ball at the target instead. +// Input : &inputdata - +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::InputThrowGrenadeAtTarget( inputdata_t &inputdata ) +{ + // Ignore if we're inside a scripted sequence + if ( this->GetState() == NPC_STATE_SCRIPT && this->m_hCine ) + return; + + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); + if ( !pEntity ) + { + DevMsg("%s (%s) received ThrowGrenadeAtTarget input, but couldn't find target entity '%s'\n", this->GetClassname(), this->GetDebugName(), inputdata.value.String() ); + return; + } + + m_hForcedGrenadeTarget = pEntity; + m_flNextGrenadeCheck = 0; + + this->ClearSchedule( "Told to throw grenade via input" ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +bool CAI_GrenadeUser::CanAltFireEnemy( bool bUseFreeKnowledge ) +{ + if (!HasGrenades()) + return false; + + if (!IsAltFireCapable()) + return false; + + if (!this->GetActiveWeapon()) + return false; + + if (this->IsCrouching()) + return false; + + if ( gpGlobals->curtime < m_flNextAltFireTime || gpGlobals->curtime < m_flNextGrenadeCheck ) + return false; + + if( !this->GetEnemy() ) + return false; + + if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1)) + return false; + + CBaseEntity *pEnemy = this->GetEnemy(); + + Vector vecTarget; + + // Determine what point we're shooting at + if( bUseFreeKnowledge ) + { + vecTarget = this->GetEnemies()->LastKnownPosition( pEnemy ) + (pEnemy->GetViewOffset()*0.75);// approximates the chest + } + else + { + vecTarget = this->GetEnemies()->LastSeenPosition( pEnemy ) + (pEnemy->GetViewOffset()*0.75);// approximates the chest + } + + // Trace a hull about the size of the combine ball (don't shoot through grates!) + trace_t tr; + + Vector mins( -12, -12, -12 ); + Vector maxs( 12, 12, 12 ); + + Vector vShootPosition = this->EyePosition(); + + if ( this->GetActiveWeapon() ) + { + this->GetActiveWeapon()->GetAttachment( "muzzle", vShootPosition ); + } + + // Trace a hull about the size of the combine ball. + UTIL_TraceHull( vShootPosition, vecTarget, mins, maxs, MASK_COMBINE_BALL_LOS, this, COLLISION_GROUP_NONE, &tr ); + + float flLength = (vShootPosition - vecTarget).Length(); + + flLength *= tr.fraction; + + // If the ball can travel at least 65% of the distance to the player then let the NPC shoot it. + // (unless it hit the world) + if( tr.fraction >= 0.65 && (!tr.m_pEnt || !tr.m_pEnt->IsWorld()) && flLength > 128.0f ) + { + // Target is valid + m_vecAltFireTarget = vecTarget; + return true; + } + + + // Check again later + m_vecAltFireTarget = vec3_origin; + m_flNextGrenadeCheck = gpGlobals->curtime + 1.0f; + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::DelayAltFireAttack( float flDelay ) +{ + float flNextAltFire = gpGlobals->curtime + flDelay; + + if( flNextAltFire > m_flNextAltFireTime ) + { + // Don't let this delay order preempt a previous request to wait longer. + m_flNextAltFireTime = flNextAltFire; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::DelaySquadAltFireAttack( float flDelay ) +{ + // Make sure to delay my own alt-fire attack. + DelayAltFireAttack( flDelay ); + + AISquadIter_t iter; + CAI_Squad *pSquad = this->GetSquad(); + CAI_BaseNPC *pSquadmate = pSquad ? pSquad->GetFirstMember( &iter ) : NULL; + while ( pSquadmate ) + { + CAI_GrenadeUser *pUser = dynamic_cast(pSquadmate); + if( pUser && pUser->IsAltFireCapable() ) + { + pUser->DelayAltFireAttack( flDelay ); + } + + pSquadmate = pSquad->GetNextMember( &iter ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +bool CAI_GrenadeUser::CanGrenadeEnemy( bool bUseFreeKnowledge ) +{ + CBaseEntity *pEnemy = this->GetEnemy(); + + Assert( pEnemy != NULL ); + + if( pEnemy ) + { + // I'm not allowed to throw grenades during dustoff + if ( this->IsCurSchedule(SCHED_DROPSHIP_DUSTOFF) ) + return false; + + if( bUseFreeKnowledge ) + { + // throw to where we think they are. + return CanThrowGrenade( this->GetEnemies()->LastKnownPosition( pEnemy ) ); + } + else + { + // hafta throw to where we last saw them. + return CanThrowGrenade( this->GetEnemies()->LastSeenPosition( pEnemy ) ); + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if the combine has grenades, hasn't checked lately, and +// can throw a grenade at the target point. +// Input : &vecTarget - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +template +bool CAI_GrenadeUser::CanThrowGrenade( const Vector &vecTarget ) +{ + if( m_iNumGrenades < 1 ) + { + // Out of grenades! + return false; + } + + if (!IsGrenadeCapable()) + { + // Must be capable of throwing grenades + return false; + } + + if ( gpGlobals->curtime < m_flNextGrenadeCheck ) + { + // Not allowed to throw another grenade right now. + return false; + } + + float flDist; + flDist = ( vecTarget - this->GetAbsOrigin() ).Length(); + + if( flDist > 1024 || flDist < 128 ) + { + // Too close or too far! + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return false; + } + + // ----------------------- + // If moving, don't check. + // ----------------------- + if ( this->m_flGroundSpeed != 0 ) + return false; + + // --------------------------------------------------------------------- + // Are any of my squad members near the intended grenade impact area? + // --------------------------------------------------------------------- + CAI_Squad *pSquad = this->GetSquad(); + if ( pSquad ) + { + if (pSquad->SquadMemberInRange( vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST )) + { + // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + + // Tell my squad members to clear out so I can get a grenade in + // Mapbase uses a new context here that gets all nondescript allies away since this code is shared between Combine and non-Combine now. + CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_OWNER_ALLIES, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST, 0.1, this ); + return false; + } + } + + return CheckCanThrowGrenade( vecTarget ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the combine can throw a grenade at the specified target point +// Input : &vecTarget - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +template +bool CAI_GrenadeUser::CheckCanThrowGrenade( const Vector &vecTarget ) +{ + //NDebugOverlay::Line( this->EyePosition(), vecTarget, 0, 255, 0, false, 5 ); + + // --------------------------------------------------------------------- + // Check that throw is legal and clear + // --------------------------------------------------------------------- + // FIXME: this is only valid for hand grenades, not RPG's + Vector vecToss; + Vector vecMins = -Vector(4,4,4); + Vector vecMaxs = Vector(4,4,4); + if( this->FInViewCone( vecTarget ) && CBaseEntity::FVisible( vecTarget ) ) + { + vecToss = VecCheckThrow( this, this->EyePosition(), vecTarget, COMBINE_GRENADE_THROW_SPEED, 1.0, &vecMins, &vecMaxs ); + } + else + { + // Have to try a high toss. Do I have enough room? + trace_t tr; + AI_TraceLine( this->EyePosition(), this->EyePosition() + Vector( 0, 0, 64 ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + if( tr.fraction != 1.0 ) + { + return false; + } + + vecToss = VecCheckToss( this, this->EyePosition(), vecTarget, -1, 1.0, true, &vecMins, &vecMaxs ); + } + + if ( vecToss != vec3_origin ) + { + m_vecTossVelocity = vecToss; + + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // 1/3 second. + return true; + } + else + { + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: This was copied from soldier code for general AI grenades. +// +// "Soldiers use CAN_RANGE_ATTACK2 to indicate whether they can throw +// a grenade. Because they check only every half-second or so, this +// condition must persist until it is updated again by the code +// that determines whether a grenade can be thrown, so prevent the +// base class from clearing it out. (sjb)" +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::ClearAttackConditions() +{ + bool fCanRangeAttack2 = IsGrenadeCapable() && this->HasCondition( COND_CAN_RANGE_ATTACK2 ); + + // Call the base class. + BaseClass::ClearAttackConditions(); + + if( fCanRangeAttack2 ) + { + // We don't allow the base class to clear this condition because we + // don't sense for it every frame. + this->SetCondition( COND_CAN_RANGE_ATTACK2 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Task helpers +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::StartTask_FaceAltFireTarget( const Task_t *pTask ) +{ + this->SetIdealActivity( (Activity)(int)pTask->flTaskData ); + this->GetMotor()->SetIdealYawToTargetAndUpdate( m_vecAltFireTarget, AI_KEEP_YAW_SPEED ); +} + +template +void CAI_GrenadeUser::StartTask_GetPathToForced( const Task_t *pTask ) +{ + if ( !m_hForcedGrenadeTarget ) + { + this->TaskFail(FAIL_NO_ENEMY); + return; + } + + float flMaxRange = 2000; + float flMinRange = 0; + + Vector vecEnemy = m_hForcedGrenadeTarget->GetAbsOrigin(); + Vector vecEnemyEye = vecEnemy + m_hForcedGrenadeTarget->GetViewOffset(); + + Vector posLos; + bool found = false; + + if ( this->GetTacticalServices()->FindLateralLos( vecEnemyEye, &posLos ) ) + { + float dist = ( posLos - vecEnemyEye ).Length(); + if ( dist < flMaxRange && dist > flMinRange ) + found = true; + } + + if ( !found && this->GetTacticalServices()->FindLos( vecEnemy, vecEnemyEye, flMinRange, flMaxRange, 1.0, &posLos ) ) + { + found = true; + } + + if ( !found ) + { + this->TaskFail( FAIL_NO_SHOOT ); + } + else + { + // else drop into run task to offer an interrupt + this->m_vInterruptSavePosition = posLos; + } +} + +template +void CAI_GrenadeUser::StartTask_DeferSquad( const Task_t *pTask ) +{ + CAI_Squad *pSquad = this->GetSquad(); + if ( pSquad ) + { + // iterate my squad and stop everyone from throwing grenades for a little while. + AISquadIter_t iter; + + CAI_BaseNPC *pSquadmate = pSquad ? pSquad->GetFirstMember( &iter ) : NULL; + while ( pSquadmate ) + { + pSquadmate->DelayGrenadeCheck(5); + + pSquadmate = pSquad->GetNextMember( &iter ); + } + } + + this->TaskComplete(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::RunTask_FaceAltFireTarget( const Task_t *pTask ) +{ + this->GetMotor()->SetIdealYawToTargetAndUpdate( m_vecAltFireTarget, AI_KEEP_YAW_SPEED ); + + // New Mapbase thing that fixes forced alt-fires not changing weapon yaw/pitch + this->SetAim( m_vecAltFireTarget - this->Weapon_ShootPosition() ); + + if (this->IsActivityFinished()) + { + this->TaskComplete(); + } +} + +template +void CAI_GrenadeUser::RunTask_GetPathToForced( const Task_t *pTask ) +{ + if ( !m_hForcedGrenadeTarget ) + { + this->TaskFail(FAIL_NO_ENEMY); + return; + } + + if ( this->GetTaskInterrupt() > 0 ) + { + this->ClearTaskInterrupt(); + + Vector vecEnemy = m_hForcedGrenadeTarget->GetAbsOrigin(); + AI_NavGoal_t goal( this->m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE ); + + this->GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET ); + this->GetNavigator()->SetArrivalDirection( vecEnemy - goal.dest ); + } + else + { + this->TaskInterrupt(); + } +} + +template +void CAI_GrenadeUser::RunTask_FaceTossDir( const Task_t *pTask ) +{ + // project a point along the toss vector and turn to face that point. + this->GetMotor()->SetIdealYawToTargetAndUpdate( this->GetLocalOrigin() + m_vecTossVelocity * 64, AI_KEEP_YAW_SPEED ); + + if ( this->FacingIdeal() ) + { + this->TaskComplete( true ); + } +} + +#endif diff --git a/mp/src/game/server/mapbase/ai_monitor.cpp b/mp/src/game/server/mapbase/ai_monitor.cpp new file mode 100644 index 00000000..754ff56d --- /dev/null +++ b/mp/src/game/server/mapbase/ai_monitor.cpp @@ -0,0 +1,733 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: An entity that watches an NPC for certain things. +// +//============================================================================= + +#include "cbase.h" +#include "ai_schedule.h" +#include "ai_hint.h" +#include "ai_route.h" +#include "ai_basenpc.h" +#include "saverestore_utlvector.h" + + +//#define AI_MONITOR_MAX_TARGETS 16 + +// Uses a CUtlVector instead of a CBitVec for conditions/schedules. +// Using a CUtlVector makes this a lot easier, if you ask me. Please note that the CBitVec version is incomplete. +#define AI_MONITOR_USE_UTLVECTOR 1 + +//----------------------------------------------------------------------------- +// Purpose: AI monitoring. Probably bad. +//----------------------------------------------------------------------------- +class CAI_Monitor : public CLogicalEntity +{ + DECLARE_CLASS( CAI_Monitor, CLogicalEntity ); +public: + CAI_Monitor(); + + void Spawn(); + void Activate( void ); + + virtual int Save( ISave &save ); + virtual int Restore( IRestore &restore ); +#if !AI_MONITOR_USE_UTLVECTOR + void SaveConditions( ISave &save, const CAI_ScheduleBits &conditions ); + void RestoreConditions( IRestore &restore, CAI_ScheduleBits *pConditions ); +#endif + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + //virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + + // Populates our NPC list. + void PopulateNPCs(inputdata_t *inputdata); + + // Does evaluation, fires outputs, etc. + bool NPCDoEval(CAI_BaseNPC *pNPC); + + // Thinks. + void MonitorThink(); + + CAI_BaseNPC *GetFirstTarget(); + + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); PopulateNPCs(&inputdata); } + void InputPopulateNPCs( inputdata_t &inputdata ); + void InputTest( inputdata_t &inputdata ); + void InputTestNPC( inputdata_t &inputdata ); + + // Allows mappers to get condition/schedule names from ID + void InputGetConditionName( inputdata_t &inputdata ) { m_OutConditionName.Set(AllocPooledString(ConditionName(inputdata.value.Int())), inputdata.pActivator, this); } + void InputGetScheduleName( inputdata_t &inputdata ) { m_OutScheduleName.Set(AllocPooledString(ScheduleName(inputdata.value.Int())), inputdata.pActivator, this); } + COutputString m_OutConditionName; + COutputString m_OutScheduleName; + + void InputSetCondition( inputdata_t &inputdata ) { SetCondition(TranslateConditionString(inputdata.value.String())); } + void InputClearCondition( inputdata_t &inputdata ) { ClearCondition(TranslateConditionString(inputdata.value.String())); } +#if AI_MONITOR_USE_UTLVECTOR + void InputClearAllConditions( inputdata_t &inputdata ) { m_Conditions.RemoveAll(); } +#else + void InputClearAllConditions( inputdata_t &inputdata ) { m_Conditions.ClearAll(); } +#endif + + void InputSetSchedule( inputdata_t &inputdata ) { SetSchedule(TranslateScheduleString(inputdata.value.String())); } + void InputClearSchedule( inputdata_t &inputdata ) { ClearSchedule(TranslateScheduleString(inputdata.value.String())); } +#if AI_MONITOR_USE_UTLVECTOR + void InputClearAllSchedules( inputdata_t &inputdata ) { m_Schedules.RemoveAll(); } +#else + void InputClearAllSchedules( inputdata_t &inputdata ) { m_Schedules.ClearAll(); } +#endif + + void InputSetHint( inputdata_t &inputdata ) { SetHint(inputdata.value.Int()); } + void InputClearHint( inputdata_t &inputdata ) { ClearHint(inputdata.value.Int()); } + void InputClearAllHints( inputdata_t &inputdata ) { m_Hints.RemoveAll(); } + +public: + + bool m_bStartDisabled; + + // The NPCs. + CUtlVector pNPCs; + int m_iMaxEnts; + + // Stop and engage cooldown at first successful pass + bool m_bCooldownAtFirstSuccess; + + // Interval between monitors + float m_flThinkTime; + #define GetThinkTime() (m_flThinkTime != 0 ? m_flThinkTime : TICK_INTERVAL) + + // Cooldown after something is satisfied + float m_flCooldownTime; + #define GetCooldownTime() (m_flCooldownTime != -1 ? m_flCooldownTime : GetThinkTime()) + + // ------------------------------ + // Conditions + // ------------------------------ +#if AI_MONITOR_USE_UTLVECTOR + CUtlVector m_Conditions; +#else + CAI_ScheduleBits m_Conditions; +#endif + + COutputInt m_OnNPCHasCondition; + COutputInt m_OnNPCLacksCondition; + + // Condition functions, most of these are from CAI_BaseNPC. +#if AI_MONITOR_USE_UTLVECTOR + inline void SetCondition( int iCondition ) { m_Conditions.HasElement(iCondition) ? NULL : m_Conditions.AddToTail(iCondition); } + inline void ClearCondition( int iCondition ) { m_Conditions.FindAndRemove(iCondition); } + inline bool HasCondition( int iCondition ) { return m_Conditions.HasElement(iCondition); } +#else + inline void SetCondition( int iCondition ) { m_Conditions.Set(iCondition); } + inline void ClearCondition( int iCondition ) { m_Conditions.Clear(iCondition); } + inline bool HasCondition( int iCondition ) { return m_Conditions.IsBitSet(iCondition); } +#endif + + static int GetConditionID(const char* condName) { return CAI_BaseNPC::GetSchedulingSymbols()->ConditionSymbolToId(condName); } + const char *ConditionName(int conditionID); + + int TranslateConditionString(const char *condName); + inline int ConditionLocalToGlobal(CAI_BaseNPC *pTarget, int conditionID) { return pTarget->GetClassScheduleIdSpace()->ConditionLocalToGlobal(conditionID); } + + // ------------------------------ + // Schedules + // ------------------------------ +#if AI_MONITOR_USE_UTLVECTOR + CUtlVector m_Schedules; +#else + CAI_ScheduleBits m_Schedules; +#endif + + bool m_bTranslateSchedules; + + COutputInt m_OnNPCRunningSchedule; + COutputInt m_OnNPCNotRunningSchedule; + + // Schedule functions, some of these are from CAI_BaseNPC. +#if AI_MONITOR_USE_UTLVECTOR + inline void SetSchedule( int iSchedule ) { m_Schedules.HasElement(iSchedule) ? NULL : m_Schedules.AddToTail(iSchedule); } + inline void ClearSchedule( int iSchedule ) { m_Schedules.FindAndRemove(iSchedule); } + inline bool HasSchedule( int iSchedule ) { return m_Schedules.HasElement(iSchedule); } +#else + inline void SetSchedule( int iSchedule ) { m_Schedules.Set(iSchedule); } + inline void ClearSchedule( int iSchedule ) { m_Schedules.Clear(iSchedule); } + inline bool HasSchedule( int iSchedule ) { return m_Schedules.IsBitSet(iSchedule); } +#endif + + static int GetScheduleID(const char* schedName) { return CAI_BaseNPC::GetSchedulingSymbols()->ScheduleSymbolToId(schedName); } + const char *ScheduleName(int scheduleID); + + int TranslateScheduleString(const char *schedName); + inline int ScheduleLocalToGlobal(CAI_BaseNPC *pTarget, int scheduleID) { return pTarget->GetClassScheduleIdSpace()->ScheduleLocalToGlobal(scheduleID); } + + // ------------------------------ + // Tasks + // ------------------------------ + + // TODO + + // ------------------------------ + // Hints + // ------------------------------ + CUtlVector m_Hints; + + COutputInt m_OnNPCUsingHint; + COutputInt m_OnNPCNotUsingHint; + + inline void SetHint( int iHint ) { m_Hints.HasElement(iHint) ? NULL : m_Hints.AddToTail(iHint); } + inline void ClearHint( int iHint ) { m_Hints.FindAndRemove(iHint); } + inline bool HasHint( int iHint ) { return m_Hints.HasElement(iHint); } + + // Only register a hint as "being used" when the NPC is this distance away or less + float m_flDistanceFromHint; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( ai_monitor, CAI_Monitor ); + + +BEGIN_DATADESC( CAI_Monitor ) + +#if AI_MONITOR_USE_UTLVECTOR + DEFINE_UTLVECTOR( m_Conditions, FIELD_INTEGER ), + DEFINE_UTLVECTOR( m_Schedules, FIELD_INTEGER ), +#endif + DEFINE_UTLVECTOR( m_Hints, FIELD_INTEGER ), + + // Keys + DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_INPUT( m_flThinkTime, FIELD_FLOAT, "SetMonitorInterval" ), + DEFINE_INPUT( m_flCooldownTime, FIELD_FLOAT, "SetCooldownTime" ), + DEFINE_KEYFIELD( m_bCooldownAtFirstSuccess, FIELD_BOOLEAN, "CooldownAt" ), + + DEFINE_KEYFIELD( m_iMaxEnts, FIELD_INTEGER, "MaxEnts" ), + + DEFINE_KEYFIELD( m_bTranslateSchedules, FIELD_BOOLEAN, "TranslateSchedules" ), + + DEFINE_KEYFIELD( m_flDistanceFromHint, FIELD_FLOAT, "HintDistance" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "UpdateActors", InputPopulateNPCs ), + DEFINE_INPUTFUNC( FIELD_VOID, "Test", InputTest ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "TestNPC", InputTestNPC ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "GetConditionName", InputGetConditionName ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "GetScheduleName", InputGetScheduleName ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetCondition", InputSetCondition ), + DEFINE_INPUTFUNC( FIELD_STRING, "ClearCondition", InputClearCondition ), + DEFINE_INPUTFUNC( FIELD_STRING, "ClearAllConditions", InputClearAllConditions ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetSchedule", InputSetSchedule ), + DEFINE_INPUTFUNC( FIELD_STRING, "ClearSchedule", InputClearSchedule ), + DEFINE_INPUTFUNC( FIELD_STRING, "ClearAllSchedules", InputClearAllSchedules ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHint", InputSetHint ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "ClearHint", InputClearHint ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "ClearAllHints", InputClearAllHints ), + + // Outputs + DEFINE_OUTPUT(m_OutConditionName, "OutConditionName"), + DEFINE_OUTPUT(m_OutScheduleName, "OutScheduleName"), + DEFINE_OUTPUT(m_OnNPCHasCondition, "OnNPCHasCondition"), + DEFINE_OUTPUT(m_OnNPCLacksCondition, "OnNPCLacksCondition"), + DEFINE_OUTPUT(m_OnNPCRunningSchedule, "OnNPCRunningSchedule"), + DEFINE_OUTPUT(m_OnNPCNotRunningSchedule, "OnNPCNotRunningSchedule"), + DEFINE_OUTPUT(m_OnNPCUsingHint, "OnNPCUsingHint"), + DEFINE_OUTPUT(m_OnNPCNotUsingHint, "OnNPCNotUsingHint"), + + DEFINE_THINKFUNC( MonitorThink ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAI_Monitor::CAI_Monitor() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Monitor::Spawn() +{ + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Monitor::Activate( void ) +{ + BaseClass::Activate(); + + if (!m_bStartDisabled) + { + SetThink(&CAI_Monitor::MonitorThink); + SetNextThink(gpGlobals->curtime + GetThinkTime()); + } + + PopulateNPCs(NULL); +} + +//----------------------------------------------------------------------------- +// Enable, disable +//----------------------------------------------------------------------------- +void CAI_Monitor::InputEnable( inputdata_t &inputdata ) +{ + PopulateNPCs(&inputdata); + + SetThink( &CAI_Monitor::MonitorThink ); + SetNextThink( gpGlobals->curtime + GetThinkTime() ); +} + +void CAI_Monitor::InputDisable( inputdata_t &inputdata ) +{ + SetThink( NULL ); +} + +void CAI_Monitor::InputPopulateNPCs( inputdata_t &inputdata ) +{ + PopulateNPCs(&inputdata); +} + +void CAI_Monitor::InputTest( inputdata_t &inputdata ) +{ + bool bFoundResults = false; + for (int i = 0; i < pNPCs.Count(); i++) + { + if (pNPCs[i] != NULL) + { + if (!bFoundResults) + bFoundResults = NPCDoEval(pNPCs[i]); + else if (!m_bCooldownAtFirstSuccess) + NPCDoEval(pNPCs[i]); + else + break; + } + else + { + // If we have a null NPC, we should probably update. + // This could probably go wrong in more than one way... + PopulateNPCs(NULL); + i--; + } + } +} + +void CAI_Monitor::InputTestNPC( inputdata_t &inputdata ) +{ + CAI_BaseNPC *pNPC = inputdata.value.Entity()->MyNPCPointer(); + if (!inputdata.value.Entity() || !pNPC) + return; + + NPCDoEval(pNPC); +} + +//----------------------------------------------------------------------------- +// Purpose: Save/restore stuff from CAI_BaseNPC +//----------------------------------------------------------------------------- +int CAI_Monitor::Save( ISave &save ) +{ +#if !AI_MONITOR_USE_UTLVECTOR + save.StartBlock(); + SaveConditions( save, m_Conditions ); + SaveConditions( save, m_Schedules ); + save.EndBlock(); +#endif + + return BaseClass::Save(save); +} + +int CAI_Monitor::Restore( IRestore &restore ) +{ +#if !AI_MONITOR_USE_UTLVECTOR + restore.StartBlock(); + RestoreConditions( restore, &m_Conditions ); + RestoreConditions( restore, &m_Schedules ); + restore.EndBlock(); +#endif + + return BaseClass::Restore(restore); +} + +#if !AI_MONITOR_USE_UTLVECTOR +void CAI_Monitor::SaveConditions( ISave &save, const CAI_ScheduleBits &conditions ) +{ + for (int i = 0; i < MAX_CONDITIONS; i++) + { + if (conditions.IsBitSet(i)) + { + const char *pszConditionName = ConditionName(AI_RemapToGlobal(i)); + if ( !pszConditionName ) + break; + save.WriteString( pszConditionName ); + } + } + save.WriteString( "" ); +} + +//------------------------------------- + +void CAI_Monitor::RestoreConditions( IRestore &restore, CAI_ScheduleBits *pConditions ) +{ + pConditions->ClearAll(); + char szCondition[256]; + for (;;) + { + restore.ReadString( szCondition, sizeof(szCondition), 0 ); + if ( !szCondition[0] ) + break; + int iCondition = GetConditionID( szCondition ); + if ( iCondition != -1 ) + pConditions->Set( AI_RemapFromGlobal( iCondition ) ); + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Monitor::PopulateNPCs(inputdata_t *inputdata) +{ + // pNPCs[i] != NULL && !pNPCs[i]->IsMarkedForDeletion() && !pNPCs[i]->GetState() != NPC_STATE_DEAD + //pNPCs = CUtlVector>(); + pNPCs.RemoveAll(); + + CBaseEntity *pActivator = inputdata ? inputdata->pActivator : NULL; + CBaseEntity *pCaller = inputdata ? inputdata->pCaller : NULL; + + CBaseEntity *pEnt = gEntList.FindEntityGeneric(NULL, STRING(m_target), this, pActivator, pCaller); + while (pEnt) + { + if (pEnt->IsNPC()) + { + pNPCs.AddToTail(pEnt->MyNPCPointer()); + DevMsg("Added %s to element %i\n", pEnt->GetDebugName(), pNPCs.Count()); + + // 0 = no limit because the list would already have at least one element by the time this is checked. + if (pNPCs.Count() == m_iMaxEnts) + break; + } + + pEnt = gEntList.FindEntityGeneric(pEnt, STRING(m_target), this, pActivator, pCaller); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAI_BaseNPC *CAI_Monitor::GetFirstTarget() +{ + for (int i = 0; i < pNPCs.Count(); i++) + { + if (pNPCs[i] != NULL) + return pNPCs[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_Monitor::NPCDoEval(CAI_BaseNPC *pNPC) +{ + // Because we return based on this + m_OnNPCHasCondition.Init(0); + m_OnNPCRunningSchedule.Init(0); + m_OnNPCUsingHint.Init(0); + + + // ---------- + // Conditions + // ---------- +#if AI_MONITOR_USE_UTLVECTOR + for (int cond = 0; cond < m_Conditions.Count(); cond++) +#else + for (int cond = 0; cond < m_Conditions.GetNumBits(); cond++) +#endif + { + if (pNPC->HasCondition(m_Conditions[cond])) + { + DevMsg("NPC has condition %i, index %i, name %s\n", m_Conditions[cond], cond, ConditionName(m_Conditions[cond])); + m_OnNPCHasCondition.Set(m_Conditions[cond], pNPC, this); + m_OutConditionName.Set(MAKE_STRING(ConditionName(m_Conditions[cond])), pNPC, this); + } + else + { + DevMsg("NPC does not have condition %i, index %i, name %s\n", m_Conditions[cond], cond, ConditionName(m_Conditions[cond])); + m_OnNPCLacksCondition.Set(m_Conditions[cond], pNPC, this); + m_OutConditionName.Set(MAKE_STRING(ConditionName(m_Conditions[cond])), pNPC, this); + } + /* + bool bDecisive = false; + switch (m_ConditionsOp) + { + case AIMONITOR_CONDITIONAL_NOR: + bConditionsTrue = true; + case AIMONITOR_CONDITIONAL_OR: + { + if (pNPC->HasCondition(m_Conditions[cond])) + { + // One is valid, pass conditions + bConditionsTrue = !bConditionsTrue; + bDecisive = true; + break; + } + } break; + case AIMONITOR_CONDITIONAL_NAND: + bConditionsTrue = true; + case AIMONITOR_CONDITIONAL_AND: + { + if (!pNPCs[i]->HasCondition(m_Conditions[cond])) + { + // One is invalid, don't pass conditions + bConditionsTrue = !bConditionsTrue; + bDecisive = true; + break; + } + } break; + } + + if (bDecisive) + break; + */ + } + + // ---------- + // Schedules + // ---------- +#if AI_MONITOR_USE_UTLVECTOR + for (int sched = 0; sched < m_Schedules.Count(); sched++) +#else + for (int sched = 0; sched < m_Schedules.GetNumBits(); sched++) +#endif + { + if (pNPC->IsCurSchedule(m_bTranslateSchedules ? pNPC->TranslateSchedule(m_Schedules[sched]) : m_Schedules[sched])) + { + DevMsg("NPC is running schedule %i, index %i, name %s\n", m_Schedules[sched], sched, ScheduleName(m_Schedules[sched])); + m_OnNPCRunningSchedule.Set(m_Schedules[sched], pNPC, this); + m_OutScheduleName.Set(AllocPooledString(ScheduleName(m_Schedules[sched])), pNPC, this); + } + else + { + DevMsg("NPC is not running schedule %i, index %i, name %s\n", m_Schedules[sched], sched, ScheduleName(m_Schedules[sched])); + m_OnNPCNotRunningSchedule.Set(m_Schedules[sched], pNPC, this); + m_OutScheduleName.Set(AllocPooledString(ScheduleName(m_Schedules[sched])), pNPC, this); + } + } + + // ---------- + // Hints + // ---------- + CAI_Hint *pHint = pNPC->GetHintNode(); + if (m_Hints.Count() > 0) + { + if (!pHint || (m_flDistanceFromHint > 0 && pHint->GetLocalOrigin().DistTo(pNPC->GetLocalOrigin()) > m_flDistanceFromHint)) + { + for (int hint = 0; hint < m_Hints.Count(); hint++) + { + m_OnNPCNotUsingHint.Set(m_Hints[hint], pNPC, this); + } + } + else + { + for (int hint = 0; hint < m_Hints.Count(); hint++) + { + if (pHint->HintType() == m_Hints[hint]) + { + DevMsg("NPC is using hint %i, index %i\n", m_Hints[hint], hint); + m_OnNPCUsingHint.Set(m_Hints[hint], pNPC, this); + } + else + { + DevMsg("NPC is not using hint %i, index %i\n", m_Hints[hint], hint); + m_OnNPCNotUsingHint.Set(m_Hints[hint], pNPC, this); + } + } + } + } + + + // Return whether any of our "valid" outputs fired. + return (m_OnNPCHasCondition.Get() != 0 + || m_OnNPCRunningSchedule.Get() != 0 + || m_OnNPCUsingHint.Get() != 0 + ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Monitor::MonitorThink() +{ + bool bMonitorFoundResults = false; + for (int i = 0; i < pNPCs.Count(); i++) + { + if (pNPCs[i] != NULL) + { + if (!bMonitorFoundResults) + bMonitorFoundResults = NPCDoEval(pNPCs[i]); + else if (!m_bCooldownAtFirstSuccess) + NPCDoEval(pNPCs[i]); + else + break; + } + else + { + // If we have a null NPC, we should probably update. + // This could probably go wrong in more than one way... + PopulateNPCs(NULL); + i--; + } + } + + if (bMonitorFoundResults) + SetNextThink(gpGlobals->curtime + GetCooldownTime()); + else + SetNextThink(gpGlobals->curtime + GetThinkTime()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CAI_Monitor::ConditionName(int conditionID) +{ + if ( AI_IdIsLocal( conditionID ) ) + { + // Get our first target and find a local condition + CAI_BaseNPC *pTarget = GetFirstTarget(); + if (!pTarget) + return NULL; + + conditionID = ConditionLocalToGlobal(pTarget, conditionID); + } + + return CAI_BaseNPC::GetSchedulingSymbols()->ConditionIdToSymbol(conditionID); +} + +const char *CAI_Monitor::ScheduleName(int scheduleID) +{ + if ( AI_IdIsLocal( scheduleID ) ) + { + // Get our first target and find a local condition + CAI_BaseNPC *pTarget = GetFirstTarget(); + if (!pTarget) + return NULL; + + scheduleID = ScheduleLocalToGlobal(pTarget, scheduleID); + } + + return CAI_BaseNPC::GetSchedulingSymbols()->ScheduleIdToSymbol(scheduleID); +} + +int CAI_Monitor::TranslateConditionString(const char *condName) +{ + if (condName[0] == 'C') + { + // String + int cond = GetConditionID(condName); + if (cond > -1) + { + DevMsg("Setting condition %i from %s\n", cond, condName); + return cond; + } + } + else + { + // Int + DevMsg("Setting condition %s\n", condName); + + // Assume the mapper didn't compensate for global ID stuff. + // (as if either of us understand it) + return atoi(condName) + GLOBAL_IDS_BASE; + } + return 0; +} + +int CAI_Monitor::TranslateScheduleString(const char *schedName) +{ + if (schedName[0] == 'S') + { + // String + int sched = GetScheduleID(schedName); + if (sched > -1) + { + DevMsg("Setting schedule %i from %s\n", sched, schedName); + return sched; + } + } + else + { + // Int + DevMsg("Setting schedule %s\n", schedName); + return atoi(schedName); + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "ConditionsSimple")) + { + // Hammer SmartEdit helper that shouldn't be overridden. + // It's not supposed to be overridden. + SetCondition(atoi(szValue)); + } + else if (FStrEq(szKeyName, "Conditions")) + { + char *token = strtok(strdup(szValue), ":"); + while (token) + { + SetCondition(TranslateConditionString(token)); + + token = strtok(NULL, ":"); + } + } + else if (FStrEq(szKeyName, "SchedulesSimple")) + { + // Hammer SmartEdit helper that shouldn't be overridden. + SetCondition(atoi(szValue)); + } + else if (FStrEq(szKeyName, "Schedules")) + { + char *token = strtok(strdup(szValue), ":"); + while (token) + { + SetSchedule(TranslateScheduleString(token)); + + token = strtok(NULL, ":"); + } + } + else if (FStrEq(szKeyName, "HintsSimple")) + { + // Hammer SmartEdit helper that shouldn't be overridden. + SetHint(atoi(szValue)); + } + else if (FStrEq(szKeyName, "Hints")) + { + char *token = strtok(strdup(szValue), ":"); + while (token) + { + SetHint(atoi(szValue)); + + token = strtok(NULL, ":"); + } + } + else + return CBaseEntity::KeyValue( szKeyName, szValue ); + + return true; +} diff --git a/mp/src/game/server/mapbase/ai_weaponmodifier.cpp b/mp/src/game/server/mapbase/ai_weaponmodifier.cpp new file mode 100644 index 00000000..2ddec2ea --- /dev/null +++ b/mp/src/game/server/mapbase/ai_weaponmodifier.cpp @@ -0,0 +1,250 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Be warned, because this entity is TERRIBLE! +// +//============================================================================= + +#include "cbase.h" +#include "ai_basenpc.h" +#include "saverestore_utlvector.h" + + + +//----------------------------------------------------------------------------- +// Purpose: A special CAI_ShotRegulator class designed to be used with ai_weaponmodifier. +// I'm too chicken to do anything fun with it. +//----------------------------------------------------------------------------- +typedef CAI_ShotRegulator CAI_CustomShotRegulator; +/* +class CAI_CustomShotRegulator : public CAI_ShotRegulator +{ + DECLARE_CLASS( CAI_CustomShotRegulator, CAI_ShotRegulator ); +public: + void SetMinBurstInterval( float flMinBurstInterval ) { m_flMinBurstInterval = flMinBurstInterval; } + void SetMaxBurstInterval( float flMaxBurstInterval ) { m_flMaxBurstInterval = flMaxBurstInterval; } +}; +*/ + +// A lot of functions can't set each range individually, so we have to do this for a lot of them. +// (again, too chicken to do something useful with CAI_CustomShotRegulator) +#define WeaponModifierSetRange(string, function) float minimum = 0; \ + float maximum = 0; \ + if (sscanf(string, "%f:%f", &minimum, &maximum)) \ + function(minimum, maximum); + +#define WEAPONMODIFIER_MAX_NPCS 16 + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CAI_WeaponModifier : public CLogicalEntity +{ + DECLARE_CLASS( CAI_WeaponModifier, CLogicalEntity ); + DECLARE_DATADESC(); +public: + + void Spawn(); + + virtual int Save( ISave &save ); + virtual int Restore( IRestore &restore ); + + bool KeyValue(const char *szKeyName, const char *szValue); + + void EnableOnNPC(CAI_BaseNPC *pNPC); + void DisableOnNPC(CAI_BaseNPC *pNPC); + + // Inputs + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputEnableOnNPC( inputdata_t &inputdata ); + void InputDisableOnNPC( inputdata_t &inputdata ); + + void InputSetBurstInterval( inputdata_t &inputdata ) { WeaponModifierSetRange(inputdata.value.String(), m_ModdedRegulator.SetBurstInterval); } + void InputSetRestInterval( inputdata_t &inputdata ) { WeaponModifierSetRange(inputdata.value.String(), m_ModdedRegulator.SetRestInterval); } + void InputSetBurstShotCountRange( inputdata_t &inputdata ) { WeaponModifierSetRange(inputdata.value.String(), m_ModdedRegulator.SetBurstShotCountRange); } + void InputSetBurstShotsRemaining( inputdata_t &inputdata ) { m_ModdedRegulator.SetBurstShotsRemaining(inputdata.value.Int()); } + + void InputEnableShooting( inputdata_t &inputdata ) { m_ModdedRegulator.EnableShooting(); } + void InputDisableShooting( inputdata_t &inputdata ) { m_ModdedRegulator.DisableShooting(); } + void InputFireNoEarlierThan( inputdata_t &inputdata ) { m_ModdedRegulator.FireNoEarlierThan(gpGlobals->curtime + inputdata.value.Float()); } + void InputReset( inputdata_t &inputdata ) { m_ModdedRegulator.Reset(inputdata.value.Bool()); } + + // The NPCs and their original regulators. + AIHANDLE m_hNPCs[WEAPONMODIFIER_MAX_NPCS]; + CAI_ShotRegulator m_StoredRegulators[WEAPONMODIFIER_MAX_NPCS]; + + CAI_CustomShotRegulator m_ModdedRegulator; + + bool m_bDisabled; +}; + +LINK_ENTITY_TO_CLASS(ai_weaponmodifier, CAI_WeaponModifier); + +BEGIN_DATADESC( CAI_WeaponModifier ) + + DEFINE_EMBEDDED( m_ModdedRegulator ), + + DEFINE_ARRAY( m_hNPCs, FIELD_EHANDLE, WEAPONMODIFIER_MAX_NPCS ), + DEFINE_EMBEDDED_ARRAY( m_StoredRegulators, WEAPONMODIFIER_MAX_NPCS ), + + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "EnableOnNPC", InputEnableOnNPC ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "DisableOnNPC", InputDisableOnNPC ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetBurstInterval", InputSetBurstInterval ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRestInterval", InputSetRestInterval ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetBurstShotCountRange", InputSetBurstShotCountRange ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetBurstShotsRemaining", InputSetBurstShotsRemaining ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableShooting", InputEnableShooting ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableShooting", InputDisableShooting ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "FireNoEarlierThan", InputFireNoEarlierThan ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "Reset", InputReset ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_WeaponModifier::Spawn() +{ + if (!m_bDisabled) + { + inputdata_t inputdata; + InputEnable(inputdata); + } + + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CAI_WeaponModifier::Save( ISave &save ) +{ + return BaseClass::Save(save); +} + +int CAI_WeaponModifier::Restore( IRestore &restore ) +{ + return BaseClass::Restore(restore); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CAI_WeaponModifier::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "BurstInterval")) + { + WeaponModifierSetRange(szValue, m_ModdedRegulator.SetBurstInterval); + } + else if (FStrEq(szKeyName, "RestInterval")) + { + WeaponModifierSetRange(szValue, m_ModdedRegulator.SetRestInterval); + } + else if (FStrEq(szKeyName, "BurstShotCountRange")) + { + WeaponModifierSetRange(szValue, m_ModdedRegulator.SetBurstShotCountRange); + } + else if (FStrEq(szKeyName, "BurstShotsRemaining")) + { + m_ModdedRegulator.SetBurstShotsRemaining(atoi(szValue)); + } + else + return BaseClass::KeyValue(szKeyName, szValue); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_WeaponModifier::EnableOnNPC( CAI_BaseNPC *pNPC ) +{ + for (int i = 0; i < WEAPONMODIFIER_MAX_NPCS; i++) + { + if (m_hNPCs[i] == NULL) + { + m_hNPCs[i] = pNPC; + m_StoredRegulators[i] = *pNPC->GetShotRegulator(); + + pNPC->SetShotRegulator(m_ModdedRegulator); + } + else if (m_hNPCs[i] == pNPC) + { + // We're already in it + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_WeaponModifier::DisableOnNPC( CAI_BaseNPC *pNPC ) +{ + for (int i = 0; i < WEAPONMODIFIER_MAX_NPCS; i++) + { + if (m_hNPCs[i] == pNPC) + { + pNPC->SetShotRegulator(m_StoredRegulators[i]); + m_hNPCs[i] = NULL; + + // Just reset it to our modded one + m_StoredRegulators[i] = m_ModdedRegulator; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_WeaponModifier::InputEnable( inputdata_t &inputdata ) +{ + CBaseEntity *pEntity = gEntList.FindEntityByName(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + while (pEntity) + { + if (pEntity->IsNPC()) + { + EnableOnNPC(pEntity->MyNPCPointer()); + } + + pEntity = gEntList.FindEntityByName(pEntity, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + } +} + +void CAI_WeaponModifier::InputDisable( inputdata_t &inputdata ) +{ + for (int i = 0; i < WEAPONMODIFIER_MAX_NPCS; i++) + { + m_hNPCs[i]->SetShotRegulator(m_StoredRegulators[i]); + m_hNPCs[i] = NULL; + + // Just reset it to our modded one + m_StoredRegulators[i] = m_ModdedRegulator; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_WeaponModifier::InputEnableOnNPC( inputdata_t &inputdata ) +{ + if (inputdata.value.Entity() && inputdata.value.Entity()->IsNPC()) + { + EnableOnNPC(inputdata.value.Entity()->MyNPCPointer()); + } +} + +void CAI_WeaponModifier::InputDisableOnNPC( inputdata_t &inputdata ) +{ + if (inputdata.value.Entity() && inputdata.value.Entity()->IsNPC()) + { + DisableOnNPC(inputdata.value.Entity()->MyNPCPointer()); + } +} diff --git a/mp/src/game/server/mapbase/closecaption_entity.cpp b/mp/src/game/server/mapbase/closecaption_entity.cpp new file mode 100644 index 00000000..a166660e --- /dev/null +++ b/mp/src/game/server/mapbase/closecaption_entity.cpp @@ -0,0 +1,81 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" + + +class CEnvCloseCaption : public CBaseEntity +{ + DECLARE_CLASS( CEnvCloseCaption, CBaseEntity ); +public: + + bool AllPlayers() { return true; } + + void InputSend( inputdata_t &inputdata ); + + //bool m_bCustom; + int m_iFlags; + float m_flDuration; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( env_closecaption, CEnvCloseCaption ); + +BEGIN_DATADESC( CEnvCloseCaption ) + + //DEFINE_KEYFIELD( m_bCustom, FIELD_BOOLEAN, "custom" ), + DEFINE_KEYFIELD( m_iFlags, FIELD_INTEGER, "flags" ), + DEFINE_INPUT( m_flDuration, FIELD_FLOAT, "SetDuration" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "Send", InputSend ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEnvCloseCaption::InputSend( inputdata_t &inputdata ) +{ + char szCC[512]; + Q_strncpy(szCC, inputdata.value.String(), sizeof(szCC)); + + byte byteflags = m_iFlags; + if ( AllPlayers() ) + { + CReliableBroadcastRecipientFilter user; + UserMessageBegin( user, "CloseCaption" ); + WRITE_STRING( szCC ); + WRITE_SHORT( MIN( 255, (int)( m_flDuration * 10.0f ) ) ), + WRITE_BYTE( byteflags ), + MessageEnd(); + } + else + { + CBaseEntity *pPlayer = NULL; + + if ( inputdata.pActivator && inputdata.pActivator->IsPlayer() ) + { + pPlayer = inputdata.pActivator; + } + else + { + pPlayer = UTIL_GetLocalPlayer(); + } + + if ( !pPlayer || !pPlayer->IsNetClient() ) + return; + + CSingleUserRecipientFilter user( (CBasePlayer *)pPlayer ); + user.MakeReliable(); + UserMessageBegin( user, "CloseCaption" ); + WRITE_STRING( szCC ); + WRITE_SHORT( MIN( 255, (int)( m_flDuration * 10.0f ) ) ), + WRITE_BYTE( byteflags ), + MessageEnd(); + } +} diff --git a/mp/src/game/server/mapbase/datadesc_mod.cpp b/mp/src/game/server/mapbase/datadesc_mod.cpp new file mode 100644 index 00000000..6ef4c6c0 --- /dev/null +++ b/mp/src/game/server/mapbase/datadesc_mod.cpp @@ -0,0 +1,199 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "datadesc_mod.h" +#include "saverestore.h" + + +// Sets a field's value to a specific string. +char *Datadesc_SetFieldString( const char *szValue, CBaseEntity *pObject, typedescription_t *pField, fieldtype_t *pFieldType ) +{ + // Copied from ::ParseKeyvalue... + fieldtype_t fieldtype = FIELD_VOID; + int fieldOffset = pField->fieldOffset[ TD_OFFSET_NORMAL ]; + switch( pField->fieldType ) + { + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: + case FIELD_STRING: + (*(string_t *)((char *)pObject + fieldOffset)) = AllocPooledString( szValue ); + fieldtype = FIELD_STRING; + break; + + case FIELD_TIME: + case FIELD_FLOAT: + (*(float *)((char *)pObject + fieldOffset)) = atof( szValue ); + fieldtype = FIELD_FLOAT; + break; + + case FIELD_BOOLEAN: + (*(bool *)((char *)pObject + fieldOffset)) = (bool)(atoi( szValue ) != 0); + fieldtype = FIELD_BOOLEAN; + break; + + case FIELD_CHARACTER: + (*(char *)((char *)pObject + fieldOffset)) = (char)atoi( szValue ); + fieldtype = FIELD_CHARACTER; + break; + + case FIELD_SHORT: + (*(short *)((char *)pObject + fieldOffset)) = (short)atoi( szValue ); + fieldtype = FIELD_SHORT; + break; + + case FIELD_INTEGER: + case FIELD_TICK: + (*(int *)((char *)pObject + fieldOffset)) = atoi( szValue ); + fieldtype = FIELD_INTEGER; + break; + + case FIELD_POSITION_VECTOR: + case FIELD_VECTOR: + UTIL_StringToVector( (float *)((char *)pObject + fieldOffset), szValue ); + fieldtype = FIELD_VECTOR; + break; + + case FIELD_VMATRIX: + case FIELD_VMATRIX_WORLDSPACE: + UTIL_StringToFloatArray( (float *)((char *)pObject + fieldOffset), 16, szValue ); + fieldtype = FIELD_VMATRIX; // ??? + break; + + case FIELD_MATRIX3X4_WORLDSPACE: + UTIL_StringToFloatArray( (float *)((char *)pObject + fieldOffset), 12, szValue ); + fieldtype = FIELD_VMATRIX; // ??? + break; + + case FIELD_COLOR32: + UTIL_StringToColor32( (color32 *) ((char *)pObject + fieldOffset), szValue ); + fieldtype = FIELD_COLOR32; + break; + + case FIELD_CUSTOM: + { + SaveRestoreFieldInfo_t fieldInfo = + { + (char *)pObject + fieldOffset, + pObject, + pField + }; + pField->pSaveRestoreOps->Parse( fieldInfo, szValue ); + fieldtype = FIELD_STRING; + break; + } + + default: + case FIELD_INTERVAL: + case FIELD_CLASSPTR: + case FIELD_MODELINDEX: + case FIELD_MATERIALINDEX: + case FIELD_EDICT: + return NULL; + //Warning( "%s cannot set field of type %i.\n", GetDebugName(), dmap->dataDesc[i].fieldType ); + break; + } + + if (pFieldType) + *pFieldType = fieldtype; + + return ((char*)pObject) + fieldOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: ReadUnregisteredKeyfields() was a feeble attempt to obtain non-keyfield keyvalues from KeyValue() with variant_t. +// +// I didn't know about GetKeyValue() until 9/29/2018. +// I don't remember why I decided to write down the date I found out about it. Maybe I considered that monumental of a discovery. +// +// However, we still use ReadUnregisteredKeyfields() since GetKeyValue() only supports a string while this function was used for entire variant_ts. +// It now calls GetKeyValue() and returns it as an allocated string. +//----------------------------------------------------------------------------- +bool ReadUnregisteredKeyfields(CBaseEntity *pTarget, const char *szKeyName, variant_t *variant) +{ + if (!pTarget) + return false; + + char szValue[256]; + if (pTarget->GetKeyValue(szKeyName, szValue, sizeof(szValue))) + { + variant->SetString(AllocPooledString(szValue)); // MAKE_STRING causes badness, must pool + return true; + } + +#if 0 + if ( FStrEq( szKeyName, "targetname" ) ) + { + variant->SetString(pTarget->GetEntityName()); + return true; + } + + if( FStrEq( szKeyName, "origin" ) ) + { + variant->SetPositionVector3D(pTarget->GetAbsOrigin()); + return true; + } + + if( FStrEq( szKeyName, "angles" ) /*|| FStrEq( szKeyName, "angle" )*/ ) + { + Vector angles; + AngleVectors(pTarget->GetAbsAngles(), &angles); + variant->SetVector3D(angles); + return true; + } + + if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" )) + { + // Copy it over since we're not going to use the alpha + color32 theircolor = pTarget->GetRenderColor(); + color32 color; + color.r = theircolor.r; + color.g = theircolor.g; + color.b = theircolor.b; + variant->SetColor32(color); + return true; + } + + if ( FStrEq( szKeyName, "renderamt" ) ) + { + char szAlpha = pTarget->GetRenderColor().a; + variant->SetString(MAKE_STRING(&szAlpha)); + return true; + } + + if ( FStrEq( szKeyName, "disableshadows" )) + { + variant->SetBool((pTarget->GetEffects() & EF_NOSHADOW) != NULL); + return true; + } + + if ( FStrEq( szKeyName, "mins" )) + { + variant->SetVector3D(pTarget->CollisionProp()->OBBMinsPreScaled()); + return true; + } + + if ( FStrEq( szKeyName, "maxs" )) + { + variant->SetVector3D(pTarget->CollisionProp()->OBBMaxsPreScaled()); + return true; + } + + if ( FStrEq( szKeyName, "disablereceiveshadows" )) + { + variant->SetBool((pTarget->GetEffects() & EF_NORECEIVESHADOW) != NULL); + return true; + } + + if ( FStrEq( szKeyName, "nodamageforces" )) + { + variant->SetBool((pTarget->GetEFlags() & EFL_NO_DAMAGE_FORCES) != NULL); + return true; + } +#endif + + return false; +} diff --git a/mp/src/game/server/mapbase/datadesc_mod.h b/mp/src/game/server/mapbase/datadesc_mod.h new file mode 100644 index 00000000..e724259b --- /dev/null +++ b/mp/src/game/server/mapbase/datadesc_mod.h @@ -0,0 +1,11 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" + +char *Datadesc_SetFieldString( const char *szValue, CBaseEntity *pObject, typedescription_t *pField, fieldtype_t *pFieldType = NULL ); + +bool ReadUnregisteredKeyfields( CBaseEntity *pTarget, const char *szKeyName, variant_t *variant ); diff --git a/mp/src/game/server/mapbase/expandedrs_combine.h b/mp/src/game/server/mapbase/expandedrs_combine.h new file mode 100644 index 00000000..01206d2f --- /dev/null +++ b/mp/src/game/server/mapbase/expandedrs_combine.h @@ -0,0 +1,173 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Response system properties (concepts, etc.) shared by npc_combine_s and npc_metropolice +// +//=============================================================================// + +#ifndef EXPANDEDRS_COMBINE_H +#define EXPANDEDRS_COMBINE_H + +#ifdef _WIN32 +#pragma once +#endif + +#if 0 +#include "ai_component.h" +#include "ai_basenpc.h" +#include "ai_sentence.h" +#endif + +// +// Concepts +// +// These should be consistent with player allies and each other. +// + +// +// Combine Soldiers +// +#define TLK_CMB_ANNOUNCE "TLK_ANNOUNCE" +#define TLK_CMB_THROWGRENADE "TLK_THROWGRENADE" +#define TLK_CMB_PLAYERHIT "TLK_PLAYERHIT" +#define TLK_CMB_ASSAULT "TLK_ASSAULT" +#define TLK_CMB_ENEMY "TLK_STARTCOMBAT" +#define TLK_CMB_KILLENEMY "TLK_ENEMY_DEAD" +#define TLK_CMB_DANGER "TLK_DANGER" +#define TLK_CMB_KICK "TLK_KICK" +#define TLK_CMB_FLANK "TLK_FLANK" +#define TLK_CMB_PAIN "TLK_WOUND" +#define TLK_CMB_LOSTENEMY "TLK_LOSTENEMY" +#define TLK_CMB_REFINDENEMY "TLK_REFINDENEMY" +#define TLK_CMB_GOALERT "TLK_GOALERT" +//#define TLK_CMB_LASTOFSQUAD "TLK_LASTOFSQUAD" +#define TLK_CMB_MANDOWN "TLK_ALLY_KILLED" +#define TLK_CMB_DIE "TLK_DEATH" +#define TLK_CMB_QUESTION "TLK_QUESTION" +#define TLK_CMB_ANSWER "TLK_ANSWER" + +// +// Metrocops +// +#define TLK_COP_MANHACKKILLED "TLK_ALLY_KILLED" +#define TLK_COP_MANDOWN "TLK_ALLY_KILLED" +#define TLK_COP_GO_ALERT "TLK_GOALERT" +#define TLK_COP_FREEZE "TLK_FREEZE" +#define TLK_COP_OVER_HERE "TLK_OVER_HERE" +#define TLK_COP_HES_RUNNING "TLK_HES_RUNNING" +#define TLK_COP_TAKE_HIM_DOWN "TLK_TAKE_HIM_DOWN" +#define TLK_COP_ARREST_IN_POS "TLK_ARREST_IN_POS" +#define TLK_COP_DEPLOY_MANHACK "TLK_DEPLOY_MANHACK" +#define TLK_COP_PLAYERHIT "TLK_PLAYERHIT" +#define TLK_COP_FLANK "TLK_FLANK" +#define TLK_COP_HEARD_SOMETHING "TLK_DARKNESS_HEARDSOUND" +#define TLK_COP_ENEMY "TLK_STARTCOMBAT" +#define TLK_COP_KILLENEMY "TLK_ENEMY_DEAD" +#define TLK_COP_NOAMMO "TLK_NOAMMO" +#define TLK_COP_LOWAMMO "TLK_LOWAMMO" +#define TLK_COP_DANGER "TLK_DANGER" +#define TLK_COP_DIE "TLK_DEATH" +#define TLK_COP_LOSTENEMY "TLK_LOSTENEMY" +#define TLK_COP_REFINDENEMY "TLK_REFINDENEMY" +#define TLK_COP_HARASS "TLK_STARE" +#define TLK_COP_IDLE "TLK_IDLE" +#define TLK_COP_QUESTION "TLK_QUESTION" +#define TLK_COP_ANSWER "TLK_ANSWER" +#define TLK_COP_PAIN "TLK_WOUND" +#define TLK_COP_COVER_HEAVY_DAMAGE "TLK_HEAVYDAMAGE" +#define TLK_COP_SHOOTCOVER "TLK_SHOOTCOVER" +#define TLK_COP_BACK_UP "TLK_BACK_UP" +#define TLK_COP_ON_FIRE "TLK_WOUND" +#define TLK_COP_HIT_BY_PHYSOBJ "TLK_PLYR_PHYSATK" +#define TLK_COP_THROWGRENADE "TLK_THROWGRENADE" +#define TLK_COP_ACTIVATE_BATON "TLK_ACTIVATE_BATON" +#define TLK_COP_DEACTIVATE_BATON "TLK_DEACTIVATE_BATON" + +#define TLK_COP_MOVE_ALONG "TLK_MOVE_ALONG" + +#define TLK_COP_FT_APPROACH "TLK_FT_APPROACH" +#define TLK_COP_FT_MOUNT "TLK_FT_MOUNT" +#define TLK_COP_FT_SCAN "TLK_FT_SCAN" +#define TLK_COP_FT_DISMOUNT "TLK_FT_DISMOUNT" +#define TLK_COP_SO_BEGIN "TLK_SO_BEGIN" +#define TLK_COP_SO_END "TLK_SO_END" +#define TLK_COP_SO_FORCE_COVER "TLK_SO_FORCE_COVER" +#define TLK_COP_SO_PEEK "TLK_SO_PEEK" +#define TLK_COP_AS_HIT_RALLY "TLK_AS_HIT_RALLY" +#define TLK_COP_AS_HIT_ASSAULT "TLK_AS_HIT_ASSAULT" +#define TLK_COP_AS_ADV_RALLY "TLK_AS_ADV_RALLY" +#define TLK_COP_AS_ADV_ASSAULT "TLK_AS_ADV_ASSAULT" + +// +// Snipers +// +#define TLK_SNIPER_DIE "TLK_DEATH" +#define TLK_SNIPER_TARGETDESTROYED "TLK_ENEMY_DEAD" +#define TLK_SNIPER_DANGER "TLK_DANGER" + + +#if 0 +static void FixupSentence(const char **ppSentence, const char **ppPrefix); + +//----------------------------------------------------------------------------- +// This is the met of the class +//----------------------------------------------------------------------------- +template< class NPC_CLASS > +class CAI_SentenceTalker : public CAI_Component +{ + DECLARE_CLASS_NOBASE( CAI_SentenceTalker ); + DECLARE_SIMPLE_DATADESC(); + +public: + //CAI_SentenceTalker(); + + void Init( NPC_CLASS *pOuter, const char *pGameSound = NULL ); + + // Check for queued-up-sentences + speak them + //void UpdateSentenceQueue(); + + // Returns the sentence index played, which can be used to determine + // the sentence length of time using engine->SentenceLength + int Speak( const char *pSentence, SentencePriority_t nSoundPriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t nCriteria = SENTENCE_CRITERIA_IN_SQUAD ); + + // Returns the sentence index played, which can be used to determine + // the sentence length of time using engine->SentenceLength. If the sentence + // was queued, then -1 is returned, which is the same result as if the sound wasn't played + //int SpeakQueued( const char *pSentence, SentencePriority_t nSoundPriority = SENTENCE_PRIORITY_NORMAL, SentenceCriteria_t nCriteria = SENTENCE_CRITERIA_IN_SQUAD ); + + // Clears the sentence queue + //void ClearQueue(); + +protected: + virtual float GetVolume() = 0; + virtual soundlevel_t GetSoundLevel() = 0; + +private: + // Speech criteria + bool ShouldSpeak( SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria ); + bool MatchesCriteria( SentenceCriteria_t nCriteria ); + + // Play the actual sentence + //int PlaySentence( const char *pSentence ); + + // Debug output + void SentenceMsg( const char *pStatus, const char *pSentence ); + + int m_voicePitch; + int m_nQueuedSentenceIndex; + float m_flQueueTimeout; + int m_nQueueSoundPriority; + + SentencePriority_t m_LastPriority; + +public: + string_t m_iRemovePrefix; +}; + +template< class NPC_CLASS > +void CAI_SentenceTalker< NPC_CLASS >::Init( NPC_CLASS *pOuter, const char *pGameSound ) +{ + SetOuter( pOuter ); +} +#endif + +#endif \ No newline at end of file diff --git a/mp/src/game/server/mapbase/func_clientclip.cpp b/mp/src/game/server/mapbase/func_clientclip.cpp new file mode 100644 index 00000000..1bfbf1d7 --- /dev/null +++ b/mp/src/game/server/mapbase/func_clientclip.cpp @@ -0,0 +1,191 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: A special brush that collides with clientside entities, primarily ragdolls. +// +//=============================================================================// + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Purpose: basic solid geometry +// enabled state: brush is visible +// disabled staute: brush not visible +//----------------------------------------------------------------------------- +class CFuncClientClip : public CBaseEntity +{ +public: + DECLARE_CLASS( CFuncClientClip, CBaseEntity ); + DECLARE_SERVERCLASS(); + + virtual void Spawn( void ); + bool CreateVPhysics( void ); + + virtual int DrawDebugTextOverlays( void ); + + void TurnOff( void ); + void TurnOn( void ); + + // Input handlers + void InputTurnOff( inputdata_t &inputdata ); + void InputTurnOn( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + CNetworkVar( bool, m_bDisabled ); + + DECLARE_DATADESC(); + + virtual bool IsOn( void ); + + int UpdateTransmitState() // always send to all clients + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } +}; + +LINK_ENTITY_TO_CLASS( func_clip_client, CFuncClientClip ); + +BEGIN_DATADESC( CFuncClientClip ) + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CFuncClientClip, DT_FuncClientClip ) + SendPropBool( SENDINFO( m_bDisabled ) ), +END_SEND_TABLE() + + +void CFuncClientClip::Spawn( void ) +{ + SetMoveType( MOVETYPE_PUSH ); // so it doesn't get pushed by anything + + SetSolid( GetParent() ? SOLID_VPHYSICS : SOLID_BSP ); + AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); + + AddSolidFlags( FSOLID_NOT_SOLID ); + + SetModel( STRING( GetModelName() ) ); + + if ( m_bDisabled ) + TurnOff(); + + // If it can't move/go away, it's really part of the world + if ( !GetEntityName() || !m_iParent ) + AddFlag( FL_WORLDBRUSH ); + + CreateVPhysics(); +} + +//----------------------------------------------------------------------------- + +bool CFuncClientClip::CreateVPhysics( void ) +{ + // NOTE: Don't init this static. It's pretty common for these to be constrained + // and dynamically parented. Initing shadow avoids having to destroy the physics + // object later and lose the constraints. + IPhysicsObject *pPhys = VPhysicsInitShadow(false, false); + if ( pPhys ) + { + int contents = modelinfo->GetModelContents( GetModelIndex() ); + if ( ! (contents & (MASK_SOLID|MASK_PLAYERSOLID|MASK_NPCSOLID)) ) + { + // leave the physics shadow there in case it has crap constrained to it + // but disable collisions with it + pPhys->EnableCollisions( false ); + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CFuncClientClip::DrawDebugTextOverlays( void ) +{ + int nOffset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + Q_snprintf( tempstr,sizeof(tempstr), "angles: %g %g %g", (double)GetLocalAngles()[PITCH], (double)GetLocalAngles()[YAW], (double)GetLocalAngles()[ROLL] ); + EntityText( nOffset, tempstr, 0 ); + nOffset++; + + Q_snprintf(tempstr, sizeof(tempstr), " enabled: %d", !m_bDisabled); + EntityText(nOffset, tempstr, 0); + nOffset++; + } + + return nOffset; +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for toggling the hidden/shown state of the brush. +//----------------------------------------------------------------------------- +void CFuncClientClip::InputToggle( inputdata_t &inputdata ) +{ + if ( IsOn() ) + { + TurnOff(); + return; + } + + TurnOn(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for hiding the brush. +//----------------------------------------------------------------------------- +void CFuncClientClip::InputTurnOff( inputdata_t &inputdata ) +{ + TurnOff(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for showing the brush. +//----------------------------------------------------------------------------- +void CFuncClientClip::InputTurnOn( inputdata_t &inputdata ) +{ + TurnOn(); +} + +//----------------------------------------------------------------------------- +// Purpose: Hides the brush. +//----------------------------------------------------------------------------- +void CFuncClientClip::TurnOff( void ) +{ + if ( !IsOn() ) + return; + + AddEffects( EF_NODRAW ); + m_bDisabled = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Shows the brush. +//----------------------------------------------------------------------------- +void CFuncClientClip::TurnOn( void ) +{ + if ( IsOn() ) + return; + + RemoveEffects( EF_NODRAW ); + m_bDisabled = false; +} + + +inline bool CFuncClientClip::IsOn( void ) +{ + return !m_bDisabled; +} diff --git a/mp/src/game/server/mapbase/func_fake_worldportal.cpp b/mp/src/game/server/mapbase/func_fake_worldportal.cpp new file mode 100644 index 00000000..3184d2fa --- /dev/null +++ b/mp/src/game/server/mapbase/func_fake_worldportal.cpp @@ -0,0 +1,100 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// (basically a combination of point_camera and func_reflective_glass) +// +//===========================================================================// + +#include "cbase.h" +#include "modelentities.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class CFuncFakeWorldPortal : public CFuncBrush +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CFuncFakeWorldPortal, CFuncBrush ); + DECLARE_SERVERCLASS(); + + CFuncFakeWorldPortal() + { + // Equivalent to SKYBOX_2DSKYBOX_VISIBLE, the original sky setting + m_iSkyMode = 2; + } + +public: + + virtual void Spawn( void ) + { + BaseClass::Spawn(); + + if (m_target != NULL_STRING) + { + m_hTargetPlane = gEntList.FindEntityByName( NULL, m_target, this ); + if (!m_hTargetPlane) + Warning("%s: Invalid target plane \"%s\"!\n", GetDebugName(), STRING(m_target)); + } + else + { + Warning("%s: No target plane!\n", GetDebugName()); + } + + if (m_iszFogController != NULL_STRING) + { + m_hFogController = gEntList.FindEntityByName( NULL, m_iszFogController, this ); + if (!m_hFogController) + Warning("%s: Invalid fog controller \"%s\"!\n", GetDebugName(), STRING(m_iszFogController)); + } + } + + // Input handlers + void InputSetTargetPlane( inputdata_t &inputdata ) { m_hTargetPlane = inputdata.value.Entity(); if (m_hTargetPlane) { m_target = m_hTargetPlane->GetEntityName(); } } + void InputSetTargetPlaneAngle( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_PlaneAngles.Init(vec.x, vec.y, vec.z); } + void InputSetSkyMode( inputdata_t &inputdata ) { m_iSkyMode = inputdata.value.Int(); } + void InputSetRenderTarget( inputdata_t &inputdata ) { m_iszRenderTarget = inputdata.value.StringID(); } + void InputSetFogController( inputdata_t &inputdata ) { m_hFogController = inputdata.value.Entity(); if (m_hFogController) { m_iszFogController = m_hFogController->GetEntityName(); } } + +private: + + CNetworkHandle( CBaseEntity, m_hTargetPlane ); + CNetworkQAngle( m_PlaneAngles ); + CNetworkVar( int, m_iSkyMode ); + CNetworkVar( float, m_flScale ); + CNetworkVar( string_t, m_iszRenderTarget ); + + CNetworkHandle( CBaseEntity, m_hFogController ); + string_t m_iszFogController; +}; + +// automatically hooks in the system's callbacks +BEGIN_DATADESC( CFuncFakeWorldPortal ) + + DEFINE_FIELD( m_hTargetPlane, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_PlaneAngles, FIELD_VECTOR, "PlaneAngles" ), + DEFINE_KEYFIELD( m_iSkyMode, FIELD_INTEGER, "SkyMode" ), + DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "scale" ), + DEFINE_KEYFIELD( m_iszRenderTarget, FIELD_STRING, "RenderTarget" ), + DEFINE_FIELD( m_hFogController, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iszFogController, FIELD_STRING, "FogController" ), + + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetTargetPlane", InputSetTargetPlane ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetTargetPlaneAngle", InputSetTargetPlaneAngle ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSkyMode", InputSetSkyMode ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRenderTarget", InputSetRenderTarget ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetFogController", InputSetFogController ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( func_fake_worldportal, CFuncFakeWorldPortal ); + +IMPLEMENT_SERVERCLASS_ST( CFuncFakeWorldPortal, DT_FuncFakeWorldPortal ) + + SendPropEHandle( SENDINFO( m_hTargetPlane ) ), + SendPropVector( SENDINFO( m_PlaneAngles ), -1, SPROP_COORD ), + SendPropInt( SENDINFO( m_iSkyMode ) ), + SendPropFloat( SENDINFO( m_flScale ) ), + SendPropStringT( SENDINFO( m_iszRenderTarget ) ), + SendPropEHandle( SENDINFO( m_hFogController ) ), + +END_SEND_TABLE() diff --git a/mp/src/game/server/mapbase/logic_eventlistener.cpp b/mp/src/game/server/mapbase/logic_eventlistener.cpp new file mode 100644 index 00000000..903703f3 --- /dev/null +++ b/mp/src/game/server/mapbase/logic_eventlistener.cpp @@ -0,0 +1,270 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ==== +// +// Purpose: Source SDK-based replication of logic_eventlistener from later versions +// of Source. +// +// This is based entirely on Source 2013 code and Portal 2's FGD entry. +// It does not actually use code from Portal 2 or later. +// +//============================================================================= + +#include "cbase.h" +#include "GameEventListener.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class CLogicEventListener : public CPointEntity, + public CGameEventListener +{ +public: + DECLARE_CLASS( CLogicEventListener, CPointEntity ); + + CLogicEventListener(); + + void Activate(); + virtual void ListenForEvents(); + + bool KeyValue(const char *szKeyName, const char *szValue); + + virtual void FireGameEvent( IGameEvent *event ); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + + string_t m_iszEventName; + + // Outputs + COutputEvent m_OnEventFired; + +protected: + + bool m_bDisabled; +}; + +LINK_ENTITY_TO_CLASS(logic_eventlistener, CLogicEventListener); + + +BEGIN_DATADESC( CLogicEventListener ) + + DEFINE_KEYFIELD(m_iszEventName, FIELD_STRING, "EventName"), + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), + DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + + // Outputs + DEFINE_OUTPUT(m_OnEventFired, "OnEventFired"), + +END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CLogicEventListener::CLogicEventListener(void) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicEventListener::Activate() +{ + BaseClass::Activate(); + ListenForEvents(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicEventListener::ListenForEvents() +{ + ListenForGameEvent(STRING(m_iszEventName)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CLogicEventListener::KeyValue(const char *szKeyName, const char *szValue) +{ + if (FStrEq(szKeyName, "IsEnabled")) + { + m_bDisabled = !FStrEq(szValue, "0"); + } + else + return BaseClass::KeyValue(szKeyName, szValue); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: called when a game event is fired +//----------------------------------------------------------------------------- +void CLogicEventListener::FireGameEvent( IGameEvent *event ) +{ + if (m_bDisabled) + return; + + m_OnEventFired.FireOutput(this, this); +} + +//------------------------------------------------------------------------------ +// Purpose: Turns on the entity, allowing it to fire outputs. +//------------------------------------------------------------------------------ +void CLogicEventListener::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//------------------------------------------------------------------------------ +// Purpose: Turns off the entity, preventing it from firing outputs. +//------------------------------------------------------------------------------ +void CLogicEventListener::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +//------------------------------------------------------------------------------ +// Purpose: Toggles the enabled/disabled state of the entity. +//------------------------------------------------------------------------------ +void CLogicEventListener::InputToggle( inputdata_t &inputdata ) +{ + m_bDisabled = !m_bDisabled; +} + + +#define POINT_EVENT_NUM_VALUES 8 + +class CPointEvent : public CLogicEventListener +{ +public: + DECLARE_CLASS( CPointEvent, CLogicEventListener ); + + CPointEvent(); + + string_t m_KeyNames[POINT_EVENT_NUM_VALUES]; + + void ListenForEvents(); + + void FireGameEvent( IGameEvent *event ); + + // Input handlers + void InputSetAllEvents( inputdata_t &inputdata ); + void InputAddEvent( inputdata_t &inputdata ); + //void InputRemoveEvent( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + + // Outputs + COutputString m_OutEventName; + COutputString m_OutValue[POINT_EVENT_NUM_VALUES]; +}; + +LINK_ENTITY_TO_CLASS(point_event, CPointEvent); + + +BEGIN_DATADESC( CPointEvent ) + + DEFINE_KEYFIELD(m_KeyNames[0], FIELD_STRING, "KeyName01"), + DEFINE_KEYFIELD(m_KeyNames[1], FIELD_STRING, "KeyName02"), + DEFINE_KEYFIELD(m_KeyNames[2], FIELD_STRING, "KeyName03"), + DEFINE_KEYFIELD(m_KeyNames[3], FIELD_STRING, "KeyName04"), + DEFINE_KEYFIELD(m_KeyNames[4], FIELD_STRING, "KeyName05"), + DEFINE_KEYFIELD(m_KeyNames[5], FIELD_STRING, "KeyName06"), + DEFINE_KEYFIELD(m_KeyNames[6], FIELD_STRING, "KeyName07"), + DEFINE_KEYFIELD(m_KeyNames[7], FIELD_STRING, "KeyName08"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_STRING, "SetAllEvents", InputSetAllEvents), + DEFINE_INPUTFUNC(FIELD_STRING, "AddEvent", InputAddEvent), + //DEFINE_INPUTFUNC(FIELD_STRING, "RemoveEvent", InputRemoveEvent), + + // Outputs + DEFINE_OUTPUT(m_OutEventName, "OutEventName"), + DEFINE_OUTPUT(m_OutValue[0], "OutValue01"), + DEFINE_OUTPUT(m_OutValue[1], "OutValue02"), + DEFINE_OUTPUT(m_OutValue[2], "OutValue03"), + DEFINE_OUTPUT(m_OutValue[3], "OutValue04"), + DEFINE_OUTPUT(m_OutValue[4], "OutValue05"), + DEFINE_OUTPUT(m_OutValue[5], "OutValue06"), + DEFINE_OUTPUT(m_OutValue[6], "OutValue07"), + DEFINE_OUTPUT(m_OutValue[7], "OutValue08"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CPointEvent::CPointEvent(void) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointEvent::ListenForEvents() +{ + // Could easily do this with strtok... + // Oh well. I don't know the performance difference. + CUtlStringList vecEvents; + Q_SplitString(STRING(m_iszEventName), ":", vecEvents); + FOR_EACH_VEC(vecEvents, i) + { + ListenForGameEvent(vecEvents[i]); + } +} + +//----------------------------------------------------------------------------- +// Purpose: called when a game event is fired +//----------------------------------------------------------------------------- +void CPointEvent::FireGameEvent( IGameEvent *event ) +{ + if (m_bDisabled) + return; + + BaseClass::FireGameEvent(event); + m_OutEventName.Set(AllocPooledString(event->GetName()), this, this); + + for (int i = 0; i < POINT_EVENT_NUM_VALUES; i++) + { + const char *szValue = event->GetString(STRING(m_KeyNames[i]), NULL); + if (szValue != NULL) + { + m_OutValue[i].Set(AllocPooledString(szValue), this, this); + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPointEvent::InputSetAllEvents( inputdata_t &inputdata ) +{ + StopListeningForAllEvents(); + + if (inputdata.value.StringID() != NULL_STRING) + { + CUtlStringList vecEvents; + Q_SplitString(inputdata.value.String(), ":", vecEvents); + FOR_EACH_VEC(vecEvents, i) + { + ListenForGameEvent(vecEvents[i]); + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPointEvent::InputAddEvent( inputdata_t &inputdata ) +{ + ListenForGameEvent(inputdata.value.String()); +} diff --git a/mp/src/game/server/mapbase/logic_externaldata.cpp b/mp/src/game/server/mapbase/logic_externaldata.cpp new file mode 100644 index 00000000..b28189f7 --- /dev/null +++ b/mp/src/game/server/mapbase/logic_externaldata.cpp @@ -0,0 +1,363 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "filesystem.h" +#include "KeyValues.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLogicExternalData : public CLogicalEntity +{ + DECLARE_CLASS( CLogicExternalData, CLogicalEntity ); + DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif + +public: + ~CLogicExternalData(); + + void LoadFile(); + void SaveFile(); + void SetBlock(string_t iszNewTarget, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL); + + void Activate(); + + // Inputs + void InputWriteKeyValue( inputdata_t &inputdata ); + void InputRemoveKeyValue( inputdata_t &inputdata ); + void InputReadKey( inputdata_t &inputdata ); + void InputSetBlock( inputdata_t &inputdata ); + void InputSave( inputdata_t &inputdata ); + void InputReload( inputdata_t &inputdata ); + +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetKeyValues( void ); + HSCRIPT ScriptGetKeyValueBlock( void ); + + void ScriptSetKeyValues( HSCRIPT hKV ); + void ScriptSetKeyValueBlock( HSCRIPT hKV ); + + void ScriptSetBlock( const char *szNewBlock, HSCRIPT hActivator = NULL, HSCRIPT hCaller = NULL ); +#endif + + char m_iszFile[MAX_PATH]; + + // Root file + KeyValues *m_pRoot; + + // Our specific block + KeyValues *m_pBlock; + //string_t m_iszBlock; // Use m_target + + bool m_bSaveEachChange; + bool m_bReloadBeforeEachAction; + string_t m_iszMapname; + + COutputString m_OutValue; +}; + +LINK_ENTITY_TO_CLASS(logic_externaldata, CLogicExternalData); + +BEGIN_DATADESC( CLogicExternalData ) + + // Keys + //DEFINE_KEYFIELD( m_iszBlock, FIELD_STRING, "Block" ), + DEFINE_KEYFIELD( m_bSaveEachChange, FIELD_BOOLEAN, "SaveEachChange" ), + DEFINE_KEYFIELD( m_bReloadBeforeEachAction, FIELD_BOOLEAN, "ReloadBeforeEachAction" ), + DEFINE_KEYFIELD( m_iszMapname, FIELD_STRING, "Mapname" ), + + // This should be cached each load + //DEFINE_ARRAY( m_iszFile, FIELD_CHARACTER, MAX_PATH ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "WriteKeyValue", InputWriteKeyValue ), + DEFINE_INPUTFUNC( FIELD_STRING, "RemoveKeyValue", InputRemoveKeyValue ), + DEFINE_INPUTFUNC( FIELD_STRING, "ReadKey", InputReadKey ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetBlock", InputSetBlock ), + DEFINE_INPUTFUNC( FIELD_VOID, "Save", InputSave ), + DEFINE_INPUTFUNC( FIELD_VOID, "Reload", InputReload ), + + // Outputs + DEFINE_OUTPUT(m_OutValue, "OutValue"), + +END_DATADESC() + +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CLogicExternalData, CBaseEntity, "An entity which loads keyvalues from an external data file." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValues, "GetKeyValues", "Gets the external data expressed in CScriptKeyValues." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBlock, "GetKeyValueBlock", "Gets the current external data block expressed in CScriptKeyValues." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValues, "SetKeyValues", "Sets the external data from a CScriptKeyValues object." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueBlock, "SetKeyValues", "Sets the current external data block from a CScriptKeyValues object." ) + + DEFINE_SCRIPTFUNC( LoadFile, "Loads external data from the external file." ) + DEFINE_SCRIPTFUNC( SaveFile, "Saves the external data to the external file." ) + +END_SCRIPTDESC(); +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CLogicExternalData::~CLogicExternalData() +{ + if (m_pRoot) + m_pRoot->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::LoadFile() +{ + if (m_pRoot) + m_pRoot->deleteThis(); + + m_pRoot = new KeyValues( m_iszFile ); + m_pRoot->LoadFromFile(g_pFullFileSystem, m_iszFile, "MOD"); + + // This shold work even if the file didn't load. + if (m_target != NULL_STRING) + { + m_pBlock = m_pRoot->FindKey(STRING(m_target), true); + } + else + { + // Just do things from root + m_pBlock = m_pRoot; + } + + if (!m_pBlock) + { + Warning("WARNING! %s has NULL m_pBlock! Removing...\n", GetDebugName()); + UTIL_Remove(this); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::SaveFile() +{ + DevMsg("Saving to %s...\n", m_iszFile); + m_pRoot->SaveToFile(g_pFullFileSystem, m_iszFile, "MOD", false, true); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::SetBlock(string_t iszNewTarget, CBaseEntity *pActivator, CBaseEntity *pCaller) +{ + if (STRING(iszNewTarget)[0] == '!') + { + if (FStrEq(STRING(iszNewTarget), "!self")) + iszNewTarget = GetEntityName(); + else if (pActivator && FStrEq(STRING(iszNewTarget), "!activator")) + iszNewTarget = pActivator->GetEntityName(); + else if (pCaller && FStrEq(STRING(iszNewTarget), "!caller")) + iszNewTarget = pCaller->GetEntityName(); + } + + m_target = iszNewTarget; + LoadFile(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::Activate() +{ + BaseClass::Activate(); + + if (m_iszMapname == NULL_STRING || STRING(m_iszMapname)[0] == '\0') + m_iszMapname = gpGlobals->mapname; + + Q_snprintf(m_iszFile, sizeof(m_iszFile), "maps/%s_externaldata.txt", STRING(m_iszMapname)); + DevMsg("LOGIC_EXTERNALDATA: %s\n", m_iszFile); + + // This handles !self, etc. even though the end result could just be assigning to itself. + // Also calls LoadFile() for initial load. + SetBlock(m_target); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::InputWriteKeyValue( inputdata_t &inputdata ) +{ + const char *szValue = inputdata.value.String(); + char key[256]; + char value[256]; + + // Separate key from value + char *delimiter = Q_strstr(szValue, " "); + if (delimiter && (delimiter + 1) != '\0') + { + Q_strncpy(key, szValue, MIN((delimiter - szValue) + 1, sizeof(key))); + Q_strncpy(value, delimiter + 1, sizeof(value)); + } + else + { + // Assume the value is just supposed to be null + Q_strncpy(key, szValue, sizeof(key)); + } + + if (m_bReloadBeforeEachAction) + LoadFile(); + + m_pBlock->SetString(key, value); + + if (m_bSaveEachChange) + SaveFile(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::InputRemoveKeyValue( inputdata_t &inputdata ) +{ + if (m_bReloadBeforeEachAction) + LoadFile(); + + KeyValues *pKV = m_pBlock->FindKey(inputdata.value.String()); + if (pKV) + { + m_pBlock->RemoveSubKey(pKV); + + if (m_bSaveEachChange) + SaveFile(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::InputReadKey( inputdata_t &inputdata ) +{ + if (m_bReloadBeforeEachAction) + LoadFile(); + + m_OutValue.Set(AllocPooledString(m_pBlock->GetString(inputdata.value.String())), inputdata.pActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::InputSetBlock( inputdata_t &inputdata ) +{ + SetBlock(inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::InputSave( inputdata_t &inputdata ) +{ + SaveFile(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicExternalData::InputReload( inputdata_t &inputdata ) +{ + LoadFile(); +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CLogicExternalData::ScriptGetKeyValues( void ) +{ + if (m_bReloadBeforeEachAction) + LoadFile(); + + HSCRIPT hScript = NULL; + if (m_pRoot) + { + // Does this need to be destructed or freed? m_pScriptModelKeyValues apparently doesn't. + hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, m_pRoot, false ); + } + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CLogicExternalData::ScriptGetKeyValueBlock( void ) +{ + if (m_bReloadBeforeEachAction) + LoadFile(); + + HSCRIPT hScript = NULL; + if (m_pBlock) + { + // Does this need to be destructed or freed? m_pScriptModelKeyValues apparently doesn't. + hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, m_pBlock, false ); + } + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CLogicExternalData::ScriptSetKeyValues( HSCRIPT hKV ) +{ + if (m_pRoot) + { + m_pRoot->deleteThis(); + m_pRoot = NULL; + } + + KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV ); + if (pKV) + { + m_pRoot = pKV; + } +} + +void CLogicExternalData::ScriptSetKeyValueBlock( HSCRIPT hKV ) +{ + if (m_pBlock) + { + m_pBlock->deleteThis(); + m_pBlock = NULL; + } + + KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV ); + if (pKV) + { + m_pBlock = pKV; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicExternalData::ScriptSetBlock( const char *szNewBlock, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + CBaseEntity *pActivator = ToEnt( hActivator ); + CBaseEntity *pCaller = ToEnt( hCaller ); + string_t iszNewTarget = AllocPooledString(szNewBlock); + if (STRING(iszNewTarget)[0] == '!') + { + if (FStrEq(STRING(iszNewTarget), "!self")) + iszNewTarget = GetEntityName(); + else if (pActivator && FStrEq(STRING(iszNewTarget), "!activator")) + iszNewTarget = pActivator->GetEntityName(); + else if (pCaller && FStrEq(STRING(iszNewTarget), "!caller")) + iszNewTarget = pCaller->GetEntityName(); + } + + m_target = iszNewTarget; + LoadFile(); +} +#endif diff --git a/mp/src/game/server/mapbase/logic_register_activator.cpp b/mp/src/game/server/mapbase/logic_register_activator.cpp new file mode 100644 index 00000000..79a61dab --- /dev/null +++ b/mp/src/game/server/mapbase/logic_register_activator.cpp @@ -0,0 +1,145 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ==== +// +// Purpose: Source SDK-based replication of logic_register_activator from later versions +// of Source. +// +// This is based entirely on Source 2013 code and Portal 2's FGD entry. +// It does not actually use code from Portal 2 or later. +// +//============================================================================= + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class CLogicRegisterActivator : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicRegisterActivator, CLogicalEntity ); + + CLogicRegisterActivator(); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + void InputFireRegisteredAsActivator1( inputdata_t &inputdata ); + void InputFireRegisteredAsActivator2( inputdata_t &inputdata ); + void InputFireRegisteredAsActivator3( inputdata_t &inputdata ); + void InputFireRegisteredAsActivator4( inputdata_t &inputdata ); + void InputRegisterEntity( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + + // Outputs + COutputEvent m_OnRegisteredActivate[ 4 ]; + + EHANDLE m_hActivator; + +private: + + bool m_bDisabled; +}; + +LINK_ENTITY_TO_CLASS(logic_register_activator, CLogicRegisterActivator); + + +BEGIN_DATADESC( CLogicRegisterActivator ) + + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"), + + DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), + DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + DEFINE_INPUTFUNC(FIELD_VOID, "FireRegisteredAsActivator1", InputFireRegisteredAsActivator1), + DEFINE_INPUTFUNC(FIELD_VOID, "FireRegisteredAsActivator2", InputFireRegisteredAsActivator2), + DEFINE_INPUTFUNC(FIELD_VOID, "FireRegisteredAsActivator3", InputFireRegisteredAsActivator3), + DEFINE_INPUTFUNC(FIELD_VOID, "FireRegisteredAsActivator4", InputFireRegisteredAsActivator4), + DEFINE_INPUTFUNC(FIELD_EHANDLE, "RegisterEntity", InputRegisterEntity), + + // Outputs + DEFINE_OUTPUT(m_OnRegisteredActivate[0], "OnRegisteredActivate1"), + DEFINE_OUTPUT(m_OnRegisteredActivate[1], "OnRegisteredActivate2"), + DEFINE_OUTPUT(m_OnRegisteredActivate[2], "OnRegisteredActivate3"), + DEFINE_OUTPUT(m_OnRegisteredActivate[3], "OnRegisteredActivate4"), + +END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CLogicRegisterActivator::CLogicRegisterActivator(void) +{ +} + +//------------------------------------------------------------------------------ +// Purpose: Turns on the entity, allowing it to fire outputs. +//------------------------------------------------------------------------------ +void CLogicRegisterActivator::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//------------------------------------------------------------------------------ +// Purpose: Turns off the entity, preventing it from firing outputs. +//------------------------------------------------------------------------------ +void CLogicRegisterActivator::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +//------------------------------------------------------------------------------ +// Purpose: Toggles the enabled/disabled state of the entity. +//------------------------------------------------------------------------------ +void CLogicRegisterActivator::InputToggle( inputdata_t &inputdata ) +{ + m_bDisabled = !m_bDisabled; +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler that fires its respective OnRegisteredActivate with the stored activator. +//----------------------------------------------------------------------------- +void CLogicRegisterActivator::InputFireRegisteredAsActivator1( inputdata_t &inputdata ) +{ + m_OnRegisteredActivate[0].FireOutput(m_hActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that fires its respective OnRegisteredActivate with the stored activator. +//----------------------------------------------------------------------------- +void CLogicRegisterActivator::InputFireRegisteredAsActivator2( inputdata_t &inputdata ) +{ + m_OnRegisteredActivate[1].FireOutput(m_hActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that fires its respective OnRegisteredActivate with the stored activator. +//----------------------------------------------------------------------------- +void CLogicRegisterActivator::InputFireRegisteredAsActivator3( inputdata_t &inputdata ) +{ + m_OnRegisteredActivate[2].FireOutput(m_hActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that fires its respective OnRegisteredActivate with the stored activator. +//----------------------------------------------------------------------------- +void CLogicRegisterActivator::InputFireRegisteredAsActivator4( inputdata_t &inputdata ) +{ + m_OnRegisteredActivate[3].FireOutput(m_hActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that stores an entity as the activator. +//----------------------------------------------------------------------------- +void CLogicRegisterActivator::InputRegisterEntity( inputdata_t &inputdata ) +{ + m_hActivator = inputdata.value.Entity(); +} diff --git a/mp/src/game/server/mapbase/logic_skill.cpp b/mp/src/game/server/mapbase/logic_skill.cpp new file mode 100644 index 00000000..2d47a7cb --- /dev/null +++ b/mp/src/game/server/mapbase/logic_skill.cpp @@ -0,0 +1,66 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Controls and detects difficulty level changes +// +//============================================================================= + +#include "cbase.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class CLogicSkill : public CLogicalEntity +{ + DECLARE_CLASS( CLogicSkill, CLogicalEntity ); + +private: + // Inputs + void InputTest( inputdata_t &inputdata ); + void InputStartListening( inputdata_t &inputdata ) {m_bListeningForSkillChanges = true;} + void InputStopListening( inputdata_t &inputdata ) {m_bListeningForSkillChanges = false;} + + // Used by gamerules to fire OnSkillChanged. + // Passes the level it changed to as well. + void InputSkillLevelChanged(inputdata_t &inputdata) { m_bListeningForSkillChanges ? m_OnSkillChanged.Set(inputdata.value.Int(), inputdata.pActivator, this) : (void)0; } + + COutputInt m_OnSkillChanged; + COutputEvent m_OnEasy; + COutputEvent m_OnMedium; + COutputEvent m_OnHard; + + bool m_bListeningForSkillChanges; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_skill, CLogicSkill); + +BEGIN_DATADESC( CLogicSkill ) + + DEFINE_KEYFIELD( m_bListeningForSkillChanges, FIELD_BOOLEAN, "ListenForSkillChange" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Test", InputTest ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartListening", InputStartListening ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopListening", InputStopListening ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "SkillLevelChanged", InputSkillLevelChanged ), + + DEFINE_OUTPUT( m_OnSkillChanged, "OnSkillChanged" ), + DEFINE_OUTPUT( m_OnEasy, "OnEasy" ), + DEFINE_OUTPUT( m_OnMedium, "OnNormal" ), + DEFINE_OUTPUT( m_OnHard, "OnHard" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicSkill::InputTest( inputdata_t &inputdata ) +{ + switch (g_pGameRules->GetSkillLevel()) + { + case SKILL_EASY: m_OnEasy.FireOutput(this, this); break; + case SKILL_MEDIUM: m_OnMedium.FireOutput(this, this); break; + case SKILL_HARD: m_OnHard.FireOutput(this, this); break; + } +} diff --git a/mp/src/game/server/mapbase/point_advanced_finder.cpp b/mp/src/game/server/mapbase/point_advanced_finder.cpp new file mode 100644 index 00000000..27f9f194 --- /dev/null +++ b/mp/src/game/server/mapbase/point_advanced_finder.cpp @@ -0,0 +1,393 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: A ballsier version of point_entity_finder. +// Originally called logic_entityfinder because a lot of this was written +// before I knew about point_entity_finder in the first place. +// +//============================================================================= + +#include "cbase.h" +#include "eventqueue.h" +#include "filters.h" +#include "saverestore_utlvector.h" + +// Uses a CUtlVector instead of an array. +#define ENTITYFINDER_UTLVECTOR 1 + +// Delays outputs directly instead of relying on an input. +#define ENTITYFINDER_OUTPUT_DELAY 1 + +#if !ENTITYFINDER_STATIC_ARRAY +#define ENTITYFINDER_MAX_STORED_ENTITIES 64 +#endif + +//----------------------------------------------------------------------------- +// Purpose: Entity finder that uses filters and other criteria to search the level for a specific entity. +//----------------------------------------------------------------------------- +class CPointAdvancedFinder : public CLogicalEntity +{ + DECLARE_CLASS(CPointAdvancedFinder, CLogicalEntity); + +private: + // Inputs + void InputSearch(inputdata_t &inputdata); + void InputSetSearchFilter(inputdata_t &inputdata); + void InputSetSearchPoint(inputdata_t &inputdata); + void InputSetRadius(inputdata_t &inputdata) { m_flRadius = inputdata.value.Float(); } + void InputSetMaxResults(inputdata_t &inputdata) { m_iNumSearches = inputdata.value.Int(); } + void InputSetOutputDelay(inputdata_t &inputdata) { m_flOutputDelay = inputdata.value.Float(); } + void InputSetFiringMethod(inputdata_t &inputdata) { m_iFiringMethod = inputdata.value.Int(); } +#ifndef ENTITYFINDER_OUTPUT_DELAY + void InputFoundEntity(inputdata_t &inputdata); +#endif + + void Spawn(); + + Vector GetSearchOrigin(); + bool SearchForEntities(inputdata_t &inputdata); + void FoundEntity(CBaseEntity *pEntity, inputdata_t &inputdata); + + + string_t m_iszSearchFilter; + CHandle m_hSearchFilter; + + string_t m_iszSearchPoint; + EHANDLE m_hSearchPoint; + + float m_flRadius; + int m_iNumSearches; + int m_iFiringMethod; + enum + { + FIRINGMETHOD_NONE = -1, // -1 for point_entity_finder compatibility + FIRINGMETHOD_NEAREST, + FIRINGMETHOD_FARTHEST, + FIRINGMETHOD_RANDOM, + }; + + float m_flOutputDelay; + float m_flLastOutputDelay = 0.0f; + +#if ENTITYFINDER_UTLVECTOR + CUtlVector m_StoredEntities; +#else + CBaseEntity *m_StoredEntities[ENTITYFINDER_MAX_STORED_ENTITIES]; +#endif + + // Outputs + COutputEHANDLE m_OnFoundEntity; + COutputEvent m_OnSearchFailed; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(point_advanced_finder, CPointAdvancedFinder); + + +BEGIN_DATADESC(CPointAdvancedFinder) + + // Keys + DEFINE_KEYFIELD(m_iszSearchFilter, FIELD_STRING, "SearchFilter"), + DEFINE_FIELD(m_hSearchFilter, FIELD_EHANDLE), + DEFINE_KEYFIELD(m_iszSearchPoint, FIELD_STRING, "SearchPoint"), + DEFINE_FIELD(m_hSearchPoint, FIELD_EHANDLE), + DEFINE_KEYFIELD(m_flRadius, FIELD_FLOAT, "radius"), + DEFINE_KEYFIELD(m_iNumSearches, FIELD_INTEGER, "NumberOfEntities"), + DEFINE_KEYFIELD(m_flOutputDelay, FIELD_FLOAT, "OutputDelay"), + DEFINE_KEYFIELD(m_iFiringMethod, FIELD_INTEGER, "Method"), +#if ENTITYFINDER_UTLVECTOR + DEFINE_UTLVECTOR( m_StoredEntities, FIELD_CLASSPTR ), +#else + DEFINE_ARRAY(m_StoredEntities, FIELD_CLASSPTR, ENTITYFINDER_MAX_STORED_ENTITIES), +#endif + DEFINE_FIELD(m_flLastOutputDelay, FIELD_FLOAT), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "BeginSearch", InputSearch), + DEFINE_INPUTFUNC(FIELD_STRING, "SetSearchFilter", InputSetSearchFilter), + DEFINE_INPUTFUNC(FIELD_STRING, "SetSearchPoint", InputSetSearchPoint), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetRadius", InputSetRadius), + DEFINE_INPUTFUNC(FIELD_INTEGER, "SetMaxResults", InputSetMaxResults), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetOutputDelay", InputSetOutputDelay), + DEFINE_INPUTFUNC(FIELD_INTEGER, "SetFiringMethod", InputSetFiringMethod), +#ifndef ENTITYFINDER_OUTPUT_DELAY + DEFINE_INPUTFUNC(FIELD_EHANDLE, "FoundEntity", InputFoundEntity), +#endif + + // Outputs + DEFINE_OUTPUT(m_OnFoundEntity, "OnFoundEntity"), + DEFINE_OUTPUT(m_OnSearchFailed, "OnSearchFailed"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAdvancedFinder::Spawn() +{ + if (m_iszSearchFilter == NULL_STRING) + { + Warning("%s (%s) has no search filter!\n", GetClassname(), GetDebugName()); + UTIL_Remove(this); + return; + } + + m_hSearchFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iszSearchFilter, this )); + + m_hSearchPoint = gEntList.FindEntityByName( NULL, m_iszSearchPoint, this ); + + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAdvancedFinder::InputSearch(inputdata_t &inputdata) +{ + SearchForEntities(inputdata); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAdvancedFinder::InputSetSearchFilter(inputdata_t &inputdata) +{ + m_iszSearchFilter = inputdata.value.StringID(); + m_hSearchFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iszSearchFilter, this, inputdata.pActivator, inputdata.pCaller )); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAdvancedFinder::InputSetSearchPoint(inputdata_t &inputdata) +{ + m_iszSearchPoint = inputdata.value.StringID(); + m_hSearchPoint = gEntList.FindEntityByName( NULL, m_iszSearchPoint, this, inputdata.pActivator, inputdata.pCaller ); +} + +#ifndef ENTITYFINDER_OUTPUT_DELAY +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAdvancedFinder::InputFoundEntity(inputdata_t &inputdata) +{ + CBaseEntity *pEntity = inputdata.value.Entity(); + if (!pEntity) + return; + + m_OnFoundEntity.Set(pEntity, pEntity, inputdata.pCaller); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline Vector CPointAdvancedFinder::GetSearchOrigin() +{ + if (m_hSearchPoint == NULL) + m_hSearchPoint = this; + + return m_hSearchPoint->GetAbsOrigin(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CPointAdvancedFinder::SearchForEntities(inputdata_t &inputdata) +{ + if ( !m_hSearchFilter ) + return false; + + m_flLastOutputDelay = 0.0f; + + int iNumResults = 0; + + float flRadius = m_flRadius * m_flRadius; + + bool bShouldStoreEntities = (m_iFiringMethod > FIRINGMETHOD_NONE || m_flOutputDelay > 0); +#if !ENTITYFINDER_UTLVECTOR + if (bShouldStoreEntities) + { + if (m_iNumSearches > ENTITYFINDER_MAX_STORED_ENTITIES) + { + Warning("%s (%s) needs to store entities, but we're asked to look for more than the maximum, %i, with %i! Reducing to max...\n", GetClassname(), GetDebugName(), ENTITYFINDER_MAX_STORED_ENTITIES, m_iNumSearches); + m_iNumSearches = ENTITYFINDER_MAX_STORED_ENTITIES; + } + else if (m_iNumSearches == 0) + { + m_iNumSearches = ENTITYFINDER_MAX_STORED_ENTITIES; + } + } + else +#endif + if (m_iNumSearches == 0) + { + m_iNumSearches = MAX_EDICTS; + } + + const CEntInfo *pInfo = gEntList.FirstEntInfo(); + + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; + if ( !ent ) + { + DevWarning( "NULL entity in global entity list!\n" ); + continue; + } + + if (iNumResults >= m_iNumSearches) + break; + + if ( m_hSearchFilter && !m_hSearchFilter->PassesFilter( this, ent ) ) + continue; + + if (flRadius > 0 && (ent->GetAbsOrigin() - GetSearchOrigin()).LengthSqr() > flRadius) + continue; + + if ( bShouldStoreEntities ) + { + // Fire it later +#if ENTITYFINDER_UTLVECTOR + m_StoredEntities.AddToTail(ent); +#else + m_StoredEntities[ iNumResults ] = ent; +#endif + } + else + { + // Fire it now + FoundEntity(ent, inputdata); + } + + iNumResults++; + } + + if (iNumResults > 0) + { + if (bShouldStoreEntities) + { + if (m_iFiringMethod == FIRINGMETHOD_NEAREST || m_iFiringMethod == FIRINGMETHOD_FARTHEST) + { + bool bNotFarthest = m_iFiringMethod != FIRINGMETHOD_FARTHEST; + float flMaxDist = m_flRadius; + float flMinDist = 0; + + if (flMaxDist == 0) + flMaxDist = MAX_TRACE_LENGTH; + + for (int iCur = 0; iCur < iNumResults; iCur++) + { + float flClosest = bNotFarthest ? flMaxDist : 0; + float flDistance = 0; + CBaseEntity *pClosest = NULL; + for (int i = 0; i < iNumResults; i++) + { + if (!m_StoredEntities[i]) + continue; + + flDistance = (m_StoredEntities[i]->GetAbsOrigin() - GetSearchOrigin()).Length(); + if (flDistance < flMaxDist && flDistance > flMinDist) + { + if (bNotFarthest ? (flDistance < flClosest) : (flDistance > flClosest)) + { + pClosest = m_StoredEntities[i]; + flClosest = flDistance; + } + } + } + + if (pClosest) + { + bNotFarthest ? flMinDist = flClosest : flMaxDist = flClosest; + FoundEntity(pClosest, inputdata); + } + else + { + DevWarning("%s (%s): NO CLOSEST!!!\n", GetClassname(), GetDebugName()); + } + } + } + else if (m_iFiringMethod == FIRINGMETHOD_RANDOM) + { + // This could probaly be better... + CUtlVector iResultIndices; + for (int i = 0; i < iNumResults; i++) + iResultIndices.AddToTail(i); + + while (iResultIndices.Count() > 0) + { + int index = iResultIndices[RandomInt(0, iResultIndices.Count() - 1)]; + + if (m_StoredEntities[index]) + { + FoundEntity(m_StoredEntities[index], inputdata); + } + else + { + DevWarning("%s (%s): Found entity is null: %i\n", GetClassname(), GetDebugName(), index); + } + + iResultIndices.FindAndRemove(index); + } + } + else if (m_iFiringMethod == FIRINGMETHOD_NONE) + { + for (int i = 0; i < iNumResults; i++) + { + if (m_StoredEntities[i]) + { + FoundEntity(m_StoredEntities[i], inputdata); + } + else + { + DevWarning("%s (%s): Found entity is null: %i\n", GetClassname(), GetDebugName(), i); + } + } + } + + m_StoredEntities.RemoveAll(); + } + return true; + } + else + { + m_OnSearchFailed.FireOutput(inputdata.pActivator, inputdata.pCaller); + return false; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAdvancedFinder::FoundEntity(CBaseEntity *pEntity, inputdata_t &inputdata) +{ +#ifdef ENTITYFINDER_OUTPUT_DELAY + variant_t variant; + variant.SetEntity(pEntity); + + DevMsg("%s (%s): Firing entity output in %f, added from %f\n", GetClassname(), GetDebugName(), m_flLastOutputDelay, m_flOutputDelay); + m_OnFoundEntity.FireOutput(variant, pEntity, this, m_flLastOutputDelay); +#else + if (m_flOutputDelay == 0) + { + // Just fire it now + m_OnFoundEntity.Set(pEntity, pEntity, inputdata.pCaller); + DevMsg("%s (%s): Delay 0, firing now\n", GetClassname(), GetDebugName()); + return; + } + + //if (m_flLastOutputDelay == 0) + //m_flLastOutputDelay = gpGlobals->curtime; + + variant_t variant; + variant.SetEntity(pEntity); + + DevMsg("%s (%s): Firing entity output in %f, added from %f\n", GetClassname(), GetDebugName(), m_flLastOutputDelay, m_flOutputDelay); + g_EventQueue.AddEvent(this, "FoundEntity", variant, m_flLastOutputDelay, inputdata.pActivator, inputdata.pCaller); +#endif + + m_flLastOutputDelay += m_flOutputDelay; +} diff --git a/mp/src/game/server/mapbase/point_copy_size.cpp b/mp/src/game/server/mapbase/point_copy_size.cpp new file mode 100644 index 00000000..379a6579 --- /dev/null +++ b/mp/src/game/server/mapbase/point_copy_size.cpp @@ -0,0 +1,143 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Copies size. +// +//============================================================================= + +#include "cbase.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPointCopySize : public CLogicalEntity +{ + DECLARE_CLASS( CPointCopySize, CLogicalEntity ); +private: + // The entity to copy the size from. + // m_target is the target that receives it. + string_t m_iszSizeSource; + EHANDLE m_hSizeSource; + EHANDLE m_hTarget; + + float m_flScale; + + void CopySize(CBaseEntity *pSource, CBaseEntity *pTarget); + + // Inputs + void InputCopySize( inputdata_t &inputdata ); + void InputCopySizeToEntity( inputdata_t &inputdata ); + void InputCopySizeFromEntity( inputdata_t &inputdata ); + + void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); m_hTarget = NULL; } + void InputSetSource( inputdata_t &inputdata ) { m_iszSizeSource = inputdata.value.StringID(); m_hSizeSource = NULL; } + + // Outputs + COutputEvent m_OnCopy; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(point_copy_size, CPointCopySize); + + +BEGIN_DATADESC( CPointCopySize ) + + // Keys + DEFINE_KEYFIELD(m_iszSizeSource, FIELD_STRING, "source"), + DEFINE_FIELD(m_hSizeSource, FIELD_EHANDLE), + DEFINE_FIELD(m_hTarget, FIELD_EHANDLE), + + DEFINE_INPUT(m_flScale, FIELD_FLOAT, "SetScale"), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "CopySize", InputCopySize ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "CopySizeToEntity", InputCopySizeToEntity ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "CopySizeFromEntity", InputCopySizeFromEntity ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetSource", InputSetSource ), + + // Outputs + DEFINE_OUTPUT(m_OnCopy, "OnCopy"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCopySize::CopySize(CBaseEntity *pSource, CBaseEntity *pTarget) +{ + const Vector cvecAlignMins = pSource->WorldAlignMins(); + const Vector cvecAlignMaxs = pSource->WorldAlignMaxs(); + + Vector vecAlignMins = Vector(cvecAlignMins); + Vector vecAlignMaxs = Vector(cvecAlignMaxs); + + if (m_flScale != 0.0f && m_flScale != 1.0f) + { + vecAlignMins *= m_flScale; + vecAlignMaxs *= m_flScale; + } + + pTarget->SetCollisionBounds(vecAlignMins, vecAlignMins); + m_OnCopy.FireOutput(pTarget, this); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCopySize::InputCopySize( inputdata_t &inputdata ) +{ + if (!m_hSizeSource) + m_hSizeSource = gEntList.FindEntityByName(NULL, STRING(m_iszSizeSource), this, inputdata.pActivator, inputdata.pCaller); + if (!m_hTarget) + m_hTarget = gEntList.FindEntityByName(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + + if (!m_hSizeSource || !m_hTarget) + { + Warning("%s (%s): Could not find %s\n", GetClassname(), GetDebugName(), !m_hSizeSource ? "size source" : "target to copy size to"); + return; + } + + CopySize(m_hSizeSource, m_hTarget); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCopySize::InputCopySizeToEntity( inputdata_t &inputdata ) +{ + if (!m_hSizeSource) + m_hSizeSource = gEntList.FindEntityByName(NULL, STRING(m_iszSizeSource), this, inputdata.pActivator, inputdata.pCaller); + + if (!m_hSizeSource) + { + Warning("%s (%s): Could not find size source\n", GetClassname(), GetDebugName()); + return; + } + + if (!inputdata.value.Entity()) + return; + + CopySize(m_hSizeSource, inputdata.value.Entity()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCopySize::InputCopySizeFromEntity( inputdata_t &inputdata ) +{ + if (!m_hTarget) + m_hTarget = gEntList.FindEntityByName(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + + if (!m_hTarget) + { + Warning("%s (%s): Could not find target to copy size to\n", GetClassname(), GetDebugName()); + return; + } + + if (!inputdata.value.Entity()) + return; + + CopySize(inputdata.value.Entity(), m_hTarget); +} diff --git a/mp/src/game/server/mapbase/point_damageinfo.cpp b/mp/src/game/server/mapbase/point_damageinfo.cpp new file mode 100644 index 00000000..74e7b406 --- /dev/null +++ b/mp/src/game/server/mapbase/point_damageinfo.cpp @@ -0,0 +1,394 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: A special entity for afflicting damage as specific as possible. +// +//============================================================================= + +#include "cbase.h" + + +//----------------------------------------------------------------------------- +// Purpose: Advanced damage information afflicting. +//----------------------------------------------------------------------------- +class CPointDamageInfo : public CLogicalEntity +{ + DECLARE_CLASS( CPointDamageInfo, CLogicalEntity ); + DECLARE_DATADESC(); + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + virtual bool KeyValue( const char *szKeyName, const Vector &vecValue ); + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + +public: + CPointDamageInfo(); + + CTakeDamageInfo m_info; + + // The actual inflictor, attacker, and weapon in CTakeDamageInfo are direct entity pointers. + // This is needed to ensure !activator functionality, entities that might not exist yet, etc. + string_t m_iszInflictor; + string_t m_iszAttacker; + string_t m_iszWeapon; + + // The maximum number of entities to be damaged if they share m_target's targetname or classname. + int m_iMaxEnts; + + // Suppresses death sounds in the best way we possibly can. + bool m_bSuppressDeathSound; + + //bool m_bDisabled; + + // Inputs + void InputSetInflictor( inputdata_t &inputdata ) { m_iszInflictor = inputdata.value.StringID(); } //{ m_info.SetInflictor(inputdata.value.Entity()); } + void InputSetAttacker( inputdata_t &inputdata ) { m_iszAttacker = inputdata.value.StringID(); } //{ m_info.SetAttacker(inputdata.value.Entity()); } + void InputSetWeapon( inputdata_t &inputdata ) { m_iszWeapon = inputdata.value.StringID(); } //{ m_info.SetWeapon(inputdata.value.Entity()); } + + void InputSetDamage( inputdata_t &inputdata ) { m_info.SetDamage(inputdata.value.Float()); } + void InputSetMaxDamage( inputdata_t &inputdata ) { m_info.SetMaxDamage(inputdata.value.Float()); } + void InputSetDamageBonus( inputdata_t &inputdata ) { m_info.SetDamageBonus(inputdata.value.Float()); } + + void InputSetDamageType( inputdata_t &inputdata ) { m_info.SetDamageType(inputdata.value.Int()); } + void InputSetDamageCustom( inputdata_t &inputdata ) { m_info.SetDamageCustom(inputdata.value.Int()); } + void InputSetDamageStats( inputdata_t &inputdata ) { m_info.SetDamageStats(inputdata.value.Int()); } + void InputSetForceFriendlyFire( inputdata_t &inputdata ) { m_info.SetForceFriendlyFire(inputdata.value.Bool()); } + + void InputSetAmmoType( inputdata_t &inputdata ) { m_info.SetAmmoType(inputdata.value.Int()); } + + void InputSetPlayerPenetrationCount( inputdata_t &inputdata ) { m_info.SetPlayerPenetrationCount( inputdata.value.Int() ); } + void InputSetDamagedOtherPlayers( inputdata_t &inputdata ) { m_info.SetDamagedOtherPlayers( inputdata.value.Int() ); } + + void InputSetDamageForce( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_info.SetDamageForce(vec); } + void InputSetDamagePosition( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_info.SetDamagePosition(vec); } + void InputSetReportedPosition( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_info.SetReportedPosition(vec); } + + void HandleDamage(CBaseEntity *pTarget); + + void ApplyDamage( const char *target, inputdata_t &inputdata ); + void ApplyDamage( CBaseEntity *target, inputdata_t &inputdata ); + + void InputApplyDamage( inputdata_t &inputdata ); + void InputApplyDamageToEntity( inputdata_t &inputdata ); + + // Outputs + COutputEvent m_OnApplyDamage; + COutputEvent m_OnApplyDeath; +}; + +LINK_ENTITY_TO_CLASS(point_damageinfo, CPointDamageInfo); + + +BEGIN_DATADESC( CPointDamageInfo ) + + DEFINE_EMBEDDED( m_info ), + + // Keys + DEFINE_KEYFIELD( m_iszInflictor, FIELD_STRING, "Inflictor" ), + DEFINE_KEYFIELD( m_iszAttacker, FIELD_STRING, "Attacker" ), + DEFINE_KEYFIELD( m_iszWeapon, FIELD_STRING, "Weapon" ), + + DEFINE_KEYFIELD( m_iMaxEnts, FIELD_INTEGER, "MaxEnts" ), + DEFINE_KEYFIELD( m_bSuppressDeathSound, FIELD_BOOLEAN, "SuppressDeathSound" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "SetInflictor", InputSetInflictor ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetAttacker", InputSetAttacker ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetWeapon", InputSetWeapon ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamage", InputSetDamage ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxDamage", InputSetMaxDamage ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamageBonus", InputSetDamageBonus ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamageType", InputSetDamageType ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamageCustom", InputSetDamageCustom ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamageStats", InputSetDamageStats ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetForceFriendlyFire", InputSetForceFriendlyFire ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetAmmoType", InputSetAmmoType ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPlayerPenetrationCount", InputSetPlayerPenetrationCount ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamagedOtherPlayers", InputSetDamagedOtherPlayers ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetDamageForce", InputSetDamageForce ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetDamagePosition", InputSetDamagePosition ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetReportedPosition", InputSetReportedPosition ), + + DEFINE_INPUTFUNC( FIELD_VOID, "ApplyDamage", InputApplyDamage ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "ApplyDamageToEntity", InputApplyDamageToEntity ), + + // Outputs + DEFINE_OUTPUT(m_OnApplyDamage, "OnApplyDamage"), + DEFINE_OUTPUT(m_OnApplyDeath, "OnApplyDeath"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPointDamageInfo::CPointDamageInfo() +{ + m_info = CTakeDamageInfo(this, this, 24, DMG_GENERIC); + m_iMaxEnts = 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CPointDamageInfo::KeyValue( const char *szKeyName, const char *szValue ) +{ + /*if (FStrEq(szKeyName, "Inflictor")) + m_info.SetInflictor(gEntList.FindEntityByName(NULL, szValue, this, NULL, NULL)); + else if (FStrEq(szKeyName, "Attacker")) + m_info.SetAttacker(gEntList.FindEntityByName(NULL, szValue, this, NULL, NULL)); + else if (FStrEq(szKeyName, "Weapon")) + m_info.SetWeapon(gEntList.FindEntityByName(NULL, szValue, this, NULL, NULL)); + + else*/ if (FStrEq(szKeyName, "Damage")) + m_info.SetDamage(atof(szValue)); + else if (FStrEq(szKeyName, "MaxDamage")) + m_info.SetMaxDamage(atof(szValue)); + else if (FStrEq(szKeyName, "DamageBonus")) + m_info.SetDamageBonus(atof(szValue)); + + else if (FStrEq(szKeyName, "DamageType") || FStrEq(szKeyName, "DamagePresets")) + m_info.AddDamageType(atoi(szValue)); + else if (FStrEq(szKeyName, "DamageOr")) + m_info.AddDamageType(atoi(szValue)); + else if (FStrEq(szKeyName, "DamageCustom")) + m_info.SetDamageCustom(atoi(szValue)); + else if (FStrEq(szKeyName, "DamageStats")) + m_info.SetDamageStats(atoi(szValue)); + else if (FStrEq(szKeyName, "ForceFriendlyFire")) + m_info.SetForceFriendlyFire(FStrEq(szValue, "1")); + + else if (FStrEq(szKeyName, "AmmoType")) + m_info.SetAmmoType(atoi(szValue)); + + else if (FStrEq(szKeyName, "PlayerPenetrationCount")) + m_info.SetPlayerPenetrationCount(atoi(szValue)); + else if (FStrEq(szKeyName, "DamagedOtherPlayers")) + m_info.SetDamagedOtherPlayers(atoi(szValue)); + + else + { + if (!BaseClass::KeyValue(szKeyName, szValue)) + { + // Ripped from variant_t::Convert()... + Vector tmpVec = vec3_origin; + if (sscanf(szValue, "[%f %f %f]", &tmpVec[0], &tmpVec[1], &tmpVec[2]) == 0) + { + // Try sucking out 3 floats with no []s + sscanf(szValue, "%f %f %f", &tmpVec[0], &tmpVec[1], &tmpVec[2]); + } + return KeyValue(szKeyName, tmpVec); + } + } + + return true; +} + +bool CPointDamageInfo::KeyValue( const char *szKeyName, const Vector &vecValue ) +{ + if (FStrEq(szKeyName, "DamageForce")) + m_info.SetDamageForce(vecValue); + else if (FStrEq(szKeyName, "DamagePosition")) + m_info.SetDamagePosition(vecValue); + else if (FStrEq(szKeyName, "ReportedPosition")) + m_info.SetReportedPosition(vecValue); + else + return CBaseEntity::KeyValue( szKeyName, vecValue ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CPointDamageInfo::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + /* + if (FStrEq(szKeyName, "Inflictor")) + Q_snprintf(szValue, iMaxLen, "%s", m_info.GetInflictor() ? "" : m_info.GetInflictor()->GetDebugName()); + else if (FStrEq(szKeyName, "Attacker")) + Q_snprintf(szValue, iMaxLen, "%s", m_info.GetAttacker() ? "" : m_info.GetAttacker()->GetDebugName()); + else if (FStrEq(szKeyName, "Weapon")) + Q_snprintf(szValue, iMaxLen, "%s", m_info.GetWeapon() ? "" : m_info.GetWeapon()->GetDebugName()); + + else*/ if (FStrEq(szKeyName, "Damage")) + Q_snprintf(szValue, iMaxLen, "%f", m_info.GetDamage()); + else if (FStrEq(szKeyName, "MaxDamage")) + Q_snprintf(szValue, iMaxLen, "%f", m_info.GetMaxDamage()); + else if (FStrEq(szKeyName, "DamageBonus")) + Q_snprintf(szValue, iMaxLen, "%f", m_info.GetDamageBonus()); + + else if (FStrEq(szKeyName, "DamageType")) + Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamageType()); + else if (FStrEq(szKeyName, "DamageCustom")) + Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamageCustom()); + else if (FStrEq(szKeyName, "DamageStats")) + Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamageStats()); + else if (FStrEq(szKeyName, "ForceFriendlyFire")) + Q_snprintf(szValue, iMaxLen, "%s", m_info.IsForceFriendlyFire() ? "1" : "0"); + + else if (FStrEq(szKeyName, "AmmoType")) + Q_snprintf(szValue, iMaxLen, "%i", m_info.GetAmmoType()); + + else if (FStrEq(szKeyName, "PlayerPenetrationCount")) + Q_snprintf(szValue, iMaxLen, "%i", m_info.GetPlayerPenetrationCount()); + else if (FStrEq(szKeyName, "DamagedOtherPlayers")) + Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamagedOtherPlayers()); + + else if (FStrEq(szKeyName, "DamageForce")) + Q_snprintf(szValue, iMaxLen, "%f %f %f", m_info.GetDamageForce().x, m_info.GetDamageForce().y, m_info.GetDamageForce().z); + else if (FStrEq(szKeyName, "DamagePosition")) + Q_snprintf(szValue, iMaxLen, "%f %f %f", m_info.GetDamagePosition().x, m_info.GetDamagePosition().y, m_info.GetDamagePosition().z); + else if (FStrEq(szKeyName, "ReportedPosition")) + Q_snprintf(szValue, iMaxLen, "%f %f %f", m_info.GetReportedPosition().x, m_info.GetReportedPosition().y, m_info.GetReportedPosition().z); + else + return BaseClass::GetKeyValue(szKeyName, szValue, iMaxLen); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointDamageInfo::HandleDamage( CBaseEntity *pTarget ) +{ + pTarget->TakeDamage(m_info); + m_OnApplyDamage.FireOutput(pTarget, this); + if (pTarget->m_lifeState == LIFE_DYING) + m_OnApplyDeath.FireOutput(pTarget, this); + + // This is the best we could do, as nodeathsound is exclusive to response system NPCs + if (m_bSuppressDeathSound) + pTarget->EmitSound("AI_BaseNPC.SentenceStop"); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CPointDamageInfo::ApplyDamage( const char *target, inputdata_t &inputdata ) +{ + if (m_iszAttacker != NULL_STRING) + m_info.SetAttacker( gEntList.FindEntityByName(NULL, STRING(m_iszAttacker), this, inputdata.pActivator, inputdata.pCaller) ); + + if (m_iszInflictor != NULL_STRING) + m_info.SetInflictor( gEntList.FindEntityByName(NULL, STRING(m_iszInflictor), this, inputdata.pActivator, inputdata.pCaller) ); + else + { + CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker()); + if (pBCC != NULL && pBCC->GetActiveWeapon()) + { + m_info.SetInflictor(pBCC->GetActiveWeapon()); + } + } + + if (m_iszWeapon != NULL_STRING) + m_info.SetWeapon( gEntList.FindEntityByName(NULL, STRING(m_iszWeapon), this, inputdata.pActivator, inputdata.pCaller) ); + else + { + CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker()); + if (pBCC != NULL && pBCC->GetActiveWeapon()) + { + m_info.SetWeapon(pBCC->GetActiveWeapon()); + } + } + + if (!m_info.GetAttacker()) + m_info.SetAttacker( this ); + + if (!m_info.GetInflictor()) + m_info.SetInflictor( this ); + + if (!m_info.GetWeapon()) + m_info.SetWeapon( this ); + + CBaseEntity *pTarget = NULL; + if (m_iMaxEnts > 0) + { + for (int i = 0; i < m_iMaxEnts; i++) + { + pTarget = gEntList.FindEntityGeneric(pTarget, target, this, inputdata.pActivator, inputdata.pCaller); + if (pTarget) + { + HandleDamage( pTarget ); + } + } + } + else + { + pTarget = gEntList.FindEntityGeneric(NULL, target, this, inputdata.pActivator, inputdata.pCaller); + while (pTarget) + { + HandleDamage( pTarget ); + pTarget = gEntList.FindEntityGeneric(pTarget, target, this, inputdata.pActivator, inputdata.pCaller); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Applies damage to a specific entity +// Input : +// Output : +//----------------------------------------------------------------------------- +void CPointDamageInfo::ApplyDamage( CBaseEntity *target, inputdata_t &inputdata ) +{ + if (!target) + return; + + if (m_iszAttacker != NULL_STRING) + m_info.SetAttacker( gEntList.FindEntityByName(NULL, STRING(m_iszAttacker), this, inputdata.pActivator, inputdata.pCaller) ); + else + m_info.SetAttacker( this ); + + if (m_iszInflictor != NULL_STRING) + m_info.SetInflictor( gEntList.FindEntityByName(NULL, STRING(m_iszInflictor), this, inputdata.pActivator, inputdata.pCaller) ); + else + { + CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker()); + if (pBCC != NULL && pBCC->GetActiveWeapon()) + { + m_info.SetInflictor(pBCC->GetActiveWeapon()); + } + else + m_info.SetInflictor(this); + } + + if (m_iszWeapon != NULL_STRING) + m_info.SetWeapon( gEntList.FindEntityByName(NULL, STRING(m_iszWeapon), this, inputdata.pActivator, inputdata.pCaller) ); + else + { + CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker()); + if (pBCC != NULL && pBCC->GetActiveWeapon()) + { + m_info.SetWeapon(pBCC->GetActiveWeapon()); + } + else + m_info.SetWeapon(this); + } + + target->TakeDamage(m_info); + m_OnApplyDamage.FireOutput(target, this); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CPointDamageInfo::InputApplyDamage( inputdata_t &inputdata ) +{ + ApplyDamage(STRING(m_target), inputdata); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CPointDamageInfo::InputApplyDamageToEntity( inputdata_t &inputdata ) +{ + ApplyDamage(inputdata.value.Entity(), inputdata); +} diff --git a/mp/src/game/server/mapbase/point_entity_replace.cpp b/mp/src/game/server/mapbase/point_entity_replace.cpp new file mode 100644 index 00000000..6152d24c --- /dev/null +++ b/mp/src/game/server/mapbase/point_entity_replace.cpp @@ -0,0 +1,496 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Replaces a thing with another thing. +// +//============================================================================= + +#include "cbase.h" +#include "TemplateEntities.h" +#include "point_template.h" +#include "saverestore_utlvector.h" +#include "datadesc_mod.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPointEntityReplace : public CLogicalEntity +{ + DECLARE_CLASS( CPointEntityReplace, CLogicalEntity ); + DECLARE_DATADESC(); + +public: + + // The entity that will replace the target. + string_t m_iszReplacementEntity; + + // Only used with certain replacement types + EHANDLE m_hReplacementEntity; + + // How we should get the replacement entity. + int m_iReplacementType; + enum + { + REPLACEMENTTYPE_TARGET_TELEPORT, // Replace with target entity + REPLACEMENTTYPE_CLASSNAME, // Replace with entity of specified classname + REPLACEMENTTYPE_TEMPLATE, // Replace with a point_template's contents + REPLACEMENTTYPE_TEMPLATE_RELATIVE, // Replace with a point_template's contents, maintaining relative position + REPLACEMENTTYPE_RANDOM_TEMPLATE, // Replace with one of a point_template's templates, randomly selected + REPLACEMENTTYPE_RANDOM_TEMPLATE_RELATIVE, // Replace with one of a point_template's templates, randomly selected, maintaining relative position + }; + + // Where the replacement entit(ies) should replace at. + int m_iReplacementLocation; + enum + { + REPLACEMENTLOC_ABSOLUTEORIGIN, + REPLACEMENTLOC_WORLDSPACECENTER, + }; + + // Do we actually replace the target entity or just function as a glorified point_teleport? + bool m_bRemoveOriginalEntity; + + // What stuff we should take. + int m_iTakeStuff; + enum + { + REPLACEMENTTAKE_NAME = (1 << 0), // Takes the target's name + REPLACEMENTTAKE_PARENT = (1 << 1), // Takes parent and transfers children + REPLACEMENTTAKE_OWNER = (1 << 2), // Takes owner entity + REPLACEMENTTAKE_MODELSTUFF = (1 << 3), // Takes model stuff + }; + + // Used to cause it to take other fields, most of that has been moved to m_bTakeModelStuff and m_iszOtherTakes + //bool m_bTakeStuff; + + // Other keyvalues to copy. + CUtlVector m_iszOtherTakes; + + // Fire OnReplace with the original entity as the caller. + bool m_bFireOriginalEntityAsCaller; + + bool KeyValue(const char *szKeyName, const char *szValue); + + void HandleReplacement(CBaseEntity *pEntity, CBaseEntity *pReplacement); + + CBaseEntity *GetReplacementEntity(inputdata_t *inputdata); + + void ReplaceEntity(CBaseEntity *pEntity, inputdata_t &inputdata); + + // Inputs + void InputReplace( inputdata_t &inputdata ); + void InputReplaceEntity( inputdata_t &inputdata ); + + void InputSetReplacementEntity( inputdata_t &inputdata ); + + COutputEHANDLE m_OnReplace; // Passes the entity we replaced the target with, fired multiple times if REPLACEMENTTYPE_TEMPLATE created multiple entities +}; + +LINK_ENTITY_TO_CLASS(point_entity_replace, CPointEntityReplace); + +BEGIN_DATADESC( CPointEntityReplace ) + + // Keys + DEFINE_KEYFIELD( m_iszReplacementEntity, FIELD_STRING, "ReplacementEntity" ), + DEFINE_FIELD( m_hReplacementEntity, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iReplacementType, FIELD_INTEGER, "ReplacementType" ), + DEFINE_KEYFIELD( m_iReplacementLocation, FIELD_INTEGER, "ReplacementLocation" ), + DEFINE_KEYFIELD( m_bRemoveOriginalEntity, FIELD_BOOLEAN, "RemoveOriginalEntity" ), + DEFINE_FIELD( m_iTakeStuff, FIELD_INTEGER ), + DEFINE_UTLVECTOR( m_iszOtherTakes, FIELD_STRING ), + DEFINE_KEYFIELD( m_bFireOriginalEntityAsCaller, FIELD_BOOLEAN, "TargetIsCaller" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Replace", InputReplace ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "ReplaceEntity", InputReplaceEntity ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetReplacementEntity", InputSetReplacementEntity ), + + // Outputs + DEFINE_OUTPUT(m_OnReplace, "OnReplace"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CPointEntityReplace::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (!stricmp(szKeyName, "OtherStuff")) + { + if (szValue && szValue[0] != '\0') + { + CUtlStringList vecKeyList; + Q_SplitString(szValue, ",", vecKeyList); + FOR_EACH_VEC(vecKeyList, i) + { + m_iszOtherTakes.AddToTail(AllocPooledString(vecKeyList[i])); + } + } + return true; + } + else if (strnicmp(szKeyName, "Take", 4) == 0) + { + const char *subject = (szKeyName + 4); + int flag = 0; + if (FStrEq(subject, "Targetname")) + flag |= REPLACEMENTTAKE_NAME; + else if (FStrEq(subject, "Parent")) + flag |= REPLACEMENTTAKE_PARENT; + else if (FStrEq(subject, "Owner")) + flag |= REPLACEMENTTAKE_OWNER; + else if (FStrEq(subject, "ModelStuff")) + flag |= REPLACEMENTTAKE_MODELSTUFF; + else + return BaseClass::KeyValue(szKeyName, szValue); + + // Remove the flag if 0, add the flag if not 0 + !FStrEq(szValue, "0") ? (m_iTakeStuff |= flag) : (m_iTakeStuff &= ~flag); + + return true; + } + + return BaseClass::KeyValue(szKeyName, szValue); +} + +//----------------------------------------------------------------------------- +// Purpose: Takes targetname, fields, etc. Keep in mind neither are checked for null! +//----------------------------------------------------------------------------- +void CPointEntityReplace::HandleReplacement(CBaseEntity *pEntity, CBaseEntity *pReplacement) +{ + if (m_iTakeStuff & REPLACEMENTTAKE_NAME) + pReplacement->SetName(pEntity->GetEntityName()); + + if (m_iTakeStuff & REPLACEMENTTAKE_PARENT) + { + if (pEntity->GetParent()) + pReplacement->SetParent(pEntity->GetParent(), pEntity->GetParentAttachment()); + + TransferChildren(pEntity, pReplacement); + } + + if (m_iTakeStuff & REPLACEMENTTAKE_OWNER) + { + if (pEntity->GetOwnerEntity()) + pReplacement->SetOwnerEntity(pEntity->GetOwnerEntity()); + } + + if (m_iTakeStuff & REPLACEMENTTAKE_MODELSTUFF) + { + pReplacement->m_nRenderMode = pEntity->m_nRenderMode; + pReplacement->m_nRenderFX = pEntity->m_nRenderFX; + pReplacement->m_clrRender = pEntity->m_clrRender; + if (pEntity->GetBaseAnimating() && pReplacement->GetBaseAnimating()) + { + CBaseAnimating *pEntityAnimating = pEntity->GetBaseAnimating(); + CBaseAnimating *pReplacementAnimating = pReplacement->GetBaseAnimating(); + + pReplacementAnimating->CopyAnimationDataFrom(pEntityAnimating); + + for ( int iPose = 0; iPose < MAXSTUDIOPOSEPARAM; ++iPose ) + { + pReplacementAnimating->SetPoseParameter( iPose, pEntityAnimating->GetPoseParameter( iPose ) ); + } + } + } + + if (m_iszOtherTakes.Count() > 0) + { + CUtlVector szValues; + szValues.AddMultipleToTail( m_iszOtherTakes.Count() ); + + for ( datamap_t *dmap = pEntity->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_SAVE | FTYPEDESC_KEY) && dmap->dataDesc[i].fieldName ) + { + DevMsg("Target Field Name: %s,\n", dmap->dataDesc[i].fieldName); + for (int i2 = 0; i2 < m_iszOtherTakes.Count(); i2++) + { + if ( FStrEq(dmap->dataDesc[i].fieldName, STRING(m_iszOtherTakes[i2])) ) + { + //szValues[i2] = (((char *)pEntity) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ]); + szValues[i2].Set( dmap->dataDesc[i].fieldType, ((char*)pEntity) + dmap->dataDesc[i].fieldOffset[TD_OFFSET_NORMAL] ); + break; + } + } + } + } + } + + for ( datamap_t *dmap = pReplacement->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_SAVE | FTYPEDESC_KEY) && dmap->dataDesc[i].fieldName ) + { + DevMsg("Replacement Field Name: %s,\n", dmap->dataDesc[i].fieldName); + for (int i2 = 0; i2 < m_iszOtherTakes.Count(); i2++) + { + if ( FStrEq(dmap->dataDesc[i].fieldName, STRING(m_iszOtherTakes[i2])) ) + { + //(void*)(((char *)pReplacement) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ]) = szValues[i2]; + + //char *data = (((char *)pReplacement) + dmap->dataDesc[i].fieldOffset[TD_OFFSET_NORMAL]); + //memcpy(data, szValues[i2], sizeof(*data)); + + //Datadesc_SetFieldString( szValues[i2], pReplacement, &dmap->dataDesc[i] ); + + szValues[i2].SetOther( (((char *)pReplacement) + dmap->dataDesc[i].fieldOffset[TD_OFFSET_NORMAL]) ); + break; + } + } + } + } + } + } + + /* + // This is largely duplicated from phys_convert + if (m_bTakeStuff) + { + pReplacement->m_nRenderMode = pEntity->m_nRenderMode; + pReplacement->m_nRenderFX = pEntity->m_nRenderFX; + const color32 rclr = pEntity->GetRenderColor(); + pReplacement->SetRenderColor(rclr.r, rclr.g, rclr.b, rclr.a); + if (pEntity->GetBaseAnimating() && pReplacement->GetBaseAnimating()) + { + CBaseAnimating *pEntityAnimating = pEntity->GetBaseAnimating(); + CBaseAnimating *pReplacementAnimating = pReplacement->GetBaseAnimating(); + + pReplacementAnimating->CopyAnimationDataFrom(pEntityAnimating); + + //pReplacementAnimating->SetCycle(pEntityAnimating->GetCycle()); + //pReplacementAnimating->IncrementInterpolationFrame(); + //pReplacementAnimating->SetSequence(pEntityAnimating->GetSequence()); + //pReplacementAnimating->m_flAnimTime = pEntityAnimating->m_flAnimTime; + //pReplacementAnimating->m_nBody = pEntityAnimating->m_nBody; + //pReplacementAnimating->m_nSkin = pEntityAnimating->m_nSkin; + //pReplacementAnimating->SetModelScale(pEntityAnimating->GetModelScale()); + } + + UTIL_TransferPoseParameters(pEntity, pReplacement); + TransferChildren(pEntity, pReplacement); + } + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline CBaseEntity *CPointEntityReplace::GetReplacementEntity(inputdata_t *inputdata) +{ + if (!m_hReplacementEntity) + m_hReplacementEntity.Set(gEntList.FindEntityByName(NULL, STRING(m_iszReplacementEntity), this, inputdata ? inputdata->pActivator : NULL, inputdata ? inputdata->pCaller : NULL)); + return m_hReplacementEntity; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointEntityReplace::ReplaceEntity(CBaseEntity *pEntity, inputdata_t &inputdata) +{ + Vector vecOrigin; + if (m_iReplacementLocation == REPLACEMENTLOC_WORLDSPACECENTER) + vecOrigin = pEntity->WorldSpaceCenter(); + else + vecOrigin = pEntity->GetAbsOrigin(); + + QAngle angAngles = pEntity->GetAbsAngles(); + Vector vecVelocity; + QAngle angVelocity; + if (pEntity->VPhysicsGetObject()) + { + AngularImpulse angImpulse; + pEntity->VPhysicsGetObject()->GetVelocity(&vecVelocity, &angImpulse); + AngularImpulseToQAngle(angImpulse, angVelocity); + } + else + { + vecVelocity = pEntity->GetAbsVelocity(); + angVelocity = pEntity->GetLocalAngularVelocity(); + } + + // No conflicts with the replacement entity until we're finished + if (m_bRemoveOriginalEntity && !(m_iTakeStuff & REPLACEMENTTAKE_MODELSTUFF)) + { + pEntity->AddSolidFlags( FSOLID_NOT_SOLID ); + pEntity->AddEffects( EF_NODRAW ); + } + + CBaseEntity *pCaller = m_bFireOriginalEntityAsCaller ? pEntity : this; + + switch (m_iReplacementType) + { + case REPLACEMENTTYPE_TARGET_TELEPORT: + { + CBaseEntity *pReplacementEntity = GetReplacementEntity(&inputdata); + if (pReplacementEntity) + { + HandleReplacement(pEntity, pReplacementEntity); + pReplacementEntity->Teleport(&vecOrigin, &angAngles, &vecVelocity); + + if (pReplacementEntity->VPhysicsGetObject()) + { + AngularImpulse angImpulse; + QAngleToAngularImpulse(angAngles, angImpulse); + pReplacementEntity->VPhysicsGetObject()->SetVelocityInstantaneous(&vecVelocity, &angImpulse); + } + else + { + pReplacementEntity->SetAbsVelocity(vecVelocity); + pReplacementEntity->SetBaseVelocity( vec3_origin ); + pReplacementEntity->SetLocalAngularVelocity(angVelocity); + } + + m_OnReplace.Set(pReplacementEntity, pReplacementEntity, pCaller); + } + } break; + case REPLACEMENTTYPE_CLASSNAME: + { + CBaseEntity *pReplacementEntity = CreateNoSpawn(STRING(m_iszReplacementEntity), vecOrigin, angAngles); + if (pReplacementEntity) + { + HandleReplacement(pEntity, pReplacementEntity); + + DispatchSpawn(pReplacementEntity); + + if (pReplacementEntity->VPhysicsGetObject()) + { + IPhysicsObject *pPhys = pReplacementEntity->VPhysicsGetObject(); + AngularImpulse angImpulse; + QAngleToAngularImpulse(angAngles, angImpulse); + pPhys->SetVelocityInstantaneous(&vecVelocity, &angImpulse); + } + else + { + pReplacementEntity->SetAbsVelocity(vecVelocity); + pReplacementEntity->SetBaseVelocity( vec3_origin ); + pReplacementEntity->SetLocalAngularVelocity(angVelocity); + } + + m_OnReplace.Set(pReplacementEntity, pReplacementEntity, pCaller); + } + } break; + case REPLACEMENTTYPE_TEMPLATE: + case REPLACEMENTTYPE_TEMPLATE_RELATIVE: + { + CPointTemplate *pTemplate = dynamic_cast(GetReplacementEntity(&inputdata)); + if (!pTemplate) + { + Warning("%s unable to retrieve entity %s. It either does not exist or is not a point_template.\n", GetDebugName(), STRING(m_iszReplacementEntity)); + return; + } + + CUtlVector hNewEntities; + if ( !pTemplate->CreateInstance( vecOrigin, angAngles, &hNewEntities ) || hNewEntities.Count() == 0 ) + return; + + CBaseEntity *pTemplateEntity = NULL; + for (int i = 0; i < hNewEntities.Count(); i++) + { + pTemplateEntity = hNewEntities[i]; + if (pTemplateEntity) + { + HandleReplacement(pEntity, pTemplateEntity); + + if (m_iReplacementType != REPLACEMENTTYPE_TEMPLATE_RELATIVE) + { + // We have to do this again. + pTemplateEntity->Teleport( &vecOrigin, &angAngles, &vecVelocity ); + } + + if (pTemplateEntity->VPhysicsGetObject()) + { + AngularImpulse angImpulse; + QAngleToAngularImpulse(angAngles, angImpulse); + pTemplateEntity->VPhysicsGetObject()->SetVelocityInstantaneous(&vecVelocity, &angImpulse); + } + else + { + pTemplateEntity->SetAbsVelocity(vecVelocity); + pTemplateEntity->SetBaseVelocity( vec3_origin ); + pTemplateEntity->SetLocalAngularVelocity(angVelocity); + } + + m_OnReplace.Set(pTemplateEntity, pTemplateEntity, pCaller); + } + } + } break; + case REPLACEMENTTYPE_RANDOM_TEMPLATE: + case REPLACEMENTTYPE_RANDOM_TEMPLATE_RELATIVE: + { + CPointTemplate *pTemplate = dynamic_cast(GetReplacementEntity(&inputdata)); + if (!pTemplate) + { + Warning("%s unable to retrieve entity %s. It either does not exist or is not a point_template.\n", GetDebugName(), STRING(m_iszReplacementEntity)); + return; + } + + CBaseEntity *pTemplateEntity = NULL; + if ( !pTemplate->CreateSpecificInstance( RandomInt(0, pTemplate->GetNumTemplates() - 1), vecOrigin, angAngles, &pTemplateEntity ) ) + return; + + if (pTemplateEntity) + { + HandleReplacement(pEntity, pTemplateEntity); + + if (m_iReplacementType != REPLACEMENTTYPE_RANDOM_TEMPLATE_RELATIVE) + { + // We have to do this again. + pTemplateEntity->Teleport( &vecOrigin, &angAngles, &vecVelocity ); + } + + if (pTemplateEntity->VPhysicsGetObject()) + { + AngularImpulse angImpulse; + QAngleToAngularImpulse(angAngles, angImpulse); + pTemplateEntity->VPhysicsGetObject()->SetVelocityInstantaneous(&vecVelocity, &angImpulse); + } + else + { + pTemplateEntity->SetAbsVelocity(vecVelocity); + pTemplateEntity->SetBaseVelocity( vec3_origin ); + pTemplateEntity->SetLocalAngularVelocity(angVelocity); + } + + m_OnReplace.Set(pTemplateEntity, pTemplateEntity, pCaller); + } + } break; + } + + if (m_bRemoveOriginalEntity) + UTIL_Remove(pEntity); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointEntityReplace::InputReplace( inputdata_t &inputdata ) +{ + CBaseEntity *pEntity = gEntList.FindEntityByName(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + if (pEntity) + ReplaceEntity(pEntity, inputdata); + else + Warning("%s unable to find replacement target %s.\n", GetDebugName(), STRING(m_target)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointEntityReplace::InputReplaceEntity( inputdata_t &inputdata ) +{ + if (inputdata.value.Entity()) + ReplaceEntity(inputdata.value.Entity(), inputdata); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointEntityReplace::InputSetReplacementEntity( inputdata_t &inputdata ) +{ + m_iszReplacementEntity = inputdata.value.StringID(); + m_hReplacementEntity = NULL; +} diff --git a/mp/src/game/server/mapbase/point_glow.cpp b/mp/src/game/server/mapbase/point_glow.cpp new file mode 100644 index 00000000..877aec83 --- /dev/null +++ b/mp/src/game/server/mapbase/point_glow.cpp @@ -0,0 +1,73 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase off-shoot of tf_glow (created using SDK code only) +// +//============================================================================= + +#include "cbase.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPointGlow : public CPointEntity +{ + DECLARE_CLASS( CPointGlow, CPointEntity ); +public: + + int UpdateTransmitState( void ) { return SetTransmitState( FL_EDICT_ALWAYS ); } + + void Spawn( void ); + + void SetGlowTarget( CBaseEntity *pActivator, CBaseEntity *pCaller ) { m_hGlowTarget = gEntList.FindEntityByName(NULL, m_target, this, pActivator, pCaller); } + + // Inputs + void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); SetGlowTarget( inputdata.pActivator, inputdata.pCaller ); } + + void InputEnable( inputdata_t &inputdata ) { m_bGlowDisabled = false; SetGlowTarget( inputdata.pActivator, inputdata.pCaller ); } + void InputDisable( inputdata_t &inputdata ) { m_bGlowDisabled = true; } + void InputToggle( inputdata_t &inputdata ) { m_bGlowDisabled ? InputEnable(inputdata) : InputDisable(inputdata); } + + void InputSetGlowColor( inputdata_t &inputdata ) { m_GlowColor = inputdata.value.Color32(); } + + CNetworkHandle( CBaseEntity, m_hGlowTarget ); + CNetworkColor32( m_GlowColor ); + CNetworkVar( bool, m_bGlowDisabled ); + + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); +}; + +LINK_ENTITY_TO_CLASS( point_glow, CPointGlow ); + + +BEGIN_DATADESC( CPointGlow ) + + // Keys + DEFINE_KEYFIELD( m_GlowColor, FIELD_COLOR32, "GlowColor" ), + DEFINE_FIELD( m_hGlowTarget, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_bGlowDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + DEFINE_INPUTFUNC( FIELD_COLOR32, "SetGlowColor", InputSetGlowColor ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CPointGlow, DT_PointGlow ) + SendPropEHandle( SENDINFO( m_hGlowTarget ) ), + SendPropInt( SENDINFO( m_GlowColor ), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt ), + SendPropBool( SENDINFO( m_bGlowDisabled ) ), +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointGlow::Spawn() +{ + m_hGlowTarget = gEntList.FindEntityByName( NULL, m_target, this ); + + BaseClass::Spawn(); +} diff --git a/mp/src/game/server/mapbase/point_projectile.cpp b/mp/src/game/server/mapbase/point_projectile.cpp new file mode 100644 index 00000000..7909c55d --- /dev/null +++ b/mp/src/game/server/mapbase/point_projectile.cpp @@ -0,0 +1,197 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Fires projectiles. What else is there to say? +// +//============================================================================= + +#include "cbase.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPointProjectile : public CBaseEntity +{ + DECLARE_CLASS( CPointProjectile, CBaseEntity ); + DECLARE_DATADESC(); + +public: + + void Precache(); + void Spawn(); + + // m_target is projectile class + + // Owner + // Handle is m_hOwnerEntity + string_t m_iszOwner; + + // Damage + float m_flDamage; + + // Speed + float m_flSpeed; + + bool m_bFireProjectilesFromOwner; + + CBaseEntity *CalculateOwner( CBaseEntity *pActivator, CBaseEntity *pCaller ); + + CBaseEntity *CreateProjectile( Vector &vecOrigin, QAngle &angAngles, Vector &vecDir, CBaseEntity *pOwner ); + + // Inputs + void InputFire( inputdata_t &inputdata ); + void InputFireAtEntity( inputdata_t &inputdata ); + void InputFireAtPosition( inputdata_t &inputdata ); + + void InputSetDamage( inputdata_t &inputdata ) { m_flDamage = inputdata.value.Float(); } + void InputSetOwner( inputdata_t &inputdata ) { m_iszOwner = inputdata.value.StringID(); SetOwnerEntity(NULL); } + void InputSetSpeed( inputdata_t &inputdata ) { m_flSpeed = inputdata.value.Float(); } + + void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); UTIL_PrecacheOther(inputdata.value.String()); } + + COutputEHANDLE m_OnFire; +}; + +LINK_ENTITY_TO_CLASS(point_projectile, CPointProjectile); + +BEGIN_DATADESC( CPointProjectile ) + + // Keys + DEFINE_KEYFIELD( m_iszOwner, FIELD_STRING, "Owner" ), + DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ), + DEFINE_KEYFIELD( m_flSpeed, FIELD_FLOAT, "Speed" ), + DEFINE_KEYFIELD( m_bFireProjectilesFromOwner, FIELD_BOOLEAN, "FireFromOwner" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Fire", InputFire ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "FireAtEntity", InputFireAtEntity ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "FireAtPosition", InputFireAtPosition ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamage", InputSetDamage ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetOwnerEntity", InputSetOwner ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetProjectileClass", InputSetTarget ), + + // Outputs + DEFINE_OUTPUT(m_OnFire, "OnFire"), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProjectile::Precache() +{ + UTIL_PrecacheOther(STRING(m_target)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProjectile::Spawn() +{ + Precache(); + + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates owner entity +//----------------------------------------------------------------------------- +inline CBaseEntity *CPointProjectile::CalculateOwner( CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + if (m_iszOwner != NULL_STRING && !GetOwnerEntity()) + { + CBaseEntity *pOwner = gEntList.FindEntityByName(NULL, STRING(m_iszOwner), this, pActivator, pCaller); + if (pOwner) + SetOwnerEntity(pOwner); + } + + return GetOwnerEntity() ? GetOwnerEntity() : this; +} + +//----------------------------------------------------------------------------- +// Purpose: Fires projectile and output +//----------------------------------------------------------------------------- +inline CBaseEntity *CPointProjectile::CreateProjectile( Vector &vecOrigin, QAngle &angAngles, Vector &vecDir, CBaseEntity *pOwner ) +{ + CBaseEntity *pProjectile = CreateNoSpawn(STRING(m_target), vecOrigin, angAngles, pOwner); + if (!pProjectile) + { + Warning("WARNING: %s unable to create projectile class %s!\n", GetDebugName(), STRING(m_target)); + return NULL; + } + + pProjectile->SetAbsVelocity(vecDir * m_flSpeed); + DispatchSpawn(pProjectile); + + pProjectile->SetDamage(m_flDamage); + + m_OnFire.Set(pProjectile, pProjectile, pOwner); + + return pProjectile; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProjectile::InputFire( inputdata_t &inputdata ) +{ + Vector vecOrigin = GetAbsOrigin(); + QAngle angAngles = GetAbsAngles(); + + CBaseEntity *pOwner = CalculateOwner(inputdata.pActivator, inputdata.pCaller); + if (pOwner && m_bFireProjectilesFromOwner) + vecOrigin = pOwner->GetAbsOrigin(); + + Vector vecDir; + AngleVectors(angAngles, &vecDir); + + CreateProjectile(vecOrigin, angAngles, vecDir, pOwner); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProjectile::InputFireAtEntity( inputdata_t &inputdata ) +{ + CBaseEntity *pTarget = inputdata.value.Entity(); + if (!pTarget) + return; + + Vector vecOrigin = GetAbsOrigin(); + + CBaseEntity *pOwner = CalculateOwner(inputdata.pActivator, inputdata.pCaller); + if (pOwner && m_bFireProjectilesFromOwner) + vecOrigin = pOwner->GetAbsOrigin(); + + Vector vecDir = (pTarget->WorldSpaceCenter() - vecOrigin); + VectorNormalize(vecDir); + + QAngle angAngles; + VectorAngles(vecDir, angAngles); + + CreateProjectile(vecOrigin, angAngles, vecDir, pOwner); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProjectile::InputFireAtPosition( inputdata_t &inputdata ) +{ + Vector vecInput; + inputdata.value.Vector3D(vecInput); + + Vector vecOrigin = GetAbsOrigin(); + + CBaseEntity *pOwner = CalculateOwner(inputdata.pActivator, inputdata.pCaller); + if (pOwner && m_bFireProjectilesFromOwner) + vecOrigin = pOwner->GetAbsOrigin(); + + Vector vecDir = (vecInput - vecOrigin); + VectorNormalize(vecDir); + + QAngle angAngles; + VectorAngles(vecDir, angAngles); + + CreateProjectile(vecOrigin, angAngles, vecDir, pOwner); +} diff --git a/mp/src/game/server/mapbase/point_radiation_source.cpp b/mp/src/game/server/mapbase/point_radiation_source.cpp new file mode 100644 index 00000000..febe0c1a --- /dev/null +++ b/mp/src/game/server/mapbase/point_radiation_source.cpp @@ -0,0 +1,140 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ==== +// +// An entity that triggers the player's geiger counter. +// +// Doesn't cause any actual damage. Should be parentable. +// +//============================================================================= + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + + +class CPointRadiationSource : public CPointEntity +{ +public: + DECLARE_CLASS( CPointRadiationSource, CPointEntity ); + DECLARE_DATADESC(); + + void Spawn(); + + void RadiationThink(); + + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + bool m_bTestPVS; + float m_flRadius; + float m_flIntensity = 1.0f; + + bool m_bDisabled; +}; + +BEGIN_DATADESC( CPointRadiationSource ) + + // Function Pointers + DEFINE_FUNCTION( RadiationThink ), + + // Fields + DEFINE_KEYFIELD( m_bTestPVS, FIELD_BOOLEAN, "TestPVS" ), + DEFINE_INPUT( m_flRadius, FIELD_FLOAT, "SetRadius" ), + DEFINE_INPUT( m_flIntensity, FIELD_FLOAT, "SetIntensity" ), + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( point_radiation_source, CPointRadiationSource ); + + +//----------------------------------------------------------------------------- +// Purpose: Called when spawning, after keyvalues have been handled. +//----------------------------------------------------------------------------- +void CPointRadiationSource::Spawn( void ) +{ + BaseClass::Spawn(); + + if (!m_bDisabled) + { + SetThink( &CPointRadiationSource::RadiationThink ); + SetNextThink( gpGlobals->curtime + random->RandomFloat(0.0, 0.5) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointRadiationSource::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; + + SetThink( &CPointRadiationSource::RadiationThink ); + SetNextThink( gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointRadiationSource::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; + + SetThink( NULL ); + SetNextThink( TICK_NEVER_THINK ); + + // Must update player + //CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + //if (pPlayer) + //{ + // pPlayer->NotifyNearbyRadiationSource(1000); + //} +} + +//----------------------------------------------------------------------------- +// Purpose: Trigger hurt that causes radiation will do a radius check and set +// the player's geiger counter level according to distance from center +// of trigger. +//----------------------------------------------------------------------------- +void CPointRadiationSource::RadiationThink( void ) +{ + CBasePlayer *pPlayer = NULL; + if (m_bTestPVS) + { + CBaseEntity *pPVSPlayer = CBaseEntity::Instance(UTIL_FindClientInPVS(edict())); + if (pPVSPlayer) + { + pPlayer = static_cast(pPVSPlayer); + } + } + else + { + pPlayer = UTIL_GetLocalPlayer(); + } + + if (pPlayer) + { + // get range to player; + float flRange = pPlayer->GetAbsOrigin().DistTo((GetAbsOrigin())); + if (m_flRadius <= 0 || flRange < m_flRadius) + { + if (m_flIntensity == 0) + { + Warning("%s: INTENSITY IS ZERO!!! Can't notify of radiation\n", GetDebugName()); + return; + } + + //flRange *= 3.0f; + flRange /= m_flIntensity; + pPlayer->NotifyNearbyRadiationSource(flRange); + } + } + + SetNextThink( gpGlobals->curtime + 0.25 ); +} diff --git a/mp/src/game/server/mapbase/variant_tools.h b/mp/src/game/server/mapbase/variant_tools.h new file mode 100644 index 00000000..21e631fb --- /dev/null +++ b/mp/src/game/server/mapbase/variant_tools.h @@ -0,0 +1,43 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VARIANT_TOOLS_H +#define VARIANT_TOOLS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "cbase.h" + +// Quick way to define variants in a datadesc. +extern ISaveRestoreOps *variantFuncs; +#define DEFINE_VARIANT(name) { FIELD_CUSTOM, #name, { offsetof(classNameTypedef, name), 0 }, 1, FTYPEDESC_SAVE, NULL, variantFuncs, NULL } +#define DEFINE_KEYVARIANT(name,mapname) { FIELD_CUSTOM, #name, { offsetof(classNameTypedef, name), 0 }, 1, FTYPEDESC_SAVE | FTYPEDESC_KEY, mapname, variantFuncs, NULL } + +// Most of these are defined in variant_t.cpp. + +// Creates a variant_t from the given string. +// It could return as a String or a Float. +variant_t Variant_Parse(const char *szValue); + +// Intended to convert FIELD_INPUT I/O parameters to other values, like integers, floats, or even entities. +// This only changes FIELD_STRING variants. Other data like FIELD_EHANDLE or FIELD_INTEGER are not affected. +variant_t Variant_ParseInput(inputdata_t inputdata); + +// A simpler version of Variant_ParseInput that does not allow FIELD_EHANDLE. +variant_t Variant_ParseString(variant_t value); + +// val1 == val2 +bool Variant_Equal(variant_t val1, variant_t val2, bool bLenAllowed = true); + +// val1 > val2 +bool Variant_Greater(variant_t val1, variant_t val2, bool bLenAllowed = true); + +// val1 >= val2 +bool Variant_GreaterOrEqual(variant_t val1, variant_t val2, bool bLenAllowed = true); + +#endif \ No newline at end of file diff --git a/mp/src/game/server/mapentities.cpp b/mp/src/game/server/mapentities.cpp index a08ad1ec..0ee82206 100644 --- a/mp/src/game/server/mapentities.cpp +++ b/mp/src/game/server/mapentities.cpp @@ -86,7 +86,7 @@ string_t ExtractParentName(string_t parentName) return parentName; char szToken[256]; - nexttoken(szToken, STRING(parentName), ','); + nexttoken(szToken, STRING(parentName), ',', sizeof(szToken)); return AllocPooledString(szToken); } @@ -208,7 +208,7 @@ void SetupParentsForSpawnList( int nEntities, HierarchicalSpawn_t *pSpawnList ) if ( strchr(STRING(pEntity->m_iParent), ',') ) { char szToken[256]; - const char *pAttachmentName = nexttoken(szToken, STRING(pEntity->m_iParent), ','); + const char *pAttachmentName = nexttoken(szToken, STRING(pEntity->m_iParent), ',', sizeof(szToken)); pEntity->m_iParent = AllocPooledString(szToken); CBaseEntity *pParent = gEntList.FindEntityByName( NULL, pEntity->m_iParent ); diff --git a/mp/src/game/server/maprules.cpp b/mp/src/game/server/maprules.cpp index 64001b57..91049ad8 100644 --- a/mp/src/game/server/maprules.cpp +++ b/mp/src/game/server/maprules.cpp @@ -12,6 +12,9 @@ #include "entitylist.h" #include "ai_hull.h" #include "entityoutput.h" +#ifdef MAPBASE +#include "eventqueue.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -224,6 +227,9 @@ public: DECLARE_DATADESC(); void InputGameEnd( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputGameEndSP( inputdata_t &inputdata ); +#endif void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); private: }; @@ -232,6 +238,9 @@ BEGIN_DATADESC( CGameEnd ) // inputs DEFINE_INPUTFUNC( FIELD_VOID, "EndGame", InputGameEnd ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "EndGameSP", InputGameEndSP ), +#endif END_DATADESC() @@ -243,6 +252,17 @@ void CGameEnd::InputGameEnd( inputdata_t &inputdata ) g_pGameRules->EndMultiplayerGame(); } +#ifdef MAPBASE +void CGameEnd::InputGameEndSP( inputdata_t &inputdata ) +{ + // This basically just acts as a shortcut for the "startupmenu force"/disconnection command. + // Things like mapping competitions could change this code based on given strings for specific endings (e.g. background maps). + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + if (pPlayer) + engine->ClientCommand(pPlayer->edict(), "startupmenu force"); +} +#endif + void CGameEnd::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if ( !CanFireForActivator( pActivator ) ) @@ -274,6 +294,12 @@ public: void InputDisplay( inputdata_t &inputdata ); void Display( CBaseEntity *pActivator ); +#ifdef MAPBASE + void InputSetText ( inputdata_t &inputdata ); + void SetText( const char* pszStr ); + + void InputSetFont( inputdata_t &inputdata ) { m_strFont = inputdata.value.StringID(); } +#endif void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { @@ -284,6 +310,11 @@ private: string_t m_iszMessage; hudtextparms_t m_textParms; + +#ifdef MAPBASE + string_t m_strFont; + bool m_bAutobreak; +#endif }; LINK_ENTITY_TO_CLASS( game_text, CGameText ); @@ -303,10 +334,19 @@ BEGIN_DATADESC( CGameText ) DEFINE_KEYFIELD( m_textParms.holdTime, FIELD_FLOAT, "holdtime" ), DEFINE_KEYFIELD( m_textParms.fxTime, FIELD_FLOAT, "fxtime" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_strFont, FIELD_STRING, "font" ), + DEFINE_KEYFIELD( m_bAutobreak, FIELD_BOOLEAN, "autobreak" ), +#endif + DEFINE_ARRAY( m_textParms, FIELD_CHARACTER, sizeof(hudtextparms_t) ), // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "Display", InputDisplay ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetText", InputSetText ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFont", InputSetFont ), +#endif END_DATADESC() @@ -332,6 +372,13 @@ bool CGameText::KeyValue( const char *szKeyName, const char *szValue ) m_textParms.b2 = color[2]; m_textParms.a2 = color[3]; } +#ifdef MAPBASE + else if (FStrEq( szKeyName, "message" )) + { + // Needed for newline support + SetText( szValue ); + } +#endif else return BaseClass::KeyValue( szKeyName, szValue ); @@ -351,7 +398,11 @@ void CGameText::Display( CBaseEntity *pActivator ) if ( MessageToAll() ) { +#ifdef MAPBASE + UTIL_HudMessageAll( m_textParms, MessageGet(), STRING(m_strFont), m_bAutobreak ); +#else UTIL_HudMessageAll( m_textParms, MessageGet() ); +#endif } else { @@ -359,16 +410,54 @@ void CGameText::Display( CBaseEntity *pActivator ) if ( gpGlobals->maxClients == 1 ) { CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); +#ifdef MAPBASE + UTIL_HudMessage( pPlayer, m_textParms, MessageGet(), STRING(m_strFont), m_bAutobreak ); +#else UTIL_HudMessage( pPlayer, m_textParms, MessageGet() ); +#endif } // Otherwise show the message to the player that triggered us. else if ( pActivator && pActivator->IsNetClient() ) { +#ifdef MAPBASE + UTIL_HudMessage( ToBasePlayer( pActivator ), m_textParms, MessageGet(), STRING(m_strFont), m_bAutobreak ); +#else UTIL_HudMessage( ToBasePlayer( pActivator ), m_textParms, MessageGet() ); +#endif } } } +#ifdef MAPBASE +void CGameText::InputSetText( inputdata_t &inputdata ) +{ + SetText( inputdata.value.String() ); +} + +void CGameText::SetText( const char* pszStr ) +{ + // Replace /n with \n + if (Q_strstr( pszStr, "/n" )) + { + CUtlStringList vecLines; + Q_SplitString( pszStr, "/n", vecLines ); + + char szMsg[512]; + Q_strncpy( szMsg, vecLines[0], sizeof( szMsg ) ); + + for (int i = 1; i < vecLines.Count(); i++) + { + Q_snprintf( szMsg, sizeof( szMsg ), "%s\n%s", szMsg, vecLines[i] ); + } + m_iszMessage = AllocPooledString( szMsg ); + } + else + { + m_iszMessage = AllocPooledString( pszStr ); + } +} +#endif + /* TODO: Replace with an entity I/O version // diff --git a/mp/src/game/server/message_entity.cpp b/mp/src/game/server/message_entity.cpp index e5bb114b..903947c1 100644 --- a/mp/src/game/server/message_entity.cpp +++ b/mp/src/game/server/message_entity.cpp @@ -17,6 +17,12 @@ #include "vstdlib/random.h" #include "engine/IEngineSound.h" #include "game.h" +#ifdef MAPBASE +#include +#include +#include "utlbuffer.h" +#include "saverestore_utlvector.h" +#endif #include "player.h" #include "entitylist.h" @@ -38,12 +44,18 @@ public: void Spawn( void ); void Activate( void ); void Think( void ); +#ifdef MAPBASE + virtual +#endif void DrawOverlays(void); virtual void UpdateOnRemove(); void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + virtual void InputSetMessage( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); @@ -68,6 +80,9 @@ BEGIN_DATADESC( CMessageEntity ) // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetMessage", InputSetMessage ), +#endif END_DATADESC() @@ -172,6 +187,19 @@ void CMessageEntity::InputDisable( inputdata_t &inputdata ) m_bEnabled = false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMessageEntity::InputSetMessage( inputdata_t &inputdata ) +{ + char newmessage[256]; + Q_strncpy(newmessage, inputdata.value.String(), sizeof(newmessage)); + + m_messageText = AllocPooledString(newmessage); +} +#endif + // This is a hack to make point_message stuff appear in developer 0 release builds // for now void DrawMessageEntities() @@ -189,3 +217,124 @@ void DrawMessageEntities() me->DrawOverlays(); } } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CMessageEntityLocalized : public CMessageEntity +{ + DECLARE_CLASS( CMessageEntityLocalized, CMessageEntity ); + +public: + bool KeyValue(const char *szKeyName, const char *szValue); + void SetMessage(const char *szValue); + void DrawOverlays(void); + void InputSetMessage( inputdata_t &inputdata ); + + CUtlVector m_messageLines; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( point_message_localized, CMessageEntityLocalized ); + +BEGIN_DATADESC( CMessageEntityLocalized ) + + DEFINE_UTLVECTOR( m_messageLines, FIELD_STRING ), + + //DEFINE_INPUTFUNC( FIELD_STRING, "SetMessage", InputSetMessage ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CMessageEntityLocalized::KeyValue(const char *szKeyName, const char *szValue) +{ + if (FStrEq(szKeyName, "message")) + { + SetMessage(szValue); + return true; + } + + return BaseClass::KeyValue(szKeyName, szValue); +} + +// I would use "\\n", but Hammer doesn't let you use back slashes. +#define CONVERSION_CHAR "/n" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMessageEntityLocalized::SetMessage(const char *szValue) +{ + // Find a localization token matching this string + wchar_t *pszMessage = g_pVGuiLocalize->Find(szValue); + + // If this is a localized string, convert it back to char. + // If it isn't, just copy it right into this. + char szBackToChar[256]; + if (pszMessage) + g_pVGuiLocalize->ConvertUnicodeToANSI(pszMessage, szBackToChar, sizeof(szBackToChar)); + else + Q_strncpy(szBackToChar, szValue, sizeof(szBackToChar)); + + // szTemp is used to turn \n from localized strings into /n. + char szTemp[256]; + if (Q_strstr(szBackToChar, "\n")) + { + char *token = strtok(szBackToChar, "\n"); + while (token) + { + Q_snprintf(szTemp, sizeof(szTemp), "%s%s%s", szTemp, token, CONVERSION_CHAR); + token = strtok(NULL, "\n"); + } + } + else + { + Q_strncpy(szTemp, szBackToChar, sizeof(szTemp)); + } + + m_messageLines.RemoveAll(); + + CUtlStringList vecLines; + Q_SplitString(szTemp, CONVERSION_CHAR, vecLines); + FOR_EACH_VEC( vecLines, i ) + { + m_messageLines.AddToTail( AllocPooledString(vecLines[i]) ); + } + + vecLines.PurgeAndDeleteElements(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMessageEntityLocalized::InputSetMessage( inputdata_t &inputdata ) +{ + SetMessage(inputdata.value.String()); +} + +//------------------------------------------- +//------------------------------------------- +void CMessageEntityLocalized::DrawOverlays(void) +{ + if ( !m_drawText ) + return; + + if ( m_bDeveloperOnly && !g_pDeveloper->GetInt() ) + return; + + if ( !m_bEnabled ) + return; + + // display text if they are within range + int offset = 0; + FOR_EACH_VEC( m_messageLines, i ) + { + EntityText( offset, STRING(m_messageLines[i]), 0 ); + offset++; + } +} +#endif diff --git a/mp/src/game/server/monstermaker.cpp b/mp/src/game/server/monstermaker.cpp index 98f1e02f..5d56eabf 100644 --- a/mp/src/game/server/monstermaker.cpp +++ b/mp/src/game/server/monstermaker.cpp @@ -18,6 +18,8 @@ #include "IEffects.h" #include "props.h" +#include "point_template.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -114,6 +116,8 @@ END_DATADESC() //----------------------------------------------------------------------------- void CBaseNPCMaker::Spawn( void ) { + ScriptInstallPreSpawnHook(); + SetSolid( SOLID_NONE ); m_nLiveChildren = 0; Precache(); @@ -830,6 +834,12 @@ void CTemplateNPCMaker::MakeNPC( void ) pent->SetAbsAngles( angles ); } + if ( !ScriptPreInstanceSpawn( &m_ScriptScope, pEntity, m_iszTemplateData ) ) + { + UTIL_RemoveImmediate( pEntity ); + return; + } + m_OnSpawnNPC.Set( pEntity, pEntity, this ); if ( m_spawnflags & SF_NPCMAKER_FADE ) @@ -867,6 +877,8 @@ void CTemplateNPCMaker::MakeNPC( void ) SetUse( NULL ); } } + + ScriptPostSpawn( &m_ScriptScope, &pEntity, 1 ); } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/npc_talker.cpp b/mp/src/game/server/npc_talker.cpp index a04c9ff3..ebb94583 100644 --- a/mp/src/game/server/npc_talker.cpp +++ b/mp/src/game/server/npc_talker.cpp @@ -373,7 +373,11 @@ void CNPCSimpleTalker::AlertFriends( CBaseEntity *pKiller ) } else { +#ifdef MAPBASE + if( IRelationType(pKiller) <= D_FR) +#else if( IRelationType(pKiller) == D_HT) +#endif { // Killed by an enemy!!! CNPCSimpleTalker *pAlly = (CNPCSimpleTalker *)pNPC; diff --git a/mp/src/game/server/npc_vehicledriver.cpp b/mp/src/game/server/npc_vehicledriver.cpp index 45da78dd..8e38d769 100644 --- a/mp/src/game/server/npc_vehicledriver.cpp +++ b/mp/src/game/server/npc_vehicledriver.cpp @@ -721,7 +721,13 @@ bool CNPC_VehicleDriver::OverridePathMove( float flInterval ) // Have we reached our target? See if we've passed the current waypoint's plane. Vector vecAbsMins, vecAbsMaxs; +#ifdef MAPBASE + vecAbsMins = m_hVehicleEntity->CollisionProp()->OBBMins(); + vecAbsMaxs = m_hVehicleEntity->CollisionProp()->OBBMaxs(); + m_hVehicleEntity->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); +#else CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); +#endif if ( BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pCurrentWaypoint->planeWaypoint ) == 3 ) { if ( WaypointReached() ) diff --git a/mp/src/game/server/particle_system.cpp b/mp/src/game/server/particle_system.cpp index 33fcd322..6ec0cb96 100644 --- a/mp/src/game/server/particle_system.cpp +++ b/mp/src/game/server/particle_system.cpp @@ -25,6 +25,9 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE(CParticleSystem, DT_ParticleSystem) SendPropInt( SENDINFO(m_iEffectIndex), MAX_PARTICLESYSTEMS_STRING_BITS, SPROP_UNSIGNED ), SendPropBool( SENDINFO(m_bActive) ), +#ifdef MAPBASE + SendPropBool( SENDINFO(m_bDestroyImmediately) ), +#endif SendPropFloat( SENDINFO(m_flStartTime) ), SendPropArray3( SENDINFO_ARRAY3(m_hControlPointEnts), SendPropEHandle( SENDINFO_ARRAY(m_hControlPointEnts) ) ), @@ -36,6 +39,9 @@ BEGIN_DATADESC( CParticleSystem ) DEFINE_KEYFIELD( m_bStartActive, FIELD_BOOLEAN, "start_active" ), DEFINE_KEYFIELD( m_bWeatherEffect, FIELD_BOOLEAN, "flag_as_weather" ), DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_FIELD( m_bDestroyImmediately, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_flStartTime, FIELD_TIME ), DEFINE_KEYFIELD( m_iszEffectName, FIELD_STRING, "effect_name" ), //DEFINE_FIELD( m_iEffectIndex, FIELD_INTEGER ), // Don't save. Refind after loading. @@ -116,6 +122,9 @@ BEGIN_DATADESC( CParticleSystem ) DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStart ), DEFINE_INPUTFUNC( FIELD_VOID, "Stop", InputStop ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "DestroyImmediately", InputDestroyImmediately ), +#endif DEFINE_THINKFUNC( StartParticleSystemThink ), @@ -199,6 +208,9 @@ void CParticleSystem::StartParticleSystem( void ) { m_flStartTime = gpGlobals->curtime; m_bActive = true; +#ifdef MAPBASE + m_bDestroyImmediately = false; +#endif // Setup our control points at this time (in case our targets weren't around at spawn time) ReadControlPointEnts(); @@ -229,6 +241,17 @@ void CParticleSystem::InputStop( inputdata_t &inputdata ) StopParticleSystem(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CParticleSystem::InputDestroyImmediately( inputdata_t &inputdata ) +{ + m_bDestroyImmediately = true; + StopParticleSystem(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Find each entity referred to by m_iszControlPointNames and // resolve it into the corresponding slot in m_hControlPointEnts diff --git a/mp/src/game/server/particle_system.h b/mp/src/game/server/particle_system.h index ecf758c2..6ce9a9c9 100644 --- a/mp/src/game/server/particle_system.h +++ b/mp/src/game/server/particle_system.h @@ -34,6 +34,9 @@ public: void InputStart( inputdata_t &inputdata ); void InputStop( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDestroyImmediately( inputdata_t &inputdata ); +#endif void StartParticleSystemThink( void ); enum { kMAXCONTROLPOINTS = 63 }; ///< actually one less than the total number of cpoints since 0 is assumed to be me @@ -47,6 +50,9 @@ protected: string_t m_iszEffectName; CNetworkVar( bool, m_bActive ); +#ifdef MAPBASE + CNetworkVar( bool, m_bDestroyImmediately ); +#endif CNetworkVar( int, m_iEffectIndex ) CNetworkVar( float, m_flStartTime ); // Time at which this effect was started. This is used after restoring an active effect. diff --git a/mp/src/game/server/pathtrack.cpp b/mp/src/game/server/pathtrack.cpp index 539a6e03..2a77666d 100644 --- a/mp/src/game/server/pathtrack.cpp +++ b/mp/src/game/server/pathtrack.cpp @@ -17,9 +17,15 @@ //----------------------------------------------------------------------------- BEGIN_DATADESC( CPathTrack ) +#ifdef MAPBASE + DEFINE_FIELD( m_pnext, FIELD_EHANDLE ), + DEFINE_FIELD( m_pprevious, FIELD_EHANDLE ), + DEFINE_FIELD( m_paltpath, FIELD_EHANDLE ), +#else DEFINE_FIELD( m_pnext, FIELD_CLASSPTR ), DEFINE_FIELD( m_pprevious, FIELD_CLASSPTR ), DEFINE_FIELD( m_paltpath, FIELD_CLASSPTR ), +#endif DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ), DEFINE_FIELD( m_length, FIELD_FLOAT ), diff --git a/mp/src/game/server/physconstraint.cpp b/mp/src/game/server/physconstraint.cpp index 8fa25cdd..467c7cfb 100644 --- a/mp/src/game/server/physconstraint.cpp +++ b/mp/src/game/server/physconstraint.cpp @@ -569,6 +569,8 @@ void CPhysConstraint::GetConstraintObjects( hl_constraint_info_t &info ) // Missing one object, assume the world instead if ( info.pObjects[0] == NULL && info.pObjects[1] ) { + // This brokens hanging lamps in hl2mp +#if !defined ( HL2MP ) if ( Q_strlen(STRING(m_nameAttach1)) ) { Warning("Bogus constraint %s (attaches ENTITY NOT FOUND:%s to %s)\n", GetDebugName(), STRING(m_nameAttach1), STRING(m_nameAttach2)); @@ -577,11 +579,15 @@ void CPhysConstraint::GetConstraintObjects( hl_constraint_info_t &info ) return; #endif // HL2_EPISODIC } +#endif info.pObjects[0] = g_PhysWorldObject; info.massScale[0] = info.massScale[1] = 1.0f; // no mass scale on world constraint + } else if ( info.pObjects[0] && !info.pObjects[1] ) { + // This brokens hanging lamps in hl2mp +#if !defined ( HL2MP ) if ( Q_strlen(STRING(m_nameAttach2)) ) { Warning("Bogus constraint %s (attaches %s to ENTITY NOT FOUND:%s)\n", GetDebugName(), STRING(m_nameAttach1), STRING(m_nameAttach2)); @@ -590,6 +596,7 @@ void CPhysConstraint::GetConstraintObjects( hl_constraint_info_t &info ) return; #endif // HL2_EPISODIC } +#endif info.pObjects[1] = info.pObjects[0]; info.pObjects[0] = g_PhysWorldObject; // Try to make the world object consistently object0 for ease of implementation info.massScale[0] = info.massScale[1] = 1.0f; // no mass scale on world constraint @@ -1038,6 +1045,16 @@ public: for ( int i = 0; i < 2; i++ ) { info.pObjects[i]->WorldToLocal( &ballsocket.constraintPosition[i], GetAbsOrigin() ); + // HACKHACK - the mapper forgot to put in some sane physics damping + float damping, adamping; + info.pObjects[i]->GetDamping(&damping, &adamping); + if (damping < .2f) { + damping = .2f; + } + if (adamping < .2f) { + adamping = .2f; + } + info.pObjects[i]->SetDamping(&damping, &adamping); } GetBreakParams( ballsocket.constraint, info ); ballsocket.constraint.torqueLimit = 0; diff --git a/mp/src/game/server/physics.cpp b/mp/src/game/server/physics.cpp index bec86f5f..3e2ab69c 100644 --- a/mp/src/game/server/physics.cpp +++ b/mp/src/game/server/physics.cpp @@ -514,7 +514,12 @@ int CCollisionEvent::ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pOb if ( pEntity0->edict() && pEntity1->edict() ) { // don't collide with your owner +#ifdef MAPBASE + if ( (pEntity0->GetOwnerEntity() == pEntity1 && !pEntity0->IsSolidFlagSet(FSOLID_COLLIDE_WITH_OWNER)) + || (pEntity1->GetOwnerEntity() == pEntity0 && !pEntity1->IsSolidFlagSet(FSOLID_COLLIDE_WITH_OWNER)) ) +#else if ( pEntity0->GetOwnerEntity() == pEntity1 || pEntity1->GetOwnerEntity() == pEntity0 ) +#endif return 0; } @@ -719,7 +724,7 @@ bool CCollisionEvent::ShouldFreezeContacts( IPhysicsObject **pObjectList, int ob { if ( m_lastTickFrictionError > gpGlobals->tickcount || m_lastTickFrictionError < (gpGlobals->tickcount-1) ) { - DevWarning("Performance Warning: large friction system (%d objects)!!!\n", objectCount ); + CGWarning( 1, CON_GROUP_PHYSICS, "Performance Warning: large friction system (%d objects)!!!\n", objectCount ); #if _DEBUG for ( int i = 0; i < objectCount; i++ ) { @@ -992,7 +997,7 @@ int CCollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObje { if ( pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL ) { - DevMsg(2, "Solving ragdoll self penetration! %s (%s) (%d v %d)\n", pObj0->GetName(), pEntity0->GetDebugName(), pObj0->GetGameIndex(), pObj1->GetGameIndex() ); + CGMsg( 2, CON_GROUP_PHYSICS, "Solving ragdoll self penetration! %s (%s) (%d v %d)\n", pObj0->GetName(), pEntity0->GetDebugName(), pObj0->GetGameIndex(), pObj1->GetGameIndex() ); ragdoll_t *pRagdoll = Ragdoll_GetRagdoll( pEntity0 ); pRagdoll->pGroup->SolvePenetration( pObj0, pObj1 ); return false; @@ -1025,11 +1030,11 @@ int CCollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObje { int index0 = physcollision->CollideIndex( pObj0->GetCollide() ); int index1 = physcollision->CollideIndex( pObj1->GetCollide() ); - DevMsg(1, "***Inter-penetration on %s (%d & %d) (%.0f, %.0f)\n", pName1?pName1:"(null)", index0, index1, gpGlobals->curtime, eventTime ); + CGMsg( 1, CON_GROUP_PHYSICS, "***Inter-penetration on %s (%d & %d) (%.0f, %.0f)\n", pName1?pName1:"(null)", index0, index1, gpGlobals->curtime, eventTime ); } else { - DevMsg(1, "***Inter-penetration between %s(%s) AND %s(%s) (%.0f, %.0f)\n", pName1?pName1:"(null)", pEntity0->GetDebugName(), pName2?pName2:"(null)", pEntity1->GetDebugName(), gpGlobals->curtime, eventTime ); + CGMsg( 1, CON_GROUP_PHYSICS, "***Inter-penetration between %s(%s) AND %s(%s) (%.0f, %.0f)\n", pName1?pName1:"(null)", pEntity0->GetDebugName(), pName2?pName2:"(null)", pEntity1->GetDebugName(), gpGlobals->curtime, eventTime ); } } #endif @@ -1148,13 +1153,13 @@ void PhysSolidOverride( solid_t &solid, string_t overrideScript ) // suck out the comma delimited tokens and turn them into quoted key/values char szToken[256]; - const char *pStr = nexttoken(szToken, STRING(overrideScript), ','); + const char *pStr = nexttoken(szToken, STRING(overrideScript), ',', sizeof(szToken)); while ( szToken[0] != 0 ) { Q_strncat( pTmpString, "\"", sizeof(pTmpString), COPY_ALL_CHARACTERS ); Q_strncat( pTmpString, szToken, sizeof(pTmpString), COPY_ALL_CHARACTERS ); Q_strncat( pTmpString, "\" ", sizeof(pTmpString), COPY_ALL_CHARACTERS ); - pStr = nexttoken(szToken, pStr, ','); + pStr = nexttoken(szToken, pStr, ',', sizeof(szToken)); } // terminate the script Q_strncat( pTmpString, "}", sizeof(pTmpString), COPY_ALL_CHARACTERS ); @@ -1328,8 +1333,8 @@ CON_COMMAND_F(surfaceprop, "Reports the surface properties at the cursor", FCVAR Vector vecVelocity = tr.startpos - tr.endpos; int length = vecVelocity.Length(); - Msg("Hit surface \"%s\" (entity %s, model \"%s\" %s), texture \"%s\"\n", physprops->GetPropName( tr.surface.surfaceProps ), tr.m_pEnt->GetClassname(), pModelName, modelStuff.Access(), tr.surface.name); - Msg("Distance to surface: %d\n", length ); + CGMsg( 0, CON_GROUP_PHYSICS, "Hit surface \"%s\" (entity %s, model \"%s\" %s), texture \"%s\"\n", physprops->GetPropName( tr.surface.surfaceProps ), tr.m_pEnt->GetClassname(), pModelName, modelStuff.Access(), tr.surface.name ); + CGMsg( 0, CON_GROUP_PHYSICS, "Distance to surface: %d\n", length ); } } @@ -1337,12 +1342,12 @@ static void OutputVPhysicsDebugInfo( CBaseEntity *pEntity ) { if ( pEntity ) { - Msg("Entity %s (%s) %s Collision Group %d\n", pEntity->GetClassname(), pEntity->GetDebugName(), pEntity->IsNavIgnored() ? "NAV IGNORE" : "", pEntity->GetCollisionGroup() ); + CGMsg( 0, CON_GROUP_PHYSICS, "Entity %s (%s) %s Collision Group %d\n", pEntity->GetClassname(), pEntity->GetDebugName(), pEntity->IsNavIgnored() ? "NAV IGNORE" : "", pEntity->GetCollisionGroup() ); CUtlVector list; g_Collisions.GetListOfPenetratingEntities( pEntity, list ); for ( int i = 0; i < list.Count(); i++ ) { - Msg(" penetration with entity %s (%s)\n", list[i]->GetDebugName(), STRING(list[i]->GetModelName()) ); + CGMsg( 0, CON_GROUP_PHYSICS, " penetration with entity %s (%s)\n", list[i]->GetDebugName(), STRING( list[i]->GetModelName() ) ); } IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; @@ -1353,7 +1358,7 @@ static void OutputVPhysicsDebugInfo( CBaseEntity *pEntity ) { for ( int i = 0; i < physCount; i++ ) { - Msg("Object %d (of %d) =========================\n", i+1, physCount ); + CGMsg( 0, CON_GROUP_PHYSICS, "Object %d (of %d) =========================\n", i + 1, physCount ); pList[i]->OutputDebugInfo(); } } @@ -1491,7 +1496,7 @@ static void DebugConstraints( CBaseEntity *pEntity ) pModel1 = STRING(pAttach[1]->GetModelName()); index1 = pAttachVPhysics[1]->GetGameIndex(); } - Msg("**********************\n%s connects %s(%s:%d) to %s(%s:%d)\n", constraints[i]->GetClassname(), pName0, pModel0, index0, pName1, pModel1, index1 ); + CGMsg( 0, CON_GROUP_PHYSICS, "**********************\n%s connects %s(%s:%d) to %s(%s:%d)\n", constraints[i]->GetClassname(), pName0, pModel0, index0, pName1, pModel1, index1 ); DebugConstraint(constraints[i]); constraints[i]->m_debugOverlays |= OVERLAY_BBOX_BIT | OVERLAY_TEXT_BIT; } @@ -1637,7 +1642,7 @@ CON_COMMAND( physics_budget, "Times the cost of each active object" ) for ( i = 0; i < ents.Count(); i++ ) { float fraction = times[i] / totalTime; - Msg( "%s (%s): %.3fms (%.3f%%) @ %s\n", ents[i]->GetClassname(), ents[i]->GetDebugName(), fraction * totalTime * 1000.0f, fraction * 100.0f, VecToString(ents[i]->GetAbsOrigin()) ); + CGMsg( 0, CON_GROUP_PHYSICS, "%s (%s): %.3fms (%.3f%%) @ %s\n", ents[i]->GetClassname(), ents[i]->GetDebugName(), fraction * totalTime * 1000.0f, fraction * 100.0f, VecToString( ents[i]->GetAbsOrigin() ) ); } g_Collisions.BufferTouchEvents( false ); } @@ -1680,7 +1685,7 @@ void PhysFrame( float deltaTime ) if ( deltaTime > 1.0f || deltaTime < 0.0f ) { deltaTime = 0; - Msg( "Reset physics clock\n" ); + CGMsg( 0, CON_GROUP_PHYSICS, "Reset physics clock\n" ); } else if ( deltaTime > 0.1f ) // limit incoming time to 100ms { @@ -1738,7 +1743,7 @@ void PhysFrame( float deltaTime ) CBaseEntity *pEntity = pItem->hEnt.Get(); if ( !pEntity ) { - Msg( "Dangling pointer to physics entity!!!\n" ); + CGMsg( 0, CON_GROUP_PHYSICS, "Dangling pointer to physics entity!!!\n" ); continue; } @@ -1760,7 +1765,7 @@ void PhysFrame( float deltaTime ) g_PhysAverageSimTime += (simRealTime * 0.2); if ( lastObjectCount != 0 || activeCount != 0 ) { - Msg( "Physics: %3d objects, %4.1fms / AVG: %4.1fms\n", activeCount, simRealTime * 1000, g_PhysAverageSimTime * 1000 ); + CGMsg( 0, CON_GROUP_PHYSICS, "Physics: %3d objects, %4.1fms / AVG: %4.1fms\n", activeCount, simRealTime * 1000, g_PhysAverageSimTime * 1000 ); } lastObjectCount = activeCount; @@ -1924,7 +1929,7 @@ void PhysForceEntityToSleep( CBaseEntity *pEntity, IPhysicsObject *pObject ) if ( !pObject || !pObject->IsMoveable() ) return; - DevMsg(2, "Putting entity to sleep: %s\n", pEntity->GetClassname() ); + CGMsg( 2, CON_GROUP_PHYSICS, "Putting entity to sleep: %s\n", pEntity->GetClassname() ); MEM_ALLOC_CREDIT(); IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; int physCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); @@ -2019,7 +2024,7 @@ void CCollisionEvent::FlushQueuedOperations() // testing, if this assert fires it proves we've fixed the crash // after that the assert + warning can safely be removed Assert(0); - Warning("Physics queue not empty, error!\n"); + CGWarning( 0, CON_GROUP_PHYSICS, "Physics queue not empty, error!\n" ); loopCount++; UpdateTouchEvents(); UpdateDamageEvents(); @@ -2768,7 +2773,7 @@ void PhysCallbackDamage( CBaseEntity *pEntity, const CTakeDamageInfo &info ) g_Collisions.AddDamageEvent( pEntity, info, pInflictorPhysics, false, vec3_origin, vec3_origin ); if ( pEntity && info.GetInflictor() ) { - DevMsg( 2, "Warning: Physics damage event with no recovery info!\nObjects: %s, %s\n", pEntity->GetClassname(), info.GetInflictor()->GetClassname() ); + CGMsg( 2, CON_GROUP_PHYSICS, "Warning: Physics damage event with no recovery info!\nObjects: %s, %s\n", pEntity->GetClassname(), info.GetInflictor()->GetClassname() ); } } else @@ -2827,10 +2832,10 @@ IPhysicsObject *FindPhysicsObjectByName( const char *pName, CBaseEntity *pErrorE { const char *pErrorName = pErrorEntity ? pErrorEntity->GetClassname() : "Unknown"; Vector origin = pErrorEntity ? pErrorEntity->GetAbsOrigin() : vec3_origin; - DevWarning("entity %s at %s has physics attachment to more than one entity with the name %s!!!\n", pErrorName, VecToString(origin), pName ); + CGWarning( 1, CON_GROUP_PHYSICS, "entity %s at %s has physics attachment to more than one entity with the name %s!!!\n", pErrorName, VecToString( origin ), pName ); while ( ( pEntity = gEntList.FindEntityByName( pEntity, pName ) ) != NULL ) { - DevWarning("Found %s\n", pEntity->GetClassname() ); + CGWarning( 1, CON_GROUP_PHYSICS, "Found %s\n", pEntity->GetClassname() ); } break; @@ -2848,7 +2853,7 @@ void CC_AirDensity( const CCommand &args ) if ( args.ArgC() < 2 ) { - Msg( "air_density \nCurrent air density is %.2f\n", physenv->GetAirDensity() ); + CGMsg( 0, CON_GROUP_PHYSICS, "air_density \nCurrent air density is %.2f\n", physenv->GetAirDensity() ); } else { diff --git a/mp/src/game/server/physics_impact_damage.cpp b/mp/src/game/server/physics_impact_damage.cpp index 164568e2..312b8a1e 100644 --- a/mp/src/game/server/physics_impact_damage.cpp +++ b/mp/src/game/server/physics_impact_damage.cpp @@ -335,15 +335,32 @@ float CalculatePhysicsImpactDamage( int index, gamevcollisionevent_t *pEvent, co if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { - if ( gpGlobals->maxClients == 1 ) + // if the player is holding the object, use its real mass (player holding reduced the mass) + CBasePlayer *pPlayer = NULL; + + if ( 1 == gpGlobals->maxClients ) { - // if the player is holding the object, use it's real mass (player holding reduced the mass) - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); - if ( pPlayer ) + pPlayer = UTIL_GetLocalPlayer(); + } + else + { + // See which MP player is holding the physics object and then use that player to get the real mass of the object. + // This is ugly but better than having linkage between an object and its "holding" player. + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { - otherMass = pPlayer->GetHeldObjectMass( pEvent->pObjects[otherIndex] ); + CBasePlayer *tempPlayer = UTIL_PlayerByIndex( i ); + if ( tempPlayer && pEvent->pEntities[index] == tempPlayer->GetHeldObject() ) + { + pPlayer = tempPlayer; + break; + } } } + + if ( pPlayer ) + { + otherMass = pPlayer->GetHeldObjectMass( pEvent->pObjects[otherIndex] ); + } } // NOTE: sum the mass of each object in this system for the purpose of damage @@ -438,16 +455,23 @@ float CalculatePhysicsImpactDamage( int index, gamevcollisionevent_t *pEvent, co } else if ( pEvent->pObjects[index]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { - if ( gpGlobals->maxClients == 1 ) + // if the player is holding the object, use it's real mass (player holding reduced the mass) + CBasePlayer *pPlayer = NULL; + if ( 1 == gpGlobals->maxClients ) { - // if the player is holding the object, use it's real mass (player holding reduced the mass) - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); - if ( pPlayer ) + pPlayer = UTIL_GetLocalPlayer(); + } + else + { + // See which MP player is holding the physics object and then use that player to get the real mass of the object. + // This is ugly but better than having linkage between an object and its "holding" player. + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { - float mass = pPlayer->GetHeldObjectMass( pEvent->pObjects[index] ); - if ( mass > 0 ) + CBasePlayer *tempPlayer = UTIL_PlayerByIndex( i ); + if ( tempPlayer && pEvent->pEntities[index] == tempPlayer->GetHeldObject() ) { - invMass = 1.0f / mass; + pPlayer = tempPlayer; + break; } } } diff --git a/mp/src/game/server/physics_prop_ragdoll.cpp b/mp/src/game/server/physics_prop_ragdoll.cpp index 72635ea8..0844f7b7 100644 --- a/mp/src/game/server/physics_prop_ragdoll.cpp +++ b/mp/src/game/server/physics_prop_ragdoll.cpp @@ -20,10 +20,18 @@ #include "AI_Criteria.h" #include "ragdoll_shared.h" #include "hierarchy.h" +#ifdef MAPBASE +#include "decals.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +ConVar ragdoll_autointeractions("ragdoll_autointeractions", "1", FCVAR_NONE, "Controls whether we should rely on hardcoded keyvalues or automatic flesh checks for ragdoll physgun interactions."); +#define IsBody() VPhysicsIsFlesh() +#endif + //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- @@ -83,9 +91,17 @@ BEGIN_DATADESC(CRagdollProp) DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "StartRagdollBoogie", InputStartRadgollBoogie ), +#else DEFINE_INPUTFUNC( FIELD_VOID, "StartRagdollBoogie", InputStartRadgollBoogie ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "EnableMotion", InputEnableMotion ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableMotion", InputDisableMotion ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), + DEFINE_INPUTFUNC( FIELD_VOID, "Sleep", InputSleep ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ), DEFINE_INPUTFUNC( FIELD_FLOAT, "FadeAndRemove", InputFadeAndRemove ), @@ -143,6 +159,19 @@ BEGIN_DATADESC(CRagdollProp) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CRagdollProp, CBaseAnimating, "Ragdoll physics prop." ) + + DEFINE_SCRIPTFUNC_NAMED( GetSourceClassNameAsCStr, "GetSourceClassName", "Gets the ragdoll's source classname." ) + DEFINE_SCRIPTFUNC( SetSourceClassName, "Sets the ragdoll's source classname." ) + DEFINE_SCRIPTFUNC( HasPhysgunInteraction, "Checks if the ragdoll has the specified interaction." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObject, "GetRagdollObject", "Gets the ragdoll object of the specified index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObjectCount, "GetRagdollObjectCount", "Gets the number of ragdoll objects on this ragdoll." ) + +END_SCRIPTDESC() +#endif + //----------------------------------------------------------------------------- // Disable auto fading under dx7 or when level fades are specified //----------------------------------------------------------------------------- @@ -359,7 +388,11 @@ void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r } m_bHasBeenPhysgunned = true; +#ifdef MAPBASE + if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" ) ) +#else if( HasPhysgunInteraction( "onpickup", "boogie" ) ) +#endif { if ( reason == PUNTED_BY_CANNON ) { @@ -397,7 +430,11 @@ void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reaso m_hPhysicsAttacker = pPhysGunUser; m_flLastPhysicsInfluenceTime = gpGlobals->curtime; +#ifdef MAPBASE + if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" ) ) +#else if( HasPhysgunInteraction( "onpickup", "boogie" ) ) +#endif { CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 3.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL ); } @@ -416,7 +453,11 @@ void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reaso if ( Reason != LAUNCHED_BY_CANNON ) return; +#ifdef MAPBASE + if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) ) +#else if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) ) +#endif { Vector vecAverageCenter( 0, 0, 0 ); @@ -615,8 +656,21 @@ void CRagdollProp::HandleFirstCollisionInteractions( int index, gamevcollisionev } } +#ifdef MAPBASE + int iVPhysicsFlesh = VPhysicsGetFlesh(); + bool bRagdollAutoInt = (ragdoll_autointeractions.GetBool() == true && iVPhysicsFlesh); + bool bAlienBloodSplat = HasPhysgunInteraction( "onfirstimpact", "alienbloodsplat" ); + if (bRagdollAutoInt && !bAlienBloodSplat) + { + // Alien blood? + bAlienBloodSplat = (iVPhysicsFlesh == CHAR_TEX_ALIENFLESH || iVPhysicsFlesh == CHAR_TEX_ANTLION); + } + + if( bRagdollAutoInt || bAlienBloodSplat || HasPhysgunInteraction( "onfirstimpact", "bloodsplat" ) ) +#else bool bAlienBloodSplat = HasPhysgunInteraction( "onfirstimpact", "alienbloodsplat" ); if( bAlienBloodSplat || HasPhysgunInteraction( "onfirstimpact", "bloodsplat" ) ) +#endif { IPhysicsObject *pObj = VPhysicsGetObject(); @@ -646,7 +700,11 @@ void CRagdollProp::ClearFlagsThink( void ) //----------------------------------------------------------------------------- AngularImpulse CRagdollProp::PhysGunLaunchAngularImpulse() { +#ifdef MAPBASE + if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) ) +#else if( HasPhysgunInteraction( "onlaunch", "spin_zaxis" ) ) +#endif { // Don't add in random angular impulse if this object is supposed to spin in a specific way. AngularImpulse ang( 0, 0, 0 ); @@ -706,14 +764,14 @@ void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const if ( m_anglesOverrideString != NULL_STRING && Q_strlen(m_anglesOverrideString.ToCStr()) > 0 ) { char szToken[2048]; - const char *pStr = nexttoken(szToken, STRING(m_anglesOverrideString), ','); + const char *pStr = nexttoken(szToken, STRING(m_anglesOverrideString), ',', sizeof(szToken)); // anglesOverride is index,angles,index,angles (e.g. "1, 22.5 123.0 0.0, 2, 0 0 0, 3, 0 0 180.0") while ( szToken[0] != 0 ) { int objectIndex = atoi(szToken); // sanity check to make sure this token is an integer Assert( atof(szToken) == ((float)objectIndex) ); - pStr = nexttoken(szToken, pStr, ','); + pStr = nexttoken(szToken, pStr, ',', sizeof(szToken)); Assert( szToken[0] ); if ( objectIndex >= m_ragdoll.listCount ) { @@ -740,7 +798,7 @@ void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const MatrixSetColumn( out, 3, pBoneToWorld[boneIndex] ); element.pObject->SetPositionMatrix( pBoneToWorld[boneIndex], true ); } - pStr = nexttoken(szToken, pStr, ','); + pStr = nexttoken(szToken, pStr, ',', sizeof(szToken)); } } @@ -1069,6 +1127,23 @@ int CRagdollProp::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ) return m_ragdoll.listCount; } +#ifdef MAPBASE +int CRagdollProp::VPhysicsGetFlesh() +{ + IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int count = VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + for ( int i = 0; i < count; i++ ) + { + int material = pList[i]->GetMaterialIndex(); + const surfacedata_t *pSurfaceData = physprops->GetSurfaceData( material ); + // Is flesh ?, don't allow pickup + if ( pSurfaceData->game.material == CHAR_TEX_ANTLION || pSurfaceData->game.material == CHAR_TEX_FLESH || pSurfaceData->game.material == CHAR_TEX_BLOODYFLESH || pSurfaceData->game.material == CHAR_TEX_ALIENFLESH ) + return pSurfaceData->game.material; + } + return 0; +} +#endif + void CRagdollProp::UpdateNetworkDataFromVPhysics( IPhysicsObject *pPhysics, int index ) { Assert(index < m_ragdoll.listCount); @@ -1441,6 +1516,12 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con maxs = pAnimating->CollisionProp()->OBBMaxs(); pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs ); +#ifdef MAPBASE + variant_t variant; + variant.SetEntity(pRagdoll); + pAnimating->FireNamedOutput("OnServerRagdoll", variant, pRagdoll, pAnimating); +#endif + return pRagdoll; } @@ -1683,6 +1764,38 @@ void CRagdollProp::InputDisableMotion( inputdata_t &inputdata ) DisableMotion(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler to start the physics prop simulating. +//----------------------------------------------------------------------------- +void CRagdollProp::InputWake( inputdata_t &inputdata ) +{ + for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll ) + { + IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject; + if ( pPhysicsObject != NULL ) + { + pPhysicsObject->Wake(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler to stop the physics prop simulating. +//----------------------------------------------------------------------------- +void CRagdollProp::InputSleep( inputdata_t &inputdata ) +{ + for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll ) + { + IPhysicsObject *pPhysicsObject = m_ragdoll.list[ iRagdoll ].pObject; + if ( pPhysicsObject != NULL ) + { + pPhysicsObject->Sleep(); + } + } +} +#endif + void CRagdollProp::InputTurnOn( inputdata_t &inputdata ) { RemoveEffects( EF_NODRAW ); @@ -1703,6 +1816,24 @@ void CRagdollProp::InputFadeAndRemove( inputdata_t &inputdata ) FadeOut( 0.0f, flFadeDuration ); } +#ifdef MAPBASE_VSCRIPT +HSCRIPT CRagdollProp::ScriptGetRagdollObject( int iIndex ) +{ + if (iIndex < 0 || iIndex > m_ragdoll.listCount) + { + Warning("%s GetRagdollObject: Index %i not valid (%i objects)\n", GetDebugName(), iIndex, m_ragdoll.listCount); + return NULL; + } + + return g_pScriptVM->RegisterInstance( m_ragdoll.list[iIndex].pObject ); +} + +int CRagdollProp::ScriptGetRagdollObjectCount() +{ + return m_ragdoll.listCount; +} +#endif + void Ragdoll_GetAngleOverrideString( char *pOut, int size, CBaseEntity *pEntity ) { CRagdollProp *pRagdoll = dynamic_cast(pEntity); diff --git a/mp/src/game/server/physics_prop_ragdoll.h b/mp/src/game/server/physics_prop_ragdoll.h index 7c24b47e..46e527c3 100644 --- a/mp/src/game/server/physics_prop_ragdoll.h +++ b/mp/src/game/server/physics_prop_ragdoll.h @@ -22,6 +22,9 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics { DECLARE_CLASS( CRagdollProp, CBaseAnimating ); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: CRagdollProp( void ); @@ -49,6 +52,9 @@ public: virtual void SetupBones( matrix3x4_t *pBoneToWorld, int boneMask ); virtual void VPhysicsUpdate( IPhysicsObject *pPhysics ); virtual int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ); +#ifdef MAPBASE + int VPhysicsGetFlesh(); +#endif virtual int DrawDebugTextOverlays(void); @@ -56,6 +62,9 @@ public: virtual IResponseSystem *GetResponseSystem(); virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set ); void SetSourceClassName( const char *pClassname ); +#ifdef MAPBASE + const char *GetSourceClassNameAsCStr() { return STRING( m_strSourceClassName ); } +#endif // Physics attacker virtual CBasePlayer *HasPhysicsAttacker( float dt ); @@ -101,10 +110,19 @@ public: void InputStartRadgollBoogie( inputdata_t &inputdata ); void InputEnableMotion( inputdata_t &inputdata ); void InputDisableMotion( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputWake( inputdata_t &inputdata ); + void InputSleep( inputdata_t &inputdata ); +#endif void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); void InputFadeAndRemove( inputdata_t &inputdata ); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetRagdollObject( int iIndex ); + int ScriptGetRagdollObjectCount(); +#endif + DECLARE_DATADESC(); protected: diff --git a/mp/src/game/server/physobj.cpp b/mp/src/game/server/physobj.cpp index efaffd07..c486fa80 100644 --- a/mp/src/game/server/physobj.cpp +++ b/mp/src/game/server/physobj.cpp @@ -387,6 +387,9 @@ BEGIN_DATADESC( CPhysBox ) DEFINE_INPUTFUNC( FIELD_VOID, "DisableMotion", InputDisableMotion ), DEFINE_INPUTFUNC( FIELD_VOID, "ForceDrop", InputForceDrop ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableFloating", InputDisableFloating ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetDebris", InputSetDebris ), +#endif // Function pointers DEFINE_ENTITYFUNC( BreakTouch ), @@ -562,6 +565,13 @@ int CPhysBox::ObjectCaps() } } +#ifdef MAPBASE + if ( HasSpawnFlags( SF_PHYSBOX_RADIUS_PICKUP ) ) + { + caps |= FCAP_USE_IN_RADIUS; + } +#endif + return caps; } @@ -689,6 +699,25 @@ void CPhysBox::InputDisableFloating( inputdata_t &inputdata ) PhysEnableFloating( VPhysicsGetObject(), false ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Adds or removes the debris spawnflag. +//----------------------------------------------------------------------------- +void CPhysBox::InputSetDebris( inputdata_t &inputdata ) +{ + if (inputdata.value.Bool()) + { + AddSpawnFlags(SF_PHYSBOX_DEBRIS); + SetCollisionGroup(COLLISION_GROUP_DEBRIS); + } + else + { + RemoveSpawnFlags(SF_PHYSBOX_DEBRIS); + SetCollisionGroup(COLLISION_GROUP_INTERACTIVE); // Is this the default collision group? + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: If we're being held by the player's hand/physgun, force it to drop us //----------------------------------------------------------------------------- @@ -871,6 +900,9 @@ BEGIN_DATADESC( CPhysExplosion ) // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "ExplodeAndRemove", InputExplodeAndRemove ), +#endif // Outputs DEFINE_OUTPUT( m_OnPushedPlayer, "OnPushedPlayer" ), @@ -888,10 +920,19 @@ void CPhysExplosion::Spawn( void ) float CPhysExplosion::GetRadius( void ) { float radius = m_radius; +#ifdef MAPBASE + if ( radius == 0 ) +#else if ( radius <= 0 ) +#endif { // Use the same radius as combat radius = m_damage * 2.5; + +#ifdef MAPBASE + if (radius < 0) + radius *= -1; +#endif } return radius; @@ -924,6 +965,17 @@ void CPhysExplosion::InputExplode( inputdata_t &inputdata ) Explode( inputdata.pActivator, inputdata.pCaller ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPhysExplosion::InputExplodeAndRemove( inputdata_t &inputdata ) +{ + Explode( inputdata.pActivator, inputdata.pCaller ); + UTIL_Remove(this); +} +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -936,6 +988,13 @@ void CPhysExplosion::Explode( CBaseEntity *pActivator, CBaseEntity *pCaller ) falloff = 1.0 / 2.5; +#ifdef MAPBASE + // For negative damage handling + float damage = m_damage; + if (damage < 0) + damage *= -1.0f; +#endif + // iterate on all entities in the vicinity. // I've removed the traceline heuristic from phys explosions. SO right now they will // affect entities through walls. (sjb) @@ -986,7 +1045,11 @@ void CPhysExplosion::Explode( CBaseEntity *pActivator, CBaseEntity *pCaller ) } adjustedDamage = flDist * falloff; +#ifdef MAPBASE + adjustedDamage = damage - adjustedDamage; +#else adjustedDamage = m_damage - adjustedDamage; +#endif if ( adjustedDamage < 1 ) { @@ -994,7 +1057,19 @@ void CPhysExplosion::Explode( CBaseEntity *pActivator, CBaseEntity *pCaller ) } CTakeDamageInfo info( this, this, adjustedDamage, DMG_BLAST ); +#ifdef MAPBASE + // Negative damage handling + Vector vecDir = (vecSpot - vecOrigin); + if (m_damage < 0) + { + vecDir *= -1.0f; + vecOrigin += vecDir; + NDebugOverlay::Cross3D(vecOrigin, 2.0f, 255, 255, 0, true, 1.0f); + } + CalculateExplosiveDamageForce( &info, vecDir, vecOrigin ); +#else CalculateExplosiveDamageForce( &info, (vecSpot - vecOrigin), vecOrigin ); +#endif if ( HasSpawnFlags( SF_PHYSEXPLOSION_PUSH_PLAYER ) ) { @@ -1019,7 +1094,11 @@ void CPhysExplosion::Explode( CBaseEntity *pActivator, CBaseEntity *pCaller ) pEntity->ViewPunch( vecDeltaAngles ); } +#ifdef MAPBASE + Vector vecPush = (vecPushDir*damage*flFalloff*2.0f); +#else Vector vecPush = (vecPushDir*m_damage*flFalloff*2.0f); +#endif if ( pEntity->GetFlags() & FL_BASEVELOCITY ) { vecPush = vecPush + pEntity->GetBaseVelocity(); @@ -1242,6 +1321,16 @@ public: SetMoveType( MOVETYPE_VPHYSICS ); SetSolid( SOLID_VPHYSICS ); m_takedamage = DAMAGE_EVENTS_ONLY; + +#ifdef MAPBASE + // If we don't have an owner entity, it means this wasn't spawned by a phys_convert and this is safe. + if ( !GetOwnerEntity() ) + { + VPhysicsInitNormal( SOLID_VPHYSICS, 0, HasSpawnFlags(SF_PHYSBOX_DEBRIS) ); + if ( HasSpawnFlags(SF_PHYSBOX_ASLEEP) ) + SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + } +#endif } }; @@ -1258,6 +1347,16 @@ public: SetMoveType( MOVETYPE_VPHYSICS ); SetSolid( SOLID_VPHYSICS ); m_takedamage = DAMAGE_EVENTS_ONLY; + +#ifdef MAPBASE + // If we don't have an owner entity, it means this wasn't spawned by a phys_convert and this is safe. + if ( !GetOwnerEntity() ) + { + VPhysicsInitNormal( SOLID_VPHYSICS, 0, HasSpawnFlags(SF_PHYSPROP_DEBRIS) ); + if ( HasSpawnFlags(SF_PHYSPROP_START_ASLEEP) ) + SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + } +#endif } int ObjectCaps() @@ -1333,6 +1432,10 @@ static CBaseEntity *CreateSimplePhysicsObject( CBaseEntity *pEntity, bool create pPhysEntity->KeyValue( "model", STRING(pEntity->GetModelName()) ); pPhysEntity->SetAbsOrigin( pEntity->GetAbsOrigin() ); pPhysEntity->SetAbsAngles( pEntity->GetAbsAngles() ); +#ifdef MAPBASE + // So the entity knows it's being spawned by a phys_convert + pPhysEntity->SetOwnerEntity( pEntity ); +#endif pPhysEntity->Spawn(); if ( !TransferPhysicsObject( pEntity, pPhysEntity, !createAsleep ) ) { @@ -1343,6 +1446,36 @@ static CBaseEntity *CreateSimplePhysicsObject( CBaseEntity *pEntity, bool create return pPhysEntity; } +#ifdef MAPBASE +// Creates func_brush and prop_physics instead, because why not? +static CBaseEntity *CreateConventionalPhysicsObject( CBaseEntity *pEntity, bool createAsleep, bool createAsDebris ) +{ + CBaseEntity *pPhysEntity = NULL; + int modelindex = pEntity->GetModelIndex(); + const model_t *model = modelinfo->GetModel( modelindex ); + if ( model && modelinfo->GetModelType(model) == mod_brush ) + { + pPhysEntity = CreateEntityByName( "func_physbox" ); + } + else + { + pPhysEntity = CreateEntityByName( "prop_physics_override" ); + } + + pPhysEntity->KeyValue( "model", STRING(pEntity->GetModelName()) ); + pPhysEntity->SetAbsOrigin( pEntity->GetAbsOrigin() ); + pPhysEntity->SetAbsAngles( pEntity->GetAbsAngles() ); + pPhysEntity->Spawn(); + if ( !TransferPhysicsObject( pEntity, pPhysEntity, !createAsleep ) ) + { + pPhysEntity->VPhysicsInitNormal( SOLID_VPHYSICS, 0, createAsleep ); + if ( createAsDebris ) + pPhysEntity->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + } + return pPhysEntity; +} +#endif + #define SF_CONVERT_ASLEEP 0x0001 #define SF_CONVERT_AS_DEBRIS 0x0002 @@ -1357,11 +1490,22 @@ public: // Input handlers void InputConvertTarget( inputdata_t &inputdata ); +#ifdef MAPBASE + enum + { + CONVERT_ENTITYTYPE_SIMPLE, // simple_physics_prop, simple_physics_brush, etc. + CONVERT_ENTITYTYPE_CONVENTIONAL, // prop_physics, func_physbox, etc. + }; +#endif + DECLARE_DATADESC(); private: string_t m_swapModel; float m_flMassOverride; +#ifdef MAPBASE + int m_iPhysicsEntityType = CONVERT_ENTITYTYPE_SIMPLE; +#endif }; LINK_ENTITY_TO_CLASS( phys_convert, CPhysConvert ); @@ -1370,6 +1514,9 @@ BEGIN_DATADESC( CPhysConvert ) DEFINE_KEYFIELD( m_swapModel, FIELD_STRING, "swapmodel" ), DEFINE_KEYFIELD( m_flMassOverride, FIELD_FLOAT, "massoverride" ), +#ifdef MAPBASE + DEFINE_INPUT( m_iPhysicsEntityType, FIELD_INTEGER, "SetConversionType" ), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "ConvertTarget", InputConvertTarget ), @@ -1432,7 +1579,16 @@ void CPhysConvert::InputConvertTarget( inputdata_t &inputdata ) } // created phys object, now move hierarchy over +#ifdef MAPBASE + CBaseEntity *pPhys; + switch (m_iPhysicsEntityType) + { + case CONVERT_ENTITYTYPE_CONVENTIONAL: pPhys = CreateConventionalPhysicsObject( pEntity, createAsleep, createAsDebris ); break; + default: pPhys = CreateSimplePhysicsObject( pEntity, createAsleep, createAsDebris ); break; + } +#else CBaseEntity *pPhys = CreateSimplePhysicsObject( pEntity, createAsleep, createAsDebris ); +#endif if ( pPhys ) { // Override the mass if specified @@ -1445,6 +1601,22 @@ void CPhysConvert::InputConvertTarget( inputdata_t &inputdata ) } } +#ifdef MAPBASE + pPhys->m_nRenderMode = pEntity->m_nRenderMode; + pPhys->m_nRenderFX = pEntity->m_nRenderFX; + const color32 rclr = pEntity->GetRenderColor(); + pPhys->SetRenderColor(rclr.r, rclr.g, rclr.b, rclr.a); + if (pEntity->GetBaseAnimating() /*&& pPhys->GetBaseAnimating()*/) + { + CBaseAnimating *pEntityAnimating = pEntity->GetBaseAnimating(); + CBaseAnimating *pPhysAnimating = pPhys->GetBaseAnimating(); + + pPhysAnimating->m_nSkin = pEntityAnimating->m_nSkin; + pPhysAnimating->m_nBody = pEntityAnimating->m_nBody; + pPhysAnimating->SetModelScale(pEntityAnimating->GetModelScale()); + } +#endif + pPhys->SetName( pEntity->GetEntityName() ); UTIL_TransferPoseParameters( pEntity, pPhys ); TransferChildren( pEntity, pPhys ); @@ -1463,6 +1635,9 @@ void CPhysConvert::InputConvertTarget( inputdata_t &inputdata ) #define SF_MAGNET_SUCK 0x0004 #define SF_MAGNET_ALLOWROTATION 0x0008 #define SF_MAGNET_COAST_HACK 0x0010 +#ifdef MAPBASE +#define SF_MAGNET_PREVENT_PICKUP 0x0020 +#endif LINK_ENTITY_TO_CLASS( phys_magnet, CPhysMagnet ); @@ -1559,6 +1734,16 @@ CPhysMagnet::~CPhysMagnet( void ) //----------------------------------------------------------------------------- void CPhysMagnet::Spawn( void ) { +#ifdef MAPBASE + // Crashes otherwise + if (GetModelName() == NULL_STRING) + { + Warning("WARNING: %s spawned with no model name\n", GetDebugName()); + UTIL_Remove(this); + return; + } +#endif + Precache(); SetMoveType( MOVETYPE_NONE ); @@ -1587,6 +1772,13 @@ void CPhysMagnet::Spawn( void ) VPhysicsGetObject()->EnableMotion( false ); } +#ifdef MAPBASE + if ( HasSpawnFlags(SF_MAGNET_PREVENT_PICKUP) ) + { + PhysSetGameFlags(VPhysicsGetObject(), FVPHYSICS_NO_PLAYER_PICKUP); + } +#endif + m_bActive = true; m_pConstraintGroup = NULL; m_flTotalMass = 0; @@ -1741,6 +1933,20 @@ void CPhysMagnet::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) BaseClass::VPhysicsCollision( index, pEvent ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPhysMagnet::CanBePickedUpByPhyscannon( void ) +{ + if ( HasSpawnFlags( SF_MAGNET_PREVENT_PICKUP ) ) + return false; + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/physobj.h b/mp/src/game/server/physobj.h index a6e9e5a5..09d333c5 100644 --- a/mp/src/game/server/physobj.h +++ b/mp/src/game/server/physobj.h @@ -37,6 +37,9 @@ #define SF_PHYSBOX_NEVER_PICK_UP 0x200000 // Physcannon will never be able to pick this up. #define SF_PHYSBOX_NEVER_PUNT 0x400000 // Physcannon will never be able to punt this object. #define SF_PHYSBOX_PREVENT_PLAYER_TOUCH_ENABLE 0x800000 // If set, the player will not cause the object to enable its motion when bumped into +#ifdef MAPBASE +#define SF_PHYSBOX_RADIUS_PICKUP 0x1000000 // Allows this object to be picked up in a radius, useful for smaller objects. Based on the prop_physics input +#endif // UNDONE: Hook collisions into the physics system to generate touch functions and take damage on falls // UNDONE: Base class PhysBrush @@ -76,6 +79,9 @@ public: void InputDisableMotion( inputdata_t &inputdata ); void InputForceDrop( inputdata_t &inputdata ); void InputDisableFloating( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetDebris( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); @@ -120,6 +126,9 @@ public: // Input handlers void InputExplode( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputExplodeAndRemove( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); private: @@ -187,6 +196,9 @@ public: void Precache( void ); void Touch( CBaseEntity *pOther ); void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); +#ifdef MAPBASE + bool CanBePickedUpByPhyscannon( void ); +#endif void DoMagnetSuck( CBaseEntity *pOther ); void SetConstraintGroup( IPhysicsConstraintGroup *pGroup ); diff --git a/mp/src/game/server/player.cpp b/mp/src/game/server/player.cpp index e0e7ccd2..59511f4a 100644 --- a/mp/src/game/server/player.cpp +++ b/mp/src/game/server/player.cpp @@ -80,6 +80,14 @@ #ifdef HL2_DLL #include "combine_mine.h" #include "weapon_physcannon.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#include "mapbase/matchers.h" +#endif +#endif + +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_funcs_shared.h" #endif ConVar autoaim_max_dist( "autoaim_max_dist", "2160" ); // 2160 = 180 feet @@ -191,6 +199,10 @@ ConVar sv_player_display_usercommand_errors( "sv_player_display_usercommand_err ConVar player_debug_print_damage( "player_debug_print_damage", "0", FCVAR_CHEAT, "When true, print amount and type of all damage received by player to console." ); +#ifdef MAPBASE +ConVar player_use_visibility_cache( "player_use_visibility_cache", "0", FCVAR_NONE, "Allows the player to use the visibility cache." ); +#endif + void CC_GiveCurrentAmmo( void ) { @@ -435,6 +447,12 @@ BEGIN_DATADESC( CBasePlayer ) DEFINE_FIELD( m_autoKickDisabled, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_FIELD( m_bInTriggerFall, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_bDrawPlayerModelExternally, FIELD_BOOLEAN ), +#endif + // Function Pointers DEFINE_FUNCTION( PlayerDeathThink ), @@ -442,7 +460,11 @@ BEGIN_DATADESC( CBasePlayer ) DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetHUDVisibility", InputSetHUDVisibility ), DEFINE_INPUTFUNC( FIELD_STRING, "SetFogController", InputSetFogController ), + DEFINE_INPUTFUNC( FIELD_INPUT, "SetPostProcessController", InputSetPostProcessController ), DEFINE_INPUTFUNC( FIELD_STRING, "HandleMapEvent", InputHandleMapEvent ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetSuppressAttacks", InputSetSuppressAttacks ), +#endif DEFINE_FIELD( m_nNumCrouches, FIELD_INTEGER ), DEFINE_FIELD( m_bDuckToggled, FIELD_BOOLEAN ), @@ -452,6 +474,8 @@ BEGIN_DATADESC( CBasePlayer ) DEFINE_FIELD( m_nNumCrateHudHints, FIELD_INTEGER ), + DEFINE_FIELD( m_hPostProcessCtrl, FIELD_EHANDLE ), + // DEFINE_FIELD( m_nBodyPitchPoseParam, FIELD_INTEGER ), @@ -461,6 +485,76 @@ BEGIN_DATADESC( CBasePlayer ) // DEFINE_UTLVECTOR( m_vecPlayerSimInfo ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +// TODO: Better placement? +ScriptHook_t g_Hook_PlayerRunCommand; + +BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptIsPlayerNoclipping, "IsNoclipping", "Returns true if the player is in noclip mode." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetExpresser, "GetExpresser", "Gets a handle for this player's expresser." ) + + DEFINE_SCRIPTFUNC( GetPlayerName, "Gets the player's name." ) + DEFINE_SCRIPTFUNC( GetUserID, "Gets the player's user ID." ) + DEFINE_SCRIPTFUNC_NAMED( GetUserID, "GetPlayerUserId", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC( GetNetworkIDString, "Gets the player's network (i.e. Steam) ID." ) + + DEFINE_SCRIPTFUNC( FragCount, "Gets the number of frags (kills) this player has in a multiplayer game." ) + DEFINE_SCRIPTFUNC( DeathCount, "Gets the number of deaths this player has had in a multiplayer game." ) + DEFINE_SCRIPTFUNC( IsConnected, "Returns true if this player is connected." ) + DEFINE_SCRIPTFUNC( IsDisconnecting, "Returns true if this player is disconnecting." ) + DEFINE_SCRIPTFUNC( IsSuitEquipped, "Returns true if this player had the HEV suit equipped." ) + + DEFINE_SCRIPTFUNC_NAMED( ArmorValue, "GetArmor", "Gets the player's armor." ) + DEFINE_SCRIPTFUNC_NAMED( SetArmorValue, "SetArmor", "Sets the player's armor." ) + + DEFINE_SCRIPTFUNC( FlashlightIsOn, "Returns true if the flashlight is on." ) + DEFINE_SCRIPTFUNC( FlashlightTurnOn, "Turns on the flashlight." ) + DEFINE_SCRIPTFUNC( FlashlightTurnOff, "Turns off the flashlight." ) + + DEFINE_SCRIPTFUNC( DisableButtons, "Disables the specified button mask." ) + DEFINE_SCRIPTFUNC( EnableButtons, "Enables the specified button mask if it was disabled before." ) + DEFINE_SCRIPTFUNC( ForceButtons, "Forces the specified button mask." ) + DEFINE_SCRIPTFUNC( UnforceButtons, "Unforces the specified button mask if it was forced before." ) + + DEFINE_SCRIPTFUNC( GetButtons, "Gets the player's active buttons." ) + DEFINE_SCRIPTFUNC( GetButtonPressed, "Gets the player's currently pressed buttons." ) + DEFINE_SCRIPTFUNC( GetButtonReleased, "Gets the player's just-released buttons." ) + DEFINE_SCRIPTFUNC( GetButtonLast, "Gets the player's previously active buttons." ) + DEFINE_SCRIPTFUNC( GetButtonDisabled, "Gets the player's currently unusable buttons." ) + DEFINE_SCRIPTFUNC( GetButtonForced, "Gets the player's currently forced buttons." ) + + DEFINE_SCRIPTFUNC( GetFOV, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFOVOwner, "GetFOVOwner", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFOV, "SetFOV", "" ) + + DEFINE_SCRIPTFUNC( ViewPunch, "Punches the player's view with the specified vector." ) + DEFINE_SCRIPTFUNC( SetMuzzleFlashTime, "Sets the player's muzzle flash time for AI." ) + DEFINE_SCRIPTFUNC( SetSuitUpdate, "Sets an update for the player's HEV suit." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAutoaimVector, "GetAutoaimVector", "Gets the player's autoaim shooting direction with the specified scale." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAutoaimVectorCustomMaxDist, "GetAutoaimVectorCustomMaxDist", "Gets the player's autoaim shooting direction with the specified scale and a custom max distance." ) + DEFINE_SCRIPTFUNC( ShouldAutoaim, "Returns true if the player should be autoaiming." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeForward, "GetEyeForward", "Gets the player's forward eye vector." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeRight, "GetEyeRight", "Gets the player's right eye vector." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeUp, "GetEyeUp", "Gets the player's up eye vector." ) + + // + // Hooks + // + BEGIN_SCRIPTHOOK( g_Hook_PlayerRunCommand, "PlayerRunCommand", FIELD_VOID, "Called when running a player command on the server." ) + DEFINE_SCRIPTHOOK_PARAM( "command", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + +END_SCRIPTDESC(); +#else +BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseAnimating, "The player entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsPlayerNoclipping, "IsNoclipping", "Returns true if the player is in noclip mode." ) +END_SCRIPTDESC(); +#endif + int giPrecacheGrunt = 0; edict_t *CBasePlayer::s_PlayerEdict = NULL; @@ -527,6 +621,30 @@ void CBasePlayer::DestroyViewModels( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::CreateHandModel(int index, int iOtherVm) +{ + Assert(index >= 0 && index < MAX_VIEWMODELS && iOtherVm >= 0 && iOtherVm < MAX_VIEWMODELS ); + + if (GetViewModel(index)) + return; + + CBaseViewModel *vm = (CBaseViewModel *)CreateEntityByName("hand_viewmodel"); + if (vm) + { + vm->SetAbsOrigin(GetAbsOrigin()); + vm->SetOwner(this); + vm->SetIndex(index); + DispatchSpawn(vm); + vm->FollowEntity(GetViewModel(iOtherVm), true); + m_hViewModel.Set(index, vm); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Static member function to create a player of the specified class // Input : *className - @@ -639,6 +757,7 @@ CBasePlayer::CBasePlayer( ) m_flMovementTimeForUserCmdProcessingRemaining = 0.0f; m_flLastObjectiveTime = -1.f; + m_hPostProcessCtrl.Set( NULL ); } CBasePlayer::~CBasePlayer( ) @@ -653,6 +772,11 @@ CBasePlayer::~CBasePlayer( ) //----------------------------------------------------------------------------- void CBasePlayer::UpdateOnRemove( void ) { + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } + VPhysicsDestroyObject(); // Remove him from his current team @@ -731,9 +855,13 @@ int CBasePlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo ) bool CBasePlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ) const { - // Team members shouldn't be adjusted unless friendly fire is on. - if ( !friendlyfire.GetInt() && pPlayer->GetTeamNumber() == GetTeamNumber() ) - return false; + //Tony; only check teams in teamplay + if ( gpGlobals->teamplay ) + { + // Team members shouldn't be adjusted unless friendly fire is on. + if ( !friendlyfire.GetInt() && pPlayer->GetTeamNumber() == GetTeamNumber() ) + return false; + } // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it. if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) @@ -910,7 +1038,11 @@ void CBasePlayer::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &v // If an NPC check if friendly fire is disallowed // -------------------------------------------------- CAI_BaseNPC *pNPC = info.GetAttacker()->MyNPCPointer(); +#ifdef MAPBASE + if ( pNPC && (pNPC->CapabilitiesGet() & bits_CAP_NO_HIT_PLAYER) && pNPC->IRelationType( this ) > D_FR ) +#else if ( pNPC && (pNPC->CapabilitiesGet() & bits_CAP_NO_HIT_PLAYER) && pNPC->IRelationType( this ) != D_HT ) +#endif return; // Prevent team damage here so blood doesn't appear @@ -949,10 +1081,22 @@ void CBasePlayer::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &v break; } +#ifdef MAPBASE + + // Damage filter bleed control needs to exist on all DLLs + bool bShouldBleed = +#ifdef HL2_EPISODIC + !g_pGameRules->Damage_ShouldNotBleed( info.GetDamageType() ) && +#endif + DamageFilterAllowsBlood( info ); + + if ( bShouldBleed ) +#else #ifdef HL2_EPISODIC // If this damage type makes us bleed, then do so bool bShouldBleed = !g_pGameRules->Damage_ShouldNotBleed( info.GetDamageType() ); if ( bShouldBleed ) +#endif #endif { SpawnBlood(ptr->endpos, vecDir, BloodColor(), info.GetDamage());// a little surface blood. @@ -1564,9 +1708,10 @@ void CBasePlayer::RemoveAllItems( bool removeSuit ) UpdateClientData(); } +//Tony; correct this for base code so that IsDead will be correct accross all games. bool CBasePlayer::IsDead() const { - return m_lifeState == LIFE_DEAD; + return m_lifeState != LIFE_ALIVE; } static float DamageForce( const Vector &size, float damage ) @@ -2872,6 +3017,10 @@ float CBasePlayer::GetHeldObjectMass( IPhysicsObject *pHeldObject ) return 0; } +CBaseEntity *CBasePlayer::GetHeldObject( void ) +{ + return NULL; +} //----------------------------------------------------------------------------- // Purpose: Server side of jumping rules. Most jumping logic is already @@ -3697,6 +3846,20 @@ void CBasePlayer::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) } } } + +#ifdef MAPBASE_VSCRIPT + // Movement hook for VScript + if (m_ScriptScope.IsInitialized() && g_Hook_PlayerRunCommand.CanRunInScope(m_ScriptScope)) + { + HSCRIPT hCmd = g_pScriptVM->RegisterInstance( ucmd ); + + // command + ScriptVariant_t args[] = { hCmd }; + g_Hook_PlayerRunCommand.Call( m_ScriptScope, NULL, args ); + + g_pScriptVM->RemoveInstance( hCmd ); + } +#endif PlayerMove()->RunCommand(this, ucmd, moveHelper); } @@ -4047,6 +4210,12 @@ void CBasePlayer::CheckTimeBasedDamage() case itbd_Acid: // OnTakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC); bDuration = ACID_DURATION; +#ifdef MAPBASE + // Prevents ant workers from inducing the Flash Plague, flashing the player's screen every time they take damage henceforth. + // I think people came up with a different name, but I can't bother to look for it right now. + // This fix might prevent other acid damage stuff as well, so it's not episodic-exclusive. + m_bitsDamageType &= ~(DMG_ACID); +#endif break; case itbd_SlowBurn: // OnTakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC); @@ -4167,6 +4336,12 @@ void CBasePlayer::UpdateGeigerCounter( void ) range = clamp( (int)range * 4, 0, 255 ); } +#ifdef MAPBASE + // If the geiger is disabled, just use 255 + if (HasSpawnFlags(SF_PLAYER_NO_GEIGER)) + range = 255; +#endif + if (range != m_igeigerRangePrev) { m_igeigerRangePrev = range; @@ -4896,6 +5071,55 @@ void CBasePlayer::InitialSpawn( void ) gamestats->Event_PlayerConnected( this ); } +//----------------------------------------------------------------------------- +// Purpose: clear our m_Local.m_TonemapParams to -1. +//----------------------------------------------------------------------------- +void CBasePlayer::ClearTonemapParams( void ) +{ + //Tony; clear all the variables to -1.0 + m_Local.m_TonemapParams.m_flAutoExposureMin = -1.0f; + m_Local.m_TonemapParams.m_flAutoExposureMax = -1.0f; + m_Local.m_TonemapParams.m_flTonemapScale = -1.0f; + m_Local.m_TonemapParams.m_flBloomScale = -1.0f; + m_Local.m_TonemapParams.m_flTonemapRate = -1.0f; +} +void CBasePlayer::InputSetTonemapScale( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flTonemapScale = inputdata.value.Float(); +} + +void CBasePlayer::InputSetTonemapRate( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flTonemapRate = inputdata.value.Float(); +} +void CBasePlayer::InputSetAutoExposureMin( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flAutoExposureMin = inputdata.value.Float(); +} + +void CBasePlayer::InputSetAutoExposureMax( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flAutoExposureMax = inputdata.value.Float(); +} + +void CBasePlayer::InputSetBloomScale( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flBloomScale = inputdata.value.Float(); +} + +//Tony; restore defaults (set min/max to -1.0 so nothing gets overridden) +void CBasePlayer::InputUseDefaultAutoExposure( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flAutoExposureMin = -1.0f; + m_Local.m_TonemapParams.m_flAutoExposureMax = -1.0f; + m_Local.m_TonemapParams.m_flTonemapRate = -1.0f; +} +void CBasePlayer::InputUseDefaultBloomScale( inputdata_t &inputdata ) +{ + m_Local.m_TonemapParams.m_flBloomScale = -1.0f; +} +// void InputSetBloomScaleRange( inputdata_t &inputdata ); + //----------------------------------------------------------------------------- // Purpose: Called everytime the player respawns //----------------------------------------------------------------------------- @@ -4907,6 +5131,9 @@ void CBasePlayer::Spawn( void ) Hints()->ResetHints(); } + //Tony; make sure tonemap params is cleared. + ClearTonemapParams(); + SetClassname( "player" ); // Shared spawning code.. @@ -4944,6 +5171,7 @@ void CBasePlayer::Spawn( void ) // Initialize the fog and postprocess controllers. InitFogController(); + InitPostProcessController(); m_DmgTake = 0; m_DmgSave = 0; @@ -4968,7 +5196,12 @@ void CBasePlayer::Spawn( void ) if ( !m_fGameHUDInitialized ) g_pGameRules->SetDefaultPlayerTeam( this ); +#ifdef MAPBASE + CBaseEntity *pSpawnPoint = g_pGameRules->GetPlayerSpawnSpot( this ); + SpawnedAtPoint( pSpawnPoint ); +#else g_pGameRules->GetPlayerSpawnSpot( this ); +#endif m_Local.m_bDucked = false;// This will persist over round restart if you hold duck otherwise. m_Local.m_bDucking = false; @@ -5004,6 +5237,9 @@ void CBasePlayer::Spawn( void ) enginesound->SetPlayerDSP( user, 0, false ); CreateViewModel(); +#ifdef MAPBASE + CreateHandModel(); +#endif SetCollisionGroup( COLLISION_GROUP_PLAYER ); @@ -5059,6 +5295,11 @@ void CBasePlayer::Spawn( void ) UpdateLastKnownArea(); m_weaponFiredTimer.Invalidate(); + + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } } void CBasePlayer::Activate( void ) @@ -5249,6 +5490,14 @@ void CBasePlayer::OnRestore( void ) m_nVehicleViewSavedFrame = 0; m_nBodyPitchPoseParam = LookupPoseParameter( "body_pitch" ); + + // HACK: (03/25/09) Then the player goes across a transition it doesn't spawn and register + // it's instance. We're hacking around this for now, but this will go away when we get around to + // having entities cross transitions and keep their script state. + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM && (gpGlobals->eLoadType == MapLoad_Transition) ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } } /* void CBasePlayer::SetTeamName( const char *pTeamName ) @@ -5702,6 +5951,25 @@ CBaseEntity *CBasePlayer::GiveNamedItem( const char *pszName, int iSubType ) DispatchSpawn( pent ); +#ifdef MAPBASE + if ( pWeapon ) + { + for (int i=0;iGetSlot() == pWeapon->GetSlot() && m_hMyWeapons[i]->GetPosition() == pWeapon->GetPosition() ) + { + // Make sure it matches the subtype + if ( m_hMyWeapons[i]->GetSubType() == iSubType ) + { + // Don't use this weapon if the slot is already occupied + UTIL_Remove( pWeapon ); + return NULL; + } + } + } + } +#endif + if ( pent != NULL && !(pent->IsMarkedForDeletion()) ) { pent->Touch( this ); @@ -5911,6 +6179,10 @@ void CBasePlayer::ImpulseCommands( ) CBaseCombatWeapon *pWeapon; pWeapon = GetActiveWeapon(); +#ifdef MAPBASE + if (!pWeapon) + return; +#endif if( pWeapon->IsEffectActive( EF_NODRAW ) ) { @@ -6038,7 +6310,12 @@ static void CreateJeep( CBasePlayer *pPlayer ) // Cheat to create a jeep in front of the player Vector vecForward; AngleVectors( pPlayer->EyeAngles(), &vecForward ); + //Tony; in sp sdk, we have prop_vehicle_hl2buggy; because episode 2 modified the jeep code to turn it into the jalopy instead of the regular buggy +#if defined ( HL2_EPISODIC ) + CBaseEntity *pJeep = (CBaseEntity *)CreateEntityByName( "prop_vehicle_hl2buggy" ); +#else CBaseEntity *pJeep = (CBaseEntity *)CreateEntityByName( "prop_vehicle_jeep" ); +#endif if ( pJeep ) { Vector vecOrigin = pPlayer->GetAbsOrigin() + vecForward * 256 + Vector(0,0,64); @@ -6047,7 +6324,11 @@ static void CreateJeep( CBasePlayer *pPlayer ) pJeep->SetAbsAngles( vecAngles ); pJeep->KeyValue( "model", "models/buggy.mdl" ); pJeep->KeyValue( "solid", "6" ); +#if defined ( HL2_EPISODIC ) + pJeep->KeyValue( "targetname", "hl2buggy" ); +#else pJeep->KeyValue( "targetname", "jeep" ); +#endif pJeep->KeyValue( "vehiclescript", "scripts/vehicles/jeep_test.txt" ); DispatchSpawn( pJeep ); pJeep->Activate(); @@ -6540,7 +6821,22 @@ bool CBasePlayer::ClientCommand( const CCommand &args ) angle.y = atof( args[5] ); angle.z = 0.0f; +#ifdef MAPBASE + #define SPECGOTO_MAX_VALUE 0xFFFF/2.0f + + // This could crash the game somehow if not checked.. Thanks to Nairda. + if (abs(angle.x) <= 360.0f && abs(angle.y) <= 360.0f && abs(origin.x) < SPECGOTO_MAX_VALUE && + abs(origin.y) < SPECGOTO_MAX_VALUE && abs(origin.z) < SPECGOTO_MAX_VALUE) + { + JumptoPosition(origin, angle); + } + else + { + engine->ClientPrintf(edict(), "spec_goto: Out-of-bounds"); + } +#else JumptoPosition( origin, angle ); +#endif } return true; @@ -6603,7 +6899,11 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) else { // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows) +#ifdef MAPBASE + if( (pWeapon->FVisible( this, MASK_SOLID ) == false && !(GetFlags() & FL_NOTARGET)) && !HasSpawnFlags(SF_WEAPON_ALWAYS_TOUCHABLE) ) +#else if( pWeapon->FVisible( this, MASK_SOLID ) == false && !(GetFlags() & FL_NOTARGET) ) +#endif return false; } @@ -6615,7 +6915,11 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) if( Weapon_EquipAmmoOnly( pWeapon ) ) { // Only remove me if I have no ammo left +#ifdef MAPBASE + if ( pWeapon->HasPrimaryAmmo() || pWeapon->HasSecondaryAmmo() ) +#else if ( pWeapon->HasPrimaryAmmo() ) +#endif return false; UTIL_Remove( pWeapon ); @@ -6626,10 +6930,56 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) return false; } } +#ifdef MAPBASE + // -------------------------------------------------------------------------------- + // If we own a weapon in the same position take the ammo but leave the weapon behind + // -------------------------------------------------------------------------------- + if (!pWeapon->HasSpawnFlags(SF_WEAPON_USED)) // Make sure we're being used and not being bumped + { + for (int i=0;iGetSlot() == m_hMyWeapons[i]->GetSlot() && + pWeapon->GetPosition() == m_hMyWeapons[i]->GetPosition()) + { + //Weapon_EquipAmmoOnly( pWeapon ); + + // I'm too lazy to make my own version of Weapon_EquipAmmoOnly that doesn't check if we already have the weapon first + int primaryGiven = (pWeapon->UsesClipsForAmmo1()) ? pWeapon->m_iClip1 : pWeapon->GetPrimaryAmmoCount(); + int secondaryGiven = (pWeapon->UsesClipsForAmmo2()) ? pWeapon->m_iClip2 : pWeapon->GetSecondaryAmmoCount(); + + int takenPrimary = GiveAmmo( primaryGiven, pWeapon->m_iPrimaryAmmoType); + int takenSecondary = GiveAmmo( secondaryGiven, pWeapon->m_iSecondaryAmmoType); + + if( pWeapon->UsesClipsForAmmo1() ) + { + pWeapon->m_iClip1 -= takenPrimary; + } + else + { + pWeapon->SetPrimaryAmmoCount( pWeapon->GetPrimaryAmmoCount() - takenPrimary ); + } + + if( pWeapon->UsesClipsForAmmo2() ) + { + pWeapon->m_iClip2 -= takenSecondary; + } + else + { + pWeapon->SetSecondaryAmmoCount( pWeapon->GetSecondaryAmmoCount() - takenSecondary ); + } + + return false; + } + } + } +#endif // ------------------------- // Otherwise take the weapon // ------------------------- +#ifndef MAPBASE else +#endif { pWeapon->CheckRespawn(); @@ -6652,6 +7002,7 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) UTIL_HudHintText( this, hint.Access() ); } +#ifndef MAPBASE // See CBasePlayer::Weapon_Equip. // Always switch to a newly-picked up weapon if ( !PlayerHasMegaPhysCannon() ) { @@ -6663,6 +7014,7 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) Weapon_Switch( pWeapon ); } +#endif #endif } return true; @@ -6715,6 +7067,30 @@ void CBasePlayer::ShowCrosshair( bool bShow ) } } +//----------------------------------------------------------------------------- +// Used by vscript to determine if the player is noclipping +//----------------------------------------------------------------------------- +bool CBasePlayer::ScriptIsPlayerNoclipping(void) +{ + return (GetMoveType() == MOVETYPE_NOCLIP); +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBasePlayer::VScriptGetExpresser() +{ + HSCRIPT hScript = NULL; + CAI_Expresser *pExpresser = GetExpresser(); + if (pExpresser) + { + hScript = g_pScriptVM->RegisterInstance( pExpresser ); + } + + return hScript; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -7349,6 +7725,24 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) { BaseClass::Weapon_Equip( pWeapon ); +#ifdef MAPBASE + // So, I discovered that BumpWeapon seems to have some deprecated code. + // It automatically switches the player to all new weapons. Sounds normal, right? + // Except that's *also* handled here. Since the BumpWeapon code implied the player could pick up weapons while carrying the mega physcannon, + // I assumed it was some old, deprecated code and, since I needed to remove a piece of (also deprecated) code in it, I decided to remove it entirely + // and hand weapon switching to this alone. Seems straightforward, right? + // + // Well, it turns out, this code was more complicated than I thought and used various weights and stuff to determine if a weapon was worth automatically switching to. + // It doesn't automatically switch to most of the weapons. Even though I seem to be right about that old code being deprecated, + // I got irritated and...uh...replaced the correct Weapon_Equip code with the old deprecated code from BumpWeapon. + // + // Trust me. It was hard and pointless to get used to. You'll thank me later. + + if ( !PlayerHasMegaPhysCannon() ) + { + Weapon_Switch( pWeapon ); + } +#else bool bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon ); #ifdef HL2_DLL @@ -7364,8 +7758,51 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) { Weapon_Switch( pWeapon ); } +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CBasePlayer::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) +{ +#ifdef HL2_DLL + // HAAAAAAAAAAAAAACKS! + if (GetActiveWeapon()) + { + int translated = baseAct; + int iActOffset = (baseAct - ACT_HL2MP_IDLE); + + string_t iszClassname = GetActiveWeapon()->m_iClassname; + if (iszClassname == gm_isz_class_Pistol || iszClassname == gm_isz_class_357) + translated = (ACT_HL2MP_IDLE_PISTOL + iActOffset); + else if (iszClassname == gm_isz_class_SMG1) + translated = (ACT_HL2MP_IDLE_SMG1 + iActOffset); + else if (iszClassname == gm_isz_class_AR2) + translated = (ACT_HL2MP_IDLE_AR2 + iActOffset); + else if (iszClassname == gm_isz_class_Shotgun) + translated = (ACT_HL2MP_IDLE_SHOTGUN + iActOffset); + else if (iszClassname == gm_isz_class_RPG) + translated = (ACT_HL2MP_IDLE_RPG + iActOffset); + else if (iszClassname == gm_isz_class_Grenade) + translated = (ACT_HL2MP_IDLE_GRENADE + iActOffset); + else if (iszClassname == gm_isz_class_Physcannon) + translated = (ACT_HL2MP_IDLE_PHYSGUN + iActOffset); + else if (iszClassname == gm_isz_class_Crossbow) + translated = (ACT_HL2MP_IDLE_CROSSBOW + iActOffset); + else if (iszClassname == gm_isz_class_Crowbar || iszClassname == gm_isz_class_Stunstick) + translated = (ACT_HL2MP_IDLE_MELEE + iActOffset); + + if (translated != baseAct) + return (Activity)translated; + } +#endif + + return BaseClass::Weapon_TranslateActivity( baseAct, pRequired ); +} +#endif + //========================================================= // HasNamedPlayerItem Does the player already have this item? @@ -7464,6 +7901,23 @@ void CBasePlayer::PlayWearableAnimsForPlaybackEvent( wearableanimplayback_t iPla } #endif // USES_ECON_ITEMS +#ifdef MAPBASE +bool CBasePlayer::ShouldUseVisibilityCache( CBaseEntity *pEntity ) +{ + // In CBaseEntity::FVisible(), players are allowed to see through CONTENTS_BLOCKLOS, which is used for + // nodraw, block LOS brushes, etc. This is so some code doesn't erronesouly assume the player can't see + // an entity (when the player can, in fact, see it) and therefore do something the player is not supposed to see. + // + // However, to reduce the number of traces FVisible() runs, CBaseCombatCharacter uses a "visibility cache" shared + // by all entities derived from it. The player is normally a part of this visibility cache, so when it runs a trace + // through a CONTENTS_BLOCKLOS surface, the visibility cache assumes entities can now see through it and therefore + // NPCs to see through the brush which should normally block their LOS. + // + // This solution stops the player from using the visibility cache altogether, toggled by a convar. + return player_use_visibility_cache.GetBool(); +} +#endif + //================================================================================ // TEAM HANDLING //================================================================================ @@ -7785,6 +8239,11 @@ void CRevertSaved::LoadThink( void ) #define SF_SPEED_MOD_SUPPRESS_SPEED (1<<5) #define SF_SPEED_MOD_SUPPRESS_ATTACK (1<<6) #define SF_SPEED_MOD_SUPPRESS_ZOOM (1<<7) +#ifdef MAPBASE +// Needs to be inverse because suppressing the flashlight is already default behavior +// and we don't want to break compatibility for existing speedmods +#define SF_SPEED_MOD_DONT_SUPPRESS_FLASHLIGHT (1<<8) +#endif class CMovementSpeedMod : public CPointEntity { @@ -7792,9 +8251,20 @@ class CMovementSpeedMod : public CPointEntity public: void InputSpeedMod(inputdata_t &data); +#ifdef MAPBASE + void InputEnable(inputdata_t &data); + void InputDisable(inputdata_t &data); + + void InputSetAdditionalButtons(inputdata_t &data); +#endif + private: int GetDisabledButtonMask( void ); +#ifdef MAPBASE + int m_iAdditionalButtons; +#endif + DECLARE_DATADESC(); }; @@ -7802,6 +8272,13 @@ LINK_ENTITY_TO_CLASS( player_speedmod, CMovementSpeedMod ); BEGIN_DATADESC( CMovementSpeedMod ) DEFINE_INPUTFUNC( FIELD_FLOAT, "ModifySpeed", InputSpeedMod ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_KEYFIELD( m_iAdditionalButtons, FIELD_INTEGER, "AdditionalButtons" ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetAdditionalButtons", InputSetAdditionalButtons ), +#endif END_DATADESC() int CMovementSpeedMod::GetDisabledButtonMask( void ) @@ -7838,6 +8315,13 @@ int CMovementSpeedMod::GetDisabledButtonMask( void ) nMask |= IN_ZOOM; } +#ifdef MAPBASE + if ( m_iAdditionalButtons != 0 ) + { + nMask |= m_iAdditionalButtons; + } +#endif + return nMask; } @@ -7871,6 +8355,10 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) pPlayer->HideViewModels(); } +#ifdef MAPBASE + if ( !HasSpawnFlags( SF_SPEED_MOD_DONT_SUPPRESS_FLASHLIGHT ) ) + { +#endif // Turn off the flashlight if ( pPlayer->FlashlightIsOn() ) { @@ -7879,6 +8367,9 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) // Disable the flashlight's further use pPlayer->SetFlashlightEnabled( false ); +#ifdef MAPBASE + } +#endif pPlayer->DisableButtons( GetDisabledButtonMask() ); // Hide the HUD @@ -7899,8 +8390,15 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) } } +#ifdef MAPBASE + if ( !HasSpawnFlags( SF_SPEED_MOD_DONT_SUPPRESS_FLASHLIGHT ) ) + { +#endif // Allow the flashlight again pPlayer->SetFlashlightEnabled( true ); +#ifdef MAPBASE + } +#endif pPlayer->EnableButtons( GetDisabledButtonMask() ); // Restore the HUD @@ -7914,6 +8412,205 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) } } +#ifdef MAPBASE +void CMovementSpeedMod::InputEnable(inputdata_t &data) +{ + CBasePlayer *pPlayer = NULL; + + if ( data.pActivator && data.pActivator->IsPlayer() ) + { + pPlayer = (CBasePlayer *)data.pActivator; + } + else if ( !g_pGameRules->IsDeathmatch() ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + + if ( pPlayer ) + { + // Holster weapon immediately, to allow it to cleanup + if ( HasSpawnFlags( SF_SPEED_MOD_SUPPRESS_WEAPONS ) ) + { + if ( pPlayer->GetActiveWeapon() ) + { + pPlayer->Weapon_SetLast( pPlayer->GetActiveWeapon() ); + pPlayer->GetActiveWeapon()->Holster(); + pPlayer->ClearActiveWeapon(); + } + + pPlayer->HideViewModels(); + } + + // Turn off the flashlight + if ( pPlayer->FlashlightIsOn() ) + { + pPlayer->FlashlightTurnOff(); + } + + // Disable the flashlight's further use + pPlayer->SetFlashlightEnabled( false ); + pPlayer->DisableButtons( GetDisabledButtonMask() ); + + // Hide the HUD + if ( HasSpawnFlags( SF_SPEED_MOD_SUPPRESS_HUD ) ) + { + pPlayer->m_Local.m_iHideHUD |= HIDEHUD_ALL; + } + } +} + +void CMovementSpeedMod::InputDisable(inputdata_t &data) +{ + CBasePlayer *pPlayer = NULL; + + if ( data.pActivator && data.pActivator->IsPlayer() ) + { + pPlayer = (CBasePlayer *)data.pActivator; + } + else if ( !g_pGameRules->IsDeathmatch() ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + + if ( pPlayer ) + { + // Bring the weapon back + if ( HasSpawnFlags( SF_SPEED_MOD_SUPPRESS_WEAPONS ) && pPlayer->GetActiveWeapon() == NULL ) + { + pPlayer->SetActiveWeapon( pPlayer->GetLastWeapon() ); + if ( pPlayer->GetActiveWeapon() ) + { + pPlayer->GetActiveWeapon()->Deploy(); + } + } + + // Allow the flashlight again + pPlayer->SetFlashlightEnabled( true ); + pPlayer->EnableButtons( GetDisabledButtonMask() ); + + // Restore the HUD + if ( HasSpawnFlags( SF_SPEED_MOD_SUPPRESS_HUD ) ) + { + pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_ALL; + } + } +} + +void CMovementSpeedMod::InputSetAdditionalButtons(inputdata_t &data) +{ + CBasePlayer *pPlayer = NULL; + + if ( data.pActivator && data.pActivator->IsPlayer() ) + { + pPlayer = (CBasePlayer *)data.pActivator; + } + else if ( !g_pGameRules->IsDeathmatch() ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + + bool bAlreadyDisabled = false; + if ( pPlayer ) + { + bAlreadyDisabled = (pPlayer->m_afButtonDisabled & GetDisabledButtonMask()) != 0; + } + + m_iAdditionalButtons = data.value.Int(); + + // If we were already disabling buttons, re-disable them + if ( bAlreadyDisabled ) + { + // We should probably do something better than this. + pPlayer->m_afButtonForced = GetDisabledButtonMask(); + } +} +#endif + +#ifdef MAPBASE +class CLogicPlayerInfo : public CPointEntity +{ + DECLARE_CLASS( CLogicPlayerInfo, CPointEntity ); +public: + void InputGetPlayerInfo( inputdata_t &inputdata ); + void InputGetPlayerByID( inputdata_t &inputdata ); + void InputGetPlayerByName( inputdata_t &inputdata ); + + void GetPlayerInfo( CBasePlayer *pPlayer ); + + COutputInt m_OutUserID; + COutputString m_OutPlayerName; + COutputEHANDLE m_OutPlayerEntity; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( logic_playerinfo, CLogicPlayerInfo ); + +BEGIN_DATADESC( CLogicPlayerInfo ) + DEFINE_INPUTFUNC( FIELD_EHANDLE, "GetPlayerInfo", InputGetPlayerInfo ), + DEFINE_INPUTFUNC( FIELD_STRING, "GetPlayerByID", InputGetPlayerByID ), + DEFINE_INPUTFUNC( FIELD_STRING, "GetPlayerByName", InputGetPlayerByName ), + + DEFINE_OUTPUT( m_OutUserID, "OutUserID" ), + DEFINE_OUTPUT( m_OutPlayerName, "OutPlayerName" ), + DEFINE_OUTPUT( m_OutPlayerEntity, "OutPlayerEntity" ), +END_DATADESC() + + +void CLogicPlayerInfo::InputGetPlayerInfo( inputdata_t &inputdata ) +{ + CBasePlayer *pPlayer = ToBasePlayer(inputdata.value.Entity()); + + // If there was no entity to begin with, try the local player + if (!pPlayer && !inputdata.value.Entity()) + pPlayer = UTIL_GetLocalPlayer(); + + if (pPlayer) + GetPlayerInfo( pPlayer ); +} + +void CLogicPlayerInfo::InputGetPlayerByID( inputdata_t &inputdata ) +{ + for (int i = 1; i < gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if (pPlayer) + { + if (Matcher_NamesMatch( inputdata.value.String(), UTIL_VarArgs("%i", pPlayer->GetUserID()) )) + { + GetPlayerInfo( pPlayer ); + return; + } + } + } +} + +void CLogicPlayerInfo::InputGetPlayerByName( inputdata_t &inputdata ) +{ + for (int i = 1; i < gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if (pPlayer) + { + if (Matcher_NamesMatch( inputdata.value.String(), pPlayer->GetPlayerName() )) + { + GetPlayerInfo( pPlayer ); + return; + } + } + } +} + +void CLogicPlayerInfo::GetPlayerInfo( CBasePlayer *pPlayer ) +{ + m_OutUserID.Set( pPlayer->GetUserID(), pPlayer, this ); + + m_OutPlayerName.Set( AllocPooledString(pPlayer->GetPlayerName()), pPlayer, this ); + + m_OutPlayerEntity.Set( pPlayer, pPlayer, this ); +} +#endif + void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID) { @@ -7922,6 +8619,17 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi pOut->m_Int = ( data & mask ); } + +#ifdef MAPBASE +// Needs to shift bits since network table only sends the player ones +void SendProxy_ShiftPlayerSpawnflags( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) +{ + int *pInt = (int *)pVarData; + + pOut->m_Int = (*pInt) >> 16; +} +#endif + // -------------------------------------------------------------------------------- // // SendTable for CPlayerState. // -------------------------------------------------------------------------------- // @@ -7978,6 +8686,14 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi SendPropInt ( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ), SendPropFloat ( SENDINFO( m_flLaggedMovementValue ), 0, SPROP_NOSCALE ), +#ifdef MAPBASE + // Transmitted from the server for internal player spawnflags. + // See baseplayer_shared.h for more details. + SendPropInt ( SENDINFO( m_spawnflags ), 3, SPROP_UNSIGNED, SendProxy_ShiftPlayerSpawnflags ), + + SendPropBool ( SENDINFO( m_bDrawPlayerModelExternally ) ), +#endif + END_SEND_TABLE() @@ -8015,6 +8731,9 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi SendPropArray ( SendPropEHandle( SENDINFO_ARRAY( m_hViewModel ) ), m_hViewModel ), SendPropString (SENDINFO(m_szLastPlaceName) ), + // Postprocess data + SendPropEHandle( SENDINFO( m_hPostProcessCtrl ) ), + #if defined USES_ECON_ITEMS SendPropUtlVector( SENDINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, SendPropEHandle( NULL, 0 ) ), #endif // USES_ECON_ITEMS @@ -8506,6 +9225,18 @@ void CBasePlayer::SetDefaultFOV( int FOV ) m_iDefaultFOV = ( FOV == 0 ) ? g_pGameRules->DefaultFOV() : FOV; } +#ifdef MAPBASE_VSCRIPT +void CBasePlayer::ScriptSetFOV(int iFOV, float flRate) +{ + m_iFOVStart = GetFOV(); + + m_flFOVTime = gpGlobals->curtime; + m_iFOV = iFOV; + + m_Local.m_flFOVRate = flRate; +} +#endif + //----------------------------------------------------------------------------- // Purpose: // static func // Input : set - @@ -8697,6 +9428,19 @@ void CBasePlayer::InputSetHUDVisibility( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetSuppressAttacks( inputdata_t &inputdata ) +{ + inputdata.value.Bool() ? + AddSpawnFlags( SF_PLAYER_SUPPRESS_FIRING ) : + RemoveSpawnFlags( SF_PLAYER_SUPPRESS_FIRING ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Set the fog controller data per player. // Input : &inputdata - @@ -8720,6 +9464,37 @@ void CBasePlayer::InitFogController( void ) m_Local.m_PlayerFog.m_hCtrl = FogSystem()->GetMasterFogController(); } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBasePlayer::InitPostProcessController( void ) +{ + // Setup with the default master controller. + m_hPostProcessCtrl = PostProcessSystem()->GetMasterPostProcessController(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetPostProcessController( inputdata_t& inputdata ) +{ + // Find the postprocess controller with the given name. + CPostProcessController* pController = NULL; + if (inputdata.value.FieldType() == FIELD_EHANDLE) + { + pController = dynamic_cast(inputdata.value.Entity().Get()); + } + else + { + pController = dynamic_cast(gEntList.FindEntityByName( NULL, inputdata.value.String() )); + } + + if (pController) + { + m_hPostProcessCtrl.Set( pController ); + } +} + //----------------------------------------------------------------------------- // Purpose: // Input : *pEntity - @@ -8857,6 +9632,14 @@ bool CBasePlayer::HandleVoteCommands( const CCommand &args ) //----------------------------------------------------------------------------- const char *CBasePlayer::GetNetworkIDString() { + //Tony; bots don't have network id's, and this can potentially crash, especially with plugins creating them. + if (IsBot()) + return "__BOT__"; + + //Tony; if networkidstring is null for any reason, the strncpy will crash! + if (!m_szNetworkIDString) + return "NULLID"; + const char *pStr = engine->GetPlayerNetworkIDString( edict() ); Q_strncpy( m_szNetworkIDString, pStr ? pStr : "", sizeof(m_szNetworkIDString) ); return m_szNetworkIDString; diff --git a/mp/src/game/server/player.h b/mp/src/game/server/player.h index e87af06d..b425f388 100644 --- a/mp/src/game/server/player.h +++ b/mp/src/game/server/player.h @@ -233,7 +233,6 @@ private: CBasePlayer *m_pParent; }; - class CBasePlayer : public CBaseCombatCharacter { public: @@ -245,6 +244,8 @@ protected: public: DECLARE_DATADESC(); DECLARE_SERVERCLASS(); + // script description + DECLARE_ENT_SCRIPTDESC(); CBasePlayer(); ~CBasePlayer(); @@ -265,6 +266,10 @@ public: void HideViewModels( void ); void DestroyViewModels( void ); +#ifdef MAPBASE + virtual void CreateHandModel( int viewmodelindex = 1, int iOtherVm = 0 ); +#endif + CPlayerState *PlayerData( void ) { return &pl; } int RequiredEdictIndex( void ) { return ENTINDEX(edict()); } @@ -289,6 +294,11 @@ public: virtual void SharedSpawn(); // Shared between client and server. virtual void ForceRespawn( void ); +#ifdef MAPBASE + // For the logic_playerproxy output + virtual void SpawnedAtPoint( CBaseEntity *pSpawnPoint ) {} +#endif + virtual void InitialSpawn( void ); virtual void InitHUD( void ) {} virtual void ShowViewPortPanel( const char * name, bool bShow = true, KeyValues *data = NULL ); @@ -383,6 +393,23 @@ public: void ShowViewModel( bool bShow ); void ShowCrosshair( bool bShow ); + bool ScriptIsPlayerNoclipping(void); + +#ifdef MAPBASE_VSCRIPT + HSCRIPT VScriptGetExpresser(); + + int GetButtons() { return m_nButtons; } + int GetButtonPressed() { return m_afButtonPressed; } + int GetButtonReleased() { return m_afButtonReleased; } + int GetButtonLast() { return m_afButtonLast; } + int GetButtonDisabled() { return m_afButtonDisabled; } + int GetButtonForced() { return m_afButtonForced; } + + const Vector& ScriptGetEyeForward() { static Vector vecForward; EyeVectors( &vecForward, NULL, NULL ); return vecForward; } + const Vector& ScriptGetEyeRight() { static Vector vecRight; EyeVectors( NULL, &vecRight, NULL ); return vecRight; } + const Vector& ScriptGetEyeUp() { static Vector vecUp; EyeVectors( NULL, NULL, &vecUp ); return vecUp; } +#endif + // View model prediction setup void CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ); @@ -416,6 +443,9 @@ public: virtual bool Weapon_ShouldSelectItem( CBaseCombatWeapon *pWeapon ); void Weapon_DropSlot( int weaponSlot ); CBaseCombatWeapon *GetLastWeapon( void ) { return m_hLastWeapon.Get(); } +#ifdef MAPBASE + virtual Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); +#endif virtual void OnMyWeaponFired( CBaseCombatWeapon *weapon ); // call this when this player fires a weapon to allow other systems to react virtual float GetTimeSinceWeaponFired( void ) const; // returns the time, in seconds, since this player fired a weapon @@ -550,8 +580,9 @@ public: virtual void PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize = true ) {} virtual void ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldindThis = NULL ) {} virtual float GetHeldObjectMass( IPhysicsObject *pHeldObject ); + virtual CBaseEntity *GetHeldObject( void ); - void CheckSuitUpdate(); + virtual void CheckSuitUpdate(); void SetSuitUpdate(const char *name, int fgroup, int iNoRepeat); virtual void UpdateGeigerCounter( void ); void CheckTimeBasedDamage( void ); @@ -561,6 +592,10 @@ public: virtual Vector GetAutoaimVector( float flScale ); virtual Vector GetAutoaimVector( float flScale, float flMaxDist ); virtual void GetAutoaimVector( autoaim_params_t ¶ms ); +#ifdef MAPBASE_VSCRIPT + Vector ScriptGetAutoaimVector( float flScale ) { return GetAutoaimVector( flScale ); } + Vector ScriptGetAutoaimVectorCustomMaxDist( float flScale, float flMaxDist ) { return GetAutoaimVector( flScale, flMaxDist ); } +#endif float GetAutoaimScore( const Vector &eyePosition, const Vector &viewDir, const Vector &vecTarget, CBaseEntity *pTarget, float fScale, CBaseCombatWeapon *pActiveWeapon ); QAngle AutoaimDeflection( Vector &vecSrc, autoaim_params_t ¶ms ); @@ -621,6 +656,10 @@ public: void PlayWearableAnimsForPlaybackEvent( wearableanimplayback_t iPlayback ); #endif +#ifdef MAPBASE + bool ShouldUseVisibilityCache( CBaseEntity *pEntity ); +#endif + public: // Player Physics Shadow void SetupVPhysicsShadow( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity, CPhysCollide *pStandModel, const char *pStandHullName, CPhysCollide *pCrouchModel, const char *pCrouchHullName ); @@ -662,7 +701,7 @@ public: bool IsConnected() const { return m_iConnected != PlayerDisconnected; } bool IsDisconnecting() const { return m_iConnected == PlayerDisconnecting; } bool IsSuitEquipped() const { return m_Local.m_bWearingSuit; } - int ArmorValue() const { return m_ArmorValue; } + virtual int ArmorValue() const { return m_ArmorValue; } bool HUDNeedsRestart() const { return m_fInitHUD; } float MaxSpeed() const { return m_flMaxspeed; } Activity GetActivity( ) const { return m_Activity; } @@ -742,6 +781,10 @@ public: int GetDefaultFOV( void ) const; // Default FOV if not specified otherwise int GetFOVForNetworking( void ); // Get the current FOV used for network computations bool SetFOV( CBaseEntity *pRequester, int FOV, float zoomRate = 0.0f, int iZoomStart = 0 ); // Alters the base FOV of the player (must have a valid requester) +#ifdef MAPBASE_VSCRIPT + void ScriptSetFOV(int iFOV, float flSpeed); // Overrides player FOV, ignores zoom owner + HSCRIPT ScriptGetFOVOwner() { return ToHScript(m_hZoomOwner); } +#endif void SetDefaultFOV( int FOV ); // Sets the base FOV if nothing else is affecting it by zooming CBaseEntity *GetFOVOwner( void ) { return m_hZoomOwner; } float GetFOVDistanceAdjustFactor(); // shared between client and server @@ -768,6 +811,9 @@ public: void InputSetHealth( inputdata_t &inputdata ); void InputSetHUDVisibility( inputdata_t &inputdata ); void InputHandleMapEvent( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetSuppressAttacks( inputdata_t &inputdata ); +#endif surfacedata_t *GetSurfaceData( void ) { return m_pSurfaceData; } void SetLadderNormal( Vector vecLadderNormal ) { m_vecLadderNormal = vecLadderNormal; } @@ -842,6 +888,10 @@ public: void InitFogController( void ); void InputSetFogController( inputdata_t &inputdata ); + CNetworkHandle( CPostProcessController, m_hPostProcessCtrl ); // active postprocessing controller + void InitPostProcessController( void ); + void InputSetPostProcessController( inputdata_t& inputdata ); + // Used by env_soundscape_triggerable to manage when the player is touching multiple // soundscape triggers simultaneously. // The one at the HEAD of the list is always the current soundscape for the player. @@ -897,6 +947,10 @@ public: int GetNumWearables( void ) const { return m_hMyWearables.Count(); } #endif +#ifdef MAPBASE + bool m_bInTriggerFall; +#endif + private: Activity m_Activity; @@ -1100,6 +1154,10 @@ public: float m_flSideMove; int m_nNumCrateHudHints; +#ifdef MAPBASE + CNetworkVar( bool, m_bDrawPlayerModelExternally ); +#endif + private: // Used in test code to teleport the player to random locations in the map. @@ -1224,6 +1282,23 @@ private: public: virtual unsigned int PlayerSolidMask( bool brushOnly = false ) const; // returns the solid mask for the given player, so bots can have a more-restrictive set +private: + // + //Tony; new tonemap controller changes, specifically for multiplayer. + // + void ClearTonemapParams(); //Tony; we need to clear our tonemap params every time we spawn to -1, if we trigger an input, the values will be set again. +public: + void InputSetTonemapScale( inputdata_t &inputdata ); //Set m_Local. +// void InputBlendTonemapScale( inputdata_t &inputdata ); //TODO; this should be calculated on the client, if we use it; perhaps an entity message would suffice? .. hmm.. + void InputSetTonemapRate( inputdata_t &inputdata ); + void InputSetAutoExposureMin( inputdata_t &inputdata ); + void InputSetAutoExposureMax( inputdata_t &inputdata ); + void InputSetBloomScale( inputdata_t &inputdata ); + + //Tony; restore defaults (set min/max to -1.0 so nothing gets overridden) + void InputUseDefaultAutoExposure( inputdata_t &inputdata ); + void InputUseDefaultBloomScale( inputdata_t &inputdata ); + }; typedef CHandle CBasePlayerHandle; diff --git a/mp/src/game/server/player_command.cpp b/mp/src/game/server/player_command.cpp index bf77b5d4..587e6178 100644 --- a/mp/src/game/server/player_command.cpp +++ b/mp/src/game/server/player_command.cpp @@ -18,6 +18,13 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +// This turned out to be causing major issues with VPhysics collision. +// It's deactivated until a fix is found. +// See prediction.cpp as well. +//#define PLAYER_COMMAND_FIX 1 +#endif + extern IGameMovement *g_pGameMovement; extern CMoveData *g_pMoveData; // This is a global because it is subclassed by each game. extern ConVar sv_noclipduringpause; @@ -53,21 +60,26 @@ void CPlayerMove::StartCommand( CBasePlayer *player, CUserCmd *cmd ) #if defined (HL2_DLL) // pull out backchannel data and move this out - int i; - for (i = 0; i < cmd->entitygroundcontact.Count(); i++) + // Let's not bother with IK Ground Contact Info in MP games -- the system needs to be re-worked, every client sends down the same info for each entity, so how would it determine which to use? + if ( 1 == gpGlobals->maxClients ) { - int entindex = cmd->entitygroundcontact[i].entindex; - CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( entindex) ); - if (pEntity) + int i; + for (i = 0; i < cmd->entitygroundcontact.Count(); i++) { - CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); - if (pAnimating) + int entindex = cmd->entitygroundcontact[i].entindex; + CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( entindex) ); + if (pEntity) { - pAnimating->SetIKGroundContactInfo( cmd->entitygroundcontact[i].minheight, cmd->entitygroundcontact[i].maxheight ); + CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); + if (pAnimating) + { + pAnimating->SetIKGroundContactInfo( cmd->entitygroundcontact[i].minheight, cmd->entitygroundcontact[i].maxheight ); + } } } } + #endif } @@ -418,6 +430,13 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper player->pl.v_angle = ucmd->viewangles + player->pl.anglechange; } +#ifdef PLAYER_COMMAND_FIX + // Let server invoke any needed impact functions + VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" ); + moveHelper->ProcessImpacts(); + VPROF_SCOPE_END(); +#endif + // Call standard client pre-think RunPreThink( player ); @@ -439,6 +458,10 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper VPROF( "pVehicle->ProcessMovement()" ); pVehicle->ProcessMovement( player, g_pMoveData ); } + +#ifdef PLAYER_COMMAND_FIX + RunPostThink( player ); +#endif // Copy output FinishMove( player, ucmd, g_pMoveData ); @@ -449,12 +472,14 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper player->pl.v_angle = player->GetLockViewanglesData(); } +#ifndef PLAYER_COMMAND_FIX // Let server invoke any needed impact functions VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" ); moveHelper->ProcessImpacts(); VPROF_SCOPE_END(); RunPostThink( player ); +#endif g_pGameMovement->FinishTrackPredictionErrors( player ); diff --git a/mp/src/game/server/playerinfomanager.cpp b/mp/src/game/server/playerinfomanager.cpp index 903efb6f..69917511 100644 --- a/mp/src/game/server/playerinfomanager.cpp +++ b/mp/src/game/server/playerinfomanager.cpp @@ -9,6 +9,16 @@ #include "playerinfomanager.h" #include "edict.h" +#if defined( TF_DLL ) +#include "tf_shareddefs.h" +#elif defined( CSTRIKE_DLL ) +#include "weapon_csbase.h" +#elif defined( DOD_DLL ) +#include "weapon_dodbase.h" +#elif defined( SDK_DLL ) +#include "weapon_sdkbase.h" +#endif + extern CGlobalVars *gpGlobals; static CPlayerInfoManager s_PlayerInfoManager; static CPluginBotManager s_BotManager; @@ -19,74 +29,43 @@ namespace // // Old version support // - abstract_class IPlayerInfo_V1 - { - public: - // returns the players name (UTF-8 encoded) - virtual const char *GetName() = 0; - // returns the userid (slot number) - virtual int GetUserID() = 0; - // returns the string of their network (i.e Steam) ID - virtual const char *GetNetworkIDString() = 0; - // returns the team the player is on - virtual int GetTeamIndex() = 0; - // changes the player to a new team (if the game dll logic allows it) - virtual void ChangeTeam( int iTeamNum ) = 0; - // returns the number of kills this player has (exact meaning is mod dependent) - virtual int GetFragCount() = 0; - // returns the number of deaths this player has (exact meaning is mod dependent) - virtual int GetDeathCount() = 0; - // returns if this player slot is actually valid - virtual bool IsConnected() = 0; - // returns the armor/health of the player (exact meaning is mod dependent) - virtual int GetArmorValue() = 0; - }; - - abstract_class IPlayerInfoManager_V1 - { - public: - virtual IPlayerInfo_V1 *GetPlayerInfo( edict_t *pEdict ) = 0; - }; - - class CPlayerInfoManager_V1: public IPlayerInfoManager_V1 - { - public: - virtual IPlayerInfo_V1 *GetPlayerInfo( edict_t *pEdict ); - }; - - static CPlayerInfoManager_V1 s_PlayerInfoManager_V1; - - - IPlayerInfo_V1 *CPlayerInfoManager_V1::GetPlayerInfo( edict_t *pEdict ) - { - CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); - if ( pPlayer ) - { - return (IPlayerInfo_V1 *)pPlayer->GetPlayerInfo(); - } - else - { - return NULL; - } - } - - EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPlayerInfoManager_V1, IPlayerInfoManager_V1, "PlayerInfoManager001", s_PlayerInfoManager_V1); + //Tony; pulled out version 1 and 2 support for orange box, we're starting fresh now with v3 player and v2 of the bot interface. } IPlayerInfo *CPlayerInfoManager::GetPlayerInfo( edict_t *pEdict ) { CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); if ( pPlayer ) - { return pPlayer->GetPlayerInfo(); - } else - { return NULL; - } +} +IPlayerInfo *CPlayerInfoManager::GetPlayerInfo( int index ) +{ + return GetPlayerInfo( engine->PEntityOfEntIndex( index ) ); } +// Games implementing advanced bot support should override this. +int CPlayerInfoManager::AliasToWeaponId(const char *weaponName) +{ + //Tony; TF doesn't support this. Should it? +#if defined ( CSTRIKE_DLL ) || defined ( DOD_DLL ) || defined ( SDK_DLL ) + return AliasToWeaponID(weaponName); +#endif + return -1; +} + +// Games implementing advanced bot support should override this. +const char *CPlayerInfoManager::WeaponIdToAlias(int weaponId) +{ +#if defined( TF_DLL ) + return WeaponIdToAlias(weaponId); +#elif defined ( CSTRIKE_DLL ) || defined ( DOD_DLL ) || defined ( SDK_DLL ) + return WeaponIDToAlias(weaponId); +#endif + return "MOD_DIDNT_IMPLEMENT_ME"; +} CGlobalVars *CPlayerInfoManager::GetGlobalVars() { return gpGlobals; @@ -121,8 +100,8 @@ edict_t *CPluginBotManager::CreateBot( const char *botname ) pPlayer->ClearFlags(); pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT ); - pPlayer->ChangeTeam( TEAM_UNASSIGNED ); + pPlayer->AddEFlags( EFL_PLUGIN_BASED_BOT ); // Mark it as a plugin based bot pPlayer->RemoveAllItems( true ); pPlayer->Spawn(); diff --git a/mp/src/game/server/playerinfomanager.h b/mp/src/game/server/playerinfomanager.h index 33eb1fa9..2b110562 100644 --- a/mp/src/game/server/playerinfomanager.h +++ b/mp/src/game/server/playerinfomanager.h @@ -19,7 +19,13 @@ class CPlayerInfoManager: public IPlayerInfoManager { public: virtual IPlayerInfo *GetPlayerInfo( edict_t *pEdict ); + virtual IPlayerInfo *GetPlayerInfo( int index ); virtual CGlobalVars *GetGlobalVars(); + // accessor to hook into aliastoweaponid + virtual int AliasToWeaponId(const char *weaponName); + // accessor to hook into weaponidtoalias + virtual const char *WeaponIdToAlias(int weaponId); + }; class CPluginBotManager: public IBotManager diff --git a/mp/src/game/server/playerlocaldata.cpp b/mp/src/game/server/playerlocaldata.cpp index ac7cf5e9..3b9aea08 100644 --- a/mp/src/game/server/playerlocaldata.cpp +++ b/mp/src/game/server/playerlocaldata.cpp @@ -59,6 +59,11 @@ BEGIN_SEND_TABLE_NOBASE( CPlayerLocalData, DT_Local ) // 3d skybox data SendPropInt(SENDINFO_STRUCTELEM(m_skybox3d.scale), 12), SendPropVector (SENDINFO_STRUCTELEM(m_skybox3d.origin), -1, SPROP_COORD), +#ifdef MAPBASE + SendPropVector (SENDINFO_STRUCTELEM(m_skybox3d.angles), -1, SPROP_COORD), + SendPropEHandle (SENDINFO_STRUCTELEM(m_skybox3d.skycamera)), + SendPropInt (SENDINFO_STRUCTELEM(m_skybox3d.skycolor), 32, (SPROP_COORD|SPROP_UNSIGNED), SendProxy_Color32ToInt), +#endif SendPropInt (SENDINFO_STRUCTELEM(m_skybox3d.area), 8, SPROP_UNSIGNED ), SendPropInt( SENDINFO_STRUCTELEM( m_skybox3d.fog.enable ), 1, SPROP_UNSIGNED ), SendPropInt( SENDINFO_STRUCTELEM( m_skybox3d.fog.blend ), 1, SPROP_UNSIGNED ), @@ -68,6 +73,9 @@ BEGIN_SEND_TABLE_NOBASE( CPlayerLocalData, DT_Local ) SendPropFloat( SENDINFO_STRUCTELEM( m_skybox3d.fog.start ), 0, SPROP_NOSCALE ), SendPropFloat( SENDINFO_STRUCTELEM( m_skybox3d.fog.end ), 0, SPROP_NOSCALE ), SendPropFloat( SENDINFO_STRUCTELEM( m_skybox3d.fog.maxdensity ), 0, SPROP_NOSCALE ), +#ifdef MAPBASE + SendPropFloat( SENDINFO_STRUCTELEM( m_skybox3d.fog.farz ), 0, SPROP_NOSCALE ), +#endif SendPropEHandle( SENDINFO_STRUCTELEM( m_PlayerFog.m_hCtrl ) ), @@ -83,6 +91,14 @@ BEGIN_SEND_TABLE_NOBASE( CPlayerLocalData, DT_Local ) SendPropInt( SENDINFO_STRUCTELEM( m_audio.soundscapeIndex ), 17, 0 ), SendPropInt( SENDINFO_STRUCTELEM( m_audio.localBits ), NUM_AUDIO_LOCAL_SOUNDS, SPROP_UNSIGNED ), SendPropEHandle( SENDINFO_STRUCTELEM( m_audio.ent ) ), + + //Tony; tonemap stuff! -- TODO! Optimize this with bit sizes from env_tonemap_controller. + SendPropFloat ( SENDINFO_STRUCTELEM( m_TonemapParams.m_flTonemapScale ), 0, SPROP_NOSCALE ), + SendPropFloat ( SENDINFO_STRUCTELEM( m_TonemapParams.m_flTonemapRate ), 0, SPROP_NOSCALE ), + SendPropFloat ( SENDINFO_STRUCTELEM( m_TonemapParams.m_flBloomScale ), 0, SPROP_NOSCALE ), + + SendPropFloat ( SENDINFO_STRUCTELEM( m_TonemapParams.m_flAutoExposureMin ), 0, SPROP_NOSCALE ), + SendPropFloat ( SENDINFO_STRUCTELEM( m_TonemapParams.m_flAutoExposureMax ), 0, SPROP_NOSCALE ), END_SEND_TABLE() BEGIN_SIMPLE_DATADESC( fogplayerparams_t ) @@ -119,6 +135,11 @@ BEGIN_SIMPLE_DATADESC( sky3dparams_t ) DEFINE_FIELD( scale, FIELD_INTEGER ), DEFINE_FIELD( origin, FIELD_VECTOR ), +#ifdef MAPBASE + DEFINE_FIELD( angles, FIELD_VECTOR ), + DEFINE_FIELD( skycamera, FIELD_EHANDLE ), + DEFINE_FIELD( skycolor, FIELD_COLOR32 ), +#endif DEFINE_FIELD( area, FIELD_INTEGER ), DEFINE_EMBEDDED( fog ), @@ -133,6 +154,16 @@ BEGIN_SIMPLE_DATADESC( audioparams_t ) END_DATADESC() +//Tony; tonepam params!! +BEGIN_SIMPLE_DATADESC ( tonemap_params_t ) + DEFINE_FIELD( m_flTonemapScale, FIELD_FLOAT ), + DEFINE_FIELD( m_flTonemapRate, FIELD_FLOAT ), + DEFINE_FIELD( m_flBloomScale, FIELD_FLOAT ), + DEFINE_FIELD( m_flAutoExposureMin, FIELD_FLOAT ), + DEFINE_FIELD( m_flAutoExposureMax, FIELD_FLOAT ), +END_DATADESC() + + BEGIN_SIMPLE_DATADESC( CPlayerLocalData ) DEFINE_AUTO_ARRAY( m_chAreaBits, FIELD_CHARACTER ), DEFINE_AUTO_ARRAY( m_chAreaPortalBits, FIELD_CHARACTER ), @@ -159,6 +190,9 @@ BEGIN_SIMPLE_DATADESC( CPlayerLocalData ) DEFINE_EMBEDDED( m_PlayerFog ), DEFINE_EMBEDDED( m_fog ), DEFINE_EMBEDDED( m_audio ), + + //Tony; added + DEFINE_EMBEDDED( m_TonemapParams ), // "Why don't we save this field, grandpa?" // @@ -223,6 +257,18 @@ void ClientData_Update( CBasePlayer *pl ) // HACKHACK: for 3d skybox // UNDONE: Support multiple sky cameras? CSkyCamera *pSkyCamera = GetCurrentSkyCamera(); +#ifdef MAPBASE + // Needs null protection now that the sky can go from valid to null + if ( !pSkyCamera ) + { + pl->m_Local.m_skybox3d.area = 255; + } + else if ( pSkyCamera != pl->m_Local.m_pOldSkyCamera ) + { + pl->m_Local.m_pOldSkyCamera = pSkyCamera; + pl->m_Local.m_skybox3d.CopyFrom(pSkyCamera->m_skyboxData); + } +#else if ( pSkyCamera != pl->m_Local.m_pOldSkyCamera ) { pl->m_Local.m_pOldSkyCamera = pSkyCamera; @@ -232,6 +278,7 @@ void ClientData_Update( CBasePlayer *pl ) { pl->m_Local.m_skybox3d.area = 255; } +#endif } diff --git a/mp/src/game/server/playerlocaldata.h b/mp/src/game/server/playerlocaldata.h index 04dc3a8f..587dbd1a 100644 --- a/mp/src/game/server/playerlocaldata.h +++ b/mp/src/game/server/playerlocaldata.h @@ -15,6 +15,7 @@ #include "playernet_vars.h" #include "networkvar.h" #include "fogcontroller.h" +#include "postprocesscontroller.h" //----------------------------------------------------------------------------- // Purpose: Player specific data ( sent only to local player, too ) @@ -83,6 +84,9 @@ public: // audio environment CNetworkVarEmbedded( audioparams_t, m_audio ); + //Tony; added so tonemap controller can work in multiplayer with inputs. + CNetworkVarEmbedded( tonemap_params_t, m_TonemapParams ); + CNetworkVar( bool, m_bSlowMovement ); }; diff --git a/mp/src/game/server/point_camera.cpp b/mp/src/game/server/point_camera.cpp index df4338b5..626bc61b 100644 --- a/mp/src/game/server/point_camera.cpp +++ b/mp/src/game/server/point_camera.cpp @@ -51,6 +51,13 @@ CPointCamera::CPointCamera() m_bFogEnable = false; +#ifdef MAPBASE + // Equivalent to SKYBOX_2DSKYBOX_VISIBLE, the original sky setting + m_iSkyMode = 2; + + m_iszRenderTarget = AllocPooledString( "_rt_Camera" ); +#endif + g_PointCameraList.Insert( this ); } @@ -187,6 +194,13 @@ void CPointCamera::InputSetOnAndTurnOthersOff( inputdata_t &inputdata ) while ((pEntity = gEntList.FindEntityByClassname( pEntity, "point_camera" )) != NULL) { CPointCamera *pCamera = (CPointCamera*)pEntity; + +#ifdef MAPBASE + // Do not turn off cameras which use different render targets + if (pCamera->m_iszRenderTarget.Get() != m_iszRenderTarget.Get()) + continue; +#endif + pCamera->InputSetOff( inputdata ); } @@ -222,6 +236,10 @@ BEGIN_DATADESC( CPointCamera ) DEFINE_KEYFIELD( m_flFogEnd, FIELD_FLOAT, "fogEnd" ), DEFINE_KEYFIELD( m_flFogMaxDensity, FIELD_FLOAT, "fogMaxDensity" ), DEFINE_KEYFIELD( m_bUseScreenAspectRatio, FIELD_BOOLEAN, "UseScreenAspectRatio" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iSkyMode, FIELD_INTEGER, "SkyMode" ), + DEFINE_KEYFIELD( m_iszRenderTarget, FIELD_STRING, "RenderTarget" ), +#endif DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ), @@ -237,6 +255,10 @@ BEGIN_DATADESC( CPointCamera ) DEFINE_INPUTFUNC( FIELD_VOID, "SetOnAndTurnOthersOff", InputSetOnAndTurnOthersOff ), DEFINE_INPUTFUNC( FIELD_VOID, "SetOn", InputSetOn ), DEFINE_INPUTFUNC( FIELD_VOID, "SetOff", InputSetOff ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSkyMode", InputSetSkyMode ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRenderTarget", InputSetRenderTarget ), +#endif END_DATADESC() @@ -250,4 +272,215 @@ IMPLEMENT_SERVERCLASS_ST( CPointCamera, DT_PointCamera ) SendPropFloat( SENDINFO( m_flFogMaxDensity ), 0, SPROP_NOSCALE ), SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bUseScreenAspectRatio ), 1, SPROP_UNSIGNED ), +#ifdef MAPBASE + SendPropInt( SENDINFO( m_iSkyMode ) ), + SendPropStringT( SENDINFO( m_iszRenderTarget ) ), +#endif END_SEND_TABLE() + +#ifdef MAPBASE + +//============================================================================= +// Orthographic point_camera +//============================================================================= + +BEGIN_DATADESC( CPointCameraOrtho ) + + DEFINE_KEYFIELD( m_bOrtho, FIELD_BOOLEAN, "IsOrtho" ), + DEFINE_ARRAY( m_OrthoDimensions, FIELD_FLOAT, CPointCameraOrtho::NUM_ORTHO_DIMENSIONS ), + + DEFINE_ARRAY( m_TargetOrtho, FIELD_FLOAT, CPointCameraOrtho::NUM_ORTHO_DIMENSIONS ), + DEFINE_FIELD( m_TargetOrthoDPS, FIELD_FLOAT ), + + DEFINE_FUNCTION( ChangeOrthoThink ), + + // Input + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetOrthoEnabled", InputSetOrthoEnabled ), + DEFINE_INPUTFUNC( FIELD_STRING, "ScaleOrtho", InputScaleOrtho ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetOrthoTop", InputSetOrthoTop ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetOrthoBottom", InputSetOrthoBottom ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetOrthoLeft", InputSetOrthoLeft ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetOrthoRight", InputSetOrthoRight ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CPointCameraOrtho, DT_PointCameraOrtho ) + SendPropInt( SENDINFO( m_bOrtho ), 1, SPROP_UNSIGNED ), + SendPropArray( SendPropFloat(SENDINFO_ARRAY(m_OrthoDimensions), CPointCameraOrtho::NUM_ORTHO_DIMENSIONS, SPROP_NOSCALE ), m_OrthoDimensions ), +END_SEND_TABLE() + +LINK_ENTITY_TO_CLASS( point_camera_ortho, CPointCameraOrtho ); + +CPointCameraOrtho::~CPointCameraOrtho() +{ +} + +CPointCameraOrtho::CPointCameraOrtho() +{ +} + +void CPointCameraOrtho::Spawn( void ) +{ + BaseClass::Spawn(); + + // If 0, get the FOV + if (m_OrthoDimensions[ORTHO_TOP] == 0.0f) + m_OrthoDimensions.Set( ORTHO_TOP, GetFOV() ); + + // If 0, get the negative top ortho + if (m_OrthoDimensions[ORTHO_BOTTOM] == 0.0f) + m_OrthoDimensions.Set( ORTHO_BOTTOM, -m_OrthoDimensions[ORTHO_TOP] ); + + // If 0, get the top ortho + if (m_OrthoDimensions[ORTHO_LEFT] == 0.0f) + m_OrthoDimensions.Set( ORTHO_LEFT, m_OrthoDimensions[ORTHO_TOP] ); + + // If 0, get the negative left ortho + if (m_OrthoDimensions[ORTHO_RIGHT] == 0.0f) + m_OrthoDimensions.Set( ORTHO_RIGHT, -m_OrthoDimensions[ORTHO_LEFT] ); +} + +bool CPointCameraOrtho::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( strncmp( szKeyName, "Ortho", 5 ) == 0 ) + { + int iOrtho = atoi(szKeyName + 5); + m_OrthoDimensions.Set( iOrtho, atof( szValue ) ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +bool CPointCameraOrtho::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if ( strncmp( szKeyName, "Ortho", 5 ) ) + { + int iOrtho = atoi(szKeyName + 5); + Q_snprintf( szValue, iMaxLen, "%f", m_OrthoDimensions[iOrtho] ); + } + else + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCameraOrtho::ChangeOrtho( int iType, const char *szChange ) +{ + // Parse the keyvalue data + char parseString[255]; + Q_strncpy( parseString, szChange, sizeof( parseString ) ); + + // Get Ortho + char *pszParam = strtok( parseString, " " ); + if (pszParam) + { + m_TargetOrtho[iType] = atof( pszParam ); + } + else + { + // Assume no change + m_TargetOrtho[iType] = m_OrthoDimensions[iType]; + } + + // Get Time + float flChangeTime; + pszParam = strtok( NULL, " " ); + if (pszParam) + { + flChangeTime = atof( pszParam ); + } + else + { + // Assume 1 second + flChangeTime = 1.0; + } + + m_TargetOrthoDPS = ( m_TargetOrtho[iType] - m_OrthoDimensions[iType] ) / flChangeTime; + + SetThink( &CPointCameraOrtho::ChangeOrthoThink ); + SetNextThink( gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCameraOrtho::InputScaleOrtho( inputdata_t &inputdata ) +{ + // Parse the keyvalue data + char parseString[255]; + Q_strncpy( parseString, inputdata.value.String(), sizeof( parseString ) ); + + // Get Scale + float flScale = 1.0f; + char *pszParam = strtok( parseString, " " ); + if (pszParam) + { + flScale = atof( pszParam ); + } + + // Get Time + float flChangeTime = 1.0f; + pszParam = strtok( NULL, " " ); + if (pszParam) + { + flChangeTime = atof( pszParam ); + } + + int iLargest = 0; + for (int i = 0; i < NUM_ORTHO_DIMENSIONS; i++) + { + m_TargetOrtho[i] = flScale * m_OrthoDimensions[i]; + + if (m_TargetOrtho[iLargest] <= m_TargetOrtho[i]) + iLargest = i; + } + + m_TargetOrthoDPS = (m_TargetOrtho[iLargest] - m_OrthoDimensions[iLargest]) / flChangeTime; + + SetThink( &CPointCameraOrtho::ChangeOrthoThink ); + SetNextThink( gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCameraOrtho::ChangeOrthoThink( void ) +{ + SetNextThink( gpGlobals->curtime + CAM_THINK_INTERVAL ); + + int iChanging = 0; + for (int i = 0; i < NUM_ORTHO_DIMENSIONS; i++) + { + float newDim = m_OrthoDimensions[i]; + if (newDim == m_TargetOrtho[i]) + continue; + + newDim += m_TargetOrthoDPS * CAM_THINK_INTERVAL; + + if (m_TargetOrthoDPS < 0) + { + if (newDim <= m_TargetOrtho[i]) + { + newDim = m_TargetOrtho[i]; + } + } + else + { + if (newDim >= m_TargetOrtho[i]) + { + newDim = m_TargetOrtho[i]; + } + } + + m_OrthoDimensions.Set(i, newDim); + } + + if (iChanging == 0) + SetThink( NULL ); +} +#endif diff --git a/mp/src/game/server/point_camera.h b/mp/src/game/server/point_camera.h index 499b3a36..c669ab82 100644 --- a/mp/src/game/server/point_camera.h +++ b/mp/src/game/server/point_camera.h @@ -37,6 +37,12 @@ public: void InputSetOnAndTurnOthersOff( inputdata_t &inputdata ); void InputSetOn( inputdata_t &inputdata ); void InputSetOff( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetSkyMode( inputdata_t &inputdata ) { m_iSkyMode = inputdata.value.Int(); } + void InputSetRenderTarget( inputdata_t &inputdata ) { m_iszRenderTarget = inputdata.value.StringID(); } + + float GetFOV() const { return m_FOV; } +#endif private: float m_TargetFOV; @@ -51,6 +57,10 @@ private: CNetworkVar( float, m_flFogMaxDensity ); CNetworkVar( bool, m_bActive ); CNetworkVar( bool, m_bUseScreenAspectRatio ); +#ifdef MAPBASE + CNetworkVar( int, m_iSkyMode ); + CNetworkVar( string_t, m_iszRenderTarget ); +#endif // Allows the mapmaker to control whether a camera is active or not bool m_bIsOn; @@ -59,5 +69,52 @@ public: CPointCamera *m_pNext; }; +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPointCameraOrtho : public CPointCamera +{ +public: + DECLARE_CLASS( CPointCameraOrtho, CPointCamera ); + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + CPointCameraOrtho(); + ~CPointCameraOrtho(); + + enum + { + ORTHO_TOP, + ORTHO_BOTTOM, + ORTHO_LEFT, + ORTHO_RIGHT, + + NUM_ORTHO_DIMENSIONS + }; + + void Spawn( void ); + + bool KeyValue( const char *szKeyName, const char *szValue ); + bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + + void ChangeOrtho( int iType, const char *szChange ); + void ChangeOrthoThink( void ); + + void InputSetOrthoEnabled( inputdata_t &inputdata ) { m_bOrtho = inputdata.value.Bool(); } + void InputScaleOrtho( inputdata_t &inputdata ); + void InputSetOrthoTop( inputdata_t &inputdata ) { ChangeOrtho(ORTHO_TOP, inputdata.value.String()); } + void InputSetOrthoBottom( inputdata_t &inputdata ) { ChangeOrtho( ORTHO_BOTTOM, inputdata.value.String() ); } + void InputSetOrthoLeft( inputdata_t &inputdata ) { ChangeOrtho( ORTHO_LEFT, inputdata.value.String() ); } + void InputSetOrthoRight( inputdata_t &inputdata ) { ChangeOrtho( ORTHO_RIGHT, inputdata.value.String() ); } + +private: + float m_TargetOrtho[NUM_ORTHO_DIMENSIONS]; + float m_TargetOrthoDPS; + + CNetworkVar( bool, m_bOrtho ); + CNetworkArray( float, m_OrthoDimensions, NUM_ORTHO_DIMENSIONS ); +}; +#endif + CPointCamera *GetPointCameraList(); #endif // CAMERA_H diff --git a/mp/src/game/server/point_devshot_camera.cpp b/mp/src/game/server/point_devshot_camera.cpp index 3e2e2dbd..4d476664 100644 --- a/mp/src/game/server/point_devshot_camera.cpp +++ b/mp/src/game/server/point_devshot_camera.cpp @@ -53,24 +53,6 @@ END_DATADESC() LINK_ENTITY_TO_CLASS( point_devshot_camera, CPointDevShotCamera ); -//----------------------------------------------------------------------------- -// Purpose: Convenience function so we don't have to make this check all over -//----------------------------------------------------------------------------- -static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) -{ - if ( gpGlobals->maxClients > 1 ) - { - if ( engine->IsDedicatedServer() ) - { - return NULL; - } - - return UTIL_GetListenServerHost(); - } - - return UTIL_GetLocalPlayer(); -} - //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -245,6 +227,10 @@ public: } } +#ifdef MAPBASE // VDC Memory Leak Fixes + pkvMapCameras->deleteThis(); +#endif + if ( !g_iDevShotCameraCount ) { Warning( "Devshots: No point_devshot_camera in %s. Moving to next map.\n", STRING( gpGlobals->mapname ) ); diff --git a/mp/src/game/server/point_entity_finder.cpp b/mp/src/game/server/point_entity_finder.cpp new file mode 100644 index 00000000..d26b71f4 --- /dev/null +++ b/mp/src/game/server/point_entity_finder.cpp @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// class CPointEntityFinder +// +// Purpose: Finds an entity using a specified heuristic and outputs it as !caller +// with the OnFoundEntity output. +//----------------------------------------------------------------------------- + +#include "cbase.h" +#include "filters.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +enum EntFinderMethod_t +{ + ENT_FIND_METHOD_NEAREST = 0, + ENT_FIND_METHOD_FARTHEST, + ENT_FIND_METHOD_RANDOM, +}; + +class CPointEntityFinder : public CBaseEntity +{ + void Activate( void ); + + DECLARE_CLASS( CPointEntityFinder, CBaseEntity ); + +private: + + EHANDLE m_hEntity; + string_t m_iFilterName; + CHandle m_hFilter; + string_t m_iRefName; + EHANDLE m_hReference; + + EntFinderMethod_t m_FindMethod; + + void FindEntity( void ); + void FindByDistance( void ); + void FindByRandom( void ); + + // Input handlers + void InputFindEntity( inputdata_t &inputdata ); + + // Output handlers + COutputEvent m_OnFoundEntity; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( point_entity_finder, CPointEntityFinder ); + +BEGIN_DATADESC( CPointEntityFinder ) + + DEFINE_KEYFIELD( m_FindMethod, FIELD_INTEGER, "method" ), + DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ), + DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iRefName, FIELD_STRING, "referencename" ), + DEFINE_FIELD( m_hReference, FIELD_EHANDLE ), + + DEFINE_OUTPUT( m_OnFoundEntity, "OnFoundEntity" ), + + //--------------------------------- + + DEFINE_INPUTFUNC( FIELD_VOID, "FindEntity", InputFindEntity ), + +END_DATADESC() + + +void CPointEntityFinder::Activate( void ) +{ + // Get the filter, if it exists. + if (m_iFilterName != NULL_STRING) + { + m_hFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iFilterName )); + } + + BaseClass::Activate(); +} + + +void CPointEntityFinder::FindEntity( void ) +{ + // Get the reference entity, if it exists. + if (m_iRefName != NULL_STRING) + { + m_hReference = gEntList.FindEntityByName( NULL, m_iRefName ); + } + + switch ( m_FindMethod ) + { + + case ( ENT_FIND_METHOD_NEAREST ): + FindByDistance(); + break; + case ( ENT_FIND_METHOD_FARTHEST ): + FindByDistance(); + break; + case ( ENT_FIND_METHOD_RANDOM ): + FindByRandom(); + break; + } +} + +void CPointEntityFinder::FindByDistance( void ) +{ + m_hEntity = NULL; + CBaseFilter *pFilter = m_hFilter.Get(); + +// go through each entity and determine whether it's closer or farther from the current entity. Pick according to Method selected. + + float flBestDist = 0; + CBaseEntity *pEntity = gEntList.FirstEnt(); + while ( pEntity ) + { + if ( FStrEq( STRING( pEntity->m_iClassname ), "worldspawn" ) + || FStrEq( STRING( pEntity->m_iClassname ), "soundent" ) + || FStrEq( STRING( pEntity->m_iClassname ), "player_manager" ) + || FStrEq( STRING( pEntity->m_iClassname ), "bodyque" ) + || FStrEq( STRING( pEntity->m_iClassname ), "ai_network" ) + || pEntity == this + || ( pFilter && !( pFilter->PassesFilter( this, pEntity ) ) ) ) + { + pEntity = gEntList.NextEnt( pEntity ); + continue; + } + + // if we have a reference entity, use that, otherwise, check against 'this' + Vector vecStart; + if ( m_hReference ) + { + vecStart = m_hReference->GetAbsOrigin(); + } + else + { + vecStart = GetAbsOrigin(); + } + + // init m_hEntity with a valid entity. + if (m_hEntity == NULL ) + { + m_hEntity = pEntity; + flBestDist = ( pEntity->GetAbsOrigin() - vecStart ).LengthSqr(); + } + + float flNewDist = ( pEntity->GetAbsOrigin() - vecStart ).LengthSqr(); + + switch ( m_FindMethod ) + { + + case ( ENT_FIND_METHOD_NEAREST ): + if ( flNewDist < flBestDist ) + { + m_hEntity = pEntity; + flBestDist = flNewDist; + } + break; + + case ( ENT_FIND_METHOD_FARTHEST ): + if ( flNewDist > flBestDist ) + { + m_hEntity = pEntity; + flBestDist = flNewDist; + } + break; + + default: + Assert( false ); + break; + } + + pEntity = gEntList.NextEnt( pEntity ); + } +} + +void CPointEntityFinder::FindByRandom( void ) +{ + // TODO: optimize the case where there is no filter + m_hEntity = NULL; + CBaseFilter *pFilter = m_hFilter.Get(); + CUtlVector ValidEnts; + + CBaseEntity *pEntity = gEntList.FirstEnt(); + do // note all valid entities. + { + if ( pFilter && pFilter->PassesFilter( this, pEntity ) ) + { + ValidEnts.AddToTail( pEntity ); + } + + pEntity = gEntList.NextEnt( pEntity ); + + } while ( pEntity ); + + // pick one at random + if ( ValidEnts.Count() != 0 ) + { + m_hEntity = ValidEnts[ RandomInt( 0, ValidEnts.Count() - 1 )]; + } +} + +void CPointEntityFinder::InputFindEntity( inputdata_t &inputdata ) +{ + FindEntity(); + + m_OnFoundEntity.FireOutput( inputdata.pActivator, m_hEntity ); +} \ No newline at end of file diff --git a/mp/src/game/server/point_spotlight.cpp b/mp/src/game/server/point_spotlight.cpp index 030537ed..f4bb60d6 100644 --- a/mp/src/game/server/point_spotlight.cpp +++ b/mp/src/game/server/point_spotlight.cpp @@ -26,6 +26,9 @@ public: DECLARE_DATADESC(); CPointSpotlight(); +#ifdef MAPBASE + ~CPointSpotlight(); +#endif void Precache(void); void Spawn(void); @@ -46,6 +49,9 @@ private: // ------------------------------ void InputLightOn( inputdata_t &inputdata ); void InputLightOff( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputLightToggle( inputdata_t &inputdata ) { m_bSpotlightOn ? InputLightOff(inputdata) : InputLightOn(inputdata); } +#endif // Creates the efficient spotlight void CreateEfficientSpotlight(); @@ -98,6 +104,9 @@ BEGIN_DATADESC( CPointSpotlight ) // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "LightOn", InputLightOn ), DEFINE_INPUTFUNC( FIELD_VOID, "LightOff", InputLightOff ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "LightToggle", InputLightToggle ), +#endif DEFINE_OUTPUT( m_OnOn, "OnLightOn" ), DEFINE_OUTPUT( m_OnOff, "OnLightOff" ), @@ -123,6 +132,16 @@ CPointSpotlight::CPointSpotlight() m_bIgnoreSolid = false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPointSpotlight::~CPointSpotlight() +{ + SpotlightDestroy(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: diff --git a/mp/src/game/server/point_template.cpp b/mp/src/game/server/point_template.cpp index b933bd2d..3cb654ca 100644 --- a/mp/src/game/server/point_template.cpp +++ b/mp/src/game/server/point_template.cpp @@ -55,14 +55,23 @@ BEGIN_DATADESC( CPointTemplate ) DEFINE_KEYFIELD( m_iszTemplateEntityNames[14], FIELD_STRING, "Template15"), DEFINE_KEYFIELD( m_iszTemplateEntityNames[15], FIELD_STRING, "Template16"), DEFINE_UTLVECTOR( m_hTemplateEntities, FIELD_CLASSPTR ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bFixupExpanded, FIELD_BOOLEAN, "FixupMode" ), +#endif DEFINE_UTLVECTOR( m_hTemplates, FIELD_EMBEDDED ), // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "ForceSpawn", InputForceSpawn ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "ForceSpawnRandomTemplate", InputForceSpawnRandomTemplate ), +#endif // Outputs DEFINE_OUTPUT( m_pOutputOnSpawned, "OnEntitySpawned" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_pOutputOutEntity, "OutSpawnedEntity" ), +#endif END_DATADESC() @@ -127,6 +136,8 @@ void PrecachePointTemplates() void CPointTemplate::Spawn( void ) { Precache(); + ScriptInstallPreSpawnHook(); + ValidateScriptScope(); } void CPointTemplate::Precache() @@ -339,7 +350,7 @@ bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecA // Some templates have Entity I/O connecting the entities within the template. // Unique versions of these templates need to be created whenever they're instanced. - if ( AllowNameFixup() && Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) ) + if ( AllowNameFixup() && ( Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) || m_ScriptScope.IsInitialized() ) ) { // This template requires instancing. // Create a new mapdata block and ask the template system to fill it in with @@ -375,7 +386,15 @@ bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecA pEntity->SetAbsOrigin( vecNewOrigin ); pEntity->SetAbsAngles( vecNewAngles ); - pSpawnList[i].m_pEntity = pEntity; + if (ScriptPreInstanceSpawn(&m_ScriptScope, pEntity, Templates_FindByIndex(iTemplateIndex))) + { + pSpawnList[i].m_pEntity = pEntity; + } + else + { + pSpawnList[i].m_pEntity = NULL; + UTIL_RemoveImmediate(pEntity); + } pSpawnList[i].m_nDepth = 0; pSpawnList[i].m_pDeferredParent = NULL; } @@ -393,6 +412,92 @@ bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecA return true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Spawn one of the entities I contain +// Input : &vecOrigin - +// &vecAngles - +// pEntities - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPointTemplate::CreateSpecificInstance( int iTemplate, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity **pOutEntity ) +{ + // Go through all our templated map data and spawn all the entities in it + int iTemplates = m_hTemplates.Count(); + if ( !iTemplates ) + { + Msg("CreateInstance called on a point_template that has no templates: %s\n", STRING(GetEntityName()) ); + return false; + } + + // Tell the template system we're about to start a new template + Templates_StartUniqueInstance(); + + CBaseEntity *pEntity = NULL; + char *pMapData; + int iTemplateIndex = m_hTemplates[iTemplate].iTemplateIndex; + + // Some templates have Entity I/O connecting the entities within the template. + // Unique versions of these templates need to be created whenever they're instanced. + if ( AllowNameFixup() && ( Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) || m_ScriptScope.IsInitialized() ) ) + { + // This template requires instancing. + // Create a new mapdata block and ask the template system to fill it in with + // a unique version (with fixed Entity I/O connections). + pMapData = Templates_GetEntityIOFixedMapData( iTemplateIndex ); + } + else + { + // Use the unmodified mapdata + pMapData = (char*)STRING( Templates_FindByIndex( iTemplateIndex ) ); + } + + // Create the entity from the mapdata + MapEntity_ParseEntity( pEntity, pMapData, NULL ); + if ( pEntity == NULL ) + { + Msg("Failed to initialize templated entity with mapdata: %s\n", pMapData ); + return false; + } + + // Get a matrix that'll convert from world to the new local space + VMatrix matNewTemplateToWorld, matStoredLocalToWorld; + matNewTemplateToWorld.SetupMatrixOrgAngles( vecOrigin, vecAngles ); + MatrixMultiply( matNewTemplateToWorld, m_hTemplates[iTemplate].matEntityToTemplate, matStoredLocalToWorld ); + + // Get the world origin & angles from the stored local coordinates + Vector vecNewOrigin; + QAngle vecNewAngles; + vecNewOrigin = matStoredLocalToWorld.GetTranslation(); + MatrixToAngles( matStoredLocalToWorld, vecNewAngles ); + + // Set its origin & angles + pEntity->SetAbsOrigin( vecNewOrigin ); + pEntity->SetAbsAngles( vecNewAngles ); + + // Spawn it + DispatchSpawn( pEntity ); + + if (pOutEntity) + { + *pOutEntity = pEntity; + } + + return true; +} +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CPointTemplate::CreationComplete( const CUtlVector &entities ) +{ + if ( !entities.Count() ) + return; + + ScriptPostSpawn( &m_ScriptScope, (CBaseEntity **)entities.Base(), entities.Count() ); +} + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -406,4 +511,104 @@ void CPointTemplate::InputForceSpawn( inputdata_t &inputdata ) // Fire our output m_pOutputOnSpawned.FireOutput( this, this ); + +#ifdef MAPBASE + for ( int i = 0; i < hNewEntities.Count(); i++ ) + { + m_pOutputOutEntity.Set(hNewEntities[i], hNewEntities[i], this); + } +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Randomly spawns one of our templates. +// This is copied from CreateInstance(). +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPointTemplate::InputForceSpawnRandomTemplate( inputdata_t &inputdata ) +{ + // Spawn our template + CBaseEntity *pEntity = NULL; + if ( !CreateSpecificInstance( RandomInt(0, GetNumTemplates() - 1), GetAbsOrigin(), GetAbsAngles(), &pEntity ) ) + return; + + // Fire our output + m_pOutputOnSpawned.FireOutput( this, this ); + m_pOutputOutEntity.Set(pEntity, pEntity, this); +} +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void ScriptInstallPreSpawnHook() +{ +#ifdef MAPBASE_VSCRIPT + if ( !g_pScriptVM ) + return; +#endif + +#ifdef IS_WINDOWS_PC + if ( !g_pScriptVM->ValueExists( "__ExecutePreSpawn " ) ) + { + //g_pScriptVM->Run( g_Script_spawn_helper ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: This function is called after a spawner creates its child entity +// but before the keyvalues are injected. This gives us an +// opportunity to change any keyvalues before the entity is +// configured and spawned. In this case, we see if there is a VScript +// that wants to change anything about this entity. +//----------------------------------------------------------------------------- +bool ScriptPreInstanceSpawn( CScriptScope *pScriptScope, CBaseEntity *pChild, string_t iszKeyValueData ) +{ + if ( !pScriptScope->IsInitialized() ) + return true; + + ScriptVariant_t result; + if ( pScriptScope->Call( "__ExecutePreSpawn", &result, ToHScript( pChild ) ) != SCRIPT_DONE ) + return true; + + if ( ( result.m_type == FIELD_BOOLEAN && !result.m_bool ) || ( result.m_type == FIELD_INTEGER && !result.m_int ) ) + return false; + + return true; + +} + +void ScriptPostSpawn( CScriptScope *pScriptScope, CBaseEntity **ppEntities, int nEntities ) +{ + if ( !pScriptScope->IsInitialized() ) + return; + + HSCRIPT hPostSpawnFunc = pScriptScope->LookupFunction( "PostSpawn" ); + + if ( !hPostSpawnFunc ) + return; + + ScriptVariant_t varEntityMakerResultTable; + if ( !g_pScriptVM->GetValue( *pScriptScope, "__EntityMakerResult", &varEntityMakerResultTable ) ) + return; + + if ( varEntityMakerResultTable.m_type != FIELD_HSCRIPT ) + return; + + HSCRIPT hEntityMakerResultTable = varEntityMakerResultTable.m_hScript; + char szEntName[256]; + for ( int i = 0; i < nEntities; i++ ) + { + V_strncpy( szEntName, ppEntities[i]->GetEntityNameAsCStr(), ARRAYSIZE(szEntName) ); + char *pAmpersand = V_strrchr( szEntName, '&' ); + if ( pAmpersand ) + *pAmpersand = 0; + g_pScriptVM->SetValue( hEntityMakerResultTable, szEntName, ToHScript( ppEntities[i] ) ); + } + pScriptScope->Call( hPostSpawnFunc, NULL, hEntityMakerResultTable ); + pScriptScope->Call( "__FinishSpawn" ); + g_pScriptVM->ReleaseValue( varEntityMakerResultTable ); + g_pScriptVM->ReleaseFunction( hPostSpawnFunc ); } diff --git a/mp/src/game/server/point_template.h b/mp/src/game/server/point_template.h index 94e37424..283a4b62 100644 --- a/mp/src/game/server/point_template.h +++ b/mp/src/game/server/point_template.h @@ -20,6 +20,10 @@ struct template_t DECLARE_SIMPLE_DATADESC(); }; +void ScriptInstallPreSpawnHook(); +bool ScriptPreInstanceSpawn( CScriptScope *pScriptScope, CBaseEntity *pChild, string_t iszKeyValueData ); +void ScriptPostSpawn( CScriptScope *pScriptScope, CBaseEntity **ppEntities, int nEntities ); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -42,6 +46,9 @@ public: void AddTemplate( CBaseEntity *pEntity, const char *pszMapData, int nLen ); bool ShouldRemoveTemplateEntities( void ); bool AllowNameFixup(); +#ifdef MAPBASE + bool NameFixupExpanded() { return m_bFixupExpanded; } +#endif // Templates accessors int GetNumTemplates( void ); @@ -49,9 +56,16 @@ public: // Template instancing bool CreateInstance( const Vector &vecOrigin, const QAngle &vecAngles, CUtlVector *pEntities ); +#ifdef MAPBASE + bool CreateSpecificInstance( int iTemplate, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity **pOutEntity ); +#endif + void CreationComplete(const CUtlVector& entities); // Inputs void InputForceSpawn( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputForceSpawnRandomTemplate( inputdata_t &inputdata ); +#endif virtual void PerformPrecache(); @@ -63,10 +77,19 @@ private: // code removes all the entities in it once it finishes turning them into templates. CUtlVector< CBaseEntity * > m_hTemplateEntities; +#ifdef MAPBASE + // Allows name fixup to target all instances of a name in a keyvalue, including output parameters. + // TODO: Support for multiple fixup modes? + bool m_bFixupExpanded; +#endif + // List of templates, generated from our template entities. CUtlVector< template_t > m_hTemplates; COutputEvent m_pOutputOnSpawned; +#ifdef MAPBASE + COutputEHANDLE m_pOutputOutEntity; +#endif }; #endif // POINT_TEMPLATE_H diff --git a/mp/src/game/server/pointhurt.cpp b/mp/src/game/server/pointhurt.cpp index 137f0432..ce103e86 100644 --- a/mp/src/game/server/pointhurt.cpp +++ b/mp/src/game/server/pointhurt.cpp @@ -30,6 +30,10 @@ public: void InputTurnOff(inputdata_t &inputdata); void InputToggle(inputdata_t &inputdata); void InputHurt(inputdata_t &inputdata); + +#ifdef MAPBASE + bool KeyValue( const char *szKeyName, const char *szValue ); +#endif DECLARE_DATADESC(); @@ -180,3 +184,21 @@ void CPointHurt::InputHurt( inputdata_t &data ) HurtThink(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CPointHurt::KeyValue( const char *szKeyName, const char *szValue ) +{ + // Additional OR flags + if (FStrEq( szKeyName, "damageor" ) || FStrEq( szKeyName, "damagepresets" )) + { + m_bitsDamageType |= atoi(szValue); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} +#endif + diff --git a/mp/src/game/server/pointteleport.cpp b/mp/src/game/server/pointteleport.cpp index 171ee780..e79f6fc3 100644 --- a/mp/src/game/server/pointteleport.cpp +++ b/mp/src/game/server/pointteleport.cpp @@ -23,7 +23,15 @@ class CPointTeleport : public CBaseEntity public: void Activate( void ); +#ifdef MAPBASE + void TeleportEntity( CBaseEntity *pTarget, const Vector &vecPosition, const QAngle &angAngles ); +#endif + void InputTeleport( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputTeleportEntity( inputdata_t &inputdata ); + void InputTeleportToCurrentPos( inputdata_t &inputdata ); +#endif private: @@ -45,6 +53,10 @@ BEGIN_DATADESC( CPointTeleport ) DEFINE_FIELD( m_vSaveAngles, FIELD_VECTOR ), DEFINE_INPUTFUNC( FIELD_VOID, "Teleport", InputTeleport ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "TeleportEntity", InputTeleportEntity ), + DEFINE_INPUTFUNC( FIELD_VOID, "TeleportToCurrentPos", InputTeleportToCurrentPos ), +#endif END_DATADESC() @@ -107,6 +119,34 @@ void CPointTeleport::Activate( void ) BaseClass::Activate(); } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPointTeleport::TeleportEntity( CBaseEntity *pTarget, const Vector &vecPosition, const QAngle &angAngles ) +{ + // in episodic, we have a special spawn flag that forces Gordon into a duck +#ifdef HL2_EPISODIC + if ( (m_spawnflags & SF_TELEPORT_INTO_DUCK) && pTarget->IsPlayer() ) + { + CBasePlayer *pPlayer = ToBasePlayer( pTarget ); + if ( pPlayer != NULL ) + { + pPlayer->m_nButtons |= IN_DUCK; + pPlayer->AddFlag( FL_DUCKING ); + pPlayer->m_Local.m_bDucked = true; + pPlayer->m_Local.m_bDucking = true; + pPlayer->m_Local.m_flDucktime = 0.0f; + pPlayer->SetViewOffset( VEC_DUCK_VIEW_SCALED( pPlayer ) ); + pPlayer->SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); + } + } +#endif + + pTarget->Teleport( &vecPosition, &angAngles, NULL ); +} +#endif + //------------------------------------------------------------------------------ // Purpose: //------------------------------------------------------------------------------ @@ -124,6 +164,9 @@ void CPointTeleport::InputTeleport( inputdata_t &inputdata ) return; } +#ifdef MAPBASE + TeleportEntity( pTarget, m_vSaveOrigin, m_vSaveAngles ); +#else // in episodic, we have a special spawn flag that forces Gordon into a duck #ifdef HL2_EPISODIC if ( (m_spawnflags & SF_TELEPORT_INTO_DUCK) && pTarget->IsPlayer() ) @@ -143,5 +186,49 @@ void CPointTeleport::InputTeleport( inputdata_t &inputdata ) #endif pTarget->Teleport( &m_vSaveOrigin, &m_vSaveAngles, NULL ); +#endif } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPointTeleport::InputTeleportEntity( inputdata_t &inputdata ) +{ + if ( !inputdata.value.Entity() ) + { + Warning( "%s unable to find entity from TeleportEntity\n", GetDebugName() ); + return; + } + + // If teleport object is in a movement hierarchy, remove it first + if ( EntityMayTeleport( inputdata.value.Entity() ) == false ) + { + Warning("ERROR: (%s) can't teleport object (%s) as it has a parent (%s)!\n",GetDebugName(),inputdata.value.Entity()->GetDebugName(),inputdata.value.Entity()->GetMoveParent()->GetDebugName()); + return; + } + + TeleportEntity( inputdata.value.Entity(), m_vSaveOrigin, m_vSaveAngles ); +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPointTeleport::InputTeleportToCurrentPos( inputdata_t &inputdata ) +{ + // Attempt to find the entity in question + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_target, this, inputdata.pActivator, inputdata.pCaller ); + if ( pTarget == NULL ) + return; + + // If teleport object is in a movement hierarchy, remove it first + if ( EntityMayTeleport( pTarget ) == false ) + { + Warning("ERROR: (%s) can't teleport object (%s) as it has a parent (%s)!\n",GetDebugName(),pTarget->GetDebugName(),pTarget->GetMoveParent()->GetDebugName()); + return; + } + + TeleportEntity( pTarget, GetAbsOrigin(), GetAbsAngles() ); +} +#endif + diff --git a/mp/src/game/server/postprocesscontroller.cpp b/mp/src/game/server/postprocesscontroller.cpp new file mode 100644 index 00000000..3e3fd6b5 --- /dev/null +++ b/mp/src/game/server/postprocesscontroller.cpp @@ -0,0 +1,207 @@ +//========= Copyright © 1996-2007, Valve Corporation, All rights reserved. ========== +// +// An entity that allows level designer control over the post-processing parameters. +// +//=================================================================================== + +#include "cbase.h" +#include "postprocesscontroller.h" +#include "entityinput.h" +#include "entityoutput.h" +#include "eventqueue.h" +#include "player.h" +#include "world.h" +#include "ndebugoverlay.h" +#include "triggers.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CPostProcessSystem s_PostProcessSystem( "PostProcessSystem" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPostProcessSystem *PostProcessSystem() +{ + return &s_PostProcessSystem; +} + + +LINK_ENTITY_TO_CLASS( postprocess_controller, CPostProcessController ); + +BEGIN_DATADESC( CPostProcessController ) + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_FADE_TIME ], FIELD_FLOAT, "fadetime" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_LOCAL_CONTRAST_STRENGTH ], FIELD_FLOAT, "localcontraststrength" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_LOCAL_CONTRAST_EDGE_STRENGTH ], FIELD_FLOAT, "localcontrastedgestrength" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_VIGNETTE_START ], FIELD_TIME, "vignettestart" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_VIGNETTE_END ], FIELD_TIME, "vignetteend" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ], FIELD_FLOAT, "vignetteblurstrength" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_FADE_TO_BLACK_STRENGTH ], FIELD_FLOAT, "fadetoblackstrength" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_DEPTH_BLUR_FOCAL_DISTANCE ], FIELD_FLOAT, "depthblurfocaldistance" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_DEPTH_BLUR_STRENGTH ], FIELD_FLOAT, "depthblurstrength" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_SCREEN_BLUR_STRENGTH ], FIELD_FLOAT, "screenblurstrength" ), + DEFINE_KEYFIELD( m_flPostProcessParameters[ PPPN_FILM_GRAIN_STRENGTH ], FIELD_FLOAT, "filmgrainstrength" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFadeTime", InputSetFadeTime ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLocalContrastStrength", InputSetLocalContrastStrength ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLocalContrastEdgeStrength", InputSetLocalContrastEdgeStrength ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetVignetteStart", InputSetVignetteStart ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetVignetteEnd", InputSetVignetteEnd ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetVignetteBlurStrength", InputSetVignetteBlurStrength ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFadeToBlackStrength", InputSetFadeToBlackStrength ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDepthBlurFocalDistance", InputSetDepthBlurFocalDistance), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDepthBlurStrength", InputSetDepthBlurStrength ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetScreenBlurStrength", InputSetScreenBlurStrength ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFilmGrainStrength", InputSetFilmGrainStrength ), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CPostProcessController, DT_PostProcessController ) + SendPropArray3( SENDINFO_ARRAY3( m_flPostProcessParameters ), SendPropFloat( SENDINFO_ARRAY( m_flPostProcessParameters ) ) ), + SendPropBool( SENDINFO(m_bMaster) ), +END_SEND_TABLE() + + +CPostProcessController::CPostProcessController() +{ + m_bMaster = false; +} + +CPostProcessController::~CPostProcessController() +{ +} + +void CPostProcessController::Spawn() +{ + BaseClass::Spawn(); + + m_bMaster = IsMaster(); +} + +int CPostProcessController::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +void CPostProcessController::InputSetFadeTime( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_FADE_TIME, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetLocalContrastStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_LOCAL_CONTRAST_STRENGTH, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetLocalContrastEdgeStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_LOCAL_CONTRAST_EDGE_STRENGTH, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetVignetteStart( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_VIGNETTE_START, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetVignetteEnd( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_VIGNETTE_END, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetVignetteBlurStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_VIGNETTE_BLUR_STRENGTH, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetFadeToBlackStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_FADE_TO_BLACK_STRENGTH, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetDepthBlurFocalDistance( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_DEPTH_BLUR_FOCAL_DISTANCE, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetDepthBlurStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_DEPTH_BLUR_STRENGTH, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetScreenBlurStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_SCREEN_BLUR_STRENGTH, inputdata.value.Float() ); +} + +void CPostProcessController::InputSetFilmGrainStrength( inputdata_t &inputdata ) +{ + m_flPostProcessParameters.Set( PPPN_FILM_GRAIN_STRENGTH, inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Clear out the PostProcess controller. +//----------------------------------------------------------------------------- +void CPostProcessSystem::LevelInitPreEntity() +{ + m_hMasterController = nullptr; + ListenForGameEvent( "round_start" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Find the master controller. If no controller is +// set as Master, use the first controller found. +//----------------------------------------------------------------------------- +void CPostProcessSystem::InitMasterController() +{ + CPostProcessController *pPostProcessController = nullptr; + + do + { + pPostProcessController = dynamic_cast( gEntList.FindEntityByClassname( pPostProcessController, "postprocess_controller" ) ); + if ( pPostProcessController ) + { + if ( m_hMasterController.Get() == nullptr ) + { + m_hMasterController = pPostProcessController; + } + else + { + if ( pPostProcessController->IsMaster() ) + { + m_hMasterController = pPostProcessController; + } + } + } + } while ( pPostProcessController ); +} + +//----------------------------------------------------------------------------- +// Purpose: On a multiplayer map restart, re-find the master controller. +//----------------------------------------------------------------------------- +void CPostProcessSystem::FireGameEvent( IGameEvent *pEvent ) +{ + InitMasterController(); +} + +//----------------------------------------------------------------------------- +// Purpose: On level load find the master PostProcess controller. If no controller is +// set as Master, use the first PostProcess controller found. +//----------------------------------------------------------------------------- +void CPostProcessSystem::LevelInitPostEntity() +{ + InitMasterController(); + + // HACK: Singleplayer games don't get a call to CBasePlayer::Spawn on level transitions. + // CBasePlayer::Activate is called before this is called so that's too soon to set up the PostProcess controller. + // We don't have a hook similar to Activate that happens after LevelInitPostEntity + // is called, or we could just do this in the player itself. + if ( gpGlobals->maxClients == 1 ) + { + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer && ( pPlayer->m_hPostProcessCtrl.Get() == nullptr ) ) + { + pPlayer->InitPostProcessController(); + } + } +} diff --git a/mp/src/game/server/postprocesscontroller.h b/mp/src/game/server/postprocesscontroller.h new file mode 100644 index 00000000..5608394a --- /dev/null +++ b/mp/src/game/server/postprocesscontroller.h @@ -0,0 +1,80 @@ +#pragma once + +#include "GameEventListener.h" +#include "postprocess_shared.h" + +// Spawn Flags +#define SF_POSTPROCESS_MASTER 0x0001 + +//============================================================================= +// Class Postprocess Controller: +//============================================================================= +class CPostProcessController : public CBaseEntity +{ +public: + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + DECLARE_CLASS( CPostProcessController, CBaseEntity ); + + CPostProcessController(); + virtual ~CPostProcessController(); + + virtual int UpdateTransmitState(); + + // Input handlers + void InputSetFadeTime(inputdata_t &data); + void InputSetLocalContrastStrength(inputdata_t &data); + void InputSetLocalContrastEdgeStrength(inputdata_t &data); + void InputSetVignetteStart(inputdata_t &data); + void InputSetVignetteEnd(inputdata_t &data); + void InputSetVignetteBlurStrength(inputdata_t &data); + void InputSetFadeToBlackStrength(inputdata_t &data); + void InputSetDepthBlurFocalDistance(inputdata_t &data); + void InputSetDepthBlurStrength(inputdata_t &data); + void InputSetScreenBlurStrength(inputdata_t &data); + void InputSetFilmGrainStrength(inputdata_t &data); + + void InputTurnOn(inputdata_t &data); + void InputTurnOff(inputdata_t &data); + + void Spawn(); + + bool IsMaster() const { return HasSpawnFlags( SF_FOG_MASTER ); } + +public: + CNetworkArray( float, m_flPostProcessParameters, POST_PROCESS_PARAMETER_COUNT ); + + CNetworkVar( bool, m_bMaster ); +}; + +//============================================================================= +// +// Postprocess Controller System. +// +class CPostProcessSystem : public CAutoGameSystem, public CGameEventListener +{ +public: + + // Creation/Init. + CPostProcessSystem( char const *name ) : CAutoGameSystem( name ) + { + m_hMasterController = nullptr; + } + + ~CPostProcessSystem() + { + m_hMasterController = nullptr; + } + + virtual void LevelInitPreEntity(); + virtual void LevelInitPostEntity(); + virtual void FireGameEvent( IGameEvent *pEvent ); + CPostProcessController *GetMasterPostProcessController() { return m_hMasterController; } + +private: + + void InitMasterController(); + CHandle< CPostProcessController > m_hMasterController; +}; + +CPostProcessSystem *PostProcessSystem(); diff --git a/mp/src/game/server/props.cpp b/mp/src/game/server/props.cpp index 9ba6c11b..06de0eae 100644 --- a/mp/src/game/server/props.cpp +++ b/mp/src/game/server/props.cpp @@ -41,6 +41,10 @@ #include "physics_collisionevent.h" #include "gamestats.h" #include "vehicle_base.h" +#ifdef MAPBASE +#include "mapbase/GlobalStrings.h" +#include "collisionutils.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -79,8 +83,17 @@ ConVar func_breakdmg_explosive( "func_breakdmg_explosive", "1.25" ); ConVar sv_turbophysics( "sv_turbophysics", "0", FCVAR_REPLICATED, "Turns on turbo physics" ); +#ifdef MAPBASE +ConVar mapbase_prop_consistency_noremove("mapbase_prop_consistency_noremove", "1", FCVAR_NONE, "Prevents the removal of props when their classes do not match up with their models' propdata."); +#endif + #ifdef HL2_EPISODIC +#ifdef MAPBASE + #define PROP_FLARE_LIFETIME GetFlareLifetime() + float GetEnvFlareLifetime( CBaseEntity *pEntity ); +#else #define PROP_FLARE_LIFETIME 30.0f +#endif #define PROP_FLARE_IGNITE_SUBSTRACT 5.0f CBaseEntity *CreateFlare( Vector vOrigin, QAngle Angles, CBaseEntity *pOwner, float flDuration ); void KillFlare( CBaseEntity *pOwnerEntity, CBaseEntity *pEntity, float flKillTime ); @@ -193,6 +206,35 @@ void CBaseProp::Spawn( void ) int iResult = ParsePropData(); if ( !OverridePropdata() ) { +#ifdef MAPBASE + if (mapbase_prop_consistency_noremove.GetBool()) + { + switch (iResult) + { + case PARSE_FAILED_BAD_DATA: + Warning("%s at %.0f %.0f %0.f uses model %s, which has an invalid prop_data type. Not deleted due to mapbase_prop_consistency_noremove.\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, szModel); + break; + case PARSE_FAILED_NO_DATA: + { + if ( FClassnameIs( this, "prop_physics" ) ) + { + Warning("%s at %.0f %.0f %0.f uses model %s, which has no propdata which means it should be used on a prop_static. Not deleted due to mapbase_prop_consistency_noremove.\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, szModel); + } + } break; + case PARSE_SUCCEEDED: + { + if (!IsPropPhysics()) + { + Warning( "%s at %.0f %.0f %0.f uses model %s, which has propdata which means that it should be used on a prop_physics. Not deleted due to mapbase_prop_consistency_noremove.\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, szModel ); + } + } + } + } + else + { + // No comment. + #define DevWarning Warning +#endif if ( iResult == PARSE_FAILED_BAD_DATA ) { DevWarning( "%s at %.0f %.0f %0.f uses model %s, which has an invalid prop_data type. DELETED.\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, szModel ); @@ -212,13 +254,21 @@ void CBaseProp::Spawn( void ) else if ( iResult == PARSE_SUCCEEDED ) { // If we have data, and we're not a physics prop, fail +#ifdef MAPBASE + if ( !IsPropPhysics() ) +#else if ( !dynamic_cast(this) ) +#endif { DevWarning( "%s at %.0f %.0f %0.f uses model %s, which has propdata which means that it be used on a prop_physics. DELETED.\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, szModel ); UTIL_Remove( this ); return; } } +#ifdef MAPBASE + #undef DevWarning + } +#endif } SetMoveType( MOVETYPE_PUSH ); @@ -275,7 +325,11 @@ bool CBaseProp::KeyValue( const char *szKeyName, const char *szValue ) if ( FStrEq(szKeyName, "health") ) { // Only override props are allowed to override health. +#ifdef MAPBASE + if ( OverridePropdata() && !FStrEq(szValue, "-1") ) +#else if ( FClassnameIs( this, "prop_physics_override" ) || FClassnameIs( this, "prop_dynamic_override" ) ) +#endif return BaseClass::KeyValue( szKeyName, szValue ); return true; @@ -702,6 +756,41 @@ void CBreakableProp::HandleInteractionStick( int index, gamevcollisionevent_t *p } } +#ifdef MAPBASE +extern int g_interactionBarnacleVictimBite; +extern ConVar npc_barnacle_ignite; +//----------------------------------------------------------------------------- +// Purpose: Uses the new CBaseEntity interaction implementation +// Input : The type of interaction, extra info pointer, and who started it +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CBreakableProp::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ) +{ +#ifdef HL2_EPISODIC + // Allows flares to ignite barnacles. + if ( interactionType == g_interactionBarnacleVictimBite ) + { + if ( npc_barnacle_ignite.GetBool() && sourceEnt->IsOnFire() == false ) + { + sourceEnt->Ignite( 25.0f ); + KillFlare( this, m_hFlareEnt, PROP_FLARE_IGNITE_SUBSTRACT ); + IGameEvent *event = gameeventmanager->CreateEvent( "flare_ignite_npc" ); + if ( event ) + { + event->SetInt( "entindex", sourceEnt->entindex() ); + gameeventmanager->FireEvent( event ); + } + } + + return true; + } +#endif + + return BaseClass::HandleInteraction(interactionType, data, sourceEnt); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Turn on prop debugging mode //----------------------------------------------------------------------------- @@ -759,6 +848,9 @@ BEGIN_DATADESC( CBreakableProp ) DEFINE_KEYFIELD( m_flPressureDelay, FIELD_FLOAT, "PressureDelay" ), DEFINE_FIELD( m_preferredCarryAngles, FIELD_VECTOR ), +#ifdef MAPBASE + DEFINE_FIELD( m_bUsesCustomCarryAngles, FIELD_BOOLEAN ), +#endif DEFINE_FIELD( m_flDefaultFadeScale, FIELD_FLOAT ), DEFINE_FIELD( m_bUsePuntSound, FIELD_BOOLEAN ), // DEFINE_FIELD( m_mpBreakMode, mp_break_t ), @@ -768,6 +860,10 @@ BEGIN_DATADESC( CBreakableProp ) DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), DEFINE_INPUTFUNC( FIELD_INTEGER, "AddHealth", InputAddHealth ), DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveHealth", InputRemoveHealth ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetInteraction", InputSetInteraction ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveInteraction", InputRemoveInteraction ), +#endif DEFINE_INPUT( m_impactEnergyScale, FIELD_FLOAT, "physdamagescale" ), DEFINE_INPUTFUNC( FIELD_VOID, "EnablePhyscannonPickup", InputEnablePhyscannonPickup ), DEFINE_INPUTFUNC( FIELD_VOID, "DisablePhyscannonPickup", InputDisablePhyscannonPickup ), @@ -899,6 +995,9 @@ void CBreakableProp::Spawn() m_impactEnergyScale = 0.1f; } +#ifdef MAPBASE + if (!m_bUsesCustomCarryAngles) +#endif m_preferredCarryAngles = QAngle( -5, 0, 0 ); // The presence of this activity causes us to have to detach it before it can be grabbed. @@ -922,6 +1021,57 @@ void CBreakableProp::Spawn() SetTouch( &CBreakableProp::BreakablePropTouch ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Handles keyvalues from the BSP. Called before spawning. +//----------------------------------------------------------------------------- +bool CBreakableProp::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( OverridePropdata() ) + { + if ( FStrEq(szKeyName, "InitialInteractions") ) + { + // Only override props are allowed to override interactions. + if (strchr(szValue, ' ')) + { + // How many interactions could there possibly be? + char szInteractions[64]; + Q_strncpy(szInteractions, szValue, sizeof(szInteractions)); + + char *token = strtok(szInteractions, " ,"); + while (token) + { + SetInteraction((propdata_interactions_t)atoi(token)); + token = strtok(token, " ,"); + } + } + else + SetInteraction((propdata_interactions_t)atoi(szValue)); + } + else if ( FStrEq(szKeyName, "preferredcarryangles") ) + { + // Only detect as custom if it's non-zero + if (!FStrEq( szValue, "0" )) + { + QAngle angCarryAngles; + UTIL_StringToVector( angCarryAngles.Base(), szValue ); + + m_preferredCarryAngles = angCarryAngles; + m_bUsesCustomCarryAngles = true; + } + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Disable auto fading under dx7 or when level fades are specified @@ -1218,6 +1368,25 @@ void CBreakableProp::InputSetHealth( inputdata_t &inputdata ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler for setting interactions. +//----------------------------------------------------------------------------- +void CBreakableProp::InputSetInteraction( inputdata_t &inputdata ) +{ + SetInteraction( (propdata_interactions_t)inputdata.value.Int() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for Adding interactions. +//----------------------------------------------------------------------------- +void CBreakableProp::InputRemoveInteraction( inputdata_t &inputdata ) +{ + RemoveInteraction( (propdata_interactions_t)inputdata.value.Int() ); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Choke point for changes to breakable health. Ensures outputs are fired. // Input : iNewHealth - @@ -1485,7 +1654,12 @@ void CBreakableProp::CreateFlare( float flLifetime ) int iAttachment = LookupAttachment( "fuse" ); Vector vOrigin; +#ifdef MAPBASE + if (!GetAttachment( iAttachment, vOrigin )) + vOrigin = GetLocalOrigin(); +#else GetAttachment( iAttachment, vOrigin ); +#endif pFlare->SetMoveType( MOVETYPE_NONE ); pFlare->SetSolid( SOLID_NONE ); @@ -2403,6 +2577,350 @@ void COrnamentProp::InputDetach( inputdata_t &inputdata ) DetachFromOwner(); } +#ifdef MAPBASE +#define SF_INTERACTABLE_USE_INTERACTS 512 // Allows +USE interaction. +#define SF_INTERACTABLE_TOUCH_INTERACTS 1024 // Allows touch interaction. +#define SF_INTERACTABLE_IGNORE_COMMANDS_WHEN_LOCKED 2048 // Completely ignores player commands when locked. +#define SF_INTERACTABLE_RADIUS_USE 4096 // Uses radius +USE + +//----------------------------------------------------------------------------- +// Purpose: Button prop for +USEable dynamic props +//----------------------------------------------------------------------------- +class CInteractableProp : public CDynamicProp +{ + DECLARE_CLASS( CInteractableProp, CDynamicProp ); +public: + DECLARE_DATADESC(); + + void Spawn(); + void Precache(); + //void Activate(); + + int ObjectCaps() + { + int caps = BaseClass::ObjectCaps(); + + if (HasSpawnFlags(SF_INTERACTABLE_USE_INTERACTS) && (!HasSpawnFlags( SF_INTERACTABLE_IGNORE_COMMANDS_WHEN_LOCKED ) || !m_bLocked)) + { + caps |= FCAP_IMPULSE_USE; + + if (HasSpawnFlags(SF_INTERACTABLE_RADIUS_USE)) + caps |= FCAP_USE_IN_RADIUS; + } + + return caps; + }; + + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void InteractablePropTouch( CBaseEntity *pOther ); + + void SetPushSequence(int iSequence); + void PushThink(); + + // Input handlers + void InputLock( inputdata_t &inputdata ); + void InputUnlock( inputdata_t &inputdata ); + void InputPress( inputdata_t &inputdata ); + + void InputEnableUseInteraction( inputdata_t &inputdata ) { AddSpawnFlags(SF_INTERACTABLE_USE_INTERACTS); } + void InputDisableUseInteraction( inputdata_t &inputdata ) { RemoveSpawnFlags(SF_INTERACTABLE_USE_INTERACTS); } + void InputEnableTouchInteraction( inputdata_t &inputdata ) { AddSpawnFlags( SF_INTERACTABLE_TOUCH_INTERACTS ); } + void InputDisableTouchInteraction( inputdata_t &inputdata ) { RemoveSpawnFlags( SF_INTERACTABLE_TOUCH_INTERACTS ); } + void InputStartIgnoringCommandsWhenLocked( inputdata_t &inputdata ) { AddSpawnFlags( SF_INTERACTABLE_IGNORE_COMMANDS_WHEN_LOCKED ); } + void InputStopIgnoringCommandsWhenLocked( inputdata_t &inputdata ) { RemoveSpawnFlags( SF_INTERACTABLE_IGNORE_COMMANDS_WHEN_LOCKED ); } + void InputEnableRadiusInteract( inputdata_t &inputdata ) { AddSpawnFlags( SF_INTERACTABLE_RADIUS_USE ); } + void InputDisableRadiusInteract( inputdata_t &inputdata ) { RemoveSpawnFlags( SF_INTERACTABLE_RADIUS_USE ); } + + COutputEvent m_OnPressed; + COutputEvent m_OnLockedUse; + COutputEvent m_OnIn; + COutputEvent m_OnOut; + + bool m_bLocked; + + float m_flCooldown; + +private: + float m_flCooldownTime; + + int m_iCurSequence = INTERACTSEQ_NONE; // Currently in a sequence + enum + { + INTERACTSEQ_NONE = -1, + INTERACTSEQ_IN, + INTERACTSEQ_OUT, + INTERACTSEQ_LOCKED, + }; + + string_t m_iszPressedSound; + string_t m_iszLockedSound; + + string_t m_iszInSequence; + string_t m_iszOutSequence; + string_t m_iszLockedSequence; + + Vector m_vecUseMins; + Vector m_vecUseMaxs; +}; + +LINK_ENTITY_TO_CLASS( prop_interactable, CInteractableProp ); + +BEGIN_DATADESC( CInteractableProp ) + + DEFINE_KEYFIELD( m_bLocked, FIELD_BOOLEAN, "Locked" ), + DEFINE_INPUT( m_flCooldown, FIELD_FLOAT, "SetCooldown" ), + DEFINE_FIELD( m_flCooldownTime, FIELD_TIME ), + DEFINE_FIELD( m_iCurSequence, FIELD_INTEGER ), + + DEFINE_KEYFIELD( m_iszPressedSound, FIELD_STRING, "PressedSound" ), + DEFINE_KEYFIELD( m_iszLockedSound, FIELD_STRING, "LockedSound" ), + DEFINE_KEYFIELD( m_iszInSequence, FIELD_STRING, "InSequence" ), + DEFINE_KEYFIELD( m_iszOutSequence, FIELD_STRING, "OutSequence" ), + DEFINE_KEYFIELD( m_iszLockedSequence, FIELD_STRING, "LockedSequence" ), + + DEFINE_KEYFIELD( m_vecUseMins, FIELD_VECTOR, "use_mins" ), + DEFINE_KEYFIELD( m_vecUseMaxs, FIELD_VECTOR, "use_maxs" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ), + DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), + DEFINE_INPUTFUNC( FIELD_VOID, "Press", InputPress ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableUseInteraction", InputEnableUseInteraction ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableUseInteraction", InputDisableUseInteraction ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableTouchInteraction", InputEnableTouchInteraction ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableTouchInteraction", InputDisableTouchInteraction ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartIgnoringCommandsWhenLocked", InputStartIgnoringCommandsWhenLocked ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopIgnoringCommandsWhenLocked", InputStopIgnoringCommandsWhenLocked ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadiusInteract", InputEnableRadiusInteract ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableRadiusInteract", InputDisableRadiusInteract ), + + // Outputs + DEFINE_OUTPUT( m_OnPressed, "OnPressed" ), + DEFINE_OUTPUT( m_OnLockedUse, "OnLockedUse" ), + DEFINE_OUTPUT( m_OnIn, "OnIn" ), + DEFINE_OUTPUT( m_OnOut, "OnOut" ), + + DEFINE_THINKFUNC( PushThink ), + DEFINE_ENTITYFUNC( InteractablePropTouch ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::Spawn( void ) +{ + BaseClass::Spawn(); + + SetTouch( &CInteractableProp::InteractablePropTouch ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::Precache( void ) +{ + BaseClass::Precache(); + + if (m_iszPressedSound != NULL_STRING) + PrecacheScriptSound( STRING(m_iszPressedSound) ); + if (m_iszLockedSound != NULL_STRING) + PrecacheScriptSound( STRING(m_iszLockedSound) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pActivator - +// *pCaller - +// useType - +// value - +//----------------------------------------------------------------------------- +void CInteractableProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (m_flCooldownTime > gpGlobals->curtime) + return; + + // If we're using +USE mins/maxs, make sure this is being +USE'd from the right place + if (m_vecUseMins.LengthSqr() != 0.0f && m_vecUseMaxs.LengthSqr() != 0.0f) + { + CBasePlayer *pPlayer = ToBasePlayer( pActivator ); + if (pPlayer) + { + Vector forward; + pPlayer->EyeVectors( &forward, NULL, NULL ); + + // This might be a little convoluted and/or seem needlessly expensive, but I couldn't figure out any better way to do this. + // TOOD: Can we calculate a box in local space instead of world space? + Vector vecWorldMins, vecWorldMaxs; + RotateAABB( EntityToWorldTransform(), m_vecUseMins, m_vecUseMaxs, vecWorldMins, vecWorldMaxs ); + TransformAABB( EntityToWorldTransform(), vecWorldMins, vecWorldMaxs, vecWorldMins, vecWorldMaxs ); + if (!IsBoxIntersectingRay( vecWorldMins, vecWorldMaxs, pPlayer->EyePosition(), forward * 1024 )) + { + // Reject this +USE if it's not in our box + DevMsg("Outside of +USE box\n"); + return; + } + } + } + + int nSequence = -1; + + if (m_bLocked) + { + m_OnLockedUse.FireOutput( pActivator, this ); + EmitSound(STRING(m_iszLockedSound)); + nSequence = LookupSequence( STRING( m_iszLockedSequence ) ); + m_iCurSequence = INTERACTSEQ_LOCKED; + } + else + { + m_OnPressed.FireOutput( pActivator, this ); + EmitSound(STRING(m_iszPressedSound)); + nSequence = LookupSequence( STRING( m_iszInSequence ) ); + m_iCurSequence = INTERACTSEQ_IN; + } + + if ( nSequence > ACTIVITY_NOT_AVAILABLE ) + { + SetPushSequence(nSequence); + + // We still fire our inherited animation outputs + m_pOutputAnimBegun.FireOutput( pActivator, this ); + } + + m_flCooldownTime = gpGlobals->curtime + m_flCooldown; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOther - +//----------------------------------------------------------------------------- +void CInteractableProp::InteractablePropTouch( CBaseEntity *pOther ) +{ + // Do base touch function first + BreakablePropTouch( pOther ); + + if ( HasSpawnFlags(SF_INTERACTABLE_TOUCH_INTERACTS) && (!HasSpawnFlags(SF_INTERACTABLE_IGNORE_COMMANDS_WHEN_LOCKED) || !m_bLocked) && pOther->IsPlayer() ) + { + Use( pOther, pOther, USE_ON, 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::InputLock( inputdata_t &inputdata ) +{ + m_bLocked = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::InputUnlock( inputdata_t &inputdata ) +{ + m_bLocked = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::InputPress( inputdata_t &inputdata ) +{ + Use( inputdata.pActivator, inputdata.pCaller, USE_ON, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::SetPushSequence( int iSequence ) +{ + m_iGoalSequence = iSequence; + + int nNextSequence; + float nextCycle; + float flInterval = 0.1f; + + if (GotoSequence( GetSequence(), GetCycle(), GetPlaybackRate(), m_iGoalSequence, nNextSequence, nextCycle, m_iTransitionDirection )) + { + FinishSetSequence( nNextSequence ); + } + + SetThink( &CInteractableProp::PushThink ); + if ( GetNextThink() <= gpGlobals->curtime ) + SetNextThink( gpGlobals->curtime + flInterval ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInteractableProp::PushThink() +{ + if ( m_nPendingSequence != -1 ) + { + FinishSetSequence( m_nPendingSequence ); + m_nPendingSequence = -1; + } + + SetNextThink( gpGlobals->curtime + 0.1f ); + + if ( ((m_iTransitionDirection > 0 && GetCycle() >= 0.999f) || (m_iTransitionDirection < 0 && GetCycle() <= 0.0f)) && !SequenceLoops() ) + { + if (!SequenceLoops()) + { + // We still fire our inherited animation outputs + m_pOutputAnimOver.FireOutput(NULL, this); + } + + if (m_iCurSequence == INTERACTSEQ_OUT) + { + m_OnOut.FireOutput( NULL, this ); + + m_iCurSequence = INTERACTSEQ_NONE; + } + else + { + m_OnIn.FireOutput( NULL, this ); + } + } + + StudioFrameAdvance(); + DispatchAnimEvents(this); + m_BoneFollowerManager.UpdateBoneFollowers(this); + + if (m_flCooldownTime < gpGlobals->curtime) + { + if (m_iCurSequence == INTERACTSEQ_IN) + { + int nSequence = LookupSequence( STRING(m_iszOutSequence) ); + if ( m_iszOutSequence != NULL_STRING && nSequence > ACTIVITY_NOT_AVAILABLE ) + { + m_iCurSequence = INTERACTSEQ_OUT; + SetPushSequence(nSequence); + + // We still fire our inherited animation outputs + m_pOutputAnimBegun.FireOutput( NULL, this ); + } + else + { + m_iCurSequence = INTERACTSEQ_NONE; + } + } + + if (m_iCurSequence == INTERACTSEQ_NONE) + { + if (m_iszDefaultAnim != NULL_STRING) + { + PropSetAnim( STRING( m_iszDefaultAnim ) ); + } + + SetNextThink( TICK_NEVER_THINK ); + } + } +} +#endif + //============================================================================= // PHYSICS PROPS @@ -2418,6 +2936,9 @@ BEGIN_DATADESC( CPhysicsProp ) DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), DEFINE_INPUTFUNC( FIELD_VOID, "Sleep", InputSleep ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableFloating", InputDisableFloating ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetDebris", InputSetDebris ), +#endif DEFINE_FIELD( m_bAwake, FIELD_BOOLEAN ), @@ -2482,10 +3003,15 @@ void CPhysicsProp::Spawn( ) { g_ActiveGibCount++; } + // Condense classname's to one, except for "prop_physics_override" if ( FClassnameIs( this, "physics_prop" ) ) { +#ifdef MAPBASE + m_iClassname = gm_isz_class_PropPhysics; +#else SetClassname( "prop_physics" ); +#endif } BaseClass::Spawn(); @@ -2494,10 +3020,17 @@ void CPhysicsProp::Spawn( ) return; // Now condense all classnames to one +#ifdef MAPBASE + if ( EntIsClass( this, gm_isz_class_PropPhysicsOverride ) ) + { + m_iClassname = gm_isz_class_PropPhysics; + } +#else if ( FClassnameIs( this, "prop_physics_override") ) { SetClassname( "prop_physics" ); } +#endif if ( HasSpawnFlags( SF_PHYSPROP_DEBRIS ) || HasInteraction( PROPINTER_PHYSGUN_CREATE_FLARE ) ) { @@ -2647,7 +3180,11 @@ bool CPhysicsProp::CanBePickedUpByPhyscannon( void ) //----------------------------------------------------------------------------- bool CPhysicsProp::OverridePropdata( void ) { +#ifdef MAPBASE + return EntIsClass(this, gm_isz_class_PropPhysicsOverride); +#else return ( FClassnameIs(this, "prop_physics_override" ) ); +#endif } //----------------------------------------------------------------------------- @@ -2700,6 +3237,25 @@ void CPhysicsProp::InputDisableFloating( inputdata_t &inputdata ) PhysEnableFloating( VPhysicsGetObject(), false ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Adds or removes the debris spawnflag. +//----------------------------------------------------------------------------- +void CPhysicsProp::InputSetDebris( inputdata_t &inputdata ) +{ + if (inputdata.value.Bool()) + { + AddSpawnFlags(SF_PHYSPROP_DEBRIS); + SetCollisionGroup(HasSpawnFlags(SF_PHYSPROP_FORCE_TOUCH_TRIGGERS) ? COLLISION_GROUP_DEBRIS_TRIGGER : COLLISION_GROUP_DEBRIS); + } + else + { + RemoveSpawnFlags(SF_PHYSPROP_DEBRIS); + SetCollisionGroup(COLLISION_GROUP_INTERACTIVE); // Is this the default collision group? + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -3561,6 +4117,10 @@ BEGIN_DATADESC(CBasePropDoor) DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), DEFINE_INPUTFUNC(FIELD_VOID, "Lock", InputLock), DEFINE_INPUTFUNC(FIELD_VOID, "Unlock", InputUnlock), +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_VOID, "AllowPlayerUse", InputAllowPlayerUse), + DEFINE_INPUTFUNC(FIELD_VOID, "DisallowPlayerUse", InputDisallowPlayerUse), +#endif DEFINE_OUTPUT(m_OnBlockedOpening, "OnBlockedOpening"), DEFINE_OUTPUT(m_OnBlockedClosing, "OnBlockedClosing"), @@ -3843,7 +4403,12 @@ void CBasePropDoor::UpdateAreaPortals(bool isOpen) return; CBaseEntity *pPortal = NULL; +#ifdef MAPBASE + // For func_areaportal_oneway. + while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal*")) != NULL) +#else while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal")) != NULL) +#endif { if (pPortal->HasTarget(name)) { @@ -4036,6 +4601,25 @@ void CBasePropDoor::InputUnlock(inputdata_t &inputdata) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Input handler that makes the door usable for players. +//----------------------------------------------------------------------------- +void CBasePropDoor::InputAllowPlayerUse(inputdata_t &inputdata) +{ + RemoveSpawnFlags(SF_DOOR_IGNORE_USE); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that makes the door unusable for players. +//----------------------------------------------------------------------------- +void CBasePropDoor::InputDisallowPlayerUse(inputdata_t &inputdata) +{ + AddSpawnFlags(SF_DOOR_IGNORE_USE); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Locks the door so that it cannot be opened. //----------------------------------------------------------------------------- @@ -4624,6 +5208,12 @@ public: if ( pPhysics->IsMoveable() && pPhysics->GetMass() < 32 ) return false; } + +#ifdef MAPBASE + // They're children, for goodness sake! + if (pEntity->GetParent() == EntityFromEntityHandle(m_pDoor)) + return false; +#endif } return true; @@ -4646,6 +5236,11 @@ inline void TraceHull_Door( const CBasePropDoor *pDoor, const Vector &vecAbsStar enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); } +#ifdef MAPBASE +// This was still broken when it was scrapped. +//#define DOOR_BREAKING_STUFF 1 +#endif + // Check directions for door movement enum doorCheck_e { @@ -4670,6 +5265,15 @@ enum PropDoorRotatingOpenDirection_e DOOR_ROTATING_OPEN_BACKWARD, }; +#ifdef DOOR_BREAKING_STUFF +enum PropDoorRotatingBreakType_e +{ + DOOR_ROTATING_BREAK_NORMAL = 0, // Base behavior. + DOOR_ROTATING_BREAK_PHYS, // Turn into a physics prop via phys_conversion. + DOOR_ROTATING_BREAK_PHYS_HINGE, // Same as above, but use a phys_hinge. +}; +#endif + //=============================================== // Rotating prop door //=============================================== @@ -4710,6 +5314,15 @@ public: void InputSetSpeed(inputdata_t &inputdata); +#ifdef DOOR_BREAKING_STUFF + void Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ); +#endif + +#ifdef MAPBASE + // Filters don't work well with the way doors are considered obstructions, so it's just a spawnflag that stops all NPCs for now. + virtual bool PassesDoorFilter(CBaseEntity *pEntity) { return !HasSpawnFlags(SF_DOOR_NONPCS); } +#endif + DECLARE_DATADESC(); private: @@ -4732,6 +5345,9 @@ private: PropDoorRotatingSpawnPos_t m_eSpawnPosition; PropDoorRotatingOpenDirection_e m_eOpenDirection; +#ifdef DOOR_BREAKING_STUFF + PropDoorRotatingBreakType_e m_eBreakType; +#endif QAngle m_angRotationAjar; // Angles to spawn at if we are set to spawn ajar. QAngle m_angRotationClosed; // Our angles when we are fully closed. @@ -4752,6 +5368,9 @@ private: BEGIN_DATADESC(CPropDoorRotating) DEFINE_KEYFIELD(m_eSpawnPosition, FIELD_INTEGER, "spawnpos"), DEFINE_KEYFIELD(m_eOpenDirection, FIELD_INTEGER, "opendir" ), +#ifdef DOOR_BREAKING_STUFF + DEFINE_KEYFIELD(m_eBreakType, FIELD_INTEGER, "breaktype" ), +#endif DEFINE_KEYFIELD(m_vecAxis, FIELD_VECTOR, "axis"), DEFINE_KEYFIELD(m_flDistance, FIELD_FLOAT, "distance"), DEFINE_KEYFIELD( m_angRotationAjar, FIELD_VECTOR, "ajarangles" ), @@ -5429,19 +6048,133 @@ void CPropDoorRotating::InputSetRotationDistance( inputdata_t &inputdata ) CalculateDoorVolume( GetLocalAngles(), m_angRotationOpenBack, &m_vecBackBoundsMin, &m_vecBackBoundsMax ); } +#ifdef DOOR_BREAKING_STUFF +//extern bool TransferPhysicsObject( CBaseEntity *pFrom, CBaseEntity *pTo, bool wakeUp ); +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropDoorRotating::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) +{ + if (m_eBreakType == DOOR_ROTATING_BREAK_NORMAL) + return BaseClass::Break( pBreaker, info ); + + if (m_eBreakType == DOOR_ROTATING_BREAK_PHYS || m_eBreakType == DOOR_ROTATING_BREAK_PHYS_HINGE) + { + DevMsg("Should break into physics\n"); + UnlinkFromParent( this ); + + CBaseEntity *pPhys = CreateNoSpawn( "prop_physics", GetLocalOrigin(), GetLocalAngles() ); + if ( pPhys ) + { + pPhys->SetModelName( GetModelName() ); + + pPhys->m_nRenderMode = m_nRenderMode; + pPhys->m_nRenderFX = m_nRenderFX; + const color32 rclr = GetRenderColor(); + pPhys->SetRenderColor(rclr.r, rclr.g, rclr.b, rclr.a); + + CBaseAnimating *pPhysAnimating = pPhys->GetBaseAnimating(); + + pPhysAnimating->m_nSkin = m_nSkin; + pPhysAnimating->m_nBody = m_nBody; + pPhysAnimating->SetModelScale(GetModelScale()); + + pPhys->SetName( GetEntityName() ); + + UTIL_TransferPoseParameters( this, pPhys ); + TransferChildren( this, pPhys ); + + AddSolidFlags( FSOLID_NOT_SOLID ); + AddEffects( EF_NODRAW ); + + + PhysBreakSound( this, VPhysicsGetObject(), WorldSpaceCenter() ); + + DispatchSpawn(pPhys); + + // Transferring the physics object in this case has proven to be buggy. + if (pPhys->VPhysicsGetObject()) //if ( !TransferPhysicsObject( this, pPhys, true ) ) + { + //pPhys->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false ); + + pPhys->VPhysicsGetObject()->SetMaterialIndex( VPhysicsGetObject()->GetMaterialIndex() ); + } + + if (m_eBreakType == DOOR_ROTATING_BREAK_PHYS_HINGE) + { + DevMsg("Should break with hinge\n"); + // This is the point where names get a little weird. + if (GetEntityName() != NULL_STRING) + SetName(NULL_STRING); + else + { + // Since we don't have a name, but the designer wants a hinge, give the new prop a name for the hinge to target. + pPhys->SetName(AllocPooledString(UTIL_VarArgs("_physdoor%i", entindex()))); + } + + CBaseEntity *pHinge = CreateNoSpawn("phys_hinge", GetLocalOrigin(), GetLocalAngles()); + pHinge->SetName(AllocPooledString(UTIL_VarArgs("%s_createdhinge", STRING(pPhys->GetEntityName())))); + pHinge->KeyValue("attach1", STRING(pPhys->GetEntityName())); + pHinge->KeyValue("hingeaxis", m_vecAxis); + pHinge->KeyValue("breaksound", "Metal_Box.BulletImpact"); + + DispatchSpawn(pHinge); + } + + BaseClass::Break( pBreaker, info ); + + //UTIL_Remove( this ); + + pPhys->VPhysicsTakeDamage(info); + + //if (pPhys->VPhysicsGetObject()) + // pPhys->VPhysicsGetObject()->ApplyForceOffset(info.GetDamageForce(), info.GetDamagePosition()); + } + else + { + BaseClass::Break( pBreaker, info ); + } + } +} +#endif + +#ifdef MAPBASE +void CPropDoorRotating::InputSetSpeed(inputdata_t &inputdata) +{ + AssertMsg1(inputdata.value.Float() > 0.0f, "InputSetSpeed on %s called with negative parameter!", GetDebugName() ); + m_flSpeed = inputdata.value.Float(); + DoorResume(); +} +#endif + // Debug sphere class CPhysSphere : public CPhysicsProp { DECLARE_CLASS( CPhysSphere, CPhysicsProp ); +#ifdef MAPBASE + DECLARE_DATADESC(); +#endif public: +#ifdef MAPBASE + float m_fRadius; +#else virtual bool OverridePropdata() { return true; } +#endif bool CreateVPhysics() { SetSolid( SOLID_BBOX ); +#ifdef MAPBASE + SetCollisionBounds( -Vector(m_fRadius), Vector(m_fRadius) ); +#else SetCollisionBounds( -Vector(12,12,12), Vector(12,12,12) ); +#endif objectparams_t params = g_PhysDefaultObjectParams; params.pGameData = static_cast(this); +#ifdef MAPBASE + IPhysicsObject *pPhysicsObject = physenv->CreateSphereObject( m_fRadius, GetModelPtr()->GetRenderHdr()->textureindex, GetAbsOrigin(), GetAbsAngles(), ¶ms, false ); +#else IPhysicsObject *pPhysicsObject = physenv->CreateSphereObject( 12, 0, GetAbsOrigin(), GetAbsAngles(), ¶ms, false ); +#endif if ( pPhysicsObject ) { VPhysicsSetObject( pPhysicsObject ); @@ -5453,16 +6186,144 @@ public: } }; +#ifdef MAPBASE +BEGIN_DATADESC( CPhysSphere ) + DEFINE_KEYFIELD( m_fRadius, FIELD_FLOAT, "radius"), +END_DATADESC() +#endif + +#ifndef MAPBASE // Yes, all I'm doing is moving this up a few lines and I'm still using the preprocessor. void CPropDoorRotating::InputSetSpeed(inputdata_t &inputdata) { AssertMsg1(inputdata.value.Float() > 0.0f, "InputSetSpeed on %s called with negative parameter!", GetDebugName() ); m_flSpeed = inputdata.value.Float(); DoorResume(); } +#endif LINK_ENTITY_TO_CLASS( prop_sphere, CPhysSphere ); +#if defined(MAPBASE) && defined(HL2_EPISODIC) +// ------------------------------------------------------------------------------------------ // +// Flare class for higher interaction possibilities, inspired by Black Mesa +// ------------------------------------------------------------------------------------------ // +class CPropFlare : public CPhysicsProp +{ + DECLARE_CLASS( CPropFlare, CPhysicsProp ); + DECLARE_DATADESC(); +public: + + void Precache() + { + BaseClass::Precache(); + + if (GetModelName() != NULL_STRING) + { + PrecacheModel(STRING(GetModelName())); + } + else + { + PrecacheModel("models/props_junk/flare.mdl"); + } + } + + void Spawn() + { + if (GetModelName() == NULL_STRING) + { + // Must've been spawned with ent_create or something + SetModelName(AllocPooledString("models/props_junk/flare.mdl")); + //SetModel("models/props_junk/flare.mdl"); + } + + if (!HasInteraction(PROPINTER_PHYSGUN_CREATE_FLARE)) + { + SetInteraction(PROPINTER_PHYSGUN_CREATE_FLARE); + } + + SetClassname( "prop_physics" ); + + return BaseClass::Spawn(); + } + + bool OverridePropdata( void ) { return true; } + + virtual float GetFlareLifetime() { return m_flFlareLifetime; } + + void InputStartFlare( inputdata_t &inputdata ) + { + CreateFlare( PROP_FLARE_LIFETIME ); + } + void InputStopFlare( inputdata_t &inputdata ) + { + KillFlare( this, m_hFlareEnt, PROP_FLARE_IGNITE_SUBSTRACT ); + } + + void InputAddFlareLifetime( inputdata_t &inputdata ) + { + if (m_hFlareEnt) + { + KillFlare( this, m_hFlareEnt, (inputdata.value.Float() * -1) ); + } + else + { + CreateFlare( inputdata.value.Float() ); + } + } + + void InputRemoveFlare( inputdata_t &inputdata ) + { + UTIL_Remove(m_hFlareEnt); + m_nSkin = 1; + } + + void InputRestoreFlare( inputdata_t &inputdata ) + { + if (!HasInteraction(PROPINTER_PHYSGUN_CREATE_FLARE) && !m_hFlareEnt) + { + SetInteraction(PROPINTER_PHYSGUN_CREATE_FLARE); + m_OnRestored.FireOutput(inputdata.pActivator, inputdata.pCaller); + m_nSkin = 0; + } + } + + int DrawDebugTextOverlays(void) + { + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + Q_snprintf(tempstr, sizeof(tempstr), "Flare Duration: %f", GetEnvFlareLifetime(m_hFlareEnt)); + EntityText(text_offset, tempstr, 0); + text_offset++; + } + + return text_offset; + } + + COutputEvent m_OnRestored; + float m_flFlareLifetime = 30.0f; +}; + +LINK_ENTITY_TO_CLASS( prop_flare, CPropFlare ); + +BEGIN_DATADESC( CPropFlare ) + + DEFINE_KEYFIELD( m_flFlareLifetime, FIELD_FLOAT, "FlareLifetime" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "StartFlare", InputStartFlare ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopFlare", InputStopFlare ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "AddFlareLifetime", InputAddFlareLifetime ), + DEFINE_INPUTFUNC( FIELD_VOID, "RemoveFlare", InputRemoveFlare ), + DEFINE_INPUTFUNC( FIELD_VOID, "RestoreFlare", InputRestoreFlare ), + DEFINE_OUTPUT( m_OnRestored, "OnRestored" ), + +END_DATADESC() +#endif + + // ------------------------------------------------------------------------------------------ // // Special version of func_physbox. // ------------------------------------------------------------------------------------------ // diff --git a/mp/src/game/server/props.h b/mp/src/game/server/props.h index 7dc9f48a..66da8131 100644 --- a/mp/src/game/server/props.h +++ b/mp/src/game/server/props.h @@ -39,6 +39,11 @@ public: // Don't treat as a live target virtual bool IsAlive( void ) { return false; } virtual bool OverridePropdata() { return true; } + +#ifdef MAPBASE + // Attempt to replace a dynamic_cast + virtual bool IsPropPhysics() { return false; } +#endif }; @@ -58,10 +63,18 @@ public: virtual void Precache(); virtual float GetAutoAimRadius() { return 24.0f; } +#ifdef MAPBASE + virtual bool KeyValue( const char *szKeyName, const char *szValue ); +#endif + void BreakablePropTouch( CBaseEntity *pOther ); virtual int OnTakeDamage( const CTakeDamageInfo &info ); void Event_Killed( const CTakeDamageInfo &info ); +#ifdef MAPBASE + // Marks Break() as virtual + virtual +#endif void Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ); void BreakThink( void ); void AnimateThink( void ); @@ -72,6 +85,10 @@ public: void InputAddHealth( inputdata_t &inputdata ); void InputRemoveHealth( inputdata_t &inputdata ); void InputSetHealth( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetInteraction( inputdata_t &inputdata ); + void InputRemoveInteraction( inputdata_t &inputdata ); +#endif int GetNumBreakableChunks( void ) { return m_iNumBreakableChunks; } @@ -86,6 +103,11 @@ public: if ( HasInteraction( PROPINTER_PHYSGUN_LAUNCH_SPIN_Z ) ) return true; +#ifdef MAPBASE + if ( m_bUsesCustomCarryAngles ) + return true; +#endif + return false; } @@ -97,6 +119,11 @@ public: void HandleFirstCollisionInteractions( int index, gamevcollisionevent_t *pEvent ); void HandleInteractionStick( int index, gamevcollisionevent_t *pEvent ); void StickAtPosition( const Vector &stickPosition, const Vector &savePosition, const QAngle &saveAngles ); + +#ifdef MAPBASE + // Uses the new CBaseEntity interaction implementation + bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ); +#endif // Disable auto fading under dx7 or when level fades are specified void DisableAutoFade(); @@ -111,6 +138,10 @@ public: int m_iMinHealthDmg; QAngle m_preferredCarryAngles; +#ifdef MAPBASE + // Indicates whether the prop is using the keyvalue carry angles. + bool m_bUsesCustomCarryAngles; +#endif public: // IBreakableWithPropData @@ -196,6 +227,9 @@ public: virtual CBasePlayer *HasPhysicsAttacker( float dt ); #ifdef HL2_EPISODIC +#ifdef MAPBASE + virtual float GetFlareLifetime() { return 30.0f; } +#endif void CreateFlare( float flLifetime ); #endif //HL2_EPISODIC @@ -243,7 +277,14 @@ private: mp_break_t m_mpBreakMode; EHANDLE m_hLastAttacker; // Last attacker that harmed me. +#ifdef MAPBASE +protected: + // Needs to be protected for prop_flare entity usage EHANDLE m_hFlareEnt; +private: +#else + EHANDLE m_hFlareEnt; +#endif string_t m_iszPuntSound; bool m_bUsePuntSound; }; @@ -344,6 +385,11 @@ public: bool CreateVPhysics( void ); bool OverridePropdata( void ); +#ifdef MAPBASE + // Attempt to replace a dynamic_cast + virtual bool IsPropPhysics() { return true; } +#endif + virtual void VPhysicsUpdate( IPhysicsObject *pPhysics ); virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); @@ -352,6 +398,9 @@ public: void InputEnableMotion( inputdata_t &inputdata ); void InputDisableMotion( inputdata_t &inputdata ); void InputDisableFloating( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetDebris( inputdata_t &inputdata ); +#endif void EnableMotion( void ); bool CanBePickedUpByPhyscannon( void ); diff --git a/mp/src/game/server/rope.cpp b/mp/src/game/server/rope.cpp index 7ca53298..516015fe 100644 --- a/mp/src/game/server/rope.cpp +++ b/mp/src/game/server/rope.cpp @@ -37,6 +37,9 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CRopeKeyframe, DT_RopeKeyframe ) SendPropInt( SENDINFO(m_Slack), 12 ), SendPropInt( SENDINFO(m_RopeLength), 15 ), SendPropInt( SENDINFO(m_fLockedPoints), 4, SPROP_UNSIGNED ), +#ifdef MAPBASE + SendPropInt( SENDINFO(m_nChangeCount), 8, SPROP_UNSIGNED ), +#endif SendPropInt( SENDINFO(m_RopeFlags), ROPE_NUMFLAGS, SPROP_UNSIGNED ), SendPropInt( SENDINFO(m_nSegments), 4, SPROP_UNSIGNED ), SendPropBool( SENDINFO(m_bConstrainBetweenEndpoints) ), @@ -86,6 +89,11 @@ BEGIN_DATADESC( CRopeKeyframe ) DEFINE_INPUTFUNC( FIELD_VECTOR, "SetForce", InputSetForce ), DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ), +#ifdef MAPBASE + // Outputs + DEFINE_OUTPUT( m_OnBreak, "OnBreak" ), +#endif + END_DATADESC() @@ -109,7 +117,12 @@ CRopeKeyframe::CRopeKeyframe() m_RopeLength = 20; m_fLockedPoints = (int) (ROPE_LOCK_START_POINT | ROPE_LOCK_END_POINT); // by default, both points are locked m_flScrollSpeed = 0; +#ifdef MAPBASE + // Because of the keyvalue switch + m_RopeFlags = ROPE_SIMULATE | ROPE_INITIAL_HANG | ROPE_USE_WIND; +#else m_RopeFlags = ROPE_SIMULATE | ROPE_INITIAL_HANG; +#endif m_iRopeMaterialModelIndex = -1; m_Subdiv = 2; @@ -201,10 +214,19 @@ CRopeKeyframe* CRopeKeyframe::Create( int iEndAttachment, int ropeWidth, const char *pMaterialName, +#ifdef MAPBASE + int numSegments, + const char *pClassName +#else int numSegments +#endif ) { +#ifdef MAPBASE + CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( pClassName ); +#else CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" ); +#endif if( !pRet ) return NULL; @@ -218,6 +240,9 @@ CRopeKeyframe* CRopeKeyframe::Create( pRet->SetMaterial( pMaterialName ); pRet->m_Width = ropeWidth; pRet->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS ); +#ifdef MAPBASE + pRet->Spawn(); +#endif return pRet; } @@ -230,10 +255,19 @@ CRopeKeyframe* CRopeKeyframe::CreateWithSecondPointDetached( int ropeWidth, const char *pMaterialName, int numSegments, +#ifdef MAPBASE + bool bInitialHang, + const char *pClassName +#else bool bInitialHang +#endif ) { +#ifdef MAPBASE + CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( pClassName ); +#else CRopeKeyframe *pRet = (CRopeKeyframe*)CreateEntityByName( "keyframe_rope" ); +#endif if( !pRet ) return NULL; @@ -329,6 +363,26 @@ void CRopeKeyframe::Init() m_bStartPointValid = (m_hStartPoint.Get() != NULL); m_bEndPointValid = (m_hEndPoint.Get() != NULL); + +#ifdef MAPBASE + // Sanity-check the rope texture scale before it goes over the wire + if ( m_TextureScale < 0.1f ) + { + Vector origin = GetAbsOrigin(); + GetEndPointPos( 0, origin ); + DevMsg( "move_rope has TextureScale less than 0.1 at (%2.2f, %2.2f, %2.2f)\n", + origin.x, origin.y, origin.z ); + m_TextureScale = 0.1f; + } + else if ( m_TextureScale > 10.0f ) + { + Vector origin = GetAbsOrigin(); + GetEndPointPos( 0, origin ); + DevMsg( "move_rope has TextureScale greater than 10 at (%2.2f, %2.2f, %2.2f)\n", + origin.x, origin.y, origin.z ); + m_TextureScale = 10.0f; + } +#endif } @@ -552,17 +606,29 @@ void CRopeKeyframe::InputSetForce( inputdata_t &inputdata ) void CRopeKeyframe::InputBreak( inputdata_t &inputdata ) { //Route through the damage code +#ifdef MAPBASE + Break(inputdata.pActivator); +#else Break(); +#endif } //----------------------------------------------------------------------------- // Purpose: Breaks the rope // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- +#ifdef MAPBASE +bool CRopeKeyframe::Break( CBaseEntity *pActivator ) +#else bool CRopeKeyframe::Break( void ) +#endif { DetachPoint( 0 ); +#ifdef MAPBASE + m_OnBreak.FireOutput(pActivator ? pActivator : this, this); +#endif + // Find whoever references us and detach us from them. // UNDONE: PERFORMANCE: This is very slow!!! CRopeKeyframe *pTest = NULL; @@ -585,6 +651,10 @@ bool CRopeKeyframe::Break( void ) //----------------------------------------------------------------------------- void CRopeKeyframe::NotifyPositionChanged( CBaseEntity *pEntity ) { +#ifdef MAPBASE + ++m_nChangeCount; +#endif + // Update our bbox? UpdateBBox( false ); @@ -619,7 +689,11 @@ int CRopeKeyframe::OnTakeDamage( const CTakeDamageInfo &info ) if( !(m_RopeFlags & ROPE_BREAKABLE) ) return false; +#ifdef MAPBASE + Break(info.GetAttacker()); +#else Break(); +#endif return 0; } @@ -630,6 +704,14 @@ void CRopeKeyframe::Precache() BaseClass::Precache(); } +#ifdef MAPBASE +void CRopeKeyframe::Spawn( void ) +{ + BaseClass::Spawn(); + Precache(); +} +#endif + void CRopeKeyframe::DetachPoint( int iPoint ) { @@ -650,10 +732,17 @@ void CRopeKeyframe::EnableCollision() void CRopeKeyframe::EnableWind( bool bEnable ) { int flag = 0; +#ifdef MAPBASE + if ( bEnable ) + flag |= ROPE_USE_WIND; + + if ( (m_RopeFlags & ROPE_USE_WIND) != flag ) +#else if ( !bEnable ) flag |= ROPE_NO_WIND; if ( (m_RopeFlags & ROPE_NO_WIND) != flag ) +#endif { m_RopeFlags |= flag; } @@ -698,6 +787,20 @@ bool CRopeKeyframe::KeyValue( const char *szKeyName, const char *szValue ) { // Legacy support for the RopeShader parameter. int iShader = atoi( szValue ); +#ifdef MAPBASE + if ( iShader == 0 ) + { + m_strRopeMaterialModel = AllocPooledString( "cable/cable.vmt" ); + } + else if ( iShader == 1 ) + { + m_strRopeMaterialModel = AllocPooledString( "cable/rope.vmt" ); + } + else + { + m_strRopeMaterialModel = AllocPooledString( "cable/chain.vmt" ); + } +#else if ( iShader == 0 ) { m_iRopeMaterialModelIndex = PrecacheModel( "cable/cable.vmt" ); @@ -710,6 +813,7 @@ bool CRopeKeyframe::KeyValue( const char *szKeyName, const char *szValue ) { m_iRopeMaterialModelIndex = PrecacheModel( "cable/chain.vmt" ); } +#endif } else if ( stricmp( szKeyName, "RopeMaterial" ) == 0 ) { @@ -725,12 +829,28 @@ bool CRopeKeyframe::KeyValue( const char *szKeyName, const char *szValue ) SetMaterial( str ); } } +#ifdef MAPBASE + else if( stricmp( szKeyName, "UseWind" ) == 0 ) + { + if( atoi( szValue ) == 1 ) + m_RopeFlags |= ROPE_USE_WIND; + else + m_RopeFlags &= ~ROPE_USE_WIND; + } +#endif else if ( stricmp( szKeyName, "NoWind" ) == 0 ) { +#ifdef MAPBASE + if ( atoi( szValue ) != 1 ) + m_RopeFlags |= ROPE_USE_WIND; + else + m_RopeFlags &= ~ROPE_USE_WIND; +#else if ( atoi( szValue ) == 1 ) { m_RopeFlags |= ROPE_NO_WIND; } +#endif } return BaseClass::KeyValue( szKeyName, szValue ); @@ -749,7 +869,9 @@ void CRopeKeyframe::InputSetScrollSpeed( inputdata_t &inputdata ) void CRopeKeyframe::SetMaterial( const char *pName ) { m_strRopeMaterialModel = AllocPooledString( pName ); +#ifndef MAPBASE m_iRopeMaterialModelIndex = PrecacheModel( STRING( m_strRopeMaterialModel ) ); +#endif } int CRopeKeyframe::UpdateTransmitState() diff --git a/mp/src/game/server/rope.h b/mp/src/game/server/rope.h index 7ab96044..74037800 100644 --- a/mp/src/game/server/rope.h +++ b/mp/src/game/server/rope.h @@ -37,7 +37,12 @@ public: const char *pMaterialName = "cable/cable.vmt", // Note: whoever creates the rope must // use PrecacheModel for whatever material // it specifies here. +#ifdef MAPBASE + int numSegments = 5, + const char *pClassName = "keyframe_rope" +#else int numSegments = 5 +#endif ); static CRopeKeyframe* CreateWithSecondPointDetached( @@ -49,7 +54,12 @@ public: // use PrecacheModel for whatever material // it specifies here. int numSegments = 5, +#ifdef MAPBASE + bool bInitialHang = false, + const char *pClassName = "keyframe_rope" +#else bool bInitialHang = false +#endif ); bool SetupHangDistance( float flHangDist ); @@ -68,6 +78,9 @@ public: virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } virtual void Activate(); virtual void Precache(); +#ifdef MAPBASE + virtual void Spawn(); +#endif virtual int OnTakeDamage( const CTakeDamageInfo &info ); virtual bool KeyValue( const char *szKeyName, const char *szValue ); @@ -92,7 +105,11 @@ public: public: +#ifdef MAPBASE + bool Break( CBaseEntity *pActivator = NULL ); +#else bool Break( void ); +#endif void DetachPoint( int iPoint ); void EndpointsChanged(); @@ -103,12 +120,21 @@ public: // Toggle wind. void EnableWind( bool bEnable ); +#ifdef MAPBASE + CBaseEntity* GetStartPoint() { return m_hStartPoint.Get(); } + int GetStartAttachment() { return m_iStartAttachment; }; +#else // Unless this is called during initialization, the caller should have done // PrecacheModel on whatever material they specify in here. void SetMaterial( const char *pName ); +#endif CBaseEntity* GetEndPoint() { return m_hEndPoint.Get(); } +#ifdef MAPBASE + int GetEndAttachment() { return m_iEndAttachment; }; +#else int GetEndAttachment() { return m_iStartAttachment; }; +#endif void SetStartPoint( CBaseEntity *pStartPoint, int attachment = 0 ); void SetEndPoint( CBaseEntity *pEndPoint, int attachment = 0 ); @@ -122,11 +148,25 @@ public: private: +#ifdef MAPBASE + // Unless this is called during initialization, the caller should have done + // PrecacheModel on whatever material they specify in here. + void SetMaterial( const char *pName ); + +protected: +#endif + void SetAttachmentPoint( CBaseHandle &hOutEnt, short &iOutAttachment, CBaseEntity *pEnt, int iAttachment ); // This is normally called by Activate but if you create the rope at runtime, // you must call it after you have setup its variables. +#ifdef MAPBASE + virtual void Init(); + +private: +#else void Init(); +#endif // These work just like the client-side versions. bool GetEndPointPos2( CBaseEntity *pEnt, int iAttachment, Vector &v ); @@ -151,6 +191,11 @@ public: // Number of subdivisions in between segments. CNetworkVar( int, m_Subdiv ); + +#ifdef MAPBASE + // Used simply to wake up rope on the client side if it has gone to sleep + CNetworkVar( unsigned char, m_nChangeCount ); +#endif //EHANDLE m_hNextLink; @@ -171,6 +216,10 @@ private: CNetworkHandle( CBaseEntity, m_hEndPoint ); CNetworkVar( short, m_iStartAttachment ); // StartAttachment/EndAttachment are attachment points. CNetworkVar( short, m_iEndAttachment ); + +#ifdef MAPBASE + COutputEvent m_OnBreak; +#endif }; diff --git a/mp/src/game/server/saverestore_gamedll.cpp b/mp/src/game/server/saverestore_gamedll.cpp index 768262e1..a1f9505d 100644 --- a/mp/src/game/server/saverestore_gamedll.cpp +++ b/mp/src/game/server/saverestore_gamedll.cpp @@ -93,6 +93,17 @@ bool ParseKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, c UTIL_StringToColor32( (color32 *) ((char *)pObject + fieldOffset), szValue ); return true; +#ifdef MAPBASE + case FIELD_EHANDLE: + ((CBaseHandle*)((char*)pObject + fieldOffset))->Set(gEntList.FindEntityByName(NULL, szValue)); + return true; + + case FIELD_INTERVAL: + extern interval_t ReadInterval( const char *pString ); + (*(interval_t*)((char *)pObject + fieldOffset)) = ReadInterval( szValue ); + return true; +#endif + case FIELD_CUSTOM: { SaveRestoreFieldInfo_t fieldInfo = @@ -106,7 +117,9 @@ bool ParseKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, c } default: +#ifndef MAPBASE case FIELD_INTERVAL: // Fixme, could write this if needed +#endif case FIELD_CLASSPTR: case FIELD_MODELINDEX: case FIELD_MATERIALINDEX: diff --git a/mp/src/game/server/sceneentity.cpp b/mp/src/game/server/sceneentity.cpp index f092624f..78ec120e 100644 --- a/mp/src/game/server/sceneentity.cpp +++ b/mp/src/game/server/sceneentity.cpp @@ -33,6 +33,8 @@ #include "SceneCache.h" #include "scripted.h" #include "env_debughistory.h" +#include "team.h" +#include "triggers.h" #ifdef HL2_EPISODIC #include "npc_alyx_episodic.h" @@ -70,6 +72,17 @@ static int speechListIndex = 0; #define SCENE_MIN_PITCH 0.25f #define SCENE_MAX_PITCH 2.5f +// New macros introduced for Mapbase's console message color changes. +#ifdef MAPBASE +#define ChoreoMsg( lvl, msg ) CGMsg( lvl, CON_GROUP_CHOREO, msg ) +#define ChoreoMsg1( lvl, msg, a ) CGMsg( lvl, CON_GROUP_CHOREO, msg, a ) +#define ChoreoMsg2( lvl, msg, a, b ) CGMsg( lvl, CON_GROUP_CHOREO, msg, a, b ) +#else +#define ChoreoMsg( lvl, msg ) DevMsg( lvl, msg ) +#define ChoreoMsg1( lvl, msg, a ) DevMsg( lvl, msg, a ) +#define ChoreoMsg2( lvl, msg, a, b ) DevMsg( lvl, msg, a, b ) +#endif + //=========================================================================================================== // SCENE LIST MANAGER //=========================================================================================================== @@ -83,6 +96,11 @@ class CSceneListManager : public CLogicalEntity DECLARE_CLASS( CSceneListManager, CLogicalEntity ); public: DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); + + HSCRIPT ScriptGetScene( int iIndex ); +#endif virtual void Activate( void ); @@ -139,6 +157,12 @@ public: bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes ); bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes ); +#ifdef MAPBASE + bool IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes, const char *pszNotThisScene = NULL ); + + CUtlVector< CHandle< CSceneEntity > > *GetActiveSceneList(); +#endif + private: @@ -317,6 +341,8 @@ public: DECLARE_CLASS( CSceneEntity, CPointEntity ); DECLARE_SERVERCLASS(); + // script description + DECLARE_ENT_SCRIPTDESC(); CSceneEntity( void ); ~CSceneEntity( void ); @@ -464,6 +490,8 @@ public: void InputScriptPlayerDeath( inputdata_t &inputdata ); + void AddBroadcastTeamTarget( int nTeamIndex ); + void RemoveBroadcastTeamTarget( int nTeamIndex ); // Data public: string_t m_iszSceneFile; @@ -481,6 +509,18 @@ public: string_t m_iszTarget7; string_t m_iszTarget8; +#ifdef MAPBASE + void SetTarget(int nTarget, string_t pTargetName, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL); + void InputSetTarget1(inputdata_t &inputdata); + void InputSetTarget2(inputdata_t &inputdata); + void InputSetTarget3(inputdata_t &inputdata); + void InputSetTarget4(inputdata_t &inputdata); + void InputSetTarget5(inputdata_t &inputdata); + void InputSetTarget6(inputdata_t &inputdata); + void InputSetTarget7(inputdata_t &inputdata); + void InputSetTarget8(inputdata_t &inputdata); +#endif + EHANDLE m_hTarget1; EHANDLE m_hTarget2; EHANDLE m_hTarget3; @@ -525,6 +565,9 @@ public: virtual CBaseEntity *FindNamedEntity( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false ); CBaseEntity *FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly = false ); virtual CBaseEntity *FindNamedEntityClosest( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false, const char *pszSecondary = NULL ); + HSCRIPT ScriptFindNamedEntity( const char *name ); + bool ScriptLoadSceneFromString( const char * pszFilename, const char *pszData ); + private: @@ -710,6 +753,17 @@ BEGIN_DATADESC( CSceneEntity ) DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitingForActor", InputStopWaitingForActor ), DEFINE_INPUTFUNC( FIELD_INTEGER, "Trigger", InputTriggerEvent ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget1", InputSetTarget1 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget2", InputSetTarget2 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget3", InputSetTarget3 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget4", InputSetTarget4 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget5", InputSetTarget5 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget6", InputSetTarget6 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget7", InputSetTarget7 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget8", InputSetTarget8 ), +#endif + DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ), DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ), @@ -735,6 +789,17 @@ BEGIN_DATADESC( CSceneEntity ) DEFINE_OUTPUT( m_OnTrigger16, "OnTrigger16"), END_DATADESC() + +BEGIN_ENT_SCRIPTDESC( CSceneEntity, CBaseEntity, "Choreographed scene which controls animation and/or dialog on one or more actors." ) + DEFINE_SCRIPTFUNC( EstimateLength, "Returns length of this scene in seconds." ) + DEFINE_SCRIPTFUNC( IsPlayingBack, "If this scene is currently playing." ) + DEFINE_SCRIPTFUNC( IsPaused, "If this scene is currently paused." ) + DEFINE_SCRIPTFUNC( AddBroadcastTeamTarget, "Adds a team (by index) to the broadcast list" ) + DEFINE_SCRIPTFUNC( RemoveBroadcastTeamTarget, "Removes a team (by index) from the broadcast list" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFindNamedEntity, "FindNamedEntity", "given an entity reference, such as !target, get actual entity from scene object" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptLoadSceneFromString, "LoadSceneFromString", "given a dummy scene name and a vcd string, load the scene" ) +END_SCRIPTDESC(); + const ConVar *CSceneEntity::m_pcvSndMixahead = NULL; //----------------------------------------------------------------------------- @@ -915,6 +980,68 @@ float CSceneEntity::GetSoundSystemLatency( void ) // Assume 100 msec sound system latency return SOUND_SYSTEM_LATENCY_DEFAULT; } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// I copied CSceneEntity's PrecacheScene to a unique static function so PrecacheInstancedScene() +// can precache loose scene files without having to use a CSceneEntity. +//----------------------------------------------------------------------------- +void PrecacheChoreoScene( CChoreoScene *scene ) +{ + Assert( scene ); + + // Iterate events and precache necessary resources + for ( int i = 0; i < scene->GetNumEvents(); i++ ) + { + CChoreoEvent *event = scene->GetEvent( i ); + if ( !event ) + continue; + + // load any necessary data + switch (event->GetType() ) + { + default: + break; + case CChoreoEvent::SPEAK: + { + // Defined in SoundEmitterSystem.cpp + // NOTE: The script entries associated with .vcds are forced to preload to avoid + // loading hitches during triggering + CBaseEntity::PrecacheScriptSound( event->GetParameters() ); + + if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER && + event->GetNumSlaves() > 0 ) + { + char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; + if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) ) + { + CBaseEntity::PrecacheScriptSound( tok ); + } + } + } + break; + case CChoreoEvent::SUBSCENE: + { + // Only allow a single level of subscenes for now + if ( !scene->IsSubScene() ) + { + CChoreoScene *subscene = event->GetSubScene(); + if ( !subscene ) + { + subscene = ChoreoLoadScene( event->GetParameters(), NULL, &g_TokenProcessor, LocalScene_Printf ); + subscene->SetSubScene( true ); + event->SetSubScene( subscene ); + + // Now precache it's resources, if any + PrecacheChoreoScene( subscene ); + } + } + } + break; + } + } +} +#endif //----------------------------------------------------------------------------- // Purpose: @@ -1490,7 +1617,11 @@ void CSceneEntity::DispatchEndGesture( CChoreoScene *scene, CBaseFlex *actor, CC void CSceneEntity::DispatchStartGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { CBaseEntity *pTarget = FindNamedEntity( event->GetParameters2( ) ); +#ifdef MAPBASE + actor->AddSceneEvent( scene, event, pTarget, this ); +#else actor->AddSceneEvent( scene, event, pTarget ); +#endif } @@ -2109,7 +2240,11 @@ void CSceneEntity::InputPitchShiftPlayback( inputdata_t &inputdata ) void CSceneEntity::InputTriggerEvent( inputdata_t &inputdata ) { +#ifdef MAPBASE + CBaseEntity *pActivator = inputdata.pActivator; +#else CBaseEntity *pActivator = this; // at some point, find this from the inputdata +#endif switch ( inputdata.value.Int() ) { case 1: @@ -2231,6 +2366,107 @@ void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CSceneEntity::SetTarget( int nTarget, string_t pTargetName, CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + if (/*m_bIsPlayingBack ||*/ !nTarget) + { + return; + } + + switch (nTarget) + { + case 1: m_iszTarget1 = pTargetName; break; + case 2: m_iszTarget2 = pTargetName; break; + case 3: m_iszTarget3 = pTargetName; break; + case 4: m_iszTarget4 = pTargetName; break; + case 5: m_iszTarget5 = pTargetName; break; + case 6: m_iszTarget6 = pTargetName; break; + case 7: m_iszTarget7 = pTargetName; break; + case 8: m_iszTarget8 = pTargetName; break; + } + + // Reset our handle. + // Internal functions set them when they're null anyway. + switch (nTarget) + { + case 1: m_hTarget1 = NULL; break; + case 2: m_hTarget2 = NULL; break; + case 3: m_hTarget3 = NULL; break; + case 4: m_hTarget4 = NULL; break; + case 5: m_hTarget5 = NULL; break; + case 6: m_hTarget6 = NULL; break; + case 7: m_hTarget7 = NULL; break; + case 8: m_hTarget8 = NULL; break; + } + + //CBaseEntity *pTarget = gEntList.FindEntityByName(NULL, pTargetName, this, pActivator, pCaller); + //if (!pTarget) + //{ + // DevWarning("%s (%s) could not find SetTarget entity %s!\n", GetClassname(), GetDebugName(), pTargetName); + // return; + //} + + /* + switch (nTarget) + { + case 1: m_hTarget1 = pTarget; break; + case 2: m_hTarget2 = pTarget; break; + case 3: m_hTarget3 = pTarget; break; + case 4: m_hTarget4 = pTarget; break; + case 5: m_hTarget5 = pTarget; break; + case 6: m_hTarget6 = pTarget; break; + case 7: m_hTarget7 = pTarget; break; + case 8: m_hTarget8 = pTarget; break; + } + */ +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CSceneEntity::InputSetTarget1( inputdata_t &inputdata ) +{ + SetTarget(1, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget2( inputdata_t &inputdata ) +{ + SetTarget(2, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget3( inputdata_t &inputdata ) +{ + SetTarget(3, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget4( inputdata_t &inputdata ) +{ + SetTarget(4, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget5( inputdata_t &inputdata ) +{ + SetTarget(5, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget6( inputdata_t &inputdata ) +{ + SetTarget(6, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget7( inputdata_t &inputdata ) +{ + SetTarget(7, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} + +void CSceneEntity::InputSetTarget8( inputdata_t &inputdata ) +{ + SetTarget(8, inputdata.value.StringID(), inputdata.pActivator, inputdata.pCaller); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CSceneEntity::InputStopWaitingForActor( inputdata_t &inputdata ) @@ -2464,7 +2700,7 @@ void CSceneEntity::StartPlayback( void ) m_pScene = LoadScene( STRING( m_iszSceneFile ), this ); if ( !m_pScene ) { - DevMsg( "%s missing from scenes.image\n", STRING( m_iszSceneFile ) ); + ChoreoMsg1( 1, "%s missing from scenes.image\n", STRING( m_iszSceneFile ) ); m_bSceneMissing = true; return; } @@ -2807,6 +3043,12 @@ void CSceneEntity::DispatchStartSubScene( CChoreoScene *scene, CBaseFlex *pActor if ( subscene ) { +#ifdef MAPBASE + // Somes may not be created with a CSceneEntity as the event callback + if (!scene->GetEventCallbackInterface()) + scene->SetEventCallbackInterface( this ); +#endif + subscene->ResetSimulation(); } } @@ -2830,7 +3072,7 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv CBaseFlex *pActor = NULL; CChoreoActor *actor = event->GetActor(); - if ( actor ) + if ( actor && (event->GetType() != CChoreoEvent::SCRIPT) && (event->GetType() != CChoreoEvent::CAMERA) ) { pActor = FindNamedActor( actor ); if (pActor == NULL) @@ -2993,6 +3235,80 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv } } break; + + case CChoreoEvent::CAMERA: + { + // begin the camera shot + const char *pszShotType = event->GetParameters(); + + CBaseEntity *pActor1 = FindNamedEntity( event->GetParameters2( ), pActor ); + CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters3( ), pActor ); + float duration = event->GetDuration(); + + // grab any camera we find in the map + // TODO: find camera that is nearest this scene entity? + CTriggerCamera *pCamera = (CTriggerCamera *)gEntList.FindEntityByClassname( NULL, "point_viewcontrol" ); + + if ( !pCamera ) + { + Warning( "CSceneEntity %s unable to find a camera (point_viewcontrol) in this map!\n", STRING(GetEntityName()) ); + } + else + { + pCamera->StartCameraShot( pszShotType, this, pActor1, pActor2, duration ); + } + } + break; + + case CChoreoEvent::SCRIPT: + { + // NOTE: this is only used by auto-generated vcds to embed script commands to map entities. + + // vscript call - param1 is entity name, param2 is function name, param3 is function parameter string + // calls a vscript function defined on the scope of the named CBaseEntity object/actor. + // script call is of the format FunctionName(pActor, pThisSceneEntity, pszScriptParameters, duration) + const char *pszActorName = event->GetParameters(); + const char *pszFunctionName = event->GetParameters2(); + const char *pszScriptParameters = event->GetParameters3(); + + float duration = event->GetDuration(); + + // TODO: should be new method CBaseEntity::CallScriptFunctionParams() + CBaseEntity *pEntity = (CBaseEntity *)gEntList.FindEntityByName( NULL, pszActorName ); + + //START_VMPROFILE + if ( !pEntity ) + { + Warning( "CSceneEntity::SCRIPT event - unable to find entity named '%s' in this map!\n", pszActorName ); + } + else + { + + if( !pEntity->ValidateScriptScope() ) + { + ChoreoMsg(1, "\n***\nCChoreoEvent::SCRIPT - FAILED to create private ScriptScope. ABORTING script call\n***\n"); + break; + } + + HSCRIPT hFunc = pEntity->m_ScriptScope.LookupFunction( pszFunctionName ); + + if( hFunc ) + { + pEntity->m_ScriptScope.Call( hFunc, NULL, ToHScript(this), pszScriptParameters, duration ); + pEntity->m_ScriptScope.ReleaseFunction( hFunc ); + + //UPDATE_VMPROFILE + } + else + { + Warning("CSceneEntity::SCRIPT event - '%s' entity has no script function '%s' defined!\n", pszActorName,pszFunctionName); + } + } + + } + break; + + case CChoreoEvent::FIRETRIGGER: { if ( IsMultiplayer() ) @@ -3201,6 +3517,19 @@ void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEven } } break; + + case CChoreoEvent::CAMERA: + { + // call the end of camera or call a dispatch function + } + break; + + case CChoreoEvent::SCRIPT: + { + // call the end of script or call a dispatch function + } + break; + case CChoreoEvent::SEQUENCE: { if ( pActor ) @@ -3309,7 +3638,7 @@ bool CSceneEntity::ShouldNetwork() const CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallback *pCallback ) { - DevMsg( 2, "Blocking load of scene from '%s'\n", filename ); + ChoreoMsg1( 2, "Blocking load of scene from '%s'\n", filename ); char loadfile[MAX_PATH]; Q_strncpy( loadfile, filename, sizeof( loadfile ) ); @@ -3318,6 +3647,44 @@ CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallbac // binary compiled vcd void *pBuffer; +#ifdef MAPBASE + // + // Raw scene file support + // + CChoreoScene *pScene; + int fileSize; + + // First, check if it's in scenes.image... + if ( CopySceneFileIntoMemory( loadfile, &pBuffer, &fileSize ) ) + { + pScene = new CChoreoScene( NULL ); + CUtlBuffer buf( pBuffer, fileSize, CUtlBuffer::READ_ONLY ); + if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) ) + { + Warning( "CSceneEntity::LoadScene: Unable to load binary scene '%s'\n", loadfile ); + delete pScene; + pScene = NULL; + } + } + // Next, check if it's a loose file... + else if (filesystem->ReadFileEx( loadfile, "MOD", &pBuffer, true )) + { + g_TokenProcessor.SetBuffer((char*)pBuffer); + pScene = ChoreoLoadScene( loadfile, NULL, &g_TokenProcessor, LocalScene_Printf ); + } + // Okay, it's definitely missing. + else + { + MissingSceneWarning( loadfile ); + return NULL; + } + + if (pScene) + { + pScene->SetPrintFunc( LocalScene_Printf ); + pScene->SetEventCallbackInterface( pCallback ); + } +#else int fileSize; if ( !CopySceneFileIntoMemory( loadfile, &pBuffer, &fileSize ) ) { @@ -3338,6 +3705,7 @@ CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallbac pScene->SetPrintFunc( LocalScene_Printf ); pScene->SetEventCallbackInterface( pCallback ); } +#endif FreeSceneFileMemory( pBuffer ); return pScene; @@ -3823,6 +4191,72 @@ CBaseEntity *CSceneEntity::FindNamedEntity( const char *name, CBaseEntity *pActo return entity; } +#ifdef MAPBASE +const char *GetFirstSoundInScene(const char *pszScene) +{ + SceneCachedData_t sceneData; + if ( scenefilecache->GetSceneCachedData( pszScene, &sceneData ) ) + { + if ( sceneData.numSounds > 0 ) + { + // 0 is the first index...right? + short stringId = scenefilecache->GetSceneCachedSound( sceneData.sceneId, 0 ); + + // Trust that it's been precached + return scenefilecache->GetSceneString( stringId ); + } + } + else + { + void *pBuffer = NULL; + if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, false, true )) + { + g_TokenProcessor.SetBuffer((char*)pBuffer); + CChoreoScene *pScene = ChoreoLoadScene( pszScene, NULL, &g_TokenProcessor, LocalScene_Printf ); + if (pScene) + { + for (int i = 0; i < pScene->GetNumEvents(); i++) + { + CChoreoEvent *pEvent = pScene->GetEvent(i); + + if (pEvent->GetType() == CChoreoEvent::SPEAK) + return pEvent->GetParameters(); + } + } + } + } + + return NULL; +} + +const char *GetFirstSoundInScene(CChoreoScene *scene) +{ + for ( int i = 0; i < scene->GetNumEvents(); i++ ) + { + CChoreoEvent *pEvent = scene->GetEvent( i ); + + if (pEvent->GetType() == CChoreoEvent::SPEAK) + return pEvent->GetParameters(); + } + + return NULL; +} + +CBaseEntity *UTIL_FindNamedSceneEntity(const char *name, CBaseEntity *pActor, CSceneEntity *scene, bool bBaseFlexOnly, bool bUseClear) +{ + if (scene) + { + CBaseEntity *pEnt = scene->FindNamedEntity(name, pActor, bBaseFlexOnly, bUseClear); + return pEnt; + } + else + { + //Warning("SCENE NOT FOUND!\n"); + return NULL; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Search for an actor by name, make sure it can do face poses @@ -3927,6 +4361,53 @@ CBaseEntity *CSceneEntity::FindNamedEntityClosest( const char *name, CBaseEntity } +HSCRIPT CSceneEntity::ScriptFindNamedEntity(const char* name) +{ + return ToHScript(FindNamedEntity(name, NULL, false, false)); +} + +//----------------------------------------------------------------------------- +// Purpose: vscript - create a scene directly from a buffer containing +// a vcd description, and load it into the scene entity. +//----------------------------------------------------------------------------- +bool CSceneEntity::ScriptLoadSceneFromString(const char* pszFilename, const char* pszData) +{ + CChoreoScene* pScene = new CChoreoScene(NULL); + + // CSceneTokenProcessor SceneTokenProcessor; + // SceneTokenProcessor.SetBuffer( pszData ); + g_TokenProcessor.SetBuffer((char*)pszData); + + if (!pScene->ParseFromBuffer(pszFilename, &g_TokenProcessor)) //&SceneTokenProcessor ) ) + { + Warning("CSceneEntity::LoadSceneFromString: Unable to parse scene data '%s'\n", pszFilename); + delete pScene; + pScene = NULL; + } + else + { + pScene->SetPrintFunc(LocalScene_Printf); + pScene->SetEventCallbackInterface(this); + + + // precache all sounds for the newly constructed scene + PrecacheScene(pScene); + } + + if (pScene != NULL) + { + // release prior scene if present + UnloadScene(); + m_pScene = pScene; + return true; + } + else + { + return false; + } +} + + //----------------------------------------------------------------------------- // Purpose: Remove all "scene" expressions from all actors in this scene //----------------------------------------------------------------------------- @@ -4350,6 +4831,44 @@ void CSceneEntity::SetRecipientFilter( IRecipientFilter *filter ) } } +//----------------------------------------------------------------------------- +// Purpose: Adds a player (by index) to the recipient filter +//----------------------------------------------------------------------------- +void CSceneEntity::AddBroadcastTeamTarget(int nTeamIndex) +{ + if (m_pRecipientFilter == NULL) + { + CRecipientFilter filter; + SetRecipientFilter(&filter); + } + + CTeam* pTeam = GetGlobalTeam(nTeamIndex); + Assert(pTeam); + if (pTeam == NULL) + return; + + m_pRecipientFilter->AddRecipientsByTeam(pTeam); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes a player (by index) from the recipient filter +//----------------------------------------------------------------------------- +void CSceneEntity::RemoveBroadcastTeamTarget(int nTeamIndex) +{ + if (m_pRecipientFilter == NULL) + { + CRecipientFilter filter; + SetRecipientFilter(&filter); + } + + CTeam* pTeam = GetGlobalTeam(nTeamIndex); + Assert(pTeam); + if (pTeam == NULL) + return; + + m_pRecipientFilter->RemoveRecipientsByTeam(pTeam); +} + //----------------------------------------------------------------------------- // Purpose: @@ -4509,7 +5028,13 @@ float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE * // *phSceneEnt - // Output : float //----------------------------------------------------------------------------- +#ifdef MAPBASE +float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, const char *soundname, EHANDLE *phSceneEnt, + float flPostDelay, bool bIsBackground, AI_Response *response, + bool bMultiplayer, IRecipientFilter *filter /* = NULL */ ) +#else float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt /*= NULL*/ ) +#endif { if ( !pActor ) { @@ -4527,10 +5052,38 @@ float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname pScene->GenerateSoundScene( pActor, soundname ); +#ifdef MAPBASE + pScene->m_bMultiplayer = bMultiplayer; + pScene->SetPostSpeakDelay( flPostDelay ); + DispatchSpawn( pScene ); + pScene->Activate(); + pScene->m_bIsBackground = bIsBackground; + + pScene->SetBackground( bIsBackground ); + pScene->SetRecipientFilter( filter ); + + if ( response ) + { + float flPreDelay = response->GetPreDelay(); + if ( flPreDelay ) + { + pScene->SetPreDelay( flPreDelay ); + } + } +#else pScene->Spawn(); pScene->Activate(); +#endif pScene->StartPlayback(); +#ifdef MAPBASE + if ( response ) + { + // If the response wants us to abort on NPC state switch, remember that + pScene->SetBreakOnNonIdle( response->ShouldBreakOnNonIdle() ); + } +#endif + if ( phSceneEnt ) { *phSceneEnt = pScene; @@ -4583,9 +5136,54 @@ int GetSceneSpeechCount( char const *pszScene ) { return cachedData.numSounds; } +#ifdef MAPBASE + else + { + void *pBuffer = NULL; + if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, false, true )) + { + int iNumSounds = 0; + + g_TokenProcessor.SetBuffer((char*)pBuffer); + CChoreoScene *pScene = ChoreoLoadScene( pszScene, NULL, &g_TokenProcessor, LocalScene_Printf ); + if (pScene) + { + for (int i = 0; i < pScene->GetNumEvents(); i++) + { + CChoreoEvent *pEvent = pScene->GetEvent(i); + + if (pEvent->GetType() == CChoreoEvent::SPEAK) + iNumSounds++; + } + } + + return iNumSounds; + } + } +#endif return 0; } +HSCRIPT ScriptCreateSceneEntity( const char* pszScene ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create a scene entity mid-game. Entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + g_pScriptVM->RegisterClass( GetScriptDescForClass( CSceneEntity ) ); + CSceneEntity *pScene = (CSceneEntity *)CBaseEntity::CreateNoSpawn( "logic_choreographed_scene", vec3_origin, vec3_angle ); + + if ( pScene ) + { + pScene->m_iszSceneFile = AllocPooledString( pszScene ); + DispatchSpawn( pScene ); + } + + return ToHScript( pScene ); +} + //----------------------------------------------------------------------------- // Purpose: Used for precaching instanced scenes // Input : *pszScene - @@ -4609,12 +5207,31 @@ void PrecacheInstancedScene( char const *pszScene ) SceneCachedData_t sceneData; if ( !scenefilecache->GetSceneCachedData( pszScene, &sceneData ) ) { +#ifdef MAPBASE + char loadfile[MAX_PATH]; + Q_strncpy( loadfile, pszScene, sizeof( loadfile ) ); + Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) ); + Q_FixSlashes( loadfile ); + + // Attempt to precache manually + void *pBuffer = NULL; + if (filesystem->ReadFileEx( loadfile, "MOD", &pBuffer, false, true )) + { + g_TokenProcessor.SetBuffer((char*)pBuffer); + CChoreoScene *pScene = ChoreoLoadScene( loadfile, NULL, &g_TokenProcessor, LocalScene_Printf ); + if (pScene) + { + PrecacheChoreoScene(pScene); + } + } +#else // Scenes are sloppy and don't always exist. // A scene that is not in the pre-built cache image, but on disk, is a true error. if ( developer.GetInt() && ( IsX360() && ( g_pFullFileSystem->GetDVDMode() != DVDMODE_STRICT ) && g_pFullFileSystem->FileExists( pszScene, "GAME" ) ) ) { Warning( "PrecacheInstancedScene: Missing scene '%s' from scene image cache.\nRebuild scene image cache!\n", pszScene ); } +#endif } else { @@ -4812,6 +5429,15 @@ void CInstancedSceneEntity::OnLoaded() { BaseClass::OnLoaded(); SetBackground( m_bIsBackground ); + +#ifdef MAPBASE + // It looks like !Target1 in those default NPC scenes was a freaking lie. + if (m_hOwner) + { + m_hTarget1 = m_hOwner; + m_iszTarget1 = m_hOwner->GetEntityName(); + } +#endif } bool g_bClientFlex = true; @@ -5243,6 +5869,40 @@ bool CSceneManager::IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pAc } +#ifdef MAPBASE +bool CSceneManager::IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes, const char *pszNotThisScene ) +{ + int c = m_ActiveScenes.Count(); + for ( int i = 0; i < c; i++ ) + { + CSceneEntity *pScene = m_ActiveScenes[ i ].Get(); + if ( !pScene || + !pScene->IsPlayingBack() || + pScene->IsPaused() || + ( bIgnoreInstancedScenes && dynamic_cast(pScene) != NULL ) || + ( pszNotThisScene == NULL || Q_strcmp( pszNotThisScene, STRING(pScene->m_iszSceneFile) ) == 0 ) + ) + { + continue; + } + + if ( pScene->InvolvesActor( pActor ) ) + { + if ( pScene->HasFlexAnimation() ) + return true; + } + } + return false; +} + + +CUtlVector< CHandle< CSceneEntity > > *CSceneManager::GetActiveSceneList() +{ + return &m_ActiveScenes; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: // Input : *actor - @@ -5328,6 +5988,18 @@ bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgno return GetSceneManager()->IsRunningScriptedSceneWithSpeechAndNotPaused( pActor, bIgnoreInstancedScenes ); } +#ifdef MAPBASE +bool IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes, const char *pszNotThisScene ) +{ + return GetSceneManager()->IsRunningScriptedSceneWithFlexAndNotPaused( pActor, bIgnoreInstancedScenes, pszNotThisScene ); +} + +CUtlVector< CHandle< CSceneEntity > > *GetActiveSceneList() +{ + return GetSceneManager()->GetActiveSceneList(); +} +#endif + //=========================================================================================================== // SCENE LIST MANAGER @@ -5376,6 +6048,14 @@ BEGIN_DATADESC( CSceneListManager ) DEFINE_INPUTFUNC( FIELD_VOID, "Shutdown", InputShutdown ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CSceneListManager, CBaseEntity, "Stores choreo scenes and cleans them up when a later scene in the list begins playing." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetScene, "GetScene", "Gets the specified scene index from this manager." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -5518,6 +6198,19 @@ void CSceneListManager::RemoveScene( int iIndex ) } } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HSCRIPT CSceneListManager::ScriptGetScene( int iIndex ) +{ + if ( iIndex < 0 || iIndex >= SCENE_LIST_MANAGER_MAX_SCENES ) + return NULL; + + return ToHScript( m_hScenes[iIndex] ); +} +#endif + void ReloadSceneFromDisk( CBaseEntity *ent ) { CSceneEntity *scene = dynamic_cast< CSceneEntity * >( ent ); diff --git a/mp/src/game/server/sceneentity.h b/mp/src/game/server/sceneentity.h index f1580798..6e005a60 100644 --- a/mp/src/game/server/sceneentity.h +++ b/mp/src/game/server/sceneentity.h @@ -24,7 +24,11 @@ struct recentNPCSpeech_t int GetRecentNPCSpeech( recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ] ); float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL, bool bMultiplayer = false, IRecipientFilter *filter = NULL ); +#ifdef MAPBASE +float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL, bool bMultiplayer = false, IRecipientFilter *filter = NULL ); +#else float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt = NULL ); +#endif void StopScriptedScene( CBaseFlex *pActor, EHANDLE hSceneEnt ); void RemoveActorFromScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly, bool nonidlescenesonly = false, const char *pszThisSceneOnly = NULL ); void RemoveAllScenesInvolvingActor( CBaseFlex *pActor ); @@ -35,14 +39,26 @@ bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes = tr bool IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes = true ); bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); +#ifdef MAPBASE +bool IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false, const char *pszNotThisScene = NULL ); +CUtlVector< CHandle< CSceneEntity > > *GetActiveSceneList(); +#endif float GetSceneDuration( char const *pszScene ); int GetSceneSpeechCount( char const *pszScene ); bool IsInInterruptableScenes( CBaseFlex *pActor ); void PrecacheInstancedScene( char const *pszScene ); +HSCRIPT ScriptCreateSceneEntity( char const *pszScene ); char const *GetSceneFilename( CBaseEntity *ent ); void ReloadSceneFromDisk( CBaseEntity *ent ); +#ifdef MAPBASE +const char *GetFirstSoundInScene(const char *pszScene); +const char *GetFirstSoundInScene(CChoreoScene *scene); + +CBaseEntity *UTIL_FindNamedSceneEntity(const char *name, CBaseEntity *pActor, CSceneEntity *scene, bool bBaseFlexOnly = false, bool bUseClear = false); +#endif + #endif // SCENEENTITY_H diff --git a/mp/src/game/server/scripted.cpp b/mp/src/game/server/scripted.cpp index 6a492708..19fc6784 100644 --- a/mp/src/game/server/scripted.cpp +++ b/mp/src/game/server/scripted.cpp @@ -31,6 +31,23 @@ ConVar ai_task_pre_script( "ai_task_pre_script", "0", FCVAR_NONE ); +// New macros introduced for Mapbase's console message color changes. +#ifdef MAPBASE +#define ScriptMsg( lvl, msg ) CGMsg( lvl, CON_GROUP_NPC_SCRIPTS, msg ) +#define ScriptMsg1( lvl, msg, a ) CGMsg( lvl, CON_GROUP_NPC_SCRIPTS, msg, a ) +#define ScriptMsg2( lvl, msg, a, b ) CGMsg( lvl, CON_GROUP_NPC_SCRIPTS, msg, a, b ) +#define ScriptMsg3( lvl, msg, a, b, c ) CGMsg( lvl, CON_GROUP_NPC_SCRIPTS, msg, a, b, c ) +#define ScriptMsg4( lvl, msg, a, b, c, d ) CGMsg( lvl, CON_GROUP_NPC_SCRIPTS, msg, a, b, c, d ) +#define ScriptMsg5( lvl, msg, a, b, c, d, e ) CGMsg( lvl, CON_GROUP_NPC_SCRIPTS, msg, a, b, c, d, e ) +#else +#define ScriptMsg( lvl, msg ) DevMsg( lvl, msg ) +#define ScriptMsg1( lvl, msg, a ) DevMsg( lvl, msg, a ) +#define ScriptMsg2( lvl, msg, a, b ) DevMsg( lvl, msg, a, b ) +#define ScriptMsg3( lvl, msg, a, b, c ) DevMsg( lvl, msg, a, b, c ) +#define ScriptMsg4( lvl, msg, a, b, c, d ) DevMsg( lvl, msg, a, b, c, d ) +#define ScriptMsg5( lvl, msg, a, b, c, d, e ) DevMsg( lvl, msg, a, b, c, d, e ) +#endif + // // targetname "me" - there can be more than one with the same name, and they act in concert @@ -100,6 +117,10 @@ BEGIN_DATADESC( CAI_ScriptedSequence ) DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ), DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ), +#ifdef MAPBASE + DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ), +#endif + // Outputs DEFINE_OUTPUT(m_OnBeginSequence, "OnBeginSequence"), DEFINE_OUTPUT(m_OnEndSequence, "OnEndSequence"), @@ -114,6 +135,10 @@ BEGIN_DATADESC( CAI_ScriptedSequence ) DEFINE_OUTPUT(m_OnScriptEvent[5], "OnScriptEvent06"), DEFINE_OUTPUT(m_OnScriptEvent[6], "OnScriptEvent07"), DEFINE_OUTPUT(m_OnScriptEvent[7], "OnScriptEvent08"), +#ifdef MAPBASE + DEFINE_OUTPUT(m_OnPreIdleSequence, "OnPreIdleSequence"), + DEFINE_OUTPUT(m_OnFoundNPC, "OnFoundNPC"), +#endif END_DATADESC() @@ -179,18 +204,31 @@ void CAI_ScriptedSequence::ScriptEntityCancel( CBaseEntity *pentCine, bool bPret if ( bPretendSuccess ) { // We need to pretend that this sequence actually finished fully +#ifdef MAPBASE + pCineTarget->m_OnEndSequence.FireOutput(pEntity, pCineTarget); + pCineTarget->m_OnPostIdleEndSequence.FireOutput(pEntity, pCineTarget); +#else pCineTarget->m_OnEndSequence.FireOutput(NULL, pCineTarget); pCineTarget->m_OnPostIdleEndSequence.FireOutput(NULL, pCineTarget); +#endif } else { // Fire the cancel +#ifdef MAPBASE + pCineTarget->m_OnCancelSequence.FireOutput(pEntity, pCineTarget); +#else pCineTarget->m_OnCancelSequence.FireOutput(NULL, pCineTarget); +#endif if ( pCineTarget->m_startTime == 0 ) { // If start time is 0, this sequence never actually ran. Fire the failed output. +#ifdef MAPBASE + pCineTarget->m_OnCancelFailedSequence.FireOutput(pEntity, pCineTarget); +#else pCineTarget->m_OnCancelFailedSequence.FireOutput(NULL, pCineTarget); +#endif } } } @@ -332,6 +370,18 @@ void CAI_ScriptedSequence::InputMoveToPosition( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets our target NPC with the generic SetTarget input. +//----------------------------------------------------------------------------- +void CAI_ScriptedSequence::InputSetTarget( inputdata_t &inputdata ) +{ + m_hActivator = inputdata.pActivator; + m_iszEntity = AllocPooledString(inputdata.value.String()); + m_hTargetEnt = NULL; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Input handler that activates the scripted sequence. @@ -343,6 +393,14 @@ void CAI_ScriptedSequence::InputBeginSequence( inputdata_t &inputdata ) // Start the script as soon as possible. m_bWaitForBeginSequence = false; + +#ifdef MAPBASE + m_hActivator = inputdata.pActivator; + + // TODO: Investigate whether this is necessary + //if (FStrEq(STRING(m_iszEntity), "!activator")) + // SetTarget(NULL); +#endif // do I already know who I should use? CBaseEntity *pEntity = GetTarget(); @@ -407,7 +465,7 @@ void CAI_ScriptedSequence::InputCancelSequence( inputdata_t &inputdata ) // We don't call CancelScript because entity I/O will handle dispatching // this input to all other scripts with our same name. // - DevMsg( 2, "InputCancelScript: Cancelling script '%s'\n", STRING( m_iszPlay )); + ScriptMsg1( 2, "InputCancelScript: Cancelling script '%s'\n", STRING( m_iszPlay )); StopThink(); ScriptEntityCancel( this ); } @@ -423,7 +481,7 @@ void CAI_ScriptedSequence::InputScriptPlayerDeath( inputdata_t &inputdata ) // We don't call CancelScript because entity I/O will handle dispatching // this input to all other scripts with our same name. // - DevMsg( 2, "InputCancelScript: Cancelling script '%s'\n", STRING( m_iszPlay )); + ScriptMsg1( 2, "InputCancelScript: Cancelling script '%s'\n", STRING( m_iszPlay )); StopThink(); ScriptEntityCancel( this ); } @@ -472,7 +530,7 @@ void CAI_ScriptedSequence::Blocked( CBaseEntity *pOther ) void CAI_ScriptedSequence::Touch( CBaseEntity *pOther ) { /* - DevMsg( 2, "Cine Touch\n" ); + ScriptMsg( 2, "Cine Touch\n" ); if (m_pentTarget && OFFSET(pOther->pev) == OFFSET(m_pentTarget)) { CAI_BaseNPC *pTarget = GetClassPtr((CAI_BaseNPC *)VARS(m_pentTarget)); @@ -521,7 +579,11 @@ CAI_BaseNPC *CAI_ScriptedSequence::FindScriptEntity( ) { interrupt = SS_INTERRUPT_BY_NAME; +#ifdef MAPBASE + pEntity = gEntList.FindEntityByNameWithin( m_hLastFoundEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius, this, m_hActivator ); +#else pEntity = gEntList.FindEntityByNameWithin( m_hLastFoundEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius ); +#endif if (!pEntity) { pEntity = gEntList.FindEntityByClassnameWithin( m_hLastFoundEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius ); @@ -551,7 +613,7 @@ CAI_BaseNPC *CAI_ScriptedSequence::FindScriptEntity( ) else if (!(m_spawnflags & SF_SCRIPT_NO_COMPLAINTS)) { // They cannot play the script. - DevMsg( "Found %s, but can't play!\n", STRING( m_iszEntity )); + ScriptMsg1( 1, "Found %s, but can't play!\n", STRING( m_iszEntity )); } } @@ -638,7 +700,7 @@ void CAI_ScriptedSequence::StartScript( void ) // Don't clear the currently playing script's target! pCine->SetTarget( NULL ); } - DevMsg( 2, "script \"%s\" kicking script \"%s\" out of the queue\n", GetDebugName(), pCine->GetDebugName() ); + ScriptMsg2( 2, "script \"%s\" kicking script \"%s\" out of the queue\n", GetDebugName(), pCine->GetDebugName() ); } pTarget->m_hCine->m_hNextCine = this; @@ -744,7 +806,7 @@ void CAI_ScriptedSequence::StartScript( void ) //pTarget->SetGroundEntity( NULL ); break; } - //DevMsg( 2, "\"%s\" found and used (INT: %s)\n", STRING( pTarget->m_iName ), FBitSet(m_spawnflags, SF_SCRIPT_NOINTERRUPT)?"No":"Yes" ); + //ScriptMsg2( 2, "\"%s\" found and used (INT: %s)\n", STRING( pTarget->m_iName ), FBitSet(m_spawnflags, SF_SCRIPT_NOINTERRUPT)?"No":"Yes" ); // Wait until all scripts of the same name are ready to play. @@ -759,6 +821,10 @@ void CAI_ScriptedSequence::StartScript( void ) DevWarning( "scripted_sequence %d:%s - restarting dormant entity %d:%s : %.1f:%.1f\n", entindex(), GetDebugName(), pTarget->entindex(), pTarget->GetDebugName(), gpGlobals->curtime, pTarget->GetNextThink() ); pTarget->SetNextThink( gpGlobals->curtime ); } + +#ifdef MAPBASE + m_OnFoundNPC.FireOutput( pTarget, this ); +#endif } } @@ -775,12 +841,12 @@ void CAI_ScriptedSequence::ScriptThink( void ) else if (FindEntity()) { StartScript( ); - DevMsg( 2, "scripted_sequence %d:\"%s\" using NPC %d:\"%s\"(%s)\n", entindex(), GetDebugName(), GetTarget()->entindex(), GetTarget()->GetEntityName().ToCStr(), STRING( m_iszEntity ) ); + ScriptMsg5( 2, "scripted_sequence %d:\"%s\" using NPC %d:\"%s\"(%s)\n", entindex(), GetDebugName(), GetTarget()->entindex(), GetTarget()->GetEntityName().ToCStr(), STRING( m_iszEntity ) ); } else { CancelScript( ); - DevMsg( 2, "scripted_sequence %d:\"%s\" can't find NPC \"%s\"\n", entindex(), GetDebugName(), STRING( m_iszEntity ) ); + ScriptMsg3( 2, "scripted_sequence %d:\"%s\" can't find NPC \"%s\"\n", entindex(), GetDebugName(), STRING( m_iszEntity ) ); // FIXME: just trying again is bad. This should fire an output instead. // FIXME: Think about puting output triggers in both StartScript() and CancelScript(). SetNextThink( gpGlobals->curtime + 1.0f ); @@ -792,10 +858,22 @@ void CAI_ScriptedSequence::ScriptThink( void ) // Purpose: Callback for firing the begin sequence output. Called by the NPC that // is running the script as it starts the action seqeunce. //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CAI_ScriptedSequence::OnBeginSequence( CBaseEntity *pActor ) +{ + m_OnBeginSequence.FireOutput( pActor, this ); +} + +void CAI_ScriptedSequence::OnPreIdleSequence( CBaseEntity *pActor ) +{ + m_OnPreIdleSequence.FireOutput( pActor, this ); +} +#else void CAI_ScriptedSequence::OnBeginSequence( void ) { m_OnBeginSequence.FireOutput( this, this ); } +#endif //----------------------------------------------------------------------------- @@ -841,7 +919,7 @@ bool CAI_ScriptedSequence::StartSequence( CAI_BaseNPC *pTarget, string_t iszSeq, // Don't blend... pTarget->IncrementInterpolationFrame(); } - //DevMsg( 2, "%s (%s): started \"%s\":INT:%s\n", STRING( pTarget->m_iName ), pTarget->GetClassname(), STRING( iszSeq), (m_spawnflags & SF_SCRIPT_NOINTERRUPT) ? "No" : "Yes" ); + //ScriptMsg4( 2, "%s (%s): started \"%s\":INT:%s\n", STRING( pTarget->m_iName ), pTarget->GetClassname(), STRING( iszSeq), (m_spawnflags & SF_SCRIPT_NOINTERRUPT) ? "No" : "Yes" ); return true; } @@ -921,7 +999,7 @@ bool CAI_ScriptedSequence::FinishedActionSequence( CAI_BaseNPC *pNPC ) //----------------------------------------------------------------------------- void CAI_ScriptedSequence::SequenceDone( CAI_BaseNPC *pNPC ) { - //DevMsg( 2, "Sequence %s finished\n", STRING( pNPC->m_hCine->m_iszPlay ) ); + //ScriptMsg1( 2, "Sequence %s finished\n", STRING( pNPC->m_hCine->m_iszPlay ) ); //Msg("%s SequenceDone() at %0.2f\n", pNPC->GetDebugName(), gpGlobals->curtime ); @@ -964,7 +1042,11 @@ void CAI_ScriptedSequence::SequenceDone( CAI_BaseNPC *pNPC ) } } +#ifdef MAPBASE + m_OnEndSequence.FireOutput(pNPC, this); +#else m_OnEndSequence.FireOutput(NULL, this); +#endif } //----------------------------------------------------------------------------- @@ -1027,7 +1109,7 @@ void CAI_ScriptedSequence::PostIdleDone( CAI_BaseNPC *pNPC ) // Only do so if we're selected, to prevent spam if ( pNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) { - DevMsg( 2, "Post Idle %s finished for %s\n", STRING( pNPC->m_hCine->m_iszPostIdle ), pNPC->GetDebugName() ); + ScriptMsg2( 2, "Post Idle %s finished for %s\n", STRING( pNPC->m_hCine->m_iszPostIdle ), pNPC->GetDebugName() ); } pNPC->m_scriptState = CAI_BaseNPC::SCRIPT_POST_IDLE; @@ -1073,7 +1155,11 @@ void CAI_ScriptedSequence::PostIdleDone( CAI_BaseNPC *pNPC ) } //Msg("%s finished post idle at %0.2f\n", pNPC->GetDebugName(), gpGlobals->curtime ); +#ifdef MAPBASE + m_OnPostIdleEndSequence.FireOutput(pNPC, this); +#else m_OnPostIdleEndSequence.FireOutput(NULL, this); +#endif } @@ -1189,7 +1275,7 @@ bool CAI_ScriptedSequence::CanEnqueueAfter( void ) if ( m_iszNextScript != NULL_STRING ) { - DevMsg( 2, "%s is specified as the 'Next Script' and cannot be kicked out of the queue\n", m_hNextCine->GetDebugName() ); + ScriptMsg1( 2, "%s is specified as the 'Next Script' and cannot be kicked out of the queue\n", m_hNextCine->GetDebugName() ); return false; } @@ -1198,7 +1284,7 @@ bool CAI_ScriptedSequence::CanEnqueueAfter( void ) return true; } - DevMsg( 2, "%s is a priority script and cannot be kicked out of the queue\n", m_hNextCine->GetDebugName() ); + ScriptMsg1( 2, "%s is a priority script and cannot be kicked out of the queue\n", m_hNextCine->GetDebugName() ); return false; } @@ -1333,7 +1419,7 @@ void CAI_ScriptedSequence::ModifyScriptedAutoMovement( Vector *vecNewPos ) //----------------------------------------------------------------------------- void CAI_ScriptedSequence::CancelScript( void ) { - DevMsg( 2, "Cancelling script: %s\n", STRING( m_iszPlay )); + ScriptMsg1( 2, "Cancelling script: %s\n", STRING( m_iszPlay )); // Don't cancel matching sequences if we're asked not to, unless we didn't actually // succeed in starting, in which case we should always cancel. This fixes @@ -1576,6 +1662,9 @@ private: // Input handlers void InputStartSchedule( inputdata_t &inputdata ); void InputStopSchedule( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetTarget( inputdata_t &inputdata ); +#endif CAI_BaseNPC *FindScriptEntity( bool bCyclic ); @@ -1660,7 +1749,7 @@ void CAI_ScriptedSchedule::ScriptThink( void ) pTarget = FindScriptEntity( (m_spawnflags & SF_SCRIPT_SEARCH_CYCLICALLY) != 0 ); if ( pTarget ) { - DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), pTarget->GetEntityName().ToCStr() ); + ScriptMsg3( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), pTarget->GetEntityName().ToCStr() ); StartSchedule( pTarget ); success = true; } @@ -1670,7 +1759,7 @@ void CAI_ScriptedSchedule::ScriptThink( void ) m_hLastFoundEntity = NULL; while ( ( pTarget = FindScriptEntity( true ) ) != NULL ) { - DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), pTarget->GetEntityName().ToCStr(), STRING( m_iszEntity ) ); + ScriptMsg3( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), pTarget->GetEntityName().ToCStr(), STRING( m_iszEntity ) ); StartSchedule( pTarget ); success = true; } @@ -1678,7 +1767,7 @@ void CAI_ScriptedSchedule::ScriptThink( void ) if ( !success ) { - DevMsg( 2, "scripted_schedule \"%s\" can't find NPC \"%s\"\n", GetDebugName(), STRING( m_iszEntity ) ); + ScriptMsg2( 2, "scripted_schedule \"%s\" can't find NPC \"%s\"\n", GetDebugName(), STRING( m_iszEntity ) ); // FIXME: just trying again is bad. This should fire an output instead. // FIXME: Think about puting output triggers on success true and sucess false // FIXME: also needs to check the result of StartSchedule(), which can fail and not complain @@ -1739,7 +1828,7 @@ void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget ) CAI_Hint *pHint = CAI_HintManager::FindHint( pTarget->GetAbsOrigin(), hintCriteria ); if ( !pHint ) { - DevMsg( 1, "Can't find goal entity %s\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() ); + ScriptMsg2( 1, "Can't find goal entity %s\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() ); return; } pGoalEnt = pHint; @@ -1780,7 +1869,7 @@ void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget ) pTarget->SetCondition( COND_SCHEDULE_DONE ); } else - DevMsg( "Scripted schedule %s specified an invalid enemy %s\n", STRING( GetEntityName() ), STRING( m_sGoalEnt ) ); + ScriptMsg2( 1, "Scripted schedule %s specified an invalid enemy %s\n", STRING( GetEntityName() ), STRING( m_sGoalEnt ) ); } bool bDidSetSchedule = false; @@ -1805,7 +1894,7 @@ void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget ) { if (!(m_spawnflags & SF_SCRIPT_NO_COMPLAINTS)) { - DevMsg( 1, "ScheduledMoveToGoalEntity to goal entity %s failed\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() ); + ScriptMsg2( 1, "ScheduledMoveToGoalEntity to goal entity %s failed\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() ); } return; } @@ -1827,7 +1916,7 @@ void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget ) { if (!(m_spawnflags & SF_SCRIPT_NO_COMPLAINTS)) { - DevMsg( 1, "ScheduledFollowPath to goal entity %s failed\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() ); + ScriptMsg2( 1, "ScheduledFollowPath to goal entity %s failed\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() ); } return; } @@ -1852,7 +1941,7 @@ void CAI_ScriptedSchedule::InputStartSchedule( inputdata_t &inputdata ) { if (( m_nForceState == 0 ) && ( m_nSchedule == 0 )) { - DevMsg( 2, "aiscripted_schedule - no schedule or state has been set!\n" ); + ScriptMsg( 2, "aiscripted_schedule - no schedule or state has been set!\n" ); } if ( !m_bDidFireOnce || ( m_spawnflags & SF_SCRIPT_REPEATABLE ) ) @@ -1864,7 +1953,7 @@ void CAI_ScriptedSchedule::InputStartSchedule( inputdata_t &inputdata ) } else { - DevMsg( 2, "aiscripted_schedule - not playing schedule again: not flagged to repeat\n" ); + ScriptMsg( 2, "aiscripted_schedule - not playing schedule again: not flagged to repeat\n" ); } } @@ -1875,7 +1964,7 @@ void CAI_ScriptedSchedule::InputStopSchedule( inputdata_t &inputdata ) { if ( !m_bDidFireOnce ) { - DevMsg( 2, "aiscripted_schedule - StopSchedule called, but schedule's never started.\n" ); + ScriptMsg( 2, "aiscripted_schedule - StopSchedule called, but schedule's never started.\n" ); return; } @@ -1898,6 +1987,17 @@ void CAI_ScriptedSchedule::InputStopSchedule( inputdata_t &inputdata ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets our target NPC with the generic SetTarget input. +//----------------------------------------------------------------------------- +void CAI_ScriptedSchedule::InputSetTarget( inputdata_t &inputdata ) +{ + m_hActivator = inputdata.pActivator; + m_iszEntity = AllocPooledString( inputdata.value.String() ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: If the target entity appears to be running this scripted schedule break it //----------------------------------------------------------------------------- @@ -1905,7 +2005,7 @@ void CAI_ScriptedSchedule::StopSchedule( CAI_BaseNPC *pTarget ) { if ( pTarget->IsCurSchedule( SCHED_IDLE_WALK ) ) { - DevMsg( 2, "%s (%s): StopSchedule called on NPC %s.\n", GetClassname(), GetDebugName(), pTarget->GetDebugName() ); + ScriptMsg3( 2, "%s (%s): StopSchedule called on NPC %s.\n", GetClassname(), GetDebugName(), pTarget->GetDebugName() ); pTarget->ClearSchedule( "Stopping scripted schedule" ); } } @@ -2189,7 +2289,7 @@ int CAI_ScriptedSentence::StartSentence( CAI_BaseNPC *pTarget ) { if ( !pTarget ) { - DevMsg( 2, "Not Playing sentence %s\n", STRING(m_iszSentence) ); + ScriptMsg1( 2, "Not Playing sentence %s\n", STRING(m_iszSentence) ); return -1; } @@ -2214,13 +2314,179 @@ int CAI_ScriptedSentence::StartSentence( CAI_BaseNPC *pTarget ) } int sentenceIndex = pTarget->PlayScriptedSentence( STRING(m_iszSentence), m_flDelay, m_flVolume, m_iSoundLevel, bConcurrent, pListener ); - DevMsg( 2, "Playing sentence %s\n", STRING(m_iszSentence) ); + ScriptMsg1( 2, "Playing sentence %s\n", STRING(m_iszSentence) ); m_OnBeginSentence.FireOutput(NULL, this); return sentenceIndex; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// This isn't exclusive to NPCs, so it could be moved if needed. +//----------------------------------------------------------------------------- +class CScriptedSound : public CPointEntity +{ +public: + DECLARE_CLASS( CScriptedSound, CPointEntity ); + DECLARE_DATADESC(); + + void Precache(); + + CBaseEntity *GetTarget(inputdata_t &inputdata); + + // Input handlers + void InputPlaySound( inputdata_t &inputdata ); + void InputPlaySoundOnEntity( inputdata_t &inputdata ); + void InputStopSound( inputdata_t &inputdata ); + void InputSetSound( inputdata_t &inputdata ); + +private: + string_t m_message; + + bool m_bGrabAll; +}; + + +BEGIN_DATADESC( CScriptedSound ) + + DEFINE_KEYFIELD( m_message, FIELD_STRING, "message" ), + DEFINE_KEYFIELD( m_bGrabAll, FIELD_BOOLEAN, "GrabAll" ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "PlaySound", InputPlaySound), + DEFINE_INPUTFUNC(FIELD_EHANDLE, "PlaySoundOnEntity", InputPlaySoundOnEntity), + DEFINE_INPUTFUNC(FIELD_VOID, "StopSound", InputStopSound), + DEFINE_INPUTFUNC(FIELD_STRING, "SetSound", InputSetSound), + +END_DATADESC() + + + +LINK_ENTITY_TO_CLASS( scripted_sound, CScriptedSound ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CScriptedSound::Precache() +{ + //PrecacheScriptSound(STRING(m_message)); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *CScriptedSound::GetTarget(inputdata_t &inputdata) +{ + CBaseEntity *pEntity = NULL; + if (m_target == NULL_STRING) + { + // Use this as the default source entity + pEntity = this; + m_bGrabAll = false; + } + else + { + pEntity = gEntList.FindEntityGeneric(NULL, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + } + + return pEntity; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CScriptedSound::InputPlaySound( inputdata_t &inputdata ) +{ + PrecacheScriptSound(STRING(m_message)); + + CBaseEntity *pEntity = GetTarget(inputdata); + const char *sound = STRING(m_message); + if (m_bGrabAll) + { + //if (pEntity) + //{ + // pEntity->PrecacheScriptSound(sound); + //} + + while (pEntity) + { + pEntity->EmitSound(sound); + pEntity = gEntList.FindEntityGeneric(pEntity, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + } + } + else if (pEntity) + { + //pEntity->PrecacheScriptSound(sound); + pEntity->EmitSound(sound); + } + else + { + Warning("%s unable to find target entity %s!\n", GetDebugName(), STRING(m_target)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CScriptedSound::InputPlaySoundOnEntity( inputdata_t &inputdata ) +{ + if (inputdata.value.Entity()) + { + inputdata.value.Entity()->PrecacheScriptSound(STRING(m_message)); + inputdata.value.Entity()->EmitSound(STRING(m_message)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CScriptedSound::InputStopSound( inputdata_t &inputdata ) +{ + CBaseEntity *pEntity = GetTarget(inputdata); + const char *sound = STRING(m_message); + if (m_bGrabAll) + { + while (pEntity) + { + pEntity->StopSound(sound); + pEntity = gEntList.FindEntityGeneric(pEntity, STRING(m_target), this, inputdata.pActivator, inputdata.pCaller); + } + } + else if (pEntity) + { + pEntity->StopSound(sound); + } + else + { + Warning("%s unable to find target entity %s!\n", GetDebugName(), STRING(m_target)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CScriptedSound::InputSetSound( inputdata_t &inputdata ) +{ + PrecacheScriptSound(inputdata.value.String()); + m_message = inputdata.value.StringID(); +} +#endif + // HACKHACK: This is a little expensive with the dynamic_cast<> and all, but it lets us solve diff --git a/mp/src/game/server/scripted.h b/mp/src/game/server/scripted.h index 574fcbd5..1b7398a4 100644 --- a/mp/src/game/server/scripted.h +++ b/mp/src/game/server/scripted.h @@ -95,7 +95,12 @@ public: bool FindEntity( void ); void StartScript( void ); void FireScriptEvent( int nEvent ); +#ifdef MAPBASE + void OnBeginSequence( CBaseEntity *pActor ); + void OnPreIdleSequence( CBaseEntity *pActor ); +#else void OnBeginSequence( void ); +#endif void SetTarget( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; }; CBaseEntity *GetTarget( void ) { return m_hTargetEnt; }; @@ -104,6 +109,9 @@ public: void InputBeginSequence( inputdata_t &inputdata ); void InputCancelSequence( inputdata_t &inputdata ); void InputMoveToPosition( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetTarget( inputdata_t &inputdata ); +#endif bool IsTimeToStart( void ); bool IsWaitingForBegin( void ); @@ -211,6 +219,10 @@ private: COutputEvent m_OnCancelSequence; COutputEvent m_OnCancelFailedSequence; // Fired when a scene is cancelled before it's ever run COutputEvent m_OnScriptEvent[MAX_SCRIPT_EVENTS]; +#ifdef MAPBASE + COutputEvent m_OnPreIdleSequence; + COutputEvent m_OnFoundNPC; +#endif static void ScriptEntityCancel( CBaseEntity *pentCine, bool bPretendSuccess = false ); @@ -223,6 +235,11 @@ private: EHANDLE m_hInteractionRelativeEntity; int m_iPlayerDeathBehavior; + +#ifdef MAPBASE + // !activator functionality + EHANDLE m_hActivator; +#endif }; diff --git a/mp/src/game/server/server_base.vpc b/mp/src/game/server/server_base.vpc index f1c3c835..df35da1e 100644 --- a/mp/src/game/server/server_base.vpc +++ b/mp/src/game/server/server_base.vpc @@ -18,6 +18,9 @@ $include "$SRCDIR\vpc_scripts\protobuf_builder.vpc" $Include "$SRCDIR\vpc_scripts\source_replay.vpc" [$TF] $Include "$SRCDIR\game\protobuf_include.vpc" +// Mapbase stuff +$Include "$SRCDIR\game\server\server_mapbase.vpc" [$MAPBASE] + $Configuration "Debug" { $General @@ -645,6 +648,11 @@ $Project $File "$SRCDIR\game\shared\voice_common.h" $File "$SRCDIR\game\shared\voice_gamemgr.cpp" $File "$SRCDIR\game\shared\voice_gamemgr.h" + $File "vscript_server.cpp" + $File "vscript_server.h" + $File "vscript_server.nut" + $File "$SRCDIR\game\shared\vscript_shared.cpp" + $File "$SRCDIR\game\shared\vscript_shared.h" $File "waterbullet.cpp" $File "waterbullet.h" $File "WaterLODControl.cpp" diff --git a/mp/src/game/server/server_mapbase.vpc b/mp/src/game/server/server_mapbase.vpc new file mode 100644 index 00000000..40581e0f --- /dev/null +++ b/mp/src/game/server/server_mapbase.vpc @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// SERVER_MAPBASE.VPC +// +// Project Base Script +//----------------------------------------------------------------------------- + +$Configuration +{ + $Compiler + { + $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS;GLOWS_ENABLE" + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] + } +} + +$Project +{ + $Folder "Source Files" + { + $File "logic_random_outputs.cpp" + $File "point_entity_finder.cpp" + $File "env_projectedtexture.h" + $File "env_global_light.cpp" + $File "skyboxswapper.cpp" + $File "env_instructor_hint.cpp" + $File "postprocesscontroller.cpp" + $File "postprocesscontroller.h" + $File "env_dof_controller.cpp" + $File "env_dof_controller.h" + + $Folder "Mapbase" + { + $File "$SRCDIR\game\shared\mapbase\mapbase_shared.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_rpc.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_game_log.cpp" + $File "$SRCDIR\game\shared\mapbase\MapEdit.cpp" + $File "$SRCDIR\game\shared\mapbase\MapEdit.h" + $File "$SRCDIR\game\shared\mapbase\matchers.cpp" + $File "$SRCDIR\game\shared\mapbase\matchers.h" + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_singletons.h" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_consts_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_consts_weapons.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.h" [$MAPBASE_VSCRIPT] + + $File "mapbase\ai_grenade.cpp" + $File "mapbase\ai_grenade.h" + $File "mapbase\ai_monitor.cpp" + $File "mapbase\ai_weaponmodifier.cpp" + $File "mapbase\closecaption_entity.cpp" + $File "mapbase\datadesc_mod.cpp" + $File "mapbase\datadesc_mod.h" + $File "mapbase\expandedrs_combine.h" + $File "mapbase\func_clientclip.cpp" + $File "mapbase\func_fake_worldportal.cpp" + $File "mapbase\GlobalStrings.cpp" + $File "mapbase\GlobalStrings.h" + $File "mapbase\logic_externaldata.cpp" + $File "mapbase\logic_skill.cpp" + $File "mapbase\point_advanced_finder.cpp" + $File "mapbase\point_copy_size.cpp" + $File "mapbase\point_damageinfo.cpp" + $File "mapbase\point_entity_replace.cpp" + //$File "mapbase\point_physics_control.cpp" // Backlogged + $File "mapbase\point_projectile.cpp" + $File "mapbase\point_radiation_source.cpp" + $File "mapbase\point_glow.cpp" + $File "mapbase\SystemConvarMod.cpp" + $File "mapbase\SystemConvarMod.h" + $File "mapbase\variant_tools.h" + + $File "mapbase\logic_eventlistener.cpp" + $File "mapbase\logic_register_activator.cpp" + } + + $Folder "HL2 DLL" + { + // Original stunstick files are conditional'd out in the HL2 VPCs + $File "$SRCDIR\game\shared\hl2mp\weapon_stunstick.cpp" + $File "$SRCDIR\game\shared\hl2mp\weapon_stunstick.h" + } + + $Folder "HL2MP" + { + $Folder "Weapons" + { + $File "hl2mp\grenade_satchel.cpp" + $File "hl2mp\grenade_satchel.h" + $File "hl2mp\grenade_tripmine.cpp" + $File "hl2mp\grenade_tripmine.h" + + $File "$SRCDIR\game\shared\hl2mp\weapon_slam.cpp" + $File "$SRCDIR\game\shared\hl2mp\weapon_slam.h" + } + } + } + + $Folder "Link Libraries" + { + $Lib "vscript" [$MAPBASE_VSCRIPT] + } +} diff --git a/mp/src/game/server/shadowcontrol.cpp b/mp/src/game/server/shadowcontrol.cpp index 36c23c28..8db9665d 100644 --- a/mp/src/game/server/shadowcontrol.cpp +++ b/mp/src/game/server/shadowcontrol.cpp @@ -40,6 +40,9 @@ private: CNetworkColor32( m_shadowColor ); CNetworkVar( float, m_flShadowMaxDist ); CNetworkVar( bool, m_bDisableShadows ); +#ifdef MAPBASE + CNetworkVar( bool, m_bEnableLocalLightShadows ); +#endif }; LINK_ENTITY_TO_CLASS(shadow_control, CShadowControl); @@ -48,12 +51,18 @@ BEGIN_DATADESC( CShadowControl ) DEFINE_KEYFIELD( m_flShadowMaxDist, FIELD_FLOAT, "distance" ), DEFINE_KEYFIELD( m_bDisableShadows, FIELD_BOOLEAN, "disableallshadows" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bEnableLocalLightShadows, FIELD_BOOLEAN, "enableshadowsfromlocallights" ), +#endif // Inputs DEFINE_INPUT( m_shadowColor, FIELD_COLOR32, "color" ), DEFINE_INPUT( m_shadowDirection, FIELD_VECTOR, "direction" ), DEFINE_INPUT( m_flShadowMaxDist, FIELD_FLOAT, "SetDistance" ), DEFINE_INPUT( m_bDisableShadows, FIELD_BOOLEAN, "SetShadowsDisabled" ), +#ifdef MAPBASE + DEFINE_INPUT( m_bEnableLocalLightShadows, FIELD_BOOLEAN, "SetShadowsFromLocalLightsEnabled" ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetAngles", InputSetAngles ), @@ -62,9 +71,17 @@ END_DATADESC() IMPLEMENT_SERVERCLASS_ST_NOBASE(CShadowControl, DT_ShadowControl) SendPropVector(SENDINFO(m_shadowDirection), -1, SPROP_NOSCALE ), +#ifdef MAPBASE + /*SendPropInt(SENDINFO(m_shadowColor), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt32 ),*/ + SendPropInt(SENDINFO(m_shadowColor), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt ), +#else SendPropInt(SENDINFO(m_shadowColor), 32, SPROP_UNSIGNED), +#endif SendPropFloat(SENDINFO(m_flShadowMaxDist), 0, SPROP_NOSCALE ), SendPropBool(SENDINFO(m_bDisableShadows)), +#ifdef MAPBASE + SendPropBool(SENDINFO(m_bEnableLocalLightShadows)), +#endif END_SEND_TABLE() @@ -74,6 +91,9 @@ CShadowControl::CShadowControl() m_flShadowMaxDist = 50.0f; m_shadowColor.Init( 64, 64, 64, 0 ); m_bDisableShadows = false; +#ifdef MAPBASE + m_bEnableLocalLightShadows = false; +#endif } diff --git a/mp/src/game/server/skyboxswapper.cpp b/mp/src/game/server/skyboxswapper.cpp new file mode 100644 index 00000000..40d1f2ba --- /dev/null +++ b/mp/src/game/server/skyboxswapper.cpp @@ -0,0 +1,80 @@ +//=========== Copyright (c) Valve Corporation, All rights reserved. =========== +// +// Simple entity to switch the map's 2D skybox texture when triggered. +// +//============================================================================= + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// CSkyboxSwapper +//----------------------------------------------------------------------------- +class CSkyboxSwapper : public CServerOnlyPointEntity +{ + DECLARE_CLASS( CSkyboxSwapper, CServerOnlyPointEntity ); +public: + DECLARE_DATADESC(); + + virtual void Spawn( void ); + virtual void Precache( void ); + + void InputTrigger( inputdata_t &inputdata ); + +protected: + string_t m_iszSkyboxName; +}; + +LINK_ENTITY_TO_CLASS(skybox_swapper, CSkyboxSwapper); + +BEGIN_DATADESC( CSkyboxSwapper ) + DEFINE_KEYFIELD( m_iszSkyboxName, FIELD_STRING, "SkyboxName" ), + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Trigger", InputTrigger), +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: not much +//----------------------------------------------------------------------------- +void CSkyboxSwapper::Spawn( void ) +{ + Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: Precache the skybox materials. +//----------------------------------------------------------------------------- +void CSkyboxSwapper::Precache( void ) +{ + if ( Q_strlen( m_iszSkyboxName.ToCStr() ) == 0 ) + { + Warning( "skybox_swapper (%s) has no skybox specified!\n", STRING(GetEntityName()) ); + return; + } + + char name[ MAX_PATH ]; + char *skyboxsuffix[ 6 ] = { "rt", "bk", "lf", "ft", "up", "dn" }; + for ( int i = 0; i < 6; i++ ) + { + Q_snprintf( name, sizeof( name ), "skybox/%s%s", m_iszSkyboxName.ToCStr(), skyboxsuffix[i] ); + PrecacheMaterial( name ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that triggers the skybox swap. +//----------------------------------------------------------------------------- +void CSkyboxSwapper::InputTrigger( inputdata_t &inputdata ) +{ + static ConVarRef skyname( "sv_skyname", false ); + if ( !skyname.IsValid() ) + { + Warning( "skybox_swapper (%s) trigger input failed - cannot find 'sv_skyname' convar!\n", STRING(GetEntityName()) ); + return; + } + skyname.SetValue( m_iszSkyboxName.ToCStr() ); +} diff --git a/mp/src/game/server/sound.cpp b/mp/src/game/server/sound.cpp index 1a83fafd..4de86c93 100644 --- a/mp/src/game/server/sound.cpp +++ b/mp/src/game/server/sound.cpp @@ -173,6 +173,9 @@ public: void ToggleSound(); void SendSound( SoundFlags_t flags ); +#ifdef MAPBASE + void SoundEnd(); +#endif // Input handlers void InputPlaySound( inputdata_t &inputdata ); @@ -182,6 +185,9 @@ public: void InputVolume( inputdata_t &inputdata ); void InputFadeIn( inputdata_t &inputdata ); void InputFadeOut( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetSound( inputdata_t &inputdata ); +#endif DECLARE_DATADESC(); @@ -197,6 +203,12 @@ public: string_t m_sSourceEntName; EHANDLE m_hSoundSource; // entity from which the sound comes int m_nSoundSourceEntIndex; // In case the entity goes away before we finish stopping the sound... + +#ifdef MAPBASE + int m_iSoundFlags; + + COutputEvent m_OnSoundFinished; +#endif }; LINK_ENTITY_TO_CLASS( ambient_generic, CAmbientGeneric ); @@ -210,6 +222,10 @@ BEGIN_DATADESC( CAmbientGeneric ) // DEFINE_FIELD( m_hSoundSource, EHANDLE ), // DEFINE_FIELD( m_nSoundSourceEntIndex, FIELD_INTERGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iSoundFlags, FIELD_INTEGER, "soundflags" ), +#endif + DEFINE_FIELD( m_flMaxRadius, FIELD_FLOAT ), DEFINE_FIELD( m_fActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_fLooping, FIELD_BOOLEAN ), @@ -224,6 +240,9 @@ BEGIN_DATADESC( CAmbientGeneric ) // Function Pointers DEFINE_FUNCTION( RampThink ), +#ifdef MAPBASE + DEFINE_THINKFUNC( SoundEnd ), +#endif // Inputs DEFINE_INPUTFUNC(FIELD_VOID, "PlaySound", InputPlaySound ), @@ -233,6 +252,11 @@ BEGIN_DATADESC( CAmbientGeneric ) DEFINE_INPUTFUNC(FIELD_FLOAT, "Volume", InputVolume ), DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeIn", InputFadeIn ), DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeOut", InputFadeOut ), +#ifdef MAPBASE + DEFINE_INPUTFUNC(FIELD_STRING, "SetSound", InputSetSound ), + + DEFINE_OUTPUT( m_OnSoundFinished, "OnSoundFinished" ), +#endif END_DATADESC() @@ -241,6 +265,10 @@ END_DATADESC() #define SF_AMBIENT_SOUND_START_SILENT 16 #define SF_AMBIENT_SOUND_NOT_LOOPING 32 +#ifdef MAPBASE +static const char *g_SoundEndContext = "SoundEnd"; +#endif + //----------------------------------------------------------------------------- // Spawn @@ -419,6 +447,24 @@ void CAmbientGeneric::InputFadeOut( inputdata_t &inputdata ) SetNextThink( gpGlobals->curtime + 0.1f ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAmbientGeneric::InputSetSound( inputdata_t &inputdata ) +{ + m_iszSound = inputdata.value.StringID(); + if (STRING(m_iszSound)[0] != '!') + { + PrecacheScriptSound(STRING(m_iszSound)); + } + + // Try to refresh the sound + if (m_fActive) + SendSound(SND_NOFLAGS); +} +#endif + void CAmbientGeneric::Precache( void ) { @@ -858,6 +904,19 @@ void CAmbientGeneric::InputPlaySound( inputdata_t &inputdata ) { //Adrian: Stop our current sound before starting a new one! SendSound( SND_STOP ); + +#ifdef MAPBASE + // Procedural handling like !activator + if (STRING(m_sSourceEntName)[0] == '!') + { + CBaseEntity *pEntity = gEntList.FindEntityProcedural(STRING(m_sSourceEntName), this, inputdata.pActivator, inputdata.pCaller); + if (pEntity) + { + m_hSoundSource = pEntity; + m_nSoundSourceEntIndex = pEntity->entindex(); + } + } +#endif ToggleSound(); } @@ -877,6 +936,52 @@ void CAmbientGeneric::InputStopSound( inputdata_t &inputdata ) void CAmbientGeneric::SendSound( SoundFlags_t flags) { +#ifdef MAPBASE + int iFlags = flags != SND_STOP ? ((int)flags | m_iSoundFlags) : flags; + char *szSoundFile = (char *)STRING( m_iszSound ); + CBaseEntity* pSoundSource = m_hSoundSource; + if ( pSoundSource ) + { + if ( iFlags & SND_STOP ) + { + UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), szSoundFile, + 0, SNDLVL_NONE, iFlags, 0); + + SetContextThink( NULL, TICK_NEVER_THINK, g_SoundEndContext ); + + m_fActive = false; + } + else + { + float duration = 0.0f; + UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), szSoundFile, + (m_dpv.vol * 0.01), m_iSoundLevel, iFlags, m_dpv.pitch, 0.0f, &duration); + + SetContextThink( &CAmbientGeneric::SoundEnd, gpGlobals->curtime + duration, g_SoundEndContext ); + + // Only mark active if this is a looping sound. If not looping, each + // trigger will cause the sound to play. If the sound is still + // playing from a previous trigger press, it will be shut off + // and then restarted. + + if (m_fLooping) + m_fActive = true; + } + } + else + { + if ( ( iFlags & SND_STOP ) && + ( m_nSoundSourceEntIndex != -1 ) ) + { + UTIL_EmitAmbientSound(m_nSoundSourceEntIndex, GetAbsOrigin(), szSoundFile, + 0, SNDLVL_NONE, iFlags, 0); + + SetContextThink( NULL, TICK_NEVER_THINK, g_SoundEndContext ); + + m_fActive = false; + } + } +#else char *szSoundFile = (char *)STRING( m_iszSound ); CBaseEntity* pSoundSource = m_hSoundSource; if ( pSoundSource ) @@ -901,14 +1006,35 @@ void CAmbientGeneric::SendSound( SoundFlags_t flags) 0, SNDLVL_NONE, flags, 0); } } +#endif } +#ifdef MAPBASE +void CAmbientGeneric::SoundEnd() +{ + m_OnSoundFinished.FireOutput(this, this); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Input handler that stops playing the sound. //----------------------------------------------------------------------------- void CAmbientGeneric::InputToggleSound( inputdata_t &inputdata ) { +#ifdef MAPBASE + // Procedural handling like !activator + if (STRING(m_sSourceEntName)[0] == '!') + { + CBaseEntity *pEntity = gEntList.FindEntityProcedural(STRING(m_sSourceEntName), this, inputdata.pActivator, inputdata.pCaller); + if (pEntity) + { + m_hSoundSource = pEntity; + m_nSoundSourceEntIndex = pEntity->entindex(); + } + } +#endif + ToggleSound(); } diff --git a/mp/src/game/server/soundent.cpp b/mp/src/game/server/soundent.cpp index 4a735637..59273206 100644 --- a/mp/src/game/server/soundent.cpp +++ b/mp/src/game/server/soundent.cpp @@ -45,6 +45,31 @@ BEGIN_SIMPLE_DATADESC( CSound ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CSound, "A sound NPCs can hear." ) + + DEFINE_SCRIPTFUNC( DoesSoundExpire, "Returns true if the sound expires." ) + DEFINE_SCRIPTFUNC( SoundExpirationTime, "Gets the sound's expiration time." ) + DEFINE_SCRIPTFUNC( SetSoundOrigin, "Sets the sound's origin." ) + DEFINE_SCRIPTFUNC( GetSoundOrigin, "Gets the sound's origin." ) + DEFINE_SCRIPTFUNC( GetSoundReactOrigin, "Gets the sound's react origin." ) + DEFINE_SCRIPTFUNC_NAMED( FIsSound, "IsSound", "Returns true if this is a type of sound (as opposed to a scent)." ) + DEFINE_SCRIPTFUNC_NAMED( FIsScent, "IsScent", "Returns true if this is a type of scent (as opposed to a sound)." ) + DEFINE_SCRIPTFUNC( IsSoundType, "Returns true if the sound type is the specified type." ) + DEFINE_SCRIPTFUNC( SoundType, "Gets the raw sound type." ) + DEFINE_SCRIPTFUNC( SoundContext, "Gets the sound type with contexts only." ) + DEFINE_SCRIPTFUNC( SoundTypeNoContext, "Gets the sound type with contexts excluded." ) + DEFINE_SCRIPTFUNC( Volume, "Gets the sound's volume." ) + DEFINE_SCRIPTFUNC( OccludedVolume, "Gets the sound's occluded volume." ) + DEFINE_SCRIPTFUNC( Reset, "Clears the volume, type, and origin for the sound without actually removing it." ) + DEFINE_SCRIPTFUNC( SoundChannel, "Gets the sound's channel." ) + DEFINE_SCRIPTFUNC( ValidateOwner, "Returns true if the sound's owner is still valid or if the sound never had an owner in the first place." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOwner, "GetOwner", "Gets the sound's owner." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetTarget, "GetTarget", "Gets the sound's target." ) + +END_SCRIPTDESC(); +#endif + //========================================================= // CSound - Clear - zeros all fields for a sound @@ -769,7 +794,11 @@ void CAISound::InputInsertSound( inputdata_t &inputdata ) if( m_iszProxyEntityName != NULL_STRING ) { +#ifdef MAPBASE + CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName, this, inputdata.pActivator, inputdata.pCaller ); +#else CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName ); +#endif if( pProxy ) { @@ -781,7 +810,26 @@ void CAISound::InputInsertSound( inputdata_t &inputdata ) } } +#ifdef MAPBASE + EHANDLE hOwner = this; + if (m_target != NULL_STRING) + { + CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_target, this, inputdata.pActivator, inputdata.pCaller ); + + if( pProxy ) + { + hOwner = pProxy; + } + else + { + DevWarning("Warning- ai_sound cannot find owner entity named '%s'. Using self.\n", STRING(m_target) ); + } + } + + g_pSoundEnt->InsertSound( m_iSoundType | m_iSoundContext, vecLocation, iVolume, m_flDuration, hOwner ); +#else g_pSoundEnt->InsertSound( m_iSoundType, vecLocation, iVolume, m_flDuration, this ); +#endif } void CAISound::InputEmitAISound( inputdata_t &inputdata ) @@ -790,7 +838,11 @@ void CAISound::InputEmitAISound( inputdata_t &inputdata ) if( m_iszProxyEntityName != NULL_STRING ) { +#ifdef MAPBASE + CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName, this, inputdata.pActivator, inputdata.pCaller ); +#else CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName ); +#endif if( pProxy ) { @@ -802,7 +854,26 @@ void CAISound::InputEmitAISound( inputdata_t &inputdata ) } } +#ifdef MAPBASE + EHANDLE hOwner = this; + if (m_target != NULL_STRING) + { + CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_target, this, inputdata.pActivator, inputdata.pCaller ); + + if( pProxy ) + { + hOwner = pProxy; + } + else + { + DevWarning("Warning- ai_sound cannot find owner entity named '%s'. Using self.\n", STRING(m_target) ); + } + } + + g_pSoundEnt->InsertSound( m_iSoundType | m_iSoundContext, vecLocation, m_iVolume, m_flDuration, hOwner ); +#else g_pSoundEnt->InsertSound( m_iSoundType | m_iSoundContext, vecLocation, m_iVolume, m_flDuration, this ); +#endif } diff --git a/mp/src/game/server/soundent.h b/mp/src/game/server/soundent.h index 4de733f4..7ce3d401 100644 --- a/mp/src/game/server/soundent.h +++ b/mp/src/game/server/soundent.h @@ -57,6 +57,16 @@ enum SOUND_CONTEXT_ALLIES_ONLY = 0x10000000, // Only player allies can hear this sound SOUND_CONTEXT_PLAYER_VEHICLE = 0x20000000, // HACK: need this because we're not treating the SOUND_xxx values as true bit values! See switch in OnListened. +#ifdef MAPBASE + // You know, I wouldn't mind this approach of leaving types and contexts on the same int + // since it was important in the GoldSrc era with how many CSounds there can be at any given time. + // I'm just frustrated that this system was retained in Source with very specific and/or useless contexts with very little room to expand. + // If this doesn't work, replace SOUND_CONTEXT_PLAYER_VEHICLE with owner server vehicle checks. + + // Only heard by NPCs the owner likes. Needed for shared grenade code. + SOUND_CONTEXT_OWNER_ALLIES = 0x40000000, +#endif + ALL_CONTEXTS = 0xFFF00000, ALL_SCENTS = SOUND_CARCASS | SOUND_MEAT | SOUND_GARBAGE, @@ -126,6 +136,12 @@ public: int SoundChannel( void ) const; bool ValidateOwner() const; +#ifdef MAPBASE_VSCRIPT + // For VScript functions + HSCRIPT ScriptGetOwner() const { return ToHScript( m_hOwner ); } + HSCRIPT ScriptGetTarget() const { return ToHScript( m_hTarget ); } +#endif + EHANDLE m_hOwner; // sound's owner EHANDLE m_hTarget; // Sounds's target - an odd concept. For a gunfire sound, the target is the entity being fired at int m_iVolume; // how loud the sound is diff --git a/mp/src/game/server/soundscape_system.cpp b/mp/src/game/server/soundscape_system.cpp index 29b29402..fc81579f 100644 --- a/mp/src/game/server/soundscape_system.cpp +++ b/mp/src/game/server/soundscape_system.cpp @@ -136,6 +136,16 @@ bool CSoundscapeSystem::Init() mapSoundscapeFilename = UTIL_VarArgs( "scripts/soundscapes_%s.txt", mapname ); } +#ifdef MAPBASE + if (filesystem->FileExists(UTIL_VarArgs("maps/%s_soundscapes.txt", mapname))) + { + // A Mapbase-specific file exists. Load that instead. + // Any additional soundscape files, like the original scripts/soundscapes version, + // could be loaded through #include and/or #base. + mapSoundscapeFilename = UTIL_VarArgs("maps/%s_soundscapes.txt", mapname); + } +#endif + KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE ); if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) ) { diff --git a/mp/src/game/server/subs.cpp b/mp/src/game/server/subs.cpp index b2bf003f..0a37e4c1 100644 --- a/mp/src/game/server/subs.cpp +++ b/mp/src/game/server/subs.cpp @@ -38,6 +38,11 @@ void CNullEntity::Spawn( void ) } LINK_ENTITY_TO_CLASS(info_null,CNullEntity); +#ifdef MAPBASE +// Eh, good enough. +LINK_ENTITY_TO_CLASS(func_null,CNullEntity); +#endif + class CBaseDMStart : public CPointEntity { public: diff --git a/mp/src/game/server/triggers.cpp b/mp/src/game/server/triggers.cpp index 1e317567..06246448 100644 --- a/mp/src/game/server/triggers.cpp +++ b/mp/src/game/server/triggers.cpp @@ -39,6 +39,10 @@ #include "hl2_player.h" #endif +#ifdef MAPBASE +#include "ai_hint.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -103,6 +107,12 @@ BEGIN_DATADESC( CBaseTrigger ) DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), DEFINE_UTLVECTOR( m_hTouchingEntities, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flWait, FIELD_FLOAT, "wait" ), + DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_sMaster, FIELD_STRING, "master" ), +#endif + // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), @@ -122,6 +132,24 @@ BEGIN_DATADESC( CBaseTrigger ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT + +BEGIN_ENT_SCRIPTDESC( CBaseTrigger, CBaseEntity, "Trigger entity" ) + DEFINE_SCRIPTFUNC( Enable, "" ) + DEFINE_SCRIPTFUNC( Disable, "" ) + DEFINE_SCRIPTFUNC( TouchTest, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsTouching, "IsTouching", "Checks whether the passed entity is touching the trigger." ) + + DEFINE_SCRIPTFUNC( UsesFilter, "Returns true if this trigger uses a filter." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesTriggerFilters, "PassesTriggerFilters", "Returns whether a target entity satisfies the trigger's spawnflags, filter, etc." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetTouchedEntityOfType, "GetTouchedEntityOfType", "Gets the first touching entity which matches the specified class." ) + + DEFINE_SCRIPTFUNC( PointIsWithin, "Checks if the given vector is within the trigger's volume." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetTouchingEntities, "GetTouchingEntities", "Gets all entities touching this trigger (and satisfying its criteria). This function copies them to a table with a maximum number of elements." ) +END_SCRIPTDESC(); + +#endif // MAPBASE_VSCRIPT LINK_ENTITY_TO_CLASS( trigger, CBaseTrigger ); @@ -266,8 +294,11 @@ void CBaseTrigger::TouchTest( void ) { if ( m_hTouchingEntities.Count() !=0 ) { - +#ifdef MAPBASE + m_OnTouching.FireOutput( m_hTouchingEntities[0], this ); +#else m_OnTouching.FireOutput( this, this ); +#endif } else { @@ -362,6 +393,10 @@ bool CBaseTrigger::PassesTriggerFilters(CBaseEntity *pOther) (HasSpawnFlags(SF_TRIGGER_ALLOW_NPCS) && (pOther->GetFlags() & FL_NPC)) || (HasSpawnFlags(SF_TRIGGER_ALLOW_PUSHABLES) && FClassnameIs(pOther, "func_pushable")) || (HasSpawnFlags(SF_TRIGGER_ALLOW_PHYSICS) && pOther->GetMoveType() == MOVETYPE_VPHYSICS) +#ifdef MAPBASE + || + (HasSpawnFlags(SF_TRIGGER_ALLOW_ITEMS) && pOther->GetMoveType() == MOVETYPE_FLYGRAVITY) +#endif #if defined( HL2_EPISODIC ) || defined( TF_DLL ) || ( HasSpawnFlags(SF_TRIG_TOUCH_DEBRIS) && @@ -549,6 +584,19 @@ bool CBaseTrigger::IsTouching( CBaseEntity *pOther ) return ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() ); } +#ifdef MAPBASE_VSCRIPT +bool CBaseTrigger::ScriptIsTouching( HSCRIPT hOther ) +{ + CBaseEntity *pOther = ToEnt(hOther); + if ( !pOther ) + return false; + + EHANDLE eOther; + eOther = pOther; + return ( m_hTouchingEntities.Find( eOther ) != m_hTouchingEntities.InvalidIndex() ); +} +#endif // MAPBASE_VSCRIPT + //----------------------------------------------------------------------------- // Purpose: Return a pointer to the first entity of the specified type being touched by this trigger //----------------------------------------------------------------------------- @@ -582,6 +630,19 @@ void CBaseTrigger::InputToggle( inputdata_t &inputdata ) PhysicsTouchTriggers(); } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: Copies touching entities to a script table +//----------------------------------------------------------------------------- +void CBaseTrigger::ScriptGetTouchingEntities( HSCRIPT hTable ) +{ + for (int i = 0; i < m_hTouchingEntities.Count(); i++) + { + g_pScriptVM->ArrayAppend( hTable, ToHScript( m_hTouchingEntities[i] ) ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Removes anything that touches it. If the trigger has a targetname, @@ -649,6 +710,9 @@ BEGIN_DATADESC( CTriggerHurt ) DEFINE_KEYFIELD( m_bitsDamageInflict, FIELD_INTEGER, "damagetype" ), DEFINE_KEYFIELD( m_damageModel, FIELD_INTEGER, "damagemodel" ), DEFINE_KEYFIELD( m_bNoDmgForce, FIELD_BOOLEAN, "nodmgforce" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flHurtRate, FIELD_FLOAT, "hurtrate" ), +#endif DEFINE_FIELD( m_flLastDmgTime, FIELD_TIME ), DEFINE_FIELD( m_flDmgResetTime, FIELD_TIME ), @@ -711,7 +775,11 @@ void CTriggerHurt::RadiationThink( void ) } float dt = gpGlobals->curtime - m_flLastDmgTime; +#ifdef MAPBASE + if ( dt >= m_flHurtRate ) +#else if ( dt >= 0.5 ) +#endif { HurtAllTouchers( dt ); } @@ -787,7 +855,11 @@ void CTriggerHurt::HurtThink() } else { +#ifdef MAPBASE + SetNextThink( gpGlobals->curtime + m_flHurtRate ); +#else SetNextThink( gpGlobals->curtime + 0.5f ); +#endif } } @@ -882,6 +954,24 @@ void CTriggerHurt::Touch( CBaseEntity *pOther ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTriggerHurt::KeyValue( const char *szKeyName, const char *szValue ) +{ + // Additional OR flags + if (FStrEq( szKeyName, "damageor" ) || FStrEq( szKeyName, "damagepresets" )) + { + m_bitsDamageInflict |= atoi(szValue); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Checks if this point is in any trigger_hurt zones with positive damage //----------------------------------------------------------------------------- @@ -1024,7 +1114,11 @@ class CTriggerLook : public CTriggerOnce DECLARE_CLASS( CTriggerLook, CTriggerOnce ); public: +#ifdef MAPBASE + CUtlVector m_hLookTargets; +#else EHANDLE m_hLookTarget; +#endif float m_flFieldOfView; float m_flLookTime; // How long must I look for float m_flLookTimeTotal; // How long have I looked @@ -1032,6 +1126,10 @@ public: float m_flTimeoutDuration; // Number of seconds after start touch to fire anyway bool m_bTimeoutFired; // True if the OnTimeout output fired since the last StartTouch. EHANDLE m_hActivator; // The entity that triggered us. +#ifdef MAPBASE + bool m_bUseLOS; // Makes lookers use LOS calculations in addition to viewcone calculations + bool m_bUseLookEntityAsCaller; // Fires OnTrigger with the seen entity +#endif void Spawn( void ); void Touch( CBaseEntity *pOther ); @@ -1043,7 +1141,11 @@ public: private: +#ifdef MAPBASE + void Trigger(CBaseEntity *pActivator, bool bTimeout, CBaseEntity *pCaller = NULL); +#else void Trigger(CBaseEntity *pActivator, bool bTimeout); +#endif void TimeoutThink(); COutputEvent m_OnTimeout; @@ -1052,12 +1154,20 @@ private: LINK_ENTITY_TO_CLASS( trigger_look, CTriggerLook ); BEGIN_DATADESC( CTriggerLook ) +#ifdef MAPBASE + DEFINE_UTLVECTOR( m_hLookTargets, FIELD_EHANDLE ), +#else DEFINE_FIELD( m_hLookTarget, FIELD_EHANDLE ), +#endif DEFINE_FIELD( m_flLookTimeTotal, FIELD_FLOAT ), DEFINE_FIELD( m_flLookTimeLast, FIELD_TIME ), DEFINE_KEYFIELD( m_flTimeoutDuration, FIELD_FLOAT, "timeout" ), DEFINE_FIELD( m_bTimeoutFired, FIELD_BOOLEAN ), DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bUseLOS, FIELD_BOOLEAN, "UseLOS" ), + DEFINE_KEYFIELD( m_bUseLookEntityAsCaller, FIELD_BOOLEAN, "LookEntityCaller" ), +#endif DEFINE_OUTPUT( m_OnTimeout, "OnTimeout" ), @@ -1075,7 +1185,9 @@ END_DATADESC() //------------------------------------------------------------------------------ void CTriggerLook::Spawn( void ) { +#ifndef MAPBASE m_hLookTarget = NULL; +#endif m_flLookTimeTotal = -1; m_bTimeoutFired = false; @@ -1140,6 +1252,17 @@ void CTriggerLook::Touch(CBaseEntity *pOther) // -------------------------------- // Make sure we have a look target // -------------------------------- +#ifdef MAPBASE + if (m_hLookTargets.Count() <= 0) + { + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_target, this, m_hActivator, this ); + while (pEntity) + { + m_hLookTargets.AddToTail(pEntity); + pEntity = gEntList.FindEntityByName( pEntity, m_target, this, m_hActivator, this ); + } + } +#else if (m_hLookTarget == NULL) { m_hLookTarget = GetNextTarget(); @@ -1148,6 +1271,7 @@ void CTriggerLook::Touch(CBaseEntity *pOther) return; } } +#endif // This is designed for single player only // so we'll always have the same player @@ -1176,6 +1300,50 @@ void CTriggerLook::Touch(CBaseEntity *pOther) vLookDir = ((CBaseCombatCharacter*)pOther)->EyeDirection3D( ); } +#ifdef MAPBASE + // Check if the player is looking at any of the entities, even if they turn to look at another entity candidate. + // This is how we're doing support for multiple entities without redesigning trigger_look. + EHANDLE hLookingAtEntity = NULL; + for (int i = 0; i < m_hLookTargets.Count(); i++) + { + if (!m_hLookTargets[i]) + continue; + + Vector vTargetDir = m_hLookTargets[i]->GetAbsOrigin() - pOther->EyePosition(); + VectorNormalize(vTargetDir); + + float fDotPr = DotProduct(vLookDir,vTargetDir); + if (fDotPr > m_flFieldOfView && (!m_bUseLOS || pOther->FVisible(pOther))) + { + hLookingAtEntity = m_hLookTargets[i]; + break; + } + } + + if (hLookingAtEntity != NULL) + { + // Is it the first time I'm looking? + if (m_flLookTimeTotal == -1) + { + m_flLookTimeLast = gpGlobals->curtime; + m_flLookTimeTotal = 0; + } + else + { + m_flLookTimeTotal += gpGlobals->curtime - m_flLookTimeLast; + m_flLookTimeLast = gpGlobals->curtime; + } + + if (m_flLookTimeTotal >= m_flLookTime) + { + Trigger(pOther, false, hLookingAtEntity); + } + } + else + { + m_flLookTimeTotal = -1; + } +#else Vector vTargetDir = m_hLookTarget->GetAbsOrigin() - pOther->EyePosition(); VectorNormalize(vTargetDir); @@ -1203,6 +1371,7 @@ void CTriggerLook::Touch(CBaseEntity *pOther) { m_flLookTimeTotal = -1; } +#endif } } @@ -1210,7 +1379,11 @@ void CTriggerLook::Touch(CBaseEntity *pOther) //----------------------------------------------------------------------------- // Purpose: Called when the trigger is fired by look logic or timeout. //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CTriggerLook::Trigger(CBaseEntity *pActivator, bool bTimeout, CBaseEntity *pCaller) +#else void CTriggerLook::Trigger(CBaseEntity *pActivator, bool bTimeout) +#endif { if (bTimeout) { @@ -1223,7 +1396,11 @@ void CTriggerLook::Trigger(CBaseEntity *pActivator, bool bTimeout) else { // Fire because the player looked at the target. +#ifdef MAPBASE + m_OnTrigger.FireOutput(pActivator, m_bUseLookEntityAsCaller ? pCaller : this); +#else m_OnTrigger.FireOutput(pActivator, this); +#endif m_flLookTimeTotal = -1; // Cancel the timeout think. @@ -1760,7 +1937,11 @@ struct collidelist_t // NOTE: This routine is relatively slow. If you need to use it for per-frame work, consider that fact. // UNDONE: Expand this to the full matrix of solid types on each side and move into enginetrace +#ifdef MAPBASE // Other files may use this +bool TestEntityTriggerIntersection_Accurate( CBaseEntity *pTrigger, CBaseEntity *pEntity ) +#else static bool TestEntityTriggerIntersection_Accurate( CBaseEntity *pTrigger, CBaseEntity *pEntity ) +#endif { Assert( pTrigger->GetSolid() == SOLID_BSP ); @@ -2174,6 +2355,11 @@ public: void Touch( CBaseEntity *pOther ); void Untouch( CBaseEntity *pOther ); +#ifdef MAPBASE + void InputSetSpeed( inputdata_t &inputdata ); + void InputSetPushDir( inputdata_t &inputdata ); +#endif + Vector m_vecPushDir; DECLARE_DATADESC(); @@ -2186,6 +2372,10 @@ BEGIN_DATADESC( CTriggerPush ) DEFINE_KEYFIELD( m_vecPushDir, FIELD_VECTOR, "pushdir" ), DEFINE_KEYFIELD( m_flAlternateTicksFix, FIELD_FLOAT, "alternateticksfix" ), //DEFINE_FIELD( m_flPushSpeed, FIELD_FLOAT ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetPushDir", InputSetPushDir ), +#endif END_DATADESC() LINK_ENTITY_TO_CLASS( trigger_push, CTriggerPush ); @@ -2332,6 +2522,35 @@ void CTriggerPush::Touch( CBaseEntity *pOther ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTriggerPush::InputSetSpeed( inputdata_t &inputdata ) +{ + m_flSpeed = inputdata.value.Float(); + + // Need to update push speed/alternative ticks + Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTriggerPush::InputSetPushDir( inputdata_t &inputdata ) +{ + inputdata.value.Vector3D( m_vecPushDir ); + + // Convert pushdir from angles to a vector + Vector vecAbsDir; + QAngle angPushDir = QAngle( m_vecPushDir.x, m_vecPushDir.y, m_vecPushDir.z ); + AngleVectors( angPushDir, &vecAbsDir ); + + // Transform the vector into entity space + VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecPushDir ); +} +#endif + //----------------------------------------------------------------------------- // Teleport trigger @@ -2756,6 +2975,9 @@ private: string_t m_strNewHintGroup; float m_flRadius; bool m_bHintGroupNavLimiting; +#ifdef MAPBASE + bool m_bChangeHints; +#endif }; LINK_ENTITY_TO_CLASS( ai_changehintgroup, CAI_ChangeHintGroup ); @@ -2766,6 +2988,9 @@ BEGIN_DATADESC( CAI_ChangeHintGroup ) DEFINE_KEYFIELD( m_strNewHintGroup, FIELD_STRING, "NewHintGroup" ), DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "Radius" ), DEFINE_KEYFIELD( m_bHintGroupNavLimiting, FIELD_BOOLEAN, "hintlimiting" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bChangeHints, FIELD_BOOLEAN, "changehints" ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), @@ -2822,6 +3047,35 @@ void CAI_ChangeHintGroup::InputActivate( inputdata_t &inputdata ) { pTarget->SetHintGroup( m_strNewHintGroup, m_bHintGroupNavLimiting ); } + +#ifdef MAPBASE + if (m_bChangeHints) + { + AIHintIter_t iter; + CAI_Hint *pHint = CAI_HintManager::GetFirstHint( &iter ); + while ( pHint != NULL ) + { + if ((GetAbsOrigin() - pHint->GetAbsOrigin()).Length() < m_flRadius) + { + bool bValid = false; + switch (m_iSearchType) + { + case 0: { bValid = pHint->NameMatches(STRING(m_strSearchName)); } break; + case 1: { bValid = pHint->ClassMatches(STRING(m_strSearchName)); } break; + + // These should be pooled, so if they're the same hintgroup, they should point to the same string... + case 2: { bValid = pHint->GetGroup() == m_strSearchName; } break; + } + + if (bValid) + pHint->SetGroup(m_strNewHintGroup); + } + + // Move to the next + pHint = CAI_HintManager::GetNextHint( &iter ); + } + } +#endif } @@ -2834,72 +3088,11 @@ void CAI_ChangeHintGroup::InputActivate( inputdata_t &inputdata ) #define SF_CAMERA_PLAYER_SNAP_TO 16 #define SF_CAMERA_PLAYER_NOT_SOLID 32 #define SF_CAMERA_PLAYER_INTERRUPT 64 - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CTriggerCamera : public CBaseEntity -{ -public: - DECLARE_CLASS( CTriggerCamera, CBaseEntity ); - - void Spawn( void ); - bool KeyValue( const char *szKeyName, const char *szValue ); - void Enable( void ); - void Disable( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void FollowTarget( void ); - void Move(void); - - // Always transmit to clients so they know where to move the view to - virtual int UpdateTransmitState(); - - DECLARE_DATADESC(); - - // Input handlers - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - -private: - EHANDLE m_hPlayer; - EHANDLE m_hTarget; - - // used for moving the camera along a path (rail rides) - CBaseEntity *m_pPath; - string_t m_sPath; - float m_flWait; - float m_flReturnTime; - float m_flStopTime; - float m_moveDistance; - float m_targetSpeed; - float m_initialSpeed; - float m_acceleration; - float m_deceleration; - int m_state; - Vector m_vecMoveDir; - - - string_t m_iszTargetAttachment; - int m_iAttachmentIndex; - bool m_bSnapToGoal; - -#if HL2_EPISODIC - bool m_bInterpolatePosition; - - // these are interpolation vars used for interpolating the camera over time - Vector m_vStartPos, m_vEndPos; - float m_flInterpStartTime; - - const static float kflPosInterpTime; // seconds +#ifdef MAPBASE +#define SF_CAMERA_PLAYER_SETFOV 128 +#define SF_CAMERA_PLAYER_NEW_BEHAVIOR 256 // In case anyone or anything relied on the broken features #endif - int m_nPlayerButtons; - int m_nOldTakeDamage; - -private: - COutputEvent m_OnEndFollow; -}; #if HL2_EPISODIC const float CTriggerCamera::kflPosInterpTime = 2.0f; @@ -2935,16 +3128,63 @@ BEGIN_DATADESC( CTriggerCamera ) DEFINE_FIELD( m_nPlayerButtons, FIELD_INTEGER ), DEFINE_FIELD( m_nOldTakeDamage, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_fov, FIELD_FLOAT, "fov" ), + DEFINE_KEYFIELD( m_fovSpeed, FIELD_FLOAT, "fov_rate" ), + + DEFINE_KEYFIELD( m_bDontSetPlayerView, FIELD_BOOLEAN, "DontSetPlayerView" ), +#endif + // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOV", InputSetFOV ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOVRate", InputSetFOVRate ), +#endif // Function Pointers +#ifdef MAPBASE + DEFINE_FUNCTION( MoveThink ), +#endif DEFINE_FUNCTION( FollowTarget ), DEFINE_OUTPUT( m_OnEndFollow, "OnEndFollow" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnStartFollow, "OnStartFollow" ), +#endif END_DATADESC() +// VScript: publish class and select members to script language +BEGIN_ENT_SCRIPTDESC( CTriggerCamera, CBaseEntity, "Server-side camera entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFov, "GetFov", "get camera's current fov setting as integer" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFov, "SetFov", "set camera's current fov in integer degrees and fov change rate as float" ) +END_SCRIPTDESC(); + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTriggerCamera::CTriggerCamera() +{ + m_fov = 90; + m_fovSpeed = 1; +} + +//------------------------------------------------------------------------------ +// Cleanup +//------------------------------------------------------------------------------ +void CTriggerCamera::UpdateOnRemove() +{ + if (m_state == USE_ON && HasSpawnFlags(SF_CAMERA_PLAYER_NEW_BEHAVIOR)) + { + Disable(); + } + + BaseClass::UpdateOnRemove(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -3029,6 +3269,29 @@ void CTriggerCamera::InputDisable( inputdata_t &inputdata ) Disable(); } +#ifdef MAPBASE +//------------------------------------------------------------------------------ +// Purpose: Input handler to set FOV. +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetFOV( inputdata_t &inputdata ) +{ + m_fov = inputdata.value.Float(); + + if ( m_state == USE_ON && m_hPlayer ) + { + ((CBasePlayer*)m_hPlayer.Get())->SetFOV( this, m_fov, m_fovSpeed ); + } +} + +//------------------------------------------------------------------------------ +// Purpose: Input handler to set FOV rate. +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetFOVRate( inputdata_t &inputdata ) +{ + m_fovSpeed = inputdata.value.Float(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -3105,6 +3368,20 @@ void CTriggerCamera::Enable( void ) m_bSnapToGoal = true; } +#ifdef MAPBASE + if ( HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) ) + { + if ( pPlayer ) + { + if ( pPlayer->GetFOVOwner() && (/*FClassnameIs( pPlayer->GetFOVOwner(), "point_viewcontrol_multiplayer" ) ||*/ FClassnameIs( pPlayer->GetFOVOwner(), "point_viewcontrol" )) ) + { + pPlayer->ClearZoomOwner(); + } + pPlayer->SetFOV( this, m_fov, m_fovSpeed ); + } + } +#endif + if ( HasSpawnFlags(SF_CAMERA_PLAYER_TARGET ) ) { m_hTarget = m_hPlayer; @@ -3187,6 +3464,9 @@ void CTriggerCamera::Enable( void ) } +#ifdef MAPBASE + if (!m_bDontSetPlayerView) +#endif pPlayer->SetViewEntity( this ); // Hide the player's viewmodel @@ -3202,6 +3482,18 @@ void CTriggerCamera::Enable( void ) SetThink( &CTriggerCamera::FollowTarget ); SetNextThink( gpGlobals->curtime ); } +#ifdef MAPBASE + else if (m_pPath && HasSpawnFlags(SF_CAMERA_PLAYER_NEW_BEHAVIOR)) + { + // Move if we have a path + SetThink( &CTriggerCamera::MoveThink ); + SetNextThink( gpGlobals->curtime ); + } +#endif + +#ifdef MAPBASE + m_OnStartFollow.FireOutput( pPlayer, this ); +#endif m_moveDistance = 0; Move(); @@ -3214,6 +3506,34 @@ void CTriggerCamera::Enable( void ) //----------------------------------------------------------------------------- void CTriggerCamera::Disable( void ) { +#ifdef MAPBASE + if ( m_hPlayer ) + { + CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + + if ( pBasePlayer->IsAlive() ) + { + if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) ) + { + pBasePlayer->RemoveSolidFlags( FSOLID_NOT_SOLID ); + } + + if (!m_bDontSetPlayerView) + pBasePlayer->SetViewEntity( NULL ); + + pBasePlayer->EnableControl(TRUE); + pBasePlayer->m_Local.m_bDrawViewmodel = true; + } + + if ( HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) ) + { + pBasePlayer->SetFOV( this, 0, m_fovSpeed ); + } + + //return the player to previous takedamage state + m_hPlayer->m_takedamage = m_nOldTakeDamage; + } +#else if ( m_hPlayer && m_hPlayer->IsAlive() ) { if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) ) @@ -3232,6 +3552,7 @@ void CTriggerCamera::Disable( void ) //return the player to previous takedamage state m_hPlayer->m_takedamage = m_nOldTakeDamage; } +#endif m_state = USE_OFF; m_flReturnTime = gpGlobals->curtime; @@ -3354,6 +3675,75 @@ void CTriggerCamera::FollowTarget( ) Move(); } +void CTriggerCamera::StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration ) +{ + // called from SceneEntity in response to a CChoreoEvent::CAMERA sent from a VCD. + // talk to vscript, start a camera move + + HSCRIPT hStartCameraShot = NULL; + + // switch to this camera + // Enable(); + + // get script module associated with this ent, lookup function in module + if( m_iszVScripts != NULL_STRING ) + { + hStartCameraShot = m_ScriptScope.LookupFunction( "ScriptStartCameraShot" ); + } + + // call the script function to begin the camera move + if ( hStartCameraShot ) + { + g_pScriptVM->Call( hStartCameraShot, m_ScriptScope, true, NULL, pszShotType, ToHScript(pSceneEntity), ToHScript(pActor1), ToHScript(pActor2), duration ); + g_pScriptVM->ReleaseFunction( hStartCameraShot ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: vscript callback to get the player's fov +//----------------------------------------------------------------------------- +int CTriggerCamera::ScriptGetFov(void) +{ + if (m_hPlayer) + { + CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + int iFOV = pBasePlayer->GetFOV(); + return iFOV; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: vscript callback to slam the player's fov +//----------------------------------------------------------------------------- +void CTriggerCamera::ScriptSetFov(int iFOV, float fovSpeed) +{ +#ifdef MAPBASE + m_fov = iFOV; + m_fovSpeed = fovSpeed; + + if ( m_state == USE_ON && m_hPlayer ) + { +#else + if ( m_hPlayer ) + { + m_fov = iFOV; + m_fovSpeed = fovSpeed; +#endif + + CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + pBasePlayer->SetFOV(this, iFOV, fovSpeed); + } +} + +#ifdef MAPBASE +void CTriggerCamera::MoveThink() +{ + Move(); + SetNextThink( gpGlobals->curtime ); +} +#endif + void CTriggerCamera::Move() { if ( HasSpawnFlags( SF_CAMERA_PLAYER_INTERRUPT ) ) @@ -4217,6 +4607,10 @@ int CTriggerImpact::DrawDebugTextOverlays(void) const int SF_TRIGGER_MOVE_AUTODISABLE = 0x80; // disable auto movement const int SF_TRIGGER_AUTO_DUCK = 0x800; // Duck automatically +#ifdef MAPBASE +const int SF_TRIGGER_AUTO_WALK = 0x1000; +const int SF_TRIGGER_DISABLE_JUMP = 0x2000; +#endif class CTriggerPlayerMovement : public CBaseTrigger { @@ -4274,6 +4668,18 @@ void CTriggerPlayerMovement::StartTouch( CBaseEntity *pOther ) pPlayer->ForceButtons( IN_DUCK ); } +#ifdef MAPBASE + if ( HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) ) + { + pPlayer->ForceButtons( IN_WALK ); + } + + if ( HasSpawnFlags( SF_TRIGGER_DISABLE_JUMP ) ) + { + pPlayer->DisableButtons( IN_JUMP ); + } +#endif + // UNDONE: Currently this is the only operation this trigger can do if ( HasSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE) ) { @@ -4296,6 +4702,18 @@ void CTriggerPlayerMovement::EndTouch( CBaseEntity *pOther ) pPlayer->UnforceButtons( IN_DUCK ); } +#ifdef MAPBASE + if ( HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) ) + { + pPlayer->UnforceButtons( IN_WALK ); + } + + if ( HasSpawnFlags( SF_TRIGGER_DISABLE_JUMP ) ) + { + pPlayer->EnableButtons( IN_JUMP ); + } +#endif + if ( HasSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE) ) { pPlayer->m_Local.m_bAllowAutoMovement = true; @@ -4452,9 +4870,89 @@ void CBaseVPhysicsTrigger::EndTouch( CBaseEntity *pOther ) //----------------------------------------------------------------------------- bool CBaseVPhysicsTrigger::PassesTriggerFilters( CBaseEntity *pOther ) { +#ifdef MAPBASE + if ( !pOther->VPhysicsGetObject() ) +#else if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS && !pOther->IsPlayer() ) +#endif return false; +#ifdef MAPBASE + // Copied and pasted code from CBaseTrigger::PassesTriggerFilters(). + if ( HasSpawnFlags(SF_TRIGGER_ALLOW_ALL) || + (HasSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS) && (pOther->GetFlags() & FL_CLIENT)) || + (HasSpawnFlags(SF_TRIGGER_ALLOW_NPCS) && (pOther->GetFlags() & FL_NPC)) || + (HasSpawnFlags(SF_TRIGGER_ALLOW_PUSHABLES) && FClassnameIs(pOther, "func_pushable")) || + (HasSpawnFlags(SF_TRIGGER_ALLOW_PHYSICS) && pOther->GetMoveType() == MOVETYPE_VPHYSICS) || + (HasSpawnFlags(SF_TRIGGER_ALLOW_ITEMS) && pOther->GetMoveType() == MOVETYPE_FLYGRAVITY) +#if defined( HL2_EPISODIC ) || defined( TF_DLL ) + || + ( HasSpawnFlags(SF_TRIG_TOUCH_DEBRIS) && + (pOther->GetCollisionGroup() == COLLISION_GROUP_DEBRIS || + pOther->GetCollisionGroup() == COLLISION_GROUP_DEBRIS_TRIGGER || + pOther->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS) + ) +#endif + ) + { + if ( pOther->GetFlags() & FL_NPC ) + { + CAI_BaseNPC *pNPC = pOther->MyNPCPointer(); + + if ( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) ) + { + if ( !pNPC || !pNPC->IsPlayerAlly() ) + { + return false; + } + } + + if ( HasSpawnFlags( SF_TRIGGER_ONLY_NPCS_IN_VEHICLES ) ) + { + if ( !pNPC || !pNPC->IsInAVehicle() ) + return false; + } + } + + bool bOtherIsPlayer = pOther->IsPlayer(); + + if ( bOtherIsPlayer ) + { + CBasePlayer *pPlayer = (CBasePlayer*)pOther; + if ( !pPlayer->IsAlive() ) + return false; + + if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES) ) + { + if ( !pPlayer->IsInAVehicle() ) + return false; + + // Make sure we're also not exiting the vehicle at the moment + IServerVehicle *pVehicleServer = pPlayer->GetVehicle(); + if ( pVehicleServer == NULL ) + return false; + + if ( pVehicleServer->IsPassengerExiting() ) + return false; + } + + if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES) ) + { + if ( pPlayer->IsInAVehicle() ) + return false; + } + + if ( HasSpawnFlags( SF_TRIGGER_DISALLOW_BOTS ) ) + { + if ( pPlayer->IsFakeClient() ) + return false; + } + } + + CBaseFilter *pFilter = m_hFilter.Get(); + return (!pFilter) ? true : pFilter->PassesFilter( this, pOther ); + } +#else // First test spawn flag filters if ( HasSpawnFlags(SF_TRIGGER_ALLOW_ALL) || (HasSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS) && (pOther->GetFlags() & FL_CLIENT)) || @@ -4488,6 +4986,7 @@ bool CBaseVPhysicsTrigger::PassesTriggerFilters( CBaseEntity *pOther ) CBaseFilter *pFilter = m_hFilter.Get(); return (!pFilter) ? true : pFilter->PassesFilter( this, pOther ); } +#endif return false; } @@ -4859,6 +5358,13 @@ void CServerRagdollTrigger::Spawn( void ) { BaseClass::Spawn(); +#ifdef MAPBASE + // This didn't use PassesTriggerFilters() before, so a trigger_serverragdoll could work regardless of flags. + // Because of this, using trigger filters now will break existing trigger_serverragdolls that functioned without the right flags ticked. + // NPCs are going to be using this in almost all circumstances, so SF_TRIGGER_ALLOW_NPCS is pretty much the main flag of concern. + AddSpawnFlags(SF_TRIGGER_ALLOW_NPCS); +#endif + InitTrigger(); } @@ -4869,10 +5375,25 @@ void CServerRagdollTrigger::StartTouch(CBaseEntity *pOther) if ( pOther->IsPlayer() ) return; +#ifdef MAPBASE + // This means base class didn't accept it (trigger filters) + if (m_hTouchingEntities.Find(pOther) == m_hTouchingEntities.InvalidIndex()) + return; +#endif + CBaseCombatCharacter *pCombatChar = pOther->MyCombatCharacterPointer(); if ( pCombatChar ) { +#ifdef MAPBASE + // The mapper or some other force might've changed it themselves. + // Pretend it never touched us... + if (pCombatChar->m_bForceServerRagdoll == true) + { + BaseClass::EndTouch(pOther); + return; + } +#endif pCombatChar->m_bForceServerRagdoll = true; } } @@ -4964,6 +5485,76 @@ void CTriggerApplyImpulse::InputApplyImpulse( inputdata_t& ) } } +#ifdef MAPBASE +class CTriggerFall : public CBaseTrigger +{ + DECLARE_CLASS( CTriggerFall, CBaseTrigger ); + +public: + + virtual void StartTouch( CBaseEntity *pOther ); + virtual void EndTouch( CBaseEntity *pOther ); + virtual void Spawn( void ); + + bool m_bStayLethal; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( trigger_fall, CTriggerFall ); + +BEGIN_DATADESC( CTriggerFall ) + + DEFINE_KEYFIELD( m_bStayLethal, FIELD_BOOLEAN, "StayLethal" ), + +END_DATADESC() + +void CTriggerFall::Spawn( void ) +{ + BaseClass::Spawn(); + + InitTrigger(); +} + +void CTriggerFall::StartTouch(CBaseEntity *pOther) +{ + BaseClass::StartTouch( pOther ); + + if ( !pOther->IsPlayer() ) + return; + + static_cast(pOther)->m_bInTriggerFall = true; +} + +void CTriggerFall::EndTouch(CBaseEntity *pOther) +{ + BaseClass::EndTouch( pOther ); + + if ( !pOther->IsPlayer() || m_bStayLethal ) + return; + + static_cast(pOther)->m_bInTriggerFall = false; +} + + + +class CTriggerWorld : public CTriggerMultiple +{ + DECLARE_CLASS( CTriggerWorld, CTriggerMultiple ); + +public: + + virtual bool PassesTriggerFilters(CBaseEntity *pOther); +}; + +LINK_ENTITY_TO_CLASS( trigger_world, CTriggerWorld ); + +bool CTriggerWorld::PassesTriggerFilters( CBaseEntity *pOther ) +{ + return pOther->IsWorld(); +} +#endif + #ifdef HL1_DLL //---------------------------------------------------------------------------------- // func_friction diff --git a/mp/src/game/server/triggers.h b/mp/src/game/server/triggers.h index 6ae7312f..fe0aefe1 100644 --- a/mp/src/game/server/triggers.h +++ b/mp/src/game/server/triggers.h @@ -11,7 +11,11 @@ #pragma once #endif +#ifdef MAPBASE +#include "baseentity.h" +#else #include "basetoggle.h" +#endif #include "entityoutput.h" // @@ -33,15 +37,24 @@ enum SF_TRIG_TOUCH_DEBRIS = 0x400, // Will touch physics debris objects SF_TRIGGER_ONLY_NPCS_IN_VEHICLES = 0X800, // *if* NPCs can fire this trigger, only NPCs in vehicles do so (respects player ally flag too) SF_TRIGGER_DISALLOW_BOTS = 0x1000, // Bots are not allowed to fire this trigger +#ifdef MAPBASE + SF_TRIGGER_ALLOW_ITEMS = 0x2000, // MOVETYPE_FLYGRAVITY (Weapons, items, flares, etc.) can fire this trigger +#endif }; // DVS TODO: get rid of CBaseToggle //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +#ifdef MAPBASE +#define CBaseToggle CBaseEntity +#endif class CBaseTrigger : public CBaseToggle { DECLARE_CLASS( CBaseTrigger, CBaseToggle ); +#ifdef MAPBASE +#undef CBaseToggle +#endif public: CBaseTrigger(); @@ -71,6 +84,9 @@ public: virtual void StartTouchAll() {} virtual void EndTouchAll() {} bool IsTouching( CBaseEntity *pOther ); +#ifdef MAPBASE_VSCRIPT + bool ScriptIsTouching( HSCRIPT hOther ); +#endif CBaseEntity *GetTouchedEntityOfType( const char *sClassName ); @@ -81,6 +97,13 @@ public: bool PointIsWithin( const Vector &vecPoint ); +#ifdef MAPBASE_VSCRIPT + bool ScriptPassesTriggerFilters( HSCRIPT hOther ) { return ToEnt(hOther) ? PassesTriggerFilters( ToEnt(hOther) ) : NULL; } + HSCRIPT ScriptGetTouchedEntityOfType( const char *sClassName ) { return ToHScript( GetTouchedEntityOfType(sClassName) ); } + + void ScriptGetTouchingEntities( HSCRIPT hTable ); +#endif + bool m_bDisabled; string_t m_iFilterName; CHandle m_hFilter; @@ -98,7 +121,23 @@ protected: // Entities currently being touched by this trigger CUtlVector< EHANDLE > m_hTouchingEntities; +#ifdef MAPBASE + // We don't descend from CBaseToggle anymore. These have to be defined here now. + EHANDLE m_hActivator; + float m_flWait; + string_t m_sMaster; // If this button has a master switch, this is the targetname. + // A master switch must be of the multisource type. If all + // of the switches in the multisource have been triggered, then + // the button will be allowed to operate. Otherwise, it will be + // deactivated. + + virtual float GetDelay( void ) { return m_flWait; } +#endif + DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; //----------------------------------------------------------------------------- @@ -187,6 +226,10 @@ public: { // This field came along after levels were built so the field defaults to 20 here in the constructor. m_flDamageCap = 20.0f; +#ifdef MAPBASE + // Uh, same here. + m_flHurtRate = 0.5f; +#endif } DECLARE_CLASS( CTriggerHurt, CTriggerHurtShim ); @@ -199,6 +242,10 @@ public: bool HurtEntity( CBaseEntity *pOther, float damage ); int HurtAllTouchers( float dt ); +#ifdef MAPBASE + bool KeyValue( const char *szKeyName, const char *szValue ); +#endif + DECLARE_DATADESC(); float m_flOriginalDamage; // Damage as specified by the level designer. @@ -209,6 +256,9 @@ public: int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does int m_damageModel; bool m_bNoDmgForce; // Should damage from this trigger impart force on what it's hurting +#ifdef MAPBASE + float m_flHurtRate; +#endif enum { @@ -223,6 +273,100 @@ public: CUtlVector m_hurtEntities; }; +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTriggerCamera : public CBaseEntity +{ +public: + DECLARE_CLASS( CTriggerCamera, CBaseEntity ); + // script description + DECLARE_ENT_SCRIPTDESC(); + +#ifdef MAPBASE + CTriggerCamera(); + + void UpdateOnRemove(); +#endif + + void Spawn( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void Enable( void ); + void Disable( void ); + + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void FollowTarget( void ); + void StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration ); + int ScriptGetFov(void); + void ScriptSetFov(int iFOV, float rate); +#ifdef MAPBASE + void MoveThink( void ); +#endif + void Move(void); + + // Always transmit to clients so they know where to move the view to + virtual int UpdateTransmitState(); + + DECLARE_DATADESC(); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + +#ifdef MAPBASE + void InputSetFOV( inputdata_t &inputdata ); + void InputSetFOVRate( inputdata_t &inputdata ); +#endif + +private: + EHANDLE m_hPlayer; + EHANDLE m_hTarget; + + // used for moving the camera along a path (rail rides) + CBaseEntity *m_pPath; + string_t m_sPath; + float m_flWait; + float m_flReturnTime; + float m_flStopTime; + float m_moveDistance; + float m_targetSpeed; + float m_initialSpeed; + float m_acceleration; + float m_deceleration; + int m_state; + Vector m_vecMoveDir; + +#ifdef MAPBASE + float m_fov; + float m_fovSpeed; + + bool m_bDontSetPlayerView; +#endif + + string_t m_iszTargetAttachment; + int m_iAttachmentIndex; + bool m_bSnapToGoal; + +#if HL2_EPISODIC + bool m_bInterpolatePosition; + + // these are interpolation vars used for interpolating the camera over time + Vector m_vStartPos, m_vEndPos; + float m_flInterpStartTime; + + const static float kflPosInterpTime; // seconds +#endif + + int m_nPlayerButtons; + int m_nOldTakeDamage; + +private: + COutputEvent m_OnEndFollow; +#ifdef MAPBASE + COutputEvent m_OnStartFollow; +#endif +}; + bool IsTakingTriggerHurtDamageAtPoint( const Vector &vecPoint ); #endif // TRIGGERS_H diff --git a/mp/src/game/server/util.cpp b/mp/src/game/server/util.cpp index 4b2008df..9c153df6 100644 --- a/mp/src/game/server/util.cpp +++ b/mp/src/game/server/util.cpp @@ -36,6 +36,9 @@ #include "datacache/imdlcache.h" #include "util.h" #include "cdll_int.h" +#ifdef MAPBASE +#include "fmtstr.h" +#endif #ifdef PORTAL #include "PortalSimulation.h" @@ -54,12 +57,7 @@ void DBG_AssertFunction( bool fExpr, const char *szExpr, const char *szFile, int { if (fExpr) return; - char szOut[512]; - if (szMessage != NULL) - Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage); - else - Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine); - Warning( "%s", szOut); + Warning("ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage ? szMessage : ""); } #endif // DEBUG @@ -209,6 +207,46 @@ void CEntityFactoryDictionary::ReportEntitySizes() } } +#ifdef MAPBASE +int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 ) +{ + CEntityFactoryDictionary *pFactoryDict = (CEntityFactoryDictionary*)EntityFactoryDictionary(); + for ( int i = pFactoryDict->m_Factories.First(); i != pFactoryDict->m_Factories.InvalidIndex(); i = pFactoryDict->m_Factories.Next( i ) ) + { + const char *name = pFactoryDict->m_Factories.GetElementName( i ); + if (Q_strnicmp(name, substring, checklen)) + continue; + + CUtlString sym = name; + int idx = symbols.Find(sym); + if (idx == symbols.InvalidIndex()) + { + symbols.Insert(sym); + } + + // Too many + if (symbols.Count() >= COMMAND_COMPLETION_MAXITEMS) + break; + } + + // Now fill in the results + for (int i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder(i)) + { + const char *name = symbols[i].String(); + + char buf[512]; + Q_strncpy(buf, name, sizeof(buf)); + Q_strlower(buf); + + CUtlString command; + command = CFmtStr("%s %s", cmdname, buf); + commands.AddToTail(command); + } + + return symbols.Count(); +} +#endif + //----------------------------------------------------------------------------- // class CFlaggedEntitiesEnum @@ -300,6 +338,14 @@ int UTIL_EntitiesInSphere( const Vector ¢er, float radius, CFlaggedEntitiesE return pEnum->GetCount(); } +#ifdef MAPBASE +int UTIL_EntitiesAtPoint( const Vector &point, CFlaggedEntitiesEnum *pEnum ) +{ + partition->EnumerateElementsAtPoint( PARTITION_ENGINE_NON_STATIC_EDICTS, point, false, pEnum ); + return pEnum->GetCount(); +} +#endif + CEntitySphereQuery::CEntitySphereQuery( const Vector ¢er, float radius, int flagMask ) { m_listIndex = 0; @@ -1049,7 +1095,7 @@ void UTIL_ScreenFade( CBaseEntity *pEntity, const color32 &color, float fadeTime } -void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage ) +void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage, const char *pszFont, bool bAutobreak ) { CRecipientFilter filter; @@ -1082,12 +1128,19 @@ void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, c WRITE_FLOAT( textparms.holdTime ); WRITE_FLOAT( textparms.fxTime ); WRITE_STRING( pMessage ); +#ifdef MAPBASE + WRITE_STRING( pszFont ); + if (bAutobreak) + { + WRITE_BYTE ( Q_strlen( pMessage ) ); + } +#endif MessageEnd(); } -void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ) +void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage, const char *pszFont, bool bAutobreak ) { - UTIL_HudMessage( NULL, textparms, pMessage ); + UTIL_HudMessage( NULL, textparms, pMessage, pszFont, bAutobreak ); } void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage ) @@ -1313,9 +1366,19 @@ void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName ) int i = modelinfo->GetModelIndex( pModelName ); if ( i == -1 ) { +#if defined(MAPBASE) && !defined(_DEBUG) + // Throwing a program-terminating error might be a little too much since we could just precache it here. + // If we're not in debug mode, just let it off with a nice warning. + if (int newi = CBaseEntity::PrecacheModel(pModelName)) + { + i = newi; + Warning("%s was not precached\n", pModelName); + } +#else Error("%i/%s - %s: UTIL_SetModel: not precached: %s\n", pEntity->entindex(), STRING( pEntity->GetEntityName() ), pEntity->GetClassname(), pModelName); +#endif } CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); @@ -1807,6 +1870,40 @@ void UTIL_PrecacheOther( const char *szClassname, const char *modelName ) UTIL_RemoveImmediate( pEntity ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Tests whether this entity exists in the dictionary and if it does, precaches it. (as opposed to complaining when it's missing) +// Input : *szClassname - +// *modelName - +//----------------------------------------------------------------------------- +bool UTIL_TestPrecacheOther( const char *szClassname, const char *modelName ) +{ +#if defined( PRECACHE_OTHER_ONCE ) + // already done this one?, if not, mark as done + if ( !g_PrecacheOtherList.AddOrMarkPrecached( szClassname ) ) + return true; +#endif + + // If we can't create it, it probably does not exist + CBaseEntity *pEntity = CreateEntityByName( szClassname ); + if (!pEntity) + return false; + + // If we have a specified model, set it before calling precache + if ( modelName && modelName[0] ) + { + pEntity->SetModelName( AllocPooledString( modelName ) ); + } + + if (pEntity) + pEntity->Precache( ); + + UTIL_RemoveImmediate( pEntity ); + + return true; +} +#endif + //========================================================= // UTIL_LogPrintf - Prints a logged message to console. // Preceded by LOG: ( timestamp ) < message > @@ -1887,7 +1984,7 @@ extern "C" void Sys_Error( char *error, ... ) // *mapData - pointer a block of entity map data // Output : -1 if the entity was not successfully created; 0 on success //----------------------------------------------------------------------------- -int DispatchSpawn( CBaseEntity *pEntity ) +int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts ) { if ( pEntity ) { @@ -1902,6 +1999,12 @@ int DispatchSpawn( CBaseEntity *pEntity ) //pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) ); //pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) ); + if (bRunVScripts) + { + pEntity->RunVScripts(); + pEntity->RunPrecacheScripts(); + } + #if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG) const char *pszClassname = NULL; int iClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.Find( pEntity->GetClassname() ); @@ -1968,6 +2071,11 @@ int DispatchSpawn( CBaseEntity *pEntity ) } gEntList.NotifySpawn( pEntity ); + + if( bRunVScripts ) + { + pEntity->RunOnPostSpawnScripts(); + } } return 0; @@ -2066,11 +2174,14 @@ void UTIL_ValidateSoundName( string_t &name, const char *defaultStr ) // sep - Character to use as separator. UNDONE: allow multiple separator chars // Output : Returns a pointer to the next token to be parsed. //----------------------------------------------------------------------------- -const char *nexttoken(char *token, const char *str, char sep) +const char *nexttoken(char *token, const char *str, char sep, size_t tokenLen) { if ((str == NULL) || (*str == '\0')) { - *token = '\0'; + if(tokenLen) + { + *token = '\0'; + } return(NULL); } @@ -2078,11 +2189,25 @@ const char *nexttoken(char *token, const char *str, char sep) // Copy everything up to the first separator into the return buffer. // Do not include separators in the return buffer. // + while ((*str != sep) && (*str != '\0') && (tokenLen > 1)) + { + *token++ = *str++; + tokenLen--; + } + + // + // If token is to big for return buffer, skip rest of token. + // while ((*str != sep) && (*str != '\0')) { - *token++ = *str++; + str++; } - *token = '\0'; + + if(tokenLen) + { + *token = '\0'; + tokenLen--; + } // // Advance the pointer unless we hit the end of the input string. @@ -2461,6 +2586,10 @@ void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *ve if ( pAnimating != NULL ) { vecPredictedVel = pAnimating->GetGroundSpeedVelocity(); +#ifdef MAPBASE + if (vecPredictedVel.IsZero()) + vecPredictedVel = pAnimating->GetSmoothedVelocity(); +#endif } else { @@ -2474,6 +2603,80 @@ void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *ve (*vecPredictedPosition) = pTarget->GetAbsOrigin() + ( vecPredictedVel * flTimeDelta ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Same as above, except you don't have to use the absolute origin and can use your own position to predict from. +//----------------------------------------------------------------------------- +void UTIL_PredictedPosition( CBaseEntity *pTarget, const Vector &vecActualPosition, float flTimeDelta, Vector *vecPredictedPosition ) +{ + if ( ( pTarget == NULL ) || ( vecPredictedPosition == NULL ) ) + return; + + Vector vecPredictedVel; + CBasePlayer *pPlayer = ToBasePlayer( pTarget ); + if ( pPlayer != NULL ) + { + if ( pPlayer->IsInAVehicle() ) + vecPredictedVel = pPlayer->GetVehicleEntity()->GetSmoothedVelocity(); + else + vecPredictedVel = pPlayer->GetSmoothedVelocity(); + } + else + { + CBaseCombatCharacter *pCCTarget = pTarget->MyCombatCharacterPointer(); + if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) + vecPredictedVel = pCCTarget->GetVehicleEntity()->GetSmoothedVelocity(); + else + { + CBaseAnimating *pAnimating = dynamic_cast(pTarget); + if ( pAnimating != NULL ) + { + vecPredictedVel = pAnimating->GetGroundSpeedVelocity(); + if (vecPredictedVel.IsZero()) + vecPredictedVel = pAnimating->GetSmoothedVelocity(); + } + else + vecPredictedVel = pTarget->GetSmoothedVelocity(); + } + } + + // Get the result + (*vecPredictedPosition) = vecActualPosition + ( vecPredictedVel * flTimeDelta ); +} + +//----------------------------------------------------------------------------- +// Purpose: Predicts angles through angular velocity instead of predicting origin through regular velocity. +//----------------------------------------------------------------------------- +void UTIL_PredictedAngles( CBaseEntity *pTarget, const QAngle &angActualAngles, float flTimeDelta, QAngle *angPredictedAngles ) +{ + if ( ( pTarget == NULL ) || ( angPredictedAngles == NULL ) ) + return; + + QAngle angPredictedVel; + CBasePlayer *pPlayer = ToBasePlayer( pTarget ); + if ( pPlayer != NULL ) + { + if ( pPlayer->IsInAVehicle() ) + angPredictedVel = pPlayer->GetVehicleEntity()->GetLocalAngularVelocity(); + else + angPredictedVel = pPlayer->GetLocalAngularVelocity(); + } + else + { + CBaseCombatCharacter *pCCTarget = pTarget->MyCombatCharacterPointer(); + if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) + angPredictedVel = pCCTarget->GetVehicleEntity()->GetLocalAngularVelocity(); + else + { + angPredictedVel = pTarget->GetLocalAngularVelocity(); + } + } + + // Get the result + (*angPredictedAngles) = angActualAngles + ( angPredictedVel * flTimeDelta ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Points the destination entity at the target entity // Input : *pDest - entity to be pointed at the target diff --git a/mp/src/game/server/util.h b/mp/src/game/server/util.h index c06cbfe9..f6df5189 100644 --- a/mp/src/game/server/util.h +++ b/mp/src/game/server/util.h @@ -187,7 +187,13 @@ extern CGlobalVars *gpGlobals; // Misc useful inline bool FStrEq(const char *sz1, const char *sz2) { +#ifdef MAPBASE + // V_stricmp() already checks if the pointers are equal, so having a pointer comparison here is pointless. + // I'm not sure if this was already automatically phased out by the compiler, but if it wasn't, then this is a very good change. + return ( V_stricmp(sz1, sz2) == 0 ); +#else return ( sz1 == sz2 || V_stricmp(sz1, sz2) == 0 ); +#endif } #if 0 @@ -200,7 +206,7 @@ inline bool FStrEq( string_t str1, string_t str2 ) } #endif -const char *nexttoken(char *token, const char *str, char sep); +const char *nexttoken(char *token, const char *str, char sep, size_t tokenLen); // Misc. Prototypes void UTIL_SetSize (CBaseEntity *pEnt, const Vector &vecMin, const Vector &vecMax); @@ -232,6 +238,24 @@ CBasePlayer* UTIL_GetLocalPlayer( void ); // get the local player on a listen server CBasePlayer *UTIL_GetListenServerHost( void ); +//----------------------------------------------------------------------------- +// Purpose: Convenience function so we don't have to make this check all over +//----------------------------------------------------------------------------- +static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) +{ + if ( gpGlobals->maxClients > 1 ) + { + if ( engine->IsDedicatedServer() ) + { + return NULL; + } + + return UTIL_GetListenServerHost(); + } + + return UTIL_GetLocalPlayer(); +} + CBasePlayer* UTIL_PlayerByUserId( int userID ); CBasePlayer* UTIL_PlayerByName( const char *name ); // not case sensitive @@ -281,6 +305,9 @@ private: int UTIL_EntitiesInBox( const Vector &mins, const Vector &maxs, CFlaggedEntitiesEnum *pEnum ); int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum ); int UTIL_EntitiesInSphere( const Vector ¢er, float radius, CFlaggedEntitiesEnum *pEnum ); +#ifdef MAPBASE +int UTIL_EntitiesAtPoint( const Vector &point, CFlaggedEntitiesEnum *pEnum ); +#endif inline int UTIL_EntitiesInBox( CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ) { @@ -300,6 +327,14 @@ inline int UTIL_EntitiesInSphere( CBaseEntity **pList, int listMax, const Vector return UTIL_EntitiesInSphere( center, radius, &sphereEnum ); } +#ifdef MAPBASE +inline int UTIL_EntitiesAtPoint( CBaseEntity **pList, int listMax, const Vector &point, int flagMask ) +{ + CFlaggedEntitiesEnum pointEnum( pList, listMax, flagMask ); + return UTIL_EntitiesAtPoint( point, &pointEnum ); +} +#endif + // marks the entity for deletion so it will get removed next frame void UTIL_Remove( IServerNetworkable *oldObj ); void UTIL_Remove( CBaseEntity *oldObj ); @@ -362,6 +397,10 @@ void UTIL_AxisStringToPointPoint( Vector &start, Vector &end, const char *pStri void UTIL_AxisStringToUnitDir( Vector &dir, const char *pString ); void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ); void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *vecPredictedPosition ); +#ifdef MAPBASE +void UTIL_PredictedPosition( CBaseEntity *pTarget, const Vector &vecActualPosition, float flTimeDelta, Vector *vecPredictedPosition ); +void UTIL_PredictedAngles( CBaseEntity *pTarget, const QAngle &angActualAngles, float flTimeDelta, QAngle *angPredictedAngles ); +#endif void UTIL_Beam( Vector &Start, Vector &End, int nModelIndex, int nHaloIndex, unsigned char FrameStart, unsigned char FrameRate, float Life, unsigned char Width, unsigned char EndWidth, unsigned char FadeLength, unsigned char Noise, unsigned char Red, unsigned char Green, unsigned char Blue, unsigned char Brightness, unsigned char Speed); @@ -393,6 +432,11 @@ void UTIL_BubbleTrail( const Vector& from, const Vector& to, int count ); // allows precacheing of other entities void UTIL_PrecacheOther( const char *szClassname, const char *modelName = NULL ); +#ifdef MAPBASE +// Tests whether this entity exists in the dictionary and if it does, precaches it. (as opposed to complaining when it's missing) +bool UTIL_TestPrecacheOther( const char *szClassname, const char *modelName = NULL ); +#endif + // prints a message to each client void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) @@ -469,8 +513,8 @@ void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName ); // prints as transparent 'title' to the HUD -void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); -void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage ); +void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage, const char *pszFont = NULL, bool bAutobreak = false ); +void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage, const char *pszFont = NULL, bool bAutobreak = false ); // brings up hud keyboard hints display void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage ); @@ -509,12 +553,17 @@ float UTIL_ScaleForGravity( float desiredGravity ); #define SF_BRUSH_ROTATE_BACKWARDS 2 #define SF_BRUSH_ROTATE_Z_AXIS 4 #define SF_BRUSH_ROTATE_X_AXIS 8 -#define SF_BRUSH_ROTATE_CLIENTSIDE 16 +// brought over from bmodels.cpp +#define SF_BRUSH_ACCDCC 16 // brush should accelerate and decelerate when toggled +#define SF_BRUSH_HURT 32 // rotating brush that inflicts pain based on rotation speed +#define SF_ROTATING_NOT_SOLID 64 // some special rotating objects are not solid. #define SF_BRUSH_ROTATE_SMALLRADIUS 128 #define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 #define SF_BRUSH_ROTATE_LARGERADIUS 512 +// changed bit to not conflict with much older flag SF_BRUSH_ACCDCC +#define SF_BRUSH_ROTATE_CLIENTSIDE 1024 #define PUSH_BLOCK_ONLY_X 1 #define PUSH_BLOCK_ONLY_Y 2 diff --git a/mp/src/game/server/variant_t.cpp b/mp/src/game/server/variant_t.cpp index 82edcfd4..3b45275b 100644 --- a/mp/src/game/server/variant_t.cpp +++ b/mp/src/game/server/variant_t.cpp @@ -6,6 +6,10 @@ #include "cbase.h" #include "variant_t.h" +#ifdef MAPBASE +#include "mapbase/variant_tools.h" +#include "mapbase/matchers.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -16,4 +20,265 @@ void variant_t::SetEntity( CBaseEntity *val ) fieldType = FIELD_EHANDLE; } +#ifdef MAPBASE +const char *variant_t::GetDebug() +{ + /* + case FIELD_BOOLEAN: *((bool *)data) = bVal != 0; break; + case FIELD_CHARACTER: *((char *)data) = iVal; break; + case FIELD_SHORT: *((short *)data) = iVal; break; + case FIELD_INTEGER: *((int *)data) = iVal; break; + case FIELD_STRING: *((string_t *)data) = iszVal; break; + case FIELD_FLOAT: *((float *)data) = flVal; break; + case FIELD_COLOR32: *((color32 *)data) = rgbaVal; break; + + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + { + ((float *)data)[0] = vecVal[0]; + ((float *)data)[1] = vecVal[1]; + ((float *)data)[2] = vecVal[2]; + break; + } + + case FIELD_EHANDLE: *((EHANDLE *)data) = eVal; break; + case FIELD_CLASSPTR: *((CBaseEntity **)data) = eVal; break; + */ + + const char *fieldtype = "unknown"; + switch (FieldType()) + { + case FIELD_VOID: fieldtype = "Void"; break; + case FIELD_FLOAT: fieldtype = "Float"; break; + case FIELD_STRING: fieldtype = "String"; break; + case FIELD_INTEGER: fieldtype = "Integer"; break; + case FIELD_BOOLEAN: fieldtype = "Boolean"; break; + case FIELD_EHANDLE: fieldtype = "Entity"; break; + case FIELD_CLASSPTR: fieldtype = "EntityPtr"; break; + case FIELD_POSITION_VECTOR: + case FIELD_VECTOR: fieldtype = "Vector"; break; + case FIELD_CHARACTER: fieldtype = "Character"; break; + case FIELD_SHORT: fieldtype = "Short"; break; + case FIELD_COLOR32: fieldtype = "Color32"; break; + default: fieldtype = UTIL_VarArgs("unknown: %i", FieldType()); + } + return UTIL_VarArgs("%s (%s)", String(), fieldtype); +} + +#ifdef MAPBASE_VSCRIPT +void variant_t::SetScriptVariant( ScriptVariant_t &var ) +{ + switch (FieldType()) + { + case FIELD_VOID: var = NULL; break; + case FIELD_INTEGER: var = iVal; break; + case FIELD_FLOAT: var = flVal; break; + case FIELD_STRING: var = STRING(iszVal); break; + case FIELD_POSITION_VECTOR: + case FIELD_VECTOR: var = reinterpret_cast(&flVal); break; // HACKHACK + case FIELD_BOOLEAN: var = bVal; break; + case FIELD_EHANDLE: var = ToHScript( eVal ); break; + case FIELD_CLASSPTR: var = ToHScript( eVal ); break; + case FIELD_SHORT: var = (short)iVal; break; + case FIELD_CHARACTER: var = (char)iVal; break; + case FIELD_COLOR32: + { + Color *clr = new Color( rgbaVal.r, rgbaVal.g, rgbaVal.b, rgbaVal.a ); + var = g_pScriptVM->RegisterInstance( clr, true ); + break; + } + default: var = ToString(); break; + } +} +#endif + +// cmp1 = val1 float +// cmp2 = val2 float +#define VariantToFloat(val1, val2, lenallowed) \ + float cmp1 = val1.Float() ? val1.Float() : val1.Int(); \ + float cmp2 = val2.Float() ? val2.Float() : val2.Int(); \ + if (lenallowed && val2.FieldType() == FIELD_STRING) \ + cmp2 = strlen(val2.String()); + +// Integer parsing has been deactivated for consistency's sake. They now become floats only. +#define INTEGER_PARSING_DEACTIVATED 1 + +// "intchar" is the result of me not knowing where to find a version of isdigit that applies to negative numbers and floats. +#define intchar(c) (c >= '-' && c <= '9') + +// Attempts to determine the field type from whatever is in the string and creates a variant_t with the converted value and resulting field type. +// Right now, Int/Float, String, and Vector are the only fields likely to be used by entities in variant_t parsing, so they're the only ones supported. +// Expand to other fields when necessary. +variant_t Variant_Parse(const char *szValue) +{ +#ifdef INTEGER_PARSING_DEACTIVATED + bool isint = true; + bool isvector = false; + for (size_t i = 0; i < strlen(szValue); i++) + { + if (!intchar(szValue[i])) + { + isint = false; + + if (szValue[i] == ' ') + isvector = true; + else + isvector = false; + } + } + + variant_t var; + + if (isint) + var.SetFloat(atof(szValue)); + else if (isvector) + { + var.SetString(MAKE_STRING(szValue)); + var.Convert(FIELD_VECTOR); + } + else + var.SetString(MAKE_STRING(szValue)); +#else + bool isint = true; + bool isfloat = false; + for (size_t i = 0; i < strlen(szValue); i++) + { + if (szValue[i] == '.') + isfloat = true; + else if (!intchar(szValue[i])) + isint = false; + } + + variant_t var = variant_t(); + + if (isint) + { + if (isfloat) + var.SetFloat(atof(szValue)); + else + var.SetInt(atoi(szValue)); + } + else + var.SetString(MAKE_STRING(szValue)); +#endif + + return var; +} + +// Passes strings to Variant_Parse, uses the other input data for finding procedural entities. +variant_t Variant_ParseInput(inputdata_t inputdata) +{ + if (inputdata.value.FieldType() == FIELD_STRING) + { + if (inputdata.value.String()[0] == '!') + { + variant_t var = variant_t(); + var.SetEntity(gEntList.FindEntityProcedural(inputdata.value.String(), inputdata.pCaller, inputdata.pActivator, inputdata.pCaller)); + if (var.Entity()) + return var; + } + } + + return Variant_Parse(inputdata.value.String()); +} + +// Passes string variants to Variant_Parse +variant_t Variant_ParseString(variant_t value) +{ + if (value.FieldType() != FIELD_STRING) + return value; + + return Variant_Parse(value.String()); +} + +bool Variant_Equal(variant_t val1, variant_t val2, bool bLenAllowed) +{ + //if (!val2.Convert(val1.FieldType())) + // return false; + + // Add more fields if they become necessary + switch (val1.FieldType()) + { + case FIELD_INTEGER: + case FIELD_FLOAT: + { + VariantToFloat(val1, val2, bLenAllowed); + return cmp1 == cmp2; + } + case FIELD_BOOLEAN: return val1.Bool() == val2.Bool(); + case FIELD_EHANDLE: return val1.Entity() == val2.Entity(); + case FIELD_VECTOR: + { + Vector vec1; val1.Vector3D(vec1); + Vector vec2; val2.Vector3D(vec2); + return vec1 == vec2; + } +#ifdef MAPBASE_MATCHERS + // logic_compare allows wildcards on either string + default: return Matcher_NamesMatch_MutualWildcard(val1.String(), val2.String()); +#else + default: return FStrEq(val1.String(), val2.String()); +#endif + } + + return false; +} + +// val1 > val2 +bool Variant_Greater(variant_t val1, variant_t val2, bool bLenAllowed) +{ + //if (!val2.Convert(val1.FieldType())) + // return false; + + // Add more fields if they become necessary + switch (val1.FieldType()) + { + case FIELD_INTEGER: + case FIELD_FLOAT: + { + VariantToFloat(val1, val2, bLenAllowed); + return cmp1 > cmp2; + } + case FIELD_BOOLEAN: return val1.Bool() && !val2.Bool(); + case FIELD_VECTOR: + { + Vector vec1; val1.Vector3D(vec1); + Vector vec2; val2.Vector3D(vec2); + return (vec1.x > vec2.x) && (vec1.y > vec2.y) && (vec1.z > vec2.z); + } + default: return strlen(val1.String()) > strlen(val2.String()); + } + + return false; +} + +// val1 >= val2 +bool Variant_GreaterOrEqual(variant_t val1, variant_t val2, bool bLenAllowed) +{ + //if (!val2.Convert(val1.FieldType())) + // return false; + + // Add more fields if they become necessary + switch (val1.FieldType()) + { + case FIELD_INTEGER: + case FIELD_FLOAT: + { + VariantToFloat(val1, val2, bLenAllowed); + return cmp1 >= cmp2; + } + case FIELD_BOOLEAN: return val1.Bool() >= val2.Bool(); + case FIELD_VECTOR: + { + Vector vec1; val1.Vector3D(vec1); + Vector vec2; val2.Vector3D(vec2); + return (vec1.x >= vec2.x) && (vec1.y >= vec2.y) && (vec1.z >= vec2.z); + } + default: return strlen(val1.String()) >= strlen(val2.String()); + } + + return false; +} +#endif + diff --git a/mp/src/game/server/variant_t.h b/mp/src/game/server/variant_t.h index d5e93ea4..fc460bfe 100644 --- a/mp/src/game/server/variant_t.h +++ b/mp/src/game/server/variant_t.h @@ -17,6 +17,10 @@ class CBaseEntity; +#ifdef MAPBASE_VSCRIPT +struct ScriptVariant_t; +#endif + // // A variant class for passing data in entity input/output connections. @@ -49,6 +53,10 @@ public: inline const CHandle &Entity(void) const; inline color32 Color32(void) const { return rgbaVal; } inline void Vector3D(Vector &vec) const; +#ifdef MAPBASE + // Gets angles from a vector + inline void Angle3D(QAngle &ang) const; +#endif fieldtype_t FieldType( void ) { return fieldType; } @@ -59,11 +67,26 @@ public: void SetEntity( CBaseEntity *val ); void SetVector3D( const Vector &val ) { vecVal[0] = val[0]; vecVal[1] = val[1]; vecVal[2] = val[2]; fieldType = FIELD_VECTOR; } void SetPositionVector3D( const Vector &val ) { vecVal[0] = val[0]; vecVal[1] = val[1]; vecVal[2] = val[2]; fieldType = FIELD_POSITION_VECTOR; } +#ifdef MAPBASE + // Passes in angles as a vector + void SetAngle3D( const QAngle &val ) { vecVal[0] = val[0]; vecVal[1] = val[1]; vecVal[2] = val[2]; fieldType = FIELD_VECTOR; } +#endif void SetColor32( color32 val ) { rgbaVal = val; fieldType = FIELD_COLOR32; } void SetColor32( int r, int g, int b, int a ) { rgbaVal.r = r; rgbaVal.g = g; rgbaVal.b = b; rgbaVal.a = a; fieldType = FIELD_COLOR32; } void Set( fieldtype_t ftype, void *data ); void SetOther( void *data ); bool Convert( fieldtype_t newType ); +#ifdef MAPBASE + // Special conversion specifically for FIELD_EHANDLE with !activator, etc. + bool Convert( fieldtype_t newType, CBaseEntity *pSelf, CBaseEntity *pActivator, CBaseEntity *pCaller ); + // Hands over the value + the field type. + // ex: "Otis (String)", "3 (Integer)", or "npc_combine_s (Entity)" + const char *GetDebug(); +#endif + +#ifdef MAPBASE_VSCRIPT + void SetScriptVariant( ScriptVariant_t &var ); +#endif static typedescription_t m_SaveBool[]; static typedescription_t m_SaveInt[]; @@ -105,6 +128,25 @@ inline void variant_t::Vector3D(Vector &vec) const } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Returns this variant as angles. +//----------------------------------------------------------------------------- +inline void variant_t::Angle3D(QAngle &ang) const +{ + if (( fieldType == FIELD_VECTOR ) || ( fieldType == FIELD_POSITION_VECTOR )) + { + ang[0] = vecVal[0]; + ang[1] = vecVal[1]; + ang[2] = vecVal[2]; + } + else + { + ang = vec3_angle; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Returns this variant as an EHANDLE. //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/vehicle_base.cpp b/mp/src/game/server/vehicle_base.cpp index f916ba69..5e348ee9 100644 --- a/mp/src/game/server/vehicle_base.cpp +++ b/mp/src/game/server/vehicle_base.cpp @@ -66,6 +66,15 @@ BEGIN_DATADESC( CPropVehicle ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CPropVehicle, CBaseAnimating, "The base class for four-wheel physics vehicles." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetVehicleType, "GetVehicleType", "Get a vehicle's type." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysics, "GetPhysics", "Get a vehicle's physics." ) + +END_SCRIPTDESC(); +#endif + LINK_ENTITY_TO_CLASS( prop_vehicle, CPropVehicle ); //----------------------------------------------------------------------------- @@ -226,6 +235,23 @@ void CPropVehicle::InputHandBrakeOff( inputdata_t &inputdata ) m_VehiclePhysics.ReleaseHandbrake(); } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HSCRIPT CPropVehicle::ScriptGetPhysics() +{ + HSCRIPT hScript = NULL; + CFourWheelVehiclePhysics *pPhysics = GetPhysics(); + if (pPhysics) + { + hScript = g_pScriptVM->RegisterInstance( pPhysics ); + } + + return hScript; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -334,6 +360,12 @@ BEGIN_DATADESC( CPropVehicleDriveable ) DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnterVehicle", InputEnterVehicle ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnterVehicleImmediate", InputEnterVehicleImmediate ), + DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicle", InputExitVehicle ), + DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicleImmediate", InputExitVehicleImmediate ), +#endif DEFINE_INPUT( m_bHasGun, FIELD_BOOLEAN, "EnableGun" ), // Outputs @@ -343,6 +375,9 @@ BEGIN_DATADESC( CPropVehicleDriveable ) DEFINE_OUTPUT( m_pressedAttack2, "PressedAttack2" ), DEFINE_OUTPUT( m_attackaxis, "AttackAxis" ), DEFINE_OUTPUT( m_attack2axis, "Attack2Axis" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), +#endif DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), DEFINE_EMBEDDEDBYREF( m_pServerVehicle ), @@ -370,6 +405,19 @@ BEGIN_DATADESC( CPropVehicleDriveable ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CPropVehicleDriveable, CPropVehicle, "The base class for driveable vehicles." ) + + DEFINE_SCRIPTFUNC( IsOverturned, "Check if the vehicle is overturned." ) + DEFINE_SCRIPTFUNC( IsVehicleBodyInWater, "Check if the vehicle's body is submerged in water." ) + DEFINE_SCRIPTFUNC( StartEngine, "Start the engine." ) + DEFINE_SCRIPTFUNC( StopEngine, "Stop the engine." ) + DEFINE_SCRIPTFUNC( IsEngineOn, "Check if the engine is on." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDriver, "GetDriver", "Get a vehicle's driver, which could be either a player or a npc_vehicledriver." ) + +END_SCRIPTDESC(); +#endif + LINK_ENTITY_TO_CLASS( prop_vehicle_driveable, CPropVehicleDriveable ); @@ -557,6 +605,10 @@ void CPropVehicleDriveable::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, ResetUseKey( pPlayer ); +#ifdef MAPBASE + m_OnPlayerUse.FireOutput(pActivator, this); +#endif + m_pServerVehicle->HandlePassengerEntry( pPlayer, (value>0) ); } @@ -847,6 +899,99 @@ void CPropVehicleDriveable::InputTurnOff( inputdata_t &inputdata ) m_VehiclePhysics.SetDisableEngine( true ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleDriveable::InputEnterVehicle( inputdata_t &inputdata ) +{ + if ( m_bEnterAnimOn ) + return; + + // Try the activator first & use them if they are a player. + CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); + if ( pPassenger == NULL ) + { + // Activator was not a player, just grab the singleplayer player. + pPassenger = UTIL_PlayerByIndex( 1 ); + if ( pPassenger == NULL ) + return; + } + + // FIXME: I hate code like this. I should really add a parameter to HandlePassengerEntry + // to allow entry into locked vehicles + bool bWasLocked = m_bLocked; + m_bLocked = false; + GetServerVehicle()->HandlePassengerEntry( pPassenger, true ); + m_bLocked = bWasLocked; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPropVehicleDriveable::InputEnterVehicleImmediate( inputdata_t &inputdata ) +{ + if ( m_bEnterAnimOn ) + return; + + // Try the activator first & use them if they are a player. + CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); + if ( pPassenger == NULL ) + { + // Activator was not a player, just grab the singleplayer player. + pPassenger = UTIL_PlayerByIndex( 1 ); + if ( pPassenger == NULL ) + return; + } + + CBasePlayer *pPlayer = ToBasePlayer( pPassenger ); + if ( pPlayer != NULL ) + { + if ( pPlayer->IsInAVehicle() ) + { + // Force the player out of whatever vehicle they are in. + pPlayer->LeaveVehicle(); + } + + pPlayer->GetInVehicle( GetServerVehicle(), VEHICLE_ROLE_DRIVER ); + } + else + { + // NPCs are not currently supported - jdw + Assert( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleDriveable::InputExitVehicle( inputdata_t &inputdata ) +{ + if (!GetDriver()) + return; + + if ( CanExitVehicle(GetDriver()) ) + { + GetServerVehicle()->HandlePassengerExit(GetDriver()->MyCombatCharacterPointer()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleDriveable::InputExitVehicleImmediate( inputdata_t &inputdata ) +{ + if (!GetDriver()) + return; + + if (GetDriver()->IsPlayer()) + { + static_cast(GetDriver())->LeaveVehicle(); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Check to see if the engine is on. //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/vehicle_base.h b/mp/src/game/server/vehicle_base.h index ab63005a..831ea6b4 100644 --- a/mp/src/game/server/vehicle_base.h +++ b/mp/src/game/server/vehicle_base.h @@ -109,6 +109,12 @@ public: void InputHandBrakeOff( inputdata_t &inputdata ); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); + + HSCRIPT ScriptGetPhysics(); + int ScriptGetVehicleType() { return GetVehicleType(); } +#endif #ifdef HL2_EPISODIC void AddPhysicsChild( CBaseEntity *pChild ); @@ -166,6 +172,9 @@ class CPropVehicleDriveable : public CPropVehicle, public IDrivableVehicle, publ DECLARE_CLASS( CPropVehicleDriveable, CPropVehicle ); DECLARE_SERVERCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: CPropVehicleDriveable( void ); ~CPropVehicleDriveable( void ); @@ -192,6 +201,12 @@ public: void InputUnlock( inputdata_t &inputdata ); void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); +#ifdef MAPBASE + virtual void InputEnterVehicle( inputdata_t &inputdata ); + virtual void InputEnterVehicleImmediate( inputdata_t &inputdata ); + virtual void InputExitVehicle( inputdata_t &inputdata ); + virtual void InputExitVehicleImmediate( inputdata_t &inputdata ); +#endif // Locals void ResetUseKey( CBasePlayer *pPlayer ); @@ -232,6 +247,10 @@ public: // If this is a vehicle, returns the vehicle interface virtual IServerVehicle *GetServerVehicle() { return m_pServerVehicle; } +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetDriver() { return ToHScript( GetDriver() ); } +#endif + protected: virtual bool ShouldThink() { return ( GetDriver() != NULL ); } @@ -251,6 +270,10 @@ protected: COutputFloat m_attackaxis; COutputFloat m_attack2axis; +#ifdef MAPBASE + COutputEvent m_OnPlayerUse; +#endif + CNetworkHandle( CBasePlayer, m_hPlayer ); public: diff --git a/mp/src/game/server/vehicle_choreo_generic.cpp b/mp/src/game/server/vehicle_choreo_generic.cpp index b644a7a4..3b3a53f9 100644 --- a/mp/src/game/server/vehicle_choreo_generic.cpp +++ b/mp/src/game/server/vehicle_choreo_generic.cpp @@ -197,6 +197,11 @@ public: // If this is a vehicle, returns the vehicle interface virtual IServerVehicle *GetServerVehicle() { return &m_ServerVehicle; } +#ifdef MAPBASE + virtual bool IsPassengerUsingStandardWeapons( int nRole = VEHICLE_ROLE_DRIVER ) { return m_bAllowStandardWeapons; } + CNetworkVar( bool, m_bAllowStandardWeapons ); +#endif + bool ShouldCollide( int collisionGroup, int contentsMask ) const; bool m_bForcePlayerEyePoint; // Uses player's eyepoint instead of 'vehicle_driver_eyes' attachment @@ -256,6 +261,10 @@ BEGIN_DATADESC( CPropVehicleChoreoGeneric ) DEFINE_KEYFIELD( m_bIgnorePlayerCollisions, FIELD_BOOLEAN, "ignoreplayer" ), DEFINE_KEYFIELD( m_bForcePlayerEyePoint, FIELD_BOOLEAN, "useplayereyes" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bAllowStandardWeapons, FIELD_BOOLEAN, "AllowStandardWeapons" ), +#endif + DEFINE_OUTPUT( m_playerOn, "PlayerOn" ), DEFINE_OUTPUT( m_playerOff, "PlayerOff" ), DEFINE_OUTPUT( m_OnOpen, "OnOpen" ), @@ -281,6 +290,9 @@ IMPLEMENT_SERVERCLASS_ST(CPropVehicleChoreoGeneric, DT_PropVehicleChoreoGeneric) SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flYawMax ) ), SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchMin ) ), SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchMax ) ), +#ifdef MAPBASE + SendPropBool( SENDINFO( m_bAllowStandardWeapons ) ), +#endif END_SEND_TABLE(); diff --git a/mp/src/game/server/vscript_server.cpp b/mp/src/game/server/vscript_server.cpp new file mode 100644 index 00000000..63f0ac0d --- /dev/null +++ b/mp/src/game/server/vscript_server.cpp @@ -0,0 +1,1390 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_server.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "eventqueue.h" +#include "characterset.h" +#include "sceneentity.h" // for exposing scene precache function +#include "isaverestore.h" +#include "gamerules.h" +#include "vscript_server.nut" +#ifdef MAPBASE_VSCRIPT +#include "world.h" +#endif + +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CScriptEntityIterator +{ +public: +#ifdef MAPBASE_VSCRIPT + HSCRIPT GetLocalPlayer() + { + return ToHScript( UTIL_GetLocalPlayerOrListenServerHost() ); + } +#endif + HSCRIPT First() { return Next(NULL); } + + HSCRIPT Next( HSCRIPT hStartEntity ) + { + return ToHScript( gEntList.NextEnt( ToEnt( hStartEntity ) ) ); + } + + HSCRIPT CreateByClassname( const char *className ) + { + return ToHScript( CreateEntityByName( className ) ); + } + + HSCRIPT FindByClassname( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByClassname( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindByName( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByName( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindInSphere( HSCRIPT hStartEntity, const Vector &vecCenter, float flRadius ) + { + return ToHScript( gEntList.FindEntityInSphere( ToEnt( hStartEntity ), vecCenter, flRadius ) ); + } + + HSCRIPT FindByTarget( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByTarget( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindByModel( HSCRIPT hStartEntity, const char *szModelName ) + { + return ToHScript( gEntList.FindEntityByModel( ToEnt( hStartEntity ), szModelName ) ); + } + + HSCRIPT FindByNameNearest( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByNameNearest( szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByNameWithin( HSCRIPT hStartEntity, const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByNameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameNearest( szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByClassnameWithin( HSCRIPT hStartEntity , const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); + } +#ifdef MAPBASE_VSCRIPT + HSCRIPT FindByClassnameWithinBox( HSCRIPT hStartEntity , const char *szName, const Vector &vecMins, const Vector &vecMaxs ) + { + return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecMins, vecMaxs ) ); + } +#endif +private: +} g_ScriptEntityIterator; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETON "The global list of entities" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( GetLocalPlayer, "Get local player or listen server host" ) +#endif + DEFINE_SCRIPTFUNC( First, "Begin an iteration over the list of entities" ) + DEFINE_SCRIPTFUNC( Next, "Continue an iteration over the list of entities, providing reference to a previously found entity" ) + DEFINE_SCRIPTFUNC( CreateByClassname, "Creates an entity by classname" ) + DEFINE_SCRIPTFUNC( FindByClassname, "Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByName, "Find entities by name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindInSphere, "Find entities within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByTarget, "Find entities by targetname. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByModel, "Find entities by model name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByNameNearest, "Find entities by name nearest to a point." ) + DEFINE_SCRIPTFUNC( FindByNameWithin, "Find entities by name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByClassnameNearest, "Find entities by class name nearest to a point." ) + DEFINE_SCRIPTFUNC( FindByClassnameWithin, "Find entities by class name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( FindByClassnameWithinBox, "Find entities by class name within an AABB. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +#endif +END_SCRIPTDESC(); + +#ifndef MAPBASE_VSCRIPT // Mapbase adds this to the base library so that CScriptKeyValues can be accessed anywhere, like VBSP. +// ---------------------------------------------------------------------------- +// KeyValues access - CBaseEntity::ScriptGetKeyFromModel returns root KeyValues +// ---------------------------------------------------------------------------- + +BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC_NAMED( ScriptFindKey, "FindKey", "Given a KeyValues object and a key name, find a KeyValues object associated with the key name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstSubKey, "GetFirstSubKey", "Given a KeyValues object, return the first sub key object" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNextKey, "GetNextKey", "Given a KeyValues object, return the next key object in a sub key group" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueInt, "GetKeyInt", "Given a KeyValues object and a key name, return associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueFloat, "GetKeyFloat", "Given a KeyValues object and a key name, return associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBool, "GetKeyBool", "Given a KeyValues object and a key name, return associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueString, "GetKeyString", "Given a KeyValues object and a key name, return associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptIsKeyValueEmpty, "IsKeyEmpty", "Given a KeyValues object and a key name, return true if key name has no value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); +END_SCRIPTDESC(); + +HSCRIPT CScriptKeyValues::ScriptFindKey( const char *pszName ) +{ + KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetFirstSubKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetFirstSubKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetNextKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetNextKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +int CScriptKeyValues::ScriptGetKeyValueInt( const char *pszName ) +{ + int i = m_pKeyValues->GetInt( pszName ); + return i; +} + +float CScriptKeyValues::ScriptGetKeyValueFloat( const char *pszName ) +{ + float f = m_pKeyValues->GetFloat( pszName ); + return f; +} + +const char *CScriptKeyValues::ScriptGetKeyValueString( const char *pszName ) +{ + const char *psz = m_pKeyValues->GetString( pszName ); + return psz; +} + +bool CScriptKeyValues::ScriptIsKeyValueEmpty( const char *pszName ) +{ + bool b = m_pKeyValues->IsEmpty( pszName ); + return b; +} + +bool CScriptKeyValues::ScriptGetKeyValueBool( const char *pszName ) +{ + bool b = m_pKeyValues->GetBool( pszName ); + return b; +} + +void CScriptKeyValues::ScriptReleaseKeyValues( ) +{ + m_pKeyValues->deleteThis(); + m_pKeyValues = NULL; +} + + +// constructors +CScriptKeyValues::CScriptKeyValues( KeyValues *pKeyValues = NULL ) +{ + m_pKeyValues = pKeyValues; +} + +// destructor +CScriptKeyValues::~CScriptKeyValues( ) +{ + if (m_pKeyValues) + { + m_pKeyValues->deleteThis(); + } + m_pKeyValues = NULL; +} +#endif + +#ifdef MAPBASE_VSCRIPT +#define RETURN_IF_CANNOT_DRAW_OVERLAY\ + if (engine->IsPaused())\ + {\ + CGWarning( 1, CON_GROUP_VSCRIPT, "debugoverlay: cannot draw while the game is paused!\n");\ + return;\ + } +class CDebugOverlayScriptHelper +{ +public: + + void Box(const Vector &origin, const Vector &mins, const Vector &maxs, int r, int g, int b, int a, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddBoxOverlay(origin, mins, maxs, vec3_angle, r, g, b, a, flDuration); + } + } + void BoxDirection(const Vector &origin, const Vector &mins, const Vector &maxs, const Vector &forward, int r, int g, int b, int a, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + QAngle f_angles = vec3_angle; + f_angles.y = UTIL_VecToYaw(forward); + + if (debugoverlay) + { + debugoverlay->AddBoxOverlay(origin, mins, maxs, f_angles, r, g, b, a, flDuration); + } + } + void BoxAngles(const Vector &origin, const Vector &mins, const Vector &maxs, const QAngle &angles, int r, int g, int b, int a, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddBoxOverlay(origin, mins, maxs, angles, r, g, b, a, flDuration); + } + } + void SweptBox(const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, const QAngle & angles, int r, int g, int b, int a, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddSweptBoxOverlay(start, end, mins, maxs, angles, r, g, b, a, flDuration); + } + } + void EntityBounds(HSCRIPT pEntity, int r, int g, int b, int a, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + CBaseEntity *pEnt = ToEnt(pEntity); + if (!pEnt) + return; + + const CCollisionProperty *pCollide = pEnt->CollisionProp(); + if (debugoverlay) + { + debugoverlay->AddBoxOverlay(pCollide->GetCollisionOrigin(), pCollide->OBBMins(), pCollide->OBBMaxs(), pCollide->GetCollisionAngles(), r, g, b, a, flDuration); + } + } + void Line(const Vector &origin, const Vector &target, int r, int g, int b, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddLineOverlay(origin, target, r, g, b, noDepthTest, flDuration); + } + } + void Triangle(const Vector &p1, const Vector &p2, const Vector &p3, int r, int g, int b, int a, bool noDepthTest, float duration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddTriangleOverlay(p1, p2, p3, r, g, b, a, noDepthTest, duration); + } + } + void EntityText(int entityID, int text_offset, const char *text, float flDuration, int r, int g, int b, int a) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddEntityTextOverlay(entityID, text_offset, flDuration, + (int)clamp(r * 255.f, 0.f, 255.f), (int)clamp(g * 255.f, 0.f, 255.f), (int)clamp(b * 255.f, 0.f, 255.f), + (int)clamp(a * 255.f, 0.f, 255.f), text); + } + } + void EntityTextAtPosition(const Vector &origin, int text_offset, const char *text, float flDuration, int r, int g, int b, int a) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddTextOverlayRGB(origin, text_offset, flDuration, r, g, b, a, "%s", text); + } + } + void Grid(const Vector &vPosition) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddGridOverlay(vPosition); + } + } + void Text(const Vector &origin, const char *text, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddTextOverlay(origin, flDuration, "%s", text); + } + } + void ScreenText(float fXpos, float fYpos, const char *text, int r, int g, int b, int a, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + if (debugoverlay) + { + debugoverlay->AddScreenTextOverlay(fXpos, fYpos, flDuration, r, g, b, a, text); + } + } + void Cross3D(const Vector &position, float size, int r, int g, int b, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Line( position + Vector(size,0,0), position - Vector(size,0,0), r, g, b, noDepthTest, flDuration ); + Line( position + Vector(0,size,0), position - Vector(0,size,0), r, g, b, noDepthTest, flDuration ); + Line( position + Vector(0,0,size), position - Vector(0,0,size), r, g, b, noDepthTest, flDuration ); + } + void Cross3DOriented(const Vector &position, const QAngle &angles, float size, int r, int g, int b, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Vector forward, right, up; + AngleVectors( angles, &forward, &right, &up ); + + forward *= size; + right *= size; + up *= size; + + Line( position + right, position - right, r, g, b, noDepthTest, flDuration ); + Line( position + forward, position - forward, r, g, b, noDepthTest, flDuration ); + Line( position + up, position - up, r, g, b, noDepthTest, flDuration ); + } + void DrawTickMarkedLine(const Vector &startPos, const Vector &endPos, float tickDist, int tickTextDist, int r, int g, int b, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Vector lineDir = (endPos - startPos); + float lineDist = VectorNormalize(lineDir); + int numTicks = lineDist / tickDist; + + Vector upVec = Vector(0,0,4); + Vector sideDir; + Vector tickPos = startPos; + int tickTextCnt = 0; + + CrossProduct(lineDir, upVec, sideDir); + + Line(startPos, endPos, r, g, b, noDepthTest, flDuration); + + for (int i = 0; i 0 ) + { + Triangle( p5, p4, p3, r, g, b, a, noDepthTest, flDuration ); + Triangle( p1, p7, p6, r, g, b, a, noDepthTest, flDuration ); + Triangle( p6, p2, p1, r, g, b, a, noDepthTest, flDuration ); + + Triangle( p3, p4, p5, r, g, b, a, noDepthTest, flDuration ); + Triangle( p6, p7, p1, r, g, b, a, noDepthTest, flDuration ); + Triangle( p1, p2, p6, r, g, b, a, noDepthTest, flDuration ); + } + } + void YawArrow(const Vector &startPos, float yaw, float length, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Vector forward = UTIL_YawToVector( yaw ); + HorzArrow( startPos, startPos + forward * length, width, r, g, b, a, noDepthTest, flDuration ); + } + void VertArrow(const Vector &startPos, const Vector &endPos, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Vector lineDir = (endPos - startPos); + VectorNormalize( lineDir ); + Vector upVec; + Vector sideDir; + float radius = width / 2.0; + + VectorVectors( lineDir, sideDir, upVec ); + + Vector p1 = startPos - upVec * radius; + Vector p2 = endPos - lineDir * width - upVec * radius; + Vector p3 = endPos - lineDir * width - upVec * width; + Vector p4 = endPos; + Vector p5 = endPos - lineDir * width + upVec * width; + Vector p6 = endPos - lineDir * width + upVec * radius; + Vector p7 = startPos + upVec * radius; + + Line(p1, p2, r,g,b,noDepthTest,flDuration); + Line(p2, p3, r,g,b,noDepthTest,flDuration); + Line(p3, p4, r,g,b,noDepthTest,flDuration); + Line(p4, p5, r,g,b,noDepthTest,flDuration); + Line(p5, p6, r,g,b,noDepthTest,flDuration); + Line(p6, p7, r,g,b,noDepthTest,flDuration); + + if ( a > 0 ) + { + Triangle( p5, p4, p3, r, g, b, a, noDepthTest, flDuration ); + Triangle( p1, p7, p6, r, g, b, a, noDepthTest, flDuration ); + Triangle( p6, p2, p1, r, g, b, a, noDepthTest, flDuration ); + + Triangle( p3, p4, p5, r, g, b, a, noDepthTest, flDuration ); + Triangle( p6, p7, p1, r, g, b, a, noDepthTest, flDuration ); + Triangle( p1, p2, p6, r, g, b, a, noDepthTest, flDuration ); + } + } + void Axis(const Vector &position, const QAngle &angles, float size, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Vector xvec, yvec, zvec; + AngleVectors( angles, &xvec, &yvec, &zvec ); + + xvec = position + (size * xvec); + yvec = position - (size * yvec); + zvec = position + (size * zvec); + + Line( position, xvec, 255, 0, 0, noDepthTest, flDuration ); + Line( position, yvec, 0, 255, 0, noDepthTest, flDuration ); + Line( position, zvec, 0, 0, 255, noDepthTest, flDuration ); + } + void Sphere(const Vector ¢er, float radius, int r, int g, int b, bool noDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + Vector edge, lastEdge; + + float axisSize = radius; + Line( center + Vector( 0, 0, -axisSize ), center + Vector( 0, 0, axisSize ), r, g, b, noDepthTest, flDuration ); + Line( center + Vector( 0, -axisSize, 0 ), center + Vector( 0, axisSize, 0 ), r, g, b, noDepthTest, flDuration ); + Line( center + Vector( -axisSize, 0, 0 ), center + Vector( axisSize, 0, 0 ), r, g, b, noDepthTest, flDuration ); + + lastEdge = Vector( radius + center.x, center.y, center.z ); + float angle; + for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) + { + edge.x = radius * cosf( angle / 180.0f * M_PI ) + center.x; + edge.y = center.y; + edge.z = radius * sinf( angle / 180.0f * M_PI ) + center.z; + + Line( edge, lastEdge, r, g, b, noDepthTest, flDuration ); + + lastEdge = edge; + } + + lastEdge = Vector( center.x, radius + center.y, center.z ); + for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) + { + edge.x = center.x; + edge.y = radius * cosf( angle / 180.0f * M_PI ) + center.y; + edge.z = radius * sinf( angle / 180.0f * M_PI ) + center.z; + + Line( edge, lastEdge, r, g, b, noDepthTest, flDuration ); + + lastEdge = edge; + } + + lastEdge = Vector( center.x, radius + center.y, center.z ); + for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) + { + edge.x = radius * cosf( angle / 180.0f * M_PI ) + center.x; + edge.y = radius * sinf( angle / 180.0f * M_PI ) + center.y; + edge.z = center.z; + + Line( edge, lastEdge, r, g, b, noDepthTest, flDuration ); + + lastEdge = edge; + } + } + void CircleOriented(const Vector &position, const QAngle &angles, float radius, int r, int g, int b, int a, bool bNoDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + matrix3x4_t xform; + AngleMatrix(angles, position, xform); + Vector xAxis, yAxis; + MatrixGetColumn(xform, 2, xAxis); + MatrixGetColumn(xform, 1, yAxis); + Circle(position, xAxis, yAxis, radius, r, g, b, a, bNoDepthTest, flDuration); + } + void Circle(const Vector &position, const Vector &xAxis, const Vector &yAxis, float radius, int r, int g, int b, int a, bool bNoDepthTest, float flDuration) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + const unsigned int nSegments = 16; + const float flRadStep = (M_PI*2.0f) / (float) nSegments; + + Vector vecLastPosition; + Vector vecStart = position + xAxis * radius; + Vector vecPosition = vecStart; + + for ( int i = 1; i <= nSegments; i++ ) + { + vecLastPosition = vecPosition; + + float flSin, flCos; + SinCos( flRadStep*i, &flSin, &flCos ); + vecPosition = position + (xAxis * flCos * radius) + (yAxis * flSin * radius); + + Line( vecLastPosition, vecPosition, r, g, b, bNoDepthTest, flDuration ); + + if ( a && i > 1 ) + { + debugoverlay->AddTriangleOverlay( vecStart, vecLastPosition, vecPosition, r, g, b, a, bNoDepthTest, flDuration ); + } + } + } + void SetDebugBits(HSCRIPT hEntity, int bit) // DebugOverlayBits_t + { + CBaseEntity *pEnt = ToEnt(hEntity); + if (!pEnt) + return; + + if (pEnt->m_debugOverlays & bit) + { + pEnt->m_debugOverlays &= ~bit; + } + else + { + pEnt->m_debugOverlays |= bit; + +#ifdef AI_MONITOR_FOR_OSCILLATION + if (pEnt->IsNPC()) + { + pEnt->MyNPCPointer()->m_ScheduleHistory.RemoveAll(); + } +#endif//AI_MONITOR_FOR_OSCILLATION + } + } + void ClearAllOverlays() + { + // Clear all entities of their debug overlays + for (CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity; pEntity = gEntList.NextEnt(pEntity)) + { + pEntity->m_debugOverlays = 0; + } + + if (debugoverlay) + { + debugoverlay->ClearAllOverlays(); + } + } + +private: +} g_ScriptDebugOverlay; + +BEGIN_SCRIPTDESC_ROOT(CDebugOverlayScriptHelper, SCRIPT_SINGLETON "CDebugOverlayScriptHelper") + DEFINE_SCRIPTFUNC( Box, "Draws a world-space axis-aligned box. Specify bounds in world space." ) + DEFINE_SCRIPTFUNC( BoxDirection, "Draw box oriented to a Vector direction" ) + DEFINE_SCRIPTFUNC( BoxAngles, "Draws an oriented box at the origin. Specify bounds in local space." ) + DEFINE_SCRIPTFUNC( SweptBox, "Draws a swept box. Specify endpoints in world space and the bounds in local space." ) + DEFINE_SCRIPTFUNC( EntityBounds, "Draws bounds of an entity" ) + DEFINE_SCRIPTFUNC( Line, "Draws a line between two points" ) + DEFINE_SCRIPTFUNC( Triangle, "Draws a filled triangle. Specify vertices in world space." ) + DEFINE_SCRIPTFUNC( EntityText, "Draws text on an entity" ) + DEFINE_SCRIPTFUNC( EntityTextAtPosition, "Draw entity text overlay at a specific position" ) + DEFINE_SCRIPTFUNC( Grid, "Add grid overlay" ) + DEFINE_SCRIPTFUNC( Text, "Draws 2D text. Specify origin in world space." ) + DEFINE_SCRIPTFUNC( ScreenText, "Draws 2D text. Specify coordinates in screen space." ) + DEFINE_SCRIPTFUNC( Cross3D, "Draws a world-aligned cross. Specify origin in world space." ) + DEFINE_SCRIPTFUNC( Cross3DOriented, "Draws an oriented cross. Specify origin in world space." ) + DEFINE_SCRIPTFUNC( DrawTickMarkedLine, "Draws a dashed line. Specify endpoints in world space." ) + DEFINE_SCRIPTFUNC( HorzArrow, "Draws a horizontal arrow. Specify endpoints in world space." ) + DEFINE_SCRIPTFUNC( YawArrow, "Draws a arrow associated with a specific yaw. Specify endpoints in world space." ) + DEFINE_SCRIPTFUNC( VertArrow, "Draws a vertical arrow. Specify endpoints in world space." ) + DEFINE_SCRIPTFUNC( Axis, "Draws an axis. Specify origin + orientation in world space." ) + DEFINE_SCRIPTFUNC( Sphere, "Draws a wireframe sphere. Specify center in world space." ) + DEFINE_SCRIPTFUNC( CircleOriented, "Draws a circle oriented. Specify center in world space." ) + DEFINE_SCRIPTFUNC( Circle, "Draws a circle. Specify center in world space." ) + DEFINE_SCRIPTFUNC( SetDebugBits, "Set debug bits on entity" ) + DEFINE_SCRIPTFUNC( ClearAllOverlays, "Clear all debug overlays at once" ) +END_SCRIPTDESC(); +#endif // MAPBASE_VSCRIPT + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static float Time() +{ + return gpGlobals->curtime; +} + +static float FrameTime() +{ + return gpGlobals->frametime; +} + +#ifdef MAPBASE_VSCRIPT +static int MaxPlayers() +{ + return gpGlobals->maxClients; +} + +static float IntervalPerTick() +{ + return gpGlobals->interval_per_tick; +} + +static int GetLoadType() +{ + return gpGlobals->eLoadType; +} +#endif + +static void SendToConsole( const char *pszCommand ) +{ + CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); + if ( !pPlayer ) + { +#ifdef MAPBASE + CGMsg( 1, CON_GROUP_VSCRIPT, "Cannot execute \"%s\", no player\n", pszCommand ); +#else + DevMsg ("Cannot execute \"%s\", no player\n", pszCommand ); +#endif + return; + } + + engine->ClientCommand( pPlayer->edict(), pszCommand ); +} + +static void SendToConsoleServer( const char *pszCommand ) +{ + // TODO: whitelist for multiplayer + engine->ServerCommand( UTIL_VarArgs("%s\n", pszCommand) ); +} + +static const char *GetMapName() +{ + return STRING( gpGlobals->mapname ); +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +#ifdef MAPBASE_VSCRIPT +static int DoEntFire( const char *pszTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#else +static void DoEntFire( const char *pszTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#endif +{ + const char *target = "", *action = "Use"; + variant_t value; + + target = STRING( AllocPooledString( pszTarget ) ); + + // Don't allow them to run anything on a point_servercommand unless they're the host player. Otherwise they can ent_fire + // and run any command on the server. Admittedly, they can only do the ent_fire if sv_cheats is on, but + // people complained about users resetting the rcon password if the server briefly turned on cheats like this: + // give point_servercommand + // ent_fire point_servercommand command "rcon_password mynewpassword" + if ( gpGlobals->maxClients > 1 && V_stricmp( target, "point_servercommand" ) == 0 ) + { + return 0; + } + + if ( *pszAction ) + { + action = STRING( AllocPooledString( pszAction ) ); + } + if ( *pszValue ) + { + value.SetString( AllocPooledString( pszValue ) ); + } + if ( delay < 0 ) + { + delay = 0; + } + +#ifdef MAPBASE_VSCRIPT + return +#endif + g_EventQueue.AddEvent( target, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); +} + + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +HSCRIPT CreateProp( const char *pszEntityName, const Vector &vOrigin, const char *pszModelName, int iAnim ) +{ + CBaseAnimating *pBaseEntity = (CBaseAnimating *)CreateEntityByName( pszEntityName ); + pBaseEntity->SetAbsOrigin( vOrigin ); + pBaseEntity->SetModel( pszModelName ); + pBaseEntity->SetPlaybackRate( 1.0f ); + + int iSequence = pBaseEntity->SelectWeightedSequence( (Activity)iAnim ); + + if ( iSequence != -1 ) + { + pBaseEntity->SetSequence( iSequence ); + } + + return ToHScript( pBaseEntity ); +} + +//-------------------------------------------------------------------------------------------------- +// Use an entity's script instance to add an entity IO event (used for firing events on unnamed entities from vscript) +//-------------------------------------------------------------------------------------------------- +#ifdef MAPBASE_VSCRIPT +static int DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#else +static void DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#endif +{ + const char *action = "Use"; + variant_t value; + + if ( *pszAction ) + { + action = STRING( AllocPooledString( pszAction ) ); + } + if ( *pszValue ) + { + value.SetString( AllocPooledString( pszValue ) ); + } + if ( delay < 0 ) + { + delay = 0; + } + + CBaseEntity* pTarget = ToEnt(hTarget); + + if ( !pTarget ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "VScript error: DoEntFire was passed an invalid entity instance.\n" ); +#ifdef MAPBASE_VSCRIPT + return 0; +#else + return; +#endif + } + +#ifdef MAPBASE_VSCRIPT + return +#endif + g_EventQueue.AddEvent( pTarget, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); +} + +static float ScriptTraceLine( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore ) +{ + // UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_BLOCKLOS, pLooker, COLLISION_GROUP_NONE, ptr ); + trace_t tr; + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceLine( vecStart, vecEnd, MASK_NPCWORLDSTATIC, pLooker, COLLISION_GROUP_NONE, &tr); + if (tr.fractionleftsolid && tr.startsolid) + { + return 1.0 - tr.fractionleftsolid; + } + else + { + return tr.fraction; + } +} + +#ifdef MAPBASE_VSCRIPT +static bool CancelEntityIOEvent( int event ) +{ + return g_EventQueue.RemoveEvent(event); +} + +static float GetEntityIOEventTimeLeft( int event ) +{ + return g_EventQueue.GetTimeLeft(event); +} + +// vscript_server.nut adds this to the base CConvars class +static const char *ScriptGetClientConvarValue( const char *pszConVar, int entindex ) +{ + return engine->GetClientConVarValue( entindex, pszConVar ); +} +#endif // MAPBASE_VSCRIPT + +bool VScriptServerInit() +{ + VMPROF_START + + if( scriptmanager != NULL ) + { + ScriptLanguage_t scriptLanguage = SL_DEFAULT; + + char const *pszScriptLanguage; +#ifdef MAPBASE_VSCRIPT + if (GetWorldEntity()->GetScriptLanguage() != SL_NONE) + { + // Allow world entity to override script language + scriptLanguage = GetWorldEntity()->GetScriptLanguage(); + + // Less than SL_NONE means the script language should literally be none + if (scriptLanguage < SL_NONE) + scriptLanguage = SL_NONE; + } + else +#endif + if ( CommandLine()->CheckParm( "-scriptlang", &pszScriptLanguage ) ) + { + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + scriptLanguage = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + scriptLanguage = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + scriptLanguage = SL_PYTHON; + } +#ifdef MAPBASE_VSCRIPT + else if( !Q_stricmp(pszScriptLanguage, "lua") ) + { + scriptLanguage = SL_LUA; + } +#endif + else + { + CGWarning( 1, CON_GROUP_VSCRIPT, "-server_script does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + scriptLanguage = SL_NONE; + } + + } + if( scriptLanguage != SL_NONE ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( scriptLanguage ); + + if( g_pScriptVM ) + { +#ifdef MAPBASE_VSCRIPT + CGMsg( 0, CON_GROUP_VSCRIPT, "VSCRIPT SERVER: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); +#else + Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); +#endif + +#ifdef MAPBASE_VSCRIPT + // MULTIPLAYER + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByIndex, "GetPlayerByIndex", "PlayerInstanceFromIndex" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByUserId, "GetPlayerByUserId", "GetPlayerFromUserID" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByName, "GetPlayerByName", "" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerByNetworkID, "GetPlayerByNetworkID", "" ); + + ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); +#else + ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); +#endif + + ScriptRegisterFunction( g_pScriptVM, SendToConsole, "Send a string to the console as a command" ); + ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLine, "TraceLine", "given 2 points & ent to ignore, return fraction along line that hits world or models" ); + + ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); + ScriptRegisterFunction( g_pScriptVM, FrameTime, "Get the time spent on the server in the last frame" ); +#ifdef MAPBASE_VSCRIPT + ScriptRegisterFunction( g_pScriptVM, SendToConsoleServer, "Send a string to the server console as a command" ); + ScriptRegisterFunction( g_pScriptVM, MaxPlayers, "Get the maximum number of players allowed on this server" ); + ScriptRegisterFunction( g_pScriptVM, IntervalPerTick, "Get the interval used between each tick" ); + ScriptRegisterFunction( g_pScriptVM, GetLoadType, "Get the way the current game was loaded (corresponds to the MapLoad enum)" ); + ScriptRegisterFunction( g_pScriptVM, DoEntFire, SCRIPT_ALIAS( "EntFire", "Generate an entity i/o event" ) ); + ScriptRegisterFunction( g_pScriptVM, DoEntFireByInstanceHandle, SCRIPT_ALIAS( "EntFireByHandle", "Generate an entity i/o event. First parameter is an entity instance." ) ); + // ScriptRegisterFunction( g_pScriptVM, IsValidEntity, "Returns true if the entity is valid." ); + + ScriptRegisterFunction( g_pScriptVM, CancelEntityIOEvent, "Remove entity I/O event." ); + ScriptRegisterFunction( g_pScriptVM, GetEntityIOEventTimeLeft, "Get time left on entity I/O event." ); + ScriptRegisterFunction( g_pScriptVM, ScriptGetClientConvarValue, SCRIPT_HIDE ); +#else + ScriptRegisterFunction( g_pScriptVM, DoEntFire, SCRIPT_ALIAS( "EntFire", "Generate and entity i/o event" ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, DoEntFireByInstanceHandle, "EntFireByHandle", "Generate and entity i/o event. First parameter is an entity instance." ); +#endif + ScriptRegisterFunction( g_pScriptVM, DoUniqueString, SCRIPT_ALIAS( "UniqueString", "Generate a string guaranteed to be unique across the life of the script VM, with an optional root string. Useful for adding data to tables when not sure what keys are already in use in that table." ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCreateSceneEntity, "CreateSceneEntity", "Create a scene entity to play the specified scene." ); +#ifndef MAPBASE_VSCRIPT + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Box, "DebugDrawBox", "Draw a debug overlay box" ); + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Line, "DebugDrawLine", "Draw a debug overlay box" ); +#endif + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + ScriptRegisterFunction( g_pScriptVM, CreateProp, "Create a physics prop" ); + + if ( GameRules() ) + { + GameRules()->RegisterScriptFunctions(); + } + + g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" ); +#endif // MAPBASE_VSCRIPT + +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->RegisterAllClasses(); + g_pScriptVM->RegisterAllEnums(); + + IGameSystem::RegisterVScriptAllSystems(); + + RegisterSharedScriptConstants(); + RegisterSharedScriptFunctions(); +#endif + + if (scriptLanguage == SL_SQUIRREL) + { + g_pScriptVM->Run( g_Script_vscript_server ); + } + + VScriptRunScript( "mapspawn", false ); + +#ifdef MAPBASE_VSCRIPT + // Since the world entity spawns before VScript is initted, RunVScripts() is called before the VM has started, so no scripts are run. + // This gets around that by calling the same function right after the VM is initted. + GetWorldEntity()->RunVScripts(); +#endif + + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); + + return true; + } + else + { + CGWarning( 1, CON_GROUP_VSCRIPT, "VM Did not start!\n" ); + } + } + } + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptServerTerm() +{ + if( g_pScriptVM != NULL ) + { + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} + + +bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + HSCRIPT hReplaceClosuresFunc = g_pScriptVM->LookupFunction( "__ReplaceClosures" ); + if ( !hReplaceClosuresFunc ) + { + return false; + } + HSCRIPT hNewScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + if ( !hNewScript ) + { + return false; + } + + g_pScriptVM->Call( hReplaceClosuresFunc, NULL, true, NULL, hNewScript, hScope ); + return true; +} + +CON_COMMAND( script_reload_code, "Execute a vscript file, replacing existing functions with the functions in the run script" ) +{ + if ( !*args[1] ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "No script specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + + VScriptServerReplaceClosures( args[1], NULL, true ); +} + +CON_COMMAND( script_reload_entity_code, "Execute all of this entity's VScripts, replacing existing functions with the functions in the run scripts" ) +{ + extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ); + + const char *pszTarget = ""; + if ( *args[1] ) + { + pszTarget = args[1]; + } + + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CBaseEntity *pEntity = NULL; + while ( (pEntity = GetNextCommandEntity( pPlayer, pszTarget, pEntity )) != NULL ) + { + if ( pEntity->m_ScriptScope.IsInitialized() && pEntity->m_iszVScripts != NULL_STRING ) + { + char szScriptsList[255]; + Q_strcpy( szScriptsList, STRING(pEntity->m_iszVScripts) ); + CUtlStringList szScripts; + V_SplitString( szScriptsList, " ", szScripts); + + for( int i = 0 ; i < szScripts.Count() ; i++ ) + { + VScriptServerReplaceClosures( szScripts[i], pEntity->m_ScriptScope, true ); + } + } + } +} + +CON_COMMAND( script_reload_think, "Execute an activation script, replacing existing functions with the functions in the run script" ) +{ + extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ); + + const char *pszTarget = ""; + if ( *args[1] ) + { + pszTarget = args[1]; + } + + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CBaseEntity *pEntity = NULL; + while ( (pEntity = GetNextCommandEntity( pPlayer, pszTarget, pEntity )) != NULL ) + { + if ( pEntity->m_ScriptScope.IsInitialized() && pEntity->m_iszScriptThinkFunction != NULL_STRING ) + { + VScriptServerReplaceClosures( STRING(pEntity->m_iszScriptThinkFunction), pEntity->m_ScriptScope, true ); + } + } +} + +class CVScriptGameSystem : public CAutoGameSystemPerFrame +{ +public: + // Inherited from IAutoServerSystem + virtual void LevelInitPreEntity( void ) + { + m_bAllowEntityCreationInScripts = true; + VScriptServerInit(); + } + + virtual void LevelInitPostEntity( void ) + { + m_bAllowEntityCreationInScripts = false; + } + + virtual void LevelShutdownPostEntity( void ) + { + VScriptServerTerm(); + } + + virtual void FrameUpdatePostEntityThink() + { + if ( g_pScriptVM ) + g_pScriptVM->Frame( gpGlobals->frametime ); + } + + bool m_bAllowEntityCreationInScripts; +}; + +CVScriptGameSystem g_VScriptGameSystem; + +#ifdef MAPBASE_VSCRIPT +ConVar script_allow_entity_creation_midgame( "script_allow_entity_creation_midgame", "1", FCVAR_NOT_CONNECTED, "Allows VScript files to create entities mid-game, as opposed to only creating entities on startup." ); +#endif + +bool IsEntityCreationAllowedInScripts( void ) +{ +#ifdef MAPBASE_VSCRIPT + if (script_allow_entity_creation_midgame.GetBool()) + return true; +#endif + + return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; +} + +static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2; + + +//----------------------------------------------------------------------------- + +class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + CVScriptSaveRestoreBlockHandler() : + m_InstanceMap( DefLessFunc(const char *) ) + { + } + const char *GetBlockName() + { + return "VScriptServer"; + } + + //--------------------------------- + + void Save( ISave *pSave ) + { + pSave->StartBlock(); + + int temp = g_pScriptVM != NULL; + pSave->WriteInt( &temp ); + if ( g_pScriptVM ) + { + temp = g_pScriptVM->GetLanguage(); + pSave->WriteInt( &temp ); + CUtlBuffer buffer; + g_pScriptVM->WriteState( &buffer ); + temp = buffer.TellPut(); + pSave->WriteInt( &temp ); + if ( temp > 0 ) + { + pSave->WriteData( (const char *)buffer.Base(), temp ); + } + } + + pSave->EndBlock(); + } + + //--------------------------------- + + void WriteSaveHeaders( ISave *pSave ) + { + pSave->WriteShort( &VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void ReadRestoreHeaders( IRestore *pRestore ) + { + // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. + short version; + pRestore->ReadShort( &version ); + m_fDoLoad = ( version == VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void Restore( IRestore *pRestore, bool createPlayers ) + { + if ( !m_fDoLoad && g_pScriptVM ) + { + return; + } + CBaseEntity *pEnt = gEntList.FirstEnt(); + while ( pEnt ) + { + if ( pEnt->m_iszScriptId != NULL_STRING ) + { +#ifndef MAPBASE_VSCRIPT + g_pScriptVM->RegisterClass( pEnt->GetScriptDesc() ); +#endif + m_InstanceMap.Insert( STRING( pEnt->m_iszScriptId ), pEnt ); + } + pEnt = gEntList.NextEnt( pEnt ); + } + + pRestore->StartBlock(); + if ( pRestore->ReadInt() && pRestore->ReadInt() == g_pScriptVM->GetLanguage() ) + { + int nBytes = pRestore->ReadInt(); + if ( nBytes > 0 ) + { + CUtlBuffer buffer; + buffer.EnsureCapacity( nBytes ); + pRestore->ReadData( (char *)buffer.AccessForDirectRead( nBytes ), nBytes, 0 ); + g_pScriptVM->ReadState( &buffer ); + } + } + pRestore->EndBlock(); + } + + void PostRestore( void ) + { + for ( int i = m_InstanceMap.FirstInorder(); i != m_InstanceMap.InvalidIndex(); i = m_InstanceMap.NextInorder( i ) ) + { + CBaseEntity *pEnt = m_InstanceMap[i]; + if ( pEnt->m_hScriptInstance ) + { + ScriptVariant_t variant; + if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) + { + pEnt->m_ScriptScope.Init( variant.m_hScript, false ); + pEnt->RunPrecacheScripts(); + } + } + else + { + // Script system probably has no internal references + pEnt->m_iszScriptId = NULL_STRING; + } + } + m_InstanceMap.Purge(); + } + + + CUtlMap m_InstanceMap; + +private: + bool m_fDoLoad; +}; + +//----------------------------------------------------------------------------- + +CVScriptSaveRestoreBlockHandler g_VScriptSaveRestoreBlockHandler; + +//------------------------------------- + +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler() +{ + return &g_VScriptSaveRestoreBlockHandler; +} + +//----------------------------------------------------------------------------- + +bool CBaseEntityScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + CBaseEntity *pEntity = (CBaseEntity *)p; + if ( pEntity->GetEntityName() != NULL_STRING ) + { + V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), STRING(pEntity->m_iClassname), STRING( pEntity->GetEntityName() ) ); + } + else + { + V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), STRING(pEntity->m_iClassname) ); + } + return true; +} + +void *CBaseEntityScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) +{ + int iEntity = g_VScriptSaveRestoreBlockHandler.m_InstanceMap.Find( pszId ); + if ( iEntity != g_VScriptSaveRestoreBlockHandler.m_InstanceMap.InvalidIndex() ) + { + CBaseEntity *pEnt = g_VScriptSaveRestoreBlockHandler.m_InstanceMap[iEntity]; + pEnt->m_hScriptInstance = hInstance; + return pEnt; + } + return NULL; +} + + +CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + + diff --git a/mp/src/game/server/vscript_server.h b/mp/src/game/server/vscript_server.h new file mode 100644 index 00000000..6a7d44d1 --- /dev/null +++ b/mp/src/game/server/vscript_server.h @@ -0,0 +1,59 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SERVER_H +#define VSCRIPT_SERVER_H + +#include "vscript/ivscript.h" +#include "tier1/KeyValues.h" +#include "vscript_shared.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +class ISaveRestoreBlockHandler; + +bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler(); + + +class CBaseEntityScriptInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); +}; + +extern CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#ifndef MAPBASE_VSCRIPT // Mapbase adds this to the base library so that CScriptKeyValues can be accessed anywhere, like VBSP. +// ---------------------------------------------------------------------------- +// KeyValues access +// ---------------------------------------------------------------------------- +class CScriptKeyValues +{ +public: + CScriptKeyValues( KeyValues *pKeyValues ); + ~CScriptKeyValues( ); + + HSCRIPT ScriptFindKey( const char *pszName ); + HSCRIPT ScriptGetFirstSubKey( void ); + HSCRIPT ScriptGetNextKey( void ); + int ScriptGetKeyValueInt( const char *pszName ); + float ScriptGetKeyValueFloat( const char *pszName ); + const char *ScriptGetKeyValueString( const char *pszName ); + bool ScriptIsKeyValueEmpty( const char *pszName ); + bool ScriptGetKeyValueBool( const char *pszName ); + void ScriptReleaseKeyValues( ); + + KeyValues *m_pKeyValues; // actual KeyValue entity +}; +#endif + +#endif // VSCRIPT_SERVER_H diff --git a/mp/src/game/server/vscript_server.nut b/mp/src/game/server/vscript_server.nut new file mode 100644 index 00000000..17ba64d3 --- /dev/null +++ b/mp/src/game/server/vscript_server.nut @@ -0,0 +1,171 @@ +static char g_Script_vscript_server[] = R"vscript( +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +function UniqueString( string = "" ) +{ + return ::DoUniqueString( string.tostring() ); +} + +local DoEntFire = ::DoEntFire +local DoEntFireByInstanceHandle = ::DoEntFireByInstanceHandle + +function EntFire( target, action, value = null, delay = 0.0, activator = null, caller = null ) +{ + if ( !value ) + { + value = ""; + } + + if ( "self" in this ) + { + if ( !caller ) + { + caller = self; + } + + if ( !activator ) + { + activator = self; + } + } + + return DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); +} + +function EntFireByHandle( target, action, value = null, delay = 0.0, activator = null, caller = null ) +{ + if ( !value ) + { + value = ""; + } + + if ( "self" in this ) + { + if ( !caller ) + { + caller = self; + } + + if ( !activator ) + { + activator = self; + } + } + + return DoEntFireByInstanceHandle( target, action.tostring(), value.tostring(), delay, activator, caller ); +} + +function DispatchParticleEffect( particleName, origin, angles, entity = null ) +{ + DoDispatchParticleEffect( particleName, origin, angles, entity ); +} + +// CConvars is declared within the library +function CConvars::GetClientConvarValue(cvar,idx) +{ + return ::ScriptGetClientConvarValue(cvar,idx); +} + +RegisterHelp( "CConvars::GetClientConvarValue", "CConvars::GetClientConvarValue(string, int)", "Returns the convar value for the entindex as a string. Only works with client convars with the FCVAR_USERINFO flag." ); + +function __ReplaceClosures( script, scope ) +{ + if ( !scope ) + { + scope = getroottable(); + } + + local tempParent = { getroottable = function() { return null; } }; + local temp = { runscript = script }; + temp.set_delegate(tempParent); + + temp.runscript() + foreach( key,val in temp ) + { + if ( typeof(val) == "function" && key != "runscript" ) + { + printl( " Replacing " + key ); + scope[key] <- val; + } + } +} + +local __OutputsPattern = regexp("^On.*Output$"); + +function ConnectOutputs( table ) +{ + local nCharsToStrip = 6; + foreach( key, val in table ) + { + if ( typeof( val ) == "function" && __OutputsPattern.match( key ) ) + { + //printl(key.slice( 0, nCharsToStrip ) ); + table.self.ConnectOutput( key.slice( 0, key.len() - nCharsToStrip ), key ); + } + } +} + +function IncludeScript( name, scope = null ) +{ + if ( !scope ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +//--------------------------------------------------------- +// Text dump this scope's contents to the console. +//--------------------------------------------------------- +function __DumpScope( depth, table ) +{ + local indent=function( count ) + { + local i; + for( i = 0 ; i < count ; i++ ) + { + print(" "); + } + } + + foreach(key, value in table) + { + indent(depth); + print( key ); + switch (type(value)) + { + case "table": + print("(TABLE)\n"); + indent(depth); + print("{\n"); + __DumpScope( depth + 1, value); + indent(depth); + print("}"); + break; + case "array": + print("(ARRAY)\n"); + indent(depth); + print("[\n") + __DumpScope( depth + 1, value); + indent(depth); + print("]"); + break; + case "string": + print(" = \""); + print(value); + print("\""); + break; + default: + print(" = "); + print(value); + break; + } + print("\n"); + } +} + +)vscript"; \ No newline at end of file diff --git a/mp/src/game/server/wcedit.cpp b/mp/src/game/server/wcedit.cpp index 43307dfc..5a454e6b 100644 --- a/mp/src/game/server/wcedit.cpp +++ b/mp/src/game/server/wcedit.cpp @@ -450,6 +450,19 @@ Vector *g_EntityPositions = NULL; QAngle *g_EntityOrientations = NULL; string_t *g_EntityClassnames = NULL; +#ifdef MAPBASE // VDC Memory Leak Fixes +class GlobalCleanUp : public CAutoGameSystem +{ + void Shutdown() + { + delete [] g_EntityPositions; + delete [] g_EntityOrientations; + delete [] g_EntityClassnames; + delete this; + } +}; +#endif + //----------------------------------------------------------------------------- // Purpose: Saves the entity's position for future communication with Hammer //----------------------------------------------------------------------------- @@ -460,6 +473,9 @@ void NWCEdit::RememberEntityPosition( CBaseEntity *pEntity ) if ( !g_EntityPositions ) { +#ifdef MAPBASE // VDC Memory Leak Fixes + new GlobalCleanUp(); +#endif g_EntityPositions = new Vector[NUM_ENT_ENTRIES]; g_EntityOrientations = new QAngle[NUM_ENT_ENTRIES]; // have to save these too because some entities change the classname on spawn (e.g. prop_physics_override, physics_prop) diff --git a/mp/src/game/server/world.cpp b/mp/src/game/server/world.cpp index 55c1e7b5..628a79bb 100644 --- a/mp/src/game/server/world.cpp +++ b/mp/src/game/server/world.cpp @@ -376,6 +376,9 @@ BEGIN_DATADESC( CWorld ) // keyvalues are parsed from map, but not saved/loaded DEFINE_KEYFIELD( m_iszChapterTitle, FIELD_STRING, "chaptertitle" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bChapterTitleNoMessage, FIELD_BOOLEAN, "chaptertitlenomessage" ), +#endif DEFINE_KEYFIELD( m_bStartDark, FIELD_BOOLEAN, "startdark" ), DEFINE_KEYFIELD( m_bDisplayTitle, FIELD_BOOLEAN, "gametitle" ), DEFINE_FIELD( m_WorldMins, FIELD_VECTOR ), @@ -390,8 +393,16 @@ BEGIN_DATADESC( CWorld ) DEFINE_KEYFIELD( m_flMaxPropScreenSpaceWidth, FIELD_FLOAT, "maxpropscreenwidth" ), DEFINE_KEYFIELD( m_flMinPropScreenSpaceWidth, FIELD_FLOAT, "minpropscreenwidth" ), DEFINE_KEYFIELD( m_iszDetailSpriteMaterial, FIELD_STRING, "detailmaterial" ), +#ifdef MAPBASE_VSCRIPT + DEFINE_KEYFIELD( m_iScriptLanguage, FIELD_INTEGER, "vscriptlanguage" ), + DEFINE_KEYFIELD( m_iScriptLanguageClient, FIELD_INTEGER, "vscriptlanguage_client" ), +#endif DEFINE_KEYFIELD( m_bColdWorld, FIELD_BOOLEAN, "coldworld" ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetChapterTitle", InputSetChapterTitle ), +#endif + END_DATADESC() @@ -407,6 +418,12 @@ IMPLEMENT_SERVERCLASS_ST(CWorld, DT_WORLD) SendPropFloat (SENDINFO(m_flMinPropScreenSpaceWidth), 0, SPROP_NOSCALE ), SendPropStringT (SENDINFO(m_iszDetailSpriteMaterial) ), SendPropInt (SENDINFO(m_bColdWorld), 1, SPROP_UNSIGNED ), +#ifdef MAPBASE + SendPropStringT (SENDINFO(m_iszChapterTitle) ), +#endif +#ifdef MAPBASE_VSCRIPT + SendPropInt (SENDINFO(m_iScriptLanguageClient), 4 ), // No SPROP_UNSIGNED to allow -1 (disabled) +#endif END_SEND_TABLE() // @@ -449,7 +466,7 @@ bool CWorld::KeyValue( const char *szKeyName, const char *szValue ) extern bool g_fGameOver; -static CWorld *g_WorldEntity = NULL; +CWorld *g_WorldEntity = NULL; CWorld* GetWorldEntity() { @@ -466,6 +483,11 @@ CWorld::CWorld( ) SetSolid( SOLID_BSP ); SetMoveType( MOVETYPE_NONE ); +#ifdef MAPBASE_VSCRIPT + m_iScriptLanguage = SL_NONE; + m_iScriptLanguageClient = -2; +#endif + m_bColdWorld = false; } @@ -530,6 +552,14 @@ void CWorld::Spawn( void ) Precache( ); GlobalEntity_Add( "is_console", STRING(gpGlobals->mapname), ( IsConsole() ) ? GLOBAL_ON : GLOBAL_OFF ); GlobalEntity_Add( "is_pc", STRING(gpGlobals->mapname), ( !IsConsole() ) ? GLOBAL_ON : GLOBAL_OFF ); + +#ifdef MAPBASE_VSCRIPT + if (m_iScriptLanguageClient.Get() == -2) + { + // Clientside language should be regular language by default + m_iScriptLanguageClient.Set( m_iScriptLanguage ); + } +#endif } static const char *g_DefaultLightstyles[] = @@ -678,6 +708,23 @@ void CWorld::Precache( void ) // Call all registered precachers. CPrecacheRegister::Precache(); +#ifdef MAPBASE + if ( m_iszChapterTitle.Get() != NULL_STRING && !m_bChapterTitleNoMessage ) + { + DevMsg( 2, "Chapter title: %s\n", STRING(m_iszChapterTitle.Get()) ); + CMessage *pMessage = (CMessage *)CBaseEntity::Create( "env_message", vec3_origin, vec3_angle, NULL ); + if ( pMessage ) + { + pMessage->SetMessage( m_iszChapterTitle.Get() ); + m_iszChapterTitle.Set( NULL_STRING ); + + // send the message entity a play message command, delayed by 1 second + pMessage->AddSpawnFlags( SF_MESSAGE_ONCE ); + pMessage->SetThink( &CMessage::SUB_CallUseToggle ); + pMessage->SetNextThink( gpGlobals->curtime + 1.0f ); + } + } +#else if ( m_iszChapterTitle != NULL_STRING ) { DevMsg( 2, "Chapter title: %s\n", STRING(m_iszChapterTitle) ); @@ -693,6 +740,7 @@ void CWorld::Precache( void ) pMessage->SetNextThink( gpGlobals->curtime + 1.0f ); } } +#endif g_iszFuncBrushClassname = AllocPooledString("func_brush"); } @@ -731,3 +779,10 @@ bool CWorld::IsColdWorld( void ) { return m_bColdWorld; } + +#ifdef MAPBASE +void CWorld::InputSetChapterTitle( inputdata_t &inputdata ) +{ + m_iszChapterTitle.Set( inputdata.value.StringID() ); +} +#endif diff --git a/mp/src/game/server/world.h b/mp/src/game/server/world.h index a8547d3c..7e949450 100644 --- a/mp/src/game/server/world.h +++ b/mp/src/game/server/world.h @@ -52,10 +52,32 @@ public: bool IsColdWorld( void ); +#ifdef MAPBASE + inline const char *GetChapterTitle() + { + return STRING(m_iszChapterTitle.Get()); + } + + void InputSetChapterTitle( inputdata_t &inputdata ); +#endif + +#ifdef MAPBASE_VSCRIPT + ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)(m_iScriptLanguage); } +#endif + private: DECLARE_DATADESC(); +#ifdef MAPBASE + // Now needs to show up on the client for RPC + CNetworkVar( string_t, m_iszChapterTitle ); + + // Suppresses m_iszChapterTitle's env_message creation, + // allowing it to only be used for saves and RPC + bool m_bChapterTitleNoMessage; +#else string_t m_iszChapterTitle; +#endif CNetworkVar( float, m_flWaveHeight ); CNetworkVector( m_WorldMins ); @@ -66,6 +88,11 @@ private: CNetworkVar( float, m_flMaxPropScreenSpaceWidth ); CNetworkVar( string_t, m_iszDetailSpriteMaterial ); +#ifdef MAPBASE_VSCRIPT + int m_iScriptLanguage; + CNetworkVar( int, m_iScriptLanguageClient ); +#endif + // start flags CNetworkVar( bool, m_bStartDark ); CNetworkVar( bool, m_bColdWorld ); diff --git a/mp/src/game/shared/Multiplayer/multiplayer_animstate.h b/mp/src/game/shared/Multiplayer/multiplayer_animstate.h index a3c7937a..5f70738d 100644 --- a/mp/src/game/shared/Multiplayer/multiplayer_animstate.h +++ b/mp/src/game/shared/Multiplayer/multiplayer_animstate.h @@ -297,7 +297,6 @@ protected: // Pose parameters. bool m_bPoseParameterInit; MultiPlayerPoseData_t m_PoseParameterData; - DebugPlayerAnimData_t m_DebugAnimData; bool m_bCurrentFeetYawInitialized; float m_flLastAnimationStateClearTime; @@ -342,6 +341,14 @@ protected: // movement playback options int m_nMovementSequence; LegAnimType_t m_LegAnimType; + + //Tony; moved debuganim data to a private block and made the 2 sdk animstates friendly. I override the base classes + //but want complete functionality. +private: + friend class CSDKPlayerAnimState; + friend class CHL2MPPlayerAnimState; + DebugPlayerAnimData_t m_DebugAnimData; + }; // If this is set, then the game code needs to make sure to send player animation events diff --git a/mp/src/game/shared/SoundEmitterSystem.cpp b/mp/src/game/shared/SoundEmitterSystem.cpp index 39aca858..14bb9b8c 100644 --- a/mp/src/game/shared/SoundEmitterSystem.cpp +++ b/mp/src/game/shared/SoundEmitterSystem.cpp @@ -130,6 +130,10 @@ void Hack_FixEscapeChars( char *str ) Q_strncpy( str, osave, len ); } +#ifdef MAPBASE +static const ConVar *pHostTimescale; +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -323,6 +327,10 @@ public: } } #endif + +#ifdef MAPBASE + pHostTimescale = cvar->FindVar( "host_timescale" ); +#endif } virtual void LevelInitPostEntity() @@ -534,7 +542,11 @@ public: params.volume, (soundlevel_t)params.soundlevel, ep.m_nFlags, +#ifdef MAPBASE + pHostTimescale->GetFloat() != 0.0f ? params.pitch * pHostTimescale->GetFloat() : params.pitch, +#else params.pitch, +#endif ep.m_nSpecialDSP, ep.m_pOrigin, NULL, @@ -613,7 +625,11 @@ public: ep.m_flVolume, ep.m_SoundLevel, ep.m_nFlags, +#ifdef MAPBASE + pHostTimescale->GetFloat() != 0.0f ? ep.m_nPitch * pHostTimescale->GetFloat() : ep.m_nPitch, +#else ep.m_nPitch, +#endif ep.m_nSpecialDSP, ep.m_pOrigin, NULL, @@ -835,6 +851,13 @@ public: params.volume = flVolume; } +#ifdef MAPBASE + if ( pHostTimescale->GetFloat() != 0.0f ) + { + params.pitch *= pHostTimescale->GetFloat(); + } +#endif + #if defined( CLIENT_DLL ) enginesound->EmitAmbientSound( params.soundname, params.volume, params.pitch, iFlags, soundtime ); #else @@ -963,6 +986,13 @@ public: if ( pSample && ( Q_stristr( pSample, ".wav" ) || Q_stristr( pSample, ".mp3" )) ) { +#ifdef MAPBASE + if ( pHostTimescale->GetFloat() != 0.0f ) + { + pitch *= pHostTimescale->GetFloat(); + } +#endif + #if defined( CLIENT_DLL ) enginesound->EmitAmbientSound( pSample, volume, pitch, flags, soundtime ); #else @@ -1193,6 +1223,19 @@ void CBaseEntity::EmitSound( const char *soundname, HSOUNDSCRIPTHANDLE& handle, EmitSound( filter, entindex(), params, handle ); } +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) +void CBaseEntity::ScriptEmitSound( const char *soundname ) +{ + EmitSound( soundname ); +} + +float CBaseEntity::ScriptSoundDuration( const char *soundname, const char *actormodel ) +{ + float duration = CBaseEntity::GetSoundDuration( soundname, actormodel ); + return duration; +} +#endif // !CLIENT + //----------------------------------------------------------------------------- // Purpose: // Input : filter - @@ -1426,7 +1469,17 @@ static const char *UTIL_TranslateSoundName( const char *soundname, const char *a void CBaseEntity::GenderExpandString( char const *in, char *out, int maxlen ) { +#ifdef MAPBASE + // This is so models without globalactors.txt declarations can still play scenes. + gender_t gender = soundemitterbase->GetActorGender(STRING(GetModelName())); + + if (gender == GENDER_NONE) + gender = GENDER_MALE; + + soundemitterbase->GenderExpandString(gender, in, out, maxlen); +#else soundemitterbase->GenderExpandString( STRING( GetModelName() ), in, out, maxlen ); +#endif } bool CBaseEntity::GetParametersForSound( const char *soundname, CSoundParameters ¶ms, const char *actormodel ) @@ -1452,6 +1505,14 @@ HSOUNDSCRIPTHANDLE CBaseEntity::PrecacheScriptSound( const char *soundname ) #endif } +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) +// Same as server version of above, but signiture changed so it can be deduced by the macros +void CBaseEntity::VScriptPrecacheScriptSound(const char* soundname) +{ + g_SoundEmitterSystem.PrecacheScriptSound(soundname); +} +#endif // !CLIENT_DLL + void CBaseEntity::PrefetchScriptSound( const char *soundname ) { g_SoundEmitterSystem.PrefetchScriptSound( soundname ); diff --git a/mp/src/game/shared/achievementmgr.cpp b/mp/src/game/shared/achievementmgr.cpp index ffc95d46..7b4727ef 100644 --- a/mp/src/game/shared/achievementmgr.cpp +++ b/mp/src/game/shared/achievementmgr.cpp @@ -1537,7 +1537,11 @@ void CAchievementMgr::OnKillEvent( CBaseEntity *pVictim, CBaseEntity *pAttacker, } CBaseCombatCharacter *pBCC = dynamic_cast( pVictim ); +#ifdef MAPBASE + if ( pBCC && ( D_FR >= pBCC->IRelationType( pLocalPlayer ) ) ) +#else if ( pBCC && ( D_HT == pBCC->IRelationType( pLocalPlayer ) ) ) +#endif { bVictimIsPlayerEnemy = true; } @@ -1647,6 +1651,13 @@ void CAchievementMgr::OnMapEvent( const char *pchEventName ) CBaseAchievement *pAchievement = m_vecMapEventListeners[iAchievement]; pAchievement->OnMapEvent( pchEventName ); } + +#ifdef MAPBASE + if (cc_achievement_debug.GetBool()) + { + Msg( "CAchievementMgr::OnMapEvent: Achievement \"%s\" not found\n", pchEventName ); + } +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/activitylist.cpp b/mp/src/game/shared/activitylist.cpp index 49e93e93..b22edfc1 100644 --- a/mp/src/game/shared/activitylist.cpp +++ b/mp/src/game/shared/activitylist.cpp @@ -2351,6 +2351,50 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_MELEE_VM_INSPECT_IDLE ); REGISTER_SHARED_ACTIVITY( ACT_MELEE_VM_INSPECT_END ); +#if AR2_ACTIVITY_FIX == 1 + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2 ); + //REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); + + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR2 ); +#endif + +#ifdef SHARED_COMBINE_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_COMBINE_THROW_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ); + + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_ADVANCE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_FORWARD ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_GROUP ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_HALT ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_LEFT ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_RIGHT ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_TAKECOVER ); +#endif + +#ifdef COMPANION_HOLSTER_WORKAROUND + REGISTER_SHARED_ACTIVITY( ACT_ARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_RIFLE ); +#endif + AssertMsg( g_HighestActivity == LAST_SHARED_ACTIVITY - 1, "Not all activities from ai_activity.h registered in activitylist.cpp" ); } diff --git a/mp/src/game/shared/ai_activity.h b/mp/src/game/shared/ai_activity.h index 0f5bba44..a6dddd16 100644 --- a/mp/src/game/shared/ai_activity.h +++ b/mp/src/game/shared/ai_activity.h @@ -11,6 +11,46 @@ #pragma once #endif +#ifdef MAPBASE + +// Mapbase adds a few shared activities. +// +// These used to be placed in between existing activities, as outside of the code activities are based off of strings. +// This seemed like a bad idea, but no problems arose at the time. +// I later discovered that apparently some things in MP use the direct integers instead of the enum names. +// I retroactively put all custom activities at the bottom of the enum instead. +// Their placements in activitylist.cpp and ai_activity.cpp have not been changed. + +// AR2 ACTIVITY FIX +// You know all of those AR2 activities that citizens and combine soldiers have? +// Yeah, those are unused. It appears Valve forgot to implement them. +// +// What could be 20-40 different animations on two different characters are not even defined in code. +// I didn't even realize they were unused until I saw ACT_RELOAD_AR2, so I don't blame them for never realizing this. +// They work surprisingly well for probably never being tested in-game. +// +// 1 = Add activities directly +// 2 = Add activities as custom activities (todo) +// +// 2 should only be preferable if adding them like this breaks something. +#define AR2_ACTIVITY_FIX 1 + +// COMPANION HOLSTER WORKAROUND +// I introduced a separate holster/unholster animation to male_shared +// and female_shared and I realized it might conflict with Alyx's animation. +// +// I came up with a solution--ACT_ARM_RIFLE and its disarm counterpart--to solve it. +// I didn't think about the fact I could've named them the same as Alyx's so her animations would overwrite it... +// ...so this has been deactivated. +//#define COMPANION_HOLSTER_WORKAROUND 1 + +// SHARED COMBINE ACTIVITIES +// This turns ACT_COMBINE_AR2_ALTFIRE and ACT_COMBINE_THROW_GRENADE into shared activities. +// This is necessary so other NPCs to use them without having to rely on a bunch of custom activities. +#define SHARED_COMBINE_ACTIVITIES 1 + +#endif + #define ACTIVITY_NOT_AVAILABLE -1 typedef enum @@ -2188,6 +2228,52 @@ typedef enum ACT_MELEE_VM_INSPECT_IDLE, ACT_MELEE_VM_INSPECT_END, +#if AR2_ACTIVITY_FIX == 1 + ACT_IDLE_AR2, + ACT_IDLE_ANGRY_AR2, + + ACT_IDLE_AR2_RELAXED, + ACT_IDLE_AR2_STIMULATED, + + ACT_WALK_AR2_RELAXED, + ACT_RUN_AR2_RELAXED, + ACT_WALK_AR2_STIMULATED, + ACT_RUN_AR2_STIMULATED, + + ACT_IDLE_AIM_AR2_STIMULATED, + ACT_WALK_AIM_AR2_STIMULATED, + ACT_RUN_AIM_AR2_STIMULATED, + + ACT_WALK_AR2, + ACT_WALK_AIM_AR2, + ACT_RUN_AR2, + ACT_RUN_AIM_AR2, + + ACT_RELOAD_AR2, + //ACT_RELOAD_AR2_LOW, + + ACT_GESTURE_RELOAD_AR2, +#endif + +#ifdef SHARED_COMBINE_ACTIVITIES + ACT_COMBINE_THROW_GRENADE, + ACT_COMBINE_AR2_ALTFIRE, + + // New gesture-based signals as activities for people who want to use them + ACT_GESTURE_SIGNAL_ADVANCE, + ACT_GESTURE_SIGNAL_FORWARD, + ACT_GESTURE_SIGNAL_GROUP, + ACT_GESTURE_SIGNAL_HALT, + ACT_GESTURE_SIGNAL_LEFT, + ACT_GESTURE_SIGNAL_RIGHT, + ACT_GESTURE_SIGNAL_TAKECOVER, +#endif + +#ifdef COMPANION_HOLSTER_WORKAROUND + ACT_ARM_RIFLE, + ACT_DISARM_RIFLE, +#endif + // this is the end of the global activities, private per-monster activities start here. LAST_SHARED_ACTIVITY, } Activity; diff --git a/mp/src/game/shared/ammodef.cpp b/mp/src/game/shared/ammodef.cpp index 257e8569..cf3ad3e8 100644 --- a/mp/src/game/shared/ammodef.cpp +++ b/mp/src/game/shared/ammodef.cpp @@ -24,6 +24,21 @@ Ammo_t *CAmmoDef::GetAmmoOfIndex(int nAmmoIndex) return &m_AmmoType[ nAmmoIndex ]; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +const char* CAmmoDef::Name(int nAmmoIndex) +{ + if ( nAmmoIndex < 1 || nAmmoIndex >= m_nAmmoIndex ) + return NULL; + + return m_AmmoType[nAmmoIndex].pName; +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : @@ -292,4 +307,24 @@ CAmmoDef::~CAmmoDef( void ) } } +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAmmoDef, SCRIPT_SINGLETON "The ammo type definition manager." ) + + DEFINE_SCRIPTFUNC( Name, "Gets the name of the specified ammo type index." ) + DEFINE_SCRIPTFUNC( Index, "Gets the index of the specified ammo type name." ) + DEFINE_SCRIPTFUNC( PlrDamage, "Gets the damage players deal for the specified ammo type." ) + DEFINE_SCRIPTFUNC( NPCDamage, "Gets the damage NPCs deal for the specified ammo type." ) + DEFINE_SCRIPTFUNC( MaxCarry, "Gets the maximum amount of this ammo type which players should be able to carry." ) + DEFINE_SCRIPTFUNC( DamageType, "Gets the type of damage this ammo type deals." ) + DEFINE_SCRIPTFUNC( TracerType, "Gets the type of tracer this ammo type uses." ) + DEFINE_SCRIPTFUNC( DamageForce, "Gets the amount of force this ammo type deals." ) + DEFINE_SCRIPTFUNC( MinSplashSize, "Gets the minimum size of water splashes caused by impacts from this ammo type." ) + DEFINE_SCRIPTFUNC( MaxSplashSize, "Gets the maximum size of water splashes caused by impacts from this ammo type." ) + DEFINE_SCRIPTFUNC( Flags, "Gets the flags this ammo type uses." ) + + DEFINE_SCRIPTFUNC( GetNumAmmoTypes, "Gets the number of ammo types which currently exist." ) + +END_SCRIPTDESC(); +#endif + diff --git a/mp/src/game/shared/ammodef.h b/mp/src/game/shared/ammodef.h index 71c1707f..d5607e02 100644 --- a/mp/src/game/shared/ammodef.h +++ b/mp/src/game/shared/ammodef.h @@ -72,6 +72,9 @@ public: Ammo_t m_AmmoType[MAX_AMMO_TYPES]; Ammo_t *GetAmmoOfIndex(int nAmmoIndex); +#ifdef MAPBASE + const char* Name(int nAmmoIndex); +#endif int Index(const char *psz); int PlrDamage(int nAmmoIndex); int NPCDamage(int nAmmoIndex); @@ -91,6 +94,11 @@ public: private: bool AddAmmoType(char const* name, int damageType, int tracerType, int nFlags, int minSplashSize, int maxSplashSize ); + +#ifdef MAPBASE_VSCRIPT + ALLOW_SCRIPT_ACCESS(); + int GetNumAmmoTypes() { return m_nAmmoIndex; } +#endif }; diff --git a/mp/src/game/shared/basecombatcharacter_shared.cpp b/mp/src/game/shared/basecombatcharacter_shared.cpp index 32e823fa..d937e59a 100644 --- a/mp/src/game/shared/basecombatcharacter_shared.cpp +++ b/mp/src/game/shared/basecombatcharacter_shared.cpp @@ -82,7 +82,11 @@ bool CBaseCombatCharacter::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) return false; } +#ifdef MAPBASE + if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) && !pWeapon->HasSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY) ) +#else if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) ) +#endif return false; if ( !pWeapon->CanDeploy() ) @@ -223,6 +227,16 @@ void CBaseCombatCharacter::SetBloodColor( int nBloodColor ) m_bloodColor = nBloodColor; } +#if defined(MAPBASE) && defined(GAME_DLL) +//----------------------------------------------------------------------------- +// Purpose: Sets blood color +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputSetBloodColor( inputdata_t &inputdata ) +{ + SetBloodColor(inputdata.value.Int()); +} +#endif + //----------------------------------------------------------------------------- /** The main visibility check. Checks all the entity specific reasons that could diff --git a/mp/src/game/shared/basecombatweapon_shared.cpp b/mp/src/game/shared/basecombatweapon_shared.cpp index 616dcf47..9785f926 100644 --- a/mp/src/game/shared/basecombatweapon_shared.cpp +++ b/mp/src/game/shared/basecombatweapon_shared.cpp @@ -192,6 +192,10 @@ void CBaseCombatWeapon::Spawn( void ) // Assume m_nViewModelIndex = 0; +#ifdef MAPBASE + // Don't reset to default ammo if we're supposed to use the keyvalue + if (!HasSpawnFlags( SF_WEAPON_PRESERVE_AMMO )) +#endif GiveDefaultAmmo(); if ( GetWorldModel() ) @@ -252,7 +256,13 @@ void CBaseCombatWeapon::Precache( void ) // Add this weapon to the weapon registry, and get our index into it // Get weapon data from script file +#ifdef MAPBASE + // Allow custom scripts to be loaded on a map-by-map basis + if ( ReadCustomWeaponDataFromFileForSlot( filesystem, GetWeaponScriptName(), &m_hWeaponFileInfo, GetEncryptionKey() ) || + ReadWeaponDataFromFileForSlot( filesystem, GetWeaponScriptName(), &m_hWeaponFileInfo, GetEncryptionKey() ) ) +#else if ( ReadWeaponDataFromFileForSlot( filesystem, GetClassname(), &m_hWeaponFileInfo, GetEncryptionKey() ) ) +#endif { // Get the ammo indexes for the ammo's specified in the data file if ( GetWpnData().szAmmo1[0] ) @@ -314,6 +324,64 @@ void CBaseCombatWeapon::Precache( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets ammo based on mapper value +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::SetAmmoFromMapper( float flAmmo, bool bSecondary ) +{ + int iFinalAmmo; + if (flAmmo > 0.0f && flAmmo < 1.0f) + { + // Ratio from max ammo + iFinalAmmo = ((float)(!bSecondary ? GetMaxClip1() : GetMaxClip2()) * flAmmo); + } + else + { + // Actual ammo value + iFinalAmmo = (int)flAmmo; + } + + !bSecondary ? + m_iClip1 = iFinalAmmo : + m_iClip2 = iFinalAmmo; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CBaseCombatWeapon::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq(szKeyName, "SetAmmo1") ) + { + SetAmmoFromMapper(atof(szValue)); + } + if ( FStrEq(szKeyName, "SetAmmo2") ) + { + SetAmmoFromMapper(atof(szValue), true); + } + else if ( FStrEq(szKeyName, "spawnflags") ) + { + m_spawnflags = atoi(szValue); +#ifndef CLIENT_DLL + // Some spawnflags have to be on the client right now + if (m_spawnflags != 0) + DispatchUpdateTransmitState(); +#endif + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CBaseCombatWeapon::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + return BaseClass::GetKeyValue(szKeyName, szValue, iMaxLen); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Get my data in the file weapon info array //----------------------------------------------------------------------------- @@ -715,6 +783,12 @@ void CBaseCombatWeapon::Drop( const Vector &vecVelocity ) SetOwnerEntity( NULL ); SetOwner( NULL ); +#ifdef MAPBASE + m_bInReload = false; + + m_OnDropped.FireOutput(pOwner, this); +#endif + // If we're not allowing to spawn due to the gamerules, // remove myself when I'm dropped by an NPC. if ( pOwner && pOwner->IsNPC() ) @@ -760,6 +834,10 @@ void CBaseCombatWeapon::OnPickedUp( CBaseCombatCharacter *pNewOwner ) // Robin: We don't want to delete weapons the player has picked up, so // clear the name of the weapon. This prevents wildcards that are meant // to find NPCs finding weapons dropped by the NPCs as well. +#ifdef MAPBASE + // Level designers might want some weapons to preserve their original names, however. + if ( !HasSpawnFlags(SF_WEAPON_PRESERVE_NAME) ) +#endif SetName( NULL_STRING ); } else @@ -971,6 +1049,64 @@ void CBaseCombatWeapon::SetPickupTouch( void ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +WeaponClass_t CBaseCombatWeapon::WeaponClassify() +{ + // For now, check how we map our "angry idle" activity. + // The function is virtual, so derived weapons can override this. + Activity idleact = ActivityOverride(ACT_IDLE_ANGRY, NULL); + switch (idleact) + { + case ACT_IDLE_ANGRY_PISTOL: return WEPCLASS_HANDGUN; + case ACT_IDLE_ANGRY_SMG1: + case ACT_IDLE_ANGRY_AR2: return WEPCLASS_RIFLE; + case ACT_IDLE_ANGRY_SHOTGUN: return WEPCLASS_SHOTGUN; + case ACT_IDLE_ANGRY_RPG: return WEPCLASS_HEAVY; + + case ACT_IDLE_ANGRY_MELEE: return WEPCLASS_MELEE; + } + return WEPCLASS_INVALID; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str) +{ + if (FStrEq(str, "WEPCLASS_HANDGUN")) + return WEPCLASS_HANDGUN; + else if (FStrEq(str, "WEPCLASS_RIFLE")) + return WEPCLASS_RIFLE; + else if (FStrEq(str, "WEPCLASS_SHOTGUN")) + return WEPCLASS_SHOTGUN; + else if (FStrEq(str, "WEPCLASS_HEAY")) + return WEPCLASS_HEAVY; + + else if (FStrEq(str, "WEPCLASS_MELEE")) + return WEPCLASS_MELEE; + + return WEPCLASS_INVALID; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseCombatWeapon::SupportsBackupActivity(Activity activity) +{ + // Derived classes should override this. + + // Pistol and melee users should not use SMG animations for missing pistol activities. + if (WeaponClassify() == WEPCLASS_HANDGUN || IsMeleeWeapon()) + return false; + + return true; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Become a child of the owner (MOVETYPE_FOLLOW) // disables collisions, touch functions, thinking @@ -997,6 +1133,12 @@ void CBaseCombatWeapon::Equip( CBaseCombatCharacter *pOwner ) } #endif +#ifdef MAPBASE + // Ammo may be overridden to 0, in which case we shouldn't autoswitch + if (m_iClip1 <= 0 && m_iClip2 <= 0) + AddSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY); +#endif + m_flNextPrimaryAttack = gpGlobals->curtime; m_flNextSecondaryAttack = gpGlobals->curtime; @@ -1365,7 +1507,12 @@ bool CBaseCombatWeapon::ReloadOrSwitchWeapons( void ) if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime && m_flNextSecondaryAttack < gpGlobals->curtime ) { // weapon isn't useable, switch. +#ifdef MAPBASE + // Ammo might be overridden to 0, in which case we shouldn't do this + if ( ( (GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) == false ) && !HasSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY) && ( g_pGameRules->SwitchToNextBestWeapon( pOwner, this ) ) ) +#else if ( ( (GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) == false ) && ( g_pGameRules->SwitchToNextBestWeapon( pOwner, this ) ) ) +#endif { m_flNextPrimaryAttack = gpGlobals->curtime + 0.3; return true; @@ -1519,6 +1666,11 @@ bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) // reset pose parameters PoseParameterOverride( true ); +#ifdef MAPBASE + if (HasSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY)) + RemoveSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY); +#endif + return true; } @@ -1538,6 +1690,134 @@ bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) } #else + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputSetAmmo1( inputdata_t &inputdata ) +{ + SetAmmoFromMapper(inputdata.value.Float()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputSetAmmo2( inputdata_t &inputdata ) +{ + SetAmmoFromMapper(inputdata.value.Float(), true); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputGiveDefaultAmmo( inputdata_t &inputdata ) +{ + GiveDefaultAmmo(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputEnablePlayerPickup( inputdata_t &inputdata ) +{ + RemoveSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputDisablePlayerPickup( inputdata_t &inputdata ) +{ + AddSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputEnableNPCPickup( inputdata_t &inputdata ) +{ + RemoveSpawnFlags(SF_WEAPON_NO_NPC_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputDisableNPCPickup( inputdata_t &inputdata ) +{ + AddSpawnFlags(SF_WEAPON_NO_NPC_PICKUP); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputBreakConstraint( inputdata_t &inputdata ) +{ + if ( m_pConstraint != NULL ) + { + physenv->DestroyConstraint( m_pConstraint ); + m_pConstraint = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputForceFire( inputdata_t &inputdata, bool bSecondary ) +{ + CBaseCombatCharacter *pOperator = GetOwner(); + + if (!pOperator) + { + // No owner. This means they want us to fire while possibly on the floor independent of any NPC...the madmapper! + pOperator = ToBaseCombatCharacter(inputdata.pActivator); + if (pOperator && pOperator->IsNPC()) + { + // Use this guy, I guess + Operator_ForceNPCFire(pOperator, bSecondary); + } + else + { + // Well...I learned this trick from ent_info. If you have any better ideas, be my guest. + pOperator = CreateEntityByName("generic_actor")->MyCombatCharacterPointer(); + pOperator->SetAbsOrigin(GetAbsOrigin()); + pOperator->SetAbsAngles(GetAbsAngles()); + SetOwnerEntity(pOperator); + + Operator_ForceNPCFire(pOperator, bSecondary); + + UTIL_RemoveImmediate(pOperator); + } + } + else if (pOperator->IsPlayer()) + { + // Owner exists and is a player. + bSecondary ? SecondaryAttack() : PrimaryAttack(); + } + else + { + // Owner exists and is a NPC. + Operator_ForceNPCFire(pOperator, bSecondary); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputForcePrimaryFire( inputdata_t &inputdata ) +{ + InputForceFire(inputdata, false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputForceSecondaryFire( inputdata_t &inputdata ) +{ + InputForceFire(inputdata, true); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1696,6 +1976,14 @@ void CBaseCombatWeapon::ItemPostFrame( void ) // Secondary attack has priority if ((pOwner->m_nButtons & IN_ATTACK2) && CanPerformSecondaryAttack() ) { +#ifdef MAPBASE + if (pOwner->HasSpawnFlags(SF_PLAYER_SUPPRESS_FIRING)) + { + // Don't do anything, just cancel the whole function + return; + } + else +#endif if (UsesSecondaryAmmo() && pOwner->GetAmmoCount(m_iSecondaryAmmoType)<=0 ) { if (m_flNextEmptySoundTime < gpGlobals->curtime) @@ -1741,6 +2029,14 @@ void CBaseCombatWeapon::ItemPostFrame( void ) if ( !bFired && (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) { +#ifdef MAPBASE + if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) + { + // Don't do anything, just cancel the whole function + return; + } + else +#endif // Clip empty? Or out of ammo on a no-clip weapon? if ( !IsMeleeWeapon() && (( UsesClipsForAmmo1() && m_iClip1 <= 0) || ( !UsesClipsForAmmo1() && pOwner->GetAmmoCount(m_iPrimaryAmmoType)<=0 )) ) @@ -2466,6 +2762,23 @@ Activity CBaseCombatWeapon::ActivityOverride( Activity baseAct, bool *pRequired return baseAct; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatWeapon::ScriptGetOwner() +{ + return ToHScript( GetOwner() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::ScriptSetOwner( HSCRIPT owner ) +{ + return SetOwner( ToEnt( owner ) ? ToEnt( owner )->MyCombatCharacterPointer() : NULL ); +} +#endif //----------------------------------------------------------------------------- // Purpose: @@ -2495,6 +2808,7 @@ void CBaseCombatWeapon::PoseParameterOverride( bool bReset ) } + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2629,6 +2943,92 @@ END_PREDICTION_DATA() // Special hack since we're aliasing the name C_BaseCombatWeapon with a macro on the client IMPLEMENT_NETWORKCLASS_ALIASED( BaseCombatWeapon, DT_BaseCombatWeapon ) +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, CBaseAnimating, "The base class for all equippable weapons." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOwner, "GetOwner", "Get the weapon's owner." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOwner, "SetOwner", "Set the weapon's owner." ) + + DEFINE_SCRIPTFUNC( Clip1, "Get the weapon's current primary ammo." ) + DEFINE_SCRIPTFUNC( Clip2, "Get the weapon's current secondary ammo." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetClip1, "SetClip1", "Set the weapon's current primary ammo." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetClip2, "SetClip2", "Set the weapon's current secondary ammo." ) + DEFINE_SCRIPTFUNC( GetMaxClip1, "Get the weapon's maximum primary ammo." ) + DEFINE_SCRIPTFUNC( GetMaxClip2, "Get the weapon's maximum secondary ammo." ) + DEFINE_SCRIPTFUNC( GetDefaultClip1, "Get the weapon's default primary ammo." ) + DEFINE_SCRIPTFUNC( GetDefaultClip2, "Get the weapon's default secondary ammo." ) + + DEFINE_SCRIPTFUNC( HasAnyAmmo, "Check if the weapon currently has ammo or doesn't need ammo." ) + DEFINE_SCRIPTFUNC( HasPrimaryAmmo, "Check if the weapon currently has ammo or doesn't need primary ammo." ) + DEFINE_SCRIPTFUNC( HasSecondaryAmmo, "Check if the weapon currently has ammo or doesn't need secondary ammo." ) + DEFINE_SCRIPTFUNC( UsesPrimaryAmmo, "Check if the weapon uses primary ammo." ) + DEFINE_SCRIPTFUNC( UsesSecondaryAmmo, "Check if the weapon uses secondary ammo." ) + DEFINE_SCRIPTFUNC( GiveDefaultAmmo, "Fill the weapon back up to default ammo." ) + + DEFINE_SCRIPTFUNC( UsesClipsForAmmo1, "Check if the weapon uses clips for primary ammo." ) + DEFINE_SCRIPTFUNC( UsesClipsForAmmo2, "Check if the weapon uses clips for secondary ammo." ) + +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( GetPrimaryAmmoType, "Get the weapon's primary ammo type." ) + DEFINE_SCRIPTFUNC( GetSecondaryAmmoType, "Get the weapon's secondary ammo type." ) +#endif + + DEFINE_SCRIPTFUNC( GetSubType, "Get the weapon's subtype." ) + DEFINE_SCRIPTFUNC( SetSubType, "Set the weapon's subtype." ) + + DEFINE_SCRIPTFUNC( GetFireRate, "Get the weapon's firing rate." ) + DEFINE_SCRIPTFUNC( AddViewKick, "Applies the weapon's view kick." ) + + DEFINE_SCRIPTFUNC( GetWorldModel, "Get the weapon's world model." ) + DEFINE_SCRIPTFUNC( GetViewModel, "Get the weapon's view model." ) + + DEFINE_SCRIPTFUNC( GetWeight, "Get the weapon's weight." ) + + DEFINE_SCRIPTFUNC( CanBePickedUpByNPCs, "Check if the weapon can be picked up by NPCs." ) + +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( CapabilitiesGet, "Get the capabilities the weapon currently possesses." ) +#endif + + DEFINE_SCRIPTFUNC( HasWeaponIdleTimeElapsed, "Returns true if the idle time has elapsed." ) + DEFINE_SCRIPTFUNC( GetWeaponIdleTime, "Returns the next time WeaponIdle() will run." ) + DEFINE_SCRIPTFUNC( SetWeaponIdleTime, "Sets the next time WeaponIdle() will run." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptWeaponClassify, "WeaponClassify", "Returns the weapon's classify class from the WEPCLASS_ constant group" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptWeaponSound, "WeaponSound", "Plays one of the weapon's sounds." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBulletSpread, "GetBulletSpread", "Returns the weapon's default bullet spread." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBulletSpreadForProficiency, "GetBulletSpreadForProficiency", "Returns the weapon's bullet spread for the specified proficiency level." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPrimaryAttackActivity, "GetPrimaryAttackActivity", "Returns the weapon's primary attack activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSecondaryAttackActivity, "GetSecondaryAttackActivity", "Returns the weapon's secondary attack activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDrawActivity, "GetDrawActivity", "Returns the weapon's draw activity." ) + DEFINE_SCRIPTFUNC( GetDefaultAnimSpeed, "Returns the weapon's default animation speed." ) + DEFINE_SCRIPTFUNC( SendWeaponAnim, "Sends a weapon animation." ) + DEFINE_SCRIPTFUNC( GetViewModelSequenceDuration, "Gets the sequence duration of the current view model animation." ) + DEFINE_SCRIPTFUNC( IsViewModelSequenceFinished, "Returns true if the current view model animation is finished." ) + + DEFINE_SCRIPTFUNC( FiresUnderwater, "Returns true if this weapon can fire underwater." ) + DEFINE_SCRIPTFUNC( SetFiresUnderwater, "Sets whether this weapon can fire underwater." ) + DEFINE_SCRIPTFUNC( AltFiresUnderwater, "Returns true if this weapon can alt-fire underwater." ) + DEFINE_SCRIPTFUNC( SetAltFiresUnderwater, "Sets whether this weapon can alt-fire underwater." ) + DEFINE_SCRIPTFUNC( MinRange1, "Returns the closest this weapon can be used." ) + DEFINE_SCRIPTFUNC( SetMinRange1, "Sets the closest this weapon can be used." ) + DEFINE_SCRIPTFUNC( MinRange2, "Returns the closest this weapon can be used." ) + DEFINE_SCRIPTFUNC( SetMinRange2, "Sets the closest this weapon can be used." ) + DEFINE_SCRIPTFUNC( ReloadsSingly, "Returns true if this weapon reloads 1 round at a time." ) + DEFINE_SCRIPTFUNC( SetReloadsSingly, "Sets whether this weapon reloads 1 round at a time." ) + DEFINE_SCRIPTFUNC( FireDuration, "Returns the amount of time that the weapon has sustained firing." ) + DEFINE_SCRIPTFUNC( SetFireDuration, "Sets the amount of time that the weapon has sustained firing." ) + + DEFINE_SCRIPTFUNC( NextPrimaryAttack, "Returns the next time PrimaryAttack() will run when the player is pressing +ATTACK." ) + DEFINE_SCRIPTFUNC( SetNextPrimaryAttack, "Sets the next time PrimaryAttack() will run when the player is pressing +ATTACK." ) + DEFINE_SCRIPTFUNC( NextSecondaryAttack, "Returns the next time SecondaryAttack() will run when the player is pressing +ATTACK2." ) + DEFINE_SCRIPTFUNC( SetNextSecondaryAttack, "Sets the next time SecondaryAttack() will run when the player is pressing +ATTACK2." ) + +END_SCRIPTDESC(); +#endif + #if !defined( CLIENT_DLL ) //----------------------------------------------------------------------------- // Purpose: Save Data for Base Weapon object @@ -2704,11 +3104,27 @@ BEGIN_DATADESC( CBaseCombatWeapon ) DEFINE_THINKFUNC( HideThink ), DEFINE_INPUTFUNC( FIELD_VOID, "HideWeapon", InputHideWeapon ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAmmo1", InputSetAmmo1 ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAmmo2", InputSetAmmo2 ), + DEFINE_INPUTFUNC( FIELD_VOID, "GiveDefaultAmmo", InputGiveDefaultAmmo ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnablePlayerPickup", InputEnablePlayerPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisablePlayerPickup", InputDisablePlayerPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableNPCPickup", InputEnableNPCPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableNPCPickup", InputDisableNPCPickup ), + DEFINE_INPUTFUNC( FIELD_VOID, "BreakConstraint", InputBreakConstraint ), + DEFINE_INPUTFUNC( FIELD_VOID, "ForcePrimaryFire", InputForcePrimaryFire ), + DEFINE_INPUTFUNC( FIELD_VOID, "ForceSecondaryFire", InputForceSecondaryFire ), +#endif + // Outputs DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse"), DEFINE_OUTPUT( m_OnPlayerPickup, "OnPlayerPickup"), DEFINE_OUTPUT( m_OnNPCPickup, "OnNPCPickup"), DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnDropped, "OnDropped" ), +#endif END_DATADESC() @@ -2755,6 +3171,16 @@ void* SendProxy_SendLocalWeaponDataTable( const SendProp *pProp, const void *pSt pRecipients->SetOnly( pPlayer->GetClientIndex() ); return (void*)pVarData; } +#ifdef MAPBASE + else if (pWeapon->HasSpawnFlags( SF_WEAPON_PRESERVE_AMMO )) + { + // Ammo values are sent to the client using this proxy. + // Preserved ammo values from the server need to be sent to the client ASAP to avoid HUD issues, etc. + // I've tried many nasty hacks, but this is the one that works well enough and there's not much else we could do. + pRecipients->SetAllRecipients(); + return (void*)pVarData; + } +#endif } return NULL; @@ -2858,6 +3284,11 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) SendPropModelIndex( SENDINFO(m_iWorldModelIndex) ), SendPropInt( SENDINFO(m_iState ), 8, SPROP_UNSIGNED ), SendPropEHandle( SENDINFO(m_hOwner) ), + +#ifdef MAPBASE + SendPropInt( SENDINFO(m_spawnflags), 8, SPROP_UNSIGNED ), +#endif + #else RecvPropDataTable("LocalWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalWeaponData)), RecvPropDataTable("LocalActiveWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponData)), @@ -2865,5 +3296,10 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) RecvPropInt( RECVINFO(m_iWorldModelIndex)), RecvPropInt( RECVINFO(m_iState), 0, &CBaseCombatWeapon::RecvProxy_WeaponState ), RecvPropEHandle( RECVINFO(m_hOwner ) ), + +#ifdef MAPBASE + RecvPropInt( RECVINFO( m_spawnflags ) ), +#endif + #endif END_NETWORK_TABLE() diff --git a/mp/src/game/shared/basecombatweapon_shared.h b/mp/src/game/shared/basecombatweapon_shared.h index d4964d83..762ca099 100644 --- a/mp/src/game/shared/basecombatweapon_shared.h +++ b/mp/src/game/shared/basecombatweapon_shared.h @@ -48,6 +48,23 @@ class CUserCmd; #define SF_WEAPON_START_CONSTRAINED (1<<0) #define SF_WEAPON_NO_PLAYER_PICKUP (1<<1) #define SF_WEAPON_NO_PHYSCANNON_PUNT (1<<2) +#ifdef MAPBASE +// I really, REALLY hope no weapon uses their own spawnflags. +// If you want yours to use spawnflags, start at 16 just to be safe. + +#define SF_WEAPON_NO_NPC_PICKUP (1<<3) // Prevents NPCs from picking up the weapon. +#define SF_WEAPON_PRESERVE_AMMO (1<<4) // Prevents the weapon from filling up to max automatically when dropped or picked up by players. +#define SF_WEAPON_PRESERVE_NAME (1<<5) // Prevents the weapon's name from being cleared upon being picked up by a player. +#define SF_WEAPON_ALWAYS_TOUCHABLE (1<<6) // Makes a weapon always touchable/pickupable, even through walls. + +// ---------------------------------------------- +// These spawnflags are not supposed to be used by level designers. +// They're just my way of trying to avoid adding new variables +// that have to stay in memory and save/load. +// ---------------------------------------------- +#define SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY (1<<6) // So weapons with ammo preserved at 0 don't switch. +#define SF_WEAPON_USED (1<<7) // Weapon is being +USE'd, not bumped +#endif //Percent #define CLIP_PERC_THRESHOLD 0.75f @@ -112,6 +129,26 @@ namespace vgui2 typedef unsigned long HFont; } +#ifdef MAPBASE +// ------------------ +// Weapon classes +// ------------------ +// I found myself in situations where this is useful. +// Their purpose is similar to Class_T on NPCs. + +enum WeaponClass_t +{ + WEPCLASS_INVALID = 0, + + WEPCLASS_HANDGUN, + WEPCLASS_RIFLE, + WEPCLASS_SHOTGUN, + WEPCLASS_HEAVY, + + WEPCLASS_MELEE, +}; +#endif + // ----------------------------------------- // Vector cones // ----------------------------------------- @@ -180,6 +217,9 @@ public: DECLARE_CLASS( CBaseCombatWeapon, BASECOMBATWEAPON_DERIVED_FROM ); DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif CBaseCombatWeapon(); virtual ~CBaseCombatWeapon(); @@ -194,12 +234,25 @@ public: virtual void Spawn( void ); virtual void Precache( void ); +#ifdef MAPBASE + void SetAmmoFromMapper( float flAmmo, bool bSecondary = false ); + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); +#endif + void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ); // Subtypes are used to manage multiple weapons of the same type on the player. virtual int GetSubType( void ) { return m_iSubType; } virtual void SetSubType( int iType ) { m_iSubType = iType; } +#ifdef MAPBASE + virtual WeaponClass_t WeaponClassify(); + static WeaponClass_t WeaponClassFromString(const char *str); + + virtual bool SupportsBackupActivity(Activity activity); +#endif + virtual void Equip( CBaseCombatCharacter *pOwner ); virtual void Drop( const Vector &vecVelocity ); @@ -286,6 +339,10 @@ public: virtual bool Reload( void ); bool DefaultReload( int iClipSize1, int iClipSize2, int iActivity ); bool ReloadsSingly( void ) const; +#ifdef MAPBASE + // Originally created for the crossbow, can be used to add special NPC reloading behavior + virtual void Reload_NPC( void ) { WeaponSound(RELOAD_NPC); m_iClip1 = GetMaxClip1(); } +#endif virtual bool AutoFiresFullClip( void ) const { return false; } virtual void UpdateAutoFire( void ); @@ -416,6 +473,52 @@ public: virtual void Activate( void ); virtual bool ShouldUseLargeViewModelVROverride() { return false; } + +#ifdef MAPBASE + // Gets the weapon script name to load. + virtual const char* GetWeaponScriptName() { return GetClassname(); } +#endif + +#ifdef MAPBASE_VSCRIPT + void ScriptSetClip1( int ammo ) { m_iClip1 = ammo; } + void ScriptSetClip2( int ammo ) { m_iClip2 = ammo; } + + HSCRIPT ScriptGetOwner(); + void ScriptSetOwner( HSCRIPT owner ); + + int ScriptWeaponClassify() { return WeaponClassify(); } + void ScriptWeaponSound( int sound_type, float soundtime = 0.0f ) { WeaponSound( (WeaponSound_t)sound_type, soundtime ); } + + const Vector& ScriptGetBulletSpread( void ) { return GetBulletSpread(); } + Vector ScriptGetBulletSpreadForProficiency( int proficiency ) { return GetBulletSpread( (WeaponProficiency_t)proficiency ); } + + int ScriptGetPrimaryAttackActivity( void ) { return GetPrimaryAttackActivity(); } + int ScriptGetSecondaryAttackActivity( void ) { return GetSecondaryAttackActivity(); } + int ScriptGetDrawActivity( void ) { return GetDrawActivity(); } + + bool FiresUnderwater() { return m_bFiresUnderwater; } + void SetFiresUnderwater( bool bVal ) { m_bFiresUnderwater = bVal; } + bool AltFiresUnderwater() { return m_bAltFiresUnderwater; } + void SetAltFiresUnderwater( bool bVal ) { m_bAltFiresUnderwater = bVal; } + float MinRange1() { return m_fMinRange1; } + void SetMinRange1( float flVal ) { m_fMinRange1 = flVal; } + float MinRange2() { return m_fMinRange2; } + void SetMinRange2( float flVal ) { m_fMinRange2 = flVal; } + float MaxRange1() { return m_fMaxRange1; } + void SetMaxRange1( float flVal ) { m_fMaxRange1 = flVal; } + float MaxRange2() { return m_fMaxRange2; } + void SetMaxRange2( float flVal ) { m_fMaxRange2 = flVal; } + //bool ReloadsSingly() { return m_bReloadsSingly; } + void SetReloadsSingly( bool bVal ) { m_bReloadsSingly = bVal; } + float FireDuration() { return m_fFireDuration; } + void SetFireDuration( float flVal ) { m_fFireDuration = flVal; } + + float NextPrimaryAttack() { return m_flNextPrimaryAttack; } + void SetNextPrimaryAttack( float flVal ) { m_flNextPrimaryAttack = flVal; } + float NextSecondaryAttack() { return m_flNextSecondaryAttack; } + void SetNextSecondaryAttack( float flVal ) { m_flNextSecondaryAttack = flVal; } +#endif + public: // Server Only Methods #if !defined( CLIENT_DLL ) @@ -462,6 +565,19 @@ public: virtual int UpdateTransmitState( void ); +#ifdef MAPBASE + void InputSetAmmo1( inputdata_t &inputdata ); + void InputSetAmmo2( inputdata_t &inputdata ); + void InputGiveDefaultAmmo( inputdata_t &inputdata ); + void InputEnablePlayerPickup( inputdata_t &inputdata ); + void InputDisablePlayerPickup( inputdata_t &inputdata ); + void InputEnableNPCPickup( inputdata_t &inputdata ); + void InputDisableNPCPickup( inputdata_t &inputdata ); + void InputBreakConstraint( inputdata_t &inputdata ); + void InputForceFire( inputdata_t &inputdata, bool bSecondary = false ); + void InputForcePrimaryFire( inputdata_t &inputdata ); + void InputForceSecondaryFire( inputdata_t &inputdata ); +#endif void InputHideWeapon( inputdata_t &inputdata ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -524,6 +640,7 @@ public: virtual int GetWorldModelIndex( void ); virtual void GetToolRecordingState( KeyValues *msg ); + void EnsureCorrectRenderingModel(); virtual void GetWeaponCrosshairScale( float &flScale ) { flScale = 1.f; } @@ -536,6 +653,9 @@ public: bool WantsToOverrideViewmodelAttachments( void ) { return false; } #endif + //Tony; notifications of any third person switches. + virtual void ThirdPersonSwitch( bool bThirdPerson ) {}; + #endif // End client-only methods virtual bool CanLower( void ) { return false; } @@ -627,6 +747,18 @@ public: CNetworkVar( bool, m_bFlipViewModel ); +#ifdef MAPBASE +#ifdef CLIENT_DLL + int m_spawnflags; + + inline bool HasSpawnFlags( int flags ) { return (m_spawnflags & flags) != 0; } + inline void RemoveSpawnFlags( int flags ) { m_spawnflags &= ~flags; } + inline void AddSpawnFlags( int flags ) { m_spawnflags |= flags; } +#else + //IMPLEMENT_NETWORK_VAR_FOR_DERIVED(m_spawnflags); +#endif +#endif + IPhysicsConstraint *GetConstraint() { return m_pConstraint; } private: @@ -649,6 +781,9 @@ protected: COutputEvent m_OnPlayerPickup; // Fired when the player picks up the weapon. COutputEvent m_OnNPCPickup; // Fired when an NPC picks up the weapon. COutputEvent m_OnCacheInteraction; // For awarding lambda cache achievements in HL2 on 360. See .FGD file for details +#ifdef MAPBASE + COutputEvent m_OnDropped; +#endif #else // Client .dll only bool m_bJustRestored; diff --git a/mp/src/game/shared/baseentity_shared.cpp b/mp/src/game/shared/baseentity_shared.cpp index 376cd34e..cf123b10 100644 --- a/mp/src/game/shared/baseentity_shared.cpp +++ b/mp/src/game/shared/baseentity_shared.cpp @@ -53,6 +53,10 @@ ConVar hl2_episodic( "hl2_episodic", "0", FCVAR_REPLICATED ); #include "tf_weaponbase.h" #endif // TF_DLL +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_funcs_shared.h" +#endif + #include "rumble_shared.h" // memdbgon must be the last include file in a .cpp file!!! @@ -408,7 +412,11 @@ bool CBaseEntity::KeyValue( const char *szKeyName, const char *szValue ) } // Do this so inherited classes looking for 'angles' don't have to bother with 'angle' +#ifdef MAPBASE + return KeyValue( "angles", szBuf ); +#else return KeyValue( szKeyName, szBuf ); +#endif } // NOTE: Have to do these separate because they set two values instead of one @@ -436,6 +444,15 @@ bool CBaseEntity::KeyValue( const char *szKeyName, const char *szValue ) return true; } +#ifdef MAPBASE + if ( FStrEq( szKeyName, "eflags" ) ) + { + // Can't use DEFINE_KEYFIELD since eflags might be set before KV are parsed + AddEFlags( atoi( szValue ) ); + return true; + } +#endif + #ifdef GAME_DLL if ( FStrEq( szKeyName, "targetname" ) ) @@ -1602,6 +1619,23 @@ typedef CTraceFilterSimpleList CBulletsTraceFilter; void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) { +#if defined(MAPBASE_VSCRIPT) && defined(GAME_DLL) + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { hInfo }; + if (g_Hook_FireBullets.Call( m_ScriptScope, &functionReturn, args )) + { + if (!functionReturn.m_bool) + return; + } + + g_pScriptVM->RemoveInstance( hInfo ); + } +#endif + static int tracerCount; trace_t tr; CAmmoDef* pAmmoDef = GetAmmoDef(); @@ -1675,6 +1709,19 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) } #endif // SERVER_DLL +#ifdef MAPBASE + if (info.m_pIgnoreEntList != NULL) + { + for (int i = 0; i < info.m_pIgnoreEntList->Count(); i++) + { + if (info.m_pIgnoreEntList->Element(i)) + { + traceFilter.AddEntityToIgnore(info.m_pIgnoreEntList->Element(i)); + } + } + } +#endif + bool bUnderwaterBullets = ShouldDrawUnderwaterBulletBubbles(); bool bStartedInWater = false; if ( bUnderwaterBullets ) @@ -2145,7 +2192,11 @@ void CBaseEntity::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir int blood = BloodColor(); +#if defined(MAPBASE) && defined(GAME_DLL) + if ( blood != DONT_BLEED && DamageFilterAllowsBlood( info ) ) +#else if ( blood != DONT_BLEED ) +#endif { SpawnBlood( vecOrigin, vecDir, blood, info.GetDamage() );// a little surface blood. TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() ); diff --git a/mp/src/game/shared/baseentity_shared.h b/mp/src/game/shared/baseentity_shared.h index 52a90ab4..7424ae73 100644 --- a/mp/src/game/shared/baseentity_shared.h +++ b/mp/src/game/shared/baseentity_shared.h @@ -68,6 +68,9 @@ enum InvalidatePhysicsBits_t #endif +#include "vscript/ivscript.h" +#include "vscript_shared.h" + #if !defined( NO_ENTITY_PREDICTION ) // CBaseEntity inlines inline bool CBaseEntity::IsPlayerSimulated( void ) const @@ -251,6 +254,17 @@ inline bool CBaseEntity::IsEffectActive( int nEffects ) const return (m_fEffects & nEffects) != 0; } +inline HSCRIPT ToHScript(CBaseEntity* pEnt) +{ + return (pEnt) ? pEnt->GetScriptInstance() : NULL; +} + +template <> ScriptClassDesc_t* GetScriptDesc(CBaseEntity*); +inline CBaseEntity* ToEnt(HSCRIPT hScript) +{ + return (hScript) ? (CBaseEntity*)g_pScriptVM->GetInstanceValue(hScript, GetScriptDescForClass(CBaseEntity)) : NULL; +} + // Shared EntityMessage between game and client .dlls #define BASEENTITY_MSG_REMOVE_DECALS 1 diff --git a/mp/src/game/shared/basegrenade_shared.cpp b/mp/src/game/shared/basegrenade_shared.cpp index b981d078..0d616099 100644 --- a/mp/src/game/shared/basegrenade_shared.cpp +++ b/mp/src/game/shared/basegrenade_shared.cpp @@ -34,13 +34,29 @@ BEGIN_DATADESC( CBaseGrenade ) DEFINE_FIELD( m_hThrower, FIELD_EHANDLE ), // m_fRegisteredSound ??? DEFINE_FIELD( m_bIsLive, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_DmgRadius, FIELD_FLOAT, "Radius" ), +#else DEFINE_FIELD( m_DmgRadius, FIELD_FLOAT ), +#endif DEFINE_FIELD( m_flDetonateTime, FIELD_TIME ), DEFINE_FIELD( m_flWarnAITime, FIELD_TIME ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ), +#else DEFINE_FIELD( m_flDamage, FIELD_FLOAT ), +#endif DEFINE_FIELD( m_iszBounceSound, FIELD_STRING ), DEFINE_FIELD( m_bHasWarnedAI, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamage", InputSetDamage ), + DEFINE_INPUTFUNC( FIELD_VOID, "Detonate", InputDetonate ), + + DEFINE_OUTPUT( m_OnDetonate, "OnDetonate" ), + DEFINE_OUTPUT( m_OnDetonate_OutPosition, "OnDetonate_OutPosition" ), +#endif + // Function Pointers DEFINE_THINKFUNC( Smoke ), DEFINE_ENTITYFUNC( BounceTouch ), @@ -58,6 +74,28 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi #endif +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseGrenade, CBaseAnimating, "The base class for grenades." ) + + DEFINE_SCRIPTFUNC( GetBlastForce, "Gets the grenade's blast force override. Grenades which use base damage force calculations return 0,0,0" ) + + DEFINE_SCRIPTFUNC( GetDamage, "Gets the grenade's blast damage." ) + DEFINE_SCRIPTFUNC( GetDamageRadius, "Gets the grenade's blast damage radius." ) + DEFINE_SCRIPTFUNC( SetDamage, "Sets the grenade's blast damage." ) + DEFINE_SCRIPTFUNC( SetDamageRadius, "Sets the grenade's blast damage radius." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetThrower, "GetThrower", "Gets the grenade's thrower." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetThrower, "SetThrower", "Sets the grenade's thrower." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOriginalThrower, "GetOriginalThrower", "Gets the grenade's original thrower after the thrower was changed due to being picked up by a gravity gun or something." ) + + DEFINE_SCRIPTFUNC_NAMED( GetDetonateTime, "GetTimer", "Gets the grenade's detonate time if it has one." ) + DEFINE_SCRIPTFUNC( HasWarnedAI, "Whether or not the grenade has issued its DANGER sound to the world sound list yet." ) + DEFINE_SCRIPTFUNC( IsLive, "Whether or not the grenade has issued its DANGER sound to the world sound list yet." ) + DEFINE_SCRIPTFUNC( GetWarnAITime, "Gets the time at which the grenade will warn/has warned AI." ) + +END_SCRIPTDESC(); +#endif + IMPLEMENT_NETWORKCLASS_ALIASED( BaseGrenade, DT_BaseGrenade ) BEGIN_NETWORK_TABLE( CBaseGrenade, DT_BaseGrenade ) @@ -180,6 +218,11 @@ void CBaseGrenade::Explode( trace_t *pTrace, int bitsDamageType ) EmitSound( "BaseGrenade.Explode" ); +#ifdef MAPBASE + m_OnDetonate.FireOutput(GetThrower(), this); + m_OnDetonate_OutPosition.Set(GetAbsOrigin(), GetThrower(), this); +#endif + SetThink( &CBaseGrenade::SUB_Remove ); SetTouch( NULL ); SetSolid( SOLID_NONE ); @@ -258,6 +301,25 @@ void CBaseGrenade::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE // Pass up so we still call any custom Use function BaseClass::Use( pActivator, pCaller, useType, value ); } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseGrenade::InputSetDamage( inputdata_t &inputdata ) +{ + SetDamage( inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseGrenade::InputDetonate( inputdata_t &inputdata ) +{ + Detonate(); +} +#endif + #endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/basegrenade_shared.h b/mp/src/game/shared/basegrenade_shared.h index 38bb684d..47840dc4 100644 --- a/mp/src/game/shared/basegrenade_shared.h +++ b/mp/src/game/shared/basegrenade_shared.h @@ -49,6 +49,9 @@ public: #if !defined( CLIENT_DLL ) DECLARE_DATADESC(); #endif +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif virtual void Precache( void ); @@ -103,6 +106,17 @@ public: void SetThrower( CBaseCombatCharacter *pThrower ); CBaseEntity *GetOriginalThrower() { return m_hOriginalThrower; } +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetThrower( void ) { return ToHScript( GetThrower() ); } + void ScriptSetThrower( HSCRIPT hThrower ) { SetThrower( ToEnt(hThrower) ? ToEnt(hThrower)->MyCombatCharacterPointer() : NULL ); } + HSCRIPT ScriptGetOriginalThrower() { return ToHScript( GetOriginalThrower() ); } + + float GetDetonateTime() { return m_flDetonateTime; } + bool HasWarnedAI() { return m_bHasWarnedAI; } + bool IsLive() { return m_bIsLive; } + float GetWarnAITime() { return m_flWarnAITime; } +#endif + #if !defined( CLIENT_DLL ) // Allow +USE pickup int ObjectCaps() @@ -111,6 +125,12 @@ public: } void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + +#ifdef MAPBASE + void InputSetDamage( inputdata_t &inputdata ); + void InputDetonate( inputdata_t &inputdata ); +#endif + #endif public: @@ -129,6 +149,11 @@ protected: CNetworkVar( float, m_flDamage ); // Damage to inflict. string_t m_iszBounceSound; // The sound to make on bouncing. If not NULL, overrides the BounceSound() function. +#if defined(MAPBASE) && !defined(CLIENT_DLL) + COutputEvent m_OnDetonate; + COutputVector m_OnDetonate_OutPosition; +#endif + private: CNetworkHandle( CBaseEntity, m_hThrower ); // Who threw this grenade EHANDLE m_hOriginalThrower; // Who was the original thrower of this grenade diff --git a/mp/src/game/shared/baseplayer_shared.cpp b/mp/src/game/shared/baseplayer_shared.cpp index 70056463..7a16042d 100644 --- a/mp/src/game/shared/baseplayer_shared.cpp +++ b/mp/src/game/shared/baseplayer_shared.cpp @@ -163,12 +163,30 @@ void CBasePlayer::ItemPreFrame() // Handle use events PlayerUse(); - CBaseCombatWeapon *pActive = GetActiveWeapon(); + //Tony; re-ordered this for efficiency and to make sure that certain things happen in the correct order! + if ( gpGlobals->curtime < m_flNextAttack ) + { + return; + } + if (!GetActiveWeapon()) + return; + +#if defined( CLIENT_DLL ) + // Not predicting this weapon + if ( !GetActiveWeapon()->IsPredicted() ) + return; +#endif + + GetActiveWeapon()->ItemPreFrame(); + + CBaseCombatWeapon *pWeapon; + + CBaseCombatWeapon *pActive = GetActiveWeapon(); // Allow all the holstered weapons to update for ( int i = 0; i < WeaponCount(); ++i ) { - CBaseCombatWeapon *pWeapon = GetWeapon( i ); + pWeapon = GetWeapon( i ); if ( pWeapon == NULL ) continue; @@ -178,20 +196,6 @@ void CBasePlayer::ItemPreFrame() pWeapon->ItemHolsterFrame(); } - - if ( gpGlobals->curtime < m_flNextAttack ) - return; - - if (!pActive) - return; - -#if defined( CLIENT_DLL ) - // Not predicting this weapon - if ( !pActive->IsPredicted() ) - return; -#endif - - pActive->ItemPreFrame(); } //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/baseplayer_shared.h b/mp/src/game/shared/baseplayer_shared.h index 8bdc0950..fc5fdf0f 100644 --- a/mp/src/game/shared/baseplayer_shared.h +++ b/mp/src/game/shared/baseplayer_shared.h @@ -53,6 +53,20 @@ enum stepsoundtimes_t void CopySoundNameWithModifierToken( char *pchDest, const char *pchSource, int nMaxLenInChars, const char *pchToken ); +#ifdef MAPBASE +// Internal player spawnflags. +// These are only meant to be used internally or accessed via logic_playerproxy. +// I'm sure this isn't a bad idea whatsoever... +// +// They start at 16 because some NPC spawnflags (e.g. Wait Till Seen) +// used in places with both NPCs and players don't check whether the target is a NPC or a player. +// Spawnflags are also transmitted to the client and use a special network proxy to get around this without having to transmit unused bits. +// Be sure to update the SendPropInt() entry for m_spawnflags in player.cpp when you add any new spawnflags! +#define SF_PLAYER_NO_GEIGER (1 << 16) +#define SF_PLAYER_HIDE_SQUAD_HUD (1 << 17) +#define SF_PLAYER_SUPPRESS_FIRING (1 << 18) +#endif + // Shared header file for players #if defined( CLIENT_DLL ) #define CBasePlayer C_BasePlayer diff --git a/mp/src/game/shared/baseviewmodel_shared.cpp b/mp/src/game/shared/baseviewmodel_shared.cpp index 20538e8c..b7fef9b7 100644 --- a/mp/src/game/shared/baseviewmodel_shared.cpp +++ b/mp/src/game/shared/baseviewmodel_shared.cpp @@ -407,15 +407,13 @@ void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePos } // Add model-specific bob even if no weapon associated (for head bob for off hand models) AddViewModelBob( owner, vmorigin, vmangles ); -#if !defined ( CSTRIKE_DLL ) - // This was causing weapon jitter when rotating in updated CS:S; original Source had this in above InPrediction block 07/14/10 - // Add lag - CalcViewModelLag( vmorigin, vmangles, vmangoriginal ); -#endif #if defined( CLIENT_DLL ) if ( !prediction->InPrediction() ) { + // Add lag + CalcViewModelLag( vmorigin, vmangles, vmangoriginal ); + // Let the viewmodel shake at about 10% of the amplitude of the player's view vieweffects->ApplyShake( vmorigin, vmangles, 0.1 ); } @@ -689,3 +687,35 @@ bool CBaseViewModel::GetAttachmentVelocity( int number, Vector &originVel, Quate } #endif + +#ifdef MAPBASE +#if defined( CLIENT_DLL ) +#define CHandViewModel C_HandViewModel +#endif + +// --------------------------------------- +// OzxyBox's hand viewmodel code. +// All credit goes to him. +// --------------------------------------- +class CHandViewModel : public CBaseViewModel +{ + DECLARE_CLASS( CHandViewModel, CBaseViewModel ); +public: + DECLARE_NETWORKCLASS(); +private: +}; + +LINK_ENTITY_TO_CLASS(hand_viewmodel, CHandViewModel); +IMPLEMENT_NETWORKCLASS_ALIASED(HandViewModel, DT_HandViewModel) + +// for whatever reason the parent doesn't get sent +// I don't really want to mess with the baseviewmodel +// so now it does +BEGIN_NETWORK_TABLE(CHandViewModel, DT_HandViewModel) +#ifndef CLIENT_DLL + SendPropEHandle(SENDINFO_NAME(m_hMoveParent, moveparent)), +#else + RecvPropInt(RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent), +#endif +END_NETWORK_TABLE() +#endif diff --git a/mp/src/game/shared/baseviewmodel_shared.h b/mp/src/game/shared/baseviewmodel_shared.h index 15d3be53..7e9c79a6 100644 --- a/mp/src/game/shared/baseviewmodel_shared.h +++ b/mp/src/game/shared/baseviewmodel_shared.h @@ -145,7 +145,11 @@ public: // Should this object receive shadows? virtual bool ShouldReceiveProjectedTextures( int flags ) { +#ifdef MAPBASE + return true; +#else return false; +#endif } // Add entity to visible view models list? diff --git a/mp/src/game/shared/beam_shared.cpp b/mp/src/game/shared/beam_shared.cpp index 77e5d5c2..c3e39af8 100644 --- a/mp/src/game/shared/beam_shared.cpp +++ b/mp/src/game/shared/beam_shared.cpp @@ -773,7 +773,9 @@ void CBeam::BeamDamage( trace_t *ptr ) if ( ptr->fraction != 1.0 && ptr->m_pEnt != NULL ) { CBaseEntity *pHit = ptr->m_pEnt; +#ifndef MAPBASE if ( pHit ) +#endif { ClearMultiDamage(); Vector dir = ptr->endpos - GetAbsOrigin(); diff --git a/mp/src/game/shared/choreoevent.cpp b/mp/src/game/shared/choreoevent.cpp index 9afb938b..0467c4de 100644 --- a/mp/src/game/shared/choreoevent.cpp +++ b/mp/src/game/shared/choreoevent.cpp @@ -2064,6 +2064,8 @@ static EventNameMap_t g_NameMap[] = { CChoreoEvent::STOPPOINT, "stoppoint" }, { CChoreoEvent::PERMIT_RESPONSES, "permitresponses" }, { CChoreoEvent::GENERIC, "generic" }, + { CChoreoEvent::CAMERA, "camera" }, + { CChoreoEvent::SCRIPT, "script" }, }; //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/choreoevent.h b/mp/src/game/shared/choreoevent.h index ad4828d3..64db52af 100644 --- a/mp/src/game/shared/choreoevent.h +++ b/mp/src/game/shared/choreoevent.h @@ -307,6 +307,12 @@ public: // A string passed to the game code for interpretation GENERIC, + // Camera control + CAMERA, + + // Script function call + SCRIPT, + // THIS MUST BE LAST!!! NUM_TYPES, } EVENTTYPE; diff --git a/mp/src/game/shared/choreoscene.h b/mp/src/game/shared/choreoscene.h index d8203c14..b4a5679d 100644 --- a/mp/src/game/shared/choreoscene.h +++ b/mp/src/game/shared/choreoscene.h @@ -84,6 +84,9 @@ public: // Event callback handler void SetEventCallbackInterface( IChoreoEventCallback *callback ); +#ifdef MAPBASE + IChoreoEventCallback *GetEventCallbackInterface() { return m_pIChoreoEventCallback; } +#endif // Loading bool ParseFromBuffer( char const *pFilename, ISceneTokenProcessor *tokenizer ); diff --git a/mp/src/game/shared/env_wind_shared.cpp b/mp/src/game/shared/env_wind_shared.cpp index 422e9e99..02df324a 100644 --- a/mp/src/game/shared/env_wind_shared.cpp +++ b/mp/src/game/shared/env_wind_shared.cpp @@ -70,6 +70,9 @@ #include "IEffects.h" #include "engine/IEngineSound.h" #include "sharedInterface.h" +#ifdef CLIENT_DLL +#include "renderparm.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -77,12 +80,20 @@ //----------------------------------------------------------------------------- // globals //----------------------------------------------------------------------------- +#ifdef MAPBASE +static CUtlLinkedList< CEnvWindShared * > s_windControllers; +#else static Vector s_vecWindVelocity( 0, 0, 0 ); - +#endif CEnvWindShared::CEnvWindShared() : m_WindAveQueue(10), m_WindVariationQueue(10) { m_pWindSound = NULL; +#ifdef MAPBASE + s_windControllers.AddToTail( this ); + m_windRadius = -1.0f; + m_flTreeSwayScale = 1.0f; +#endif } CEnvWindShared::~CEnvWindShared() @@ -91,6 +102,9 @@ CEnvWindShared::~CEnvWindShared() { CSoundEnvelopeController::GetController().Shutdown( m_pWindSound ); } +#ifdef MAPBASE + s_windControllers.FindAndRemove( this ); +#endif } void CEnvWindShared::Init( int nEntIndex, int iRandomSeed, float flTime, @@ -103,6 +117,13 @@ void CEnvWindShared::Init( int nEntIndex, int iRandomSeed, float flTime, m_Stream.SetSeed( iRandomSeed ); m_WindVariationStream.SetSeed( iRandomSeed ); m_iWindDir = m_iInitialWindDir = iInitialWindYaw; +#ifdef MAPBASE + // Bound it for networking as a postive integer + m_iInitialWindDir = (int)( anglemod( m_iInitialWindDir ) ); + + if (m_windRadiusInner == 0.0f) + m_windRadiusInner = m_windRadius; +#endif m_flAveWindSpeed = m_flWindSpeed = m_flInitialWindSpeed = flInitialWindSpeed; @@ -163,6 +184,31 @@ void CEnvWindShared::UpdateWindSound( float flTotalWindSpeed ) } +#ifdef MAPBASE +#define TREE_SWAY_UPDATE_TIME 2.0f + +void CEnvWindShared::UpdateTreeSway( float flTime ) +{ +#ifdef CLIENT_DLL + while( flTime >= m_flSwayTime ) + { + // Since the wind is constantly changing, but we need smooth values, we cache them off here. + m_PrevSwayVector = m_CurrentSwayVector; + m_CurrentSwayVector = m_flTreeSwayScale != 1.0f ? (m_currentWindVector * m_flTreeSwayScale) : m_currentWindVector; + m_flSwayTime += TREE_SWAY_UPDATE_TIME; + } + + // Update vertex shader + float flPercentage = ( 1 - ( ( m_flSwayTime - flTime ) / TREE_SWAY_UPDATE_TIME ) ); + CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); + // Dividing by 2 helps the numbers the shader is expecting stay in line with other expected game values. + Vector vecWind = Lerp( flPercentage, m_PrevSwayVector, m_CurrentSwayVector ) / 25.f; + pRenderContext->SetVectorRenderingParameter( VECTOR_RENDERPARM_WIND_DIRECTION, vecWind ); +#endif +} +#endif + + //----------------------------------------------------------------------------- // Updates the wind speed //----------------------------------------------------------------------------- @@ -179,6 +225,14 @@ float CEnvWindShared::WindThink( float flTime ) ComputeWindVariation( flTime ); +#if defined(MAPBASE) && defined(CLIENT_DLL) + if (m_flTreeSwayScale != 0.0f) + { + // Update Tree Sway + UpdateTreeSway( flTime ); + } +#endif + while (true) { // First, simulate up to the next switch time... @@ -218,9 +272,15 @@ float CEnvWindShared::WindThink( float flTime ) // We're about to exit, let's set the wind velocity... QAngle vecWindAngle( 0, m_iWindDir + m_flWindAngleVariation, 0 ); +#ifdef MAPBASE + AngleVectors( vecWindAngle, &m_currentWindVector ); + float flTotalWindSpeed = m_flWindSpeed * m_flWindSpeedVariation; + m_currentWindVector *= flTotalWindSpeed; +#else AngleVectors( vecWindAngle, &s_vecWindVelocity ); float flTotalWindSpeed = m_flWindSpeed * m_flWindSpeedVariation; s_vecWindVelocity *= flTotalWindSpeed; +#endif // If we reached a steady state, we don't need to be called until the switch time // Otherwise, we should be called immediately @@ -278,9 +338,66 @@ float CEnvWindShared::WindThink( float flTime ) //----------------------------------------------------------------------------- void ResetWindspeed() { +#ifdef MAPBASE + FOR_EACH_LL( s_windControllers, it ) + { + s_windControllers[it]->m_currentWindVector.Init( 0, 0, 0 ); + } +#else s_vecWindVelocity.Init( 0, 0, 0 ); +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// GetWindspeedAtTime was never finished to actually take time in to consideration. We don't need +// features that aren't written, but we do need to have multiple wind controllers on a map, so +// we need to find the one that is affecting the given location and return its speed. +// +// NEW WITH MAPBASE: Inner-radius! +// You can now choose an inner-radius for your wind, which allows for varying intensities at different distances. +// This can mix in with a global wind controller or even other wind controllers. +//----------------------------------------------------------------------------- +Vector GetWindspeedAtLocation( const Vector &location ) +{ + Vector wind = Vector( 0, 0, 0 ); + + FOR_EACH_LL( s_windControllers, it ) + { + CEnvWindShared *thisWindController = s_windControllers[it]; + float distance = (thisWindController->m_location - location).Length(); + + if( distance < thisWindController->m_windRadius ) + { + if (distance > thisWindController->m_windRadiusInner) + { + // New with Mapbase: Inner-radius! + wind += thisWindController->m_currentWindVector * + ((distance - thisWindController->m_windRadiusInner) / (thisWindController->m_windRadius - thisWindController->m_windRadiusInner)); + } + else + { + // This location is within our area of influence, so return our computer wind vector + return thisWindController->m_currentWindVector; + } + } + } + + FOR_EACH_LL( s_windControllers, it ) + { + CEnvWindShared *thisWindController = s_windControllers[it]; + + if( thisWindController->m_windRadius == -1.0f ) + { + // We do a second search for a global controller so you don't have to worry about order in the list. + //wind += thisWindController->m_currentWindVector; + wind = VectorLerp( wind, thisWindController->m_currentWindVector, 1.0f ); + } + } + + return wind;// No wind +} +#endif //----------------------------------------------------------------------------- // Method to sample the windspeed at a particular time @@ -289,5 +406,16 @@ void GetWindspeedAtTime( float flTime, Vector &vecVelocity ) { // For now, ignore history and time.. fix later when we use wind to affect // client-side prediction +#ifdef MAPBASE + if ( s_windControllers.Count() == 0 ) + { + vecVelocity.Init( 0, 0, 0 ); + } + else + { + VectorCopy( s_windControllers[ s_windControllers.Head() ]->m_currentWindVector, vecVelocity ); + } +#else VectorCopy( s_vecWindVelocity, vecVelocity ); +#endif } diff --git a/mp/src/game/shared/env_wind_shared.h b/mp/src/game/shared/env_wind_shared.h index e1084e9f..bea9ea62 100644 --- a/mp/src/game/shared/env_wind_shared.h +++ b/mp/src/game/shared/env_wind_shared.h @@ -145,6 +145,10 @@ public: void Init( int iEntIndex, int iRandomSeed, float flTime, int iWindDir, float flInitialWindSpeed ); +#ifdef MAPBASE + void SetLocation( const Vector &location ); +#endif + // Method to update the wind speed // Time passed in here is global time, not delta time // The function returns the time at which it must be called again @@ -157,6 +161,10 @@ public: CNetworkVar( int, m_iMinWind ); // the slowest the wind can normally blow CNetworkVar( int, m_iMaxWind ); // the fastest the wind can normally blow +#ifdef MAPBASE + CNetworkVar( float, m_windRadius ); // the radius this entity affects with its windiness, so a map can have multiple + CNetworkVar( float, m_windRadiusInner ); // the inner-radius for noticable distance fading +#endif CNetworkVar( int, m_iMinGust ); // the slowest that a gust can be CNetworkVar( int, m_iMaxGust ); // the fastest that a gust can be @@ -166,10 +174,21 @@ public: CNetworkVar( float, m_flGustDuration ); // max time between gusts CNetworkVar( int, m_iGustDirChange ); // max number of degrees wind dir changes on gusts. +#ifdef MAPBASE + CNetworkVector( m_location ); // The location of this wind controller +#endif int m_iszGustSound; // name of the wind sound to play for gusts. int m_iWindDir; // wind direction (yaw) float m_flWindSpeed; // the wind speed +#ifdef MAPBASE + Vector m_currentWindVector; // For all the talk of proper prediction, we ended up just storing and returning through a static vector. Now we can have multiple env_wind, so we need this in here. + Vector m_CurrentSwayVector; + Vector m_PrevSwayVector; + + CNetworkVar( float, m_flTreeSwayScale ); +#endif + CNetworkVar( int, m_iInitialWindDir ); CNetworkVar( float, m_flInitialWindSpeed ); @@ -196,7 +215,12 @@ private: // Updates the wind sound void UpdateWindSound( float flTotalWindSpeed ); +#ifdef MAPBASE + void UpdateTreeSway( float flTime ); +#endif + float m_flVariationTime; + float m_flSwayTime; float m_flSimTime; // What's the time I last simulated up to? float m_flSwitchTime; // when do I actually switch from gust to not gust float m_flAveWindSpeed; // the average wind speed @@ -227,6 +251,19 @@ private: CEnvWindShared( const CEnvWindShared & ); // not defined, not accessible }; +#ifdef MAPBASE +//----------------------------------------------------------------------------- +inline void CEnvWindShared::SetLocation( const Vector &location ) +{ + m_location = location; +} + + +//----------------------------------------------------------------------------- +// Method to sample the wind speed at a particular location +//----------------------------------------------------------------------------- +Vector GetWindspeedAtLocation( const Vector &location ); +#endif //----------------------------------------------------------------------------- // Method to sample the windspeed at a particular time diff --git a/mp/src/game/shared/eventlist.cpp b/mp/src/game/shared/eventlist.cpp index f52f8021..804ab941 100644 --- a/mp/src/game/shared/eventlist.cpp +++ b/mp/src/game/shared/eventlist.cpp @@ -252,4 +252,9 @@ void EventList_RegisterSharedEvents( void ) REGISTER_SHARED_ANIMEVENT( AE_TAUNT_ENABLE_MOVE, AE_TYPE_CLIENT | AE_TYPE_SERVER ); REGISTER_SHARED_ANIMEVENT( AE_TAUNT_DISABLE_MOVE, AE_TYPE_CLIENT | AE_TYPE_SERVER ); + +#ifdef MAPBASE + REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE_FORCED, AE_TYPE_SERVER ); +#endif } \ No newline at end of file diff --git a/mp/src/game/shared/eventlist.h b/mp/src/game/shared/eventlist.h index 69c6f0ca..2e5a12ee 100644 --- a/mp/src/game/shared/eventlist.h +++ b/mp/src/game/shared/eventlist.h @@ -90,6 +90,11 @@ typedef enum AE_TAUNT_ENABLE_MOVE, AE_TAUNT_DISABLE_MOVE, +#ifdef MAPBASE + AE_NPC_RESPONSE, // Play a response system concept if we're not speaking + AE_NPC_RESPONSE_FORCED, // Always play a response system concept +#endif + LAST_SHARED_ANIMEVENT, } Animevent; diff --git a/mp/src/game/shared/func_ladder.cpp b/mp/src/game/shared/func_ladder.cpp index 05f81571..306ae77d 100644 --- a/mp/src/game/shared/func_ladder.cpp +++ b/mp/src/game/shared/func_ladder.cpp @@ -5,6 +5,9 @@ //=============================================================================// #include "cbase.h" #include "func_ladder.h" +#ifdef MAPBASE +#include "hl_gamemovement.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -13,6 +16,10 @@ /*static*/ ConVar sv_showladders( "sv_showladders", "0", 0, "Show bbox and dismount points for all ladders (must be set before level load.)\n" ); #endif +#if MAPBASE +extern IGameMovement *g_pGameMovement; +#endif + CUtlVector< CFuncLadder * > CFuncLadder::s_Ladders; //----------------------------------------------------------------------------- // Purpose: @@ -105,7 +112,11 @@ void CFuncLadder::Spawn() } // Force geometry overlays on, but only if developer 2 is set... +#ifdef MAPBASE + if ( sv_showladders.GetBool() ) +#else if ( developer.GetInt() > 1 ) +#endif { m_debugOverlays |= OVERLAY_TEXT_BIT; } @@ -385,6 +396,26 @@ void CFuncLadder::InputDisable( inputdata_t &inputdata ) m_bDisabled = true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CFuncLadder::InputForcePlayerOn( inputdata_t &inputdata ) +{ + static_cast(g_pGameMovement)->ForcePlayerOntoLadder(this); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CFuncLadder::InputCheckPlayerOn( inputdata_t &inputdata ) +{ + static_cast(g_pGameMovement)->MountPlayerOntoLadder(this); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pPlayer - @@ -462,6 +493,10 @@ BEGIN_DATADESC( CFuncLadder ) DEFINE_KEYFIELD( m_surfacePropName,FIELD_STRING, "ladderSurfaceProperties" ), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "ForcePlayerOn", InputForcePlayerOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "CheckPlayerOn", InputCheckPlayerOn ), +#endif DEFINE_OUTPUT( m_OnPlayerGotOnLadder, "OnPlayerGotOnLadder" ), DEFINE_OUTPUT( m_OnPlayerGotOffLadder, "OnPlayerGotOffLadder" ), diff --git a/mp/src/game/shared/func_ladder.h b/mp/src/game/shared/func_ladder.h index 914389d0..50c76eac 100644 --- a/mp/src/game/shared/func_ladder.h +++ b/mp/src/game/shared/func_ladder.h @@ -59,6 +59,11 @@ public: void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputForcePlayerOn( inputdata_t &inputdata ); + void InputCheckPlayerOn( inputdata_t &inputdata ); +#endif + bool IsEnabled() const; void PlayerGotOn( CBasePlayer *pPlayer ); diff --git a/mp/src/game/shared/gamemovement.cpp b/mp/src/game/shared/gamemovement.cpp index 76e4f22f..e4c4fafe 100644 --- a/mp/src/game/shared/gamemovement.cpp +++ b/mp/src/game/shared/gamemovement.cpp @@ -55,6 +55,10 @@ ConVar player_limit_jump_speed( "player_limit_jump_speed", "1", FCVAR_REPLICATED // duck controls. Its value is meaningless anytime we don't have the options window open. ConVar option_duck_method("option_duck_method", "1", FCVAR_REPLICATED|FCVAR_ARCHIVE );// 0 = HOLD to duck, 1 = Duck is a toggle +#ifdef MAPBASE +ConVar player_crouch_multiplier( "player_crouch_multiplier", "0.33333333", FCVAR_NONE ); +#endif + #ifdef STAGING_ONLY #ifdef CLIENT_DLL ConVar debug_latch_reset_onduck( "debug_latch_reset_onduck", "1", FCVAR_CHEAT ); @@ -2840,7 +2844,7 @@ inline bool CGameMovement::OnLadder( trace_t &trace ) // HPE_BEGIN // [sbodenbender] make ladders easier to climb in cstrike //============================================================================= -#if defined (CSTRIKE_DLL) +#if defined (CSTRIKE_DLL) || defined(HL2_USES_FUNC_LADDER_CODE) ConVar sv_ladder_dampen ( "sv_ladder_dampen", "0.2", FCVAR_REPLICATED, "Amount to dampen perpendicular movement on a ladder", true, 0.0f, true, 1.0f ); ConVar sv_ladder_angle( "sv_ladder_angle", "-0.707", FCVAR_REPLICATED, "Cos of angle of incidence to ladder perpendicular for applying ladder_dampen", true, -1.0f, true, 1.0f ); #endif @@ -3902,6 +3906,16 @@ void CGameMovement::CheckFalling( void ) if ( player->GetGroundEntity() == NULL || player->m_Local.m_flFallVelocity <= 0 ) return; +#ifdef MAPBASE +#ifdef GAME_DLL // Let's hope we could work without transmitting to the client... + if ( player->m_bInTriggerFall ) + { + // This lets the fall damage functions do their magic without having to change them. + player->m_Local.m_flFallVelocity += (PLAYER_FATAL_FALL_SPEED + PLAYER_LAND_ON_FLOATING_OBJECT); + } +#endif +#endif + if ( !IsDead() && player->m_Local.m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHOLD ) { bool bAlive = true; @@ -4293,7 +4307,8 @@ void CGameMovement::HandleDuckingSpeedCrop( void ) { if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) && ( player->GetFlags() & FL_DUCKING ) && ( player->GetGroundEntity() != NULL ) ) { - float frac = 0.33333333f; + // Mapbase makes this an adjustable convar + float frac = player_crouch_multiplier.GetFloat(); mv->m_flForwardMove *= frac; mv->m_flSideMove *= frac; mv->m_flUpMove *= frac; diff --git a/mp/src/game/shared/gamemovement.h b/mp/src/game/shared/gamemovement.h index 79c83fc1..1c7a3d0f 100644 --- a/mp/src/game/shared/gamemovement.h +++ b/mp/src/game/shared/gamemovement.h @@ -25,6 +25,13 @@ #define GAMEMOVEMENT_TIME_TO_UNDUCK ( TIME_TO_UNDUCK * 1000.0f ) // ms #define GAMEMOVEMENT_TIME_TO_UNDUCK_INV ( GAMEMOVEMENT_DUCK_TIME - GAMEMOVEMENT_TIME_TO_UNDUCK ) +#ifdef MAPBASE +// reddit.com/r/SourceEngine/comments/8vx53b/how_to_get_brush_ladders_working/ +// +// Implements code that allows func_ladder to be used in HL2. +#define HL2_USES_FUNC_LADDER_CODE 1 +#endif + enum { SPEED_CROPPED_RESET = 0, diff --git a/mp/src/game/shared/gamerules.cpp b/mp/src/game/shared/gamerules.cpp index 81fa7bfa..cb199617 100644 --- a/mp/src/game/shared/gamerules.cpp +++ b/mp/src/game/shared/gamerules.cpp @@ -71,6 +71,67 @@ IMPLEMENT_NETWORKCLASS_ALIASED( GameRulesProxy, DT_GameRulesProxy ) BEGIN_NETWORK_TABLE_NOBASE( CGameRulesProxy, DT_GameRulesProxy ) END_NETWORK_TABLE() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CGameRules, SCRIPT_SINGLETON "The container of the game's rules, handling behavior which could be different on a game-by-game basis." ) + + DEFINE_SCRIPTFUNC( Name, "Gets the name of these rules." ) + + DEFINE_SCRIPTFUNC( Damage_IsTimeBased, "Damage types that are time-based." ) + DEFINE_SCRIPTFUNC( Damage_ShouldGibCorpse, "Damage types that gib the corpse." ) + DEFINE_SCRIPTFUNC( Damage_ShowOnHUD, "Damage types that have client HUD art." ) + DEFINE_SCRIPTFUNC( Damage_NoPhysicsForce, "Damage types that don't have to supply a physics force & position." ) + DEFINE_SCRIPTFUNC( Damage_ShouldNotBleed, "Damage types that don't make the player bleed." ) + + DEFINE_SCRIPTFUNC( ShouldCollide, "Returns whether two collision groups collide with each other in this game." ) + + DEFINE_SCRIPTFUNC( DefaultFOV, "Default player FOV in this game." ) + + DEFINE_SCRIPTFUNC( GetDamageMultiplier, "Ammo type damage multiplier." ) + + DEFINE_SCRIPTFUNC( IsMultiplayer, "Returns true if this is a multiplayer game (like co-op or deathmatch)." ) + + DEFINE_SCRIPTFUNC( InRoundRestart, "Returns true if the round is restarting." ) + + DEFINE_SCRIPTFUNC( AllowThirdPersonCamera, "Returns true if third-person camera is allowed." ) + +#ifdef CLIENT_DLL + + DEFINE_SCRIPTFUNC( IsBonusChallengeTimeBased, "" ) + DEFINE_SCRIPTFUNC( AllowMapParticleEffect, "" ) + DEFINE_SCRIPTFUNC( AllowWeatherParticles, "" ) + DEFINE_SCRIPTFUNC( AllowMapVisionFilterShaders, "" ) + DEFINE_SCRIPTFUNC( TranslateEffectForVisionFilter, "" ) + DEFINE_SCRIPTFUNC( IsLocalPlayer, "" ) + DEFINE_SCRIPTFUNC( ShouldWarnOfAbandonOnQuit, "" ) + +#else + + DEFINE_SCRIPTFUNC( RefreshSkillData, "" ) + + DEFINE_SCRIPTFUNC( IsSkillLevel, "Returns true if the game is set to the specified difficulty/skill level." ) + DEFINE_SCRIPTFUNC( GetSkillLevel, "Returns the game's difficulty/skill level." ) + DEFINE_SCRIPTFUNC( SetSkillLevel, "Sets the game's difficulty/skill level." ) + + DEFINE_SCRIPTFUNC_NAMED( FAllowFlashlight, "AllowFlashlight", "Returns true if players are allowed to switch on their flashlight." ) + + DEFINE_SCRIPTFUNC( IsDeathmatch, "" ) + DEFINE_SCRIPTFUNC( IsTeamplay, "" ) + DEFINE_SCRIPTFUNC( IsCoOp, "" ) + + DEFINE_SCRIPTFUNC( GetGameDescription, "This is the game description that gets seen in server browsers." ) + + DEFINE_SCRIPTFUNC( AllowSPRespawn, "" ) + + DEFINE_SCRIPTFUNC_NAMED( FAllowNPCs, "AllowNPCs", "Returns true if NPCs are allowed." ) + +#endif + + DEFINE_SCRIPTFUNC( GetGameTypeName, "" ) + DEFINE_SCRIPTFUNC( GetGameType, "" ) + +END_SCRIPTDESC() +#endif + CGameRulesProxy::CGameRulesProxy() { @@ -607,6 +668,30 @@ void CGameRules::EndGameFrame( void ) } } +#ifdef MAPBASE +void CGameRules::OnSkillLevelChanged( int iNewLevel ) +{ + variant_t varNewLevel; + varNewLevel.SetInt(iNewLevel); + + // Iterate through all logic_skill entities and fire them + CBaseEntity *pEntity = gEntList.FindEntityByClassname(NULL, "logic_skill"); + while (pEntity) + { + pEntity->AcceptInput("SkillLevelChanged", UTIL_GetLocalPlayer(), NULL, varNewLevel, 0); + pEntity = gEntList.FindEntityByClassname(pEntity, "logic_skill"); + } + + // Fire game event for difficulty level changed + IGameEvent *event = gameeventmanager->CreateEvent("skill_changed"); + if (event) + { + event->SetInt("skill_level", iNewLevel); + gameeventmanager->FireEvent(event); + } +} +#endif + //----------------------------------------------------------------------------- // trace line rules //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/gamerules.h b/mp/src/game/shared/gamerules.h index 5ba66820..8639c142 100644 --- a/mp/src/game/shared/gamerules.h +++ b/mp/src/game/shared/gamerules.h @@ -173,6 +173,8 @@ public: virtual bool InRoundRestart( void ) { return false; } + virtual void RegisterScriptFunctions( void ){ }; + //Allow thirdperson camera. virtual bool AllowThirdPersonCamera( void ) { return false; } @@ -235,7 +237,11 @@ public: virtual bool IsSkillLevel( int iLevel ) { return GetSkillLevel() == iLevel; } virtual int GetSkillLevel() { return g_iSkillLevel; } +#ifdef MAPBASE + virtual void OnSkillLevelChanged( int iNewLevel ); +#else virtual void OnSkillLevelChanged( int iNewLevel ) {}; +#endif virtual void SetSkillLevel( int iLevel ) { int oldLevel = g_iSkillLevel; @@ -294,6 +300,10 @@ public: virtual CBaseEntity *GetPlayerSpawnSpot( CBasePlayer *pPlayer );// Place this player on their spawnspot and face them the proper direction. virtual bool IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer ); +#ifdef MAPBASE + virtual bool AllowSPRespawn() { return false; } +#endif + virtual bool AllowAutoTargetCrosshair( void ) { return TRUE; }; virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args ); // handles the user commands; returns TRUE if command handled properly virtual void ClientSettingsChanged( CBasePlayer *pPlayer ); // the player has changed cvars diff --git a/mp/src/game/shared/gamestringpool.cpp b/mp/src/game/shared/gamestringpool.cpp index f75d4ff9..ee529bf1 100644 --- a/mp/src/game/shared/gamestringpool.cpp +++ b/mp/src/game/shared/gamestringpool.cpp @@ -12,6 +12,9 @@ #include "igamesystem.h" #endif #include "gamestringpool.h" +#if defined(MAPBASE) && defined(GAME_DLL) +#include "mapbase/GlobalStrings.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -26,6 +29,9 @@ class CGameStringPool : public CBaseGameSystem #endif { virtual char const *Name() { return "CGameStringPool"; } +#if defined(MAPBASE) && defined(GAME_DLL) + virtual void LevelInitPreEntity() { InitGlobalStrings(); } +#endif virtual void LevelShutdownPostEntity() { FreeAll(); } void FreeAll() @@ -50,15 +56,16 @@ public: void Dump( void ) { CUtlVector strings( 0, m_Strings.Count() ); - for (UtlHashHandle_t i = m_Strings.FirstHandle(); i != m_Strings.InvalidHandle(); i = m_Strings.NextHandle(i)) + for ( UtlHashHandle_t i = m_Strings.FirstHandle(); i != m_Strings.InvalidHandle(); i = m_Strings.NextHandle(i) ) { - strings.AddToTail( strings[i] ); + strings.AddToTail( m_Strings[i] ); } + struct _Local { static int __cdecl F(const char * const *a, const char * const *b) { return strcmp(*a, *b); } }; strings.Sort( _Local::F ); - + for ( int i = 0; i < strings.Count(); ++i ) { DevMsg( " %d (0x%p) : %s\n", i, strings[i], strings[i] ); diff --git a/mp/src/game/shared/hl2/hl2_gamerules.cpp b/mp/src/game/shared/hl2/hl2_gamerules.cpp index bdd5abec..d805c8f8 100644 --- a/mp/src/game/shared/hl2/hl2_gamerules.cpp +++ b/mp/src/game/shared/hl2/hl2_gamerules.cpp @@ -38,10 +38,217 @@ BEGIN_NETWORK_TABLE_NOBASE( CHalfLife2, DT_HL2GameRules ) #endif END_NETWORK_TABLE() +#if MAPBASE && GAME_DLL +extern bool g_bUseLegacyFlashlight; +extern bool g_bCacheLegacyFlashlightStatus; + +BEGIN_DATADESC( CHalfLife2Proxy ) + + // These get the gamerules values on save and write to them on restore + DEFINE_FIELD( m_save_DefaultCitizenType, FIELD_INTEGER ), + DEFINE_FIELD( m_save_LegacyFlashlight, FIELD_CHARACTER ), + DEFINE_FIELD( m_save_PlayerSquadAutosummonDisabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_save_StunstickPickupBehavior, FIELD_INTEGER ), + DEFINE_FIELD( m_save_AllowSPRespawn, FIELD_BOOLEAN ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "EpisodicOn", InputEpisodicOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "EpisodicOff", InputEpisodicOff ), + + // These are FIELD_STRING because they call KeyValue() directly + DEFINE_INPUTFUNC( FIELD_STRING, "SetFriendlyFire", InputSetFriendlyFire ), + DEFINE_INPUTFUNC( FIELD_STRING, "DefaultCitizenType", InputSetDefaultCitizenType ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetLegacyFlashlight", InputSetLegacyFlashlight ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerSquadAutosummon", InputSetPlayerSquadAutosummon ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetStunstickPickupBehavior", InputSetStunstickPickupBehavior ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetAllowSPRespawn", InputSetAllowSPRespawn ), + +END_DATADESC() +#endif LINK_ENTITY_TO_CLASS( hl2_gamerules, CHalfLife2Proxy ); IMPLEMENT_NETWORKCLASS_ALIASED( HalfLife2Proxy, DT_HalfLife2Proxy ) +#if defined(MAPBASE) && defined(GAME_DLL) +void CHalfLife2Proxy::InputEpisodicOn( inputdata_t &inputdata ) { KeyValue("SetEpisodic", "1"); } +void CHalfLife2Proxy::InputEpisodicOff( inputdata_t &inputdata ) { KeyValue("SetEpisodic", "0"); } +void CHalfLife2Proxy::InputSetFriendlyFire( inputdata_t &inputdata ) { KeyValue("GlobalFriendlyFire", inputdata.value.String()); } +void CHalfLife2Proxy::InputSetDefaultCitizenType( inputdata_t &inputdata ) { KeyValue("DefaultCitizenType", inputdata.value.String()); } +void CHalfLife2Proxy::InputSetLegacyFlashlight( inputdata_t &inputdata ) { KeyValue("SetLegacyFlashlight", inputdata.value.String()); } +void CHalfLife2Proxy::InputSetPlayerSquadAutosummon( inputdata_t &inputdata ) { KeyValue("SetPlayerSquadAutosummon", inputdata.value.String()); } +void CHalfLife2Proxy::InputSetStunstickPickupBehavior( inputdata_t &inputdata ) { KeyValue("SetStunstickPickupBehavior", inputdata.value.String()); } +void CHalfLife2Proxy::InputSetAllowSPRespawn( inputdata_t &inputdata ) { KeyValue( "SetAllowSPRespawn", inputdata.value.String() ); } + +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CHalfLife2Proxy::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "DefaultCitizenType")) + { + HL2GameRules()->SetDefaultCitizenType(atoi(szValue)); + } + else if (FStrEq(szKeyName, "GlobalFriendlyFire")) + { + HL2GameRules()->SetGlobalFriendlyFire(TO_THREESTATE(atoi(szValue))); + } + else if (FStrEq(szKeyName, "SetEpisodic") && !FStrEq(szValue, "2")) + { + hl2_episodic.SetValue(!FStrEq(szValue, "0")); + } + else if (FStrEq(szKeyName, "SetLegacyFlashlight")) + { + // Turn off flashlights first + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer ) + { + if (pPlayer->FlashlightIsOn()) + pPlayer->FlashlightTurnOff(); + } + } + + g_bUseLegacyFlashlight = !FStrEq(szValue, "0"); + + // We have overridden it, don't test directory + g_bCacheLegacyFlashlightStatus = false; + + // Tell our save/load we've modified it + // 1 = modified, 2 = legacy enabled + m_save_LegacyFlashlight |= 1; + if (g_bUseLegacyFlashlight) + m_save_LegacyFlashlight |= 2; + } + else if (FStrEq(szKeyName, "SetPlayerSquadAutosummon")) + { + HL2GameRules()->SetAutosummonDisabled(FStrEq(szValue, "0")); + } + else if (FStrEq(szKeyName, "SetStunstickPickupBehavior")) + { + HL2GameRules()->SetStunstickPickupBehavior(atoi(szValue)); + } + else if (FStrEq(szKeyName, "SetAllowSPRespawn")) + { + HL2GameRules()->SetAllowSPRespawn(!FStrEq(szValue, "0")); + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} + +bool CHalfLife2Proxy::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if (FStrEq(szKeyName, "DefaultCitizenType")) + { + Q_snprintf( szValue, iMaxLen, "%i", HL2GameRules()->GetDefaultCitizenType() ); + } + else if (FStrEq(szKeyName, "GlobalFriendlyFire")) + { + Q_snprintf( szValue, iMaxLen, "%i", HL2GameRules()->GlobalFriendlyFire() ); + } + else if (FStrEq(szKeyName, "SetEpisodic") && !FStrEq(szValue, "2")) + { + Q_snprintf( szValue, iMaxLen, "%s", hl2_episodic.GetString() ); + } + else if (FStrEq(szKeyName, "SetLegacyFlashlight")) + { + Q_snprintf( szValue, iMaxLen, "%d", g_bUseLegacyFlashlight ); + } + else if (FStrEq(szKeyName, "SetPlayerSquadAutosummon")) + { + Q_snprintf( szValue, iMaxLen, "%d", HL2GameRules()->AutosummonDisabled() ); + } + else if (FStrEq(szKeyName, "SetStunstickPickupBehavior")) + { + Q_snprintf( szValue, iMaxLen, "%i", HL2GameRules()->GetStunstickPickupBehavior() ); + } + else if (FStrEq(szKeyName, "SetAllowSPRespawn")) + { + Q_snprintf( szValue, iMaxLen, "%d", HL2GameRules()->AllowSPRespawn() ); + } + else + { + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Saves the current object out to disk, by iterating through the objects +// data description hierarchy +// Input : &save - save buffer which the class data is written to +// Output : int - 0 if the save failed, 1 on success +//----------------------------------------------------------------------------- +int CHalfLife2Proxy::Save( ISave &save ) +{ + m_save_DefaultCitizenType = HL2GameRules()->GetDefaultCitizenType(); + m_save_PlayerSquadAutosummonDisabled = HL2GameRules()->AutosummonDisabled(); + + // As a static variable, this is actually kept across save games, but lost when the game exits. + // NOTE: Now set in KeyValue() directly + //m_save_LegacyFlashlight = (g_bUseLegacyFlashlight); + + m_save_StunstickPickupBehavior = HL2GameRules()->GetStunstickPickupBehavior(); + + m_save_AllowSPRespawn = HL2GameRules()->AllowSPRespawn(); + + return BaseClass::Save(save); +} + +//----------------------------------------------------------------------------- +// Purpose: Restores the current object from disk, by iterating through the objects +// data description hierarchy +// Input : &restore - restore buffer which the class data is read from +// Output : int - 0 if the restore failed, 1 on success +//----------------------------------------------------------------------------- +int CHalfLife2Proxy::Restore( IRestore &restore ) +{ + int base = BaseClass::Restore(restore); + + HL2GameRules()->SetDefaultCitizenType(m_save_DefaultCitizenType); + HL2GameRules()->SetAutosummonDisabled(m_save_PlayerSquadAutosummonDisabled); + + // Are we modding the legacy flashlight? + if (m_save_LegacyFlashlight & 1) + { + g_bUseLegacyFlashlight = (m_save_LegacyFlashlight & 2) != 0; + + // If we've got the desired legacy flashlight state saved, don't bother caching. + g_bCacheLegacyFlashlightStatus = false; + } + + HL2GameRules()->SetStunstickPickupBehavior(m_save_StunstickPickupBehavior); + + HL2GameRules()->SetAllowSPRespawn(m_save_AllowSPRespawn); + + return base; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHalfLife2Proxy::UpdateOnRemove() +{ + // Were we modding the legacy flashlight? + if (m_save_LegacyFlashlight & 1) + { + // Restore the default state. + g_bCacheLegacyFlashlightStatus = true; + } + + BaseClass::UpdateOnRemove(); +} +#endif + #ifdef CLIENT_DLL void RecvProxy_HL2GameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) @@ -128,6 +335,10 @@ ConVar sk_plr_dmg_buckshot ( "sk_plr_dmg_buckshot","0", FCVAR_REPLICATED); ConVar sk_npc_dmg_buckshot ( "sk_npc_dmg_buckshot","0", FCVAR_REPLICATED); ConVar sk_max_buckshot ( "sk_max_buckshot","0", FCVAR_REPLICATED); ConVar sk_plr_num_shotgun_pellets( "sk_plr_num_shotgun_pellets","7", FCVAR_REPLICATED); +#ifdef MAPBASE +ConVar sk_plr_num_shotgun_pellets_double( "sk_plr_num_shotgun_pellets_double","12", FCVAR_REPLICATED); +ConVar sk_npc_num_shotgun_pellets( "sk_npc_num_shotgun_pellets","8", FCVAR_REPLICATED); +#endif ConVar sk_plr_dmg_rpg_round ( "sk_plr_dmg_rpg_round","0", FCVAR_REPLICATED); ConVar sk_npc_dmg_rpg_round ( "sk_npc_dmg_rpg_round","0", FCVAR_REPLICATED); @@ -206,7 +417,11 @@ bool CHalfLife2::Damage_IsTimeBased( int iDmgType ) // Damage types that are time-based. #ifdef HL2_EPISODIC // This makes me think EP2 should have its own rules, but they are #ifdef all over in here. +#ifdef MAPBASE + return ( ( iDmgType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) ) != 0 ); +#else return ( ( iDmgType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_SLOWBURN ) ) != 0 ); +#endif #else return BaseClass::Damage_IsTimeBased( iDmgType ); #endif @@ -253,6 +468,12 @@ ConVar alyx_darkness_force( "alyx_darkness_force", "0", FCVAR_CHEAT | FCVAR_REP m_flLastHealthDropTime = 0.0f; m_flLastGrenadeDropTime = 0.0f; + +#ifdef MAPBASE + m_DefaultCitizenType = 0; + m_bPlayerSquadAutosummonDisabled = false; + m_bAllowSPRespawn = false; +#endif } //----------------------------------------------------------------------------- @@ -1291,6 +1512,10 @@ ConVar alyx_darkness_force( "alyx_darkness_force", "0", FCVAR_CHEAT | FCVAR_REP { case CLASS_NONE: return "CLASS_NONE"; case CLASS_PLAYER: return "CLASS_PLAYER"; +#ifdef MAPBASE + case CLASS_PLAYER_ALLY: return "CLASS_PLAYER_ALLY"; + case CLASS_PLAYER_ALLY_VITAL: return "CLASS_PLAYER_ALLY_VITAL"; +#endif case CLASS_ANTLION: return "CLASS_ANTLION"; case CLASS_BARNACLE: return "CLASS_BARNACLE"; case CLASS_BULLSEYE: return "CLASS_BULLSEYE"; @@ -1314,6 +1539,9 @@ ConVar alyx_darkness_force( "alyx_darkness_force", "0", FCVAR_CHEAT | FCVAR_REP case CLASS_MISSILE: return "CLASS_MISSILE"; case CLASS_FLARE: return "CLASS_FLARE"; case CLASS_EARTH_FAUNA: return "CLASS_EARTH_FAUNA"; +#ifdef MAPBASE + case CLASS_HACKED_ROLLERMINE: return "CLASS_HACKED_ROLLERMINE"; +#endif default: return "MISSING CLASS in ClassifyText()"; } @@ -1400,7 +1628,12 @@ ConVar alyx_darkness_force( "alyx_darkness_force", "0", FCVAR_CHEAT | FCVAR_REP // came from the player's physcannon. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); +#ifdef MAPBASE + // Friendly fire needs to be handled here. + if ( pPlayer && !pVictim->MyNPCPointer()->FriendlyFireEnabled() ) +#else if( pPlayer ) +#endif { CBaseEntity *pWeapon = pPlayer->HasNamedPlayerItem("weapon_physcannon"); @@ -1769,6 +2002,96 @@ bool CHalfLife2::ShouldBurningPropsEmitLight() } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Gets the default citizen type. +//----------------------------------------------------------------------------- +int CHalfLife2::GetDefaultCitizenType() +{ + return m_DefaultCitizenType; +} + +//----------------------------------------------------------------------------- +// Sets the default citizen type. +//----------------------------------------------------------------------------- +void CHalfLife2::SetDefaultCitizenType(int val) +{ + m_DefaultCitizenType = val; +} + +//----------------------------------------------------------------------------- +// Gets our global friendly fire override. +//----------------------------------------------------------------------------- +ThreeState_t CHalfLife2::GlobalFriendlyFire() +{ + return GlobalEntity_IsInTable(FRIENDLY_FIRE_GLOBALNAME) ? TO_THREESTATE(GlobalEntity_GetState(FRIENDLY_FIRE_GLOBALNAME)) : TRS_NONE; +} + +//----------------------------------------------------------------------------- +// Sets our global friendly fire override. +//----------------------------------------------------------------------------- +void CHalfLife2::SetGlobalFriendlyFire(ThreeState_t val) +{ + GlobalEntity_Add(MAKE_STRING(FRIENDLY_FIRE_GLOBALNAME), gpGlobals->mapname, (GLOBALESTATE)val); +} + +//----------------------------------------------------------------------------- +// Gets our autosummon setting. +//----------------------------------------------------------------------------- +bool CHalfLife2::AutosummonDisabled() +{ + return m_bPlayerSquadAutosummonDisabled; +} + +//----------------------------------------------------------------------------- +// Sets our autosummon setting. +//----------------------------------------------------------------------------- +void CHalfLife2::SetAutosummonDisabled(bool toggle) +{ + m_bPlayerSquadAutosummonDisabled = toggle; +} + +//----------------------------------------------------------------------------- +// Gets our stunstick pickup setting. +//----------------------------------------------------------------------------- +int CHalfLife2::GetStunstickPickupBehavior() +{ + return m_StunstickPickupBehavior; +} + +//----------------------------------------------------------------------------- +// Sets our stunstick pickup setting. +//----------------------------------------------------------------------------- +void CHalfLife2::SetStunstickPickupBehavior(int val) +{ + m_StunstickPickupBehavior = val; +} + +//----------------------------------------------------------------------------- +// Gets our SP respawn setting. +//----------------------------------------------------------------------------- +bool CHalfLife2::AllowSPRespawn() +{ + return m_bAllowSPRespawn; +} + +//----------------------------------------------------------------------------- +// Sets our SP respawn setting. +//----------------------------------------------------------------------------- +void CHalfLife2::SetAllowSPRespawn( bool toggle ) +{ + m_bAllowSPRespawn = toggle; +} + +//BEGIN_SIMPLE_DATADESC( CHalfLife2 ) +// +// DEFINE_FIELD( m_DefaultCitizenType, FIELD_INTEGER ), +// DEFINE_FIELD( m_bPlayerSquadAutosummonDisabled, FIELD_BOOLEAN ), +// +//END_DATADESC() +#endif + + #endif//CLIENT_DLL // ------------------------------------------------------------------------------------ // @@ -1868,9 +2191,20 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_grenade", 0, 0); #ifdef HL2_EPISODIC def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0); +#ifdef MAPBASE + // + // Always gibbing would make it look better on antlions, etc. + // Hopefully this is very good and isn't bad. + // + def.AddAmmoType("CombineHeavyCannon", DMG_BULLET | DMG_ALWAYSGIB, TRACER_LINE, 40, 40, NULL, 10 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 10 kg weight at 750 ft/s +#else def.AddAmmoType("CombineHeavyCannon", DMG_BULLET, TRACER_LINE, 40, 40, NULL, 10 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 10 kg weight at 750 ft/s +#endif def.AddAmmoType("ammo_proto1", DMG_BULLET, TRACER_LINE, 0, 0, 10, 0, 0 ); #endif // HL2_EPISODIC +#ifdef MAPBASE + def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); +#endif } return &def; diff --git a/mp/src/game/shared/hl2/hl2_gamerules.h b/mp/src/game/shared/hl2/hl2_gamerules.h index 4f64793f..a8eace12 100644 --- a/mp/src/game/shared/hl2/hl2_gamerules.h +++ b/mp/src/game/shared/hl2/hl2_gamerules.h @@ -19,12 +19,46 @@ #define CHalfLife2Proxy C_HalfLife2Proxy #endif +#if MAPBASE && GAME_DLL +#define FRIENDLY_FIRE_GLOBALNAME "friendly_fire_override" +#endif + class CHalfLife2Proxy : public CGameRulesProxy { public: DECLARE_CLASS( CHalfLife2Proxy, CGameRulesProxy ); DECLARE_NETWORKCLASS(); + +#if defined(MAPBASE) && defined(GAME_DLL) + bool KeyValue( const char *szKeyName, const char *szValue ); + bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + + virtual int Save( ISave &save ); + virtual int Restore( IRestore &restore ); + virtual void UpdateOnRemove(); + + // Inputs + void InputEpisodicOn( inputdata_t &inputdata ); + void InputEpisodicOff( inputdata_t &inputdata ); + void InputSetFriendlyFire( inputdata_t &inputdata ); + void InputSetDefaultCitizenType( inputdata_t &inputdata ); + void InputSetLegacyFlashlight( inputdata_t &inputdata ); + void InputSetPlayerSquadAutosummon( inputdata_t &inputdata ); + void InputSetStunstickPickupBehavior( inputdata_t &inputdata ); + void InputSetAllowSPRespawn( inputdata_t &inputdata ); + + // Gamerules classes don't seem to support datadescs, so the hl2_gamerules entity takes the current values + // from the actual gamerules and saves them in the entity itself, where they're saved via the entity's own datadesc. + // When the save is loaded, the entity sets the main gamerules values according to what was saved. + int m_save_DefaultCitizenType; + char m_save_LegacyFlashlight; + bool m_save_PlayerSquadAutosummonDisabled; + int m_save_StunstickPickupBehavior; + bool m_save_AllowSPRespawn; + + DECLARE_DATADESC(); +#endif }; @@ -47,6 +81,10 @@ public: virtual void LevelInitPreEntity(); #endif +#ifdef MAPBASE_VSCRIPT + virtual void RegisterScriptFunctions( void ); +#endif + private: // Rules change for the mega physgun CNetworkVar( bool, m_bMegaPhysgun ); @@ -88,11 +126,35 @@ public: virtual bool IsAlyxInDarknessMode(); +#ifdef MAPBASE + int GetDefaultCitizenType(); + void SetDefaultCitizenType(int val); + + ThreeState_t GlobalFriendlyFire(); + void SetGlobalFriendlyFire(ThreeState_t val); + + bool AutosummonDisabled(); + void SetAutosummonDisabled(bool toggle); + + int GetStunstickPickupBehavior(); + void SetStunstickPickupBehavior(int val); + + virtual bool AllowSPRespawn(); + void SetAllowSPRespawn( bool toggle ); +#endif + private: float m_flLastHealthDropTime; float m_flLastGrenadeDropTime; +#ifdef MAPBASE + int m_DefaultCitizenType; + bool m_bPlayerSquadAutosummonDisabled; + int m_StunstickPickupBehavior; + bool m_bAllowSPRespawn; +#endif + void AdjustPlayerDamageTaken( CTakeDamageInfo *pInfo ); float AdjustPlayerDamageInflicted( float damage ); diff --git a/mp/src/game/shared/hl2/hl2_usermessages.cpp b/mp/src/game/shared/hl2/hl2_usermessages.cpp index a5c81329..0ab97330 100644 --- a/mp/src/game/shared/hl2/hl2_usermessages.cpp +++ b/mp/src/game/shared/hl2/hl2_usermessages.cpp @@ -41,7 +41,12 @@ void RegisterUserMessages( void ) usermessages->Register( "KeyHintText", -1 ); // Displays hint text display usermessages->Register( "SquadMemberDied", 0 ); usermessages->Register( "AmmoDenied", 2 ); +#ifdef MAPBASE + // This sends the credits file now + usermessages->Register( "CreditsMsg", -1 ); +#else usermessages->Register( "CreditsMsg", 1 ); +#endif usermessages->Register( "LogoTimeMsg", 4 ); usermessages->Register( "AchievementEvent", -1 ); usermessages->Register( "UpdateJalopyRadar", -1 ); diff --git a/mp/src/game/shared/hl2/hl_gamemovement.cpp b/mp/src/game/shared/hl2/hl_gamemovement.cpp index e7e573ec..d4fc3aef 100644 --- a/mp/src/game/shared/hl2/hl_gamemovement.cpp +++ b/mp/src/game/shared/hl2/hl_gamemovement.cpp @@ -9,6 +9,10 @@ #include "utlrbtree.h" #include "hl2_shareddefs.h" +#ifdef HL2MP +#include "hl2mp_gamerules.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -283,7 +287,11 @@ bool CHL2GameMovement::ContinueForcedMove() //----------------------------------------------------------------------------- bool CHL2GameMovement::OnLadder( trace_t &trace ) { +#ifdef HL2_USES_FUNC_LADDER_CODE + return ( GetLadder() != NULL ) ? true : BaseClass::OnLadder(trace); +#else return ( GetLadder() != NULL ) ? true : false; +#endif } //----------------------------------------------------------------------------- @@ -378,6 +386,104 @@ void CHL2GameMovement::Findladder( float maxdist, CFuncLadder **ppLadder, Vector } +#ifdef MAPBASE +bool CHL2GameMovement::ForcePlayerOntoLadder(CFuncLadder *ladder) +{ + Vector topPosition; + Vector bottomPosition; + + ladder->GetTopPosition(topPosition); + ladder->GetBottomPosition(bottomPosition); + + Vector closest; + CalcClosestPointOnLineSegment(mv->GetAbsOrigin(), bottomPosition, topPosition, closest, NULL); + + StartForcedMove( true, player->MaxSpeed(), closest, ladder ); + + return true; +} + +bool CHL2GameMovement::MountPlayerOntoLadder(CFuncLadder *ladder) +{ + Vector topPosition; + Vector bottomPosition; + + ladder->GetTopPosition(topPosition); + ladder->GetBottomPosition(bottomPosition); + + Vector closest; + CalcClosestPointOnLineSegment(mv->GetAbsOrigin(), bottomPosition, topPosition, closest, NULL); + +#if 0 + // Compute parametric distance along ladder vector... + float oldt; + CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &oldt ); + + // Perform the move accounting for any base velocity. + VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity); + TryPlayerMove(); + VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity); + + // Pressed buttons are "changed(xor)" and'ed with the mask of currently held buttons + bool pressing_forward_or_side = mv->m_flForwardMove != 0.0f || mv->m_flSideMove != 0.0f; + + Vector ladderVec = topPosition - bottomPosition; + float LadderLength = VectorNormalize( ladderVec ); + // This test is not perfect by any means, but should help a bit + bool moving_along_ladder = false; + if ( pressing_forward_or_side ) + { + float fwdDot = m_vecForward.Dot( ladderVec ); + if ( fabs( fwdDot ) > 0.9f ) + { + moving_along_ladder = true; + } + } + + // Compute parametric distance along ladder vector... + float newt; + CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &newt ); + + // Fudge of 2 units + float tolerance = 1.0f / LadderLength; + + bool wouldleaveladder = false; + // Moving pPast top or bottom? + if ( newt < -tolerance ) + { + wouldleaveladder = newt < oldt; + } + else if ( newt > ( 1.0f + tolerance ) ) + { + wouldleaveladder = newt > oldt; + } + + // See if we are near the top or bottom but not moving + float dist1sqr, dist2sqr; + + dist1sqr = ( topPosition - mv->GetAbsOrigin() ).LengthSqr(); + dist2sqr = ( bottomPosition - mv->GetAbsOrigin() ).LengthSqr(); + + float dist = MIN( dist1sqr, dist2sqr ); + bool neardismountnode = ( dist < 16.0f * 16.0f ) ? true : false; + float ladderUnitsPerTick = ( MAX_CLIMB_SPEED * gpGlobals->interval_per_tick ); + bool neardismountnode2 = ( dist < ladderUnitsPerTick * ladderUnitsPerTick ) ? true : false; + + // Really close to node, cvar is set, and pressing a key, then simulate a +USE + bool auto_dismount_use = ( neardismountnode2 && + sv_autoladderdismount.GetBool() && + pressing_forward_or_side && + !moving_along_ladder ); + + bool fully_underwater = ( player->GetWaterLevel() == WL_Eyes ) ? true : false; +#endif + + StartForcedMove( true, player->MaxSpeed(), closest, ladder ); + + return true; +} +#endif + static bool NearbyDismountLessFunc( const NearbyDismount_t& lhs, const NearbyDismount_t& rhs ) { return lhs.distSqr < rhs.distSqr; @@ -527,6 +633,9 @@ void CHL2GameMovement::FullLadderMove() Assert( ladder ); if ( !ladder ) { +#ifdef HL2_USES_FUNC_LADDER_CODE + BaseClass::FullLadderMove(); +#endif return; } @@ -887,7 +996,11 @@ bool CHL2GameMovement::LadderMove( void ) if ( player->GetMoveType() == MOVETYPE_NOCLIP ) { SetLadder( NULL ); +#ifdef HL2_USES_FUNC_LADDER_CODE + return BaseClass::LadderMove(); +#else return false; +#endif } // If being forced to mount/dismount continue to act like we are on the ladder @@ -954,7 +1067,11 @@ bool CHL2GameMovement::LadderMove( void ) } } +#ifdef HL2_USES_FUNC_LADDER_CODE + return BaseClass::LadderMove(); +#else return false; +#endif } if ( !ladder && @@ -968,7 +1085,11 @@ bool CHL2GameMovement::LadderMove( void ) ladder = GetLadder(); if ( !ladder ) { +#ifdef HL2_USES_FUNC_LADDER_CODE + return BaseClass::LadderMove(); +#else return false; +#endif } // Don't play the deny sound @@ -1032,7 +1153,11 @@ bool CHL2GameMovement::LadderMove( void ) { mv->m_vecVelocity.z = mv->m_vecVelocity.z + 50; } +#ifdef HL2_USES_FUNC_LADDER_CODE + return BaseClass::LadderMove(); +#else return false; +#endif } if ( forwardSpeed != 0 || rightSpeed != 0 ) @@ -1064,7 +1189,11 @@ bool CHL2GameMovement::LadderMove( void ) player->SetMoveType( MOVETYPE_WALK ); // Remove from ladder SetLadder( NULL ); +#ifdef HL2_USES_FUNC_LADDER_CODE + return BaseClass::LadderMove(); +#else return false; +#endif } bool ishorizontal = fabs( topPosition.z - bottomPosition.z ) < 64.0f ? true : false; @@ -1142,6 +1271,29 @@ bool CHL2GameMovement::CanAccelerate() return true; } +//----------------------------------------------------------------------------- +// Purpose: Allow bots etc to use slightly different solid masks +//----------------------------------------------------------------------------- +unsigned int CHL2GameMovement::PlayerSolidMask( bool brushOnly ) +{ + int mask = 0; +#ifdef HL2MP + if ( HL2MPRules()->IsTeamplay() ) + { + switch ( player->GetTeamNumber() ) + { + case TEAM_REBELS: + mask = CONTENTS_TEAM1; + break; + + case TEAM_COMBINE: + mask = CONTENTS_TEAM2; + break; + } + } +#endif + return ( mask | BaseClass::PlayerSolidMask( brushOnly ) ); +} #ifndef PORTAL // Portal inherits from this but needs to declare it's own global interface // Expose our interface. diff --git a/mp/src/game/shared/hl2/hl_gamemovement.h b/mp/src/game/shared/hl2/hl_gamemovement.h index 1e9abefe..fba5c8c1 100644 --- a/mp/src/game/shared/hl2/hl_gamemovement.h +++ b/mp/src/game/shared/hl2/hl_gamemovement.h @@ -44,6 +44,15 @@ public: virtual void SetGroundEntity( trace_t *pm ); virtual bool CanAccelerate( void ); + virtual unsigned int PlayerSolidMask( bool brushOnly = false ); + +#ifdef MAPBASE + // Called by mappers who need a player to be on a ladder. + bool ForcePlayerOntoLadder(CFuncLadder *ladder); + // Called by mappers who want a player to be on a ladder. + bool MountPlayerOntoLadder(CFuncLadder *ladder); +#endif + private: // See if we are pressing use near a ladder "mount" point and if so, latch us onto the ladder diff --git a/mp/src/game/shared/hl2mp/weapon_physcannon.cpp b/mp/src/game/shared/hl2mp/weapon_physcannon.cpp index 4972a1ed..09be768a 100644 --- a/mp/src/game/shared/hl2mp/weapon_physcannon.cpp +++ b/mp/src/game/shared/hl2mp/weapon_physcannon.cpp @@ -3592,6 +3592,19 @@ CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon ) return NULL; } +CBaseEntity *GetPlayerHeldEntity( CBasePlayer *pPlayer ) +{ + CBaseEntity *pObject = NULL; + CPlayerPickupController *pPlayerPickupController = (CPlayerPickupController *)(pPlayer->GetUseEntity()); + + if ( pPlayerPickupController ) + { + pObject = pPlayerPickupController->GetGrabController().GetAttached(); + } + + return pObject; +} + float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject ) { float mass = 0.0f; diff --git a/mp/src/game/shared/hl2mp/weapon_slam.cpp b/mp/src/game/shared/hl2mp/weapon_slam.cpp index bcdec40c..5bdea44f 100644 --- a/mp/src/game/shared/hl2mp/weapon_slam.cpp +++ b/mp/src/game/shared/hl2mp/weapon_slam.cpp @@ -11,9 +11,17 @@ #include "engine/IEngineSound.h" #if defined( CLIENT_DLL ) +#ifdef HL2MP #include "c_hl2mp_player.h" #else + #include "hl2_player_shared.h" +#endif +#else +#ifdef HL2MP #include "hl2mp_player.h" +#else + #include "hl2_player.h" +#endif #include "grenade_tripmine.h" #include "grenade_satchel.h" #include "entitylist.h" @@ -25,6 +33,11 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifndef HL2MP +#define ToHL2MPPlayer(ent) dynamic_cast(ent) +#define CHL2MP_Player CHL2_Player +#endif + #define SLAM_PRIMARY_VOLUME 450 IMPLEMENT_NETWORKCLASS_ALIASED( Weapon_SLAM, DT_Weapon_SLAM ) @@ -114,7 +127,9 @@ void CWeapon_SLAM::Spawn( ) Precache( ); +#if defined(HL2MP) || !defined(CLIENT_DLL) FallInit();// get ready to fall down +#endif m_tSlamState = (int)SLAM_SATCHEL_THROW; m_flWallSwitchTime = 0; @@ -755,6 +770,14 @@ void CWeapon_SLAM::ItemPostFrame( void ) SLAMThink(); +#ifdef MAPBASE + if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) + { + WeaponIdle(); + return; + } +#endif + if ((pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) { SecondaryAttack(); diff --git a/mp/src/game/shared/hl2mp/weapon_slam.h b/mp/src/game/shared/hl2mp/weapon_slam.h index 0b59bed7..51a48155 100644 --- a/mp/src/game/shared/hl2mp/weapon_slam.h +++ b/mp/src/game/shared/hl2mp/weapon_slam.h @@ -15,7 +15,11 @@ #define WEAPONSLAM_H #include "basegrenade_shared.h" +#ifdef HL2MP #include "weapon_hl2mpbasehlmpcombatweapon.h" +#else +#include "basehlcombatweapon_shared.h" +#endif enum { @@ -28,6 +32,10 @@ enum #define CWeapon_SLAM C_Weapon_SLAM #endif +#ifndef HL2MP +#define CBaseHL2MPCombatWeapon CBaseHLCombatWeapon +#endif + class CWeapon_SLAM : public CBaseHL2MPCombatWeapon { public: diff --git a/mp/src/game/shared/hl2mp/weapon_smg1.cpp b/mp/src/game/shared/hl2mp/weapon_smg1.cpp index bd6e1d42..6fa863ba 100644 --- a/mp/src/game/shared/hl2mp/weapon_smg1.cpp +++ b/mp/src/game/shared/hl2mp/weapon_smg1.cpp @@ -99,6 +99,19 @@ acttable_t CWeaponSMG1::m_acttable[] = }; IMPLEMENT_ACTTABLE(CWeaponSMG1); + +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the SMG1's activity table. +acttable_t *GetSMG1Acttable() +{ + return CWeaponSMG1::m_acttable; +} + +int GetSMG1ActtableCount() +{ + return ARRAYSIZE(CWeaponSMG1::m_acttable); +} +#endif #endif //========================================================= diff --git a/mp/src/game/shared/hl2mp/weapon_stunstick.cpp b/mp/src/game/shared/hl2mp/weapon_stunstick.cpp index 9f706983..649101dd 100644 --- a/mp/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/mp/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -7,7 +7,10 @@ #include "cbase.h" #include "npcevent.h" +#include "weapon_stunstick.h" +#ifdef HL2MP #include "weapon_hl2mpbasebasebludgeon.h" +#endif #include "IEffects.h" #include "debugoverlay_shared.h" @@ -36,98 +39,11 @@ extern ConVar metropolice_move_and_melee; -#define STUNSTICK_RANGE 75.0f -#define STUNSTICK_REFIRE 0.8f -#define STUNSTICK_BEAM_MATERIAL "sprites/lgtning.vmt" -#define STUNSTICK_GLOW_MATERIAL "sprites/light_glow02_add" -#define STUNSTICK_GLOW_MATERIAL2 "effects/blueflare1" -#define STUNSTICK_GLOW_MATERIAL_NOZ "sprites/light_glow02_add_noz" - -#ifdef CLIENT_DLL -#define CWeaponStunStick C_WeaponStunStick +#ifdef MAPBASE +ConVar sk_plr_dmg_stunstick ( "sk_plr_dmg_stunstick","0"); +ConVar sk_npc_dmg_stunstick ( "sk_npc_dmg_stunstick","0"); #endif -class CWeaponStunStick : public CBaseHL2MPBludgeonWeapon -{ - DECLARE_CLASS( CWeaponStunStick, CBaseHL2MPBludgeonWeapon ); - -public: - - CWeaponStunStick(); - - DECLARE_NETWORKCLASS(); - DECLARE_PREDICTABLE(); - -#ifndef CLIENT_DLL - DECLARE_ACTTABLE(); -#endif - -#ifdef CLIENT_DLL - virtual int DrawModel( int flags ); - virtual void ClientThink( void ); - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual RenderGroup_t GetRenderGroup( void ); - virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel ); - -#endif - - virtual void Precache(); - - void Spawn(); - - float GetRange( void ) { return STUNSTICK_RANGE; } - float GetFireRate( void ) { return STUNSTICK_REFIRE; } - - - bool Deploy( void ); - bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); - - void Drop( const Vector &vecVelocity ); - void ImpactEffect( trace_t &traceHit ); - void SecondaryAttack( void ) {} - void SetStunState( bool state ); - bool GetStunState( void ); - -#ifndef CLIENT_DLL - void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - int WeaponMeleeAttack1Condition( float flDot, float flDist ); -#endif - - float GetDamageForActivity( Activity hitActivity ); - - CWeaponStunStick( const CWeaponStunStick & ); - -private: - -#ifdef CLIENT_DLL - - #define NUM_BEAM_ATTACHMENTS 9 - - struct stunstickBeamInfo_t - { - int IDs[2]; // 0 - top, 1 - bottom - }; - - stunstickBeamInfo_t m_BeamAttachments[NUM_BEAM_ATTACHMENTS]; // Lookup for arc attachment points on the head of the stick - int m_BeamCenterAttachment; // "Core" of the effect (center of the head) - - void SetupAttachmentPoints( void ); - void DrawFirstPersonEffects( void ); - void DrawThirdPersonEffects( void ); - void DrawEffects( void ); - bool InSwing( void ); - - bool m_bSwungLastFrame; - - #define FADE_DURATION 0.25f - - float m_flFadeTime; - -#endif - - CNetworkVar( bool, m_bActive ); -}; - //----------------------------------------------------------------------------- // CWeaponStunStick //----------------------------------------------------------------------------- @@ -153,6 +69,7 @@ PRECACHE_WEAPON_REGISTER( weapon_stunstick ); acttable_t CWeaponStunStick::m_acttable[] = { +#ifdef HL2MP { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, @@ -161,6 +78,9 @@ acttable_t CWeaponStunStick::m_acttable[] = { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, +#endif + { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true }, }; IMPLEMENT_ACTTABLE(CWeaponStunStick); @@ -213,7 +133,14 @@ void CWeaponStunStick::Precache() //----------------------------------------------------------------------------- float CWeaponStunStick::GetDamageForActivity( Activity hitActivity ) { +#ifdef MAPBASE + if ( ( GetOwner() != NULL ) && ( GetOwner()->IsPlayer() ) ) + return sk_plr_dmg_stunstick.GetFloat(); + + return sk_npc_dmg_stunstick.GetFloat(); +#else return 40.0f; +#endif } //----------------------------------------------------------------------------- @@ -358,6 +285,35 @@ void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseComba CBasePlayer *pPlayer = ToBasePlayer( pHurt ); bool bFlashed = false; + +#ifdef MAPBASE + CNPC_MetroPolice *pCop = dynamic_cast(pOperator); + + if ( pCop != NULL && pPlayer != NULL ) + { + // See if we need to knock out this target + if ( pCop->ShouldKnockOutTarget( pHurt ) ) + { + float yawKick = random->RandomFloat( -48, -24 ); + + //Kick the player angles + pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) ); + + color32 white = {255,255,255,255}; + UTIL_ScreenFade( pPlayer, white, 0.2f, 1.0f, FFADE_OUT|FFADE_PURGE|FFADE_STAYOUT ); + bFlashed = true; + + pCop->KnockOutTarget( pHurt ); + + break; + } + else + { + // Notify that we've stunned a target + pCop->StunnedTarget( pHurt ); + } + } +#endif // Punch angles if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) ) @@ -474,8 +430,19 @@ void CWeaponStunStick::Drop( const Vector &vecVelocity ) SetStunState( false ); #ifndef CLIENT_DLL +#ifdef MAPBASE +#ifdef HL2MP + if (!GetOwner() || GetOwner()->IsNPC()) + BaseClass::Drop(vecVelocity); + else + UTIL_Remove( this ); +#else + BaseClass::Drop(vecVelocity); +#endif +#else UTIL_Remove( this ); #endif +#endif } @@ -566,7 +533,11 @@ int C_WeaponStunStick::DrawModel( int flags ) return 0; // Only render these on the transparent pass +#ifdef MAPBASE + if ( m_bActive && flags & STUDIO_TRANSPARENCY ) +#else if ( flags & STUDIO_TRANSPARENCY ) +#endif { DrawEffects(); return 1; @@ -842,6 +813,40 @@ void C_WeaponStunStick::DrawFirstPersonEffects( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Draw our special effects +//----------------------------------------------------------------------------- +void C_WeaponStunStick::DrawNPCEffects( void ) +{ + if ( m_bActive ) + { + Vector vecOrigin; + QAngle vecAngles; + float color[3]; + + color[0] = color[1] = color[2] = random->RandomFloat( 0.1f, 0.2f ); + + GetAttachment( 1, vecOrigin, vecAngles ); + + Vector vForward; + AngleVectors( vecAngles, &vForward ); + + Vector vEnd = vecOrigin - vForward * 1.0f; + + IMaterial *pMaterial = materials->FindMaterial( "effects/stunstick", NULL, false ); + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->Bind( pMaterial ); + DrawHalo( pMaterial, vEnd, random->RandomFloat( 4.0f, 6.0f ), color ); + + color[0] = color[1] = color[2] = random->RandomFloat( 0.9f, 1.0f ); + + DrawHalo( pMaterial, vEnd, random->RandomFloat( 2.0f, 3.0f ), color ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Draw our special effects //----------------------------------------------------------------------------- @@ -851,6 +856,13 @@ void C_WeaponStunStick::DrawEffects( void ) { DrawFirstPersonEffects(); } +#ifdef MAPBASE + else if ( GetOwner() && GetOwner()->IsNPC() ) + { + // Original HL2 stunstick FX + DrawNPCEffects(); + } +#endif else { DrawThirdPersonEffects(); diff --git a/mp/src/game/shared/hl2mp/weapon_stunstick.h b/mp/src/game/shared/hl2mp/weapon_stunstick.h new file mode 100644 index 00000000..3cfd054a --- /dev/null +++ b/mp/src/game/shared/hl2mp/weapon_stunstick.h @@ -0,0 +1,116 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This is technically a Mapbase addition, but it's just weapon_stunstick's class declaration. +// All actual changes are still nested in #ifdef MAPBASE. +// +//=============================================================================// + +#ifndef WEAPON_STUNSTICK_H +#define WEAPON_STUNSTICK_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef CLIENT_DLL +#include "c_basehlcombatweapon.h" +#else +#include "basebludgeonweapon.h" +#endif + +#define STUNSTICK_RANGE 75.0f +#define STUNSTICK_REFIRE 0.8f +#define STUNSTICK_BEAM_MATERIAL "sprites/lgtning.vmt" +#define STUNSTICK_GLOW_MATERIAL "sprites/light_glow02_add" +#define STUNSTICK_GLOW_MATERIAL2 "effects/blueflare1" +#define STUNSTICK_GLOW_MATERIAL_NOZ "sprites/light_glow02_add_noz" + +#ifdef CLIENT_DLL +#define CWeaponStunStick C_WeaponStunStick +#define CBaseHLBludgeonWeapon C_BaseHLBludgeonWeapon +#endif + +class CWeaponStunStick : public CBaseHLBludgeonWeapon +{ + DECLARE_CLASS( CWeaponStunStick, CBaseHLBludgeonWeapon ); + +public: + + CWeaponStunStick(); + + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + +#ifndef CLIENT_DLL + DECLARE_ACTTABLE(); +#endif + +#ifdef CLIENT_DLL + virtual int DrawModel( int flags ); + virtual void ClientThink( void ); + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual RenderGroup_t GetRenderGroup( void ); + virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel ); + +#endif + + virtual void Precache(); + + void Spawn(); + + float GetRange( void ) { return STUNSTICK_RANGE; } + float GetFireRate( void ) { return STUNSTICK_REFIRE; } + + + bool Deploy( void ); + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + + void Drop( const Vector &vecVelocity ); + void ImpactEffect( trace_t &traceHit ); + void SecondaryAttack( void ) {} + void SetStunState( bool state ); + bool GetStunState( void ); + +#ifndef CLIENT_DLL + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + int WeaponMeleeAttack1Condition( float flDot, float flDist ); +#endif + + float GetDamageForActivity( Activity hitActivity ); + + CWeaponStunStick( const CWeaponStunStick & ); + +private: + +#ifdef CLIENT_DLL + + #define NUM_BEAM_ATTACHMENTS 9 + + struct stunstickBeamInfo_t + { + int IDs[2]; // 0 - top, 1 - bottom + }; + + stunstickBeamInfo_t m_BeamAttachments[NUM_BEAM_ATTACHMENTS]; // Lookup for arc attachment points on the head of the stick + int m_BeamCenterAttachment; // "Core" of the effect (center of the head) + + void SetupAttachmentPoints( void ); + void DrawFirstPersonEffects( void ); + void DrawThirdPersonEffects( void ); +#ifdef MAPBASE + void DrawNPCEffects( void ); +#endif + void DrawEffects( void ); + bool InSwing( void ); + + bool m_bSwungLastFrame; + + #define FADE_DURATION 0.25f + + float m_flFadeTime; + +#endif + + CNetworkVar( bool, m_bActive ); +}; + +#endif // WEAPON_STUNSTICK_H diff --git a/mp/src/game/shared/igamesystem.cpp b/mp/src/game/shared/igamesystem.cpp index 6a4d7f4f..19cbc78c 100644 --- a/mp/src/game/shared/igamesystem.cpp +++ b/mp/src/game/shared/igamesystem.cpp @@ -344,6 +344,15 @@ void IGameSystem::PreClientUpdateAllSystems() #endif +#ifdef MAPBASE_VSCRIPT + +void IGameSystem::RegisterVScriptAllSystems() +{ + InvokeMethod( &IGameSystem::RegisterVScript ); +} + +#endif + //----------------------------------------------------------------------------- // Invokes a method on all installed game systems in proper order diff --git a/mp/src/game/shared/igamesystem.h b/mp/src/game/shared/igamesystem.h index 6dc98350..85621924 100644 --- a/mp/src/game/shared/igamesystem.h +++ b/mp/src/game/shared/igamesystem.h @@ -98,6 +98,13 @@ public: static CBasePlayer *RunCommandPlayer(); static CUserCmd *RunCommandUserCmd(); #endif + +#ifdef MAPBASE_VSCRIPT + // This should be abstract, but there's a lot of systems which derive from + // this interface that would need to have this declared + virtual void RegisterVScript() { ; } + static void RegisterVScriptAllSystems(); +#endif }; class IGameSystemPerFrame : public IGameSystem diff --git a/mp/src/game/shared/in_buttons.h b/mp/src/game/shared/in_buttons.h index 32486853..870961f8 100644 --- a/mp/src/game/shared/in_buttons.h +++ b/mp/src/game/shared/in_buttons.h @@ -11,6 +11,11 @@ #pragma once #endif +#ifdef MAPBASE +// That one article on the VDC. +//#define VGUI_SCREEN_FIX 1 +#endif + #define IN_ATTACK (1 << 0) #define IN_JUMP (1 << 1) #define IN_DUCK (1 << 2) @@ -38,4 +43,8 @@ #define IN_GRENADE2 (1 << 24) // grenade 2 #define IN_ATTACK3 (1 << 25) +#ifdef VGUI_SCREEN_FIX +#define IN_VALIDVGUIINPUT (1 << 23) //bitflag for vgui fix +#endif + #endif // IN_BUTTONS_H diff --git a/mp/src/game/shared/mapbase/MapEdit.cpp b/mp/src/game/shared/mapbase/MapEdit.cpp new file mode 100644 index 00000000..32659964 --- /dev/null +++ b/mp/src/game/shared/mapbase/MapEdit.cpp @@ -0,0 +1,897 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: The flimsy MapEdit system that was +// heavily inspired by Synergy's MapEdit, completely based on the Commentary System +// and originally used for Lambda Fortress. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "MapEdit.h" +#include "filesystem.h" + +#ifndef CLIENT_DLL +#include +#include "utldict.h" +#include "isaverestore.h" +#include "eventqueue.h" +#include "saverestore_utlvector.h" +#include "ai_basenpc.h" +#include "triggers.h" +#include "mapbase/SystemConvarMod.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifndef CLIENT_DLL + +#define MAPEDIT_SPAWNED_SEMAPHORE "mapedit_semaphore" +#define MAPEDIT_DEFAULT_FILE UTIL_VarArgs("maps/%s_auto.txt", STRING(gpGlobals->mapname)) + +ConVar mapedit_enabled("mapedit_enabled", "1", FCVAR_ARCHIVE, "Is automatic MapEdit enabled?"); +ConVar mapedit_stack("mapedit_stack", "1", FCVAR_ARCHIVE, "If multiple MapEdit scripts are loaded, should they stack or replace each other?"); +ConVar mapedit_debug("mapedit_debug", "0", FCVAR_NONE, "Should MapEdit give debug messages?"); + +inline void DebugMsg(const tchar *pMsg, ...) +{ + if (mapedit_debug.GetBool() == true) + { + Msg("%s", pMsg); + } +} + +//bool g_bMapEditAvailable; +bool g_bMapEditLoaded = false; +bool IsMapEditLoaded( void ) +{ + return g_bMapEditLoaded; +} + +bool IsAutoMapEditAllowed(void) +{ + return mapedit_enabled.GetBool(); +} + +void CV_GlobalChange_MapEdit( IConVar *var, const char *pOldString, float flOldValue ); + +//----------------------------------------------------------------------------- +// Purpose: Game system for MapEdit stuff +//----------------------------------------------------------------------------- +class CMapEdit : public CAutoGameSystemPerFrame +{ +public: + DECLARE_DATADESC(); + + virtual void LevelInitPreEntity() + { + m_bMapEditConvarsChanging = false; + CalculateAvailableState(); + } + + void CalculateAvailableState( void ) + { + // Set the available cvar if we can find commentary data for this level + char szFullName[512]; + Q_snprintf(szFullName,sizeof(szFullName), "maps/%s_auto.txt", STRING( gpGlobals->mapname) ); + if ( filesystem->FileExists( szFullName ) ) + { + bool bAllowed = IsAutoMapEditAllowed(); + g_bMapEditLoaded = bAllowed; + //if (bAllowed) + // gEntList.AddListenerEntity( this ); + } + else + { + g_bMapEditLoaded = false; + //gEntList.RemoveListenerEntity( this ); + } + } + + virtual void LevelShutdownPreEntity() + { + ShutDownMapEdit(); + } + + void ParseEntKVBlock( CBaseEntity *pNode, KeyValues *pkvNode ) + { + KeyValues *pkvNodeData = pkvNode->GetFirstSubKey(); + while ( pkvNodeData ) + { + // Handle the connections block + if ( !Q_strcmp(pkvNodeData->GetName(), "connections") ) + { + ParseEntKVBlock( pNode, pkvNodeData ); + } + else + { + #define MAPEDIT_STRING_LENGTH_MAX 1024 + + const char *pszValue = pkvNodeData->GetString(); + Assert( Q_strlen(pszValue) < MAPEDIT_STRING_LENGTH_MAX ); + if ( Q_strnchr(pszValue, '^', MAPEDIT_STRING_LENGTH_MAX) ) + { + // We want to support quotes in our strings so that we can specify multiple parameters in + // an output inside our commentary files. We convert ^s to "s here. + char szTmp[MAPEDIT_STRING_LENGTH_MAX]; + Q_strncpy( szTmp, pszValue, MAPEDIT_STRING_LENGTH_MAX ); + int len = Q_strlen( szTmp ); + for ( int i = 0; i < len; i++ ) + { + if ( szTmp[i] == '^' ) + { + szTmp[i] = '"'; + } + } + + pszValue = szTmp; + } + + char cOperatorChar = pszValue[0]; + if (cOperatorChar == '+') + { + pszValue++; + char szExistingValue[MAPEDIT_STRING_LENGTH_MAX]; + if (pNode->GetKeyValue(pkvNodeData->GetName(), szExistingValue, sizeof(szExistingValue) )) + { + // Right now, this only supports adding floats/integers. + // Add Vector support later. + float flResult = atof(szExistingValue) + atof(pszValue); + pszValue = UTIL_VarArgs("%f", flResult); + } + } + else if (cOperatorChar == '-') + { + pszValue++; + char szExistingValue[MAPEDIT_STRING_LENGTH_MAX]; + if (pNode->GetKeyValue(pkvNodeData->GetName(), szExistingValue, sizeof(szExistingValue) )) + { + // Right now, this only supports subtracting floats/integers. + // Add Vector support later. + float flResult = atof(szExistingValue) - atof(pszValue); + pszValue = UTIL_VarArgs("%f", flResult); + } + } + else if (cOperatorChar == '|' /*&& pszValue[1] == '='*/) + { + pszValue++; + char szExistingValue[MAPEDIT_STRING_LENGTH_MAX]; + if (pNode->GetKeyValue(pkvNodeData->GetName(), szExistingValue, sizeof(szExistingValue))) + { + int iResult = atoi(szExistingValue) | atoi(pszValue); + pszValue = UTIL_VarArgs("%i", iResult); + } + } + + pNode->KeyValue(pkvNodeData->GetName(), pszValue); + } + + pkvNodeData = pkvNodeData->GetNextKey(); + } + } + + virtual void LevelInitPostEntity( void ) + { + if ( !IsMapEditLoaded() ) + { + return; + } + + if ( gpGlobals->eLoadType == MapLoad_LoadGame || gpGlobals->eLoadType == MapLoad_Background ) + { + return; + } + + m_bMapEditLoadedMidGame = false; + InitMapEdit(); + + //IGameEvent *event = gameeventmanager->CreateEvent( "playing_mapedit" ); + //gameeventmanager->FireEventClientSide( event ); + } + + bool MapEditConvarsChanging( void ) + { + return m_bMapEditConvarsChanging; + } + + void SetMapEditConvarsChanging( bool bChanging ) + { + m_bMapEditConvarsChanging = bChanging; + } + + void ConvarChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) + { + ConVarRef var( pConVar ); + + // A convar has been changed by a commentary node. We need to store + // the old state. If the engine shuts down, we need to restore any + // convars that the commentary changed to their previous values. + for ( int i = 0; i < m_ModifiedConvars.Count(); i++ ) + { + // If we find it, just update the current value + if ( !Q_strncmp( var.GetName(), m_ModifiedConvars[i].pszConvar, MAX_MODIFIED_CONVAR_STRING ) ) + { + Q_strncpy( m_ModifiedConvars[i].pszCurrentValue, var.GetString(), MAX_MODIFIED_CONVAR_STRING ); + //Msg(" Updating Convar %s: value %s (org %s)\n", m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue ); + return; + } + } + + // We didn't find it in our list, so add it + modifiedconvars_t newConvar; + Q_strncpy( newConvar.pszConvar, var.GetName(), MAX_MODIFIED_CONVAR_STRING ); + Q_strncpy( newConvar.pszCurrentValue, var.GetString(), MAX_MODIFIED_CONVAR_STRING ); + Q_strncpy( newConvar.pszOrgValue, pOldString, MAX_MODIFIED_CONVAR_STRING ); + m_ModifiedConvars.AddToTail( newConvar ); + + /* + Msg(" Commentary changed '%s' to '%s' (was '%s')\n", var->GetName(), var->GetString(), pOldString ); + Msg(" Convars stored: %d\n", m_ModifiedConvars.Count() ); + for ( int i = 0; i < m_ModifiedConvars.Count(); i++ ) + { + Msg(" Convar %d: %s, value %s (org %s)\n", i, m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue ); + } + */ + } + + CBaseEntity *FindMapEditEntity( CBaseEntity *pStartEntity, const char *szName, const char *szValue = NULL ) + { + CBaseEntity *pEntity = NULL; + DebugMsg("MapEdit Find Debug: Starting Search, Name: %s, Value: %s\n", szName, szValue); + + // First, find by targetname/classname + pEntity = gEntList.FindEntityGeneric(pStartEntity, szName); + + if (!pEntity) + { + DebugMsg("MapEdit Find Debug: \"%s\" not found as targetname or classname\n", szName); + + if (szValue) + { + if (!Q_strnicmp(szName, "#find_", 6)) + { + const char *pName = szName + 6; + if (!Q_stricmp(pName, "by_keyfield")) + { + char key[64]; + char value[64]; + + // Separate key from value + char *delimiter = Q_strstr(szValue, " "); + if (delimiter) + { + Q_strncpy(key, szValue, MIN((delimiter - szValue) + 1, sizeof(key))); + Q_strncpy(value, delimiter + 1, sizeof(value)); + } + else + { + // Assume the value doesn't matter and we're just looking for the key + Q_strncpy(key, szValue, sizeof(key)); + } + + if (!key) + { + Warning("MapEdit: Possible find_by_keyfield syntax error: key not detected in \"%s\"\n", szValue); + } + + // Find entities with matching keyfield + variant_t variant; + const CEntInfo *pInfo = pStartEntity ? gEntList.GetEntInfoPtr(pStartEntity->GetRefEHandle())->m_pNext : gEntList.FirstEntInfo(); + for (; pInfo; pInfo = pInfo->m_pNext) + { + CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; + if (!ent) + { + DevWarning("NULL entity in global entity list!\n"); + continue; + } + + if (!ent->edict()) + continue; + + if (ent->ReadKeyField(key, &variant)) + { + // Does the value matter? + if (value) + { + if (Q_stricmp(variant.String(), value) == 0) + { + // The entity has the keyfield and it matches the value. + return ent; + } + } + else + { + // The value doesn't matter and the entity has the keyfield. + return ent; + } + } + } + } + else if (!Q_stricmp(pName, "by_origin")) + { + // Find entities at this origin + Vector vecOrigin; + UTIL_StringToVector(vecOrigin.Base(), szValue); + if (vecOrigin.IsValid()) + { + DebugMsg("MapEdit Find Debug: \"%s\" is valid vector\n", szValue); + const CEntInfo *pInfo = pStartEntity ? gEntList.GetEntInfoPtr(pStartEntity->GetRefEHandle())->m_pNext : gEntList.FirstEntInfo(); + for (; pInfo; pInfo = pInfo->m_pNext) + { + CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; + if (!ent) + { + DevWarning("NULL entity in global entity list!\n"); + continue; + } + + if (!ent->edict()) + continue; + + if (ent->GetLocalOrigin() == vecOrigin) + return ent; + } + } + } + } + else if (!pStartEntity) + { + // Try the entity index + int iEntIndex = atoi(szName); + if (!pStartEntity && UTIL_EntityByIndex(iEntIndex)) + { + DebugMsg("MapEdit Find Debug: \"%s\" is valid index\n", szName); + return CBaseEntity::Instance(iEntIndex); + } + } + } + + DebugMsg("MapEdit Find Debug: \"%s\" not found\n", szName); + return NULL; + } + + DebugMsg("MapEdit Find Debug: \"%s\" found as targetname or classname\n", szName); + return pEntity; + } + + void InitMapEdit( const char* pFile = MAPEDIT_DEFAULT_FILE ) + { + // Install the global cvar callback + cvar->InstallGlobalChangeCallback( CV_GlobalChange_MapEdit ); + + + // If we find the commentary semaphore, the commentary entities already exist. + // This occurs when you transition back to a map that has saved commentary nodes in it. + if ( gEntList.FindEntityByName( NULL, MAPEDIT_SPAWNED_SEMAPHORE ) ) + return; + + // Spawn the commentary semaphore entity + CBaseEntity *pSemaphore = CreateEntityByName( "info_target" ); + pSemaphore->SetName( MAKE_STRING(MAPEDIT_SPAWNED_SEMAPHORE) ); + + bool oldLock = engine->LockNetworkStringTables( false ); + + SpawnMapEdit(pFile); + + engine->LockNetworkStringTables( oldLock ); + } + + void LoadFromFormat_Original(KeyValues *pkvFile) + { + KeyValues *pkvNode = pkvFile->GetFirstSubKey(); + while ( pkvNode ) + { + const char *pNodeName = pkvNode->GetName(); + if (FStrEq(pNodeName, "create")) + { + KeyValues *pkvClassname = pkvNode->GetFirstSubKey(); + while (pkvClassname) + { + pNodeName = pkvClassname->GetName(); + + CBaseEntity *pNode = CreateEntityByName(pNodeName); + if (pNode) + { + ParseEntKVBlock(pNode, pkvClassname); + + EHANDLE hHandle; + hHandle = pNode; + m_hSpawnedEntities.AddToTail(hHandle); + DebugMsg("MapEdit Debug: Spawned entity %s\n", pNodeName); + } + else + { + Warning("MapEdit: Failed to spawn mapedit entity, type: '%s'\n", pNodeName); + } + + pkvClassname = pkvClassname->GetNextKey(); + } + pkvClassname->deleteThis(); + } + else if (FStrEq(pNodeName, "edit")) + { + KeyValues *pName = pkvNode->GetFirstSubKey(); + while (pName) + { + pNodeName = pName->GetName(); + + CBaseEntity *pNode = NULL; + + pNode = FindMapEditEntity(NULL, pNodeName, pName->GetString()); + + while (pNode) + { + DebugMsg("MapEdit Debug: Editing %s (%s)\n", pNodeName, pNode->GetDebugName()); + + ParseEntKVBlock(pNode, pName); + pNode = FindMapEditEntity(pNode, pNodeName, pName->GetString()); + } + + pName = pName->GetNextKey(); + } + pName->deleteThis(); + } + else if (FStrEq(pNodeName, "delete")) + { + KeyValues *pName = pkvNode->GetFirstSubKey(); + while (pName) + { + pNodeName = pName->GetName(); + + CBaseEntity *pNode = NULL; + + pNode = FindMapEditEntity(NULL, pNodeName, pName->GetString()); + + while (pNode) + { + DebugMsg("MapEdit Debug: Deleting %s (%s)\n", pNodeName, pNode->GetDebugName()); + + UTIL_Remove(pNode); + pNode = FindMapEditEntity(pNode, pNodeName, pName->GetString()); + } + + pName = pName->GetNextKey(); + } + pName->deleteThis(); + } + else if (FStrEq(pNodeName, "fire")) + { + KeyValues *pName = pkvNode->GetFirstSubKey(); + while (pName) + { + pNodeName = pName->GetName(); + + const char *pInputName = NULL; + variant_t varInputParam; + float flInputDelay = 0.0f; + CBaseEntity *pActivator = NULL; + CBaseEntity *pCaller = NULL; + int iOutputID = 0; + + char *pszValue = strdup(pName->GetString()); + int iter = 0; + char *inputparams = strtok(pszValue, ","); + while (inputparams) + { + switch (iter) + { + // Input name + case 0: + pInputName = inputparams; break; + // Input parameter + case 1: + varInputParam.SetString(AllocPooledString(inputparams)); break; + // Input delay + case 2: + flInputDelay = atof(inputparams); break; + // Activator + case 3: + pActivator = gEntList.FindEntityByName(NULL, inputparams); break; + // Caller + case 4: + pCaller = gEntList.FindEntityByName(NULL, inputparams); break; + // Output ID + case 5: + iOutputID = atoi(inputparams); break; + } + iter++; + inputparams = strtok(NULL, ","); + } + + DebugMsg("MapEdit Debug: Firing input %s on %s\n", pInputName, pNodeName); + g_EventQueue.AddEvent(pNodeName, pInputName, varInputParam, flInputDelay, pActivator, pCaller, iOutputID); + + pName = pName->GetNextKey(); + } + } + else if (FStrEq(pNodeName, "console")) + { + KeyValues *pkvNodeData = pkvNode->GetFirstSubKey(); + const char *pKey; + const char *pValue; + while (pkvNodeData) + { + SetMapEditConvarsChanging(true); + + pKey = pkvNodeData->GetName(); + pValue = pkvNodeData->GetString(); + + engine->ServerCommand(UTIL_VarArgs("%s %s", pKey, pValue)); + engine->ServerCommand("mapedit_cvarsnotchanging\n"); + + pkvNodeData = pkvNodeData->GetNextKey(); + } + pkvNodeData->deleteThis(); + } + + pkvNode = pkvNode->GetNextKey(); + } + pkvNode->deleteThis(); + } + + void SpawnMapEdit(const char *pFile = NULL) + { + // Find the commentary file + char szFullName[512]; + if (pFile == NULL) + { + DebugMsg("MapEdit Debug: NULL file, loading default\n"); + Q_snprintf(szFullName,sizeof(szFullName), "maps/%s_auto.txt", STRING( gpGlobals->mapname )); + } + else + { + DebugMsg("MapEdit Debug: File not NULL, loading %s\n", pFile); + Q_strncpy(szFullName, pFile, sizeof(szFullName)); + } + KeyValues *pkvFile = new KeyValues( "MapEdit" ); + if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) ) + { + Msg( "MapEdit: Loading MapEdit data from %s. \n", szFullName ); + + if (gpGlobals->eLoadType != MapLoad_LoadGame) + { + // Support for multiple formats + const char *szVersion = pkvFile->GetString("version", "original"); + if (FStrEq(szVersion, "original")) + LoadFromFormat_Original(pkvFile); + // TODO: More formats + } + + // Then activate all the entities + for ( int i = 0; i < m_hSpawnedEntities.Count(); i++ ) + { + DispatchSpawn(m_hSpawnedEntities[i]); + } + } + else + { + Msg( "MapEdit: Could not find MapEdit data file '%s'. \n", szFullName ); + } + + pkvFile->deleteThis(); + } + + void ShutDownMapEdit( void ) + { + // Destroy all the entities created by commentary + for ( int i = m_hSpawnedEntities.Count()-1; i >= 0; i-- ) + { + if ( m_hSpawnedEntities[i] ) + { + UTIL_Remove( m_hSpawnedEntities[i] ); + } + } + m_hSpawnedEntities.Purge(); + + // Remove the semaphore + CBaseEntity *pSemaphore = gEntList.FindEntityByName( NULL, MAPEDIT_SPAWNED_SEMAPHORE ); + if ( pSemaphore ) + { + UTIL_Remove( pSemaphore ); + } + + // Remove our global convar callback + cvar->RemoveGlobalChangeCallback( CV_GlobalChange_MapEdit ); + + // Reset any convars that have been changed by the commentary + for ( int i = 0; i < m_ModifiedConvars.Count(); i++ ) + { + ConVar *pConVar = (ConVar *)cvar->FindVar( m_ModifiedConvars[i].pszConvar ); + if ( pConVar ) + { + pConVar->SetValue( m_ModifiedConvars[i].pszOrgValue ); + } + } + m_ModifiedConvars.Purge(); + } + + void SetMapEdit( bool bMapEdit, const char *pFile = NULL ) + { + //g_bMapEditLoaded = bMapEdit; + //CalculateAvailableState(); + + // If we're turning on commentary, create all the entities. + if ( bMapEdit ) + { + if (filesystem->FileExists(pFile) || pFile == NULL) + { + g_bMapEditLoaded = true; + m_bMapEditLoadedMidGame = true; + InitMapEdit(pFile); + } + else + { + Warning("MapEdit: No such file \"%s\"!\n", pFile); + } + } + else + { + ShutDownMapEdit(); + } + } + + void OnRestore( void ) + { + cvar->RemoveGlobalChangeCallback( CV_GlobalChange_MapEdit ); + + if ( !IsMapEditLoaded() ) + return; + + // Set any convars that have already been changed by the commentary before the save + for ( int i = 0; i < m_ModifiedConvars.Count(); i++ ) + { + ConVar *pConVar = (ConVar *)cvar->FindVar( m_ModifiedConvars[i].pszConvar ); + if ( pConVar ) + { + //Msg(" Restoring Convar %s: value %s (org %s)\n", m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue ); + pConVar->SetValue( m_ModifiedConvars[i].pszCurrentValue ); + } + } + + // Install the global cvar callback + cvar->InstallGlobalChangeCallback( CV_GlobalChange_MapEdit ); + } + + bool MapEditLoadedMidGame( void ) + { + return m_bMapEditLoadedMidGame; + } + +private: + bool m_bMapEditConvarsChanging; + bool m_bMapEditLoadedMidGame; + + CUtlVector< modifiedconvars_t > m_ModifiedConvars; + CUtlVector m_hSpawnedEntities; +}; + +CMapEdit g_MapEdit; + +BEGIN_DATADESC_NO_BASE( CMapEdit ) + + DEFINE_FIELD( m_bMapEditLoadedMidGame, FIELD_BOOLEAN ), + + DEFINE_UTLVECTOR( m_ModifiedConvars, FIELD_EMBEDDED ), + DEFINE_UTLVECTOR( m_hSpawnedEntities, FIELD_EHANDLE ), + + //DEFINE_UTLVECTOR( m_SpawnEditLookup, FIELD_EMBEDDED ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: We need to revert back any convar changes that are made by the +// commentary system during commentary. This code stores convar changes +// made by the commentary system, and reverts them when finished. +//----------------------------------------------------------------------------- +void CV_GlobalChange_MapEdit( IConVar *var, const char *pOldString, float flOldValue ) +{ + if ( !g_MapEdit.MapEditConvarsChanging() ) + { + // A convar has changed, but not due to mapedit. Ignore it. + return; + } + + g_MapEdit.ConvarChanged( var, pOldString, flOldValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CC_MapEditNotChanging( void ) +{ + g_MapEdit.SetMapEditConvarsChanging( false ); +} +static ConCommand mapedit_cvarsnotchanging( "mapedit_cvarsnotchanging", CC_MapEditNotChanging, 0 ); + +// ======================================================== +// Static functions that can be accessed from outside +// ======================================================== + +//----------------------------------------------------------------------------- +// Purpose: Reloads automatic MapEdit after cleanup +//----------------------------------------------------------------------------- +void MapEdit_MapReload( void ) +{ + Msg("MapEdit: Map reloading\n"); + + g_MapEdit.ShutDownMapEdit(); + + g_MapEdit.LevelInitPreEntity(); + + g_MapEdit.LevelInitPostEntity(); +} + +//----------------------------------------------------------------------------- +// Purpose: Loads a specific MapEdit file. +//----------------------------------------------------------------------------- +void MapEdit_LoadFile(const char *pFile, bool bStack) +{ + if (!filesystem->FileExists(pFile)) + { + Warning("MapEdit: No such file \"%s\"!\n", pFile); + return; + } + + if (IsMapEditLoaded()) + { + if (bStack) + { + g_MapEdit.SpawnMapEdit(pFile); + return; + } + else + { + g_MapEdit.SetMapEdit(false); + } + } + + g_MapEdit.SetMapEdit(true, pFile); +} + + +//----------------------------------------------------------------------------- +// Purpose: MapEdit specific logic_auto replacement. +// Fires outputs based upon how MapEdit has been activated. +//----------------------------------------------------------------------------- +class CMapEditAuto : public CBaseEntity +{ + DECLARE_CLASS( CMapEditAuto, CBaseEntity ); +public: + DECLARE_DATADESC(); + + void Spawn(void); + void Think(void); + +private: + // fired if loaded due to new map + COutputEvent m_OnMapEditNewGame; + + // fired if loaded in the middle of a map + COutputEvent m_OnMapEditMidGame; +}; + +LINK_ENTITY_TO_CLASS(mapedit_auto, CMapEditAuto); + +BEGIN_DATADESC( CMapEditAuto ) + // Outputs + DEFINE_OUTPUT(m_OnMapEditNewGame, "OnMapEditNewGame"), + DEFINE_OUTPUT(m_OnMapEditMidGame, "OnMapEditMidGame"), +END_DATADESC() + +//------------------------------------------------------------------------------ +// Purpose : Fire my outputs here if I fire on map reload +//------------------------------------------------------------------------------ +void CMapEditAuto::Spawn(void) +{ + BaseClass::Spawn(); + SetNextThink( gpGlobals->curtime + 0.1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapEditAuto::Think(void) +{ + if ( g_MapEdit.MapEditLoadedMidGame() ) + { + m_OnMapEditMidGame.FireOutput(NULL, this); + } + else + { + m_OnMapEditNewGame.FireOutput(NULL, this); + } +} + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +void CC_MapEdit_Load( const CCommand& args ) +{ + const char *sFile = args[1] ? args[1] : NULL; + + MapEdit_LoadFile(sFile, mapedit_stack.GetBool()); +} +static ConCommand mapedit_load("mapedit_load", CC_MapEdit_Load, "Forces mapedit to load a specific file. If there is no input value, it will load the map's default file.", FCVAR_CHEAT); + +//------------------------------------------------------------------------------ +// Purpose : Unloads all MapEdit entities. Does not undo modifications or deletions. +// Input : +// Output : +//------------------------------------------------------------------------------ +void CC_MapEdit_Unload( const CCommand& args ) +{ + g_MapEdit.SetMapEdit(false); +} +static ConCommand mapedit_unload("mapedit_unload", CC_MapEdit_Unload, "Forces mapedit to unload.", FCVAR_CHEAT); + +#else + +//------------------------------------------------------------------------------ +// Purpose : Prints MapEdit data from a specific file to the console. +// Input : The file to print +// Output : The file's data +//------------------------------------------------------------------------------ +void CC_MapEdit_Print( const CCommand& args ) +{ + const char *szFullName = args[1]; + if (szFullName && filesystem->FileExists(szFullName)) + { + KeyValues *pkvFile = new KeyValues( "MapEdit" ); + if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) ) + { + Msg( "MapEdit: Printing MapEdit data from %s. \n", szFullName ); + + // Load each commentary block, and spawn the entities + KeyValues *pkvNode = pkvFile->GetFirstSubKey(); + while ( pkvNode ) + { + // Get node name + const char *pNodeName = pkvNode->GetName(); + Msg("- Section Name: %s\n", pNodeName); + + // Skip the trackinfo + if ( !Q_strncmp( pNodeName, "trackinfo", 9 ) ) + { + pkvNode = pkvNode->GetNextKey(); + continue; + } + + KeyValues *pClassname = pkvNode->GetFirstSubKey(); + while (pClassname) + { + // Use the classname instead + pNodeName = pClassname->GetName(); + + Msg(" %s\n", pNodeName); + for ( KeyValues *sub = pClassname->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + if (!Q_strcmp(sub->GetName(), "connections")) + { + Msg("- connections\n"); + for ( KeyValues *sub2 = sub->GetFirstSubKey(); sub2; sub2 = sub2->GetNextKey() ) + { + Msg("- \"%s\", \"%s\"\n", sub2->GetName(), sub2->GetString()); + } + continue; + } + Msg("- %s, %s\n", sub->GetName(), sub->GetString()); + } + + pClassname = pClassname->GetNextKey(); + } + + pkvNode = pkvNode->GetNextKey(); + } + + pkvNode->deleteThis(); + } + } +} +static ConCommand mapedit_print("mapedit_print", CC_MapEdit_Print, "Prints a mapedit file in the console."); + +#endif diff --git a/mp/src/game/shared/mapbase/MapEdit.h b/mp/src/game/shared/mapbase/MapEdit.h new file mode 100644 index 00000000..899f6712 --- /dev/null +++ b/mp/src/game/shared/mapbase/MapEdit.h @@ -0,0 +1,14 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Accessing MapEdit +// +// $NoKeywords: $ +//=============================================================================// + +extern ConVar mapedit_enabled; +extern ConVar mapedit_stack; +extern ConVar mapedit_debug; + +void MapEdit_MapReload( void ); + +void MapEdit_LoadFile( const char *pFile, bool bStack = mapedit_stack.GetBool() ); diff --git a/mp/src/game/shared/mapbase/mapbase_game_log.cpp b/mp/src/game/shared/mapbase/mapbase_game_log.cpp new file mode 100644 index 00000000..c51ab0c4 --- /dev/null +++ b/mp/src/game/shared/mapbase/mapbase_game_log.cpp @@ -0,0 +1,243 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: A special system designed to record game information for map testing. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "tier0/icommandline.h" +#include "igamesystem.h" +#include "filesystem.h" +#include "utlbuffer.h" +#ifdef CLIENT_DLL +#else +#include "ammodef.h" +#include "ai_basenpc.h" +#include "ai_squad.h" +#include "fmtstr.h" +#include "GameEventListener.h" +#include "saverestore_utlvector.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef GAME_DLL +// ------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------ + +class CMapbaseGameLogger : public CLogicalEntity, public CGameEventListener +{ +public: + DECLARE_DATADESC(); + DECLARE_CLASS( CMapbaseGameLogger, CLogicalEntity ); + + CMapbaseGameLogger() + { + pGameLoggerEnt = this; + } + + void Activate() + { + BaseClass::Activate(); + + ListenForGameEvent("skill_changed"); + } + + void FireGameEvent( IGameEvent *event ) + { + if (FStrEq(event->GetName(), "skill_changed")) + { + m_ListSkillChanged.AddToTail(event->GetInt("skill_level")); + m_ListSkillChangedTime.AddToTail(gpGlobals->curtime); + } + } + + float m_flLastLogTime; + int m_iSaveID; + + CUtlVector m_ListSkillChanged; + CUtlVector m_ListSkillChangedTime; + + static CMapbaseGameLogger *GetGameLoggerEnt() + { + if (!pGameLoggerEnt) + pGameLoggerEnt = static_cast(CBaseEntity::Create("mapbase_game_logger", vec3_origin, vec3_angle)); + + return pGameLoggerEnt; + } + +private: + static CHandle pGameLoggerEnt; +}; + +LINK_ENTITY_TO_CLASS( mapbase_game_logger, CMapbaseGameLogger ); + +BEGIN_DATADESC( CMapbaseGameLogger ) + + DEFINE_FIELD( m_flLastLogTime, FIELD_TIME ), + DEFINE_FIELD( m_iSaveID, FIELD_INTEGER ), + + DEFINE_UTLVECTOR( m_ListSkillChanged, FIELD_INTEGER ), + DEFINE_UTLVECTOR( m_ListSkillChangedTime, FIELD_TIME ), + +END_DATADESC() + +CHandle CMapbaseGameLogger::pGameLoggerEnt; + +void MapbaseGameLog_CVarToggle( IConVar *var, const char *pOldString, float flOldValue ); +ConVar mapbase_game_log_on_autosave( "mapbase_game_log_on_autosave", "0", FCVAR_NONE, "Logs information to %mapname%_log_%number%.txt on each autosave", MapbaseGameLog_CVarToggle ); + +void MapbaseGameLog_Init() +{ + if (mapbase_game_log_on_autosave.GetBool()) + { + // Create the game logger ent + CMapbaseGameLogger::GetGameLoggerEnt(); + } +} + +void MapbaseGameLog_Record( const char *szContext ) +{ + CMapbaseGameLogger *pGameLoggerEnt = CMapbaseGameLogger::GetGameLoggerEnt(); + if (!pGameLoggerEnt) + { + Warning("Failed to get game logger ent\n"); + return; + } + + KeyValues *pKV = new KeyValues( "Log" ); + + KeyValues *pKVLogInfo = pKV->FindKey( "logging_info", true ); + if ( pKVLogInfo ) + { + pKVLogInfo->SetString("context", szContext); + pKVLogInfo->SetFloat("last_log", pGameLoggerEnt->m_flLastLogTime > 0.0f ? gpGlobals->curtime - pGameLoggerEnt->m_flLastLogTime : -1.0f); + } + + KeyValues *pKVGameInfo = pKV->FindKey( "game_info", true ); + if ( pKVGameInfo ) + { + pKVGameInfo->SetInt("skill", g_pGameRules->GetSkillLevel()); + + if (pGameLoggerEnt->m_ListSkillChanged.Count() > 0) + { + KeyValues *pKVSkill = pKVGameInfo->FindKey("skill_changes", true); + for (int i = 0; i < pGameLoggerEnt->m_ListSkillChanged.Count(); i++) + { + float flTime = pGameLoggerEnt->m_ListSkillChangedTime[i]; + switch (pGameLoggerEnt->m_ListSkillChanged[i]) + { + case SKILL_EASY: pKVSkill->SetString(CNumStr(flTime), "easy"); break; + case SKILL_MEDIUM: pKVSkill->SetString(CNumStr(flTime), "normal"); break; + case SKILL_HARD: pKVSkill->SetString(CNumStr(flTime), "hard"); break; + } + } + } + } + + KeyValues *pKVPlayer = pKV->FindKey( "player", true ); + if ( pKVPlayer ) + { + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + + if ( pPlayer ) + { + pKVPlayer->SetInt("health", pPlayer->GetHealth()); + pKVPlayer->SetInt("armor", pPlayer->ArmorValue()); + + pKVPlayer->SetString("position", CFmtStrN<128>("[%f %f %f]", pPlayer->GetAbsOrigin().x, pPlayer->GetAbsOrigin().y, pPlayer->GetAbsOrigin().z)); + pKVPlayer->SetString("angles", CFmtStrN<128>("[%f %f %f]", pPlayer->EyeAngles().x, pPlayer->EyeAngles().y, pPlayer->EyeAngles().z)); + + KeyValues *pKVWeapons = pKVPlayer->FindKey( "weapons", true ); + if ( pKVWeapons ) + { + // Cycle through all of the player's weapons + for ( int i = 0; i < pPlayer->WeaponCount(); i++ ) + { + CBaseCombatWeapon *pWeapon = pPlayer->GetWeapon(i); + if ( !pWeapon ) + continue; + + if ( pPlayer->GetActiveWeapon() == pWeapon ) + pKVWeapons->SetString(pWeapon->GetClassname(), CFmtStrN<32>("%i; %i (active)", pWeapon->m_iClip1.Get(), pWeapon->m_iClip2.Get())); + else + pKVWeapons->SetString(pWeapon->GetClassname(), CFmtStrN<32>("%i; %i", pWeapon->m_iClip1.Get(), pWeapon->m_iClip2.Get())); + } + } + + KeyValues *pKVAmmo = pKVPlayer->FindKey( "ammo", true ); + if ( pKVAmmo ) + { + // Cycle through all of the player's ammo + for ( int i = 0; i < GetAmmoDef()->m_nAmmoIndex; i++ ) + { + int iAmmo = pPlayer->GetAmmoCount( i ); + if ( iAmmo > 0 ) + pKVAmmo->SetInt( GetAmmoDef()->m_AmmoType[i].pName, iAmmo ); + } + } + } + } + + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + int nAIs = g_AI_Manager.NumAIs(); + for (int i = 0; i < nAIs; i++) + { + CAI_BaseNPC *pNPC = ppAIs[i]; + + if (!pNPC->IsAlive() || pNPC->GetSleepState() != AISS_AWAKE) + continue; + + KeyValues *pKVNPC = pKV->FindKey( CNumStr( pNPC->entindex() ), true ); + if (pKVNPC) + { + pKVNPC->SetString("classname", pNPC->GetClassname()); + pKVNPC->SetString("name", STRING(pNPC->GetEntityName())); + + pKVNPC->SetString("position", CFmtStrN<128>("[%f %f %f]", pNPC->GetAbsOrigin().x, pNPC->GetAbsOrigin().y, pNPC->GetAbsOrigin().z)); + + pKVNPC->SetInt("health", pNPC->GetHealth()); + + if (pNPC->GetActiveWeapon()) + pKVNPC->SetString("weapon", pNPC->GetActiveWeapon()->GetClassname()); + + if (pNPC->GetSquad()) + pKVNPC->SetString("squad", pNPC->GetSquad()->GetName()); + } + } + + CFmtStrN pathfmt("map_logs/%s_log_%i.txt", STRING(gpGlobals->mapname), pGameLoggerEnt->m_iSaveID); + + pGameLoggerEnt->m_flLastLogTime = gpGlobals->curtime; + pGameLoggerEnt->m_iSaveID++; + + // Create the folder first, since "map_logs" is not standard and is unlikely to exist + g_pFullFileSystem->CreateDirHierarchy( "map_logs", "MOD" ); + + if (pKV->SaveToFile( g_pFullFileSystem, pathfmt, "MOD" )) + { + Msg("Saved game log file to \"%s\"\n", pathfmt.Get()); + } + + pKV->deleteThis(); +} + +static void CC_Mapbase_GameLogRecord( const CCommand& args ) +{ + MapbaseGameLog_Record( "command" ); +} + +static ConCommand mapbase_game_log_record("mapbase_game_log_record", CC_Mapbase_GameLogRecord, "Records game data to %mapname%_log_%number%." ); + +void MapbaseGameLog_CVarToggle( IConVar *var, const char *pOldString, float flOldValue ) +{ + if (mapbase_game_log_on_autosave.GetBool()) + { + // Create the game logger ent + CMapbaseGameLogger::GetGameLoggerEnt(); + } +} +#endif diff --git a/mp/src/game/shared/mapbase/mapbase_rpc.cpp b/mp/src/game/shared/mapbase/mapbase_rpc.cpp new file mode 100644 index 00000000..ecbb36ca --- /dev/null +++ b/mp/src/game/shared/mapbase/mapbase_rpc.cpp @@ -0,0 +1,606 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase's RPC implementation. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#ifdef CLIENT_DLL + +#ifdef STEAM_RPC +#include "clientsteamcontext.h" +#include "steam/steamclientpublic.h" +#endif + +#ifdef DISCORD_RPC +#include "discord_rpc.h" +#include +#include "c_world.h" +#endif + +#include "filesystem.h" +#include "c_playerresource.h" +#include +#include + +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// From mapbase_shared.cpp +extern const char *g_MapName; + +// The game's name found in gameinfo.txt. Mostly used for Discord RPC. +extern char g_iszGameName[128]; + +#ifdef MAPBASE_RPC +void MapbaseRPC_CVarToggle( IConVar *var, const char *pOldString, float flOldValue ); + +ConVar mapbase_rpc_enabled("mapbase_rpc_enabled", "1", FCVAR_ARCHIVE, "Controls whether Mapbase's RPC stuff is enabled on this client.", MapbaseRPC_CVarToggle); + +//----------------------------------------------------------------------------- +// RPC Stuff +// +// Mapbase has some special "RPC" integration stuff for things like Discord. +// There's a section that goes into more detail below. +//----------------------------------------------------------------------------- + +void MapbaseRPC_Init(); +void MapbaseRPC_Shutdown(); + +void MapbaseRPC_Update( int iType, const char *pMapName ); +void MapbaseRPC_Update( int iRPCMask, int iType, const char *pMapName ); + +#ifdef STEAM_RPC +void MapbaseRPC_UpdateSteam( int iType, const char *pMapName ); +#endif + +#ifdef DISCORD_RPC +void MapbaseRPC_UpdateDiscord( int iType, const char *pMapName ); +void MapbaseRPC_GetDiscordParameters( DiscordRichPresence &discordPresence, int iType, const char *pMapName ); +#endif + +enum RPCClients_t +{ + RPC_STEAM, + RPC_DISCORD, + + NUM_RPCS, +}; + +static const char *g_pszRPCNames[] = { + "Steam", + "Discord", +}; + +// This is a little dodgy, but it stops us from having to add spawnflag definitions for each RPC. +#define RPCFlag(rpc) (1 << rpc) + +// The global game_metadata entity. +// There can be only one...for each RPC. +static EHANDLE g_Metadata[NUM_RPCS]; + +// Don't update constantly +#define RPC_UPDATE_COOLDOWN 5.0f + +// How long to wait before updating in case multiple variables are changing +#define RPC_UPDATE_WAIT 0.25f +#endif + +#ifdef CLIENT_DLL +#define CMapbaseMetadata C_MapbaseMetadata +#endif + +class CMapbaseMetadata : public CBaseEntity +{ +public: +#ifndef CLIENT_DLL + DECLARE_DATADESC(); +#endif + DECLARE_NETWORKCLASS(); + DECLARE_CLASS( CMapbaseMetadata, CBaseEntity ); + +#ifdef MAPBASE_RPC +#ifdef CLIENT_DLL + ~C_MapbaseMetadata() + { + for (int i = 0; i < NUM_RPCS; i++) + { + if (g_Metadata[i] == this) + { + g_Metadata[i] = NULL; + } + } + } + + void OnDataChanged( DataUpdateType_t updateType ) + { + if (updateType == DATA_UPDATE_CREATED) + { + for (int i = 0; i < NUM_RPCS; i++) + { + // See if we're updating this RPC. + if (m_spawnflags & RPCFlag(i)) + { + if (g_Metadata[i]) + { + Warning("Warning: Metadata entity for %s already exists, replacing with new one\n", g_pszRPCNames[i]); + + // Inherit their update timer + m_flRPCUpdateTimer = static_cast(g_Metadata[i].Get())->m_flRPCUpdateTimer; + + g_Metadata[i].Get()->Remove(); + } + + DevMsg("Becoming metadata entity for %s\n", g_pszRPCNames[i]); + g_Metadata[i] = this; + } + } + } + + // Avoid spamming updates + if (gpGlobals->curtime > (m_flRPCUpdateTimer - RPC_UPDATE_WAIT)) + { + // Multiple variables might be changing, wait until they're probably all finished + m_flRPCUpdateTimer = gpGlobals->curtime + RPC_UPDATE_WAIT; + } + + DevMsg("Metadata changed; updating in %f\n", m_flRPCUpdateTimer - gpGlobals->curtime); + + // Update when the cooldown is over + SetNextClientThink( m_flRPCUpdateTimer ); + } + + void ClientThink() + { + // NOTE: Client thinking should be limited by the update timer! + UpdateRPCThink(); + + // Wait until our data is changed again + SetNextClientThink( CLIENT_THINK_NEVER ); + } + + void UpdateRPCThink() + { + DevMsg("Global metadata entity: %s\n", g_Metadata != NULL ? "Valid" : "Invalid!?"); + + MapbaseRPC_Update(m_spawnflags, RPCSTATE_UPDATE, g_MapName); + + m_flRPCUpdateTimer = gpGlobals->curtime + RPC_UPDATE_COOLDOWN; + } +#else + int UpdateTransmitState() // always send to all clients + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } +#endif +#endif + +#ifdef CLIENT_DLL + char m_iszRPCState[128]; + char m_iszRPCDetails[128]; + +#ifdef MAPBASE_RPC + // Built-in update spam limiter + float m_flRPCUpdateTimer = RPC_UPDATE_COOLDOWN; + + int m_spawnflags; +#endif +#else + CNetworkVar( string_t, m_iszRPCState ); + CNetworkVar( string_t, m_iszRPCDetails ); +#endif + + // TODO: Player-specific control + //CNetworkVar( int, m_iLimitingID ); +}; + +LINK_ENTITY_TO_CLASS( game_metadata, CMapbaseMetadata ); + +IMPLEMENT_NETWORKCLASS_ALIASED(MapbaseMetadata, DT_MapbaseMetadata) + +BEGIN_NETWORK_TABLE_NOBASE(CMapbaseMetadata, DT_MapbaseMetadata) + +#ifdef MAPBASE_RPC +#ifdef CLIENT_DLL + RecvPropString(RECVINFO(m_iszRPCState)), + RecvPropString(RECVINFO(m_iszRPCDetails)), + RecvPropInt( RECVINFO( m_spawnflags ) ), +#else + SendPropStringT(SENDINFO(m_iszRPCState) ), + SendPropStringT(SENDINFO(m_iszRPCDetails) ), + SendPropInt( SENDINFO(m_spawnflags), 8, SPROP_UNSIGNED ), +#endif +#endif + +END_NETWORK_TABLE() + +#ifndef CLIENT_DLL +BEGIN_DATADESC( CMapbaseMetadata ) + + // Inputs + DEFINE_INPUT( m_iszRPCState, FIELD_STRING, "SetRPCState" ), + DEFINE_INPUT( m_iszRPCDetails, FIELD_STRING, "SetRPCDetails" ), + +END_DATADESC() +#endif + +#ifdef MAPBASE_RPC +//----------------------------------------------------------------------------- +// Purpose: Mapbase's special integration with rich presence clients, most notably Discord. +// +// This only has Discord and crude groundwork for Steam as of writing, +//----------------------------------------------------------------------------- + +//----------------------------------------- +// !!! FOR MODS !!! +// +// Create your own Discord "application" if you want to change what info/images show up, etc. +// You can change the app ID in "scripts/mapbase_rpc.txt". It's located in the shared content VPK and the mod templates. +// You could override that file in your mod to change it to your own app ID. +// +// This code automatically shows the mod's title in the details, but it's easy to change this code if you want things to be chapter-specific, etc. +// +//----------------------------------------- + +// Changing the default value of the convars below will not work. +// Use "scripts/mapbase_rpc.txt" instead. +static ConVar cl_discord_appid("cl_discord_appid", "582595088719413250", FCVAR_NONE); +static ConVar cl_discord_largeimage("cl_discord_largeimage", "mb_logo_episodic", FCVAR_NONE); +static ConVar cl_discord_largeimage_text("cl_discord_largeimage_text", "Half-Life 2", FCVAR_NONE); +static int64_t startTimestamp = time(0); + +// + +int MapbaseRPC_GetPlayerCount() +{ + int iNumPlayers = 0; + + if (g_PR) + { + for (; iNumPlayers <= gpGlobals->maxClients; iNumPlayers++) + { + if (!g_PR->IsConnected( iNumPlayers )) + break; + } + } + + return iNumPlayers; +} + +//----------------------------------------------------------------------------- +// Discord RPC handlers +//----------------------------------------------------------------------------- +static void HandleDiscordReady(const DiscordUser* connectedUser) +{ + DevMsg("Discord: Connected to user %s#%s - %s\n", + connectedUser->username, + connectedUser->discriminator, + connectedUser->userId); +} + +static void HandleDiscordDisconnected(int errcode, const char* message) +{ + DevMsg("Discord: Disconnected (%d: %s)\n", errcode, message); +} + +static void HandleDiscordError(int errcode, const char* message) +{ + DevMsg("Discord: Error (%d: %s)\n", errcode, message); +} + +static void HandleDiscordJoin(const char* secret) +{ + // Not implemented +} + +static void HandleDiscordSpectate(const char* secret) +{ + // Not implemented +} + +static void HandleDiscordJoinRequest(const DiscordUser* request) +{ + // Not implemented +} + +void MapbaseRPC_Init() +{ + // Only init if RPC is enabled + if (mapbase_rpc_enabled.GetInt() <= 0) + return; + + // First, load the config + // (we need its values immediately) + KeyValues *pKV = new KeyValues( "MapbaseRPC" ); + if (pKV->LoadFromFile( filesystem, "scripts/mapbase_rpc.txt" )) + { + const char *szAppID = pKV->GetString("discord_appid", cl_discord_appid.GetString()); + cl_discord_appid.SetValue(szAppID); + + const char *szLargeImage = pKV->GetString("discord_largeimage", cl_discord_largeimage.GetString()); + cl_discord_largeimage.SetValue(szLargeImage); + + const char *szLargeImageText = pKV->GetString("discord_largeimage_text", cl_discord_largeimage_text.GetString()); + cl_discord_largeimage_text.SetValue( szLargeImageText ); + } + pKV->deleteThis(); + + // Steam RPC + if (steamapicontext) + { + if (steamapicontext->SteamFriends()) + steamapicontext->SteamFriends()->ClearRichPresence(); + } + + // Discord RPC + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + + handlers.ready = HandleDiscordReady; + handlers.disconnected = HandleDiscordDisconnected; + handlers.errored = HandleDiscordError; + handlers.joinGame = HandleDiscordJoin; + handlers.spectateGame = HandleDiscordSpectate; + handlers.joinRequest = HandleDiscordJoinRequest; + + char appid[255]; + sprintf(appid, "%d", engine->GetAppID()); + Discord_Initialize(cl_discord_appid.GetString(), &handlers, 1, appid); + + if (!g_bTextMode) + { + DiscordRichPresence discordPresence; + memset(&discordPresence, 0, sizeof(discordPresence)); + + MapbaseRPC_GetDiscordParameters(discordPresence, RPCSTATE_INIT, NULL); + + discordPresence.startTimestamp = startTimestamp; + + Discord_UpdatePresence(&discordPresence); + } +} + +void MapbaseRPC_Shutdown() +{ + // Discord RPC + Discord_ClearPresence(); + Discord_Shutdown(); + + // Steam RPC + if (steamapicontext) + { + if (steamapicontext->SteamFriends()) + steamapicontext->SteamFriends()->ClearRichPresence(); + } +} + +void MapbaseRPC_Update( int iType, const char *pMapName ) +{ + // All RPCs + MapbaseRPC_Update( INT_MAX, iType, pMapName ); +} + +void MapbaseRPC_Update( int iRPCMask, int iType, const char *pMapName ) +{ + // Only update if RPC is enabled + if (mapbase_rpc_enabled.GetInt() <= 0) + return; + + if (iRPCMask & RPCFlag(RPC_STEAM)) + MapbaseRPC_UpdateSteam(iType, pMapName); + if (iRPCMask & RPCFlag(RPC_DISCORD)) + MapbaseRPC_UpdateDiscord(iType, pMapName); +} + +#ifdef STEAM_RPC +void MapbaseRPC_UpdateSteam( int iType, const char *pMapName ) +{ + // No Steam + if (!steamapicontext || !steamapicontext->SteamFriends()) + return; + + const char *pszStatus = NULL; + + if (g_Metadata[RPC_STEAM] != NULL) + { + C_MapbaseMetadata *pMetadata = static_cast(g_Metadata[RPC_STEAM].Get()); + + if (pMetadata->m_iszRPCDetails[0] != NULL) + pszStatus = pMetadata->m_iszRPCDetails; + else if (pMetadata->m_iszRPCState[0] != NULL) + pszStatus = pMetadata->m_iszRPCState; + else + { + if (engine->IsLevelMainMenuBackground()) + pszStatus = VarArgs("Main Menu (%s)", pMapName ? pMapName : "N/A"); + else + pszStatus = VarArgs("Map: %s", pMapName ? pMapName : "N/A"); + } + } + else + { + switch (iType) + { + case RPCSTATE_INIT: + case RPCSTATE_LEVEL_SHUTDOWN: + { + pszStatus = "Main Menu"; + } break; + case RPCSTATE_LEVEL_INIT: + default: + { + // Say we're in the main menu if it's a background map + if (engine->IsLevelMainMenuBackground()) + { + pszStatus = VarArgs("Main Menu (%s)", pMapName ? pMapName : "N/A"); + } + else + { + pszStatus = VarArgs("Map: %s", pMapName ? pMapName : "N/A"); + } + } break; + } + } + + DevMsg( "Updating Steam\n" ); + + if (pszStatus) + { + steamapicontext->SteamFriends()->SetRichPresence( "gamestatus", pszStatus ); + steamapicontext->SteamFriends()->SetRichPresence( "steam_display", "#SteamRPC_Status" ); + + if (gpGlobals->maxClients > 1) + { + // Players in server + const CSteamID *serverID = serverengine->GetGameServerSteamID(); + if (serverID) + { + char szGroupID[32]; + Q_snprintf(szGroupID, sizeof(szGroupID), "%i", serverID->GetAccountID()); + + char szGroupSize[8]; + Q_snprintf(szGroupSize, sizeof(szGroupSize), "%i", MapbaseRPC_GetPlayerCount()); + + steamapicontext->SteamFriends()->SetRichPresence( "steam_player_group", szGroupID ); + steamapicontext->SteamFriends()->SetRichPresence( "steam_player_group_size", szGroupSize ); + } + else + { + DevWarning("Steam RPC cannot update player count (no server ID)\n"); + } + } + } +} +#endif + +#ifdef DISCORD_RPC +void MapbaseRPC_GetDiscordMapInfo( char *pDetails, size_t iSize, const char *pMapName ) +{ + if (!pMapName) + pMapName = "N/A"; + + // Say we're in the main menu if it's a background map + if (engine->IsLevelMainMenuBackground()) + { + Q_snprintf( pDetails, iSize, "Main Menu (%s)", pMapName ); + } + else + { + // Show the chapter title first + const char *szChapterTitle = NULL; + + C_World *pWorld = GetClientWorldEntity(); + if ( pWorld && pWorld->m_iszChapterTitle[0] != '\0' ) + { + szChapterTitle = g_pVGuiLocalize->FindAsUTF8( pWorld->m_iszChapterTitle ); + if (!szChapterTitle || szChapterTitle[0] == '\0') + szChapterTitle = pWorld->m_iszChapterTitle; + } + + if (szChapterTitle) + { + Q_snprintf( pDetails, iSize, "%s (%s)", szChapterTitle, pMapName ); + } + else + { + Q_snprintf( pDetails, iSize, "%s", pMapName ); + } + } +} + +void MapbaseRPC_GetDiscordParameters( DiscordRichPresence &discordPresence, int iType, const char *pMapName ) +{ + static char details[128]; + static char state[128]; + + details[0] = '\0'; + state[0] = '\0'; + + if (g_Metadata[RPC_DISCORD] != NULL) + { + C_MapbaseMetadata *pMetadata = static_cast(g_Metadata[RPC_DISCORD].Get()); + + if (pMetadata->m_iszRPCState[0] != NULL) + Q_strncpy( state, pMetadata->m_iszRPCState, sizeof(state) ); + else + Q_strncpy( state, g_iszGameName, sizeof(state) ); + + if (pMetadata->m_iszRPCDetails[0] != NULL) + Q_strncpy( details, pMetadata->m_iszRPCDetails, sizeof(details) ); + else + { + MapbaseRPC_GetDiscordMapInfo( details, sizeof(details), pMapName ); + } + } + else + { + Q_strncpy( state, g_iszGameName, sizeof(state) ); + + switch (iType) + { + case RPCSTATE_INIT: + case RPCSTATE_LEVEL_SHUTDOWN: + { + Q_strncpy( details, "Main Menu", sizeof(details) ); + } break; + case RPCSTATE_LEVEL_INIT: + default: + { + MapbaseRPC_GetDiscordMapInfo( details, sizeof(details), pMapName ); + } break; + } + } + + if (gpGlobals->maxClients > 1) + { + Q_snprintf( details, sizeof(details), "%s (%i/%i)", details, MapbaseRPC_GetPlayerCount(), gpGlobals->maxClients ); + } + + if (state[0] != '\0') + discordPresence.state = state; + if (details[0] != '\0') + discordPresence.details = details; + + // Generic Mapbase logo. Specific to the Mapbase Discord application. + discordPresence.smallImageKey = "mb_logo_general"; + discordPresence.smallImageText = "Mapbase"; + + discordPresence.largeImageKey = cl_discord_largeimage.GetString(); + discordPresence.largeImageText = cl_discord_largeimage_text.GetString(); +} + +void MapbaseRPC_UpdateDiscord( int iType, const char *pMapName ) +{ + DiscordRichPresence discordPresence; + memset(&discordPresence, 0, sizeof(discordPresence)); + + DevMsg("Updating Discord\n"); + + discordPresence.startTimestamp = startTimestamp; + + MapbaseRPC_GetDiscordParameters( discordPresence, iType, pMapName ); + + Discord_UpdatePresence(&discordPresence); +} + +void MapbaseRPC_CVarToggle( IConVar *var, const char *pOldString, float flOldValue ) +{ + if (flOldValue <= 0 && mapbase_rpc_enabled.GetInt() > 0) + { + // Turning on + MapbaseRPC_Init(); + MapbaseRPC_Update( g_MapName != NULL ? RPCSTATE_UPDATE : RPCSTATE_INIT, g_MapName ); + } + else if (mapbase_rpc_enabled.GetInt() <= 0) + { + // Turning off + MapbaseRPC_Shutdown(); + } +} +#endif + +#endif diff --git a/mp/src/game/shared/mapbase/mapbase_shared.cpp b/mp/src/game/shared/mapbase/mapbase_shared.cpp new file mode 100644 index 00000000..2763b457 --- /dev/null +++ b/mp/src/game/shared/mapbase/mapbase_shared.cpp @@ -0,0 +1,618 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Carries the Mapbase CAutoGameSystem that loads manifest among other things. +// Also includes code that does not fit anywhere else. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "tier0/icommandline.h" +#include "igamesystem.h" +#include "filesystem.h" +#include +#include +#include "saverestore_utlvector.h" +#include "props_shared.h" +#include "utlbuffer.h" +#ifdef CLIENT_DLL +#include "hud_closecaption.h" +#include "panelmetaclassmgr.h" +#include "c_soundscape.h" +#else +#include "soundscape_system.h" +#include "AI_ResponseSystem.h" +#include "mapbase/SystemConvarMod.h" +#include "gameinterface.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define GENERIC_MANIFEST_FILE "scripts/mapbase_default_manifest.txt" + +#ifdef CLIENT_DLL +#define AUTOLOADED_MANIFEST_FILE VarArgs("maps/%s_manifest.txt", g_MapName) +#else +#define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", g_MapName) +#endif + +const char *g_MapName; + +extern ISoundEmitterSystemBase *soundemitterbase; + +ConVar mapbase_load_default_manifest("mapbase_load_default_manifest", "1", FCVAR_ARCHIVE, "Should we automatically load our default manifest file? (\"maps/%mapname%_manifest.txt\")"); + +ConVar mapbase_load_soundscripts("mapbase_load_soundscripts", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscripts? e.g. \"maps/mapname_level_sounds.txt\""); + +//ConVar mapbase_load_propdata("mapbase_load_propdata", "1", FCVAR_ARCHIVE, "Should we load map-specific propdata files? e.g. \"maps/mapname_propdata.txt\""); + +//ConVar mapbase_load_soundscapes("mapbase_load_soundscapes", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscapes? e.g. \"maps/mapname_soundscapes.txt\""); + +ConVar mapbase_load_localization("mapbase_load_localization", "1", FCVAR_ARCHIVE, "Should we load map-specific localized text files? e.g. \"maps/mapname_english.txt\""); + +#ifdef CLIENT_DLL + +//ConVar mapbase_load_cc("mapbase_load_cc", "1", FCVAR_ARCHIVE, "Should we load map-specific closed captioning? e.g. \"maps/mapname_closecaption_english.txt\" and \"maps/mapname_closecaption_english.dat\""); + +#else + +ConVar mapbase_load_sentences("mapbase_load_sentences", "1", FCVAR_ARCHIVE, "Should we load map-specific sentences? e.g. \"maps/mapname_sentences.txt\""); + +ConVar mapbase_load_talker("mapbase_load_talker", "1", FCVAR_ARCHIVE, "Should we load map-specific talker files? e.g. \"maps/mapname_talker.txt\""); +ConVar mapbase_flush_talker("mapbase_flush_talker", "1", FCVAR_NONE, "Normally, when a map with custom talker files is unloaded, the response system resets to rid itself of the custom file(s). Turn this convar off to prevent that from happening."); + +ConVar mapbase_load_actbusy("mapbase_load_actbusy", "1", FCVAR_ARCHIVE, "Should we load map-specific actbusy files? e.g. \"maps/mapname_actbusy.txt\""); + +#endif + +#ifdef GAME_DLL +// This cvar should change with each Mapbase update +ConVar mapbase_version( "mapbase_version", "6.1", FCVAR_NONE, "The version of Mapbase currently being used in this mod." ); + +extern void MapbaseGameLog_Init(); + +extern void ParseCustomActbusyFile(const char *file); + +extern bool LoadResponseSystemFile(const char *scriptfile); +extern void ReloadResponseSystem(); + +// Reloads the response system when the map changes to avoid custom talker leaking +static bool g_bMapContainsCustomTalker; +#endif + +// Indicates this is a core Mapbase mod and not a mod using its code. +static bool g_bMapbaseCore; + +// The game's name found in gameinfo.txt. Mostly used for Discord RPC. +char g_iszGameName[128]; + +enum +{ + MANIFEST_SOUNDSCRIPTS, + //MANIFEST_PROPDATA, + //MANIFEST_SOUNDSCAPES, + MANIFEST_LOCALIZATION, +#ifdef CLIENT_DLL + //MANIFEST_CLOSECAPTION, + MANIFEST_VGUI, +#else + MANIFEST_TALKER, + MANIFEST_SENTENCES, + MANIFEST_ACTBUSY, +#endif + + // Must always be kept below + MANIFEST_NUM_TYPES, +}; + +struct ManifestType_t +{ + //int type; + const char *string; + ConVar *cvar; +}; + +// KEEP THS IN SYNC WITH THE ENUM! +static const ManifestType_t gm_szManifestFileStrings[MANIFEST_NUM_TYPES] = { + { "soundscripts", &mapbase_load_soundscripts }, + //{ "propdata", &mapbase_load_propdata }, + //{ "soundscapes", &mapbase_load_soundscapes }, + { "localization", &mapbase_load_localization }, +#ifdef CLIENT_DLL + //{ "closecaption", &mapbase_load_cc }, + { "vgui", NULL }, +#else + { "talker", &mapbase_load_talker }, + { "sentences", &mapbase_load_sentences }, + { "actbusy", &mapbase_load_actbusy }, +#endif +}; + +//----------------------------------------------------------------------------- +// Purpose: System used to load map-specific files, etc. +//----------------------------------------------------------------------------- +class CMapbaseSystem : public CAutoGameSystem +{ +public: + DECLARE_DATADESC(); + + CMapbaseSystem() : CAutoGameSystem( "CMapbaseSystem" ) + { + } + + inline bool GetGameInfoKeyValues(KeyValues *pKeyValues) + { + return pKeyValues->LoadFromFile( filesystem, "gameinfo.txt", "MOD" ); + } + + virtual bool Init() + { + // Checks gameinfo.txt for additional command line options + KeyValues *gameinfo = new KeyValues("GameInfo"); + if (GetGameInfoKeyValues(gameinfo)) + { + KeyValues *pCommandLineList = gameinfo->FindKey("CommandLine", false); + if (pCommandLineList) + { + for (KeyValues *pKey = pCommandLineList->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) + { + CommandLine()->AppendParm( pKey->GetName(), pKey->GetString() ); + } + } + } + gameinfo->deleteThis(); + +#ifdef CLIENT_DLL + InitializeRTs(); +#endif + + return true; + } + + void RefreshCustomTalker() + { +#ifdef GAME_DLL + if (g_bMapContainsCustomTalker && mapbase_flush_talker.GetBool()) + { + CGMsg( 1, "Mapbase Misc.", "Mapbase: Reloading response system to flush custom talker\n" ); + ReloadResponseSystem(); + g_bMapContainsCustomTalker = false; + } +#endif + } + + virtual void LevelInitPreEntity() + { +#ifdef GAME_DLL + CGMsg( 0, "Mapbase Misc.", "Mapbase system loaded\n" ); +#endif + + // Checks gameinfo.txt for Mapbase-specific options + KeyValues *gameinfo = new KeyValues("GameInfo"); + if (GetGameInfoKeyValues(gameinfo)) + { + // Indicates this is a core Mapbase mod and not a mod using its code + g_bMapbaseCore = gameinfo->GetBool("mapbase_core", false); + + if (!gameinfo->GetBool("hide_mod_name", false)) + { + // Store the game's name + const char *pszGameName = gameinfo->GetString("game_rpc", NULL); + if (pszGameName == NULL) + pszGameName = gameinfo->GetString("game"); + + Q_strncpy(g_iszGameName, pszGameName, sizeof(g_iszGameName)); + } + } + gameinfo->deleteThis(); + + RefreshMapName(); + + // Shared Mapbase localization file + g_pVGuiLocalize->AddFile( "resource/mapbase_%language%.txt" ); + } + + virtual void OnRestore() + { + RefreshMapName(); + } + + virtual void LevelInitPostEntity() + { + // Check for a generic "mapname_manifest.txt" file and load it. + if (filesystem->FileExists( AUTOLOADED_MANIFEST_FILE, "GAME" )) + { + AddManifestFile( AUTOLOADED_MANIFEST_FILE ); + } + else + { + // Load the generic script instead. + ParseGenericManifest(); + } + +#ifdef GAME_DLL + MapbaseGameLog_Init(); +#endif + } + + virtual void LevelShutdownPreEntity() + { + // How would we make sure they don't last between maps? + // TODO: Investigate ReloadLocalizationFiles() + //g_pVGuiLocalize->RemoveAll(); + //g_pVGuiLocalize->ReloadLocalizationFiles(); + } + + virtual void LevelShutdownPostEntity() + { + g_MapName = NULL; + + RefreshCustomTalker(); + } + + bool RefreshMapName() + { +#ifdef GAME_DLL + const char *pszMapName = STRING(gpGlobals->mapname); +#else + //char mapname[128]; + //Q_StripExtension(MapName(), mapname, sizeof(mapname)); + const char *pszMapName = MapName(); +#endif + + if (g_MapName == NULL || !FStrEq(pszMapName, g_MapName)) + { + g_MapName = pszMapName; + return true; + } + + return false; + } + +#ifdef CLIENT_DLL + bool m_bInitializedRTs = false; + CUtlVector m_CameraTextures; + + //----------------------------------------------------------------------------- + // Initialize custom RT textures if necessary + //----------------------------------------------------------------------------- + void InitializeRTs() + { + if (!m_bInitializedRTs) + { + int iNumCameras = CommandLine()->ParmValue( "-numcameratextures", 3 ); + + materials->BeginRenderTargetAllocation(); + + for (int i = 0; i < iNumCameras; i++) + { + char szName[32]; + Q_snprintf( szName, sizeof(szName), "_rt_Camera%i", i ); + + int iRefIndex = m_CameraTextures.AddToTail(); + + //m_CameraTextures[iRefIndex].InitRenderTarget( + // 256, 256, RT_SIZE_DEFAULT, + // g_pMaterialSystem->GetBackBufferFormat(), + // MATERIAL_RT_DEPTH_SHARED, true, szName ); + + m_CameraTextures[iRefIndex].Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2( + szName, + 256, 256, RT_SIZE_DEFAULT, + g_pMaterialSystem->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_SHARED, + 0, + CREATERENDERTARGETFLAGS_HDR ) ); + } + + materials->EndRenderTargetAllocation(); + + m_bInitializedRTs = true; + } + } + + void Shutdown() + { + if (m_bInitializedRTs) + { + for (int i = 0; i < m_CameraTextures.Count(); i++) + { + m_CameraTextures[i].Shutdown(); + } + m_bInitializedRTs = false; + } + } +#endif + + // Get a generic, hardcoded manifest with hardcoded names. + void ParseGenericManifest() + { + if (!mapbase_load_default_manifest.GetBool()) + return; + + KeyValues *pKV = new KeyValues("DefaultManifest"); + pKV->LoadFromFile(filesystem, GENERIC_MANIFEST_FILE); + + AddManifestFile(pKV/*, true*/); + + pKV->deleteThis(); + } + + void AddManifestFile( const char *file ) + { + KeyValues *pKV = new KeyValues(file); + if ( !pKV->LoadFromFile( filesystem, file ) ) + { + Warning("Mapbase Manifest: \"%s\" is unreadable or missing (can't load KV, check for syntax errors)\n", file); + pKV->deleteThis(); + return; + } + + CGMsg( 1, "Mapbase Misc.", "===== Mapbase Manifest: Loading manifest file %s =====\n", file ); + + AddManifestFile(pKV, false); + + CGMsg( 1, "Mapbase Misc.", "==============================================================================\n" ); + + pKV->deleteThis(); + } + + void LoadFromValue( const char *value, int type, bool bDontWarn ) + { + if (!filesystem->FileExists(value, "MOD")) + { + if (!bDontWarn) + { + Warning("Mapbase Manifest: WARNING! \"%s\" does not exist!\n", value); + } + return; + } + + switch (type) + { + case MANIFEST_SOUNDSCRIPTS: { soundemitterbase->AddSoundOverrides(value); } break; + //case MANIFEST_PROPDATA: { g_PropDataSystem.ParsePropDataFile(value); } break; + case MANIFEST_LOCALIZATION: { g_pVGuiLocalize->AddFile( value, "MOD", true ); } break; +#ifdef CLIENT_DLL + //case MANIFEST_CLOSECAPTION: { todo } break; + case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break; + //case MANIFEST_SOUNDSCAPES: { Soundscape_AddFile(value); } break; +#else + case MANIFEST_TALKER: { + g_bMapContainsCustomTalker = true; + LoadResponseSystemFile(value); //PrecacheCustomResponseSystem( value ); + } break; + //case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break; + case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break; + case MANIFEST_ACTBUSY: { ParseCustomActbusyFile(value); } break; +#endif + } + } + + // This doesn't call deleteThis()! + void AddManifestFile(KeyValues *pKV, bool bDontWarn = false) + { + char value[MAX_PATH]; + const char *name; + for (KeyValues *pKey = pKV->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) + { + value[0] = '\0'; + name = pKey->GetName(); + + // Parse %mapname%, etc. + bool inparam = false; + CUtlStringList outStrings; + V_SplitString( pKey->GetString(), "%", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + if (inparam) + { + if (FStrEq( outStrings[i], "mapname" )) + { + Q_strncat( value, g_MapName, sizeof( value ) ); + } + else if (FStrEq( outStrings[i], "language" )) + { +#ifdef CLIENT_DLL + char uilanguage[64]; + engine->GetUILanguage(uilanguage, sizeof(uilanguage)); + Q_strncat( value, uilanguage, sizeof( value ) ); +#else + // Give up, use English + Q_strncat( value, "english", sizeof( value ) ); +#endif + } + } + else + { + Q_strncat( value, outStrings[i], sizeof( value ) ); + } + + inparam = !inparam; + } + + outStrings.PurgeAndDeleteElements(); + + if (FStrEq(name, "NoErrors")) + { + bDontWarn = pKey->GetBool(); + } + + for (int i = 0; i < MANIFEST_NUM_TYPES; i++) + { + if (FStrEq(name, gm_szManifestFileStrings[i].string)) + { + if (!gm_szManifestFileStrings[i].cvar || gm_szManifestFileStrings[i].cvar->GetBool()) + { + LoadFromValue(value, i, bDontWarn); + } + break; + } + } + } + } + +#ifdef MAPBASE_VSCRIPT + void ScriptAddManifestFile( const char *szScript ) { AddManifestFile( szScript ); } + + void LoadSoundscriptFile( const char *szScript ) { LoadFromValue(szScript, MANIFEST_SOUNDSCRIPTS, false); } +#ifndef CLIENT_DLL + void LoadTalkerFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_TALKER, false ); } + void LoadActbusyFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_ACTBUSY, false ); } +#endif + + const char *GetModName() { return g_iszGameName; } + bool IsCoreMapbase() { return g_bMapbaseCore; } + + virtual void RegisterVScript() + { + g_pScriptVM->RegisterInstance( this, "Mapbase" ); + } +#endif +}; + +CMapbaseSystem g_MapbaseSystem; + +BEGIN_DATADESC_NO_BASE( CMapbaseSystem ) + + //DEFINE_UTLVECTOR( m_StoredManifestFiles, FIELD_STRING ), + +END_DATADESC() + +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CMapbaseSystem, SCRIPT_SINGLETON "All-purpose Mapbase system primarily used for map-specific files." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddManifestFile, "AddManifestFile", "Loads a manifest file." ) + DEFINE_SCRIPTFUNC( LoadSoundscriptFile, "Loads a custom soundscript file." ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( LoadTalkerFile, "Loads a custom talker file." ) + DEFINE_SCRIPTFUNC( LoadActbusyFile, "Loads a custom actbusy file." ) +#endif + DEFINE_SCRIPTFUNC( GetModName, "Gets the name of the mod. This is the name which shows up on Steam, RPC, etc." ) + DEFINE_SCRIPTFUNC( IsCoreMapbase, "Indicates whether this is one of the original Mapbase mods or just a separate mod using its code." ) +END_SCRIPTDESC(); +#endif + +static void CC_Mapbase_LoadManifestFile( const CCommand& args ) +{ + g_MapbaseSystem.AddManifestFile(args[1]); +} + +#ifdef CLIENT_DLL +static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile_client", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file on the client. If you don't want this to be saved and found when reloaded, type a '1' after the file path." ); +#else +static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file. If you don't want this to be saved and found when reloaded, type a '1' after the file path." ); +#endif + +#ifdef GAME_DLL +static CUtlVector g_MapbaseChapterMaps; +static CUtlVector g_MapbaseChapterList; +CUtlVector *Mapbase_GetChapterMaps() +{ + if (g_MapbaseChapterMaps.Count() == 0) + { + // Check the chapter list + KeyValues *chapterlist = new KeyValues("ChapterList"); + if (chapterlist->LoadFromFile(filesystem, "scripts/chapters.txt", "MOD")) + { + KeyValues *pKey = chapterlist->GetFirstSubKey(); + if (pKey) + { + if (Q_stricmp( pKey->GetName(), "Chapters" ) == 0) + { + for (KeyValues *pChapters = pKey->GetFirstSubKey(); pChapters; pChapters = pChapters->GetNextKey()) + { + int index = g_MapbaseChapterList.AddToTail(); + g_MapbaseChapterList[index].iChapter = atoi(pChapters->GetName()); + Q_strncpy(g_MapbaseChapterList[index].pChapterName, pChapters->GetString(), sizeof(g_MapbaseChapterList[index])); + } + } + + for (pKey = pKey->GetNextKey(); pKey; pKey = pKey->GetNextKey()) + { + int index = g_MapbaseChapterMaps.AddToTail(); + Q_strncpy(g_MapbaseChapterMaps[index].pBSPName, pKey->GetName(), sizeof(g_MapbaseChapterMaps[index].pBSPName)); + Q_strncpy(g_MapbaseChapterMaps[index].pTitleName, pKey->GetString(), sizeof(g_MapbaseChapterMaps[index].pTitleName)); + + //comment.pBSPName = pKey->GetName(); + //comment.pTitleName = pKey->GetString(); + } + } + } + chapterlist->deleteThis(); + } + + return &g_MapbaseChapterMaps; +} + +CUtlVector *Mapbase_GetChapterList() +{ + return &g_MapbaseChapterList; +} + +ThreeState_t Flashlight_GetLegacyVersionKey() +{ + KeyValues *gameinfo = new KeyValues( "GameInfo" ); + if (g_MapbaseSystem.GetGameInfoKeyValues( gameinfo )) + { + // -1 = default + int iUseLegacyFlashlight = gameinfo->GetInt( "use_legacy_flashlight", -1 ); + if (iUseLegacyFlashlight > -1) + return iUseLegacyFlashlight != 0 ? TRS_TRUE : TRS_FALSE; + } + gameinfo->deleteThis(); + + return TRS_NONE; +} + +#define SF_MANIFEST_START_ACTIVATED (1 << 0) + +class CMapbaseManifestEntity : public CPointEntity +{ +public: + DECLARE_DATADESC(); + + DECLARE_CLASS( CMapbaseManifestEntity, CPointEntity ); + + void Spawn( void ) + { + BaseClass::Spawn(); + if (HasSpawnFlags(SF_MANIFEST_START_ACTIVATED)) + { + LoadManifestFile(); + } + } + + void LoadManifestFile( void ) + { + const char *scriptfile = STRING(m_target); + if ( filesystem->FileExists( scriptfile, "MOD" ) ) + { + CGMsg(0, "Mapbase Misc.", "Mapbase: Adding manifest file \"%s\"\n", scriptfile); + g_MapbaseSystem.AddManifestFile(scriptfile); + } + else + { + Warning("Mapbase: Manifest file \"%s\" does not exist!", scriptfile); + } + } + + void InputActivate(inputdata_t &inputdata) + { + LoadManifestFile(); + } +}; + +LINK_ENTITY_TO_CLASS( mapbase_manifest, CMapbaseManifestEntity ); + +BEGIN_DATADESC( CMapbaseManifestEntity ) + + // Needs to be set up in the Activate methods of derived classes + //DEFINE_CUSTOM_FIELD( m_pInstancedResponseSystem, responseSystemSaveRestoreOps ), + + // Function Pointers + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + +END_DATADESC() +#endif diff --git a/mp/src/game/shared/mapbase/matchers.cpp b/mp/src/game/shared/mapbase/matchers.cpp new file mode 100644 index 00000000..f1f1d26b --- /dev/null +++ b/mp/src/game/shared/mapbase/matchers.cpp @@ -0,0 +1,239 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: General matching functions for things like wildcards and !=. +// +// $NoKeywords: $ +//============================================================================= + +#include "cbase.h" + +#include "matchers.h" +#include "fmtstr.h" + +// glibc (Linux) uses these tokens when including , so we must not #define them +#undef max +#undef min +#include +#undef MINMAX_H +#include "minmax.h" + +ConVar mapbase_wildcards_enabled("mapbase_wildcards_enabled", "1", FCVAR_NONE, "Toggles Mapbase's '?' wildcard and true '*' features. Useful for maps that have '?' in their targetnames."); +ConVar mapbase_regex_enabled("mapbase_regex_enabled", "1", FCVAR_NONE, "Toggles Mapbase's regex matching handover."); + +#ifdef CLIENT_DLL +// FIXME: There is no clientside equivalent to the RS code +static bool ResponseSystemCompare(const char *criterion, const char *value) { return Matcher_NamesMatch(criterion, value); } +#else +extern bool ResponseSystemCompare(const char *criterion, const char *value); +#endif + +//============================================================================= +// These are the "matchers" that compare with wildcards ("any*" for text starting with "any") +// and operators (<3 for numbers less than 3). +// +// Matcher_Match - Matching function using RS operators and NamesMatch wildcards/regex. +// Matcher_Regex - Uses regex functions from the std library. +// Matcher_NamesMatch - Based on Valve's original NamesMatch function, using wildcards and regex. +// +// AppearsToBeANumber - Response System-based function which checks if the string might be a number. +//============================================================================= + +bool Matcher_Match(const char *pszQuery, const char *szValue) +{ + // I wasn't kidding when I said all this did was hijack response system matching. + return ResponseSystemCompare(pszQuery, szValue); +} + +bool Matcher_Match(const char *pszQuery, int iValue) { return Matcher_Match(pszQuery, CNumStr(iValue)); } +bool Matcher_Match(const char *pszQuery, float flValue) { return Matcher_Match(pszQuery, CNumStr(flValue)); } + +// ------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- + +// The recursive part of Mapbase's modified version of Valve's NamesMatch(). +bool Matcher_RunCharCompare(const char *pszQuery, const char *szValue) +{ + // This matching model is based off of the ASW SDK + while ( *szValue && *pszQuery ) + { + char cName = *szValue; + char cQuery = *pszQuery; + if ( cName != cQuery && tolower(cName) != tolower(cQuery) ) // people almost always use lowercase, so assume that first + { + // Now we'll try the new and improved Mapbase wildcards! + switch (*pszQuery) + { + case '*': + { + // Return true at classic trailing * + if ( *(pszQuery+1) == 0 ) + return true; + + if (mapbase_wildcards_enabled.GetBool()) + { + // There's text after this * which we need to test. + // This recursion allows for multiple wildcards + int vlen = Q_strlen(szValue); + ++pszQuery; + for (int i = 0; i < vlen; i++) + { + if (Matcher_RunCharCompare(pszQuery, szValue + i)) + return true; + } + } + return false; + } break; + case '?': + // Just skip if we're capable of lazy wildcards + if (mapbase_wildcards_enabled.GetBool()) + break; + default: + return false; + } + } + ++szValue; + ++pszQuery; + } + + // Include a classic trailing * check for when szValue is something like "value" and pszQuery is "value*" + return ( ( *pszQuery == 0 && *szValue == 0 ) || *pszQuery == '*' ); +} + +// Regular expressions based off of the std library. +// The C++ is strong in this one. +bool Matcher_Regex(const char *pszQuery, const char *szValue) +{ + std::regex regex; + + // Since I can't find any other way to check for valid regex, + // use a try-catch here to see if it throws an exception. + try { regex = std::regex(pszQuery); } + catch (std::regex_error &e) + { + Msg("Invalid regex \"%s\" (%s)\n", pszQuery, e.what()); + return false; + } + + std::match_results results; + bool bMatch = std::regex_match( szValue, results, regex ); + if (!bMatch) + return false; + + // Only match the *whole* string + return Q_strlen(results.str(0).c_str()) == Q_strlen(szValue); +} + +// The entry point for Mapbase's modified version of Valve's NamesMatch(). +bool Matcher_NamesMatch(const char *pszQuery, const char *szValue) +{ + if ( szValue == NULL ) + return (*pszQuery == 0 || *pszQuery == '*'); + + // If the pointers are identical, we're identical + if ( szValue == pszQuery ) + return true; + + // Check for regex + if ( *pszQuery == '@' && mapbase_regex_enabled.GetBool() ) + { + // Make sure it has a forward slash + // (prevents confusion with instance fixup escape) + if (*(pszQuery+1) == '/') + { + return Matcher_Regex( pszQuery+2, szValue ); + } + } + + return Matcher_RunCharCompare( pszQuery, szValue ); +} + +bool Matcher_NamesMatch_Classic(const char *pszQuery, const char *szValue) +{ + if ( szValue == NULL ) + return (!pszQuery || *pszQuery == 0 || *pszQuery == '*'); + + // If the pointers are identical, we're identical + if ( szValue == pszQuery ) + return true; + + while ( *szValue && *pszQuery ) + { + unsigned char cName = *szValue; + unsigned char cQuery = *pszQuery; + // simple ascii case conversion + if ( cName == cQuery ) + ; + else if ( cName - 'A' <= (unsigned char)'Z' - 'A' && cName - 'A' + 'a' == cQuery ) + ; + else if ( cName - 'a' <= (unsigned char)'z' - 'a' && cName - 'a' + 'A' == cQuery ) + ; + else + break; + ++szValue; + ++pszQuery; + } + + if ( *pszQuery == 0 && *szValue == 0 ) + return true; + + // @TODO (toml 03-18-03): Perhaps support real wildcards. Right now, only thing supported is trailing * + if ( *pszQuery == '*' ) + return true; + + return false; +} + +bool Matcher_NamesMatch_MutualWildcard(const char *pszQuery, const char *szValue) +{ + if ( szValue == NULL ) + return (!pszQuery || *pszQuery == 0 || *pszQuery == '*'); + + if ( pszQuery == NULL ) + return (!szValue || *szValue == 0 || *szValue == '*'); + + // If the pointers are identical, we're identical + if ( szValue == pszQuery ) + return true; + + while ( *szValue && *pszQuery ) + { + unsigned char cName = *szValue; + unsigned char cQuery = *pszQuery; + // simple ascii case conversion + if ( cName == cQuery ) + ; + else if ( cName - 'A' <= (unsigned char)'Z' - 'A' && cName - 'A' + 'a' == cQuery ) + ; + else if ( cName - 'a' <= (unsigned char)'z' - 'a' && cName - 'a' + 'A' == cQuery ) + ; + else + break; + ++szValue; + ++pszQuery; + } + + if ( *pszQuery == 0 && *szValue == 0 ) + return true; + + // @TODO (toml 03-18-03): Perhaps support real wildcards. Right now, only thing supported is trailing * + if ( *pszQuery == '*' || *szValue == '*' ) + return true; + + return false; +} + +// Matcher_Compare is a deprecated alias originally used when Matcher_Match didn't support wildcards. +/* +bool Matcher_Compare(const char *pszQuery, const char *szValue) +{ + return Matcher_Match(pszQuery, szValue); +#if 0 + // I have to do this so wildcards could test *before* the response system comparison. + // I know it removes the operators twice, but I won't worry about it. + bool match = Matcher_NamesMatch(Matcher_RemoveOperators(pszQuery), szValue); + if (match) + return Matcher_Match(pszQuery, szValue); + return false; +#endif +} +*/ diff --git a/mp/src/game/shared/mapbase/matchers.h b/mp/src/game/shared/mapbase/matchers.h new file mode 100644 index 00000000..52e8cc1a --- /dev/null +++ b/mp/src/game/shared/mapbase/matchers.h @@ -0,0 +1,68 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: General matching functions for things like wildcards and !=. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef MAPBASE_MATCHERS_H +#define MAPBASE_MATCHERS_H +#ifdef _WIN32 +#pragma once +#endif + + +#define MAPBASE_MATCHERS 1 + +// Compares with != and the like. Basically hijacks the response system matching. +// This also loops back around to Matcher_NamesMatch. +// pszQuery = The value that should have the operator(s) at the beginning. +// szValue = The value tested against the criterion. +bool Matcher_Match( const char *pszQuery, const char *szValue ); +bool Matcher_Match( const char *pszQuery, int iValue ); +bool Matcher_Match( const char *pszQuery, float flValue ); + +// Regular expressions based off of the std library. +// pszQuery = The regex text. +// szValue = The value that should be matched. +bool Matcher_Regex( const char *pszQuery, const char *szValue ); + +// Compares two strings with support for wildcards or regex. This code is an expanded version of baseentity.cpp's NamesMatch(). +// pszQuery = The value that should have the wildcard. +// szValue = The value tested against the query. +// Use Matcher_Match if you want <, !=, etc. as well. +bool Matcher_NamesMatch( const char *pszQuery, const char *szValue ); + +// Identical to baseentity.cpp's original NamesMatch(). +// pszQuery = The value that should have the wildcard. +// szValue = The value tested against the query. +bool Matcher_NamesMatch_Classic( const char *pszQuery, const char *szValue ); + +// Identical to Matcher_NamesMatch_Classic(), but either value could use a wildcard. +// pszQuery = The value that serves as the query. This value can use wildcards. +// szValue = The value tested against the query. This value can use wildcards as well. +bool Matcher_NamesMatch_MutualWildcard( const char *pszQuery, const char *szValue ); + +// Deprecated; do not use +static inline bool Matcher_Compare( const char *pszQuery, const char *szValue ) { return Matcher_Match( pszQuery, szValue ); } + +// Taken from the Response System. +// Checks if the specified string appears to be a number of some sort. +static bool AppearsToBeANumber( char const *token ) +{ + if ( atof( token ) != 0.0f ) + return true; + + char const *p = token; + while ( *p ) + { + if ( *p != '0' ) + return false; + + p++; + } + + return true; +} + +#endif diff --git a/mp/src/game/shared/mapbase/vscript_consts_shared.cpp b/mp/src/game/shared/mapbase/vscript_consts_shared.cpp new file mode 100644 index 00000000..b03ee0e8 --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -0,0 +1,512 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript constants and enums shared between the server and client. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "activitylist.h" +#include "in_buttons.h" +#ifdef CLIENT_DLL +#include "c_ai_basenpc.h" +#else +#include "ai_basenpc.h" +#include "globalstate.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( IN, "Button mask bindings" ) + + DEFINE_ENUMCONST_NAMED( IN_ATTACK, "ATTACK", "Button for +attack" ) + DEFINE_ENUMCONST_NAMED( IN_JUMP, "JUMP", "Button for +jump" ) + DEFINE_ENUMCONST_NAMED( IN_DUCK, "DUCK", "Button for +duck" ) + DEFINE_ENUMCONST_NAMED( IN_FORWARD, "FORWARD", "Button for +forward" ) + DEFINE_ENUMCONST_NAMED( IN_BACK, "BACK", "Button for +back" ) + DEFINE_ENUMCONST_NAMED( IN_USE, "USE", "Button for +use" ) + DEFINE_ENUMCONST_NAMED( IN_CANCEL, "CANCEL", "Special button flag for attack cancel" ) + DEFINE_ENUMCONST_NAMED( IN_LEFT, "LEFT", "Button for +left" ) + DEFINE_ENUMCONST_NAMED( IN_RIGHT, "RIGHT", "Button for +right" ) + DEFINE_ENUMCONST_NAMED( IN_MOVELEFT, "MOVELEFT", "Button for +moveleft" ) + DEFINE_ENUMCONST_NAMED( IN_MOVERIGHT, "MOVERIGHT", "Button for +moveright" ) + DEFINE_ENUMCONST_NAMED( IN_ATTACK2, "ATTACK2", "Button for +attack2" ) + DEFINE_ENUMCONST_NAMED( IN_RUN, "RUN", "Unused button (see IN.SPEED for sprint)" ) + DEFINE_ENUMCONST_NAMED( IN_RELOAD, "RELOAD", "Button for +reload" ) + DEFINE_ENUMCONST_NAMED( IN_ALT1, "ALT1", "Button for +alt1" ) + DEFINE_ENUMCONST_NAMED( IN_ALT2, "ALT2", "Button for +alt2" ) + DEFINE_ENUMCONST_NAMED( IN_SCORE, "SCORE", "Button for +score" ) + DEFINE_ENUMCONST_NAMED( IN_SPEED, "SPEED", "Button for +speed" ) + DEFINE_ENUMCONST_NAMED( IN_WALK, "WALK", "Button for +walk" ) + DEFINE_ENUMCONST_NAMED( IN_ZOOM, "ZOOM", "Button for +zoom" ) + DEFINE_ENUMCONST_NAMED( IN_WEAPON1, "WEAPON1", "Special button used by weapons themselves" ) + DEFINE_ENUMCONST_NAMED( IN_WEAPON2, "WEAPON2", "Special button used by weapons themselves" ) + DEFINE_ENUMCONST_NAMED( IN_BULLRUSH, "BULLRUSH", "Unused button" ) + DEFINE_ENUMCONST_NAMED( IN_GRENADE1, "GRENADE1", "Button for +grenade1" ) + DEFINE_ENUMCONST_NAMED( IN_GRENADE2, "GRENADE2", "Button for +grenade2" ) + DEFINE_ENUMCONST_NAMED( IN_ATTACK3, "ATTACK3", "Button for +attack3" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( RenderMode, "Render modes used by Get/SetRenderMode" ) + + DEFINE_ENUMCONST_NAMED( kRenderNormal, "Normal", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransColor, "Color", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransTexture, "Texture", "" ) + DEFINE_ENUMCONST_NAMED( kRenderGlow, "Glow", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAlpha, "Solid", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAdd, "Additive", "" ) + DEFINE_ENUMCONST_NAMED( kRenderEnvironmental, "Environmental", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAddFrameBlend, "AdditiveFractionalFrame", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAlphaAdd, "Alpha Add", "" ) + DEFINE_ENUMCONST_NAMED( kRenderWorldGlow, "WorldSpaceGlow", "" ) + DEFINE_ENUMCONST_NAMED( kRenderNone, "None", "" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( Hitgroup, "Hit groups from traces" ) + + DEFINE_ENUMCONST_NAMED( HITGROUP_GENERIC, "Generic", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_HEAD, "Head", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_CHEST, "Chest", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_STOMACH, "Stomach", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_LEFTARM, "LeftArm", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_RIGHTARM, "RightArm", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_LEFTLEG, "LeftLeg", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_RIGHTLEG, "RightLeg", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_GEAR, "Gear", "" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( MapLoad, "Map load enum for GetLoadType()" ) + + DEFINE_ENUMCONST_NAMED( MapLoad_NewGame, "NewGame", "Map was loaded from a new game" ) + DEFINE_ENUMCONST_NAMED( MapLoad_LoadGame, "LoadGame", "Map was loaded from a save file" ) + DEFINE_ENUMCONST_NAMED( MapLoad_Transition, "Transition", "Map was loaded from a level transition" ) + DEFINE_ENUMCONST_NAMED( MapLoad_Background, "Background", "Map was loaded as a background map" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +void RegisterActivityConstants() +{ + // Make sure there are no activities declared yet + if (g_pScriptVM->ValueExists( "ACT_RESET" )) + return; + + // Register activity constants by just iterating through the entire activity list + for (int i = 0; i < ActivityList_HighestIndex(); i++) + { + ScriptRegisterConstantNamed( g_pScriptVM, i, ActivityList_NameForIndex(i), "" ); + } +} + +//============================================================================= +//============================================================================= + +extern void RegisterWeaponScriptConstants(); + +void RegisterSharedScriptConstants() +{ + // + // Activities + // + + // Scripts have to use this function before using any activity constants. + // This is because initializing 1,700+ constants every time a level loads and letting them lay around + // usually doing nothing sounds like a bad idea. + ScriptRegisterFunction( g_pScriptVM, RegisterActivityConstants, "Registers all activity IDs as usable constants." ); + + + // + // Damage Types + // + ScriptRegisterConstant( g_pScriptVM, DMG_GENERIC, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_CRUSH, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_BULLET, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_SLASH, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_BURN, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_VEHICLE, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_FALL, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_BLAST, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_CLUB, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_SHOCK, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_SONIC, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_ENERGYBEAM, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_PREVENT_PHYSICS_FORCE, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_NEVERGIB, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_ALWAYSGIB, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_DROWN, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_PARALYZE, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_NERVEGAS, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_POISON, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_RADIATION, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_DROWNRECOVER, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_ACID, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_SLOWBURN, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_REMOVENORAGDOLL, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_PHYSGUN, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_PLASMA, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_AIRBOAT, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_DISSOLVE, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_BLAST_SURFACE, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_DIRECT, "Damage type used in damage information." ); + ScriptRegisterConstant( g_pScriptVM, DMG_BUCKSHOT, "Damage type used in damage information." ); + + // + // Collision Groups + // + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NONE, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DEBRIS, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DEBRIS_TRIGGER, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_INTERACTIVE_DEBRIS, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_INTERACTIVE, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PLAYER, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_BREAKABLE_GLASS, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_VEHICLE, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PLAYER_MOVEMENT, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NPC, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_IN_VEHICLE, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_WEAPON, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_VEHICLE_CLIP, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PROJECTILE, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DOOR_BLOCKER, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PASSABLE_DOOR, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DISSOLVING, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PUSHAWAY, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NPC_ACTOR, "Collision group used in GetCollisionGroup(), etc." ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NPC_SCRIPTED, "Collision group used in GetCollisionGroup(), etc." ); + + // + // Flags + // + ScriptRegisterConstant( g_pScriptVM, FL_ONGROUND, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_DUCKING, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_WATERJUMP, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_ONTRAIN, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_INRAIN, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_FROZEN, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_ATCONTROLS, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_CLIENT, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_FAKECLIENT, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_INWATER, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_FLY, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_SWIM, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_CONVEYOR, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_NPC, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_GODMODE, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_NOTARGET, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_AIMTARGET, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_PARTIALGROUND, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_STATICPROP, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_GRAPHED, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_GRENADE, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_STEPMOVEMENT, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_DONTTOUCH, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_BASEVELOCITY, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_WORLDBRUSH, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_OBJECT, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_KILLME, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_ONFIRE, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_DISSOLVING, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_TRANSRAGDOLL, "Flag used in GetFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FL_UNBLOCKABLE_BY_PLAYER, "Flag used in GetFlags(), etc." ); + + // + // Entity Flags + // + ScriptRegisterConstant( g_pScriptVM, EFL_KILLME, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DORMANT, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NOCLIP_ACTIVE, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_SETTING_UP_BONES, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_KEEP_ON_RECREATE_ENTITIES, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_HAS_PLAYER_CHILD, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_SHADOWUPDATE, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NOTIFY, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_FORCE_CHECK_TRANSMIT, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_BOT_FROZEN, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_SERVER_ONLY, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_AUTO_EDICT_ATTACH, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_ABSTRANSFORM, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_ABSVELOCITY, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_ABSANGVELOCITY, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_SPATIAL_PARTITION, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_PLUGIN_BASED_BOT, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_IN_SKYBOX, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_USE_PARTITION_WHEN_NOT_SOLID, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_TOUCHING_FLUID, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_IS_BEING_LIFTED_BY_BARNACLE, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_ROTORWASH_PUSH, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_THINK_FUNCTION, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_GAME_PHYSICS_SIMULATION, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_CHECK_UNTOUCH, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DONTBLOCKLOS, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_DONTWALKON, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_DISSOLVE, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_MEGAPHYSCANNON_RAGDOLL, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_WATER_VELOCITY_CHANGE, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_PHYSCANNON_INTERACTION, "Entity flag used in GetEFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_DAMAGE_FORCES, "Entity flag used in GetEFlags(), etc." ); + + // + // Effects + // + ScriptRegisterConstant( g_pScriptVM, EF_BONEMERGE, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_BRIGHTLIGHT, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_DIMLIGHT, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_NOINTERP, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_NOSHADOW, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_NODRAW, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_NORECEIVESHADOW, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_BONEMERGE_FASTCULL, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_ITEM_BLINK, "Effect flag used in GetEffects(), etc." ); + ScriptRegisterConstant( g_pScriptVM, EF_PARENT_ANIMATES, "Effect flag used in GetEffects(), etc." ); + + // + // Solid Flags + // + ScriptRegisterConstant( g_pScriptVM, FSOLID_CUSTOMRAYTEST, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_CUSTOMBOXTEST, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_NOT_SOLID, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_TRIGGER, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_NOT_STANDABLE, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_VOLUME_CONTENTS, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_FORCE_WORLD_ALIGNED, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_USE_TRIGGER_BOUNDS, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_ROOT_PARENT_ALIGNED, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_TRIGGER_TOUCH_DEBRIS, "Solid flag used in GetSolidFlags(), etc." ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_COLLIDE_WITH_OWNER, "Solid flag used in GetSolidFlags(), etc." ); + + // + // Movetypes + // + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_NONE, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_ISOMETRIC, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_WALK, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_STEP, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_FLY, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_FLYGRAVITY, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_VPHYSICS, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_PUSH, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_NOCLIP, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_LADDER, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_OBSERVER, "Move type used in GetMoveType(), etc." ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_CUSTOM, "Move type used in GetMoveType(), etc." ); + +#ifdef GAME_DLL + // + // Sound Types, Contexts, and Channels + // (QueryHearSound hook can use these) + // + ScriptRegisterConstant( g_pScriptVM, SOUND_NONE, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_COMBAT, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_WORLD, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_PLAYER, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_DANGER, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_BULLET_IMPACT, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CARCASS, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_MEAT, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_GARBAGE, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_THUMPER, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_BUGBAIT, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_PHYSICS_DANGER, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_DANGER_SNIPERONLY, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_MOVE_AWAY, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_PLAYER_VEHICLE, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_READINESS_LOW, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_READINESS_MEDIUM, "Sound type used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_READINESS_HIGH, "Sound type used in QueryHearSound hooks, etc." ); + + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_FROM_SNIPER, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_GUNFIRE, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_MORTAR, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_COMBINE_ONLY, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_REACT_TO_SOURCE, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_EXPLOSION, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_EXCLUDE_COMBINE, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_DANGER_APPROACH, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_ALLIES_ONLY, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_PLAYER_VEHICLE, "Sound context used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_OWNER_ALLIES, "Sound context used in QueryHearSound hooks, etc." ); + + ScriptRegisterConstant( g_pScriptVM, ALL_CONTEXTS, "All sound contexts useable in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, ALL_SCENTS, "All \"scent\" sound types useable in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, ALL_SOUNDS, "All sound types useable in QueryHearSound hooks, etc." ); + + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_UNSPECIFIED, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_REPEATING, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_REPEATED_DANGER, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_WEAPON, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_INJURY, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_BULLET_IMPACT, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_NPC_FOOTSTEP, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_SPOOKY_NOISE, "Sound channel used in QueryHearSound hooks, etc." ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_ZOMBINE_GRENADE, "Sound channel used in QueryHearSound hooks, etc." ); + + ScriptRegisterConstantNamed( g_pScriptVM, (int)SOUNDENT_VOLUME_MACHINEGUN, "SOUNDENT_VOLUME_MACHINEGUN", "Sound volume preset for use in InsertAISound, etc." ); + ScriptRegisterConstantNamed( g_pScriptVM, (int)SOUNDENT_VOLUME_SHOTGUN, "SOUNDENT_VOLUME_SHOTGUN", "Sound volume preset for use in InsertAISound, etc." ); + ScriptRegisterConstantNamed( g_pScriptVM, (int)SOUNDENT_VOLUME_PISTOL, "SOUNDENT_VOLUME_PISTOL", "Sound volume preset for use in InsertAISound, etc." ); + ScriptRegisterConstantNamed( g_pScriptVM, (int)SOUNDENT_VOLUME_EMPTY, "SOUNDENT_VOLUME_PISTOL", "Sound volume preset for use in InsertAISound, etc." ); + + // + // Capabilities + // + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_GROUND, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_JUMP, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_FLY, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_CLIMB, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_SWIM, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_CRAWL, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_SHOOT, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_SKIP_NAV_GROUND_CHECK, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_USE, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + //ScriptRegisterConstant( g_pScriptVM, bits_CAP_HEAR, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_AUTO_DOORS, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_OPEN_DOORS, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_TURN_HEAD, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_RANGE_ATTACK1, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_RANGE_ATTACK2, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_MELEE_ATTACK1, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_MELEE_ATTACK2, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_RANGE_ATTACK1, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_RANGE_ATTACK2, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_MELEE_ATTACK1, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_MELEE_ATTACK2, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_USE_WEAPONS, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + //ScriptRegisterConstant( g_pScriptVM, bits_CAP_STRAFE, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_ANIMATEDFACE, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_USE_SHOT_REGULATOR, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_FRIENDLY_DMG_IMMUNE, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_SQUAD, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_DUCK, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_NO_HIT_PLAYER, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_AIM_GUN, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_NO_HIT_SQUADMATES, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_SIMPLE_RADIUS_DAMAGE, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + + ScriptRegisterConstant( g_pScriptVM, bits_CAP_DOORS_GROUP, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_RANGE_ATTACK_GROUP, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MELEE_ATTACK_GROUP, "NPC/player/weapon capability used in GetCapabilities(), etc." ); + + // + // Class_T classes + // + ScriptRegisterConstant( g_pScriptVM, CLASS_NONE, "No class." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_PLAYER, "Used by players." ); + +#ifdef HL2_DLL + + ScriptRegisterConstant( g_pScriptVM, CLASS_PLAYER_ALLY, "Used by citizens, hacked manhacks, and other misc. allies." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_PLAYER_ALLY_VITAL, "Used by Alyx, Barney, and other allies vital to HL2." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ANTLION, "Used by antlions, antlion guards, etc." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_BARNACLE, "Used by barnacles." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSEYE, "Used by npc_bullseye." ); + //ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSQUID, "Used by bullsquids." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_CITIZEN_PASSIVE, "Used by citizens when the \"gordon_precriminal\" or \"citizens_passive\" states are enabled." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_CITIZEN_REBEL, "UNUSED IN HL2. Rebels normally use CLASS_PLAYER_ALLY." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE, "Used by Combine soldiers, Combine turrets, and other misc. Combine NPCs." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_GUNSHIP, "Used by Combine gunships, helicopters, etc." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_CONSCRIPT, "UNUSED IN HL2. Would've been used by conscripts." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_HEADCRAB, "Used by headcrabs." ); + //ScriptRegisterConstant( g_pScriptVM, CLASS_HOUNDEYE, "Used by houndeyes." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_MANHACK, "Used by Combine manhacks." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_METROPOLICE, "Used by Combine metrocops." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_MILITARY, "In HL2, this is only used by npc_combinecamera and func_guntarget. This appears to be recognized as a Combine class." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_SCANNER, "Used by Combine city scanners and claw scanners." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_STALKER, "Used by Combine stalkers." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_VORTIGAUNT, "Used by vortigaunts." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ZOMBIE, "Used by zombies." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_PROTOSNIPER, "Used by Combine snipers." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_MISSILE, "Used by RPG and APC missiles." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_FLARE, "Used by env_flares." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_EARTH_FAUNA, "Used by birds and other terrestrial animals." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_HACKED_ROLLERMINE, "Used by rollermines which were hacked by Alyx." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_HUNTER, "Used by Combine hunters." ); + +#elif defined( HL1_DLL ) + + ScriptRegisterConstant( g_pScriptVM, CLASS_HUMAN_PASSIVE, "Used by scientists." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_HUMAN_MILITARY, "Used by HECU marines, etc." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_MILITARY, "Used by alien grunts, alien slaves/vortigaunts, etc." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_MONSTER, "Used by zombies, houndeyes, barnacles, and other misc. monsters." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_PREY, "Used by headcrabs, etc." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_PREDATOR, "Used by bullsquids, etc." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_INSECT, "Used by cockroaches." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_PLAYER_ALLY, "Used by security guards/Barneys." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_PLAYER_BIOWEAPON, "Used by a player's hivehand hornets." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_BIOWEAPON, "Used by an alien grunt's hivehand hornets." ); + +#else + + ScriptRegisterConstant( g_pScriptVM, CLASS_PLAYER_ALLY, "Used by player allies." ); + +#endif + + ScriptRegisterConstant( g_pScriptVM, NUM_AI_CLASSES, "Number of AI classes." ); + + // + // Misc. AI + // + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_INVALID, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_NONE, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_IDLE, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_ALERT, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_COMBAT, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_SCRIPT, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_PLAYDEAD, "NPC state type used in GetNPCState(), etc." ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_PRONE, "When in clutches of barnacle (NPC state type used in GetNPCState(), etc.)" ); + ScriptRegisterConstant( g_pScriptVM, NPC_STATE_DEAD, "NPC state type used in GetNPCState(), etc." ); + + ScriptRegisterConstant( g_pScriptVM, AISS_AWAKE, "NPC is awake. (NPC sleep state used in Get/SetSleepState())" ); + ScriptRegisterConstant( g_pScriptVM, AISS_WAITING_FOR_THREAT, "NPC is asleep and will awaken upon seeing an enemy. (NPC sleep state used in Get/SetSleepState())" ); + ScriptRegisterConstant( g_pScriptVM, AISS_WAITING_FOR_PVS, "NPC is asleep and will awaken upon entering a player's PVS. (NPC sleep state used in Get/SetSleepState())" ); + ScriptRegisterConstant( g_pScriptVM, AISS_WAITING_FOR_INPUT, "NPC is asleep and will only awaken upon receiving the Wake input. (NPC sleep state used in Get/SetSleepState())" ); + //ScriptRegisterConstant( g_pScriptVM, AISS_AUTO_PVS, "" ); + //ScriptRegisterConstant( g_pScriptVM, AISS_AUTO_PVS_AFTER_PVS, "" ); + ScriptRegisterConstant( g_pScriptVM, AI_SLEEP_FLAGS_NONE, "No sleep flags. (NPC sleep flag used in Add/Remove/HasSleepFlags())" ); + ScriptRegisterConstant( g_pScriptVM, AI_SLEEP_FLAG_AUTO_PVS, "Indicates a NPC will sleep upon exiting PVS. (NPC sleep flag used in Add/Remove/HasSleepFlags())" ); + ScriptRegisterConstant( g_pScriptVM, AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS, "Indicates a NPC will sleep upon exiting PVS after entering PVS for the first time(?????) (NPC sleep flag used in Add/Remove/HasSleepFlags())" ); + + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_PLAYING, "SCRIPT_PLAYING", "Playing the action animation." ); + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_WAIT, "SCRIPT_WAIT", "Waiting on everyone in the script to be ready. Plays the pre idle animation if there is one." ); + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_POST_IDLE, "SCRIPT_POST_IDLE", "Playing the post idle animation after playing the action animation." ); + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_CLEANUP, "SCRIPT_CLEANUP", "Cancelling the script / cleaning up." ); + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_WALK_TO_MARK, "SCRIPT_WALK_TO_MARK", "Walking to the scripted sequence position." ); + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_RUN_TO_MARK, "SCRIPT_RUN_TO_MARK", "Running to the scripted sequence position." ); + ScriptRegisterConstantNamed( g_pScriptVM, CAI_BaseNPC::SCRIPT_PLAYING, "SCRIPT_PLAYING", "Moving to the scripted sequence position while playing a custom movement animation." ); +#endif + + // + // Misc. General + // + ScriptRegisterConstant( g_pScriptVM, DAMAGE_NO, "Don't take damage (Use with GetTakeDamage/SetTakeDamage)" ); + ScriptRegisterConstant( g_pScriptVM, DAMAGE_EVENTS_ONLY, "Call damage functions, but don't modify health (Use with GetTakeDamage/SetTakeDamage)" ); + ScriptRegisterConstant( g_pScriptVM, DAMAGE_YES, "Allow damage to be taken (Use with GetTakeDamage/SetTakeDamage)" ); + ScriptRegisterConstant( g_pScriptVM, DAMAGE_AIM, "(Use with GetTakeDamage/SetTakeDamage)" ); + +#ifdef GAME_DLL + ScriptRegisterConstant( g_pScriptVM, GLOBAL_OFF, "Global state used by the Globals singleton." ); + ScriptRegisterConstant( g_pScriptVM, GLOBAL_ON, "Global state used by the Globals singleton." ); + ScriptRegisterConstant( g_pScriptVM, GLOBAL_DEAD, "Global state used by the Globals singleton." ); +#endif + + RegisterWeaponScriptConstants(); +} diff --git a/mp/src/game/shared/mapbase/vscript_consts_weapons.cpp b/mp/src/game/shared/mapbase/vscript_consts_weapons.cpp new file mode 100644 index 00000000..ad63efb7 --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_consts_weapons.cpp @@ -0,0 +1,98 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript constants and enums shared between the server and client. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "basecombatweapon_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( WeaponSound, "Weapon sounds." ) + + DEFINE_ENUMCONST( EMPTY, "" ) + DEFINE_ENUMCONST( SINGLE, "" ) + DEFINE_ENUMCONST( SINGLE_NPC, "" ) + DEFINE_ENUMCONST( WPN_DOUBLE, "" ) + DEFINE_ENUMCONST( DOUBLE_NPC, "" ) + DEFINE_ENUMCONST( BURST, "" ) + DEFINE_ENUMCONST( RELOAD, "" ) + DEFINE_ENUMCONST( RELOAD_NPC, "" ) + DEFINE_ENUMCONST( MELEE_MISS, "" ) + DEFINE_ENUMCONST( MELEE_HIT, "" ) + DEFINE_ENUMCONST( MELEE_HIT_WORLD, "" ) + DEFINE_ENUMCONST( SPECIAL1, "" ) + DEFINE_ENUMCONST( SPECIAL2, "" ) + DEFINE_ENUMCONST( SPECIAL3, "" ) + DEFINE_ENUMCONST( TAUNT, "" ) + DEFINE_ENUMCONST( DEPLOY, "" ) + + DEFINE_ENUMCONST( NUM_SHOOT_SOUND_TYPES, "" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +void RegisterWeaponScriptConstants() +{ + // + // Weapon classify + // + ScriptRegisterConstant( g_pScriptVM, WEPCLASS_INVALID, "Invalid weapon class." ); + ScriptRegisterConstant( g_pScriptVM, WEPCLASS_HANDGUN, "Weapon class for pistols, revolvers, etc." ); + ScriptRegisterConstant( g_pScriptVM, WEPCLASS_RIFLE, "Weapon class for (assault) rifles, SMGs, etc." ); + ScriptRegisterConstant( g_pScriptVM, WEPCLASS_SHOTGUN, "Weapon class for shotguns." ); + ScriptRegisterConstant( g_pScriptVM, WEPCLASS_HEAVY, "Weapon class for RPGs, etc." ); + + ScriptRegisterConstant( g_pScriptVM, WEPCLASS_MELEE, "Weapon class for melee weapons." ); + + // + // Vector cones + // + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_PRECALCULATED, "This is just a zero vector, but it adds some context indicating that the person writing the code is not allowing " + "FireBullets() to modify the direction of the shot because the shot direction " + "being passed into the function has already been modified by another piece of " + "code and should be fired as specified." ); + + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_1DEGREES, "1-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_2DEGREES, "2-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_3DEGREES, "3-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_4DEGREES, "4-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_5DEGREES, "5-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_6DEGREES, "6-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_7DEGREES, "7-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_8DEGREES, "8-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_9DEGREES, "9-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_10DEGREES, "10-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_15DEGREES, "15-degree weapon vector cone." ); + ScriptRegisterConstantFromTemp( g_pScriptVM, VECTOR_CONE_20DEGREES, "20-degree weapon vector cone." ); + + // + // Weapon proficiency + // + ScriptRegisterConstant( g_pScriptVM, WEAPON_PROFICIENCY_INVALID, "Invalid weapon proficiency." ); + ScriptRegisterConstant( g_pScriptVM, WEAPON_PROFICIENCY_POOR, "Poor weapon proficiency. Causes low accuracy." ); + ScriptRegisterConstant( g_pScriptVM, WEAPON_PROFICIENCY_AVERAGE, "Average weapon proficiency. Causes average accuracy." ); + ScriptRegisterConstant( g_pScriptVM, WEAPON_PROFICIENCY_GOOD, "Good weapon proficiency. Causes good accuracy." ); + ScriptRegisterConstant( g_pScriptVM, WEAPON_PROFICIENCY_VERY_GOOD, "Very good weapon proficiency. Causes very good accuracy." ); + ScriptRegisterConstant( g_pScriptVM, WEAPON_PROFICIENCY_PERFECT, "Perfect weapon proficiency. Causes perfect accuracy." ); + + // + // Autoaim + // + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_2DEGREES, "2-degree autoaim cone." ); + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_5DEGREES, "5-degree autoaim cone." ); + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_8DEGREES, "8-degree autoaim cone." ); + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_10DEGREES, "10-degree autoaim cone." ); + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_20DEGREES, "20-degree autoaim cone." ); + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_SCALE_DEFAULT, "Indicates default auto aim scale." ); + ScriptRegisterConstant( g_pScriptVM, AUTOAIM_SCALE_DIRECT_ONLY, "Indicates auto aim should not be used except for direct hits." ); +} diff --git a/mp/src/game/shared/mapbase/vscript_funcs_hl2.cpp b/mp/src/game/shared/mapbase/vscript_funcs_hl2.cpp new file mode 100644 index 00000000..189f8883 --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_funcs_hl2.cpp @@ -0,0 +1,63 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript functions for Half-Life 2. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "hl2_gamerules.h" +#ifndef CLIENT_DLL +#include "eventqueue.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#ifndef CLIENT_DLL +extern CBaseEntity *CreatePlayerLoadSave( Vector vOrigin, float flDuration, float flHoldTime, float flLoadTime ); + +HSCRIPT ScriptGameOver( const char *pszMessage, float flDelay, float flFadeTime, float flLoadTime, int r, int g, int b ) +{ + CBaseEntity *pPlayer = AI_GetSinglePlayer(); + if (pPlayer) + { + UTIL_ShowMessage( pszMessage, ToBasePlayer( pPlayer ) ); + ToBasePlayer( pPlayer )->NotifySinglePlayerGameEnding(); + } + else + { + // TODO: How should MP handle this? + return NULL; + } + + CBaseEntity *pReload = CreatePlayerLoadSave( vec3_origin, flFadeTime, flLoadTime + 1.0f, flLoadTime ); + if (pReload) + { + pReload->SetRenderColor( r, g, b, 255 ); + g_EventQueue.AddEvent( pReload, "Reload", flDelay, pReload, pReload ); + } + + return ToHScript( pReload ); +} + +bool ScriptMegaPhyscannonActive() +{ + return HL2GameRules()->MegaPhyscannonActive(); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHalfLife2::RegisterScriptFunctions( void ) +{ + BaseClass::RegisterScriptFunctions(); + +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGameOver, "GameOver", "Ends the game and reloads the last save." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMegaPhyscannonActive, "MegaPhyscannonActive", "Checks if supercharged gravity gun mode is enabled." ); +#endif +} diff --git a/mp/src/game/shared/mapbase/vscript_funcs_math.cpp b/mp/src/game/shared/mapbase/vscript_funcs_math.cpp new file mode 100644 index 00000000..cc8714aa --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_funcs_math.cpp @@ -0,0 +1,308 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Shared VScript math functions. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//============================================================================= +// +// matrix3x4_t +// +//============================================================================= + +// +// Exposes matrix3x4_t to VScript +// +class CScriptMatrix3x4 +{ +public: + CScriptMatrix3x4() + { + matrix = new matrix3x4_t(); + m_bFree = true; + } + + ~CScriptMatrix3x4() + { + if (m_bFree == true) + delete matrix; + } + + CScriptMatrix3x4( matrix3x4_t &inmatrix ) { matrix = &inmatrix; } + + matrix3x4_t *GetMatrix() { return matrix; } + void SetMatrix( matrix3x4_t &inmatrix ) { matrix = &inmatrix; } + + void Init( const Vector& xAxis, const Vector& yAxis, const Vector& zAxis, const Vector &vecOrigin ) + { + matrix->Init( xAxis, yAxis, zAxis, vecOrigin ); + } + +private: + matrix3x4_t *matrix; + bool m_bFree = false; +}; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMatrix3x4, "matrix3x4_t", "A 3x4 matrix transform." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC( Init, "Creates a matrix where the X axis = forward, the Y axis = left, and the Z axis = up." ) + +END_SCRIPTDESC(); + +matrix3x4_t *ToMatrix3x4( HSCRIPT hMat ) { return HScriptToClass( hMat )->GetMatrix(); } + +HSCRIPT ScriptCreateMatrixInstance( matrix3x4_t &matrix ) +{ + CScriptMatrix3x4 *smatrix = new CScriptMatrix3x4( matrix ); + + return g_pScriptVM->RegisterInstance( smatrix ); +} + +void ScriptFreeMatrixInstance( HSCRIPT hMat ) +{ + CScriptMatrix3x4 *smatrix = HScriptToClass( hMat ); + if (smatrix) + { + g_pScriptVM->RemoveInstance( hMat ); + delete smatrix; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptConcatTransforms( HSCRIPT hMat1, HSCRIPT hMat2, HSCRIPT hOut ) +{ + if (!hMat1 || !hMat2 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + ConcatTransforms( *pMat1, *pMat2, *pOut ); +} + +void ScriptMatrixCopy( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixCopy( *pMat1, *pOut ); +} + +void ScriptMatrixInvert( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixInvert( *pMat1, *pOut ); +} + +void ScriptMatricesAreEqual( HSCRIPT hMat1, HSCRIPT hMat2 ) +{ + if (!hMat1 || !hMat2) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + + MatricesAreEqual( *pMat1, *pMat2 ); +} + +const Vector& ScriptMatrixGetColumn( HSCRIPT hMat1, int column ) +{ + static Vector outvec; + outvec.Zero(); + if (!hMat1) + return outvec; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixGetColumn( *pMat1, column, outvec ); + return outvec; +} + +void ScriptMatrixSetColumn( const Vector& vecset, int column, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + static Vector outvec; + MatrixSetColumn( vecset, column, *pMat1 ); +} + +void ScriptMatrixAngles( HSCRIPT hMat1, const QAngle& angset, const Vector& vecset ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixAngles( *pMat1, *const_cast(&angset), *const_cast(&vecset) ); +} + +void ScriptAngleMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleMatrix( angset, vecset, *pMat1 ); +} + +void ScriptAngleIMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleIMatrix( angset, vecset, *pMat1 ); +} + +void ScriptSetIdentityMatrix( HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetIdentityMatrix( *pMat1 ); +} + +void ScriptSetScaleMatrix( float x, float y, float z, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetScaleMatrix( x, y, z, *pMat1 ); +} + +//============================================================================= +// +// Misc. Vector/QAngle functions +// +//============================================================================= + +const Vector& ScriptAngleVectors( const QAngle &angles ) +{ + static Vector forward; + AngleVectors( angles, &forward ); + return forward; +} + +const QAngle& ScriptVectorAngles( const Vector &forward ) +{ + static QAngle angles; + VectorAngles( forward, angles ); + return angles; +} + +const Vector& ScriptCalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point ) +{ + static Vector outvec; + CalcClosestPointOnAABB( mins, maxs, point, outvec ); + return outvec; +} + +const Vector& ScriptCalcClosestPointOnLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLine( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLine( point, vLineA, vLineB ); +} + +const Vector& ScriptCalcClosestPointOnLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLineSegment( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLineSegment( point, vLineA, vLineB ); +} + +#ifndef CLIENT_DLL +const Vector& ScriptPredictedPosition( HSCRIPT hTarget, float flTimeDelta ) +{ + static Vector predicted; + UTIL_PredictedPosition( ToEnt(hTarget), flTimeDelta, &predicted ); + return predicted; +} +#endif + +void RegisterMathScriptFunctions() +{ + ScriptRegisterFunction( g_pScriptVM, RandomFloat, "Generate a random floating point number within a range, inclusive." ); + ScriptRegisterFunction( g_pScriptVM, RandomInt, "Generate a random integer within a range, inclusive." ); + + ScriptRegisterFunction( g_pScriptVM, Approach, "Returns a value which approaches the target value from the input value with the specified speed." ); + ScriptRegisterFunction( g_pScriptVM, ApproachAngle, "Returns an angle which approaches the target angle from the input angle with the specified speed." ); + ScriptRegisterFunction( g_pScriptVM, AngleDiff, "Returns the degrees difference between two yaw angles." ); + ScriptRegisterFunction( g_pScriptVM, AngleDistance, "Returns the distance between two angles." ); + ScriptRegisterFunction( g_pScriptVM, AngleNormalize, "Clamps an angle to be in between -360 and 360." ); + ScriptRegisterFunction( g_pScriptVM, AngleNormalizePositive, "Clamps an angle to be in between 0 and 360." ); + ScriptRegisterFunction( g_pScriptVM, AnglesAreEqual, "Checks if two angles are equal based on a given tolerance value." ); + + // + // matrix3x4_t + // + g_pScriptVM->RegisterClass( GetScriptDescForClass( CScriptMatrix3x4 ) ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptFreeMatrixInstance, "FreeMatrixInstance", "Frees an allocated matrix instance." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptConcatTransforms, "ConcatTransforms", "Concatenates two transformation matrices into another matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixCopy, "MatrixCopy", "Copies a matrix to another matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixInvert, "MatrixInvert", "Inverts a matrix and copies the result to another matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatricesAreEqual, "MatricesAreEqual", "Checks if two matrices are equal." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixGetColumn, "MatrixGetColumn", "Gets the column of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixSetColumn, "MatrixSetColumn", "Sets the column of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixAngles, "MatrixAngles", "Gets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleMatrix, "AngleMatrix", "Sets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleIMatrix, "AngleIMatrix", "Sets the inverted angles and position of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetIdentityMatrix, "SetIdentityMatrix", "Turns a matrix into an identity matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetScaleMatrix, "SetScaleMatrix", "Scales a matrix." ); + + // + // Misc. Vector/QAngle functions + // + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleVectors, "AngleVectors", "Turns an angle into a direction vector." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorAngles, "VectorAngles", "Turns a direction vector into an angle." ); + + ScriptRegisterFunction( g_pScriptVM, CalcSqrDistanceToAABB, "Returns the squared distance to a bounding box." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnAABB, "CalcClosestPointOnAABB", "Returns the closest point on a bounding box." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcDistanceToLine, "CalcDistanceToLine", "Returns the distance to a line." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnLine, "CalcClosestPointOnLine", "Returns the closest point on a line." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcDistanceToLineSegment, "CalcDistanceToLineSegment", "Returns the distance to a line segment." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnLineSegment, "CalcClosestPointOnLineSegment", "Returns the closest point on a line segment." ); + +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPredictedPosition, "PredictedPosition", "Predicts what an entity's position will be in a given amount of time." ); +#endif +} diff --git a/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp new file mode 100644 index 00000000..c0668e8f --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -0,0 +1,861 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: This file contains general shared VScript bindings which Mapbase adds onto +// what was ported from Alien Swarm instead of cluttering the existing files. +// +// This includes various functions, classes, etc. which were either created from +// scratch or were based on/inspired by things documented in APIs from L4D2 or even +// Source 2 games like Dota 2 or Half-Life: Alyx. +// +// Other VScript bindings can be found in files like vscript_singletons.cpp and +// things not exclusive to the game DLLs are embedded/recreated in the library itself +// via vscript_bindings_base.cpp. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "matchers.h" +#include "takedamageinfo.h" + +#ifndef CLIENT_DLL +#include "globalstate.h" +#include "vscript_server.h" +#include "soundent.h" +#endif // !CLIENT_DLL + +#include "con_nprint.h" +#include "particle_parse.h" + +#include "vscript_funcs_shared.h" +#include "vscript_singletons.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IScriptManager *scriptmanager; + +#ifndef CLIENT_DLL +void EmitSoundOn( const char *pszSound, HSCRIPT hEnt ) +{ + CBaseEntity *pEnt = ToEnt( hEnt ); + if (!pEnt) + return; + + pEnt->EmitSound( pszSound ); +} + +void EmitSoundOnClient( const char *pszSound, HSCRIPT hEnt, HSCRIPT hPlayer ) +{ + CBaseEntity *pEnt = ToEnt( hEnt ); + CBasePlayer *pPlayer = ToBasePlayer( ToEnt( hPlayer ) ); + if (!pEnt || !pPlayer) + return; + + CSingleUserRecipientFilter filter( pPlayer ); + + EmitSound_t params; + params.m_pSoundName = pszSound; + params.m_flSoundTime = 0.0f; + params.m_pflSoundDuration = NULL; + params.m_bWarnOnDirectWaveReference = true; + + pEnt->EmitSound( filter, pEnt->entindex(), params ); +} + +void AddThinkToEnt( HSCRIPT entity, const char *pszFuncName ) +{ + CBaseEntity *pEntity = ToEnt( entity ); + if (!pEntity) + return; + + pEntity->ScriptSetThinkFunction(pszFuncName, TICK_INTERVAL); +} + +void ParseScriptTableKeyValues( CBaseEntity *pEntity, HSCRIPT hKV ) +{ + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( hKV, nIterator, &varKey, &varValue )) != -1) + { + switch (varValue.m_type) + { + case FIELD_CSTRING: pEntity->KeyValue( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_INTEGER: pEntity->KeyValueFromInt( varKey.m_pszString, varValue.m_int ); break; + case FIELD_FLOAT: pEntity->KeyValue( varKey.m_pszString, varValue.m_float ); break; + case FIELD_VECTOR: pEntity->KeyValue( varKey.m_pszString, *varValue.m_pVector ); break; + case FIELD_HSCRIPT: + { + if ( varValue.m_hScript ) + { + // Entity + if (ToEnt( varValue.m_hScript )) + { + pEntity->KeyValue( varKey.m_pszString, STRING( ToEnt( varValue.m_hScript )->GetEntityName() ) ); + } + + // Color + else if (Color *color = HScriptToClass( varValue.m_hScript )) + { + char szTemp[64]; + Q_snprintf( szTemp, sizeof( szTemp ), "%i %i %i %i", color->r(), color->g(), color->b(), color->a() ); + pEntity->KeyValue( varKey.m_pszString, szTemp ); + } + } + break; + } + } + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } +} + +void PrecacheEntityFromTable( const char *pszClassname, HSCRIPT hKV ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "VScript error: A script attempted to create an entity mid-game. Due to the server's settings, entity creation from scripts is only allowed during map init.\n" ); + return; + } + + // This is similar to UTIL_PrecacheOther(), but we can't check if we can only precache it once. + // Probably for the best anyway, as similar classes can still have different precachable properties. + CBaseEntity *pEntity = CreateEntityByName( pszClassname ); + if (!pEntity) + { + Assert( !"PrecacheEntityFromTable: only works for CBaseEntities" ); + return; + } + + ParseScriptTableKeyValues( pEntity, hKV ); + + pEntity->Precache(); + + UTIL_RemoveImmediate( pEntity ); +} + +HSCRIPT SpawnEntityFromTable( const char *pszClassname, HSCRIPT hKV ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "VScript error: A script attempted to create an entity mid-game. Due to the server's settings, entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + CBaseEntity *pEntity = CreateEntityByName( pszClassname ); + if ( !pEntity ) + { + Assert( !"SpawnEntityFromTable: only works for CBaseEntities" ); + return NULL; + } + + gEntList.NotifyCreateEntity( pEntity ); + + ParseScriptTableKeyValues( pEntity, hKV ); + + DispatchSpawn( pEntity ); + pEntity->Activate(); + + return ToHScript( pEntity ); +} +#endif + +HSCRIPT EntIndexToHScript( int index ) +{ +#ifdef GAME_DLL + edict_t *e = INDEXENT(index); + if ( e && !e->IsFree() ) + { + return ToHScript( GetContainingEntity( e ) ); + } +#else // CLIENT_DLL + if ( index < NUM_ENT_ENTRIES ) + { + return ToHScript( CBaseEntity::Instance( index ) ); + } +#endif + return NULL; +} + +//----------------------------------------------------------------------------- +// Mapbase-specific functions start here +//----------------------------------------------------------------------------- + +#ifndef CLIENT_DLL +void SaveEntityKVToTable( HSCRIPT hEnt, HSCRIPT hTable ) +{ + CBaseEntity *pEnt = ToEnt( hEnt ); + if (pEnt == NULL) + return; + + variant_t var; // For Set() + ScriptVariant_t varScript, varTable = hTable; + + // loop through the data description list, reading each data desc block + for ( datamap_t *dmap = pEnt->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap ) + { + // search through all the readable fields in the data description, looking for a match + for ( int i = 0; i < dmap->dataNumFields; i++ ) + { + if ( dmap->dataDesc[i].flags & (FTYPEDESC_KEY) ) + { + var.Set( dmap->dataDesc[i].fieldType, ((char*)pEnt) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ] ); + var.SetScriptVariant( varScript ); + g_pScriptVM->SetValue( varTable, dmap->dataDesc[i].externalName, varScript ); + } + } + } +} + +HSCRIPT SpawnEntityFromKeyValues( const char *pszClassname, HSCRIPT hKV ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create an entity mid-game. Due to the server's settings, entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + CBaseEntity *pEntity = CreateEntityByName( pszClassname ); + if ( !pEntity ) + { + Assert( !"SpawnEntityFromKeyValues: only works for CBaseEntities" ); + return NULL; + } + + gEntList.NotifyCreateEntity( pEntity ); + + KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV ); + for (pKV = pKV->GetFirstSubKey(); pKV != NULL; pKV = pKV->GetNextKey()) + { + pEntity->KeyValue( pKV->GetName(), pKV->GetString() ); + } + + DispatchSpawn( pEntity ); + pEntity->Activate(); + + return ToHScript( pEntity ); +} + +void ScriptDispatchSpawn( HSCRIPT hEntity ) +{ + CBaseEntity *pEntity = ToEnt( hEntity ); + if (pEntity) + { + DispatchSpawn( pEntity ); + } +} +#endif // !CLIENT_DLL + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static HSCRIPT CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Vector &vecForce, const Vector &vecDamagePos, float flDamage, int iDamageType ) +{ + // The script is responsible for deleting this via DestroyDamageInfo(). + CTakeDamageInfo *damageInfo = new CTakeDamageInfo(ToEnt(hInflictor), ToEnt(hAttacker), flDamage, iDamageType); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( damageInfo, true ); + + damageInfo->SetDamagePosition( vecDamagePos ); + damageInfo->SetDamageForce( vecForce ); + + return hScript; +} + +static void DestroyDamageInfo( HSCRIPT hDamageInfo ) +{ + if (hDamageInfo) + { + CTakeDamageInfo *pInfo = (CTakeDamageInfo*)g_pScriptVM->GetInstanceValue( hDamageInfo, GetScriptDescForClass( CTakeDamageInfo ) ); + if (pInfo) + { + g_pScriptVM->RemoveInstance( hDamageInfo ); + delete pInfo; + } + } +} + +void ScriptCalculateExplosiveDamageForce( HSCRIPT info, const Vector &vecDir, const Vector &vecForceOrigin, float flScale ) { CalculateExplosiveDamageForce( HScriptToClass(info), vecDir, vecForceOrigin, flScale ); } +void ScriptCalculateBulletDamageForce( HSCRIPT info, int iBulletType, const Vector &vecBulletDir, const Vector &vecForceOrigin, float flScale ) { CalculateBulletDamageForce( HScriptToClass(info), iBulletType, vecBulletDir, vecForceOrigin, flScale ); } +void ScriptCalculateMeleeDamageForce( HSCRIPT info, const Vector &vecMeleeDir, const Vector &vecForceOrigin, float flScale ) { CalculateMeleeDamageForce( HScriptToClass( info ), vecMeleeDir, vecForceOrigin, flScale ); } +void ScriptGuessDamageForce( HSCRIPT info, const Vector &vecForceDir, const Vector &vecForceOrigin, float flScale ) { GuessDamageForce( HScriptToClass( info ), vecForceDir, vecForceOrigin, flScale ); } + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT_NAMED( CTraceInfoAccessor, "CGameTrace", "Handle for accessing trace_t info." ) + DEFINE_SCRIPT_CONSTRUCTOR() + + DEFINE_SCRIPTFUNC( DidHitWorld, "Returns whether the trace hit the world entity or not." ) + DEFINE_SCRIPTFUNC( DidHitNonWorldEntity, "Returns whether the trace hit something other than the world entity." ) + DEFINE_SCRIPTFUNC( GetEntityIndex, "Returns the index of whatever entity this trace hit." ) + DEFINE_SCRIPTFUNC( DidHit, "Returns whether the trace hit anything." ) + + DEFINE_SCRIPTFUNC( FractionLeftSolid, "If this trace started within a solid, this is the point in the trace's fraction at which it left that solid." ) + DEFINE_SCRIPTFUNC( HitGroup, "Returns the specific hit group this trace hit if it hit an entity." ) + DEFINE_SCRIPTFUNC( PhysicsBone, "Returns the physics bone this trace hit if it hit an entity." ) + DEFINE_SCRIPTFUNC( Entity, "Returns the entity this trace has hit." ) + DEFINE_SCRIPTFUNC( HitBox, "Returns the hitbox of the entity this trace has hit. If it hit the world entity, this returns the static prop index." ) + + DEFINE_SCRIPTFUNC( IsDispSurface, "Returns whether this trace hit a displacement." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceWalkable, "Returns whether DISPSURF_FLAG_WALKABLE is ticked on the displacement this trace hit." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceBuildable, "Returns whether DISPSURF_FLAG_BUILDABLE is ticked on the displacement this trace hit." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceProp1, "Returns whether DISPSURF_FLAG_SURFPROP1 is ticked on the displacement this trace hit." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceProp2, "Returns whether DISPSURF_FLAG_SURFPROP2 is ticked on the displacement this trace hit." ) + + DEFINE_SCRIPTFUNC( StartPos, "Gets the trace's start position." ) + DEFINE_SCRIPTFUNC( EndPos, "Gets the trace's end position." ) + + DEFINE_SCRIPTFUNC( Fraction, "Gets the fraction of the trace completed. For example, if the trace stopped exactly halfway to the end position, this would be 0.5." ) + DEFINE_SCRIPTFUNC( Contents, "Gets the contents of the surface the trace has hit." ) + DEFINE_SCRIPTFUNC( DispFlags, "Gets the displacement flags of the surface the trace has hit." ) + + DEFINE_SCRIPTFUNC( AllSolid, "Returns whether the trace is completely within a solid." ) + DEFINE_SCRIPTFUNC( StartSolid, "Returns whether the trace started within a solid." ) + + DEFINE_SCRIPTFUNC( Surface, "Returns the trace's surface." ) + DEFINE_SCRIPTFUNC( Plane, "Returns the trace's plane." ) + + DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) +END_SCRIPTDESC(); + +BEGIN_SCRIPTDESC_ROOT_NAMED( surfacedata_t, "surfacedata_t", "Handle for accessing surface data." ) + DEFINE_SCRIPTFUNC( GetFriction, "The surface's friction." ) + DEFINE_SCRIPTFUNC( GetThickness, "The surface's thickness." ) + + DEFINE_SCRIPTFUNC( GetJumpFactor, "The surface's jump factor." ) + DEFINE_SCRIPTFUNC( GetMaterialChar, "The surface's material character." ) + + DEFINE_SCRIPTFUNC( GetSoundStepLeft, "The surface's left step sound." ) + DEFINE_SCRIPTFUNC( GetSoundStepRight, "The surface's right step sound." ) + DEFINE_SCRIPTFUNC( GetSoundImpactSoft, "The surface's soft impact sound." ) + DEFINE_SCRIPTFUNC( GetSoundImpactHard, "The surface's hard impact sound." ) + DEFINE_SCRIPTFUNC( GetSoundScrapeSmooth, "The surface's smooth scrape sound." ) + DEFINE_SCRIPTFUNC( GetSoundScrapeRough, "The surface's rough scrape sound." ) + DEFINE_SCRIPTFUNC( GetSoundBulletImpact, "The surface's bullet impact sound." ) + DEFINE_SCRIPTFUNC( GetSoundRolling, "The surface's rolling sound." ) + DEFINE_SCRIPTFUNC( GetSoundBreak, "The surface's break sound." ) + DEFINE_SCRIPTFUNC( GetSoundStrain, "The surface's strain sound." ) +END_SCRIPTDESC(); + +const char* surfacedata_t::GetSoundStepLeft() { return physprops->GetString( sounds.stepleft ); } +const char* surfacedata_t::GetSoundStepRight() { return physprops->GetString( sounds.stepright ); } +const char* surfacedata_t::GetSoundImpactSoft() { return physprops->GetString( sounds.impactSoft ); } +const char* surfacedata_t::GetSoundImpactHard() { return physprops->GetString( sounds.impactHard ); } +const char* surfacedata_t::GetSoundScrapeSmooth() { return physprops->GetString( sounds.scrapeSmooth ); } +const char* surfacedata_t::GetSoundScrapeRough() { return physprops->GetString( sounds.scrapeRough ); } +const char* surfacedata_t::GetSoundBulletImpact() { return physprops->GetString( sounds.bulletImpact ); } +const char* surfacedata_t::GetSoundRolling() { return physprops->GetString( sounds.rolling ); } +const char* surfacedata_t::GetSoundBreak() { return physprops->GetString( sounds.breakSound ); } +const char* surfacedata_t::GetSoundStrain() { return physprops->GetString( sounds.strainSound ); } + +BEGIN_SCRIPTDESC_ROOT_NAMED( CSurfaceScriptAccessor, "csurface_t", "Handle for accessing csurface_t info." ) + DEFINE_SCRIPTFUNC( Name, "The surface's name." ) + DEFINE_SCRIPTFUNC( SurfaceProps, "The surface's properties." ) + + DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) +END_SCRIPTDESC(); + +CPlaneTInstanceHelper g_PlaneTInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT( cplane_t, "Handle for accessing cplane_t info." ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_PlaneTInstanceHelper ) +END_SCRIPTDESC(); + +static HSCRIPT ScriptTraceLineComplex( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore, int iMask, int iCollisionGroup ) +{ + // The script is responsible for deleting this via Destroy(). + CTraceInfoAccessor *traceInfo = new CTraceInfoAccessor(); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo, true ); + + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceLine( vecStart, vecEnd, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + + // The trace's destruction should destroy this automatically + CSurfaceScriptAccessor *surfaceInfo = new CSurfaceScriptAccessor( traceInfo->GetTrace().surface ); + HSCRIPT hSurface = g_pScriptVM->RegisterInstance( surfaceInfo ); + traceInfo->SetSurface( hSurface ); + + HSCRIPT hPlane = g_pScriptVM->RegisterInstance( &(traceInfo->GetTrace().plane) ); + traceInfo->SetPlane( hPlane ); + + return hScript; +} + +static HSCRIPT ScriptTraceHullComplex( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin, const Vector &hullMax, + HSCRIPT entIgnore, int iMask, int iCollisionGroup ) +{ + // The script is responsible for deleting this via Destroy(). + CTraceInfoAccessor *traceInfo = new CTraceInfoAccessor(); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo, true ); + + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + + // The trace's destruction should destroy this automatically + CSurfaceScriptAccessor *surfaceInfo = new CSurfaceScriptAccessor( traceInfo->GetTrace().surface ); + HSCRIPT hSurface = g_pScriptVM->RegisterInstance( surfaceInfo ); + traceInfo->SetSurface( hSurface ); + + HSCRIPT hPlane = g_pScriptVM->RegisterInstance( &(traceInfo->GetTrace().plane) ); + traceInfo->SetPlane( hPlane ); + + return hScript; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT( FireBulletsInfo_t, "Handle for accessing FireBulletsInfo_t info." ) + DEFINE_SCRIPT_CONSTRUCTOR() + + DEFINE_SCRIPTFUNC( GetShots, "Gets the number of shots which should be fired." ) + DEFINE_SCRIPTFUNC( SetShots, "Sets the number of shots which should be fired." ) + + DEFINE_SCRIPTFUNC( GetSource, "Gets the source of the bullets." ) + DEFINE_SCRIPTFUNC( SetSource, "Sets the source of the bullets." ) + DEFINE_SCRIPTFUNC( GetDirShooting, "Gets the direction of the bullets." ) + DEFINE_SCRIPTFUNC( SetDirShooting, "Sets the direction of the bullets." ) + DEFINE_SCRIPTFUNC( GetSpread, "Gets the spread of the bullets." ) + DEFINE_SCRIPTFUNC( SetSpread, "Sets the spread of the bullets." ) + + DEFINE_SCRIPTFUNC( GetDistance, "Gets the distance the bullets should travel." ) + DEFINE_SCRIPTFUNC( SetDistance, "Sets the distance the bullets should travel." ) + + DEFINE_SCRIPTFUNC( GetAmmoType, "Gets the ammo type the bullets should use." ) + DEFINE_SCRIPTFUNC( SetAmmoType, "Sets the ammo type the bullets should use." ) + + DEFINE_SCRIPTFUNC( GetTracerFreq, "Gets the tracer frequency." ) + DEFINE_SCRIPTFUNC( SetTracerFreq, "Sets the tracer frequency." ) + + DEFINE_SCRIPTFUNC( GetDamage, "Gets the damage the bullets should deal. 0 = use ammo type" ) + DEFINE_SCRIPTFUNC( SetDamage, "Sets the damage the bullets should deal. 0 = use ammo type" ) + DEFINE_SCRIPTFUNC( GetPlayerDamage, "Gets the damage the bullets should deal when hitting the player. 0 = use regular damage" ) + DEFINE_SCRIPTFUNC( SetPlayerDamage, "Sets the damage the bullets should deal when hitting the player. 0 = use regular damage" ) + + DEFINE_SCRIPTFUNC( GetFlags, "Gets the flags the bullets should use." ) + DEFINE_SCRIPTFUNC( SetFlags, "Sets the flags the bullets should use." ) + + DEFINE_SCRIPTFUNC( GetDamageForceScale, "Gets the scale of the damage force applied by the bullets." ) + DEFINE_SCRIPTFUNC( SetDamageForceScale, "Sets the scale of the damage force applied by the bullets." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttacker, "GetAttacker", "Gets the entity considered to be the one who fired the bullets." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAttacker, "SetAttacker", "Sets the entity considered to be the one who fired the bullets." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAdditionalIgnoreEnt, "GetAdditionalIgnoreEnt", "Gets the optional entity which the bullets should ignore." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAdditionalIgnoreEnt, "SetAdditionalIgnoreEnt", "Sets the optional entity which the bullets should ignore." ) + + DEFINE_SCRIPTFUNC( GetPrimaryAttack, "Gets whether the bullets came from a primary attack." ) + DEFINE_SCRIPTFUNC( SetPrimaryAttack, "Sets whether the bullets came from a primary attack." ) + + //DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT FireBulletsInfo_t::ScriptGetAttacker() +{ + return ToHScript( m_pAttacker ); +} + +void FireBulletsInfo_t::ScriptSetAttacker( HSCRIPT value ) +{ + m_pAttacker = ToEnt( value ); +} + +HSCRIPT FireBulletsInfo_t::ScriptGetAdditionalIgnoreEnt() +{ + return ToHScript( m_pAdditionalIgnoreEnt ); +} + +void FireBulletsInfo_t::ScriptSetAdditionalIgnoreEnt( HSCRIPT value ) +{ + m_pAdditionalIgnoreEnt = ToEnt( value ); +} + +static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Vector &vecDirShooting, + const Vector &vecSpread, float iDamage, HSCRIPT pAttacker ) +{ + // The script is responsible for deleting this via DestroyFireBulletsInfo(). + FireBulletsInfo_t *info = new FireBulletsInfo_t(); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( info, true ); + + info->SetShots( cShots ); + info->SetSource( vecSrc ); + info->SetDirShooting( vecDirShooting ); + info->SetSpread( vecSpread ); + info->SetDamage( iDamage ); + info->ScriptSetAttacker( pAttacker ); + + return hScript; +} + +static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo ) +{ + g_pScriptVM->RemoveInstance( hBulletsInfo ); +} + +// For the function in baseentity.cpp +FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ) +{ + return HScriptToClass( hBulletsInfo ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) + DEFINE_SCRIPTFUNC( GetCommandNumber, "For matching server and client commands for debugging." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetTickCount, "GetTickCount", "The tick the client created this command." ) + + DEFINE_SCRIPTFUNC( GetViewAngles, "Player instantaneous view angles." ) + DEFINE_SCRIPTFUNC( SetViewAngles, "Sets player instantaneous view angles." ) + + DEFINE_SCRIPTFUNC( GetForwardMove, "Forward velocity." ) + DEFINE_SCRIPTFUNC( SetForwardMove, "Sets forward velocity." ) + DEFINE_SCRIPTFUNC( GetSideMove, "Side velocity." ) + DEFINE_SCRIPTFUNC( SetSideMove, "Sets side velocity." ) + DEFINE_SCRIPTFUNC( GetUpMove, "Up velocity." ) + DEFINE_SCRIPTFUNC( SetUpMove, "Sets up velocity." ) + + DEFINE_SCRIPTFUNC( GetButtons, "Attack button states." ) + DEFINE_SCRIPTFUNC( SetButtons, "Sets attack button states." ) + DEFINE_SCRIPTFUNC( GetImpulse, "Impulse command issued." ) + DEFINE_SCRIPTFUNC( SetImpulse, "Sets impulse command issued." ) + + DEFINE_SCRIPTFUNC( GetWeaponSelect, "Current weapon id." ) + DEFINE_SCRIPTFUNC( SetWeaponSelect, "Sets current weapon id." ) + DEFINE_SCRIPTFUNC( GetWeaponSubtype, "Current weapon subtype id." ) + DEFINE_SCRIPTFUNC( SetWeaponSubtype, "Sets current weapon subtype id." ) + + DEFINE_SCRIPTFUNC( GetRandomSeed, "For shared random functions." ) + + DEFINE_SCRIPTFUNC( GetMouseX, "Mouse accum in x from create move." ) + DEFINE_SCRIPTFUNC( SetMouseX, "Sets mouse accum in x from create move." ) + DEFINE_SCRIPTFUNC( GetMouseY, "Mouse accum in y from create move." ) + DEFINE_SCRIPTFUNC( SetMouseY, "Sets mouse accum in y from create move." ) +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT( IPhysicsObject, "VPhysics object class." ) + + DEFINE_SCRIPTFUNC( IsStatic, "" ) + DEFINE_SCRIPTFUNC( IsAsleep, "" ) + DEFINE_SCRIPTFUNC( IsTrigger, "" ) + DEFINE_SCRIPTFUNC( IsFluid, "" ) + DEFINE_SCRIPTFUNC( IsHinged, "" ) + DEFINE_SCRIPTFUNC( IsCollisionEnabled, "" ) + DEFINE_SCRIPTFUNC( IsGravityEnabled, "" ) + DEFINE_SCRIPTFUNC( IsDragEnabled, "" ) + DEFINE_SCRIPTFUNC( IsMotionEnabled, "" ) + DEFINE_SCRIPTFUNC( IsMoveable, "" ) + DEFINE_SCRIPTFUNC( IsAttachedToConstraint, "" ) + + DEFINE_SCRIPTFUNC( EnableCollisions, "" ) + DEFINE_SCRIPTFUNC( EnableGravity, "" ) + DEFINE_SCRIPTFUNC( EnableDrag, "" ) + DEFINE_SCRIPTFUNC( EnableMotion, "" ) + + DEFINE_SCRIPTFUNC( Wake, "" ) + DEFINE_SCRIPTFUNC( Sleep, "" ) + + DEFINE_SCRIPTFUNC( SetMass, "" ) + DEFINE_SCRIPTFUNC( GetMass, "" ) + DEFINE_SCRIPTFUNC( GetInvMass, "" ) + DEFINE_SCRIPTFUNC( GetInertia, "" ) + DEFINE_SCRIPTFUNC( GetInvInertia, "" ) + DEFINE_SCRIPTFUNC( SetInertia, "" ) + + DEFINE_SCRIPTFUNC( ApplyForceCenter, "" ) + DEFINE_SCRIPTFUNC( ApplyForceOffset, "" ) + DEFINE_SCRIPTFUNC( ApplyTorqueCenter, "" ) + + DEFINE_SCRIPTFUNC( GetName, "" ) + +END_SCRIPTDESC(); + +static const Vector &GetPhysVelocity( HSCRIPT hPhys ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return vec3_origin; + + static Vector vecVelocity; + pPhys->GetVelocity( &vecVelocity, NULL ); + return vecVelocity; +} + +static const Vector &GetPhysAngVelocity( HSCRIPT hPhys ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return vec3_origin; + + static Vector vecAngVelocity; + pPhys->GetVelocity( NULL, &vecAngVelocity ); + return vecAngVelocity; +} + +static void SetPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vector& vecAngVelocity ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return; + + pPhys->SetVelocity( &vecVelocity, &vecAngVelocity ); +} + +static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vector& vecAngVelocity ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return; + + pPhys->AddVelocity( &vecVelocity, &vecAngVelocity ); +} + +//============================================================================= +//============================================================================= + +static int ScriptPrecacheModel( const char *modelname ) +{ + return CBaseEntity::PrecacheModel( modelname ); +} + +static void ScriptPrecacheOther( const char *classname ) +{ + UTIL_PrecacheOther( classname ); +} + +#ifndef CLIENT_DLL +// TODO: Move this? +static void ScriptInsertSound( int iType, const Vector &vecOrigin, int iVolume, float flDuration, HSCRIPT hOwner, int soundChannelIndex, HSCRIPT hSoundTarget ) +{ + CSoundEnt::InsertSound( iType, vecOrigin, iVolume, flDuration, ToEnt(hOwner), soundChannelIndex, ToEnt(hSoundTarget) ); +} +#endif + +//============================================================================= +//============================================================================= + +static void ScriptEntitiesInBox( HSCRIPT hTable, int listMax, const Vector &hullMin, const Vector &hullMax, int iMask ) +{ + CBaseEntity *list[1024]; + int count = UTIL_EntitiesInBox( list, listMax, hullMin, hullMax, iMask ); + + for ( int i = 0; i < count; i++ ) + { + g_pScriptVM->ArrayAppend( hTable, ToHScript(list[i]) ); + } +} + +static void ScriptEntitiesAtPoint( HSCRIPT hTable, int listMax, const Vector &point, int iMask ) +{ + CBaseEntity *list[1024]; + int count = UTIL_EntitiesAtPoint( list, listMax, point, iMask ); + + for ( int i = 0; i < count; i++ ) + { + g_pScriptVM->ArrayAppend( hTable, ToHScript(list[i]) ); + } +} + +static void ScriptEntitiesInSphere( HSCRIPT hTable, int listMax, const Vector ¢er, float radius, int iMask ) +{ + CBaseEntity *list[1024]; + int count = UTIL_EntitiesInSphere( list, listMax, center, radius, iMask ); + + for ( int i = 0; i < count; i++ ) + { + g_pScriptVM->ArrayAppend( hTable, ToHScript(list[i]) ); + } +} + +//----------------------------------------------------------------------------- + +static void ScriptDecalTrace( HSCRIPT hTrace, const char *decalName ) +{ + CTraceInfoAccessor *traceInfo = HScriptToClass(hTrace); + UTIL_DecalTrace( &traceInfo->GetTrace(), decalName ); +} + +//----------------------------------------------------------------------------- +// Simple particle effect dispatch +//----------------------------------------------------------------------------- +static void ScriptDispatchParticleEffect( const char *pszParticleName, const Vector &vecOrigin, const QAngle &vecAngles, HSCRIPT hEntity ) +{ + DispatchParticleEffect( pszParticleName, vecOrigin, vecAngles, ToEnt(hEntity) ); +} + +#ifndef CLIENT_DLL +const Vector& ScriptPredictedPosition( HSCRIPT hTarget, float flTimeDelta ) +{ + static Vector predicted; + UTIL_PredictedPosition( ToEnt(hTarget), flTimeDelta, &predicted ); + return predicted; +} +#endif + +//============================================================================= +//============================================================================= + +bool ScriptMatcherMatch( const char *pszQuery, const char *szValue ) { return Matcher_Match( pszQuery, szValue ); } + +//============================================================================= +//============================================================================= + +#ifndef CLIENT_DLL +bool IsDedicatedServer() +{ + return engine->IsDedicatedServer(); +} +#endif + +bool ScriptIsServer() +{ +#ifdef GAME_DLL + return true; +#else + return false; +#endif +} + +bool ScriptIsClient() +{ +#ifdef CLIENT_DLL + return true; +#else + return false; +#endif +} + +// Notification printing on the right edge of the screen +void NPrint( int pos, const char* fmt ) +{ + engine->Con_NPrintf(pos, fmt); +} + +void NXPrint( int pos, int r, int g, int b, bool fixed, float ftime, const char* fmt ) +{ + static con_nprint_t *info = new con_nprint_t; + + info->index = pos; + info->time_to_live = ftime; + info->color[0] = r / 255.f; + info->color[1] = g / 255.f; + info->color[2] = b / 255.f; + info->fixed_width_font = fixed; + + engine->Con_NXPrintf(info, fmt); + + // delete info; +} + +//============================================================================= +//============================================================================= + +void RegisterSharedScriptFunctions() +{ + // + // Due to this being a custom integration of VScript based on the Alien Swarm SDK, we don't have access to + // some of the code normally available in games like L4D2 or Valve's original VScript DLL. + // Instead, that code is recreated here, shared between server and client. + // + +#ifndef CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, EmitSoundOn, "Play named sound on an entity." ); + ScriptRegisterFunction( g_pScriptVM, EmitSoundOnClient, "Play named sound only on the client for the specified player." ); + + ScriptRegisterFunction( g_pScriptVM, AddThinkToEnt, "This will put a think function onto an entity, or pass null to remove it. This is NOT chained, so be careful." ); + ScriptRegisterFunction( g_pScriptVM, PrecacheEntityFromTable, "Precache an entity from KeyValues in a table." ); + ScriptRegisterFunction( g_pScriptVM, SpawnEntityFromTable, "Native function for entity spawning." ); +#endif // !CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, EntIndexToHScript, "Returns the script handle for the given entity index." ); + + //----------------------------------------------------------------------------- + // Functions, etc. unique to Mapbase + //----------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + + ScriptRegisterFunction( g_pScriptVM, NPrint, "Notification print" ); + ScriptRegisterFunction( g_pScriptVM, NXPrint, "Notification print, customised" ); + +#ifndef CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, SaveEntityKVToTable, "Saves an entity's keyvalues to a table." ); + ScriptRegisterFunction( g_pScriptVM, SpawnEntityFromKeyValues, "Spawns an entity with the keyvalues in a CScriptKeyValues handle." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDispatchSpawn, "DispatchSpawn", "Spawns an unspawned entity." ); +#endif + + ScriptRegisterFunction( g_pScriptVM, CreateDamageInfo, "Creates damage info." ); + ScriptRegisterFunction( g_pScriptVM, DestroyDamageInfo, "Destroys damage info." ); + ScriptRegisterFunction( g_pScriptVM, ImpulseScale, "Returns an impulse scale required to push an object." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateExplosiveDamageForce, "CalculateExplosiveDamageForce", "Fill out a damage info handle with a damage force for an explosive." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateBulletDamageForce, "CalculateBulletDamageForce", "Fill out a damage info handle with a damage force for a bullet impact." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateMeleeDamageForce, "CalculateMeleeDamageForce", "Fill out a damage info handle with a damage force for a melee impact." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGuessDamageForce, "GuessDamageForce", "Try and guess the physics force to use." ); + + ScriptRegisterFunction( g_pScriptVM, CreateFireBulletsInfo, "Creates FireBullets info." ); + ScriptRegisterFunction( g_pScriptVM, DestroyFireBulletsInfo, "Destroys FireBullets info." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLineComplex, "TraceLineComplex", "Complex version of TraceLine which takes 2 points, an ent to ignore, a trace mask, and a collision group. Returns a handle which can access all trace info." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceHullComplex, "TraceHullComplex", "Takes 2 points, min/max hull bounds, an ent to ignore, a trace mask, and a collision group to trace to a point using a hull. Returns a handle which can access all trace info." ); + + // + // VPhysics + // + ScriptRegisterFunction( g_pScriptVM, GetPhysVelocity, "Gets physics velocity for the given VPhysics object" ); + ScriptRegisterFunction( g_pScriptVM, GetPhysAngVelocity, "Gets physics angular velocity for the given VPhysics object" ); + ScriptRegisterFunction( g_pScriptVM, SetPhysVelocity, "Sets physics velocity for the given VPhysics object" ); + ScriptRegisterFunction( g_pScriptVM, AddPhysVelocity, "Adds physics velocity for the given VPhysics object" ); + + // + // Precaching + // + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheModel, "PrecacheModel", "Precaches a model for later usage." ); + ScriptRegisterFunction( g_pScriptVM, PrecacheMaterial, "Precaches a material for later usage." ); + ScriptRegisterFunction( g_pScriptVM, PrecacheParticleSystem, "Precaches a particle system for later usage." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheOther, "PrecacheOther", "Precaches an entity class for later usage." ); + + // + // NPCs + // +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptInsertSound, "InsertAISound", "Inserts an AI sound." ); +#endif + + // + // Misc. Utility + // + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesInBox, "EntitiesInBox", "Gets all entities which are within a worldspace box. This function copies them to an array with a maximum number of elements." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesAtPoint, "EntitiesAtPoint", "Gets all entities which are intersecting a point in space. This function copies them to an array with a maximum number of elements." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesInSphere, "EntitiesInSphere", "Gets all entities which are within a sphere. This function copies them to an array with a maximum number of elements." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDecalTrace, "DecalTrace", "Creates a dynamic decal based on the given trace info. The trace information can be generated by TraceLineComplex() and the decal name must be from decals_subrect.txt." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDispatchParticleEffect, "DoDispatchParticleEffect", SCRIPT_ALIAS( "DispatchParticleEffect", "Dispatches a one-off particle system" ) ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatcherMatch, "Matcher_Match", "Compares a string to a query using Mapbase's matcher system, supporting wildcards, RS matchers, etc." ); + ScriptRegisterFunction( g_pScriptVM, Matcher_NamesMatch, "Compares a string to a query using Mapbase's matcher system using wildcards only." ); + ScriptRegisterFunction( g_pScriptVM, AppearsToBeANumber, "Checks if the given string appears to be a number." ); + +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPredictedPosition, "PredictedPosition", "Predicts what an entity's position will be in a given amount of time." ); +#endif + +#ifndef CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, IsDedicatedServer, "Is this a dedicated server?" ); +#endif + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsServer, "IsServer", "Returns true if the script is being run on the server." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsClient, "IsClient", "Returns true if the script is being run on the client." ); + + RegisterScriptSingletons(); + +#ifdef CLIENT_DLL + VScriptRunScript( "vscript_client", true ); +#else + VScriptRunScript( "vscript_server", true ); +#endif +} diff --git a/mp/src/game/shared/mapbase/vscript_funcs_shared.h b/mp/src/game/shared/mapbase/vscript_funcs_shared.h new file mode 100644 index 00000000..ad3192bf --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_funcs_shared.h @@ -0,0 +1,124 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: See vscript_funcs_shared.cpp +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_SHARED +#define VSCRIPT_FUNCS_SHARED +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Exposes csurface_t to VScript +//----------------------------------------------------------------------------- +class CSurfaceScriptAccessor +{ +public: + CSurfaceScriptAccessor( csurface_t &surf ) { m_surf = &surf; m_surfaceData = g_pScriptVM->RegisterInstance( physprops->GetSurfaceData( m_surf->surfaceProps ) ); } + ~CSurfaceScriptAccessor() { delete m_surfaceData; } + + // cplane_t stuff + const char* Name() const { return m_surf->name; } + HSCRIPT SurfaceProps() const { return m_surfaceData; } + + void Destroy() { delete this; } + +private: + csurface_t *m_surf; + HSCRIPT m_surfaceData; +}; + +//----------------------------------------------------------------------------- +// Exposes cplane_t to VScript +//----------------------------------------------------------------------------- +class CPlaneTInstanceHelper : public IScriptInstanceHelper +{ + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ) + { + cplane_t *pi = ((cplane_t *)p); + if (FStrEq(pszKey, "normal")) + variant = pi->normal; + else if (FStrEq(pszKey, "dist")) + variant = pi->dist; + else + return false; + + return true; + } + + //bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); +}; + +//----------------------------------------------------------------------------- +// Exposes trace_t to VScript +//----------------------------------------------------------------------------- +class CTraceInfoAccessor +{ +public: + ~CTraceInfoAccessor() + { + if (m_surfaceAccessor) + { + CSurfaceScriptAccessor *pScriptSurface = HScriptToClass( m_surfaceAccessor ); + //g_pScriptVM->RemoveInstance( m_surfaceAccessor ); + delete pScriptSurface; + } + + //if (m_planeAccessor) + //{ + // g_pScriptVM->RemoveInstance( m_planeAccessor ); + //} + } + + // CGrameTrace stuff + bool DidHitWorld() const { return m_tr.DidHitWorld(); } + bool DidHitNonWorldEntity() const { return m_tr.DidHitNonWorldEntity(); } + int GetEntityIndex() const { return m_tr.GetEntityIndex(); } + bool DidHit() const { return m_tr.DidHit(); } + + float FractionLeftSolid() const { return m_tr.fractionleftsolid; } + int HitGroup() const { return m_tr.hitgroup; } + int PhysicsBone() const { return m_tr.physicsbone; } + + HSCRIPT Entity() const { return ToHScript(m_tr.m_pEnt); } + + int HitBox() const { return m_tr.hitbox; } + + // CBaseTrace stuff + bool IsDispSurface() { return m_tr.IsDispSurface(); } + bool IsDispSurfaceWalkable() { return m_tr.IsDispSurfaceWalkable(); } + bool IsDispSurfaceBuildable() { return m_tr.IsDispSurfaceBuildable(); } + bool IsDispSurfaceProp1() { return m_tr.IsDispSurfaceProp1(); } + bool IsDispSurfaceProp2() { return m_tr.IsDispSurfaceProp2(); } + + const Vector& StartPos() const { return m_tr.startpos; } + const Vector& EndPos() const { return m_tr.endpos; } + + float Fraction() const { return m_tr.fraction; } + + int Contents() const { return m_tr.contents; } + int DispFlags() const { return m_tr.dispFlags; } + + bool AllSolid() const { return m_tr.allsolid; } + bool StartSolid() const { return m_tr.startsolid; } + + HSCRIPT Surface() { return m_surfaceAccessor; } + void SetSurface( HSCRIPT hSurfAccessor ) { m_surfaceAccessor = hSurfAccessor; } + + HSCRIPT Plane() { return m_planeAccessor; } + void SetPlane( HSCRIPT hPlaneAccessor ) { m_planeAccessor = hPlaneAccessor; } + + trace_t &GetTrace() { return m_tr; } + void Destroy() { delete this; } + +private: + trace_t m_tr; + + HSCRIPT m_surfaceAccessor; + HSCRIPT m_planeAccessor; +}; + +#endif diff --git a/mp/src/game/shared/mapbase/vscript_singletons.cpp b/mp/src/game/shared/mapbase/vscript_singletons.cpp new file mode 100644 index 00000000..0f7fcec4 --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_singletons.cpp @@ -0,0 +1,1356 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: This file contains brand new VScript singletons and singletons replicated from API +// documentation in other games. +// +// See vscript_funcs_shared.cpp for more information. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include +#include +#include "ammodef.h" + +#ifndef CLIENT_DLL +#include "usermessages.h" +#include "ai_squad.h" +#endif // !CLIENT_DLL + +#include "filesystem.h" +#include "igameevents.h" + +#include "vscript_singletons.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IScriptManager *scriptmanager; + +//============================================================================= +// Net Prop Manager +// Based on L4D2 API +//============================================================================= +class CScriptNetPropManager +{ +public: + +#ifdef CLIENT_DLL + RecvProp *RecurseTable( RecvTable *pTable, const char *pszPropName ) +#else + SendProp *RecurseTable( SendTable *pTable, const char *pszPropName ) +#endif + { +#ifdef CLIENT_DLL + RecvProp *pProp = NULL; +#else + SendProp *pProp = NULL; +#endif + for (int i = 0; i < pTable->GetNumProps(); i++) + { + pProp = pTable->GetProp( i ); + if (pProp->GetType() == DPT_DataTable) + { + pProp = RecurseTable(pProp->GetDataTable(), pszPropName); + if (pProp) + return pProp; + } + else + { + if (FStrEq( pProp->GetName(), pszPropName )) + return pProp; + } + } + + return NULL; + } + +#ifdef CLIENT_DLL + RecvProp *RecurseNetworkClass( ClientClass *pClass, const char *pszPropName ) +#else + SendProp *RecurseNetworkClass( ServerClass *pClass, const char *pszPropName ) +#endif + { +#ifdef CLIENT_DLL + RecvProp *pProp = RecurseTable( pClass->m_pRecvTable, pszPropName ); +#else + SendProp *pProp = RecurseTable( pClass->m_pTable, pszPropName ); +#endif + if (pProp) + return pProp; + + if (pClass->m_pNext) + return RecurseNetworkClass( pClass->m_pNext, pszPropName ); + else + return NULL; + } + +#ifdef CLIENT_DLL + RecvProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) + { + if (pEnt) + { + return RecurseNetworkClass( pEnt->GetClientClass(), pszPropName ); + } + + return NULL; + } +#else + SendProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) + { + if (pEnt) + { + return RecurseNetworkClass( pEnt->GetServerClass(), pszPropName ); + } + + return NULL; + } +#endif + + int GetPropArraySize( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp) + { + // TODO: Is this what this function wants? + return pProp->GetNumElements(); + } + + return -1; + } + + #define GetPropFunc( name, varType, propType, defaultval ) \ + varType name( HSCRIPT hEnt, const char *pszPropName ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + return *(varType*)((char *)pEnt + pProp->GetOffset()); \ + } \ + return defaultval; \ + } \ + + #define GetPropFuncArray( name, varType, propType, defaultval ) \ + varType name( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + return ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; \ + } \ + return defaultval; \ + } \ + + GetPropFunc( GetPropFloat, float, DPT_Float, -1 ); + GetPropFuncArray( GetPropFloatArray, float, DPT_Float, -1 ); + GetPropFunc( GetPropInt, int, DPT_Int, -1 ); + GetPropFuncArray( GetPropIntArray, int, DPT_Int, -1 ); + GetPropFunc( GetPropVector, Vector, DPT_Vector, vec3_invalid ); + GetPropFuncArray( GetPropVectorArray, Vector, DPT_Vector, vec3_invalid ); + + HSCRIPT GetPropEntity( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return ToHScript( *(CHandle*)((char *)pEnt + pProp->GetOffset()) ); + } + + return NULL; + } + + HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return ToHScript( ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] ); + } + + return NULL; + } + + const char *GetPropString( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return (const char*)((char *)pEnt + pProp->GetOffset()); + } + + return NULL; + } + + const char *GetPropStringArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return ((const char**)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; + } + + return NULL; + } + + const char *GetPropType( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp) + { + switch (pProp->GetType()) + { + case DPT_Int: return "integer"; + case DPT_Float: return "float"; + case DPT_Vector: return "vector"; + case DPT_VectorXY: return "vector2d"; + case DPT_String: return "string"; + case DPT_Array: return "array"; + case DPT_DataTable: return "datatable"; + } + } + + return NULL; + } + + bool HasProp( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + return GetPropByName( pEnt, pszPropName ) != NULL; + } + + #define SetPropFunc( name, varType, propType ) \ + void name( HSCRIPT hEnt, const char *pszPropName, varType value ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + *(varType*)((char *)pEnt + pProp->GetOffset()) = value; \ + } \ + } \ + + #define SetPropFuncArray( name, varType, propType ) \ + void name( HSCRIPT hEnt, const char *pszPropName, varType value, int iArrayElement ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = value; \ + } \ + } \ + + SetPropFunc( SetPropFloat, float, DPT_Float ); + SetPropFuncArray( SetPropFloatArray, float, DPT_Float ); + SetPropFunc( SetPropInt, int, DPT_Int ); + SetPropFuncArray( SetPropIntArray, int, DPT_Int ); + SetPropFunc( SetPropVector, Vector, DPT_Vector ); + SetPropFuncArray( SetPropVectorArray, Vector, DPT_Vector ); + SetPropFunc( SetPropString, const char*, DPT_String ); + SetPropFuncArray( SetPropStringArray, const char*, DPT_String ); + + void SetPropEntity( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + *((CHandle*)((char *)pEnt + pProp->GetOffset())) = ToEnt(value); + } + } + + HSCRIPT SetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value, int iArrayElement ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = ToEnt(value); + } + + return NULL; + } + +private: +} g_ScriptNetPropManager; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties of an entity." ) + DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an netprop array, or -1." ) + DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an EHANDLE valued netprop (21 bit integer). Returns the script handle of the entity." ) + DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an EHANDLE valued netprop (21 bit integer) from an array. Returns the script handle of the entity." ) + DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropString, "Reads a string valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropType, "Returns the name of the netprop type as a string." ) + DEFINE_SCRIPTFUNC( HasProp, "Checks if a netprop exists." ) + DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an EHANDLE valued netprop (21 bit integer) to reference the specified entity." ) + DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an EHANDLE valued netprop (21 bit integer) from an array to reference the specified entity." ) + DEFINE_SCRIPTFUNC( SetPropFloat, "Sets a netprop to the specified float." ) + DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a netprop from an array to the specified float." ) + DEFINE_SCRIPTFUNC( SetPropInt, "Sets a netprop to the specified integer." ) + DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets a netprop from an array to the specified integer." ) + DEFINE_SCRIPTFUNC( SetPropString, "Sets a netprop to the specified string." ) + DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a netprop from an array to the specified string." ) + DEFINE_SCRIPTFUNC( SetPropVector, "Sets a netprop to the specified vector." ) + DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a netprop from an array to the specified vector." ) +END_SCRIPTDESC(); + +//============================================================================= +// Localization Interface +// Unique to Mapbase +//============================================================================= +class CScriptLocalize +{ +public: + + const char *GetTokenAsUTF8( const char *pszToken ) + { + const char *pText = g_pVGuiLocalize->FindAsUTF8( pszToken ); + if ( pText ) + { + return pText; + } + + return NULL; + } + + void AddStringAsUTF8( const char *pszToken, const char *pszString ) + { + wchar_t wpszString[256]; + g_pVGuiLocalize->ConvertANSIToUnicode( pszString, wpszString, sizeof(wpszString) ); + + // TODO: This is a fake file name! Should "fileName" mean anything? + g_pVGuiLocalize->AddString( pszToken, wpszString, "resource/vscript_localization.txt" ); + } + +private: +} g_ScriptLocalize; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptLocalize, "CLocalize", SCRIPT_SINGLETON "Accesses functions related to localization strings." ) + + DEFINE_SCRIPTFUNC( GetTokenAsUTF8, "Gets the current language's token as a UTF-8 string (not Unicode)." ) + + DEFINE_SCRIPTFUNC( AddStringAsUTF8, "Adds a new localized token as a UTF-8 string (not Unicode)." ) + +END_SCRIPTDESC(); + +//============================================================================= +// Game Event Listener +// Based on Source 2 API +//============================================================================= +class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSystem +{ +public: + CScriptGameEventListener() : m_bActive(false) {} + ~CScriptGameEventListener() + { + StopListeningForEvent(); + } + + intptr_t ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); + void StopListeningForEvent(); + +public: + static bool StopListeningToGameEvent( intptr_t listener ); + static void StopListeningToAllGameEvents( const char* szContext ); + +public: + void FireGameEvent( IGameEvent *event ); + void LevelShutdownPreEntity(); + +private: + bool m_bActive; + const char *m_pszContext; + HSCRIPT m_hCallback; + + static const char *FindContext( const char *szContext, CScriptGameEventListener *pIgnore = NULL ); + //inline const char *GetContext( CScriptGameEventListener *p ); + //inline const char *GetContext(); + +public: + static void DumpEventListeners(); +#ifndef CLIENT_DLL + static void LoadAllEvents(); + static void LoadEventsFromFile( const char *filename, const char *pathID = NULL ); + static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); +#endif // !CLIENT_DLL + +private: +#ifndef CLIENT_DLL + static CUtlVector< KeyValues* > s_GameEvents; +#endif // !CLIENT_DLL + static CUtlVectorAutoPurge< CScriptGameEventListener* > s_GameEventListeners; + +}; + +#ifndef CLIENT_DLL +CUtlVector< KeyValues* > CScriptGameEventListener::s_GameEvents; +#endif // !CLIENT_DLL +CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_GameEventListeners; + +#if 0 +#ifdef CLIENT_DLL +CON_COMMAND_F( cl_dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) +{ + CScriptGameEventListener::DumpEventListeners(); +} +#else // GAME_DLL +CON_COMMAND_F( dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) +{ + CScriptGameEventListener::DumpEventListeners(); +} +#endif // CLIENT_DLL +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CScriptGameEventListener::DumpEventListeners() +{ + CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump start\n" ); + FOR_EACH_VEC( s_GameEventListeners, i ) + { + CGMsg( 0, CON_GROUP_VSCRIPT, " %d (0x%p) %d : %s\n", i,s_GameEventListeners[i], + s_GameEventListeners[i], + s_GameEventListeners[i]->m_pszContext ? s_GameEventListeners[i]->m_pszContext : ""); + } + CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump end\n" ); +} + +void CScriptGameEventListener::FireGameEvent( IGameEvent *event ) +{ + ScriptVariant_t hTable; + g_pScriptVM->CreateTable( hTable ); + // TODO: pass event data on client +#ifdef GAME_DLL + WriteEventData( event, hTable ); +#endif + g_pScriptVM->SetValue( hTable, "game_event_listener", reinterpret_cast(this) ); // POINTER_TO_INT + // g_pScriptVM->SetValue( hTable, "game_event_name", event->GetName() ); + g_pScriptVM->ExecuteFunction( m_hCallback, &hTable, 1, NULL, NULL, true ); + g_pScriptVM->ReleaseScript( hTable ); +} + +void CScriptGameEventListener::LevelShutdownPreEntity() +{ + s_GameEventListeners.FindAndFastRemove(this); + delete this; +} + +//----------------------------------------------------------------------------- +// Executed in LevelInitPreEntity +//----------------------------------------------------------------------------- +#ifndef CLIENT_DLL +void CScriptGameEventListener::LoadAllEvents() +{ + // Listed in the same order they are loaded in GameEventManager + const char *filenames[] = + { + "resource/serverevents.res", + "resource/gameevents.res", + "resource/mapbaseevents.res", + "resource/modevents.res" + }; + + const char *pathlist[] = + { + "GAME", + "MOD" + }; + + // Destroy old KeyValues + if ( s_GameEvents.Count() ) + { + for ( int i = 0; i < s_GameEvents.Count(); ++i ) + s_GameEvents[i]->deleteThis(); + s_GameEvents.Purge(); + } + + for ( int j = 0; j < ARRAYSIZE(pathlist); ++j ) + for ( int i = 0; i < ARRAYSIZE(filenames); ++i ) + { + LoadEventsFromFile( filenames[i], pathlist[j] ); + } +} + +//----------------------------------------------------------------------------- +// Load event files into a lookup array to be able to return the event data to the VM. +//----------------------------------------------------------------------------- +void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const char *pathID ) +{ + KeyValues *pKV = new KeyValues("GameEvents"); + + if ( !pKV->LoadFromFile( filesystem, filename, pathID ) ) + { + // CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Failed to load file %s, %s\n", filename, pathID ); + pKV->deleteThis(); + return; + } + + // Set the key value types to what they are from their string description values to read the correct data type in WriteEventData. + // There might be a better way of doing this, but this is okay since it's only done on file load. + for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) + for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + if ( sub->GetDataType() == KeyValues::TYPE_STRING ) + { + const char *szVal = sub->GetString(); + if ( !V_stricmp( szVal, "byte" ) || !V_stricmp( szVal, "short" ) || !V_stricmp( szVal, "long" ) || !V_stricmp( szVal, "bool" ) ) + { + sub->SetInt( NULL, 0 ); + } + else if ( !V_stricmp( szVal, "float" ) ) + { + sub->SetFloat( NULL, 0.0f ); + } + } + // none : value is not networked + // string : a zero terminated string + // bool : unsigned int, 1 bit + // byte : unsigned int, 8 bit + // short : signed int, 16 bit + // long : signed int, 32 bit + // float : float, 32 bit + } + + CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded %s, %s\n", filename, pathID ); + + s_GameEvents.AddToTail(pKV); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CScriptGameEventListener::WriteEventData( IGameEvent *event, HSCRIPT hTable ) +{ + // TODO: Something more efficient than iterating through all the events that ever exist one by one + + const char *szEvent = event->GetName(); + for ( int i = 0; i < s_GameEvents.Count(); ++i ) + { + KeyValues *pKV = s_GameEvents[i]; + for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) + { + if ( !V_stricmp( key->GetName(), szEvent ) ) + { + for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + const char *szKey = sub->GetName(); + switch ( sub->GetDataType() ) + { + case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, szKey, event->GetString( szKey ) ); break; + case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, szKey, event->GetInt ( szKey ) ); break; + case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, szKey, event->GetFloat ( szKey ) ); break; + // default: DevWarning( 2, "CScriptGameEventListener::WriteEventData: unknown data type '%d' on key '%s' in event '%s'\n", sub->GetDataType(), szKey, szEvent ); + } + } + return; + } + } + } +} +#endif // !CLIENT_DLL +//----------------------------------------------------------------------------- +// Find if context is in use by others; used to alloc/dealloc only when required. +// Returns allocated pointer to string +// Expects non-NULL context input +//----------------------------------------------------------------------------- +const char *CScriptGameEventListener::FindContext( const char *szContext, CScriptGameEventListener *pIgnore ) +{ + for ( int i = s_GameEventListeners.Count(); i--; ) + { + CScriptGameEventListener *pCur = s_GameEventListeners[i]; + if ( pCur != pIgnore ) + { + if ( pCur->m_pszContext && !V_stricmp( szContext, pCur->m_pszContext ) ) + { + return pCur->m_pszContext; + } + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +intptr_t CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) +{ + m_bActive = true; + + char *psz; + + if ( szContext && *szContext ) + { + psz = const_cast(FindContext(szContext)); + if ( !psz ) + { + int len = V_strlen(szContext) + 1; + if ( len > 1 ) + { + int size = min( len, 256 ); // arbitrary clamp + psz = new char[size]; + V_strncpy( psz, szContext, size ); + } + } + } + else + { + psz = NULL; + } + + m_pszContext = psz; + m_hCallback = hFunc; + + if ( gameeventmanager ) +#ifdef CLIENT_DLL + gameeventmanager->AddListener( this, szEvent, false ); +#else + gameeventmanager->AddListener( this, szEvent, true ); +#endif + s_GameEventListeners.AddToTail( this ); + + return reinterpret_cast(this); // POINTER_TO_INT +} + +//----------------------------------------------------------------------------- +// Free stuff. Called from the destructor, does not remove itself from the listener list. +//----------------------------------------------------------------------------- +void CScriptGameEventListener::StopListeningForEvent() +{ + if ( !m_bActive ) + return; + + if ( g_pScriptVM ) + { + g_pScriptVM->ReleaseScript( m_hCallback ); + } + else if ( m_hCallback ) + { + AssertMsg( 0, "LEAK (0x%p)\n", (void*)m_hCallback ); + } + + if ( m_pszContext ) + { + if ( !FindContext( m_pszContext, this ) ) + { + delete[] m_pszContext; + } + + m_pszContext = NULL; + } + + m_hCallback = NULL; + + if ( gameeventmanager ) + gameeventmanager->RemoveListener( this ); + + m_bActive = false; +} + +//----------------------------------------------------------------------------- +// Stop the specified event listener. +//----------------------------------------------------------------------------- +bool CScriptGameEventListener::StopListeningToGameEvent( intptr_t listener ) +{ + CScriptGameEventListener *p = reinterpret_cast(listener); // INT_TO_POINTER + + bool bRemoved = s_GameEventListeners.FindAndFastRemove(p); + if ( bRemoved ) + { + delete p; + } + + return bRemoved; +} + +//----------------------------------------------------------------------------- +// Stops listening to all events within a context. +//----------------------------------------------------------------------------- +void CScriptGameEventListener::StopListeningToAllGameEvents( const char* szContext ) +{ + if ( szContext ) + { + if ( *szContext ) + { + // Iterate from the end so they can be safely removed as they are deleted + for ( int i = s_GameEventListeners.Count(); i--; ) + { + CScriptGameEventListener *pCur = s_GameEventListeners[i]; + if ( pCur->m_pszContext && !V_stricmp( szContext, pCur->m_pszContext ) ) + { + s_GameEventListeners.Remove(i); // keep list order + delete pCur; + } + } + } + else // empty (NULL) context + { + for ( int i = s_GameEventListeners.Count(); i--; ) + { + CScriptGameEventListener *pCur = s_GameEventListeners[i]; + if ( !pCur->m_pszContext ) + { + s_GameEventListeners.Remove(i); + delete pCur; + } + } + } + } +#if 0 + if ( !szContext ) + { + for ( int i = s_GameEventListeners.Count(); i--; ) + delete s_GameEventListeners[i]; + s_GameEventListeners.Purge(); + } +#endif +} + +//============================================================================= +//============================================================================= + +static int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) +{ + CScriptGameEventListener *p = new CScriptGameEventListener(); + return p->ListenToGameEvent( szEvent, hFunc, szContext ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void FireGameEvent( const char* szEvent, HSCRIPT hTable ) +{ + IGameEvent *event = gameeventmanager->CreateEvent( szEvent ); + if ( event ) + { + ScriptVariant_t key, val; + int nIterator = -1; + while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) + { + switch ( val.m_type ) + { + case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break; + case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break; + case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break; + case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break; + } + + g_pScriptVM->ReleaseValue(key); + g_pScriptVM->ReleaseValue(val); + } + +#ifdef CLIENT_DLL + gameeventmanager->FireEventClientSide(event); +#else + gameeventmanager->FireEvent(event); +#endif + } +} + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Copy of FireGameEvent, server only with no broadcast to clients. +//----------------------------------------------------------------------------- +static void FireGameEventLocal( const char* szEvent, HSCRIPT hTable ) +{ + IGameEvent *event = gameeventmanager->CreateEvent( szEvent ); + if ( event ) + { + ScriptVariant_t key, val; + int nIterator = -1; + while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) + { + switch ( val.m_type ) + { + case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break; + case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break; + case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break; + case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break; + } + + g_pScriptVM->ReleaseValue(key); + g_pScriptVM->ReleaseValue(val); + } + + gameeventmanager->FireEvent(event,true); + } +} +#endif // !CLIENT_DLL + +//============================================================================= +// Save/Restore Utility +// Based on Source 2 API +//============================================================================= +class CScriptSaveRestoreUtil : public CAutoGameSystem +{ +public: + static void SaveTable( const char *szId, HSCRIPT hTable ); + static void RestoreTable( const char *szId, HSCRIPT hTable ); + static void ClearSavedTable( const char *szId ); + +// IGameSystem interface +public: + void OnSave() + { + if ( g_pScriptVM ) + { + HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnSave" ); + if ( hFunc ) + { + g_pScriptVM->Call( hFunc ); + } + } + } + + void OnRestore() + { + if ( g_pScriptVM ) + { + HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnRestore" ); + if ( hFunc ) + { + g_pScriptVM->Call( hFunc ); + } + } + } + + void Shutdown() + { + FOR_EACH_VEC( m_aKeyValues, i ) + m_aKeyValues[i]->deleteThis(); + m_aKeyValues.Purge(); + m_aContext.PurgeAndDeleteElements(); + } + +private: + static int GetIndexForContext( const char *szId ); + + // indices must match, always remove keeping order + static CUtlStringList m_aContext; + static CUtlVector m_aKeyValues; + +} g_ScriptSaveRestoreUtil; + +CUtlStringList CScriptSaveRestoreUtil::m_aContext; +CUtlVector CScriptSaveRestoreUtil::m_aKeyValues; + +int CScriptSaveRestoreUtil::GetIndexForContext( const char *szId ) +{ + int idx = -1; + FOR_EACH_VEC( m_aContext, i ) + { + if ( !V_stricmp( szId, m_aContext[i] ) ) + { + idx = i; + break; + } + } + return idx; +} + +//----------------------------------------------------------------------------- +// Store a table with primitive values that will persist across level transitions and save loads. +//----------------------------------------------------------------------------- +void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable ) +{ + int idx = GetIndexForContext(szId); + + KeyValues *pKV; + + if ( idx == -1 ) + { + pKV = new KeyValues("ScriptSavedTable"); + m_aKeyValues.AddToTail(pKV); + + if ( V_strlen(szId) > 255 ) // arbitrary clamp + { + char c[256]; + V_strncpy( c, szId, sizeof(c) ); + m_aContext.CopyAndAddToTail(c); + } + else + { + m_aContext.CopyAndAddToTail(szId); + } + } + else + { + pKV = m_aKeyValues[idx]; + pKV->Clear(); + } + + ScriptVariant_t key, val; + int nIterator = -1; + while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) + { + switch ( val.m_type ) + { + case FIELD_FLOAT: pKV->SetFloat ( key.m_pszString, val.m_float ); break; + case FIELD_INTEGER: pKV->SetInt ( key.m_pszString, val.m_int ); break; + case FIELD_BOOLEAN: pKV->SetBool ( key.m_pszString, val.m_bool ); break; + case FIELD_CSTRING: pKV->SetString( key.m_pszString, val.m_pszString ); break; + } + + g_pScriptVM->ReleaseValue(key); + g_pScriptVM->ReleaseValue(val); + } +} + +//----------------------------------------------------------------------------- +// Retrieves a table from storage. Write into input table. +//----------------------------------------------------------------------------- +void CScriptSaveRestoreUtil::RestoreTable( const char *szId, HSCRIPT hTable ) +{ + int idx = GetIndexForContext(szId); + + KeyValues *pKV; + + if ( idx == -1 ) + { + // DevWarning( 2, "RestoreTable could not find saved table with context '%s'\n", szId ); + return; + } + else + { + pKV = m_aKeyValues[idx]; + } + + FOR_EACH_SUBKEY( pKV, key ) + { + switch ( key->GetDataType() ) + { + case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetString() ); break; + case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetInt() ); break; + case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetFloat() ); break; + } + } +} + +//----------------------------------------------------------------------------- +// Remove a saved table. +//----------------------------------------------------------------------------- +void CScriptSaveRestoreUtil::ClearSavedTable( const char *szId ) +{ + int idx = GetIndexForContext(szId); + + if ( idx == -1 ) + { + // DevWarning( 2, "ClearSavedTable could not find saved table with context '%s'\n", szId ); + return; + } + + m_aKeyValues[idx]->deleteThis(); + m_aKeyValues.Remove(idx); + + delete[] m_aContext[idx]; + m_aContext.Remove(idx); +} + +//============================================================================= +// Read/Write to File +// Based on L4D2/Source 2 API +//============================================================================= +#define SCRIPT_MAX_FILE_READ_SIZE (16 * 1024) // 16KB +#define SCRIPT_MAX_FILE_WRITE_SIZE (64 * 1024 * 1024) // 64MB +#define SCRIPT_RW_PATH_ID "MOD" +#define SCRIPT_RW_FULL_PATH_FMT "vscript_io/%s" + +class CScriptReadWriteFile : public CAutoGameSystem +{ +public: + static bool ScriptFileWrite( const char *szFile, const char *szInput ); + static const char *ScriptFileRead( const char *szFile ); + //static const char *CRC32_Checksum( const char *szFilename ); + + // NOTE: These two functions are new with Mapbase and have no Valve equivalent + static bool ScriptKeyValuesWrite( const char *szFile, HSCRIPT hInput ); + static HSCRIPT ScriptKeyValuesRead( const char *szFile ); + + void LevelShutdownPostEntity() + { + if ( m_pszReturnReadFile ) + { + delete[] m_pszReturnReadFile; + m_pszReturnReadFile = NULL; + } + + //if ( m_pszReturnCRC32 ) + //{ + // delete[] m_pszReturnCRC32; + // m_pszReturnCRC32 = NULL; + //} + } + +private: + static const char *m_pszReturnReadFile; + //static const char *m_pszReturnCRC32; + +} g_ScriptReadWrite; + +const char *CScriptReadWriteFile::m_pszReturnReadFile = NULL; +//const char *CScriptReadWriteFile::m_pszReturnCRC32 = NULL; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CScriptReadWriteFile::ScriptFileWrite( const char *szFile, const char *szInput ) +{ + size_t len = strlen(szInput); + if ( len > SCRIPT_MAX_FILE_WRITE_SIZE ) + { + DevWarning( 2, "Input is too large for a ScriptFileWrite ( %s / %d MB )\n", V_pretifymem(len,2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) ); + return false; + } + + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return false; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + buf.PutString(szInput); + + int nSize = V_strlen(pszFullName) + 1; + char *pszDir = (char*)stackalloc(nSize); + V_memcpy( pszDir, pszFullName, nSize ); + V_StripFilename( pszDir ); + + g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID ); + bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf ); + buf.Purge(); + return res; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +const char *CScriptReadWriteFile::ScriptFileRead( const char *szFile ) +{ + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return NULL; + } + + unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID ); + if ( size >= SCRIPT_MAX_FILE_READ_SIZE ) + { + DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptFileRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE ); + return NULL; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( pszFullName, SCRIPT_RW_PATH_ID, buf, SCRIPT_MAX_FILE_READ_SIZE ) ) + { + return NULL; + } + + // first time calling, allocate + if ( !m_pszReturnReadFile ) + m_pszReturnReadFile = new char[SCRIPT_MAX_FILE_READ_SIZE]; + + V_strncpy( const_cast(m_pszReturnReadFile), (const char*)buf.Base(), buf.Size() ); + buf.Purge(); + return m_pszReturnReadFile; +} + +//----------------------------------------------------------------------------- +// Get the checksum of any file. Can be used to check the existence or validity of a file. +// Returns unsigned int as hex string. +//----------------------------------------------------------------------------- +/* +const char *CScriptReadWriteFile::CRC32_Checksum( const char *szFilename ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::READ_ONLY ); + if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, buf ) ) + return NULL; + + // first time calling, allocate + if ( !m_pszReturnCRC32 ) + m_pszReturnCRC32 = new char[9]; // 'FFFFFFFF\0' + + V_snprintf( const_cast(m_pszReturnCRC32), 9, "%X", CRC32_ProcessSingleBuffer( buf.Base(), buf.Size()-1 ) ); + buf.Purge(); + + return m_pszReturnCRC32; +} +*/ + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CScriptReadWriteFile::ScriptKeyValuesWrite( const char *szFile, HSCRIPT hInput ) +{ + KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hInput ); + if (!pKV) + { + return false; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + pKV->RecursiveSaveToFile( buf, 0 ); + + if ( buf.Size() > SCRIPT_MAX_FILE_WRITE_SIZE ) + { + DevWarning( 2, "Input is too large for a ScriptFileWrite ( %s / %d MB )\n", V_pretifymem(buf.Size(),2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) ); + buf.Purge(); + return false; + } + + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + buf.Purge(); + return false; + } + + int nSize = V_strlen(pszFullName) + 1; + char *pszDir = (char*)stackalloc(nSize); + V_memcpy( pszDir, pszFullName, nSize ); + V_StripFilename( pszDir ); + + g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID ); + bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf ); + buf.Purge(); + return res; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT CScriptReadWriteFile::ScriptKeyValuesRead( const char *szFile ) +{ + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return NULL; + } + + unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID ); + if ( size >= SCRIPT_MAX_FILE_READ_SIZE ) + { + DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptFileRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE ); + return NULL; + } + + KeyValues *pKV = new KeyValues( szFile ); + if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, SCRIPT_RW_PATH_ID ) ) + { + pKV->deleteThis(); + return NULL; + } + + HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); // bAllowDestruct is supposed to automatically remove the involved KV + + return hScript; +} +#undef SCRIPT_MAX_FILE_READ_SIZE +#undef SCRIPT_MAX_FILE_WRITE_SIZE +#undef SCRIPT_RW_PATH_ID +#undef SCRIPT_RW_FULL_PATH_FMT + +//============================================================================= +// User Message Helper +// Based on Source 2 API +//============================================================================= +#ifndef CLIENT_DLL +class CNetMsgScriptHelper +{ +private: + CRecipientFilter filter; + bf_write message; + byte data_msg[ MAX_USER_MSG_DATA ]; + + inline void SendMsg( bf_write *bf ) + { + bf_read buffer = bf_read(); + buffer.StartReading( message.GetData(), message.m_nDataBytes ); + bf->WriteBitsFromBuffer( &buffer, message.GetNumBitsWritten() ); + engine->MessageEnd(); + } + +public: + inline void Reset() + { + message.StartWriting( data_msg, sizeof(data_msg) ); + filter.Reset(); + } + + void SendUserMessage( HSCRIPT player, const char *msg, bool bReliable ) + { + int msg_type = usermessages->LookupUserMessage(msg); + if ( msg_type == -1 ) + { + g_pScriptVM->RaiseException("UserMessageBegin: Unregistered message"); + return; + } + + CBaseEntity *pPlayer = ToEnt(player); + if ( pPlayer ) + { + filter.AddRecipient( (CBasePlayer*)pPlayer ); + } + + if ( bReliable ) + { + filter.MakeReliable(); + } + + SendMsg( engine->UserMessageBegin( &filter, msg_type ) ); + } + + void SendEntityMessage( HSCRIPT hEnt, bool bReliable ) + { + CBaseEntity *entity = ToEnt(hEnt); + if ( !entity ) + { + g_pScriptVM->RaiseException("EntityMessageBegin: invalid entity"); + return; + } + + SendMsg( engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), bReliable ) ); + } + +public: + void AddRecipient( HSCRIPT player ) + { + CBaseEntity *pPlayer = ToEnt(player); + if ( pPlayer ) + { + filter.AddRecipient( (CBasePlayer*)pPlayer ); + } + } + + void AddRecipientsByPVS( const Vector &pos ) + { + filter.AddRecipientsByPVS(pos); + } + + void AddAllPlayers() + { + filter.AddAllPlayers(); + } + +public: + void WriteByte( int iValue ) { message.WriteByte( iValue ); } + void WriteChar( int iValue ) { message.WriteChar( iValue ); } + void WriteShort( int iValue ) { message.WriteShort( iValue ); } + void WriteWord( int iValue ) { message.WriteWord( iValue ); } + void WriteLong( int iValue ) { message.WriteLong( iValue ); } + void WriteFloat( float flValue ) { message.WriteFloat( flValue ); } + void WriteAngle( float flValue ) { message.WriteBitAngle( flValue, 8 ); } + void WriteCoord( float flValue ) { message.WriteBitCoord( flValue ); } + void WriteVec3Coord( const Vector& rgflValue ) { message.WriteBitVec3Coord( rgflValue ); } + void WriteVec3Normal( const Vector& rgflValue ) { message.WriteBitVec3Normal( rgflValue ); } + void WriteAngles( const QAngle& rgflValue ) { message.WriteBitAngles( rgflValue ); } + void WriteString( const char *sz ) { message.WriteString( sz ); } + void WriteEntity( int iValue ) { message.WriteShort( iValue ); } + void WriteBool( bool bValue ) { message.WriteOneBit( bValue ? 1 : 0 ); } + void WriteEHandle( HSCRIPT hEnt ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + long iEncodedEHandle; + if ( pEnt ) + { + EHANDLE hEnt = pEnt; + int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; + iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS); + } + else + { + iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE; + } + message.WriteLong( iEncodedEHandle ); + } + +} g_ScriptNetMsg; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "NetworkMessages" ) + DEFINE_SCRIPTFUNC( Reset, "" ) + DEFINE_SCRIPTFUNC( SendUserMessage, "" ) + DEFINE_SCRIPTFUNC( SendEntityMessage, "" ) + DEFINE_SCRIPTFUNC( AddRecipient, "" ) + DEFINE_SCRIPTFUNC( AddRecipientsByPVS, "" ) + DEFINE_SCRIPTFUNC( AddAllPlayers, "" ) + DEFINE_SCRIPTFUNC( WriteByte, "" ) + DEFINE_SCRIPTFUNC( WriteChar, "" ) + DEFINE_SCRIPTFUNC( WriteShort, "" ) + DEFINE_SCRIPTFUNC( WriteWord, "" ) + DEFINE_SCRIPTFUNC( WriteLong, "" ) + DEFINE_SCRIPTFUNC( WriteFloat, "" ) + DEFINE_SCRIPTFUNC( WriteAngle, "" ) + DEFINE_SCRIPTFUNC( WriteCoord, "" ) + DEFINE_SCRIPTFUNC( WriteVec3Coord, "" ) + DEFINE_SCRIPTFUNC( WriteVec3Normal, "" ) + DEFINE_SCRIPTFUNC( WriteAngles, "" ) + DEFINE_SCRIPTFUNC( WriteString, "" ) + DEFINE_SCRIPTFUNC( WriteEntity, "" ) + DEFINE_SCRIPTFUNC( WriteEHandle, "" ) + DEFINE_SCRIPTFUNC( WriteBool, "" ) +END_SCRIPTDESC(); + +#endif // !CLIENT_DLL + +void RegisterScriptSingletons() +{ + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::SaveTable, "SaveTable", "Store a table with primitive values that will persist across level transitions and save loads." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::RestoreTable, "RestoreTable", "Retrieves a table from storage. Write into input table." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::ClearSavedTable, "ClearSavedTable", "Removes the table with the given context." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::ScriptFileWrite, "StringToFile", "Stores the string into the file" ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::ScriptFileRead, "FileToString", "Returns the string from the file, null if no file or file is too big." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::ScriptKeyValuesWrite, "KeyValuesToFile", "Stores the CScriptKeyValues into the file" ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::ScriptKeyValuesRead, "FileToKeyValues", "Returns the CScriptKeyValues from the file, null if no file or file is too big." ); + + ScriptRegisterFunction( g_pScriptVM, ListenToGameEvent, "Register as a listener for a game event from script." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToGameEvent, "StopListeningToGameEvent", "Stop the specified event listener." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToAllGameEvents, "StopListeningToAllGameEvents", "Stop listening to all game events within a specific context." ); + ScriptRegisterFunction( g_pScriptVM, FireGameEvent, "Fire a game event." ); +#ifndef CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, FireGameEventLocal, "Fire a game event without broadcasting to the client." ); +#endif + + g_pScriptVM->RegisterInstance( &g_ScriptNetPropManager, "NetProps" ); + g_pScriptVM->RegisterInstance( &g_ScriptLocalize, "Localize" ); +#ifndef CLIENT_DLL + g_pScriptVM->RegisterInstance( &g_ScriptNetMsg, "NetMsg" ); +#endif + + // Singletons not unique to VScript (not declared or defined here) + g_pScriptVM->RegisterInstance( GameRules(), "GameRules" ); + g_pScriptVM->RegisterInstance( GetAmmoDef(), "AmmoDef" ); +#ifndef CLIENT_DLL + g_pScriptVM->RegisterInstance( &g_AI_SquadManager, "Squads" ); +#endif + +#ifndef CLIENT_DLL + CScriptGameEventListener::LoadAllEvents(); +#endif // !CLIENT_DLL +} diff --git a/mp/src/game/shared/mapbase/vscript_singletons.h b/mp/src/game/shared/mapbase/vscript_singletons.h new file mode 100644 index 00000000..b190fe45 --- /dev/null +++ b/mp/src/game/shared/mapbase/vscript_singletons.h @@ -0,0 +1,16 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: See vscript_singletons.cpp +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_MATH +#define VSCRIPT_FUNCS_MATH +#ifdef _WIN32 +#pragma once +#endif + +void RegisterScriptSingletons(); + +#endif diff --git a/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp new file mode 100644 index 00000000..9a537723 --- /dev/null +++ b/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -0,0 +1,591 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript-driven custom weapon class. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "tier1/fmtstr.h" +#include "weapon_custom_scripted.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +//========================================================= + +BEGIN_DATADESC( CWeaponCustomScripted ) + + DEFINE_AUTO_ARRAY( m_iszClientScripts, FIELD_CHARACTER ), + DEFINE_AUTO_ARRAY( m_iszWeaponScriptName, FIELD_CHARACTER ), + +END_DATADESC() + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCustomScripted, DT_WeaponCustomScripted ) + +BEGIN_NETWORK_TABLE( CWeaponCustomScripted, DT_WeaponCustomScripted ) + +#ifdef CLIENT_DLL + RecvPropString( RECVINFO(m_iszClientScripts) ), + RecvPropString( RECVINFO(m_iszWeaponScriptName) ), +#else + SendPropString( SENDINFO(m_iszClientScripts) ), + SendPropString( SENDINFO(m_iszWeaponScriptName) ), +#endif + +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponCustomScripted ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_custom_scripted1, CWeaponCustomScripted ); + +// Only need one of the names +PRECACHE_WEAPON_REGISTER( weapon_custom_scripted1 ); + +//IMPLEMENT_ACTTABLE( CWeaponCustomScripted ); + +#define DEFINE_STATIC_HOOK( name ) ScriptHook_t CWeaponCustomScripted::g_Hook_##name + +DEFINE_STATIC_HOOK( HasAnyAmmo ); +DEFINE_STATIC_HOOK( HasPrimaryAmmo ); +DEFINE_STATIC_HOOK( HasSecondaryAmmo ); + +DEFINE_STATIC_HOOK( CanHolster ); +DEFINE_STATIC_HOOK( CanDeploy ); +DEFINE_STATIC_HOOK( Deploy ); +DEFINE_STATIC_HOOK( Holster ); + +DEFINE_STATIC_HOOK( ItemPreFrame ); +DEFINE_STATIC_HOOK( ItemPostFrame ); +DEFINE_STATIC_HOOK( ItemBusyFrame ); +DEFINE_STATIC_HOOK( ItemHolsterFrame ); +DEFINE_STATIC_HOOK( WeaponIdle ); +DEFINE_STATIC_HOOK( HandleFireOnEmpty ); + +DEFINE_STATIC_HOOK( CheckReload ); +DEFINE_STATIC_HOOK( FinishReload ); +DEFINE_STATIC_HOOK( AbortReload ); +DEFINE_STATIC_HOOK( Reload ); +DEFINE_STATIC_HOOK( Reload_NPC ); + +DEFINE_STATIC_HOOK( PrimaryAttack ); +DEFINE_STATIC_HOOK( SecondaryAttack ); + +DEFINE_STATIC_HOOK( GetPrimaryAttackActivity ); +DEFINE_STATIC_HOOK( GetSecondaryAttackActivity ); +DEFINE_STATIC_HOOK( GetDrawActivity ); +DEFINE_STATIC_HOOK( GetDefaultAnimSpeed ); + +DEFINE_STATIC_HOOK( GetBulletSpread ); +DEFINE_STATIC_HOOK( GetBulletSpreadForProficiency ); +DEFINE_STATIC_HOOK( GetFireRate ); +DEFINE_STATIC_HOOK( GetMinBurst ); +DEFINE_STATIC_HOOK( GetMaxBurst ); +DEFINE_STATIC_HOOK( GetMinRestTime ); +DEFINE_STATIC_HOOK( GetMaxRestTime ); + +DEFINE_STATIC_HOOK( AddViewKick ); + +#ifndef CLIENT_DLL +DEFINE_STATIC_HOOK( WeaponLOSCondition ); +DEFINE_STATIC_HOOK( WeaponRangeAttack1Condition ); +DEFINE_STATIC_HOOK( WeaponRangeAttack2Condition ); +DEFINE_STATIC_HOOK( WeaponMeleeAttack1Condition ); +DEFINE_STATIC_HOOK( WeaponMeleeAttack2Condition ); +#endif + +DEFINE_STATIC_HOOK( ActivityList ); + +#define DEFINE_SIMPLE_WEAPON_HOOK( name, returnType, description ) DEFINE_SIMPLE_SCRIPTHOOK( CWeaponCustomScripted::g_Hook_##name, #name, returnType, description ) +#define BEGIN_WEAPON_HOOK( name, returnType, description ) BEGIN_SCRIPTHOOK( CWeaponCustomScripted::g_Hook_##name, #name, returnType, description ) + +BEGIN_ENT_SCRIPTDESC( CWeaponCustomScripted, CBaseCombatWeapon, "Special weapon class with tons of hooks" ) + + DEFINE_SIMPLE_WEAPON_HOOK( HasAnyAmmo, FIELD_BOOLEAN, "Should return true if weapon has ammo" ) + DEFINE_SIMPLE_WEAPON_HOOK( HasPrimaryAmmo, FIELD_BOOLEAN, "Should return true if weapon has primary ammo" ) + DEFINE_SIMPLE_WEAPON_HOOK( HasSecondaryAmmo, FIELD_BOOLEAN, "Should return true if weapon has secondary ammo" ) + + DEFINE_SIMPLE_WEAPON_HOOK( CanHolster, FIELD_BOOLEAN, "Should return true if weapon can be holstered" ) + DEFINE_SIMPLE_WEAPON_HOOK( CanDeploy, FIELD_BOOLEAN, "Should return true if weapon can be deployed" ) + DEFINE_SIMPLE_WEAPON_HOOK( Deploy, FIELD_BOOLEAN, "Called when weapon is being deployed" ) + BEGIN_WEAPON_HOOK( Holster, FIELD_BOOLEAN, "Called when weapon is being holstered" ) + DEFINE_SCRIPTHOOK_PARAM( "switchingto", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + DEFINE_SIMPLE_WEAPON_HOOK( ItemPreFrame, FIELD_VOID, "Called each frame by the player PreThink" ) + DEFINE_SIMPLE_WEAPON_HOOK( ItemPostFrame, FIELD_VOID, "Called each frame by the player PostThink" ) + DEFINE_SIMPLE_WEAPON_HOOK( ItemBusyFrame, FIELD_VOID, "Called each frame by the player PostThink, if the player's not ready to attack yet" ) + DEFINE_SIMPLE_WEAPON_HOOK( ItemHolsterFrame, FIELD_VOID, "Called each frame by the player PreThink, if the weapon is holstered" ) + DEFINE_SIMPLE_WEAPON_HOOK( WeaponIdle, FIELD_VOID, "Called when no buttons pressed" ) + DEFINE_SIMPLE_WEAPON_HOOK( HandleFireOnEmpty, FIELD_VOID, "Called when they have the attack button down but they are out of ammo. The default implementation either reloads, switches weapons, or plays an empty sound." ) + + DEFINE_SIMPLE_WEAPON_HOOK( CheckReload, FIELD_VOID, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( FinishReload, FIELD_VOID, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( AbortReload, FIELD_VOID, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( Reload, FIELD_BOOLEAN, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( Reload_NPC, FIELD_VOID, "" ) + + DEFINE_SIMPLE_WEAPON_HOOK( PrimaryAttack, FIELD_VOID, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( SecondaryAttack, FIELD_VOID, "" ) + + DEFINE_SIMPLE_WEAPON_HOOK( GetPrimaryAttackActivity, FIELD_VARIANT, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetSecondaryAttackActivity, FIELD_VARIANT, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetDrawActivity, FIELD_VARIANT, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetDefaultAnimSpeed, FIELD_FLOAT, "" ) + + DEFINE_SIMPLE_WEAPON_HOOK( GetBulletSpread, FIELD_VECTOR, "" ) + BEGIN_WEAPON_HOOK( GetBulletSpreadForProficiency, FIELD_VECTOR, "Returns the bullet spread of a specific proficiency level. If this isn't defined, it will fall back to GetBulletSpread." ) + DEFINE_SCRIPTHOOK_PARAM( "proficiency", FIELD_INTEGER ) + END_SCRIPTHOOK() + DEFINE_SIMPLE_WEAPON_HOOK( GetFireRate, FIELD_FLOAT, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetMinBurst, FIELD_INTEGER, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetMaxBurst, FIELD_INTEGER, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetMinRestTime, FIELD_FLOAT, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( GetMaxRestTime, FIELD_FLOAT, "" ) + + DEFINE_SIMPLE_WEAPON_HOOK( AddViewKick, FIELD_VOID, "" ) + +#ifndef CLIENT_DLL + DEFINE_SIMPLE_WEAPON_HOOK( WeaponLOSCondition, FIELD_BOOLEAN, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( WeaponRangeAttack1Condition, FIELD_INTEGER, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( WeaponRangeAttack2Condition, FIELD_INTEGER, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( WeaponMeleeAttack1Condition, FIELD_INTEGER, "" ) + DEFINE_SIMPLE_WEAPON_HOOK( WeaponMeleeAttack2Condition, FIELD_INTEGER, "" ) +#endif + + DEFINE_SIMPLE_WEAPON_HOOK( ActivityList, FIELD_HSCRIPT, "" ) + +END_SCRIPTDESC(); + +CWeaponCustomScripted::CWeaponCustomScripted() +{ + //m_fMinRange1 = 65; + //m_fMaxRange1 = 2048; + // + //m_fMinRange2 = 256; + //m_fMaxRange2 = 1024; + // + //m_nShotsFired = 0; + //m_nVentPose = -1; + // + //m_bAltFiresUnderwater = false; +} + +bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached, ScriptVariant_t *retVal, ScriptVariant_t *pArgs ) +{ + if (!hook.CheckFuncValid(cached)) + cached = hook.CanRunInScope(m_ScriptScope); + + if (cached) + { + return hook.Call( m_ScriptScope, retVal, pArgs, false ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCustomScripted::Spawn( void ) +{ +#ifdef CLIENT_DLL + if (m_iszClientScripts[0] != '\0' && ValidateScriptScope()) + { + RunScriptFile( m_iszClientScripts ); + } +#endif + + BaseClass::Spawn(); +} + +bool CWeaponCustomScripted::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "vscripts_client" ) ) + { + Q_strcpy( m_iszClientScripts.GetForModify(), szValue ); + } + else if ( FStrEq( szKeyName, "weapondatascript_name" ) ) + { + Q_strcpy( m_iszWeaponScriptName.GetForModify(), szValue ); + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} + +bool CWeaponCustomScripted::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if ( FStrEq( szKeyName, "vscripts_client" ) ) + { + Q_snprintf( szValue, iMaxLen, "%s", m_iszClientScripts.Get() ); + return true; + } + else if ( FStrEq( szKeyName, "weapondatascript_name" ) ) + { + Q_snprintf( szValue, iMaxLen, "%s", m_iszWeaponScriptName.Get() ); + return true; + } + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#define SIMPLE_VOID_OVERRIDE( name, pArgs ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal, pArgs ) && retVal.m_bool == false) \ + return; + +#define SIMPLE_BOOL_OVERRIDE( name, pArgs ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal, pArgs ) && retVal.m_type == FIELD_BOOLEAN) \ + return retVal.m_bool; + +#define SIMPLE_FLOAT_OVERRIDE( name, pArgs ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal, pArgs ) && retVal.m_type == FIELD_FLOAT) \ + return retVal.m_float; + +#define SIMPLE_INT_OVERRIDE( name, pArgs ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal, pArgs ) && retVal.m_type == FIELD_INTEGER) \ + return retVal.m_int; + +#define SIMPLE_VECTOR_OVERRIDE( name, pArgs ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal, pArgs ) && retVal.m_type == FIELD_VECTOR) \ + return *retVal.m_pVector; + +#define SIMPLE_VECTOR_REF_OVERRIDE( name, pArgs ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal, pArgs ) && retVal.m_type == FIELD_VECTOR) \ + { \ + static Vector vec = *retVal.m_pVector; \ + return vec; \ + } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponCustomScripted::HasAnyAmmo( void ) +{ + SIMPLE_BOOL_OVERRIDE( HasAnyAmmo, NULL ); + + return BaseClass::HasAnyAmmo(); +} + +bool CWeaponCustomScripted::HasPrimaryAmmo( void ) +{ + SIMPLE_BOOL_OVERRIDE( HasPrimaryAmmo, NULL ); + + return BaseClass::HasPrimaryAmmo(); +} + +bool CWeaponCustomScripted::HasSecondaryAmmo( void ) +{ + SIMPLE_BOOL_OVERRIDE( HasSecondaryAmmo, NULL ); + + return BaseClass::HasSecondaryAmmo(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponCustomScripted::CanHolster( void ) +{ + SIMPLE_BOOL_OVERRIDE( CanHolster, NULL ); + + return BaseClass::CanHolster(); +} + +bool CWeaponCustomScripted::CanDeploy( void ) +{ + SIMPLE_BOOL_OVERRIDE( CanDeploy, NULL ); + + return BaseClass::CanDeploy(); +} + +bool CWeaponCustomScripted::Deploy( void ) +{ + SIMPLE_BOOL_OVERRIDE( Deploy, NULL ); + + return BaseClass::Deploy(); +} + +bool CWeaponCustomScripted::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + ScriptVariant_t pArgs[] = { ToHScript( pSwitchingTo ) }; + SIMPLE_BOOL_OVERRIDE( Holster, pArgs ); + + return BaseClass::Holster( pSwitchingTo ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCustomScripted::ItemPreFrame( void ) +{ + SIMPLE_VOID_OVERRIDE( ItemPreFrame, NULL ); + + BaseClass::ItemPostFrame(); +} + +void CWeaponCustomScripted::ItemPostFrame( void ) +{ + SIMPLE_VOID_OVERRIDE( ItemPostFrame, NULL ); + + BaseClass::ItemPostFrame(); +} + +void CWeaponCustomScripted::ItemBusyFrame( void ) +{ + SIMPLE_VOID_OVERRIDE( ItemBusyFrame, NULL ); + + BaseClass::ItemBusyFrame(); +} + +void CWeaponCustomScripted::ItemHolsterFrame( void ) +{ + SIMPLE_VOID_OVERRIDE( ItemHolsterFrame, NULL ); + + BaseClass::ItemHolsterFrame(); +} + +void CWeaponCustomScripted::WeaponIdle( void ) +{ + SIMPLE_VOID_OVERRIDE( WeaponIdle, NULL ); + + BaseClass::WeaponIdle(); +} + +void CWeaponCustomScripted::HandleFireOnEmpty( void ) +{ + SIMPLE_VOID_OVERRIDE( HandleFireOnEmpty, NULL ); + + BaseClass::HandleFireOnEmpty(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCustomScripted::CheckReload( void ) +{ + SIMPLE_VOID_OVERRIDE( CheckReload, NULL ); + + BaseClass::CheckReload(); +} + +void CWeaponCustomScripted::FinishReload( void ) +{ + SIMPLE_VOID_OVERRIDE( FinishReload, NULL ); + + BaseClass::FinishReload(); +} + +void CWeaponCustomScripted::AbortReload( void ) +{ + SIMPLE_VOID_OVERRIDE( AbortReload, NULL ); + + BaseClass::AbortReload(); +} + +bool CWeaponCustomScripted::Reload( void ) +{ + SIMPLE_BOOL_OVERRIDE( Reload, NULL ); + + return BaseClass::Reload(); +} + +void CWeaponCustomScripted::Reload_NPC( void ) +{ + SIMPLE_VOID_OVERRIDE( Reload_NPC, NULL ); + + BaseClass::Reload_NPC(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCustomScripted::PrimaryAttack( void ) +{ + SIMPLE_VOID_OVERRIDE( PrimaryAttack, NULL ); + + BaseClass::PrimaryAttack(); +} + +void CWeaponCustomScripted::SecondaryAttack( void ) +{ + SIMPLE_VOID_OVERRIDE( SecondaryAttack, NULL ); + + BaseClass::SecondaryAttack(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#define ACTIVITY_FUNC_OVERRIDE( name ) ScriptVariant_t retVal; \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal ) && retVal.m_bool == false) \ + { \ + if (retVal.m_type == FIELD_INTEGER) \ + { \ + Activity activity = (Activity)retVal.m_int; \ + if (activity != ACT_INVALID) \ + return (Activity)retVal.m_int; \ + } \ + else \ + { \ + Activity activity = (Activity)LookupActivity( retVal.m_pszString ); \ + if (activity != ACT_INVALID) \ + return activity; \ + } \ + } + +Activity CWeaponCustomScripted::GetPrimaryAttackActivity( void ) +{ + ACTIVITY_FUNC_OVERRIDE( GetPrimaryAttackActivity ); + + return BaseClass::GetPrimaryAttackActivity(); +} + +Activity CWeaponCustomScripted::GetSecondaryAttackActivity( void ) +{ + ACTIVITY_FUNC_OVERRIDE( GetSecondaryAttackActivity ); + + return BaseClass::GetSecondaryAttackActivity(); +} + +Activity CWeaponCustomScripted::GetDrawActivity( void ) +{ + ACTIVITY_FUNC_OVERRIDE( GetDrawActivity ); + + return BaseClass::GetDrawActivity(); +} + +float CWeaponCustomScripted::GetDefaultAnimSpeed( void ) +{ + SIMPLE_FLOAT_OVERRIDE( GetDefaultAnimSpeed, NULL ); + + return BaseClass::GetDefaultAnimSpeed(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector& CWeaponCustomScripted::GetBulletSpread( void ) +{ + SIMPLE_VECTOR_REF_OVERRIDE( GetBulletSpread, NULL ); + + // HACKHACK: Need to skip CBaseHLCombatWeapon here to recognize this overload for some reason + return CBaseCombatWeapon::GetBulletSpread(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Vector CWeaponCustomScripted::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + ScriptVariant_t pArgs[] = { (int)proficiency }; + SIMPLE_VECTOR_OVERRIDE( GetBulletSpreadForProficiency, pArgs ); + + return BaseClass::GetBulletSpread( proficiency ); +} + +float CWeaponCustomScripted::GetFireRate( void ) +{ + SIMPLE_FLOAT_OVERRIDE( GetFireRate, NULL ); + + return BaseClass::GetFireRate(); +} + +int CWeaponCustomScripted::GetMinBurst( void ) +{ + SIMPLE_INT_OVERRIDE( GetMinBurst, NULL ); + + return BaseClass::GetMinBurst(); +} + +int CWeaponCustomScripted::GetMaxBurst( void ) +{ + SIMPLE_INT_OVERRIDE( GetMaxBurst, NULL ); + + return BaseClass::GetMaxBurst(); +} + +float CWeaponCustomScripted::GetMinRestTime( void ) +{ + SIMPLE_FLOAT_OVERRIDE( GetMinRestTime, NULL ); + + return BaseClass::GetMinRestTime(); +} + +float CWeaponCustomScripted::GetMaxRestTime( void ) +{ + SIMPLE_FLOAT_OVERRIDE( GetMaxRestTime, NULL ); + + return BaseClass::GetMaxRestTime(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCustomScripted::AddViewKick( void ) +{ + SIMPLE_VOID_OVERRIDE( AddViewKick, NULL ); + + return BaseClass::AddViewKick(); +} + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponCustomScripted::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) +{ + ScriptVariant_t pArgs[] = { ownerPos, targetPos, bSetConditions }; + SIMPLE_BOOL_OVERRIDE( WeaponLOSCondition, pArgs ); + + return BaseClass::WeaponLOSCondition( ownerPos, targetPos, bSetConditions ); +} + +int CWeaponCustomScripted::WeaponRangeAttack1Condition( float flDot, float flDist ) +{ + ScriptVariant_t pArgs[] = { flDot, flDist }; + SIMPLE_INT_OVERRIDE( WeaponRangeAttack1Condition, pArgs ); + + return BaseClass::WeaponRangeAttack1Condition( flDot, flDist ); +} + +int CWeaponCustomScripted::WeaponRangeAttack2Condition( float flDot, float flDist ) +{ + ScriptVariant_t pArgs[] = { flDot, flDist }; + SIMPLE_INT_OVERRIDE( WeaponRangeAttack2Condition, pArgs ); + + return BaseClass::WeaponRangeAttack2Condition( flDot, flDist ); +} + +int CWeaponCustomScripted::WeaponMeleeAttack1Condition( float flDot, float flDist ) +{ + ScriptVariant_t pArgs[] = { flDot, flDist }; + SIMPLE_INT_OVERRIDE( WeaponMeleeAttack1Condition, pArgs ); + + return BaseClass::WeaponMeleeAttack1Condition( flDot, flDist ); +} + +int CWeaponCustomScripted::WeaponMeleeAttack2Condition( float flDot, float flDist ) +{ + ScriptVariant_t pArgs[] = { flDot, flDist }; + SIMPLE_INT_OVERRIDE( WeaponMeleeAttack2Condition, pArgs ); + + return BaseClass::WeaponMeleeAttack2Condition( flDot, flDist ); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +acttable_t *CWeaponCustomScripted::ActivityList( int &iActivityCount ) +{ + // TODO + + return BaseClass::ActivityList( iActivityCount ); +} diff --git a/mp/src/game/shared/mapbase/weapon_custom_scripted.h b/mp/src/game/shared/mapbase/weapon_custom_scripted.h new file mode 100644 index 00000000..dd9841dd --- /dev/null +++ b/mp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -0,0 +1,201 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: VScript-driven custom weapon class. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_MATH +#define VSCRIPT_FUNCS_MATH +#ifdef _WIN32 +#pragma once +#endif + +#include "basecombatweapon_shared.h" +#ifdef CLIENT_DLL +#include "vscript_client.h" +#endif + +// The base class of the scripted weapon is game-specific. +#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL) +#include "basehlcombatweapon_shared.h" +#define SCRIPTED_WEAPON_DERIVED_FROM CBaseHLCombatWeapon +#else +#define SCRIPTED_WEAPON_DERIVED_FROM CBaseCombatWeapon +#endif + +#ifdef CLIENT_DLL +#define CWeaponCustomScripted C_WeaponCustomScripted +#endif + +#define DECLARE_CACHED_HOOK(name) static ScriptHook_t g_Hook_##name; \ + HSCRIPT m_Func_##name; + +class CWeaponCustomScripted : public SCRIPTED_WEAPON_DERIVED_FROM +{ +public: + DECLARE_CLASS( CWeaponCustomScripted, SCRIPTED_WEAPON_DERIVED_FROM ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponCustomScripted(); + + bool RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached, ScriptVariant_t *retVal = NULL, ScriptVariant_t *pArgs = NULL ); + + bool KeyValue( const char *szKeyName, const char *szValue ); + bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + + // Base script has a function for this + //void Precache( void ); + + void Spawn( void ); + + bool IsPredicted( void ) const { return m_iszClientScripts[0] != '\0'; } + + const char* GetWeaponScriptName() { return m_iszWeaponScriptName[0] != '\0' ? m_iszWeaponScriptName : BaseClass::GetWeaponScriptName(); } + + // Weapon selection + bool HasAnyAmmo( void ); // Returns true is weapon has ammo + bool HasPrimaryAmmo( void ); // Returns true is weapon has ammo + bool HasSecondaryAmmo( void ); // Returns true is weapon has ammo + + bool CanHolster( void ); // returns true if the weapon can be holstered + bool CanDeploy( void ); // return true if the weapon's allowed to deploy + bool Deploy( void ); // returns true is deploy was successful + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + + // Weapon behaviour + void ItemPreFrame( void ); // called each frame by the player PreThink + void ItemPostFrame( void ); // called each frame by the player PostThink + void ItemBusyFrame( void ); // called each frame by the player PostThink, if the player's not ready to attack yet + void ItemHolsterFrame( void ); // called each frame by the player PreThink, if the weapon is holstered + void WeaponIdle( void ); // called when no buttons pressed + void HandleFireOnEmpty(); // Called when they have the attack button down + + // Reloading + void CheckReload( void ); + void FinishReload( void ); + void AbortReload( void ); + bool Reload( void ); + void Reload_NPC( void ); + + // Weapon firing + void PrimaryAttack( void ); // do "+ATTACK" + void SecondaryAttack( void ); // do "+ATTACK2" + + // Firing animations + Activity GetPrimaryAttackActivity( void ); + Activity GetSecondaryAttackActivity( void ); + Activity GetDrawActivity( void ); + float GetDefaultAnimSpeed( void ); + + // Bullet launch information + const Vector& GetBulletSpread( void ); + Vector GetBulletSpread( WeaponProficiency_t proficiency ); + float GetFireRate( void ); + int GetMinBurst(); + int GetMaxBurst(); + float GetMinRestTime(); + float GetMaxRestTime(); + + void AddViewKick( void ); + +#ifndef CLIENT_DLL + bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ); + int WeaponRangeAttack1Condition( float flDot, float flDist ); + int WeaponRangeAttack2Condition( float flDot, float flDist ); + int WeaponMeleeAttack1Condition( float flDot, float flDist ); + int WeaponMeleeAttack2Condition( float flDot, float flDist ); +#endif + + ALLOW_SCRIPT_ACCESS(); +private: + + // Weapon selection + DECLARE_CACHED_HOOK( HasAnyAmmo ); + DECLARE_CACHED_HOOK( HasPrimaryAmmo ); + DECLARE_CACHED_HOOK( HasSecondaryAmmo ); + + DECLARE_CACHED_HOOK( CanHolster ); + DECLARE_CACHED_HOOK( CanDeploy ); + DECLARE_CACHED_HOOK( Deploy ); + DECLARE_CACHED_HOOK( Holster ); + + // Weapon behaviour + DECLARE_CACHED_HOOK( ItemPreFrame ); + DECLARE_CACHED_HOOK( ItemPostFrame ); + DECLARE_CACHED_HOOK( ItemBusyFrame ); + DECLARE_CACHED_HOOK( ItemHolsterFrame ); + DECLARE_CACHED_HOOK( WeaponIdle ); + DECLARE_CACHED_HOOK( HandleFireOnEmpty ); + + // Reloading + DECLARE_CACHED_HOOK( CheckReload ); + DECLARE_CACHED_HOOK( FinishReload ); + DECLARE_CACHED_HOOK( AbortReload ); + DECLARE_CACHED_HOOK( Reload ); + DECLARE_CACHED_HOOK( Reload_NPC ); + + // Weapon firing + DECLARE_CACHED_HOOK( PrimaryAttack ); + DECLARE_CACHED_HOOK( SecondaryAttack ); + + // Firing animations + DECLARE_CACHED_HOOK( GetPrimaryAttackActivity ); + DECLARE_CACHED_HOOK( GetSecondaryAttackActivity ); + DECLARE_CACHED_HOOK( GetDrawActivity ); + DECLARE_CACHED_HOOK( GetDefaultAnimSpeed ); + + // Bullet launch information + DECLARE_CACHED_HOOK( GetBulletSpread ); + DECLARE_CACHED_HOOK( GetBulletSpreadForProficiency ); + DECLARE_CACHED_HOOK( GetFireRate ); + DECLARE_CACHED_HOOK( GetMinBurst ); + DECLARE_CACHED_HOOK( GetMaxBurst ); + DECLARE_CACHED_HOOK( GetMinRestTime ); + DECLARE_CACHED_HOOK( GetMaxRestTime ); + + DECLARE_CACHED_HOOK( AddViewKick ); + +#ifndef CLIENT_DLL + DECLARE_CACHED_HOOK( WeaponLOSCondition ); + DECLARE_CACHED_HOOK( WeaponRangeAttack1Condition ); + DECLARE_CACHED_HOOK( WeaponRangeAttack2Condition ); + DECLARE_CACHED_HOOK( WeaponMeleeAttack1Condition ); + DECLARE_CACHED_HOOK( WeaponMeleeAttack2Condition ); +#endif + + DECLARE_CACHED_HOOK( ActivityList ); + +private: + + CNetworkString( m_iszClientScripts, 256 ); + CNetworkString( m_iszWeaponScriptName, 256 ); + +protected: + + DECLARE_ACTTABLE(); + DECLARE_DATADESC(); + DECLARE_ENT_SCRIPTDESC(); +}; + +/* +class CWeaponCustomScripted1 : public CWeaponCustomScripted +{ + DECLARE_PREDICTABLE(); +}; +class CWeaponCustomScripted2 : public CWeaponCustomScripted +{ + DECLARE_PREDICTABLE(); +}; +class CWeaponCustomScripted3 : public CWeaponCustomScripted +{ + DECLARE_PREDICTABLE(); +}; +class CWeaponCustomScripted4 : public CWeaponCustomScripted +{ + DECLARE_PREDICTABLE(); +}; +*/ + +#endif diff --git a/mp/src/game/shared/multiplay_gamerules.cpp b/mp/src/game/shared/multiplay_gamerules.cpp index ae26bde6..70b495da 100644 --- a/mp/src/game/shared/multiplay_gamerules.cpp +++ b/mp/src/game/shared/multiplay_gamerules.cpp @@ -205,7 +205,8 @@ int CMultiplayRules::Damage_GetShouldNotBleed( void ) bool CMultiplayRules::Damage_IsTimeBased( int iDmgType ) { // Damage types that are time-based. - return ( ( iDmgType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) ) != 0 ); + //Tony; fixed. return Damage_GetTimeBased instead of checking them directly. + return ( ( iDmgType & Damage_GetTimeBased() ) != 0 ); } //----------------------------------------------------------------------------- @@ -369,7 +370,9 @@ ConVarRef suitcharger( "sk_suitcharger" ); if ( g_fGameOver ) // someone else quit the game already { - ChangeLevel(); // intermission is over + // Tony; wait for intermission to end + if ( m_flIntermissionEndTime && ( m_flIntermissionEndTime < gpGlobals->curtime ) ) + ChangeLevel(); // intermission is over return; } diff --git a/mp/src/game/shared/physics_shared.cpp b/mp/src/game/shared/physics_shared.cpp index ba5e0d2a..2ab9396a 100644 --- a/mp/src/game/shared/physics_shared.cpp +++ b/mp/src/game/shared/physics_shared.cpp @@ -1003,7 +1003,11 @@ void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, float ene if ( psurf->sounds.scrapeSmooth && phit->audio.roughnessFactor < psurf->audio.roughThreshold ) { soundName = psurf->sounds.scrapeSmooth; +#ifdef MAPBASE + soundHandle = &psurf->soundhandles.scrapeSmooth; +#else soundHandle = &psurf->soundhandles.scrapeRough; +#endif } const char *pSoundName = physprops->GetString( soundName ); diff --git a/mp/src/game/shared/playernet_vars.h b/mp/src/game/shared/playernet_vars.h index ac9f7766..961e0796 100644 --- a/mp/src/game/shared/playernet_vars.h +++ b/mp/src/game/shared/playernet_vars.h @@ -98,6 +98,16 @@ struct sky3dparams_t // 3d skybox camera data CNetworkVar( int, scale ); CNetworkVector( origin ); +#ifdef MAPBASE + // Skybox angle support + CNetworkQAngle( angles ); + + // Skybox entity-based option + CNetworkHandle( CBaseEntity, skycamera ); + + // Sky clearcolor + CNetworkColor32( skycolor ); +#endif CNetworkVar( int, area ); // 3d skybox fog data @@ -119,5 +129,50 @@ struct audioparams_t CNetworkHandle( CBaseEntity, ent ); // the entity setting the soundscape }; +//Tony; new tonemap information. +// In single player the values are coped directly from the single env_tonemap_controller entity. +// This will allow the controller to work as it always did. +// That way nothing in ep2 will break. With these new params, the controller can properly be used in mp. + + +// Map specific objectives, such as blowing out a wall ( and bringing in more light ) +// can still change values on a particular controller as necessary via inputs, but the +// effects will not directly affect any players who are referencing this controller +// unless the option to update on inputs is set. ( otherwise the values are simply cached +// and changes only take effect when the players controller target is changed ) + +struct tonemap_params_t +{ + DECLARE_CLASS_NOBASE( tonemap_params_t ); + DECLARE_EMBEDDED_NETWORKVAR(); + +#ifndef CLIENT_DLL + DECLARE_SIMPLE_DATADESC(); +#endif + tonemap_params_t() + { + m_flAutoExposureMin = -1.0f; + m_flAutoExposureMax = -1.0f; + m_flTonemapScale = -1.0f; + m_flBloomScale = -1.0f; + m_flTonemapRate = -1.0f; + } + //Tony; all of these are initialized to -1! + CNetworkVar( float, m_flTonemapScale ); + CNetworkVar( float, m_flTonemapRate ); + CNetworkVar( float, m_flBloomScale ); + + CNetworkVar( float, m_flAutoExposureMin ); + CNetworkVar( float, m_flAutoExposureMax ); + +// BLEND TODO +// +// //Tony; Time it takes for a blend to finish, default to 0; this is for the the effect of InputBlendTonemapScale. +// //When +// CNetworkVar( float, m_flBlendTime ); + + //Tony; these next 4 variables do not have to be networked; but I want to update them on the client whenever m_flBlendTime changes. + //TODO +}; #endif // PLAYERNET_VARS_H diff --git a/mp/src/game/shared/point_posecontroller.cpp b/mp/src/game/shared/point_posecontroller.cpp index 660d6248..43404fc8 100644 --- a/mp/src/game/shared/point_posecontroller.cpp +++ b/mp/src/game/shared/point_posecontroller.cpp @@ -326,6 +326,13 @@ void CPoseController::InputGetFMod( inputdata_t &inputdata ) m_fFModAmplitude.Get() ); } +#ifdef MAPBASE +void CPoseController::InputSetTarget( inputdata_t &inputdata ) +{ + SetPropName( inputdata.value.String() ); +} +#endif + #else //#ifndef CLIENT_DLL //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/point_posecontroller.h b/mp/src/game/shared/point_posecontroller.h index 2e11bc5e..ab8e2e83 100644 --- a/mp/src/game/shared/point_posecontroller.h +++ b/mp/src/game/shared/point_posecontroller.h @@ -73,6 +73,10 @@ public: void InputRandomizeFMod( inputdata_t &inputdata ); void InputGetFMod( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetTarget( inputdata_t &inputdata ); +#endif + private: CNetworkArray( EHANDLE, m_hProps, MAX_POSE_CONTROLLED_PROPS ); // Handles to controlled models diff --git a/mp/src/game/shared/postprocess_shared.h b/mp/src/game/shared/postprocess_shared.h new file mode 100644 index 00000000..9d3c7df0 --- /dev/null +++ b/mp/src/game/shared/postprocess_shared.h @@ -0,0 +1,54 @@ +//====== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======= +// +// Purpose: common definitions for post-processing effects +// +//============================================================================= + +#ifndef POSTPROCESS_SHARED_H +#define POSTPROCESS_SHARED_H + +#if defined( COMPILER_MSVC ) +#pragma once +#endif + +enum PostProcessParameterNames_t +{ + PPPN_FADE_TIME = 0, + PPPN_LOCAL_CONTRAST_STRENGTH, + PPPN_LOCAL_CONTRAST_EDGE_STRENGTH, + PPPN_VIGNETTE_START, + PPPN_VIGNETTE_END, + PPPN_VIGNETTE_BLUR_STRENGTH, + PPPN_FADE_TO_BLACK_STRENGTH, + PPPN_DEPTH_BLUR_FOCAL_DISTANCE, + PPPN_DEPTH_BLUR_STRENGTH, + PPPN_SCREEN_BLUR_STRENGTH, + PPPN_FILM_GRAIN_STRENGTH, + + POST_PROCESS_PARAMETER_COUNT +}; + +struct PostProcessParameters_t +{ + PostProcessParameters_t() + { + memset( m_flParameters, 0, sizeof( m_flParameters ) ); + m_flParameters[ PPPN_VIGNETTE_START ] = 0.8f; + m_flParameters[ PPPN_VIGNETTE_END ] = 1.1f; + } + + float m_flParameters[ POST_PROCESS_PARAMETER_COUNT ]; + + bool operator !=(PostProcessParameters_t other) + { + for (int i = 0; i < POST_PROCESS_PARAMETER_COUNT; ++i) + { + if (m_flParameters[i] != other.m_flParameters[i]) + return true; + } + + return false; + } +}; + +#endif // POSTPROCESS_SHARED_H \ No newline at end of file diff --git a/mp/src/game/shared/precipitation_shared.h b/mp/src/game/shared/precipitation_shared.h index 791ec29f..cb5f819c 100644 --- a/mp/src/game/shared/precipitation_shared.h +++ b/mp/src/game/shared/precipitation_shared.h @@ -18,8 +18,29 @@ enum PrecipitationType_t PRECIPITATION_TYPE_SNOW, PRECIPITATION_TYPE_ASH, PRECIPITATION_TYPE_SNOWFALL, + PRECIPITATION_TYPE_PARTICLERAIN, + PRECIPITATION_TYPE_PARTICLEASH, + PRECIPITATION_TYPE_PARTICLERAINSTORM, + PRECIPITATION_TYPE_PARTICLESNOW, NUM_PRECIPITATION_TYPES }; +// Returns true if the precipitation type involves the new particle system code +// +// NOTE: We can get away with >= PARTICLERAIN, but if you're adding any new precipitation types +// which DO NOT use the new particle system, please change this code to prevent it from being recognized +// as a particle type. +inline bool IsParticleRainType( PrecipitationType_t type ) +{ + // m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAIN || m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH + // || m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM || m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW + return type >= PRECIPITATION_TYPE_PARTICLERAIN; +} + +#ifdef MAPBASE +#define SF_PRECIP_PARTICLE_CLAMP (1 << 0) // Clamps particle types to the precipitation bounds; Mapbase uses this to compensate for the lack of blocker support. +#define SF_PRECIP_PARTICLE_NO_OUTER (1 << 1) // Suppresses the outer particle system. +#endif + #endif // PRECIPITATION_SHARED_H diff --git a/mp/src/game/shared/predicted_viewmodel.cpp b/mp/src/game/shared/predicted_viewmodel.cpp index 06691d02..53c024c2 100644 --- a/mp/src/game/shared/predicted_viewmodel.cpp +++ b/mp/src/game/shared/predicted_viewmodel.cpp @@ -6,6 +6,10 @@ #include "cbase.h" #include "predicted_viewmodel.h" +#ifdef CLIENT_DLL +#include "prediction.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -24,6 +28,7 @@ CPredictedViewModel::CPredictedViewModel() : m_LagAnglesHistory("CPredictedViewM { m_vLagAngles.Init(); m_LagAnglesHistory.Setup( &m_vLagAngles, 0 ); + m_vPredictedOffset.Init(); } #else CPredictedViewModel::CPredictedViewModel() @@ -46,26 +51,53 @@ ConVar cl_wpn_sway_scale( "cl_wpn_sway_scale", "1.0", FCVAR_CLIENTDLL|FCVAR_CHEA void CPredictedViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles ) { - #ifdef CLIENT_DLL - // Calculate our drift - Vector forward, right, up; - AngleVectors( angles, &forward, &right, &up ); - - // Add an entry to the history. - m_vLagAngles = angles; - m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat(), false ); - - // Interpolate back 100ms. - m_LagAnglesHistory.Interpolate( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() ); - - // Now take the 100ms angle difference and figure out how far the forward vector moved in local space. - Vector vLaggedForward; - QAngle angleDiff = m_vLagAngles - angles; - AngleVectors( -angleDiff, &vLaggedForward, 0, 0 ); - Vector vForwardDiff = Vector(1,0,0) - vLaggedForward; +#ifdef CLIENT_DLL +#ifdef SDK_DLL + //DM- take care of prediction first + if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) + { + origin += m_vPredictedOffset; + return; + } - // Now offset the origin using that. - vForwardDiff *= cl_wpn_sway_scale.GetFloat(); - origin += forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z; - #endif + Vector oldOrigin = origin; + BaseClass::CalcViewModelLag( origin, angles, original_angles ); + + m_vPredictedOffset = origin - oldOrigin; + + return; //kick it back off to CBaseViewModel for proper computation, + //don't perform the unnecessary checks below +#endif + float interp = cl_wpn_sway_interp.GetFloat(); + if ( !interp ) + return; + + if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) + { + origin += m_vPredictedOffset; + return; + } + + // Calculate our drift + Vector forward, right, up; + AngleVectors( angles, &forward, &right, &up ); + + // Add an entry to the history. + m_vLagAngles = angles; + m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, interp, false ); + + // Interpolate back 100ms. + m_LagAnglesHistory.Interpolate( gpGlobals->curtime, interp ); + + // Now take the 100ms angle difference and figure out how far the forward vector moved in local space. + Vector vLaggedForward; + QAngle angleDiff = m_vLagAngles - angles; + AngleVectors( -angleDiff, &vLaggedForward, 0, 0 ); + Vector vForwardDiff = Vector(1,0,0) - vLaggedForward; + + // Now offset the origin using that. + vForwardDiff *= cl_wpn_sway_scale.GetFloat(); + m_vPredictedOffset = forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z; + origin += m_vPredictedOffset; +#endif } \ No newline at end of file diff --git a/mp/src/game/shared/predicted_viewmodel.h b/mp/src/game/shared/predicted_viewmodel.h index 8e5af39c..5284d508 100644 --- a/mp/src/game/shared/predicted_viewmodel.h +++ b/mp/src/game/shared/predicted_viewmodel.h @@ -49,6 +49,7 @@ private: // This is used to lag the angles. CInterpolatedVar m_LagAnglesHistory; QAngle m_vLagAngles; + Vector m_vPredictedOffset; CPredictedViewModel( const CPredictedViewModel & ); // not defined, not accessible diff --git a/mp/src/game/shared/props_shared.h b/mp/src/game/shared/props_shared.h index 07878d82..470de3b3 100644 --- a/mp/src/game/shared/props_shared.h +++ b/mp/src/game/shared/props_shared.h @@ -32,6 +32,9 @@ #define SF_PHYSPROP_ALWAYS_PICK_UP 0x100000 // Physcannon can always pick this up, no matter what mass or constraints may apply. #define SF_PHYSPROP_NO_COLLISIONS 0x200000 // Don't enable collisions on spawn #define SF_PHYSPROP_IS_GIB 0x400000 // Limit # of active gibs +#ifdef MAPBASE +#define SF_PHYSPROP_NO_ZOMBIE_SWAT 0x800000 // Zombies are not allowed to swat this +#endif // Any barrel farther away than this is ignited rather than exploded. #define PROP_EXPLOSION_IGNITE_RADIUS 32.0f diff --git a/mp/src/game/shared/ragdoll_shared.cpp b/mp/src/game/shared/ragdoll_shared.cpp index 0fc50772..3403426b 100644 --- a/mp/src/game/shared/ragdoll_shared.cpp +++ b/mp/src/game/shared/ragdoll_shared.cpp @@ -90,9 +90,9 @@ public: if ( m_bSelfCollisions ) { char szToken[256]; - const char *pStr = nexttoken(szToken, pValue, ','); + const char *pStr = nexttoken(szToken, pValue, ',', sizeof(szToken)); int index0 = atoi(szToken); - nexttoken( szToken, pStr, ',' ); + nexttoken( szToken, pStr, ',' , sizeof(szToken) ); int index1 = atoi(szToken); m_pSet->EnableCollisions( index0, index1 ); @@ -881,6 +881,27 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next ) { +#ifdef MAPBASE + next = m_LRU.Next(i); + + CBaseAnimating *pRagdoll = m_LRU[i].Get(); + + if ( pRagdoll ) + { + IPhysicsObject *pObject = pRagdoll->VPhysicsGetObject(); + if ( pRagdoll->GetEffectEntity() || ( pObject && !pObject->IsAsleep()) ) + continue; + + // float distToPlayer = (pPlayer->GetAbsOrigin() - pRagdoll->GetAbsOrigin()).LengthSqr(); + float distToPlayer = (PlayerOrigin - pRagdoll->GetAbsOrigin()).LengthSqr(); + + if (distToPlayer > furthestDistSq) + { + furthestOne = i; + furthestDistSq = distToPlayer; + } + } +#else CBaseAnimating *pRagdoll = m_LRU[i].Get(); next = m_LRU.Next(i); @@ -899,6 +920,7 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION furthestDistSq = distToPlayer; } } +#endif else // delete bad rags first. { furthestOne = i; @@ -1011,9 +1033,19 @@ void CRagdollLRURetirement::Update( float frametime ) // Non-episodic version CBaseAnimating *pRagdoll = m_LRU[i].Get(); +#ifdef MAPBASE + if ( pRagdoll ) + { + //Just ignore it until we're done burning/dissolving. + IPhysicsObject *pObject = pRagdoll->VPhysicsGetObject(); + if ( pRagdoll->GetEffectEntity() || ( pObject && !pObject->IsAsleep()) ) + continue; + } +#else //Just ignore it until we're done burning/dissolving. if ( pRagdoll && pRagdoll->GetEffectEntity() ) continue; +#endif #ifdef CLIENT_DLL m_LRU[ i ]->SUB_Remove(); diff --git a/mp/src/game/shared/sceneentity_shared.cpp b/mp/src/game/shared/sceneentity_shared.cpp index 80fcdc71..e8cb1905 100644 --- a/mp/src/game/shared/sceneentity_shared.cpp +++ b/mp/src/game/shared/sceneentity_shared.cpp @@ -44,7 +44,11 @@ void Scene_Printf( const char *pFormat, ... ) Q_vsnprintf(msg, sizeof(msg), pFormat, marker); va_end(marker); +#ifdef MAPBASE + CGMsg( 0, CON_GROUP_CHOREO, "%8.3f[%d] %s: %s", gpGlobals->curtime, gpGlobals->tickcount, CBaseEntity::IsServer() ? "sv" : "cl", msg ); +#else Msg( "%8.3f[%d] %s: %s", gpGlobals->curtime, gpGlobals->tickcount, CBaseEntity::IsServer() ? "sv" : "cl", msg ); +#endif } //----------------------------------------------------------------------------- @@ -109,7 +113,7 @@ void CSceneTokenProcessor::Error( const char *fmt, ... ) va_end( argptr ); Warning( "%s", string ); - Assert(0); + AssertMsg(0, "%s", string); } //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/sceneentity_shared.h b/mp/src/game/shared/sceneentity_shared.h index 6293e23b..a190e53b 100644 --- a/mp/src/game/shared/sceneentity_shared.h +++ b/mp/src/game/shared/sceneentity_shared.h @@ -36,6 +36,7 @@ public: m_pEvent( 0 ), m_pScene( 0 ), m_pActor( 0 ), + m_hSceneEntity(0), m_bStarted( false ), m_iLayer( -1 ), m_iPriority( 0 ), @@ -63,6 +64,8 @@ public: // Current actor CChoreoActor *m_pActor; + CHandle< CSceneEntity > m_hSceneEntity; + // Set after the first time the event has been configured ( allows // bumping markov index only at start of event playback, not every frame ) bool m_bStarted; diff --git a/mp/src/game/shared/shareddefs.h b/mp/src/game/shared/shareddefs.h index 5236693f..3ea5074e 100644 --- a/mp/src/game/shared/shareddefs.h +++ b/mp/src/game/shared/shareddefs.h @@ -212,7 +212,14 @@ enum CastVote #define bits_SUIT_DEVICE_FLASHLIGHT 0x00000002 #define bits_SUIT_DEVICE_BREATHER 0x00000004 -#define MAX_SUIT_DEVICES 3 +#ifdef MAPBASE +// Custom suit power devices +#define bits_SUIT_DEVICE_CUSTOM0 0x00000008 +#define bits_SUIT_DEVICE_CUSTOM1 0x00000010 +#define bits_SUIT_DEVICE_CUSTOM2 0x00000020 +#endif + +#define MAX_SUIT_DEVICES 6 // Mapbase boosts this to 6 for the custom devices //=================================================================================================================== @@ -583,6 +590,7 @@ enum EFL_SETTING_UP_BONES = (1<<3), // Set while a model is setting up its bones. EFL_KEEP_ON_RECREATE_ENTITIES = (1<<4), // This is a special entity that should not be deleted when we restart entities only + //Tony; BUG?? I noticed this today while performing stealz on flag 16! look at the definition of the flag above... EFL_HAS_PLAYER_CHILD= (1<<4), // One of the child entities is a player. EFL_DIRTY_SHADOWUPDATE = (1<<5), // Client only- need shadow manager to update the shadow... @@ -604,7 +612,7 @@ enum EFL_DIRTY_ABSANGVELOCITY = (1<<13), EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS = (1<<14), EFL_DIRTY_SPATIAL_PARTITION = (1<<15), -// UNUSED = (1<<16), + EFL_PLUGIN_BASED_BOT = (1<<16), //this is set on plugin bots, so that if any games include their own bot code, they won't affect plugin bots. EFL_IN_SKYBOX = (1<<17), // This is set if the entity detects that it's in the skybox. // This forces it to pass the "in PVS" for transmission. @@ -660,6 +668,10 @@ class CBaseEntity; // Bullet firing information //----------------------------------------------------------------------------- class CBaseEntity; +#ifdef MAPBASE_VSCRIPT +// For the VScript functions in FireBUlletsInfo_t +FORWARD_DECLARE_HANDLE( HSCRIPT ); +#endif enum FireBulletsFlags_t { @@ -692,6 +704,9 @@ struct FireBulletsInfo_t #endif m_bPrimaryAttack = true; m_bUseServerRandomSeed = false; +#ifdef MAPBASE + m_pIgnoreEntList = NULL; +#endif } FireBulletsInfo_t( int nShots, const Vector &vecSrc, const Vector &vecDir, const Vector &vecSpread, float flDistance, int nAmmoType, bool bPrimaryAttack = true ) @@ -711,8 +726,15 @@ struct FireBulletsInfo_t m_flDamageForceScale = 1.0f; m_bPrimaryAttack = bPrimaryAttack; m_bUseServerRandomSeed = false; +#ifdef MAPBASE + m_pIgnoreEntList = NULL; +#endif } +#ifdef MAPBASE + ~FireBulletsInfo_t() {} +#endif + int m_iShots; Vector m_vecSrc; Vector m_vecDirShooting; @@ -728,6 +750,54 @@ struct FireBulletsInfo_t CBaseEntity *m_pAdditionalIgnoreEnt; bool m_bPrimaryAttack; bool m_bUseServerRandomSeed; +#ifdef MAPBASE + // This variable is like m_pAdditionalIgnoreEnt, but it's a list of entities instead of just one. + // Since func_tanks already use m_pAdditionalIgnoreEnt for parents, they needed another way to stop hitting their controllers. + // After much trial and error, I decided to just add more excluded entities to the bullet firing info. + // It could've just been a single entity called "m_pAdditionalIgnoreEnt2", but since these are just pointers, + // I planned ahead and made it a CUtlVector instead. + CUtlVector *m_pIgnoreEntList; +#endif + +#ifdef MAPBASE_VSCRIPT // These functions are used by VScript to expose FireBulletsInfo_t to users. + int GetShots() { return m_iShots; } + void SetShots( int value ) { m_iShots = value; } + + Vector GetSource() { return m_vecSrc; } + void SetSource( Vector value ) { m_vecSrc = value; } + Vector GetDirShooting() { return m_vecDirShooting; } + void SetDirShooting( Vector value ) { m_vecDirShooting = value; } + Vector GetSpread() { return m_vecSpread; } + void SetSpread( Vector value ) { m_vecSpread = value; } + + float GetDistance() { return m_flDistance; } + void SetDistance( float value ) { m_flDistance = value; } + + int GetAmmoType() { return m_iAmmoType; } + void SetAmmoType( int value ) { m_iAmmoType = value; } + + int GetTracerFreq() { return m_iTracerFreq; } + void SetTracerFreq( int value ) { m_iTracerFreq = value; } + + float GetDamage() { return m_flDamage; } + void SetDamage( float value ) { m_flDamage = value; } + int GetPlayerDamage() { return m_iPlayerDamage; } + void SetPlayerDamage( float value ) { m_iPlayerDamage = value; } + + int GetFlags() { return m_nFlags; } + void SetFlags( float value ) { m_nFlags = value; } + + float GetDamageForceScale() { return m_flDamageForceScale; } + void SetDamageForceScale( float value ) { m_flDamageForceScale = value; } + + HSCRIPT ScriptGetAttacker(); + void ScriptSetAttacker( HSCRIPT value ); + HSCRIPT ScriptGetAdditionalIgnoreEnt(); + void ScriptSetAdditionalIgnoreEnt( HSCRIPT value ); + + bool GetPrimaryAttack() { return m_bPrimaryAttack; } + void SetPrimaryAttack( bool value ) { m_bPrimaryAttack = value; } +#endif }; //----------------------------------------------------------------------------- @@ -905,6 +975,39 @@ enum #define COMMENTARY_BUTTONS (IN_USE) #endif +enum tprbGameInfo_e +{ + // Teamplay Roundbased Game rules shared + TPRBGAMEINFO_GAMESTATE = 1, //gets the state of the current game (waiting for players, setup, active, overtime, stalemate, roundreset) + TPRBGAMEINFO_RESERVED1, + TPRBGAMEINFO_RESERVED2, + TPRBGAMEINFO_RESERVED3, + TPRBGAMEINFO_RESERVED4, + TPRBGAMEINFO_RESERVED5, + TPRBGAMEINFO_RESERVED6, + TPRBGAMEINFO_RESERVED7, + TPRBGAMEINFO_RESERVED8, + + TPRBGAMEINFO_LASTGAMEINFO, +}; +// Mark it off so valvegame_plugin_def.h ignores it, if both headers are included in a plugin. +#define TPRBGAMEINFO_x 1 + +//Tony; (t)eam(p)lay(r)ound(b)ased gamerules -- Game Info values +#define TPRB_STATE_WAITING (1<<0) +#define TPRB_STATE_SETUP (1<<1) +#define TPRB_STATE_ACTIVE (1<<2) +#define TPRB_STATE_ROUNDWON (1<<3) +#define TPRB_STATE_OVERTIME (1<<4) +#define TPRB_STATE_STALEMATE (1<<5) +#define TPRB_STATE_ROUNDRESET (1<<6) +#define TPRB_STATE_WAITINGREADYSTART (1<<7) + +//Tony; including sdk_shareddefs.h because I use it in a _lot_ of places that needs to be seen before many other things. +#ifdef SDK_DLL +#include "sdk_shareddefs.h" +#endif + #define TEAM_TRAIN_MAX_TEAMS 4 #define TEAM_TRAIN_MAX_HILLS 5 #define TEAM_TRAIN_FLOATS_PER_HILL 2 diff --git a/mp/src/game/shared/takedamageinfo.cpp b/mp/src/game/shared/takedamageinfo.cpp index f3eb84c3..77235614 100644 --- a/mp/src/game/shared/takedamageinfo.cpp +++ b/mp/src/game/shared/takedamageinfo.cpp @@ -31,6 +31,61 @@ BEGIN_SIMPLE_DATADESC( CTakeDamageInfo ) DEFINE_FIELD( m_iDamagedOtherPlayers, FIELD_INTEGER), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CTakeDamageInfo, "Damage information handler." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetInflictor, "GetInflictor", "Gets the inflictor." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetInflictor, "SetInflictor", "Sets the inflictor." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "Gets the weapon." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetWeapon, "SetWeapon", "Sets the weapon." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttacker, "GetAttacker", "Gets the attacker." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAttacker, "SetAttacker", "Sets the attacker." ) + + DEFINE_SCRIPTFUNC( GetDamage, "Gets the damage." ) + DEFINE_SCRIPTFUNC( SetDamage, "Sets the damage." ) + DEFINE_SCRIPTFUNC( GetMaxDamage, "Gets the max damage." ) + DEFINE_SCRIPTFUNC( SetMaxDamage, "Sets the max damage." ) + DEFINE_SCRIPTFUNC( ScaleDamage, "Scales the damage." ) + DEFINE_SCRIPTFUNC( AddDamage, "Adds to the damage." ) + DEFINE_SCRIPTFUNC( SubtractDamage, "Removes from the damage." ) + DEFINE_SCRIPTFUNC( GetDamageBonus, "Gets the damage bonus." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDamageBonusProvider, "GetDamageBonusProvider", "Gets the damage bonus provider." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetDamageBonus, "SetDamageBonus", "Sets the damage bonus." ) + + DEFINE_SCRIPTFUNC( GetBaseDamage, "Gets the base damage." ) + DEFINE_SCRIPTFUNC( BaseDamageIsValid, "Checks if the base damage is valid." ) + + DEFINE_SCRIPTFUNC( GetDamageForce, "Gets the damage force." ) + DEFINE_SCRIPTFUNC( SetDamageForce, "Sets the damage force." ) + DEFINE_SCRIPTFUNC( ScaleDamageForce, "Scales the damage force." ) + + DEFINE_SCRIPTFUNC( GetDamagePosition, "Gets the damage position." ) + DEFINE_SCRIPTFUNC( SetDamagePosition, "Sets the damage position." ) + + DEFINE_SCRIPTFUNC( GetReportedPosition, "Gets the reported damage position." ) + DEFINE_SCRIPTFUNC( SetReportedPosition, "Sets the reported damage position." ) + + DEFINE_SCRIPTFUNC( GetDamageType, "Gets the damage type." ) + DEFINE_SCRIPTFUNC( SetDamageType, "Sets the damage type." ) + DEFINE_SCRIPTFUNC( AddDamageType, "Adds to the damage type." ) + DEFINE_SCRIPTFUNC( GetDamageCustom, "Gets the damage custom." ) + DEFINE_SCRIPTFUNC( SetDamageCustom, "Sets the damage custom." ) + DEFINE_SCRIPTFUNC( GetDamageStats, "Gets the damage stats." ) + DEFINE_SCRIPTFUNC( SetDamageStats, "Sets the damage stats." ) + DEFINE_SCRIPTFUNC( IsForceFriendlyFire, "Gets force friendly fire." ) + DEFINE_SCRIPTFUNC( SetForceFriendlyFire, "Sets force friendly fire." ) + + DEFINE_SCRIPTFUNC( GetAmmoType, "Gets the ammo type." ) + DEFINE_SCRIPTFUNC( SetAmmoType, "Sets the ammo type." ) + DEFINE_SCRIPTFUNC( GetAmmoName, "Gets the ammo type name." ) + + DEFINE_SCRIPTFUNC( GetPlayerPenetrationCount, "Gets the player penetration count." ) + DEFINE_SCRIPTFUNC( SetPlayerPenetrationCount, "Sets the player penetration count." ) + + DEFINE_SCRIPTFUNC( GetDamagedOtherPlayers, "Gets whether other players have been damaged." ) + DEFINE_SCRIPTFUNC( SetDamagedOtherPlayers, "Sets whether other players have been damaged." ) +END_SCRIPTDESC(); +#endif + void CTakeDamageInfo::Init( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iCustomDamage ) { m_hInflictor = pInflictor; @@ -167,6 +222,71 @@ const char *CTakeDamageInfo::GetAmmoName() const return pszAmmoType; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetInflictor() const +{ + return ToHScript( GetInflictor() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetInflictor( HSCRIPT pInflictor ) +{ + SetInflictor( ToEnt( pInflictor ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetWeapon() const +{ + return ToHScript( GetWeapon() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetWeapon( HSCRIPT pWeapon ) +{ + SetWeapon( ToEnt( pWeapon ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetAttacker() const +{ + return ToHScript( GetAttacker() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetAttacker( HSCRIPT pAttacker ) +{ + SetAttacker( ToEnt( pAttacker ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetDamageBonusProvider() const +{ + return ToHScript( GetDamageBonusProvider() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetDamageBonus( float flBonus ) +{ + m_flDamageBonus = flBonus; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetDamageBonusProvider( HSCRIPT pProvider ) +{ + m_hDamageBonusProvider = ToEnt( pProvider ); +} +#endif + // -------------------------------------------------------------------------------------------------- // // MultiDamage // Collects multiple small damages into a single damage diff --git a/mp/src/game/shared/takedamageinfo.h b/mp/src/game/shared/takedamageinfo.h index 43dfdf49..1cea7a98 100644 --- a/mp/src/game/shared/takedamageinfo.h +++ b/mp/src/game/shared/takedamageinfo.h @@ -14,6 +14,10 @@ #include "networkvar.h" // todo: change this when DECLARE_CLASS is moved into a better location. +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#endif + // Used to initialize m_flBaseDamage to something that we know pretty much for sure // hasn't been modified by a user. #define BASEDAMAGE_NOT_SPECIFIED FLT_MAX @@ -105,6 +109,19 @@ public: // For designer debug output. static void DebugGetDamageTypeString(unsigned int DamageType, char *outbuf, int outbuflength ); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetInflictor() const; + void ScriptSetInflictor( HSCRIPT pInflictor ); + HSCRIPT ScriptGetWeapon() const; + void ScriptSetWeapon( HSCRIPT pWeapon ); + HSCRIPT ScriptGetAttacker() const; + void ScriptSetAttacker( HSCRIPT pAttacker ); + + HSCRIPT ScriptGetDamageBonusProvider() const; + void ScriptSetDamageBonus( float flBonus ); + void ScriptSetDamageBonusProvider( HSCRIPT pProvider ); +#endif + //private: void CopyDamageToBaseDamage(); diff --git a/mp/src/game/shared/teamplay_round_timer.cpp b/mp/src/game/shared/teamplay_round_timer.cpp index e0739e77..60db7805 100644 --- a/mp/src/game/shared/teamplay_round_timer.cpp +++ b/mp/src/game/shared/teamplay_round_timer.cpp @@ -1371,14 +1371,14 @@ void CTeamRoundTimer::InputAddTeamTime( inputdata_t &input ) int nSeconds = 0; // get the team - p = nexttoken( token, p, ' ' ); + p = nexttoken( token, p, ' ', sizeof(token) ); if ( token[0] ) { nTeam = Q_atoi( token ); } // get the time - p = nexttoken( token, p, ' ' ); + p = nexttoken( token, p, ' ', sizeof(token) ); if ( token[0] ) { nSeconds = Q_atoi( token ); diff --git a/mp/src/game/shared/usercmd.h b/mp/src/game/shared/usercmd.h index 0005bde9..f3ff1b1b 100644 --- a/mp/src/game/shared/usercmd.h +++ b/mp/src/game/shared/usercmd.h @@ -133,6 +133,39 @@ public: impulse = 0; } +#ifdef MAPBASE_VSCRIPT // These functions are needed for exposing CUserCmd to VScript. + int GetCommandNumber() { return command_number; } + + int ScriptGetTickCount() { return tick_count; } + + const QAngle& GetViewAngles() { return viewangles; } + void SetViewAngles( const QAngle& val ) { viewangles = val; } + + float GetForwardMove() { return forwardmove; } + void SetForwardMove( float val ) { forwardmove = val; } + float GetSideMove() { return sidemove; } + void SetSideMove( float val ) { sidemove = val; } + float GetUpMove() { return upmove; } + void SetUpMove( float val ) { upmove = val; } + + int GetButtons() { return buttons; } + void SetButtons( int val ) { buttons = val; } + int GetImpulse() { return impulse; } + void SetImpulse( int val ) { impulse = val; } + + int GetWeaponSelect() { return weaponselect; } + void SetWeaponSelect( int val ) { weaponselect = val; } + int GetWeaponSubtype() { return weaponsubtype; } + void SetWeaponSubtype( int val ) { weaponsubtype = val; } + + int GetRandomSeed() { return random_seed; } + + int GetMouseX() { return mousedx; } + void SetMouseX( int val ) { mousedx = val; } + int GetMouseY() { return mousedy; } + void SetMouseY( int val ) { mousedy = val; } +#endif + // For matching server and client commands for debugging int command_number; diff --git a/mp/src/game/shared/util_shared.cpp b/mp/src/game/shared/util_shared.cpp index de7aea4e..988b2c49 100644 --- a/mp/src/game/shared/util_shared.cpp +++ b/mp/src/game/shared/util_shared.cpp @@ -219,6 +219,15 @@ bool PassServerEntityFilter( const IHandleEntity *pTouch, const IHandleEntity *p if ( !pEntTouch || !pEntPass ) return true; +#ifdef MAPBASE + // don't clip against own missiles + if ( pEntTouch->GetOwnerEntity() == pEntPass && !pEntTouch->IsSolidFlagSet(FSOLID_COLLIDE_WITH_OWNER) ) + return false; + + // don't clip against owner + if ( pEntPass->GetOwnerEntity() == pEntTouch && !pEntPass->IsSolidFlagSet(FSOLID_COLLIDE_WITH_OWNER) ) + return false; +#else // don't clip against own missiles if ( pEntTouch->GetOwnerEntity() == pEntPass ) return false; @@ -226,6 +235,7 @@ bool PassServerEntityFilter( const IHandleEntity *pTouch, const IHandleEntity *p // don't clip against owner if ( pEntPass->GetOwnerEntity() == pEntTouch ) return false; +#endif return true; @@ -986,6 +996,57 @@ void UTIL_StringToColor32( color32 *color, const char *pString ) color->a = tmp[3]; } +#ifdef MAPBASE +void UTIL_StringToFloatArray_PreserveArray( float *pVector, int count, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + Q_strncpy( tempString, pString, sizeof(tempString) ); + pstr = pfront = tempString; + + for ( j = 0; j < count; j++ ) // lifted from pr_edict.c + { + pVector[j] = atof( pfront ); + + // skip any leading whitespace + while ( *pstr && *pstr <= ' ' ) + pstr++; + + // skip to next whitespace + while ( *pstr && *pstr > ' ' ) + pstr++; + + if (!*pstr) + break; + + pstr++; + pfront = pstr; + } +} + +void UTIL_StringToIntArray_PreserveArray( int *pVector, int count, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + Q_strncpy( tempString, pString, sizeof(tempString) ); + pstr = pfront = tempString; + + for ( j = 0; j < count; j++ ) // lifted from pr_edict.c + { + pVector[j] = atoi( pfront ); + + while ( *pstr && *pstr != ' ' ) + pstr++; + if (!*pstr) + break; + pstr++; + pfront = pstr; + } +} +#endif + #ifndef _XBOX void UTIL_DecodeICE( unsigned char * buffer, int size, const unsigned char *key) { @@ -1100,6 +1161,59 @@ int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int } +static char s_NumBitsInNibble[ 16 ] = +{ + 0, // 0000 = 0 + 1, // 0001 = 1 + 1, // 0010 = 2 + 2, // 0011 = 3 + 1, // 0100 = 4 + 2, // 0101 = 5 + 2, // 0110 = 6 + 3, // 0111 = 7 + 1, // 1000 = 8 + 2, // 1001 = 9 + 2, // 1010 = 10 + 3, // 1011 = 11 + 2, // 1100 = 12 + 3, // 1101 = 13 + 3, // 1110 = 14 + 4, // 1111 = 15 +}; + +int UTIL_CountNumBitsSet( unsigned int nVar ) +{ + int nNumBits = 0; + + while ( nVar > 0 ) + { + // Look up and add in bits in the bottom nibble + nNumBits += s_NumBitsInNibble[ nVar & 0x0f ]; + + // Shift one nibble to the right + nVar >>= 4; + } + + return nNumBits; +} + +int UTIL_CountNumBitsSet( uint64 nVar ) +{ + int nNumBits = 0; + + while ( nVar > 0 ) + { + // Look up and add in bits in the bottom nibble + nNumBits += s_NumBitsInNibble[ nVar & 0x0f ]; + + // Shift one nibble to the right + nVar >>= 4; + } + + return nNumBits; +} + + int find_day_of_week( struct tm& found_day, int day_of_week, int step ) { return 0; diff --git a/mp/src/game/shared/util_shared.h b/mp/src/game/shared/util_shared.h index 83793418..549d1dc4 100644 --- a/mp/src/game/shared/util_shared.h +++ b/mp/src/game/shared/util_shared.h @@ -358,6 +358,14 @@ void UTIL_StringToIntArray( int *pVector, int count, const char *pString ); void UTIL_StringToFloatArray( float *pVector, int count, const char *pString ); void UTIL_StringToColor32( color32 *color, const char *pString ); +#ifdef MAPBASE +// Version of UTIL_StringToIntArray that doesn't set all untouched array elements to 0. +void UTIL_StringToIntArray_PreserveArray( int *pVector, int count, const char *pString ); + +// Version of UTIL_StringToFloatArray that doesn't set all untouched array elements to 0. +void UTIL_StringToFloatArray_PreserveArray( float *pVector, int count, const char *pString ); +#endif + CBasePlayer *UTIL_PlayerByIndex( int entindex ); //============================================================================= @@ -590,6 +598,22 @@ public: return (m_timestamp > 0.0f) ? m_duration : 0.0f; } + /// 1.0 for newly started, 0.0 for elapsed + float GetRemainingRatio( void ) const + { + if (HasStarted() && m_duration > 0.0f) + { + float left = GetRemainingTime() / m_duration; + if (left < 0.0f) + return 0.0f; + if (left > 1.0f) + return 1.0f; + return left; + } + + return 0.0f; + } + private: float m_duration; float m_timestamp; @@ -608,6 +632,9 @@ char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *p int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings ); +int UTIL_CountNumBitsSet( unsigned int nVar ); +int UTIL_CountNumBitsSet( uint64 nVar ); + //----------------------------------------------------------------------------- // Holidays //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/vscript_shared.cpp b/mp/src/game/shared/vscript_shared.cpp new file mode 100644 index 00000000..ebe997a8 --- /dev/null +++ b/mp/src/game/shared/vscript_shared.cpp @@ -0,0 +1,276 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_shared.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "characterset.h" +#include "isaverestore.h" +#include "gamerules.h" + +IScriptVM * g_pScriptVM; +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +#ifdef MAPBASE_VSCRIPT +// This is to ensure a dependency exists between the vscript library and the game DLLs +extern int vscript_token; +int vscript_token_hack = vscript_token; +#endif + + + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return NULL; + } + + static const char *pszExtensions[] = + { + "", // SL_NONE + ".gm", // SL_GAMEMONKEY + ".nut", // SL_SQUIRREL + ".lua", // SL_LUA + ".py", // SL_PYTHON + }; + + const char *pszVMExtension = pszExtensions[g_pScriptVM->GetLanguage()]; + const char *pszIncomingExtension = V_strrchr( pszScriptName , '.' ); + if ( pszIncomingExtension && V_strcmp( pszIncomingExtension, pszVMExtension ) != 0 ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Script file type does not match VM type\n" ); + return NULL; + } + + CFmtStr scriptPath; + if ( pszIncomingExtension ) + { + scriptPath.sprintf( "scripts/vscripts/%s", pszScriptName ); + } + else + { + scriptPath.sprintf( "scripts/vscripts/%s%s", pszScriptName, pszVMExtension ); + } + + const char *pBase; + CUtlBuffer bufferScript; + + if ( g_pScriptVM->GetLanguage() == SL_PYTHON ) + { + // python auto-loads raw or precompiled modules - don't load data here + pBase = NULL; + } + else + { + bool bResult = filesystem->ReadFile( scriptPath, "GAME", bufferScript ); + +#ifdef MAPBASE_VSCRIPT + if ( !bResult && bWarnMissing ) +#else + if( !bResult ) +#endif + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Script not found (%s) \n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + + pBase = (const char *) bufferScript.Base(); + + if ( !pBase || !*pBase ) + { + return NULL; + } + } + + + const char *pszFilename = V_strrchr( scriptPath, '/' ); + pszFilename++; + HSCRIPT hScript = g_pScriptVM->CompileScript( pBase, pszFilename ); + if ( !hScript ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "FAILED to compile and execute script file named %s\n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + return hScript; +} + +static int g_ScriptServerRunScriptDepth; + +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + if ( !pszScriptName || !*pszScriptName ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Cannot run script: NULL script name\n" ); + return false; + } + + // Prevent infinite recursion in VM + if ( g_ScriptServerRunScriptDepth > 16 ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "IncludeScript stack overflow\n" ); + return false; + } + + g_ScriptServerRunScriptDepth++; + HSCRIPT hScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + bool bSuccess = false; + if ( hScript ) + { +#ifdef GAME_DLL + if ( gpGlobals->maxClients == 1 ) + { + CBaseEntity *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer ) + { + g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); + } + } +#endif + bSuccess = ( g_pScriptVM->Run( hScript, hScope ) != SCRIPT_ERROR ); + if ( !bSuccess ) + { + Warning( "Error running script named %s\n", pszScriptName ); + Assert( "Error running script" ); + } + } + g_ScriptServerRunScriptDepth--; + return bSuccess; +} + +#ifdef CLIENT_DLL +CON_COMMAND( script_client, "Run the text as a script" ) +#else +CON_COMMAND( script, "Run the text as a script" ) +#endif +{ + if ( !*args[1] ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "No function name specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + + const char *pszScript = args.GetCommandString(); + +#ifdef CLIENT_DLL + pszScript += 13; +#else + pszScript += 6; +#endif + + while ( *pszScript == ' ' ) + { + pszScript++; + } + + if ( !*pszScript ) + { + return; + } + + if ( *pszScript != '\"' ) + { + g_pScriptVM->Run( pszScript ); + } + else + { + pszScript++; + const char *pszEndQuote = pszScript; + while ( *pszEndQuote != '\"' ) + { + pszEndQuote++; + } + if ( !*pszEndQuote ) + { + return; + } + *((char *)pszEndQuote) = 0; + g_pScriptVM->Run( pszScript ); + *((char *)pszEndQuote) = '\"'; + } +} + + +CON_COMMAND_SHARED( script_execute, "Run a vscript file" ) +{ + if ( !*args[1] ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "No script specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + + VScriptRunScript( args[1], true ); +} + +CON_COMMAND_SHARED( script_debug, "Connect the vscript VM to the script debugger" ) +{ + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + g_pScriptVM->ConnectDebugger(); +} + +CON_COMMAND_SHARED( script_help, "Output help for script functions, optionally with a search string" ) +{ + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + const char *pszArg1 = "*"; + if ( *args[1] ) + { + pszArg1 = args[1]; + } + + g_pScriptVM->Run( CFmtStr( "PrintHelp( \"%s\" );", pszArg1 ) ); +} + +CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) +{ + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); + return; + } + g_pScriptVM->DumpState(); +} diff --git a/mp/src/game/shared/vscript_shared.h b/mp/src/game/shared/vscript_shared.h new file mode 100644 index 00000000..a71d20b2 --- /dev/null +++ b/mp/src/game/shared/vscript_shared.h @@ -0,0 +1,39 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SHARED_H +#define VSCRIPT_SHARED_H + +#include "vscript/ivscript.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM * g_pScriptVM; + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing = false ); +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = false ) { return VScriptRunScript( pszScriptName, NULL, bWarnMissing ); } + +#define DECLARE_ENT_SCRIPTDESC() ALLOW_SCRIPT_ACCESS(); virtual ScriptClassDesc_t *GetScriptDesc() + +#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description ) +#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) + +#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc( className * ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); } + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#ifdef MAPBASE_VSCRIPT +void RegisterSharedScriptConstants(); +void RegisterSharedScriptFunctions(); +#endif + +#endif // VSCRIPT_SHARED_H diff --git a/mp/src/game/shared/weapon_parse.cpp b/mp/src/game/shared/weapon_parse.cpp index c42ad7f1..31ff154a 100644 --- a/mp/src/game/shared/weapon_parse.cpp +++ b/mp/src/game/shared/weapon_parse.cpp @@ -76,10 +76,12 @@ extern itemFlags_t g_ItemFlags[8]; static CUtlDict< FileWeaponInfo_t*, unsigned short > m_WeaponInfoDatabase; +#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. #ifdef _DEBUG // used to track whether or not two weapons have been mistakenly assigned the wrong slot bool g_bUsedWeaponSlots[MAX_WEAPON_SLOTS][MAX_WEAPON_POSITIONS] = { { false } }; +#endif #endif //----------------------------------------------------------------------------- @@ -154,9 +156,11 @@ void ResetFileWeaponInfoDatabase( void ) } m_WeaponInfoDatabase.RemoveAll(); +#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. #ifdef _DEBUG memset(g_bUsedWeaponSlots, 0, sizeof(g_bUsedWeaponSlots)); #endif +#endif } #endif @@ -279,7 +283,11 @@ bool ReadWeaponDataFromFileForSlot( IFileSystem* filesystem, const char *szWeapo FileWeaponInfo_t *pFileInfo = GetFileWeaponInfoFromHandle( *phandle ); Assert( pFileInfo ); +#ifdef MAPBASE + if ( pFileInfo->bParsedScript && !pFileInfo->bCustom ) +#else if ( pFileInfo->bParsedScript ) +#endif return true; char sz[128]; @@ -296,6 +304,9 @@ bool ReadWeaponDataFromFileForSlot( IFileSystem* filesystem, const char *szWeapo if ( !pKV ) return false; +#ifdef MAPBASE + pFileInfo->bCustom = false; +#endif pFileInfo->Parse( pKV, szWeaponName ); pKV->deleteThis(); @@ -303,6 +314,49 @@ bool ReadWeaponDataFromFileForSlot( IFileSystem* filesystem, const char *szWeapo return true; } +#ifdef MAPBASE +extern const char *g_MapName; + +bool ReadCustomWeaponDataFromFileForSlot( IFileSystem* filesystem, const char *szWeaponName, WEAPON_FILE_INFO_HANDLE *phandle, const unsigned char *pICEKey ) +{ + if ( !phandle ) + { + Assert( 0 ); + return false; + } + + *phandle = FindWeaponInfoSlot( szWeaponName ); + FileWeaponInfo_t *pFileInfo = GetFileWeaponInfoFromHandle( *phandle ); + Assert( pFileInfo ); + + // Just parse the custom script anyway even if it was already loaded. This is because after one is loaded, + // there's no way of distinguishing between maps with no custom scripts and maps with their own new custom scripts. + //if ( pFileInfo->bParsedScript && pFileInfo->bCustom ) + // return true; + + char sz[128]; + Q_snprintf( sz, sizeof( sz ), "maps/%s_%s", g_MapName, szWeaponName ); + + KeyValues *pKV = ReadEncryptedKVFile( filesystem, sz, pICEKey, +#if defined( DOD_DLL ) + true // Only read .ctx files! +#else + false +#endif + ); + + if ( !pKV ) + return false; + + pFileInfo->bCustom = true; + pFileInfo->Parse( pKV, szWeaponName ); + + pKV->deleteThis(); + + return true; +} +#endif + //----------------------------------------------------------------------------- // FileWeaponInfo_t implementation. @@ -412,6 +466,7 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam m_bAllowFlipping = ( pKeyValuesData->GetInt( "AllowFlipping", 1 ) != 0 ) ? true : false; m_bMeleeWeapon = ( pKeyValuesData->GetInt( "MeleeWeapon", 0 ) != 0 ) ? true : false; +#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. #if defined(_DEBUG) && defined(HL2_CLIENT_DLL) // make sure two weapons aren't in the same slot & position if ( iSlot >= MAX_WEAPON_SLOTS || @@ -428,6 +483,7 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam } g_bUsedWeaponSlots[iSlot][iPosition] = true; } +#endif #endif // Primary ammo used diff --git a/mp/src/game/shared/weapon_parse.h b/mp/src/game/shared/weapon_parse.h index f1d4c924..56803dfa 100644 --- a/mp/src/game/shared/weapon_parse.h +++ b/mp/src/game/shared/weapon_parse.h @@ -77,6 +77,10 @@ public: public: bool bParsedScript; bool bLoadedHudElements; +#ifdef MAPBASE + // Indicates the currently loaded data is from a map-specific script and should be flushed. + bool bCustom; +#endif // SHARED char szClassName[MAX_WEAPON_STRING]; @@ -136,6 +140,12 @@ public: bool ReadWeaponDataFromFileForSlot( IFileSystem* filesystem, const char *szWeaponName, WEAPON_FILE_INFO_HANDLE *phandle, const unsigned char *pICEKey = NULL ); +#ifdef MAPBASE +// For map-specific weapon data +bool ReadCustomWeaponDataFromFileForSlot( IFileSystem* filesystem, const char *szWeaponName, + WEAPON_FILE_INFO_HANDLE *phandle, const unsigned char *pICEKey = NULL ); +#endif + // If weapon info has been loaded for the specified class name, this returns it. WEAPON_FILE_INFO_HANDLE LookupWeaponInfoSlot( const char *name ); diff --git a/mp/src/game/shared/weapon_proficiency.h b/mp/src/game/shared/weapon_proficiency.h index 6532b79c..e91f67cf 100644 --- a/mp/src/game/shared/weapon_proficiency.h +++ b/mp/src/game/shared/weapon_proficiency.h @@ -19,6 +19,11 @@ struct WeaponProficiencyInfo_t enum WeaponProficiency_t { +#ifdef MAPBASE + // For the override + WEAPON_PROFICIENCY_INVALID = -1, +#endif + WEAPON_PROFICIENCY_POOR = 0, WEAPON_PROFICIENCY_AVERAGE, WEAPON_PROFICIENCY_GOOD, diff --git a/mp/src/lib/public/fgdlib.lib b/mp/src/lib/public/fgdlib.lib index d4fc94ff9e13eae7760500c72c03ed6ed5ba5443..ad59f66a4b87905aa198f144669ea440bf2f6eaa 100644 GIT binary patch delta 746966 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«|f!2u|9kkCcaL3`rANBO;N> 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%0A=WZ2~tBO)<$`7ZY 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 h>r64YuRSdJY8r7oy<=Jj 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

XDUE&S3zIdrj}R4e%O;U|kP&0JiHdah-VK?i`Nf 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 z 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 H)7AeEi&IHi diff --git a/mp/src/lib/public/tier1.lib b/mp/src/lib/public/tier1.lib index 8f62b04e64f9ca56d906180eb3a3c19a333c5599..efd1caa9079a5bce9de4c11861985b640ec61684 100644 GIT binary patch delta 847762 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%0AHi>ZQ3B~CGhsn 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 ^l! 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àL|2XWbPqw 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

>izgmcd*`Gv>-! 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�b}dZuVQg1$V_KigQ8aDrHy85vxXVE!O*UzLCGja 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𝔫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ðE|F4|GoK=e2_&CUW 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 diff --git a/mp/src/materialsystem/stdshaders/BaseVSShader.cpp b/mp/src/materialsystem/stdshaders/BaseVSShader.cpp index fdaeb563..408e0b91 100644 --- a/mp/src/materialsystem/stdshaders/BaseVSShader.cpp +++ b/mp/src/materialsystem/stdshaders/BaseVSShader.cpp @@ -20,28 +20,30 @@ #endif #if SUPPORT_DX8 -#include "lightmappedgeneric_flashlight_vs11.inc" -#include "flashlight_ps11.inc" +#include "SDK_lightmappedgeneric_flashlight_vs11.inc" +#include "SDK_flashlight_ps11.inc" #endif #ifdef STDSHADER_DX9_DLL_EXPORT -#include "lightmappedgeneric_flashlight_vs20.inc" +#include "SDK_lightmappedgeneric_flashlight_vs20.inc" +#include "SDK_lightmappedgeneric_flashlight_vs30.inc" #endif #ifdef STDSHADER_DX9_DLL_EXPORT -#include "flashlight_ps20.inc" -#include "flashlight_ps20b.inc" +#include "SDK_flashlight_ps20.inc" +#include "SDK_flashlight_ps20b.inc" +#include "SDK_flashlight_ps30.inc" #endif -#include "unlitgeneric_vs11.inc" -#include "VertexLitGeneric_EnvmappedBumpmap_NoLighting_ps14.inc" -#include "VertexLitGeneric_EnvmappedBumpmap_NoLighting.inc" -#include "vertexlitgeneric_flashlight_vs11.inc" -#include "LightmappedGeneric_BaseTexture.inc" -#include "LightmappedGeneric_BumpmappedLightmap_Base_ps14.inc" -#include "LightmappedGeneric_BumpmappedLightmap_Blend_ps14.inc" -#include "lightmappedgeneric_bumpmappedenvmap_ps14.inc" -#include "lightmappedgeneric_bumpmappedenvmap.inc" -#include "lightmappedgeneric_basetextureblend.inc" -#include "lightmappedgeneric_bumpmappedlightmap.inc" +#include "SDK_unlitgeneric_vs11.inc" +#include "SDK_VertexLitGeneric_EnvmappedBumpmap_NoLighting_ps14.inc" +#include "SDK_VertexLitGeneric_EnvmappedBumpmap_NoLighting.inc" +#include "SDK_vertexlitgeneric_flashlight_vs11.inc" +#include "SDK_LightmappedGeneric_BaseTexture.inc" +#include "SDK_LightmappedGeneric_BumpmappedLightmap_Base_ps14.inc" +#include "SDK_LightmappedGeneric_BumpmappedLightmap_Blend_ps14.inc" +#include "SDK_lightmappedgeneric_bumpmappedenvmap_ps14.inc" +#include "SDK_lightmappedgeneric_bumpmappedenvmap.inc" +#include "SDK_lightmappedgeneric_basetextureblend.inc" +#include "SDK_lightmappedgeneric_bumpmappedlightmap.inc" #endif // GAME_SHADER_DLL // memdbgon must be the last include file in a .cpp file!!! @@ -49,6 +51,13 @@ static ConVar mat_fullbright( "mat_fullbright","0", FCVAR_CHEAT ); +// NOTE: This is externed in BaseVSShader.h so it needs to be here +ConVar r_flashlightbrightness( "r_flashlightbrightness", "0.25", FCVAR_CHEAT ); + +#ifdef MAPBASE +ConVar mat_specular_disable_on_missing( "mat_specular_disable_on_missing", "1", FCVAR_ARCHIVE, "Disables specular reflections on a material when the envmap cannot be found." ); +#endif + // These functions are to be called from the shaders. //----------------------------------------------------------------------------- @@ -981,7 +990,7 @@ void CBaseVSShader::VertexShaderUnlitGenericPass( int baseTextureVar, int frameV s_pShaderShadow->SetPixelShader( pshName ); // Compute the vertex shader index. - unlitgeneric_vs11_Static_Index vshIndex; + sdk_unlitgeneric_vs11_Static_Index vshIndex; vshIndex.SetDETAIL( bDetail ); vshIndex.SetENVMAP( bEnvmap ); vshIndex.SetENVMAPCAMERASPACE( bEnvmap && bEnvmapCameraSpace ); @@ -1063,7 +1072,7 @@ void CBaseVSShader::VertexShaderUnlitGenericPass( int baseTextureVar, int frameV s_pShaderAPI->SetPixelShaderConstant( 0, flConsts, 3 ); // Compute the vertex shader index. - unlitgeneric_vs11_Dynamic_Index vshIndex; + sdk_unlitgeneric_vs11_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); vshIndex.SetSKINNING( s_pShaderAPI->GetCurrentNumBones() > 0 ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); @@ -1083,7 +1092,7 @@ void CBaseVSShader::DrawWorldBaseTexture( int baseTextureVar, int baseTextureTra VERTEX_POSITION, 1, 0, 0 ); s_pShaderShadow->SetPixelShader( "LightmappedGeneric_BaseTexture" ); SetNormalBlendingShadowState(); - lightmappedgeneric_basetexture_Static_Index vshIndex; + sdk_lightmappedgeneric_basetexture_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "LightmappedGeneric_BaseTexture", vshIndex.GetIndex() ); FogToOOOverbright(); @@ -1102,7 +1111,7 @@ void CBaseVSShader::DrawWorldBaseTexture( int baseTextureVar, int baseTextureTra } SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, baseTextureTransformVar ); SetColorPixelShaderConstant( 0, colorVar, alphaVar ); - lightmappedgeneric_basetexture_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_basetexture_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } @@ -1126,7 +1135,7 @@ void CBaseVSShader::DrawWorldBumpedDiffuseLighting( int bumpmapVar, int bumpFram } s_pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 3, 0, 0 ); - lightmappedgeneric_bumpmappedlightmap_Static_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedlightmap_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "LightmappedGeneric_BumpmappedLightmap", vshIndex.GetIndex() ); if ( bSSBump ) @@ -1150,7 +1159,7 @@ void CBaseVSShader::DrawWorldBumpedDiffuseLighting( int bumpmapVar, int bumpFram SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, bumpTransformVar ); SetModulationPixelShaderDynamicState( 3 ); - lightmappedgeneric_bumpmappedlightmap_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedlightmap_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } @@ -1170,7 +1179,7 @@ void CBaseVSShader::DrawWorldBumpedDiffuseLighting_Base_ps14( int bumpmapVar, in s_pShaderShadow->EnableTexture( SHADER_SAMPLER4, true ); s_pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 3, 0, 0 ); - lightmappedgeneric_bumpmappedlightmap_base_ps14_Static_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedlightmap_base_ps14_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "LightmappedGeneric_BumpmappedLightmap_Base_ps14", vshIndex.GetIndex() ); s_pShaderShadow->SetPixelShader( "LightmappedGeneric_BumpmappedLightmap_Base_ps14" ); @@ -1193,7 +1202,7 @@ void CBaseVSShader::DrawWorldBumpedDiffuseLighting_Base_ps14( int bumpmapVar, in SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, baseTextureTransformVar ); SetModulationPixelShaderDynamicState( 3 ); - lightmappedgeneric_bumpmappedlightmap_base_ps14_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedlightmap_base_ps14_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } @@ -1217,7 +1226,7 @@ void CBaseVSShader::DrawWorldBumpedDiffuseLighting_Blend_ps14( int bumpmapVar, i s_pShaderShadow->EnableTexture( SHADER_SAMPLER5, true ); s_pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 3, 0, 0 ); - lightmappedgeneric_bumpmappedlightmap_blend_ps14_Static_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedlightmap_blend_ps14_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "LightmappedGeneric_BumpmappedLightmap_Blend_ps14", vshIndex.GetIndex() ); s_pShaderShadow->SetPixelShader( "LightmappedGeneric_BumpmappedLightmap_Blend_ps14" ); @@ -1242,7 +1251,7 @@ void CBaseVSShader::DrawWorldBumpedDiffuseLighting_Blend_ps14( int bumpmapVar, i SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, baseTextureTransform2Var ); SetModulationPixelShaderDynamicState( 3 ); - lightmappedgeneric_bumpmappedlightmap_blend_ps14_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedlightmap_blend_ps14_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } @@ -1288,7 +1297,7 @@ void CBaseVSShader::DrawWorldBumpedSpecularLighting( int bumpmapVar, int envmapV if( g_pHardwareConfig->SupportsPixelShaders_1_4() ) { - lightmappedgeneric_bumpmappedenvmap_ps14_Static_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedenvmap_ps14_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "LightmappedGeneric_BumpmappedEnvmap_ps14", vshIndex.GetIndex() ); int nPshIndex = bHasNormalMapAlphaEnvMapMask ? 1 : 0; @@ -1296,7 +1305,7 @@ void CBaseVSShader::DrawWorldBumpedSpecularLighting( int bumpmapVar, int envmapV } else { - lightmappedgeneric_bumpmappedenvmap_Static_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedenvmap_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "LightmappedGeneric_BumpmappedEnvmap", vshIndex.GetIndex() ); int nPshIndex = bHasNormalMapAlphaEnvMapMask ? 1 : 0; @@ -1315,13 +1324,13 @@ void CBaseVSShader::DrawWorldBumpedSpecularLighting( int bumpmapVar, int envmapV { s_pShaderAPI->BindStandardTexture( SHADER_SAMPLER4, TEXTURE_NORMALIZATION_CUBEMAP ); - lightmappedgeneric_bumpmappedenvmap_ps14_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedenvmap_ps14_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } else { - lightmappedgeneric_bumpmappedenvmap_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_bumpmappedenvmap_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } @@ -1389,7 +1398,7 @@ void CBaseVSShader::DrawModelBumpedSpecularLighting( int bumpMapVar, int bumpMap if( g_pHardwareConfig->SupportsPixelShaders_1_4() ) { - vertexlitgeneric_envmappedbumpmap_nolighting_ps14_Static_Index vshIndex; + sdk_vertexlitgeneric_envmappedbumpmap_nolighting_ps14_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "VertexLitGeneric_EnvmappedBumpmap_NoLighting_ps14", vshIndex.GetIndex() ); if( bHasNormalMapAlphaEnvMapMask ) { @@ -1402,7 +1411,7 @@ void CBaseVSShader::DrawModelBumpedSpecularLighting( int bumpMapVar, int bumpMap } else { - vertexlitgeneric_envmappedbumpmap_nolighting_Static_Index vshIndex; + sdk_vertexlitgeneric_envmappedbumpmap_nolighting_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "VertexLitGeneric_EnvmappedBumpmap_NoLighting", vshIndex.GetIndex() ); // This version does not multiply by lighting // NOTE: We don't support multiplying by lighting for bumped specular stuff. @@ -1446,14 +1455,14 @@ void CBaseVSShader::DrawModelBumpedSpecularLighting( int bumpMapVar, int bumpMap if( g_pHardwareConfig->SupportsPixelShaders_1_4() ) { - vertexlitgeneric_envmappedbumpmap_nolighting_ps14_Dynamic_Index vshIndex; + sdk_vertexlitgeneric_envmappedbumpmap_nolighting_ps14_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); vshIndex.SetSKINNING( s_pShaderAPI->GetCurrentNumBones() > 0 ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } else { - vertexlitgeneric_envmappedbumpmap_nolighting_Dynamic_Index vshIndex; + sdk_vertexlitgeneric_envmappedbumpmap_nolighting_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); vshIndex.SetSKINNING( s_pShaderAPI->GetCurrentNumBones() > 0 ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); @@ -1479,7 +1488,7 @@ void CBaseVSShader::DrawBaseTextureBlend( int baseTextureVar, int baseTextureTra s_pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 2, 0, 0 ); - lightmappedgeneric_basetextureblend_Static_Index vshIndex; + sdk_lightmappedgeneric_basetextureblend_Static_Index vshIndex; s_pShaderShadow->SetVertexShader( "lightmappedgeneric_basetextureblend", vshIndex.GetIndex() ); s_pShaderShadow->SetPixelShader( "lightmappedgeneric_basetextureblend", 0 ); @@ -1505,7 +1514,7 @@ void CBaseVSShader::DrawBaseTextureBlend( int baseTextureVar, int baseTextureTra SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, baseTextureTransformVar ); SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, baseTextureTransform2Var ); SetColorPixelShaderConstant( 0, colorVar, alphaVar ); - lightmappedgeneric_basetextureblend_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_basetextureblend_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( s_pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); s_pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } @@ -1631,6 +1640,7 @@ void CBaseVSShader::SetFlashlightVertexShaderConstants( bool bBump, int bumpTran atten[1] = flashlightState.m_fLinearAtten; atten[2] = flashlightState.m_fQuadraticAtten; atten[3] = flashlightState.m_FarZ; + /*atten[3] = flashlightState.m_FarZAtten;*/ s_pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_5, atten, 1 ); if ( bDetail ) @@ -1703,7 +1713,7 @@ void CBaseVSShader::DrawFlashlight_dx80( IMaterialVar** params, IShaderDynamicAP if( bLightmappedGeneric ) { bool bUsingVertexColor = IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR ); - lightmappedgeneric_flashlight_vs11_Static_Index vshIndex; + sdk_lightmappedgeneric_flashlight_vs11_Static_Index vshIndex; vshIndex.SetNORMALMAP( bBump ); vshIndex.SetWORLDVERTEXTRANSITION( bWorldVertexTransition ); vshIndex.SetVERTEXCOLOR( bUsingVertexColor ); @@ -1722,7 +1732,7 @@ void CBaseVSShader::DrawFlashlight_dx80( IMaterialVar** params, IShaderDynamicAP } else { - vertexlitgeneric_flashlight_vs11_Static_Index vshIndex; + sdk_vertexlitgeneric_flashlight_vs11_Static_Index vshIndex; vshIndex.SetTEETH( bTeeth ); pShaderShadow->SetVertexShader( "vertexlitgeneric_flashlight_vs11", vshIndex.GetIndex() ); @@ -1732,7 +1742,7 @@ void CBaseVSShader::DrawFlashlight_dx80( IMaterialVar** params, IShaderDynamicAP bool bNoCull = IS_FLAG_SET( MATERIAL_VAR_NOCULL ); - flashlight_ps11_Static_Index pshIndex; + sdk_flashlight_ps11_Static_Index pshIndex; pshIndex.SetNORMALMAP( bBump ); pshIndex.SetNOCULL( bNoCull ); pShaderShadow->SetPixelShader( "flashlight_ps11", pshIndex.GetIndex() ); @@ -1774,13 +1784,13 @@ void CBaseVSShader::DrawFlashlight_dx80( IMaterialVar** params, IShaderDynamicAP if( bLightmappedGeneric ) { - lightmappedgeneric_flashlight_vs11_Dynamic_Index vshIndex; + sdk_lightmappedgeneric_flashlight_vs11_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } else { - vertexlitgeneric_flashlight_vs11_Dynamic_Index vshIndex; + sdk_vertexlitgeneric_flashlight_vs11_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); if( bTeeth ) @@ -1797,7 +1807,7 @@ void CBaseVSShader::DrawFlashlight_dx80( IMaterialVar** params, IShaderDynamicAP pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } - flashlight_ps11_Dynamic_Index pshIndex; + sdk_flashlight_ps11_Dynamic_Index pshIndex; pShaderAPI->SetPixelShaderIndex( pshIndex.GetIndex() ); SetFlashlightVertexShaderConstants( bBump, bumpTransform, false, -1, true ); @@ -1885,12 +1895,24 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP if( vars.m_bLightmappedGeneric ) { - lightmappedgeneric_flashlight_vs20_Static_Index vshIndex; - vshIndex.SetWORLDVERTEXTRANSITION( vars.m_bWorldVertexTransition ); - vshIndex.SetNORMALMAP( vars.m_bBump ); - vshIndex.SetSEAMLESS( bSeamless ); - vshIndex.SetDETAIL( bDetail ); - pShaderShadow->SetVertexShader( "lightmappedgeneric_flashlight_vs20", vshIndex.GetIndex() ); + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + sdk_lightmappedgeneric_flashlight_vs30_Static_Index vshIndex; + vshIndex.SetWORLDVERTEXTRANSITION( vars.m_bWorldVertexTransition ); + vshIndex.SetNORMALMAP( vars.m_bBump ); + vshIndex.SetSEAMLESS( bSeamless ); + vshIndex.SetDETAIL( bDetail ); + pShaderShadow->SetVertexShader( "sdk_lightmappedgeneric_flashlight_vs30", vshIndex.GetIndex() ); + } + else + { + sdk_lightmappedgeneric_flashlight_vs20_Static_Index vshIndex; + vshIndex.SetWORLDVERTEXTRANSITION( vars.m_bWorldVertexTransition ); + vshIndex.SetNORMALMAP( vars.m_bBump ); + vshIndex.SetSEAMLESS( bSeamless ); + vshIndex.SetDETAIL( bDetail ); + pShaderShadow->SetVertexShader( "sdk_lightmappedgeneric_flashlight_vs20", vshIndex.GetIndex() ); + } unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL; if( vars.m_bBump ) @@ -1907,9 +1929,9 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP } else { - vertexlitgeneric_flashlight_vs11_Static_Index vshIndex; + sdk_vertexlitgeneric_flashlight_vs11_Static_Index vshIndex; vshIndex.SetTEETH( vars.m_bTeeth ); - pShaderShadow->SetVertexShader( "vertexlitgeneric_flashlight_vs11", vshIndex.GetIndex() ); + pShaderShadow->SetVertexShader( "sdk_vertexlitgeneric_flashlight_vs11", vshIndex.GetIndex() ); unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL; int numTexCoords = 1; @@ -1921,11 +1943,12 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP { nBumpMapVariant = ( vars.m_bSSBump ) ? 2 : 1; } - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + + if ( g_pHardwareConfig->SupportsShaderModel_3_0() && vars.m_bLightmappedGeneric ) { int nShadowFilterMode = g_pHardwareConfig->GetShadowFilterMode(); - flashlight_ps20b_Static_Index pshIndex; + sdk_flashlight_ps30_Static_Index pshIndex; pshIndex.SetNORMALMAP( nBumpMapVariant ); pshIndex.SetNORMALMAP2( bBump2 ); pshIndex.SetWORLDVERTEXTRANSITION( vars.m_bWorldVertexTransition ); @@ -1933,18 +1956,32 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP pshIndex.SetDETAILTEXTURE( bDetail ); pshIndex.SetDETAIL_BLEND_MODE( nDetailBlendMode ); pshIndex.SetFLASHLIGHTDEPTHFILTERMODE( nShadowFilterMode ); - pShaderShadow->SetPixelShader( "flashlight_ps20b", pshIndex.GetIndex() ); + pShaderShadow->SetPixelShader( "sdk_flashlight_ps30", pshIndex.GetIndex() ); } - else + else if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - flashlight_ps20_Static_Index pshIndex; + int nShadowFilterMode = g_pHardwareConfig->GetShadowFilterMode(); + + sdk_flashlight_ps20b_Static_Index pshIndex; pshIndex.SetNORMALMAP( nBumpMapVariant ); pshIndex.SetNORMALMAP2( bBump2 ); pshIndex.SetWORLDVERTEXTRANSITION( vars.m_bWorldVertexTransition ); pshIndex.SetSEAMLESS( bSeamless ); pshIndex.SetDETAILTEXTURE( bDetail ); pshIndex.SetDETAIL_BLEND_MODE( nDetailBlendMode ); - pShaderShadow->SetPixelShader( "flashlight_ps20", pshIndex.GetIndex() ); + pshIndex.SetFLASHLIGHTDEPTHFILTERMODE( nShadowFilterMode ); + pShaderShadow->SetPixelShader( "sdk_flashlight_ps20b", pshIndex.GetIndex() ); + } + else + { + sdk_flashlight_ps20_Static_Index pshIndex; + pshIndex.SetNORMALMAP( nBumpMapVariant ); + pshIndex.SetNORMALMAP2( bBump2 ); + pshIndex.SetWORLDVERTEXTRANSITION( vars.m_bWorldVertexTransition ); + pshIndex.SetSEAMLESS( bSeamless ); + pshIndex.SetDETAILTEXTURE( bDetail ); + pshIndex.SetDETAIL_BLEND_MODE( nDetailBlendMode ); + pShaderShadow->SetPixelShader( "sdk_flashlight_ps20", pshIndex.GetIndex() ); } FogToBlack(); } @@ -1954,6 +1991,20 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP ITexture *pFlashlightDepthTexture; FlashlightState_t flashlightState = pShaderAPI->GetFlashlightStateEx( worldToTexture, &pFlashlightDepthTexture ); + if ( pFlashlightDepthTexture == NULL ) + { + const int iFlashlightShadowIndex = ( flashlightState.m_nShadowQuality >> 16 ) - 1; + + if ( iFlashlightShadowIndex >= 0 + && iFlashlightShadowIndex <= ( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_LAST - INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST ) ) + { + pFlashlightDepthTexture = (ITexture*)pShaderAPI->GetIntRenderingParameter( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST + iFlashlightShadowIndex ); + } + } + + float flFlashlightPos[4] = { XYZ( flashlightState.m_vecLightOrigin ) }; + pShaderAPI->SetPixelShaderConstant( PSREG_FRESNEL_SPEC_PARAMS, flFlashlightPos ); + SetFlashLightColorFromState( flashlightState, pShaderAPI ); BindTexture( SHADER_SAMPLER0, flashlightState.m_pSpotlightTexture, flashlightState.m_nSpotlightTextureFrame ); @@ -2017,9 +2068,19 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP if( vars.m_bLightmappedGeneric ) { - DECLARE_DYNAMIC_VERTEX_SHADER( lightmappedgeneric_flashlight_vs20 ); - SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); - SET_DYNAMIC_VERTEX_SHADER( lightmappedgeneric_flashlight_vs20 ); + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs30 ); + } + else + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs20 ); + } + if ( bSeamless ) { float const0[4]={ vars.m_fSeamlessScale,0,0,0}; @@ -2045,7 +2106,7 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP } else { - vertexlitgeneric_flashlight_vs11_Dynamic_Index vshIndex; + sdk_vertexlitgeneric_flashlight_vs11_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); vshIndex.SetSKINNING( pShaderAPI->GetCurrentNumBones() > 0 ); pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); @@ -2068,18 +2129,25 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP vEyePos_SpecExponent[3] = 0.0f; pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( flashlight_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_flashlight_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, flashlightState.m_bEnableShadows && ( pFlashlightDepthTexture != NULL ) ); - SET_DYNAMIC_PIXEL_SHADER( flashlight_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_flashlight_ps30 ); + } + else if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_flashlight_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, flashlightState.m_bEnableShadows && ( pFlashlightDepthTexture != NULL ) ); + SET_DYNAMIC_PIXEL_SHADER( sdk_flashlight_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( flashlight_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_flashlight_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( flashlight_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_flashlight_ps20 ); } float atten[4]; // Set the flashlight attenuation factors @@ -2087,6 +2155,7 @@ void CBaseVSShader::DrawFlashlight_dx90( IMaterialVar** params, IShaderDynamicAP atten[1] = flashlightState.m_fLinearAtten; atten[2] = flashlightState.m_fQuadraticAtten; atten[3] = flashlightState.m_FarZ; + /*atten[3] = flashlightState.m_FarZAtten;*/ s_pShaderAPI->SetPixelShaderConstant( PSREG_FLASHLIGHT_ATTENUATION, atten, 1 ); SetFlashlightVertexShaderConstants( vars.m_bBump, vars.m_nBumpTransform, bDetail, vars.m_nDetailScale, bSeamless ? false : true ); diff --git a/mp/src/materialsystem/stdshaders/BaseVSShader.h b/mp/src/materialsystem/stdshaders/BaseVSShader.h index 860c6876..c22030a5 100644 --- a/mp/src/materialsystem/stdshaders/BaseVSShader.h +++ b/mp/src/materialsystem/stdshaders/BaseVSShader.h @@ -25,6 +25,11 @@ #define SUPPORT_DX8 1 #define SUPPORT_DX7 1 #endif + +#ifdef MAPBASE +extern ConVar mat_specular_disable_on_missing; +#endif + //----------------------------------------------------------------------------- // Helper macro for vertex shaders //----------------------------------------------------------------------------- @@ -297,6 +302,9 @@ public: int m_nFlashlightTextureFrameVar; int m_nBaseTexture2Var; int m_nBaseTexture2FrameVar; +#ifdef MAPBASE + int m_nBaseTexture2TransformVar; +#endif int m_nBumpmap2Var; int m_nBumpmap2Frame; int m_nBump2Transform; @@ -337,6 +345,8 @@ private: }; +extern ConVar r_flashlightbrightness; + FORCEINLINE void SetFlashLightColorFromState( FlashlightState_t const &state, IShaderDynamicAPI *pShaderAPI, int nPSRegister=28, bool bFlashlightNoLambert=false ) { // Old code @@ -349,8 +359,7 @@ FORCEINLINE void SetFlashLightColorFromState( FlashlightState_t const &state, IS // flToneMapScale = 1.0f; //float flFlashlightScale = 1.0f / flToneMapScale; - // Force flashlight to 25% bright always - float flFlashlightScale = 0.25f; + float flFlashlightScale = r_flashlightbrightness.GetFloat(); if ( !g_pHardwareConfig->GetHDREnabled() ) { @@ -364,6 +373,9 @@ FORCEINLINE void SetFlashLightColorFromState( FlashlightState_t const &state, IS flFlashlightScale *= 2.5f; // Magic number that works well on the NVIDIA 8800 } + // INSOLENCE: This causes very odd projected texture flickering bugs, so it's commented out for now + /*flFlashlightScale *= state.m_fBrightnessScale;*/ + // Generate pixel shader constant float const *pFlashlightColor = state.m_Color; float vPsConst[4] = { flFlashlightScale * pFlashlightColor[0], flFlashlightScale * pFlashlightColor[1], flFlashlightScale * pFlashlightColor[2], pFlashlightColor[3] }; @@ -386,8 +398,11 @@ FORCEINLINE float ShadowAttenFromState( FlashlightState_t const &state ) FORCEINLINE float ShadowFilterFromState( FlashlightState_t const &state ) { - // We developed shadow maps at 1024, so we expect the penumbra size to have been tuned relative to that - return state.m_flShadowFilterSize / 1024.0f; + //// We developed shadow maps at 1024, so we expect the penumbra size to have been tuned relative to that + //return state.m_flShadowFilterSize / 1024.0f; + + // INSOLENCE: Get the shadow map resolution from the same place we get the shadow filter size + return state.m_flShadowFilterSize / state.m_flShadowMapResolution; } diff --git a/mp/src/materialsystem/stdshaders/MonitorScreen_dx9.cpp b/mp/src/materialsystem/stdshaders/MonitorScreen_dx9.cpp new file mode 100644 index 00000000..09f1460f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/MonitorScreen_dx9.cpp @@ -0,0 +1,182 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BaseVSShader.h" +#include "SDK_unlittwotexture_vs20.inc" +#include "SDK_monitorscreen_ps20.inc" +#include "SDK_monitorscreen_ps20b.inc" +#include "cpp_shader_constant_register_map.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DEFINE_FALLBACK_SHADER( SDK_MonitorScreen, SDK_MonitorScreen_DX9 ) + +BEGIN_VS_SHADER( SDK_MonitorScreen_DX9, + "This is a shader that does a contrast/saturation version of base times lightmap." ) + + BEGIN_SHADER_PARAMS + SHADER_PARAM( CONTRAST, SHADER_PARAM_TYPE_FLOAT, "0.0", "contrast 0 == normal 1 == color*color" ) + SHADER_PARAM( SATURATION, SHADER_PARAM_TYPE_FLOAT, "1.0", "saturation 0 == greyscale 1 == normal" ) + SHADER_PARAM( TINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "monitor tint" ) + SHADER_PARAM( TEXTURE2, SHADER_PARAM_TYPE_TEXTURE, "shadertest/lightmappedtexture", "second texture" ) + SHADER_PARAM( FRAME2, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $texture2" ) + SHADER_PARAM( TEXTURE2TRANSFORM, SHADER_PARAM_TYPE_MATRIX, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$texture2 texcoord transform" ) + END_SHADER_PARAMS + + // Set up anything that is necessary to make decisions in SHADER_FALLBACK. + SHADER_INIT_PARAMS() + { + SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_HW_SKINNING ); + if( !params[CONTRAST]->IsDefined() ) + { + params[CONTRAST]->SetFloatValue( 0.0f ); + } + if( !params[SATURATION]->IsDefined() ) + { + params[SATURATION]->SetFloatValue( 1.0f ); + } + if( !params[TINT]->IsDefined() ) + { + params[TINT]->SetVecValue( 1.0f, 1.0f, 1.0f ); + } + if (!IS_FLAG_DEFINED( MATERIAL_VAR_MODEL )) + { + CLEAR_FLAGS( MATERIAL_VAR_MODEL ); + } + } + + SHADER_FALLBACK + { + if( params && !params[BASETEXTURE]->IsDefined() ) + return "SDK_LightmappedGeneric"; + + return 0; + } + + SHADER_INIT + { + if (params[BASETEXTURE]->IsDefined()) + { + LoadTexture( BASETEXTURE ); + } + if (params[TEXTURE2]->IsDefined()) + { + LoadTexture( TEXTURE2 ); + } + } + + SHADER_DRAW + { + bool bHasTexture2 = params[TEXTURE2]->IsTexture(); + BlendType_t nBlendType = EvaluateBlendRequirements( BASETEXTURE, true ); + bool bFullyOpaque = (nBlendType != BT_BLENDADD) && (nBlendType != BT_BLEND) && !IS_FLAG_SET(MATERIAL_VAR_ALPHATEST); //dest alpha is free for special use + + SHADOW_STATE + { + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + if ( bHasTexture2 ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, true ); + } + + pShaderShadow->EnableSRGBWrite( true ); + + // Either we've got a constant modulation + bool isTranslucent = IsAlphaModulating(); + + // Or we've got a texture alpha on either texture + isTranslucent = isTranslucent || TextureIsTranslucent( BASETEXTURE, true ) || + TextureIsTranslucent( TEXTURE2, true ); + + if ( isTranslucent ) + { + if ( IS_FLAG_SET(MATERIAL_VAR_ADDITIVE) ) + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE ); + else + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA ); + } + else + { + if ( IS_FLAG_SET(MATERIAL_VAR_ADDITIVE) ) + EnableAlphaBlending( SHADER_BLEND_ONE, SHADER_BLEND_ONE ); + else + DisableAlphaBlending( ); + } + + // Set stream format (note that this shader supports compression) + unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_COMPRESSED; + int nTexCoordCount = 1; + int userDataSize = 0; + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, NULL, userDataSize ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_monitorscreen_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( TEXTURE2, (bHasTexture2)?(1):(0) ); + SET_STATIC_PIXEL_SHADER( sdk_monitorscreen_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_monitorscreen_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( TEXTURE2, (bHasTexture2)?(1):(0) ); + SET_STATIC_PIXEL_SHADER( sdk_monitorscreen_ps20 ); + } + + DefaultFog(); + + pShaderShadow->EnableAlphaWrites( bFullyOpaque ); + } + DYNAMIC_STATE + { + BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME ); + if( bHasTexture2 ) + { + BindTexture( SHADER_SAMPLER1, TEXTURE2, FRAME2 ); + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, TEXTURE2TRANSFORM ); + } + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, BASETEXTURETRANSFORM ); + SetPixelShaderConstant( 1, CONTRAST ); + SetPixelShaderConstant( 2, SATURATION ); + SetPixelShaderConstant( 3, TINT ); + SetModulationVertexShaderDynamicState(); + + pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); + + float vEyePos_SpecExponent[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent ); + vEyePos_SpecExponent[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); + + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_monitorscreen_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_monitorscreen_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_monitorscreen_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_monitorscreen_ps20 ); + } + } + Draw(); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/SDK_BlurFilter_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_BlurFilter_ps2x.fxc new file mode 100644 index 00000000..bfd082b3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_BlurFilter_ps2x.fxc @@ -0,0 +1,91 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "APPROX_SRGB_ADAPTER" "0..1" [ps20b] [PC] + +#define HDRTYPE HDR_TYPE_NONE +#include "common_ps_fxc.h" + +sampler TexSampler : register( s0 ); + +struct PS_INPUT +{ + float2 coordTap0 : TEXCOORD0; + float2 coordTap1 : TEXCOORD1; + float2 coordTap2 : TEXCOORD2; + float2 coordTap3 : TEXCOORD3; + float2 coordTap1Neg : TEXCOORD4; + float2 coordTap2Neg : TEXCOORD5; + float2 coordTap3Neg : TEXCOORD6; +}; + +float2 psTapOffs[3] : register( c0 ); +float3 scale_factor : register( c3 ); + +float4 SampleTexture( sampler texSampler, float2 uv ) +{ + float4 cSample = tex2D( texSampler, uv ); + + #if ( APPROX_SRGB_ADAPTER ) + { + cSample.rgb = max( cSample.rgb, float3( 0.00001f, 0.00001f, 0.00001f ) ); // rsqrt doesn't like inputs of zero + + float3 ooSQRT; // + ooSQRT.r = rsqrt( cSample.r ); // + ooSQRT.g = rsqrt( cSample.g ); // Approximate linear-to-sRGB conversion + ooSQRT.b = rsqrt( cSample.b ); // + cSample.rgb *= ooSQRT.rgb; // + } + #endif + + return cSample; +} + +float4 main( PS_INPUT i ) : COLOR +{ + float4 s0, s1, s2, s3, s4, s5, s6, color; + + // Sample taps with coordinates from VS + s0 = SampleTexture( TexSampler, i.coordTap0 ); + s1 = SampleTexture( TexSampler, i.coordTap1 ); + s2 = SampleTexture( TexSampler, i.coordTap2 ); + s3 = SampleTexture( TexSampler, i.coordTap3 ); + s4 = SampleTexture( TexSampler, i.coordTap1Neg ); + s5 = SampleTexture( TexSampler, i.coordTap2Neg ); + s6 = SampleTexture( TexSampler, i.coordTap3Neg ); + + color = s0 * 0.2013f; + color += ( s1 + s4 ) * 0.2185f; + color += ( s2 + s5 ) * 0.0821f; + color += ( s3 + s6 ) * 0.0461f; + + // Compute tex coords for other taps + float2 coordTap4 = i.coordTap0 + psTapOffs[0]; + float2 coordTap5 = i.coordTap0 + psTapOffs[1]; + float2 coordTap6 = i.coordTap0 + psTapOffs[2]; + float2 coordTap4Neg = i.coordTap0 - psTapOffs[0]; + float2 coordTap5Neg = i.coordTap0 - psTapOffs[1]; + float2 coordTap6Neg = i.coordTap0 - psTapOffs[2]; + + // Sample the taps + s1 = SampleTexture( TexSampler, coordTap4 ); + s2 = SampleTexture( TexSampler, coordTap5 ); + s3 = SampleTexture( TexSampler, coordTap6 ); + s4 = SampleTexture( TexSampler, coordTap4Neg ); + s5 = SampleTexture( TexSampler, coordTap5Neg ); + s6 = SampleTexture( TexSampler, coordTap6Neg ); + + color += ( s1 + s4 ) * 0.0262f; + color += ( s2 + s5 ) * 0.0162f; + color += ( s3 + s6 ) * 0.0102f; + color.xyz*=scale_factor.xyz; + + #if ( APPROX_SRGB_ADAPTER ) + { + color.xyz *= color.xyz; // Approximate sRGB-to-linear conversion + } + #endif + + return color; + //return FinalOutput( color, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_Refract_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_Refract_vs20.fxc new file mode 100644 index 00000000..698dd192 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_Refract_vs20.fxc @@ -0,0 +1,140 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "MODEL" "0..1" +// STATIC: "COLORMODULATE" "0..1" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const bool g_bModel = MODEL ? true : false; + +const float4 cBumpTexCoordTransform[4] : register( SHADER_SPECIFIC_CONST_1 ); + +const float g_flTime : register( SHADER_SPECIFIC_CONST_5 ); + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float4 vBaseTexCoord : TEXCOORD0; +#if !MODEL + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL0; +#else + float4 vUserData : TANGENT; +#endif +#if COLORMODULATE + float4 vColor : COLOR0; +#endif +}; + +struct VS_OUTPUT +{ + float4 vProjPos_POSITION : POSITION; +#if !defined( _X360 ) + float vFog : FOG; +#endif + float4 vBumpTexCoord : TEXCOORD0; + float3 vTangentEyeVect : TEXCOORD1; + float3 vWorldNormal : TEXCOORD2; + float3 vWorldTangent : TEXCOORD3; + float3 vWorldBinormal : TEXCOORD4; + float3 vRefractXYW : TEXCOORD5; + float3 vWorldViewVector : TEXCOORD6; +#if COLORMODULATE + float4 vColor : COLOR0; +#endif + float4 fogFactorW : COLOR1; + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + +#if COLORMODULATE + o.vColor = v.vColor; +#endif + + float3 worldNormal, worldPos, worldTangentS, worldTangentT; + + float3 vObjNormal; +#if MODEL + float4 vObjTangent; + DecompressVertex_NormalTangent( v.vNormal, v.vUserData, vObjNormal, vObjTangent ); + + SkinPositionNormalAndTangentSpace( + g_bSkinning, + v.vPos, vObjNormal, vObjTangent, + v.vBoneWeights, v.vBoneIndices, + worldPos, worldNormal, worldTangentS, worldTangentT ); +#else + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + worldPos = mul( v.vPos, cModel[0] ); + worldTangentS = mul( v.vTangentS, ( const float3x3 )cModel[0] ); + worldTangentT = mul( v.vTangentT, ( const float3x3 )cModel[0] ); + worldNormal = mul( vObjNormal, ( float3x3 )cModel[0] ); +#endif + + // World normal + o.vWorldNormal.xyz = normalize( worldNormal.xyz ); + + // Projected position + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.vProjPos_POSITION = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); + //o.projNormal.xyz = mul( worldNormal, cViewProj ); + + // Map projected position to the refraction texture + float2 vRefractPos; + vRefractPos.x = vProjPos.x; + vRefractPos.y = -vProjPos.y; // invert Y + vRefractPos = (vRefractPos + vProjPos.w) * 0.5f; + + // Refraction transform + o.vRefractXYW = float3(vRefractPos.x, vRefractPos.y, vProjPos.w); + + // Compute fog based on the position + float3 vWorldPos = mul( v.vPos, cModel[0] ); + o.fogFactorW = CalcFog( vWorldPos, vProjPos, FOGTYPE_RANGE ); +#if !defined( _X360 ) + o.vFog = o.fogFactorW; +#endif + + // Eye vector + float3 vWorldEyeVect = normalize( cEyePos - vWorldPos ); + o.vWorldViewVector.xyz = -vWorldEyeVect.xyz; + + // Transform to the tangent space + o.vTangentEyeVect.x = dot( vWorldEyeVect, worldTangentS ); + o.vTangentEyeVect.y = dot( vWorldEyeVect, worldTangentT ); + o.vTangentEyeVect.z = dot( vWorldEyeVect, worldNormal ); + + // Tranform bump coordinates + o.vBumpTexCoord.x = dot( v.vBaseTexCoord, cBumpTexCoordTransform[0] ); + o.vBumpTexCoord.y = dot( v.vBaseTexCoord, cBumpTexCoordTransform[1] ); + + // Tranform bump coordinates (note wz, not zw) + o.vBumpTexCoord.w = dot( v.vBaseTexCoord, cBumpTexCoordTransform[2] ); + o.vBumpTexCoord.z = dot( v.vBaseTexCoord, cBumpTexCoordTransform[3] ); + + + // Tangent space transform + o.vWorldNormal.xyz = normalize( worldNormal.xyz ); + o.vWorldTangent.xyz = worldTangentS.xyz; + o.vWorldBinormal.xyz = worldTangentT.xyz; + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_ps2x.fxc new file mode 100644 index 00000000..85d55107 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_ps2x.fxc @@ -0,0 +1,159 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// STATIC: "CUBEMAP" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" +// STATIC: "HDRTYPE" "0..2" +// STATIC: "PARALLAXCORRECT" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +// SKIP: $PARALLAXCORRECT && !$CUBEMAP +// SKIP: $PARALLAXCORRECT [ps20] + +#include "common_ps_fxc.h" + +// HDRFIXME: Need to make this work. + +#define USE_32BIT_LIGHTMAPS_ON_360 //uncomment to use 32bit lightmaps, be sure to keep this in sync with the same #define in materialsystem/cmatlightmaps.cpp + +#include "common_ps_fxc.h" +#include "common_lightmappedgeneric_fxc.h" + +const HALF4 g_EnvmapTint : register( c0 ); +const HALF3 g_DiffuseModulation : register( c1 ); +const HALF3 g_EnvmapContrast : register( c2 ); +const HALF3 g_EnvmapSaturation : register( c3 ); +const HALF4 g_FresnelReflection : register( c4 ); +const HALF3 g_EyePos : register( c5 ); +const HALF3 g_OverbrightFactor : register( c6 ); + +const HALF4 g_FogParams : register( c12 ); + +// Parallax cubemaps +#if (PARALLAXCORRECT) +const float3 cubemapPos : register(c7); +const float4x4 obbMatrix : register(c8); //through c11 +#endif + +// CENTROID: TEXCOORD2 + +sampler BaseTextureSampler : register( s0 ); +sampler LightmapSampler : register( s1 ); +sampler EnvmapSampler : register( s2 ); +sampler DetailSampler : register( s3 ); +sampler EnvmapMaskSampler : register( s5 ); + +sampler NormalizeSampler : register( s6 ); + +struct PS_INPUT +{ + HALF2 baseTexCoord : TEXCOORD0; + HALF2 detailTexCoord : TEXCOORD1; + HALF2 lightmapTexCoord : TEXCOORD2; + HALF2 envmapMaskTexCoord : TEXCOORD3; + HALF4 worldPos_projPosZ : TEXCOORD4; + HALF3 worldSpaceNormal : TEXCOORD5; + HALF4 vertexColor : COLOR; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + bool bCubemap = CUBEMAP ? true : false; + bool bVertexColor = VERTEXCOLOR ? true : false; + bool bEnvmapMask = ENVMAPMASK ? true : false; + bool bBaseAlphaEnvmapMask = BASEALPHAENVMAPMASK ? true : false; + + HALF4 baseColor = tex2D( BaseTextureSampler, i.baseTexCoord ); + HALF4 detailColor = tex2D( DetailSampler, i.detailTexCoord ); + + HALF2 lightmapCoordinates = i.lightmapTexCoord; + HALF3 lightmapColor = LightMapSample( LightmapSampler, lightmapCoordinates ); + + HALF3 specularFactor = 1.0f; + if( bEnvmapMask ) + { + specularFactor = tex2D( EnvmapMaskSampler, i.detailTexCoord ).xyz; + } + + if( bBaseAlphaEnvmapMask ) + { + specularFactor *= 1.0 - baseColor.a; // this blows! + } + + HALF3 diffuseLighting = lightmapColor; + diffuseLighting *= g_DiffuseModulation; + diffuseLighting *= LIGHT_MAP_SCALE; + + HALF3 albedo = baseColor; + HALF alpha = 1.0f; + + if( !bBaseAlphaEnvmapMask ) + { + alpha *= baseColor.a; + } + + albedo *= detailColor; + alpha *= detailColor.a; + + // FIXME: seperate vertexcolor and vertexalpha? + // vertex alpha is ignored if vertexcolor isn't set. . need to check other version. + if( bVertexColor ) + { + albedo *= i.vertexColor; + alpha *= i.vertexColor.a; // not sure about this one + } + + HALF3 specularLighting = HALF3( 0.0f, 0.0f, 0.0f ); + if( bCubemap ) + { + float3 worldVertToEyeVector = g_EyePos - i.worldPos_projPosZ.xyz; + worldVertToEyeVector = NormalizeWithCubemap( NormalizeSampler, worldVertToEyeVector ); + HALF3 reflectVect = CalcReflectionVectorUnnormalized( i.worldSpaceNormal, worldVertToEyeVector ); + + // Calc Fresnel factor + HALF3 worldSpaceNormal = NormalizeWithCubemap( NormalizeSampler, i.worldSpaceNormal ); + HALF fresnel = 1.0 - dot( worldSpaceNormal, worldVertToEyeVector ); + fresnel = pow( fresnel, 5.0 ); + fresnel = fresnel * g_FresnelReflection.b + g_FresnelReflection.a; + + //Parallax correction (2_0b and beyond) + //Adapted from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ +#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) +#if (PARALLAXCORRECT) + float3 worldPos = i.worldPos_projPosZ.xyz; + float3 positionLS = mul(float4(worldPos, 1), obbMatrix); + float3 rayLS = mul(reflectVect, (float3x3) obbMatrix); + + float3 firstPlaneIntersect = (float3(1.0f, 1.0f, 1.0f) - positionLS) / rayLS; + float3 secondPlaneIntersect = (-positionLS) / rayLS; + float3 furthestPlane = max(firstPlaneIntersect, secondPlaneIntersect); + float distance = min(furthestPlane.x, min(furthestPlane.y, furthestPlane.z)); + + // Use distance in WS directly to recover intersection + float3 intersectPositionWS = worldPos + reflectVect * distance; + reflectVect = intersectPositionWS - cubemapPos; +#endif +#endif + + specularLighting = texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + + specularLighting *= g_EnvmapTint; +#if HDRTYPE == HDR_TYPE_NONE + HALF3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast ); + HALF3 greyScale = dot( specularLighting, HALF3( 0.299f, 0.587f, 0.114f ) ); + specularLighting = lerp( greyScale, specularLighting, g_EnvmapSaturation ); +#endif + specularLighting *= fresnel; + } + + // Do it somewhat unlit + HALF3 result = albedo*(g_OverbrightFactor.z*diffuseLighting + g_OverbrightFactor.y) + specularLighting; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( HALF4( result, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_vs20.fxc new file mode 100644 index 00000000..fec0b49f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_ShatteredGlass_vs20.fxc @@ -0,0 +1,67 @@ +// STATIC: "ENVMAP_MASK" "0..1" + +// DYNAMIC: "DOWATERFOG" "0..1" +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_UseSeparateEnvmapMask = ENVMAP_MASK; + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cDetailTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_2 ); + +struct VS_INPUT +{ + float3 vPos : POSITION; + float4 vNormal : NORMAL; + float2 vBaseTexCoord : TEXCOORD0; + float2 vLightmapTexCoord : TEXCOORD1; + float2 vDetailTexCoord : TEXCOORD2; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + float fog : FOG; +#endif + float2 baseTexCoord : TEXCOORD0; + float2 detailTexCoord : TEXCOORD1; + float2 lightmapTexCoord : TEXCOORD2; + float2 envmapMaskTexCoord : TEXCOORD3; + float4 worldPos_projPosZ : TEXCOORD4; + float3 worldNormal : TEXCOORD5; + float4 vertexColor : COLOR; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + float4 projPos; + projPos = mul( float4( v.vPos, 1 ), cModelViewProj ); + o.projPos = projPos; + + o.worldPos_projPosZ.w = projPos.z; + o.worldPos_projPosZ.xyz = mul( float4( v.vPos, 1 ), cModel[0] ); + o.worldNormal = mul( vObjNormal, ( float3x3 )cModel[0] ); + o.baseTexCoord.x = dot( v.vBaseTexCoord, cBaseTexCoordTransform[0] ) + cBaseTexCoordTransform[0].w; + o.baseTexCoord.y = dot( v.vBaseTexCoord, cBaseTexCoordTransform[1] ) + cBaseTexCoordTransform[1].w; + o.detailTexCoord.x = dot( v.vDetailTexCoord, cDetailTexCoordTransform[0] ) + cDetailTexCoordTransform[0].w; + o.detailTexCoord.y = dot( v.vDetailTexCoord, cDetailTexCoordTransform[1] ) + cDetailTexCoordTransform[1].w; + o.envmapMaskTexCoord.x = dot( v.vDetailTexCoord, cDetailTexCoordTransform[0] ) + cDetailTexCoordTransform[0].w; + o.envmapMaskTexCoord.y = dot( v.vDetailTexCoord, cDetailTexCoordTransform[1] ) + cDetailTexCoordTransform[1].w; + o.lightmapTexCoord = v.vLightmapTexCoord; + +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + o.fog = CalcFixedFunctionFog( o.worldPos_projPosZ.xyz, g_FogType ); +#endif + + o.vertexColor = cModulationColor; + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_WaterCheap_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_WaterCheap_ps2x.fxc new file mode 100644 index 00000000..34b9bac3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_WaterCheap_ps2x.fxc @@ -0,0 +1,151 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "MULTITEXTURE" "0..1" +// STATIC: "FRESNEL" "0..1" +// STATIC: "BLEND" "0..1" +// STATIC: "REFRACTALPHA" "0..1" +// STATIC: "HDRTYPE" "0..2" +// STATIC: "NORMAL_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMAL_DECODE_MODE" "0..0" [PC] + +// DYNAMIC: "HDRENABLED" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +#include "common_ps_fxc.h" + +const HALF3 g_WaterFogColor : register( c0 ); +const HALF4 g_CheapWaterParams : register( c1 ); +const HALF4 g_ReflectTint : register( c2 ); +const float4 g_PixelFogParams : register( c3 ); +const float3 g_EyePos : register( c4 ); + +#define g_CheapWaterStart g_CheapWaterParams.x +#define g_CheapWaterEnd g_CheapWaterParams.y +#define g_CheapWaterDeltaRecip g_CheapWaterParams.z +#define g_CheapWaterStartDivDelta g_CheapWaterParams.w + +sampler EnvmapSampler : register( s0 ); +sampler NormalMapSampler : register( s1 ); +#if REFRACTALPHA +sampler RefractSampler : register( s2 ); +#endif +sampler NormalizeSampler : register( s6 ); + +struct PS_INPUT +{ + float2 normalMapTexCoord : TEXCOORD0; + HALF3 worldSpaceEyeVect : TEXCOORD1; + HALF3x3 tangentSpaceTranspose : TEXCOORD2; + float4 vRefract_W_ProjZ : TEXCOORD5; + +#if MULTITEXTURE + float4 vExtraBumpTexCoord : TEXCOORD6; +#endif + float3 vWorldPos : TEXCOORD7; + float4 fogFactorW : COLOR1; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + bool bBlend = BLEND ? true : false; + +#if MULTITEXTURE + float3 vNormal = tex2D( NormalMapSampler, i.normalMapTexCoord ); + float3 vNormal1 = tex2D( NormalMapSampler, i.vExtraBumpTexCoord.xy ); + float3 vNormal2 = tex2D( NormalMapSampler, i.vExtraBumpTexCoord.zw ); + vNormal = 0.33 * ( vNormal + vNormal1 + vNormal2 ); + +#if ( NORMAL_DECODE_MODE == NORM_DECODE_ATI2N ) + vNormal.xy = vNormal.xy * 2.0f - 1.0f; + vNormal.z = sqrt( 1.0f - dot(vNormal.xy, vNormal.xy) ); +#else + vNormal = 2.0 * vNormal - 1.0; +#endif + +#else + float3 vNormal = DecompressNormal( NormalMapSampler, i.normalMapTexCoord, NORMAL_DECODE_MODE ); +#endif + + HALF3 worldSpaceNormal = mul( vNormal, i.tangentSpaceTranspose ); + HALF3 worldSpaceEye; + + HALF flWorldSpaceDist = 1.0f; + +#ifdef NV3X + // for some reason, fxc doesn't convert length( half3 v ) into all _pp opcodes. + if (bBlend) + { + worldSpaceEye = i.worldSpaceEyeVect; + HALF worldSpaceDistSqr = dot( worldSpaceEye, worldSpaceEye ); + HALF rcpWorldSpaceDist = rsqrt( worldSpaceDistSqr ); + worldSpaceEye *= rcpWorldSpaceDist; + flWorldSpaceDist = worldSpaceDistSqr * rcpWorldSpaceDist; + } + else + { + worldSpaceEye = NormalizeWithCubemap( NormalizeSampler, i.worldSpaceEyeVect ); + } +#else // !NV3X + if (bBlend) + { + worldSpaceEye = i.worldSpaceEyeVect; + flWorldSpaceDist = length( worldSpaceEye ); + worldSpaceEye /= flWorldSpaceDist; + } + else + { + worldSpaceEye = NormalizeWithCubemap( NormalizeSampler, i.worldSpaceEyeVect ); + } +#endif + + HALF3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, worldSpaceEye ); + HALF3 specularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= g_ReflectTint; + +#if FRESNEL + // FIXME: It's unclear that we want to do this for cheap water + // but the code did this previously and I didn't want to change it + HALF flDotResult = dot( worldSpaceEye, worldSpaceNormal ); + flDotResult = 1.0f - max( 0.0f, flDotResult ); + + HALF flFresnelFactor = flDotResult * flDotResult; + flFresnelFactor *= flFresnelFactor; + flFresnelFactor *= flDotResult; +#else + HALF flFresnelFactor = g_ReflectTint.a; +#endif + + HALF flAlpha; + if (bBlend) + { + HALF flReflectAmount = saturate( flWorldSpaceDist * g_CheapWaterDeltaRecip - g_CheapWaterStartDivDelta ); + flAlpha = saturate( flFresnelFactor + flReflectAmount ); + +#if REFRACTALPHA + // Perform division by W only once + float ooW = 1.0f / i.vRefract_W_ProjZ.z; + float2 unwarpedRefractTexCoord = i.vRefract_W_ProjZ * ooW; + float fogDepthValue = tex2D( RefractSampler, unwarpedRefractTexCoord ).a; + // Fade on the border between the water and land. + flAlpha *= saturate( ( fogDepthValue - .05f ) * 20.0f ); +#endif + } + else + { + flAlpha = 1.0f; +#if HDRTYPE == 0 || HDRENABLED == 0 + specularLighting = lerp( g_WaterFogColor, specularLighting, flFresnelFactor ); +#else + specularLighting = lerp( GammaToLinear( g_WaterFogColor ), specularLighting, flFresnelFactor ); +#endif + } + + // multiply the color by alpha.since we are using alpha blending to blend against dest alpha for borders. +#if (PIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE) + float fogFactor = CalcRangeFogFactorNonFixedFunction( i.vWorldPos, g_EyePos, g_PixelFogParams.z, g_PixelFogParams.x, g_PixelFogParams.w ); +#else + float fogFactor = 0; +#endif + + return FinalOutput( float4( specularLighting, flAlpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_WaterCheap_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_WaterCheap_vs20.fxc new file mode 100644 index 00000000..4b9086b3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_WaterCheap_vs20.fxc @@ -0,0 +1,87 @@ +// STATIC: "BLEND" "0..1" +#include "common_vs_fxc.h" + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vNormal : NORMAL; + float2 vNormalMapCoord : TEXCOORD0; + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + float2 normalMapTexCoord : TEXCOORD0; + float3 worldVertToEyeVector : TEXCOORD1; + float3x3 tangentSpaceTranspose : TEXCOORD2; + float4 vRefract_W_ProjZ : TEXCOORD5; + float4 vExtraBumpTexCoord : TEXCOORD6; + float3 vWorldPos : TEXCOORD7; + float4 fogFactorW : COLOR1; +}; + +const float4 cNormalMapTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +const float4 TexOffsets : register( SHADER_SPECIFIC_CONST_3 ); + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + float4 projPos; + float3 worldPos; + + projPos = mul( v.vPos, cModelViewProj ); + o.projPos = projPos; + +#if BLEND + // Map projected position to the reflection texture + o.vRefract_W_ProjZ.x = projPos.x; + o.vRefract_W_ProjZ.y = -projPos.y; // invert Y + o.vRefract_W_ProjZ.xy = (o.vRefract_W_ProjZ + projPos.w) * 0.5f; + o.vRefract_W_ProjZ.z = projPos.w; +#endif + + o.vRefract_W_ProjZ.w = projPos.z; + + worldPos = mul( v.vPos, cModel[0] ); + + float3 worldTangentS = mul( v.vTangentS, ( const float3x3 )cModel[0] ); + float3 worldTangentT = mul( v.vTangentT, ( const float3x3 )cModel[0] ); + float3 worldNormal = mul( vObjNormal, ( float3x3 )cModel[0] ); + o.tangentSpaceTranspose[0] = worldTangentS; + o.tangentSpaceTranspose[1] = worldTangentT; + o.tangentSpaceTranspose[2] = worldNormal; + + float3 worldVertToEyeVector = VSHADER_VECT_SCALE * (cEyePos - worldPos); + o.worldVertToEyeVector = worldVertToEyeVector; + + // FIXME: need to add a normalMapTransform to all of the water shaders. + //o.normalMapTexCoord.x = dot( v.vNormalMapCoord, cNormalMapTransform[0] ) + cNormalMapTransform[0].w; + //o.normalMapTexCoord.y = dot( v.vNormalMapCoord, cNormalMapTransform[1] ) + cNormalMapTransform[1].w; + o.normalMapTexCoord = v.vNormalMapCoord; + + float f45x=v.vNormalMapCoord.x+v.vNormalMapCoord.y; + float f45y=v.vNormalMapCoord.y-v.vNormalMapCoord.x; + o.vExtraBumpTexCoord.x=f45x*0.1+TexOffsets.x; + o.vExtraBumpTexCoord.y=f45y*0.1+TexOffsets.y; + o.vExtraBumpTexCoord.z=v.vNormalMapCoord.y*0.45+TexOffsets.z; + o.vExtraBumpTexCoord.w=v.vNormalMapCoord.x*0.45+TexOffsets.w; + + o.fogFactorW = CalcFog( worldPos, projPos, FOGTYPE_RANGE ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + o.vWorldPos = worldPos; + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_Water_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_Water_vs20.fxc new file mode 100644 index 00000000..bee4aa50 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_Water_vs20.fxc @@ -0,0 +1,118 @@ +// STATIC: "BASETEXTURE" "0..1" +// STATIC: "MULTITEXTURE" "0..1" + +// SKIP: $MULTITEXTURE && $BASETEXTURE + +#include "common_vs_fxc.h" + +const float4 cBumpTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_1 ); +const float4 TexOffsets : register( SHADER_SPECIFIC_CONST_3 ); + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vNormal : NORMAL; + float4 vBaseTexCoord : TEXCOORD0; + float2 vLightmapTexCoord : TEXCOORD1; + float2 vLightmapTexCoordOffset : TEXCOORD2; + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL0; +}; + +struct VS_OUTPUT +{ + float4 vProjPos_POSITION : POSITION; +#if !defined( _X360 ) + float vFog : FOG; +#endif + float2 vBumpTexCoord : TEXCOORD0; + float3 vTangentEyeVect : TEXCOORD1; + float4 vReflectXY_vRefractYX : TEXCOORD2; + float4 vWorldPos_projPosW : TEXCOORD3; + float4 vProjPos : TEXCOORD4; + float screenCoord : TEXCOORD5; +#if MULTITEXTURE + float4 vExtraBumpTexCoord : TEXCOORD6; +#endif +#if BASETEXTURE + HALF4 lightmapTexCoord1And2 : TEXCOORD6; + HALF4 lightmapTexCoord3 : TEXCOORD7; +#endif + float4 fogFactorW : COLOR1; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + // Projected position + float4 vProjPos = mul( v.vPos, cModelViewProj ); + o.vProjPos = o.vProjPos_POSITION = vProjPos; + + // Project tangent basis + float2 vProjTangentS = mul( v.vTangentS, cViewProj ); + float2 vProjTangentT = mul( v.vTangentT, cViewProj ); + + // Map projected position to the reflection texture + float2 vReflectPos; + vReflectPos = (vProjPos.xy + vProjPos.w) * 0.5f; + + // Map projected position to the refraction texture + float2 vRefractPos; + vRefractPos.x = vProjPos.x; + vRefractPos.y = -vProjPos.y; // invert Y + vRefractPos = (vRefractPos + vProjPos.w) * 0.5f; + + // Reflection transform + o.vReflectXY_vRefractYX = float4( vReflectPos.x, vReflectPos.y, vRefractPos.y, vRefractPos.x ); + o.vWorldPos_projPosW.w = vProjPos.w; + + o.screenCoord = vProjPos.x; + + // Compute fog based on the position + float3 vWorldPos = mul( v.vPos, cModel[0] ); + o.fogFactorW = CalcFog( vWorldPos, vProjPos, FOGTYPE_RANGE ); +#if !defined( _X360 ) + o.vFog = o.fogFactorW; +#endif + o.vWorldPos_projPosW.xyz = vWorldPos; + + // Eye vector + float3 vWorldEyeVect = cEyePos - vWorldPos; + // Transform to the tangent space + o.vTangentEyeVect.x = dot( vWorldEyeVect, v.vTangentS ); + o.vTangentEyeVect.y = dot( vWorldEyeVect, v.vTangentT ); + o.vTangentEyeVect.z = dot( vWorldEyeVect, vObjNormal ); + + // Tranform bump coordinates + o.vBumpTexCoord.x = dot( v.vBaseTexCoord, cBumpTexCoordTransform[0] ); + o.vBumpTexCoord.y = dot( v.vBaseTexCoord, cBumpTexCoordTransform[1] ); + float f45x=v.vBaseTexCoord.x+v.vBaseTexCoord.y; + float f45y=v.vBaseTexCoord.y-v.vBaseTexCoord.x; +#if MULTITEXTURE + o.vExtraBumpTexCoord.x=f45x*0.1+TexOffsets.x; + o.vExtraBumpTexCoord.y=f45y*0.1+TexOffsets.y; + o.vExtraBumpTexCoord.z=v.vBaseTexCoord.y*0.45+TexOffsets.z; + o.vExtraBumpTexCoord.w=v.vBaseTexCoord.x*0.45+TexOffsets.w; +#endif + +#if BASETEXTURE + o.lightmapTexCoord1And2.xy = v.vLightmapTexCoord + v.vLightmapTexCoordOffset; + + float2 lightmapTexCoord2 = o.lightmapTexCoord1And2.xy + v.vLightmapTexCoordOffset; + float2 lightmapTexCoord3 = lightmapTexCoord2 + v.vLightmapTexCoordOffset; + + // reversed component order + o.lightmapTexCoord1And2.w = lightmapTexCoord2.x; + o.lightmapTexCoord1And2.z = lightmapTexCoord2.y; + + o.lightmapTexCoord3.xy = lightmapTexCoord3; +#endif + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_ps2x.fxc new file mode 100644 index 00000000..f27e870d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_ps2x.fxc @@ -0,0 +1,47 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "MACROS" "0..1" + +#define HDRTYPE HDR_TYPE_NONE +#include "common_ps_fxc.h" + + +sampler BaseSampler : register( s0 ); +// NOTE: LightmapSampler is at the same place as the lightmap sampler in lightmappedgeneric so that we have +// generally the same texture state here. +// CENTROID: TEXCOORD1 +sampler LightmapSampler: register( s1 ); +sampler BaseSampler2: register( s2 ); +sampler LightmapAlphaSampler: register( s3 ); +sampler MacrosSampler: register( s4 ); + +struct PS_INPUT +{ + float2 baseCoord : TEXCOORD0; + float2 baseCoord2 : TEXCOORD1; + float2 lightmapCoord : TEXCOORD2; + float2 macrosCoord : TEXCOORD3; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + bool bMacros = MACROS ? true : false; + + float4 base = tex2D( BaseSampler, i.baseCoord ); + float4 base2 = tex2D( BaseSampler2, i.baseCoord2 ); + + float4 lightmap = tex2D( LightmapSampler, i.lightmapCoord ); + float blendFactor = lightmap.a; + + float4 color = 2.0f * lightmap * lerp( base2, base, blendFactor ); + if( bMacros ) + { + float4 macros = tex2D( MacrosSampler, i.macrosCoord ); + + // Not sure what to do with macro alpha + color.rgb *= 2.0f * lerp( macros.a, macros.b, blendFactor ); + } + + return FinalOutput( color, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_vs20.fxc new file mode 100644 index 00000000..f5d88b1e --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_WorldVertexTransition_vs20.fxc @@ -0,0 +1,64 @@ +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cBaseTexCoordTransform2[2] : register( SHADER_SPECIFIC_CONST_2 ); +const float4 cMacrosTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_4 ); + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vColor : COLOR0; + float4 vTexCoord0 : TEXCOORD0; + float4 vTexCoord1 : TEXCOORD1; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + float2 baseCoord : TEXCOORD0; + float2 baseCoord2 : TEXCOORD1; + float2 lightmapCoord : TEXCOORD2; + float2 macrosCoord : TEXCOORD3; + float4 color : COLOR0; + float4 fogFactorW : COLOR1; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 worldNormal, worldPos; + float2 texCoord; + worldPos = mul( v.vPos, cModel[0] ); + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + o.fogFactorW = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + o.color = v.vColor; + + o.baseCoord.x = dot( v.vTexCoord0, cBaseTexCoordTransform[0] ); + o.baseCoord.y = dot( v.vTexCoord0, cBaseTexCoordTransform[1] ); + + o.baseCoord2.x = dot( v.vTexCoord0, cBaseTexCoordTransform2[0] ); + o.baseCoord2.y = dot( v.vTexCoord0, cBaseTexCoordTransform2[1] ); + + o.lightmapCoord = v.vTexCoord1; + + o.macrosCoord.x = dot( v.vTexCoord0, cMacrosTexCoordTransform[0] ); + o.macrosCoord.y = dot( v.vTexCoord0, cMacrosTexCoordTransform[1] ); + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_cable_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_cable_ps2x.fxc new file mode 100644 index 00000000..2eb0dbf4 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_cable_ps2x.fxc @@ -0,0 +1,54 @@ +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +sampler NormalSampler : register( s0 ); +sampler BaseTextureSampler : register( s1 ); + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +struct PS_INPUT +{ + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog + + float4 directionalLightColor : COLOR0; + float4 fogFactorW : COLOR1; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float3 vNormalMapDir = tex2D( NormalSampler, i.vTexCoord0 ); // Get the 3-vector from the normal map + float4 textureColor = tex2D( BaseTextureSampler, i.vTexCoord1 ); // Interpret tcoord t1 as color data. + + //Expand compacted vectors + //TODO: find if there's a better way to expand a color normal to a full vector ( _bx2 was used in the assembly code ) + vNormalMapDir = (vNormalMapDir - 0.5) * 2.0; + float3 vLightDir = float3( 0.0f, 0.0f, 1.0f ); + + float lightDirDotNormalMap = dot( vNormalMapDir, vLightDir ); //normalMap dot dirLightDir + + // do half-lambert on the dot + lightDirDotNormalMap = lightDirDotNormalMap * 0.5 + 0.5; + lightDirDotNormalMap = lightDirDotNormalMap * lightDirDotNormalMap; + + float4 resultColor; + resultColor.xyz = lightDirDotNormalMap * ( textureColor.rgb * i.directionalLightColor.rgb ); + resultColor.a = textureColor.a * i.directionalLightColor.a; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( resultColor, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_cable_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_cable_vs20.fxc new file mode 100644 index 00000000..adb3559e --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_cable_vs20.fxc @@ -0,0 +1,80 @@ +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; + +struct VS_INPUT +{ + float4 vPos : POSITION; + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + + float4 directionalLightColor : COLOR0; + + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; +}; + +struct VS_OUTPUT +{ + float4 vProjPos : POSITION; + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog + + float4 directionalLightColor : COLOR0; + + float4 fogFactorW : COLOR1; + +#if !defined( _X360 ) + float fog : FOG; +#endif +}; + + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 worldPos; + worldPos = mul( v.vPos, cModel[0] ); + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.vProjPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); + + o.fogFactorW = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + + //------------------------------------------------------------------------------ + // Setup the tangent space + //------------------------------------------------------------------------------ + + // Get S crossed with T (call it R) + float3 r = cross( v.vTangentS, v.vTangentT ); + + // Normalize S (into s) + float3 s = normalize( v.vTangentS ); + + // Normalize R (into r) + r = normalize( r ); + + // Regenerate T (into t) + float3 t = cross( r, v.vTangentS ); + + //------------------------------------------------------------------------------ + // Copy texcoords for the normal map and base texture + //------------------------------------------------------------------------------ + o.vTexCoord0 = v.vTexCoord0; + o.vTexCoord1 = v.vTexCoord1; + + // Pass the dirlight color through + o.directionalLightColor = v.directionalLightColor; + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_ps2x.fxc new file mode 100644 index 00000000..3c432132 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_ps2x.fxc @@ -0,0 +1,106 @@ +//========= Copyright ? 1996-2006, Valve Corporation, All rights reserved. ============// + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps30][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "BUMPMAP" "0..1" + +// Includes ======================================================================================= +#include "common_vertexlitgeneric_dx9.h" + +// Texture Samplers =============================================================================== +sampler g_tRefractionSampler : register( s0 ); +#if BUMPMAP + sampler g_tBumpSampler : register( s1 ); +#endif + +// Shaders Constants and Globals ================================================================== +const float4 g_mViewProj0 : register( c0 ); // 1st row of matrix +const float4 g_mViewProj1 : register( c1 ); // 2nd row of matrix + +const float4 g_vCameraPosition : register( c5 ); +const float4 g_vPackedConst6 : register( c6 ); +#define g_flCloakFactor g_vPackedConst6.x // Default = 1.0f +#define g_flRefractAmount g_vPackedConst6.y // Default = 1.0f + +const float4 g_cCloakColorTint : register( c7 ); + +// 8 2D Poisson offsets (designed to use .xy and .wz swizzles (not .zw) +static const float4 g_vPoissonOffset[4] = { float4 (-0.0876f, 0.9703f, 0.5651f, 0.4802f ), + float4 ( 0.1851f, 0.1580f, -0.0617f, -0.2616f ), + float4 (-0.5477f, -0.6603f, 0.0711f, -0.5325f ), + float4 (-0.0751f, -0.8954f, 0.4054f, 0.6384f ) }; + +// Interpolated values ============================================================================ +struct PS_INPUT +{ + float3 vWorldNormal : TEXCOORD0; // World-space normal + float3 vProjPosForRefract : TEXCOORD1; + float3 vWorldViewVector : TEXCOORD2; + #if BUMPMAP + float3x3 mTangentSpaceTranspose : TEXCOORD3; + // second row : TEXCOORD4; + // third row : TEXCOORD5; + float2 vTexCoord0 : TEXCOORD6; + #endif +}; + +// Main =========================================================================================== +float4 main( PS_INPUT i ) : COLOR +{ + float3 vWorldNormal = normalize( i.vWorldNormal.xyz ); + + #if BUMPMAP + float4 vBumpTexel = tex2D( g_tBumpSampler, i.vTexCoord0.xy ); + float3 vTangentNormal = ( 2.0f * vBumpTexel ) - 1.0f; + vWorldNormal.xyz = mul( i.mTangentSpaceTranspose, vTangentNormal.xyz ); + #endif + + // Transform world space normal into clip space and project + float3 vProjNormal; + vProjNormal.x = dot( vWorldNormal.xyz, g_mViewProj0.xyz ); // 1st row + vProjNormal.y = dot( vWorldNormal.xyz, g_mViewProj1.xyz ); // 2nd row + + // Compute coordinates for sampling refraction + float2 vRefractTexCoordNoWarp = i.vProjPosForRefract.xy / i.vProjPosForRefract.z; + float2 vRefractTexCoord = vProjNormal.xy; + float scale = lerp( g_flRefractAmount, 0.0f, saturate( g_flCloakFactor ) ); + vRefractTexCoord.xy *= scale; + vRefractTexCoord.xy += vRefractTexCoordNoWarp.xy; + + // Blur by scalable Poisson filter + float flBlurAmount = lerp( 0.05f, 0.0f, saturate( g_flCloakFactor ) ); + float3 cRefract = tex2D( g_tRefractionSampler, vRefractTexCoord.xy ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[0].xy * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[0].wz * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[1].xy * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[1].wz * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[2].xy * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[2].wz * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[3].xy * flBlurAmount ) ); + cRefract += tex2D( g_tRefractionSampler, vRefractTexCoord.xy + ( g_vPoissonOffset[3].wz * flBlurAmount ) ); + cRefract /= 9.0f; + + // 1-(N.V) for Fresnel term (NOTE: If this math changes, you need to update the C code that mimics this on the CPU) + float flFresnel = 1.0f - saturate( dot( i.vWorldNormal.xyz, normalize( -i.vWorldViewVector.xyz ) ) ); + float flCloakLerpFactor = saturate( lerp( 1.0f, flFresnel - 1.35f, saturate( g_flCloakFactor ) ) ); + flCloakLerpFactor = 1.0f - smoothstep( 0.4f, 0.425f, flCloakLerpFactor ); + + // Slightly dim the facing pixels and brighten the silhouette pixels + cRefract.rgb *= lerp( flFresnel * 0.4 + 0.8, 1.0f, saturate( g_flCloakFactor ) * saturate( g_flCloakFactor ) ); // This gives a scalar in the range [0.8 1.2] + + // Refract color tint + float fColorTintStrength = saturate( ( saturate( g_flCloakFactor ) - 0.75f ) * 4.0f ); + cRefract.rgb *= lerp( g_cCloakColorTint, 1.0f, fColorTintStrength ); + + //===============// + // Combine terms // + //===============// + float4 result; + result.rgb = cRefract.rgb; + + // Set alpha to cloak mask + result.a = flCloakLerpFactor; + + return FinalOutput( result, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_vs20.fxc new file mode 100644 index 00000000..8f84ef55 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_cloak_blended_pass_vs20.fxc @@ -0,0 +1,132 @@ +//========= Copyright ? 1996-2006, Valve Corporation, All rights reserved. ============// + +// STATIC: "BUMPMAP" "0..1" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] + +// Includes +#include "common_vs_fxc.h" + +// Globals +static const bool g_bSkinning = SKINNING ? true : false; +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +// Structs +struct VS_INPUT +{ + float4 vPos : POSITION; // Position + float4 vNormal : NORMAL; // Normal + float4 vBoneWeights : BLENDWEIGHT; // Skin weights + float4 vBoneIndices : BLENDINDICES; // Skin indices + float4 vTexCoord0 : TEXCOORD0; // Base texture coordinates + + #if BUMPMAP + float4 vTangent : TANGENT; + #endif + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; + + #ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; + #endif +}; + +struct VS_OUTPUT +{ + float4 vProjPosition : POSITION; // Projection-space position + float3 vWorldNormal : TEXCOORD0; // World-space normal + float3 vProjPosForRefract : TEXCOORD1; + float3 vWorldViewVector : TEXCOORD2; + + #if BUMPMAP + float3x3 mTangentSpaceTranspose : TEXCOORD3; + // second row : TEXCOORD4; + // third row : TEXCOORD5; + float2 vTexCoord0 : TEXCOORD6; + #endif +}; + +// Main +VS_OUTPUT main( const VS_INPUT i ) +{ + VS_OUTPUT o; + + // Flexes coming in from a separate stream (contribution masked by cFlexScale.x) + float4 vObjPosition = i.vPos; + float3 vObjNormal; + + #if BUMPMAP + float4 vObjTangent; + DecompressVertex_NormalTangent( i.vNormal, i.vTangent, vObjNormal, vObjTangent ); + + #if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( i.vPosFlex, i.vNormalFlex, vObjPosition.xyz, vObjNormal, vObjTangent.xyz ); + #else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, i.vVertexID, float3( 0, 0, 0 ), vObjPosition.xyz, vObjNormal, vObjTangent.xyz ); + #endif + #else // !BUMPMAP + DecompressVertex_Normal( i.vNormal, vObjNormal ); + + #if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( i.vPosFlex, i.vNormalFlex, vObjPosition.xyz, vObjNormal ); + #else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, i.vVertexID, float3( 0, 0, 0 ), + vObjPosition.xyz, vObjNormal ); + #endif + #endif + + // Transform the position + float3 vWorldPosition = { 0.0f, 0.0f, 0.0f }; + float3 vWorldNormal = { 0.0f, 0.0f, 0.0f }; + #if BUMPMAP + float3 vWorldTangent = { 0.0f, 0.0f, 0.0f }; + float3 vWorldBinormal = { 0.0f, 0.0f, 0.0f }; + SkinPositionNormalAndTangentSpace( g_bSkinning, vObjPosition, vObjNormal.xyz, vObjTangent.xyzw, i.vBoneWeights, i.vBoneIndices, vWorldPosition, vWorldNormal, vWorldTangent, vWorldBinormal ); + #else + SkinPositionAndNormal( g_bSkinning, vObjPosition, vObjNormal.xyz, i.vBoneWeights, i.vBoneIndices, vWorldPosition, vWorldNormal ); + #endif + o.vWorldNormal.xyz = normalize( vWorldNormal.xyz ); + + // Transform into projection space + float4 vProjPosition = mul( float4( vWorldPosition, 1.0f ), cViewProj ); + o.vProjPosition = vProjPosition; + + // Map projected position to the refraction texture + float2 vRefractPos; + vRefractPos.x = vProjPosition.x; + vRefractPos.y = -vProjPosition.y; // Invert Y + vRefractPos = (vRefractPos + vProjPosition.w) * 0.5f; + o.vProjPosForRefract.xyz = float3(vRefractPos.x, vRefractPos.y, vProjPosition.w); + + // View vector + float3 vWorldViewVector = normalize (vWorldPosition.xyz - cEyePos.xyz); + o.vWorldViewVector.xyz = vWorldViewVector.xyz; + + // Tangent space transform + #if BUMPMAP + o.mTangentSpaceTranspose[0] = float3( vWorldTangent.x, vWorldBinormal.x, vWorldNormal.x ); + o.mTangentSpaceTranspose[1] = float3( vWorldTangent.y, vWorldBinormal.y, vWorldNormal.y ); + o.mTangentSpaceTranspose[2] = float3( vWorldTangent.z, vWorldBinormal.z, vWorldNormal.z ); + #endif + + // Texture coordinates + #if BUMPMAP + o.vTexCoord0.x = dot( i.vTexCoord0.xy, cBaseTexCoordTransform[0] ); + o.vTexCoord0.y = dot( i.vTexCoord0.xy, cBaseTexCoordTransform[1] ); + #endif + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_decalmodulate_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_decalmodulate_ps2x.fxc new file mode 100644 index 00000000..1b0518c5 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_decalmodulate_ps2x.fxc @@ -0,0 +1,101 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// +// paired with "vertexlit_and_unlit_generic_vs##" + +// STATIC: "VERTEXALPHA" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] [PC] + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps30] + +// Flashlight shadow filter mode is irrelevant if there is no flashlight +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] + +#include "common_ps_fxc.h" +#include "common_flashlight_fxc.h" +#include "shader_constant_register_map.h" + +sampler TexSampler : register( s0 ); + +sampler RandRotSampler : register( s6 ); // RandomRotation sampler +sampler FlashlightSampler : register( s7 ); +sampler ShadowDepthSampler : register( s8 ); // Flashlight shadow depth map sampler + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); +const float4 g_FogTweakParams : register( c0 ); +#define g_fFogExponentTweak g_FogTweakParams.x +#define g_fFogScaleTweak g_FogTweakParams.y + +const float4 g_FlashlightAttenuationFactors : register( c22 ); +const float3 g_FlashlightPos : register( c23 ); +const float4x4 g_FlashlightWorldToTexture : register( c24 ); // through c27 + +struct PS_INPUT +{ + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate + + float4 worldPos_projPosZ : TEXCOORD1; // Necessary for pixel fog + + float4 color : COLOR1; + +#if FLASHLIGHT + float4 vProjPos : TEXCOORD2; +#endif +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 result = tex2D( TexSampler, i.baseTexCoord ); + + // Blend towards grey based on alpha + float flFactor = 1.0; +#if VERTEXALPHA + flFactor *= i.color.w; +#endif + +#if FLASHLIGHT + //if( bFlashlight ) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; + float2 vProjPos = float2(0, 0); +// On ps_2_b, we can do shadow mapping +#if ( FLASHLIGHTSHADOWS && (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) ) + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = FLASHLIGHTSHADOWS; + vProjPos = i.vProjPos.xy / i.vProjPos.w; // Screen-space position for shadow map noise +#endif + + float4 flashlightSpacePosition = mul( float4( i.worldPos_projPosZ.xyz, 1.0f ), g_FlashlightWorldToTexture ); + + float3 flashlightColor = DoFlashlight( g_FlashlightPos, i.worldPos_projPosZ.xyz, flashlightSpacePosition, + float3( 0.0f, 0.0f, 1.0f ), g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, vProjPos, false, float4(3/1024.0f, 0.0005f, 0.0f, 0.0f), false ); + + flFactor *= (flashlightColor.x + flashlightColor.y + flashlightColor.z); + + //result.xyz *= flashlightColor.xyz; + + //result.a *= (flashlightColor.x * flashlightColor.y * flashlightColor.z); + } +#endif + + result.xyz = lerp( float3( 0.5, 0.5, 0.5 ), result.xyz, flFactor ); + + // Since we're blending with a mod2x, we need to compensate with this hack + // NOTE: If the fog color (not fog density) is extremely dark, this can makes some decals seem + // a little transparent, but it's better than not doing this + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + fogFactor = pow( saturate( g_fFogScaleTweak * fogFactor ), g_fFogExponentTweak ); + + return FinalOutput( result, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_decalmodulate_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_decalmodulate_vs20.fxc new file mode 100644 index 00000000..de31830f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_decalmodulate_vs20.fxc @@ -0,0 +1,155 @@ +// based on vertexlit_and_unlit_generic_vs20.fxc +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "LIGHTING_PREVIEW" "0..1" [PC] +// STATIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// STATIC: "FLASHLIGHT" "0..1" + +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..0" [vs20] +// DYNAMIC: "SKINNING" "0..1" [vs30] + +// DYNAMIC: "MORPHING" "0..1" [vs30] [ = pShaderAPI->IsHWMorphingEnabled() ] + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; +static const bool g_bVertexColor = VERTEXCOLOR ? true : false; +#if ( defined( SHADER_MODEL_VS_3_0 ) && MORPHING ) + #define DECALOFFSET 1 +#else + #define DECALOFFSET 0 +#endif + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_10 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_11 ); +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float4 vColor : COLOR0; + float3 vSpecular : COLOR1; + // make these float2's and stick the [n n 0 1] in the dot math. + float4 vTexCoord0 : TEXCOORD0; + float4 vTexCoord1 : TEXCOORD1; + float4 vTexCoord2 : TEXCOORD2; + float4 vTexCoord3 : TEXCOORD3; + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + float fog : FOG; +#endif + + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate + float4 worldPos_ProjPosZ : TEXCOORD1; + float4 color : COLOR1; // Vertex color (from lighting or unlit) + +#if FLASHLIGHT + float4 vProjPos : TEXCOORD2; +#endif +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + float3 vNormal = 0; + if ( LIGHTING_PREVIEW || DECALOFFSET ) + { + // The vertex only contains valid normals if they are actually needed (fetching them when absent makes D3D complain) + DecompressVertex_Normal( v.vNormal, vNormal ); + } + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, + v.vVertexID, v.vTexCoord2, vPosition.xyz, vNormal ); +#endif + + // Perform skinning + float3 worldNormal, worldPos; + SkinPositionAndNormal( + g_bSkinning, + vPosition, vNormal, + v.vBoneWeights, v.vBoneIndices, + worldPos, worldNormal ); + + if ( !g_bVertexColor ) + { + worldNormal = normalize( worldNormal ); + } + +#if defined( SHADER_MODEL_VS_3_0 ) && MORPHING + // Avoid z precision errors + worldPos += worldNormal * 0.05f * v.vTexCoord2.z; +#endif + + // Transform into projection space + float4 projPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = projPos; + +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + o.fog = CalcFixedFunctionFog( worldPos, g_FogType ); +#endif + +#if FLASHLIGHT + // Transform into projection space + projPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.vProjPos = projPos; +#endif + + o.worldPos_ProjPosZ.xyz = worldPos.xyz; + o.worldPos_ProjPosZ.w = projPos.z; + + if ( g_bVertexColor ) + { + // Assume that this is unlitgeneric if you are using vertex color. + o.color.rgb = GammaToLinear( v.vColor.rgb ); + o.color.a = v.vColor.a; + } + else + { + o.color = float4( 1.0f, 1.0f, 1.0f, 1.0f ); + } + + // Base texture coordinates + o.baseTexCoord.x = dot( v.vTexCoord0, cBaseTexCoordTransform[0] ); + o.baseTexCoord.y = dot( v.vTexCoord0, cBaseTexCoordTransform[1] ); + +#if LIGHTING_PREVIEW + float dot=0.5+0.5*worldNormal*float3(0.7071,0.7071,0); + o.color.xyz=float3(dot,dot,dot); +#endif + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_depthwrite_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_depthwrite_ps2x.fxc new file mode 100644 index 00000000..1baabcf3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_depthwrite_ps2x.fxc @@ -0,0 +1,44 @@ +// STATIC: "COLOR_DEPTH" "0..1" + +// DYNAMIC: "ALPHACLIP" "0..1" + +const float g_AlphaThreshold : register( c0 ); + +const float2 g_vNearFarPlanes : register( c1 ); + #define g_flNearPlane g_vNearFarPlanes.x + #define g_flFarPlane g_vNearFarPlanes.y + +struct PS_INPUT +{ +#if ALPHACLIP + float2 texCoord0 : TEXCOORD0; +#endif + +#if COLOR_DEPTH + float4 vWorldPos_projPosZ : TEXCOORD1; +#endif +}; + +sampler BaseTextureSampler : register( s0 ); + +float4 main( PS_INPUT i ) : COLOR +{ + float4 color = float4( 1, 0, 0, 1 ); // opaque alpha....the color doesn't matter for this shader + +#if ALPHACLIP + color = tex2D( BaseTextureSampler, i.texCoord0 ); + + clip( color.a - g_AlphaThreshold ); + +#endif + +#if ( COLOR_DEPTH == 1 ) + + return float4( i.vWorldPos_projPosZ.w / g_flFarPlane, 0.0, 0.0, 1.0 ); + +#else + + return color; + +#endif +} diff --git a/mp/src/materialsystem/stdshaders/SDK_depthwrite_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_depthwrite_vs20.fxc new file mode 100644 index 00000000..306a8846 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_depthwrite_vs20.fxc @@ -0,0 +1,121 @@ +// STATIC: "ONLY_PROJECT_POSITION" "0..1" [XBOX] +// STATIC: "ONLY_PROJECT_POSITION" "0..0" [PC] +// STATIC: "COLOR_DEPTH" "0..1" +// STATIC: "TREESWAY" "0..2" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +#if ( TREESWAY ) + const float4 g_vTreeSwayParams0 : register( SHADER_SPECIFIC_CONST_2 ); + const float4 g_vTreeSwayParams1 : register( SHADER_SPECIFIC_CONST_3 ); + const float4 g_vTreeSwayParams2 : register( SHADER_SPECIFIC_CONST_4 ); + const float4 g_vTreeSwayParams3 : register( SHADER_SPECIFIC_CONST_5 ); + const float4 g_vTreeSwayParams4 : register( SHADER_SPECIFIC_CONST_9 ); + + #define g_flTime g_vTreeSwayParams0.x + #define g_vWindDir g_vTreeSwayParams0.yz + + #define g_flScrumbleFalloffCurve g_vTreeSwayParams1.x + #define g_flSwayFalloffCurve g_vTreeSwayParams1.y + #define g_flScrumbleSpeed g_vTreeSwayParams1.z + #define g_flFastSwaySpeedScale g_vTreeSwayParams1.w + + + #define g_flHeight g_vTreeSwayParams2.x + #define g_flStartHeight g_vTreeSwayParams2.y + #define g_flRadius g_vTreeSwayParams2.z + #define g_flStartRadius g_vTreeSwayParams2.w + + #define g_flSwaySpeed g_vTreeSwayParams3.x + #define g_flSwayIntensity g_vTreeSwayParams3.y + #define g_flScrumbleWaveCount g_vTreeSwayParams3.z + #define g_flScrumbleIntensity g_vTreeSwayParams3.w + + #define g_flWindSpeedLerpStart g_vTreeSwayParams4.x + #define g_flWindSpeedLerpEnd g_vTreeSwayParams4.y + + #include "tree_sway.h" +#endif + +struct VS_INPUT +{ + float4 vPos : POSITION; + float2 vTexCoord : TEXCOORD0; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + + // Position delta stream + float3 vPosFlex : POSITION1; + +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 vProjPos : POSITION; + +#if (ONLY_PROJECT_POSITION == 0) //360 sometimes runs without the pixel shader component, but has to patch this output if it does. + float2 texCoord : TEXCOORD0; +#endif + +#if COLOR_DEPTH + float4 vWorldPos_projPosZ : TEXCOORD1; +#endif + +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + float3 vWorldPos; + float4 vPosition = v.vPos; + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, vPosition.xyz ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, + v.vVertexID, float3(0, 0, 0), vPosition.xyz ); +#endif + + #if ( TREESWAY ) + { + vPosition.xyz = ComputeTreeSway( vPosition.xyz, g_flTime ); + } + #endif + + SkinPosition( g_bSkinning, vPosition, v.vBoneWeights, v.vBoneIndices, vWorldPos ); + + float4 vProjPos = mul( float4( vWorldPos, 1.0f ), cViewProj ); + + o.vProjPos = vProjPos; + +#if (ONLY_PROJECT_POSITION == 0) + o.texCoord = v.vTexCoord; +#endif + +#if ( COLOR_DEPTH && !ONLY_PROJECT_POSITION ) + o.vWorldPos_projPosZ.z = vProjPos.z; + o.vWorldPos_projPosZ.w = vProjPos.w; +#endif + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_ps2x.fxc new file mode 100644 index 00000000..1bae9265 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_ps2x.fxc @@ -0,0 +1,51 @@ +//========= Copyright ? 1996-2006, Valve Corporation, All rights reserved. ============// + +// Includes ======================================================================================= +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps30][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +#include "common_vertexlitgeneric_dx9.h" + +// Texture Samplers =============================================================================== +sampler g_tBaseSampler : register( s0 ); +sampler g_tFlowSampler : register( s1 ); +sampler g_tSelfIllumSampler : register( s2 ); + +// Shaders Constants and Globals ================================================================== +const float4 g_vPackedConst0 : register( c0 ); +#define g_flBlendStrength g_vPackedConst0.x +#define g_flTime g_vPackedConst0.y + +const float2 g_vEmissiveScrollVector : register( c1 ); +const float3 g_cSelfIllumTint : register( c2 ); + +// Interpolated values ============================================================================ +struct PS_INPUT +{ + float2 vTexCoord0 : TEXCOORD0; +}; + +// Main =========================================================================================== +//float4 main( PS_INPUT i ) : COLOR // Non-HDR for debugging +float4 main( PS_INPUT i ) : COLOR +{ + // Color texture + float4 cBaseColor = tex2D( g_tBaseSampler, i.vTexCoord0.xy ); + + // Fetch from dudv map and then fetch from emissive texture with new uv's & scroll + float4 vFlowValue = tex2D( g_tFlowSampler, i.vTexCoord0.xy ); + float2 vEmissiveTexCoord = vFlowValue.xy + ( g_vEmissiveScrollVector.xy * g_flTime ); + float4 cEmissiveColor = tex2D( g_tSelfIllumSampler, vEmissiveTexCoord.xy ); + + //===============// + // Combine terms // + //===============// + float4 result; + result.rgb = cBaseColor.rgb * cEmissiveColor.rgb * g_cSelfIllumTint.rgb; + result.rgb *= g_flBlendStrength; + + // Set alpha to 0.0f so it doesn't change dest alpha (I should probably disable dest alpha writes) + result.a = 0.0f; + + return FinalOutput( result, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_vs20.fxc new file mode 100644 index 00000000..80d21659 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_emissive_scroll_blended_pass_vs20.fxc @@ -0,0 +1,68 @@ +//========= Copyright ? 1996-2006, Valve Corporation, All rights reserved. ============// + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] + +// Includes +#include "common_vs_fxc.h" + +// Globals +static const bool g_bSkinning = SKINNING ? true : false; + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +// Structs +struct VS_INPUT +{ + float4 vPos : POSITION; // Position + float4 vBoneWeights : BLENDWEIGHT; // Skin weights + float4 vBoneIndices : BLENDINDICES; // Skin indices + float4 vTexCoord0 : TEXCOORD0; // Base texture coordinates + + float3 vPosFlex : POSITION1; // Delta positions for flexing + +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 vProjPosition : POSITION; // Projection-space position + float2 vTexCoord0 : TEXCOORD0; +}; + +// Main +VS_OUTPUT main( const VS_INPUT i ) +{ + VS_OUTPUT o; + + float4 vObjPosition = i.vPos; + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( i.vPosFlex, vObjPosition.xyz ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, i.vVertexID, float3( 0, 0, 0 ), vObjPosition.xyz ); +#endif + + // Transform the position + float3 vWorldPosition = { 0.0f, 0.0f, 0.0f }; + SkinPosition( g_bSkinning, vObjPosition, i.vBoneWeights, i.vBoneIndices, vWorldPosition ); + + // Transform into projection space + float4 vProjPosition = mul( float4( vWorldPosition, 1.0f ), cViewProj ); + o.vProjPosition = vProjPosition; + + // Pass through tex coords + o.vTexCoord0.xy = i.vTexCoord0.xy; + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_engine_post_ps20b.fxc b/mp/src/materialsystem/stdshaders/SDK_engine_post_ps20b.fxc new file mode 100644 index 00000000..e7d53ba0 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_engine_post_ps20b.fxc @@ -0,0 +1,394 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// FIXMEL4DTOMAINMERGE +// Need to re-enable bloom and disable other L4D-only features in here and the cpp file. + +// STATIC: "TOOL_MODE" "0..1" +// STATIC: "DEPTH_BLUR_ENABLE" "0..1" + +// DYNAMIC: "AA_ENABLE" "0..0" [PC] +// DYNAMIC: "AA_ENABLE" "0..1" [XBOX] + +// DYNAMIC: "COL_CORRECT_NUM_LOOKUPS" "0..3" + +// DYNAMIC: "CONVERT_FROM_LINEAR" "0..1" [ps20b] [ps30] [PC] +// DYNAMIC: "CONVERT_TO_LINEAR" "0..1" [ps20b] [ps30] [PC] +// DYNAMIC: "CONVERT_FROM_LINEAR" "0..0" [ps20b] [XBOX] +// DYNAMIC: "CONVERT_TO_LINEAR" "0..0" [ps20b] [XBOX] +// SKIP: ( $CONVERT_FROM_LINEAR == 0 ) && ( $CONVERT_TO_LINEAR == 1 ) +// SKIP: ( $TOOL_MODE == 0 ) && ( $CONVERT_TO_LINEAR == 1 ) + +// DYNAMIC: "NOISE_ENABLE" "0..1" [ps20b] [ps30] +// DYNAMIC: "VIGNETTE_ENABLE" "0..1" [ps20b] [ps30] +// DYNAMIC: "LOCAL_CONTRAST_ENABLE" "0..1" [ps20b] [ps30] +// DYNAMIC: "BLURRED_VIGNETTE_ENABLE" "0..1" [ps20b] [ps30] +// DYNAMIC: "VOMIT_ENABLE" "0..1" +// SKIP: ( $LOCAL_CONTRAST_ENABLE == 0 ) && ( $BLURRED_VIGNETTE_ENABLE == 1 ) + +// DYNAMIC: "TV_GAMMA" "0..1" [ps20b] [PC] +// DYNAMIC: "DESATURATEENABLE" "0..1" [ps20b] [PC] +// SKIP: ( $TOOL_MODE == 0 ) && $TV_GAMMA +// SKIP: ( $TOOL_MODE == 0 ) && $DESATURATEENABLE + +#include "common_ps_fxc.h" + +sampler BaseTextureSampler : register( s0 ); +sampler FBTextureSampler : register( s1 ); +sampler ColorCorrectionVolumeTexture0 : register( s2 ); +sampler ColorCorrectionVolumeTexture1 : register( s3 ); +sampler ColorCorrectionVolumeTexture2 : register( s4 ); +sampler ColorCorrectionVolumeTexture3 : register( s5 ); +sampler NoiseSampler : register( s6 ); +sampler VignetteSampler : register( s7 ); +sampler ScreenEffectSampler : register( s8 ); // used for vomit/paint screen particle effects + +float4 psTapOffs_Packed : register( c0 ); // psTapOffs_packed contains 1-pixel offsets: ( +dX, 0, +dY, -dX ) +float4 tweakables : register( c1 ); // (x - AA strength/unused) (y - reduction of 1-pixel-line blur) + // (z - edge threshold multipler) (w - tap offset multiplier) +float4 uvTransform : register( c2 ); // Transform BaseTexture UVs for use with the FBTexture + +float ColorCorrectionDefaultWeight : register( c3 ); +float4 ColorCorrectionVolumeWeights : register( c4 ); + +// Bloom & Depth Blur parameters +// x: bloom amount; multiply bloom downscale buffer by this value and add to base color +// y: bloom lerp amount; lerp between base color and blurred bloom buffer with this factor (allows for color bleeding in dark areas) +// z: depth blur focal plane distance. Value is in dest alpha space [0,1], not world units. +// w: depth blur scale value; scale distance from focal plane by this amount +float4 BloomParameters : register( c5 ); +#define g_flBloomAmount ( BloomParameters.x ) +#define g_flBloomLerpFactor ( BloomParameters.y ) +#define g_flDepthBlurFocalDistance ( BloomParameters.z ) +#define g_flDepthBlurScale ( BloomParameters.w ) + +float g_flNoiseScalar : register( c6 ); +float g_flTime : register( c7 ); +float4 g_vLocalContrastParams : register( c8 ); +#define g_flLocalContrastStrength g_vLocalContrastParams.x +#define g_flLocalContrastMidToneMask g_vLocalContrastParams.y +#define g_flBlurredVignetteStrength g_vLocalContrastParams.z + +float4 g_vLocalContrastVignetteParams : register( c9 ); +#define g_flLocalContrastVignetteStart g_vLocalContrastVignetteParams.x +#define g_flLocalContrastVignetteEnd g_vLocalContrastVignetteParams.y +#define g_flLocalContrastEdgeStrength g_vLocalContrastVignetteParams.z + +float g_flFadeToBlackStrength : register( c10 ); + +float4 g_vVomitColor[2] : register( c11 ); +#define g_flVomitRefractStrength g_vVomitColor[0].a + +float4 g_vViewportTransform : register( c13 ); +float4 g_vInvViewportTransform : register( c14 ); + +float4 g_vViewFadeColor : register( c15 ); + +float2 g_c16 : register( c16 ); +#define g_flDesaturation g_c16.x +#define g_flFadeMode2 g_c16.y + +float Luminance( float3 cColor ) +{ + float3 tmpv = { 0.2125, 0.7154, 0.0721 }; + float flLuminance = dot( cColor.rgb, tmpv.rgb ); + return flLuminance; +} + +float4 GetBloomColor( float2 bloomUV ) +{ + return tex2D( BaseTextureSampler, bloomUV ); +} + +float4 PerformColorCorrection( float4 outColor ) +{ + if (COL_CORRECT_NUM_LOOKUPS > 0) + { + // NOTE: This code requires the color correction texture to be 32 units to be correct. + // This code will cause (0,0,0) to be read from 0.5f/32 + // and (1,1,1) to be read from 31.5f/32 + float4 offsetOutColor = outColor*(31.0f/32.0f) + (0.5f/32.0f); + + outColor.rgb = outColor.rgb * ColorCorrectionDefaultWeight; + outColor.rgb += tex3D( ColorCorrectionVolumeTexture0, offsetOutColor.rgb ) * ColorCorrectionVolumeWeights.x; + if (COL_CORRECT_NUM_LOOKUPS > 1) + { + outColor.rgb += tex3D( ColorCorrectionVolumeTexture1, offsetOutColor.rgb ) * ColorCorrectionVolumeWeights.y; + if (COL_CORRECT_NUM_LOOKUPS > 2) + { + outColor.rgb += tex3D( ColorCorrectionVolumeTexture2, offsetOutColor.rgb ) * ColorCorrectionVolumeWeights.z; + if (COL_CORRECT_NUM_LOOKUPS > 3) + { + outColor.rgb += tex3D( ColorCorrectionVolumeTexture3, offsetOutColor.rgb ) * ColorCorrectionVolumeWeights.w; + } + } + } + } + + return outColor; +} + +float3 PerformVomitBlend( float3 vRefractParams, float3 vFullResColor, float3 vBlurredColor ) +{ + float3 vVomitColor = lerp( g_vVomitColor[0].rgb, g_vVomitColor[1].rgb, vRefractParams.z ); // vomit tint + + vFullResColor.rgb *= lerp( float3( 1, 1, 1 ), vVomitColor, vRefractParams.z ); // vomit tint full-res buffer + vFullResColor.rgb = lerp ( vFullResColor.rgb, vVomitColor.rgb * vBlurredColor.rgb, vRefractParams.z ); + return vFullResColor.rgb; +} + +float2 PerformUVTransform( float2 bloomUVs ) +{ + // NOTE: 'wz' is used since 'zw' is not a valid swizzle for ps20 shaders + return bloomUVs*uvTransform.wz + uvTransform.xy; +} + +// Apply TV Gamma for movie layoff specific to 360 TV movie playback path +float3 SrgbGammaToTvGamma( float3 cInput ) +{ + float3 cLinear = SrgbGammaToLinear( cInput ); + return pow( cLinear, 1.0f / 2.5f ); +} + + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 fbTexCoord = 0; + #if !defined( SHADER_MODEL_PS_2_0 ) + { + fbTexCoord.xy = PerformUVTransform( i.baseTexCoord ); + fbTexCoord.zw = i.baseTexCoord; + } + #else + { + fbTexCoord.xy = PerformUVTransform( i.baseTexCoord ); + } + #endif + + float4 cBloomBlurredLum = GetBloomColor( i.baseTexCoord ); // bloom color and blurred luminance in alpha + float4 vVomitRefractParams; + #if ( VOMIT_ENABLE == 1 ) + { + // perturb texture coordinate + vVomitRefractParams = tex2D( ScreenEffectSampler, i.baseTexCoord ); + fbTexCoord = fbTexCoord + g_flVomitRefractStrength * ( vVomitRefractParams.xyxy - 0.5 ); + + #if !defined( SHADER_MODEL_PS_2_0 ) + { + // screen coords -> viewport coords + float4 vNormalizedTexCoord = g_vViewportTransform.xyxy * fbTexCoord + g_vViewportTransform.zwzw; + // mirrored repeat texcoord math doesn't fit into 2.0 + vNormalizedTexCoord = min( 2.0 - vNormalizedTexCoord, abs( vNormalizedTexCoord ) ); + // viewport coords -> screen coords + fbTexCoord = g_vInvViewportTransform.xyxy * vNormalizedTexCoord + g_vInvViewportTransform.zwzw; + + cBloomBlurredLum = GetBloomColor( fbTexCoord.zw ); // fetch again with perturbed texcoords + } + #else + { + cBloomBlurredLum = GetBloomColor( fbTexCoord.xy ); // fetch again with perturbed texcoords + } + #endif + } + #endif + + float4 rawColor = tex2D( FBTextureSampler, fbTexCoord.xy ).rgba; + float3 baseColor = rawColor.rgb; + float depthValue = rawColor.a; + + #if ( CONVERT_FROM_LINEAR == 1 ) + { + baseColor.rgb = SrgbLinearToGamma( baseColor.rgb ); + } + #endif + + float4 outColor = float4( baseColor, 1 ); + + #if ( AA_ENABLE == 1 ) + { + float3 up; + float3 dn; + float3 lf; + float3 rt; + float3 uplf; + float3 uprt; + float3 dnlf; + float3 dnrt; + + // psTapOffs_packed contains 1-pixel offsets: ( +dX, 0, +dY, -dX ) + float4 texelDelta = psTapOffs_Packed.xyzw; + dn = tex2D( FBTextureSampler, fbTexCoord.xy + texelDelta.yz ).rgb; // ( 0,+1) + rt = tex2D( FBTextureSampler, fbTexCoord.xy + texelDelta.xy ).rgb; // (+1, 0) + up = tex2D( FBTextureSampler, fbTexCoord.xy - texelDelta.yz ).rgb; // ( 0,-1) + lf = tex2D( FBTextureSampler, fbTexCoord.xy - texelDelta.xy ).rgb; // (-1, 0) + dnlf = tex2D( FBTextureSampler, fbTexCoord.xy + texelDelta.wz ).rgb; // (-1,+1) + uprt = tex2D( FBTextureSampler, fbTexCoord.xy - texelDelta.wz ).rgb; // (+1,-1) + texelDelta.y = texelDelta.z; // Can't quite get all 8 sample offsets from a single float4 with the allowed swizzles! + uplf = tex2D( FBTextureSampler, fbTexCoord.xy + texelDelta.xy ).rgb; // (+1,+1) + dnrt = tex2D( FBTextureSampler, fbTexCoord.xy - texelDelta.xy ).rgb; // (-1,-1) + + // Generate the edge mask + float flBaseLum = Luminance( baseColor.rgb ); + float flEdge = saturate( abs( Luminance( dn.rgb ) - flBaseLum ) - 0.1 ); + flEdge += saturate( abs( Luminance( up.rgb ) - flBaseLum ) - 0.1 ); + flEdge += saturate( abs( Luminance( lf.rgb ) - flBaseLum ) - 0.1 ); + flEdge += saturate( abs( Luminance( rt.rgb ) - flBaseLum ) - 0.1 ); + flEdge *= 5.0; + + // Average full 3x3 neighborhood of pixels giving more weight to the center sample + float3 vBlurColor = ( baseColor.rgb * 4.0f ) + up.rgb + dn.rgb + lf.rgb + rt.rgb + dnrt.rgb + uprt.rgb + dnlf.rgb + uplf.rgb; + vBlurColor.rgb *= 0.0833333; // 1.0 / 12.0 + + // Lerp between crisp and blurry pixel based on edge mask + outColor.rgb = lerp( baseColor.rgb, vBlurColor.rgb, saturate( flEdge ) ); + } + #endif + + #if ( VOMIT_ENABLE == 1 ) + { + outColor.rgb = PerformVomitBlend( vVomitRefractParams.xyz, outColor.rgb, cBloomBlurredLum.aaa ); + } + #endif + + #if ( LOCAL_CONTRAST_ENABLE == 1 ) + { + float fMask = 1.0; + + // Extract midtones and limit contrast enhancement there + // TODO: This can probably go away for perf. + //float fBrightness = dot( outColor.rgb, float3( 0.3, 0.59, 0.11 ) ); + // bell-shaped mask + //fMask = smoothstep( 0.5 - g_flLocalContrastMidToneMask, 0.5, fBrightness ); + //fMask *= smoothstep( 0.5 + g_flLocalContrastMidToneMask, 0.5, fBrightness ); + + //fMask = smoothstep( 1.0, 0.5, fBrightness ); + + /* + // unsharp mask on luminance only + // This is the technically correct way, going to YUV, applying contrast to Y, and converting back to RGB + float3 outColorYUV; + outColorYUV.x = dot( outColor.rgb, float3( 0.299, 0.587, 0.114 ) ); + outColorYUV.y = dot( outColor.rgb, float3( -0.14713, -0.28886, 0.436 ) ); + outColorYUV.z = dot( outColor.rgb, float3( 0.615, -0.51499, -0.10001 ) ); + outColorYUV.x = outColorYUV.x + g_flLocalContrastStrength * fMask * ( outColorYUV.x - cBloomBlurredLum.aaa ); + outColor.r = dot( outColorYUV.xyz, float3( 1.0, 0.0, 1.13983 ) ); + outColor.g = dot( outColorYUV.xyz, float3( 1.0, -0.39465, -0.58060 ) ); + outColor.b = dot( outColorYUV.xyz, float3( 1.0, 2.03211, 0.0 ) ); + */ + + // This applies the delta contrast derived from the luminance to all color channels. The difference to the + // correct way is imperceptible. + float fLuminance = dot( outColor.rgb, float3( 0.299, 0.587, 0.114 ) ); + float fContrastLum = fLuminance + g_flLocalContrastStrength * ( fLuminance - cBloomBlurredLum.a ); + + // Mask off pixels that got very bright, to control super-contrast + //fMask = 1.0 - smoothstep( 0.3, 1.0, fContrastLum ); + + float2 vCenterDir = ( 2.0 * i.baseTexCoord.xy ) - 1.0; + float fMyVignette = smoothstep( g_flLocalContrastVignetteStart, g_flLocalContrastVignetteEnd, length( vCenterDir ) ); + float fMyVignette2 = fMyVignette; + fMyVignette = lerp( g_flLocalContrastStrength, g_flLocalContrastEdgeStrength, fMyVignette ); + + fMask = fMyVignette; + + // If the mask is positive, only brighten pixels. If the mask is negative, don't let it get less than -1.0. + //outColor.rgb += fMask * ( fLuminance - cBloomBlurredLum.aaa ); + outColor.rgb += max( fMask * ( fLuminance - cBloomBlurredLum.aaa ), -1.0 + step( 0.0, fMask ) ); // Selective clamp to positive adds 4 instructions + + #if ( BLURRED_VIGNETTE_ENABLE == 1 ) + outColor.rgb = lerp( outColor.rgb, cBloomBlurredLum.aaa, fMyVignette2 * g_flBlurredVignetteStrength ); + #endif + } + #endif + + // Composite bloom and full-screen + depth blur effects + #if ( DEPTH_BLUR_ENABLE ) + { + float blurFactor = g_flBloomLerpFactor + abs( depthValue - g_flDepthBlurFocalDistance ) * g_flDepthBlurScale; + blurFactor = clamp( blurFactor, 0, 1 ); + outColor.rgb = lerp( outColor.rgb, cBloomBlurredLum.rgb, blurFactor ); + outColor.rgb += g_flBloomAmount * cBloomBlurredLum.rgb; + } + #else + { + outColor.rgb += g_flBloomAmount * cBloomBlurredLum.rgb; + } + #endif + + // Used to be FADE_TYPE 0..2 combo + float3 vFadeDestColor = lerp( g_vViewFadeColor.rgb, g_vViewFadeColor.rgb * outColor.rgb, g_flFadeMode2 ); + outColor.rgb = lerp( outColor.rgb, vFadeDestColor.rgb, g_vViewFadeColor.a ); + + #if ( DESATURATEENABLE ) + { + float flLum = saturate( dot( outColor.rgb, float3( 0.3f, 0.59f, 0.11f) ) ); + outColor.rgb = lerp( saturate( outColor.rgb ), flLum.xxx, saturate( g_flDesaturation ) ); + } + #else + { + outColor = PerformColorCorrection( outColor ); // Color correction + } + #endif + + // Vignette + #if ( VIGNETTE_ENABLE == 1 ) + { + // Vignette + float2 vUv = i.baseTexCoord.xy; + //float2 vTmp = ( vUv.xy * 2.0 ) - 1.0; + float flVignette; + + //flVignette = 1.0 - pow( abs( vTmp.x ), 6.0f ); + //flVignette *= 1.0 - pow( abs( vTmp.y ), 6.0f ); + //flVignette = 1.0 - ( 1.0 - flVignette ) * ( ( saturate( ( 1.0 - vUv.y ) - 0.3 ) / 0.7 ) ); + + // This tex2D solves the 3 lines of math above + flVignette = tex2D( VignetteSampler, vUv.xy ).r; // Red is for the PC + flVignette = saturate( flVignette * 0.55 + 0.46 ); + + outColor.rgb *= flVignette; + } + #endif + + // Noise + #if ( NOISE_ENABLE == 1 ) + { + // Additive Noise + float2 vUv0 = i.baseTexCoord.xy * 10.0 + g_flTime; + float2 vUv1 = i.baseTexCoord.yx * 20.0 - g_flTime; + float2 vNoiseTexelUv; + vNoiseTexelUv.x = tex2D( NoiseSampler, vUv0.xy ).g; + vNoiseTexelUv.y = tex2D( NoiseSampler, vUv1.xy ).g; + float flNoiseTexel = tex2D( NoiseSampler, vNoiseTexelUv.xy ).g; + + float3 vTmp = { 0.2125f, 0.7154f, 0.0721f }; + float flLuminance = saturate( dot( outColor.rgb, vTmp.rgb ) ); + + float flNoiseScalar = 0.2f + 0.8f * ( saturate( pow( 1.0 - flLuminance, 12.0 ) ) ); + outColor.rgb += ( ( flNoiseTexel * 0.3f ) - 0.15f ) * g_flNoiseScalar * flNoiseScalar; + } + #endif + + // Fade to black + outColor.rgb = lerp( outColor.rgb, 0.0, g_flFadeToBlackStrength ); + + #if TV_GAMMA + { + // Used for SFM to record movies in native TV gamma space + outColor.rgb = SrgbGammaToTvGamma( outColor.rgb ); + } + #endif + + #if ( CONVERT_TO_LINEAR == 1 ) + { + // If we have a float back buffer, we want to remain in linear space after this shader + outColor.rgb = SrgbGammaToLinear( outColor.rgb ); + } + #endif + + return FinalOutput( outColor, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_eye_refract_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_eye_refract_ps2x.fxc new file mode 100644 index 00000000..057e8306 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eye_refract_ps2x.fxc @@ -0,0 +1,494 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. =========================== + +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" + +// STATIC: "SPHERETEXKILLCOMBO" "0..1" [ps20b] +// STATIC: "SPHERETEXKILLCOMBO" "0..1" [ps30] + +// STATIC: "RAYTRACESPHERE" "0..1" [ps20b] +// STATIC: "RAYTRACESPHERE" "0..1" [ps30] + +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] + +// DYNAMIC: "NUM_LIGHTS" "0..2" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps30] + +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] + +// We don't use other lights when doing the flashlight +// SKIP: ( $FLASHLIGHT != 0 ) && ( $NUM_LIGHTS > 0 ) + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps30] + +// SKIP: ( $RAYTRACESPHERE == 0 ) && ( $SPHERETEXKILLCOMBO == 1 ) [ps30] +// SKIP: ( $RAYTRACESPHERE == 0 ) && ( $SPHERETEXKILLCOMBO == 1 ) [ps20b] + +// Debug 2.0 shader locally +//#ifdef SHADER_MODEL_PS_2_B +//#undef SHADER_MODEL_PS_2_B +//#define SHADER_MODEL_PS_2_0 +//#endif + + +// Includes ======================================================================================= +#include "common_flashlight_fxc.h" +#include "shader_constant_register_map.h" + +// Texture Samplers =============================================================================== +sampler g_tCorneaSampler : register( s0 ); +sampler g_tIrisSampler : register( s1 ); +sampler g_tEyeReflectionCubemapSampler : register( s2 ); +sampler g_tEyeAmbientOcclSampler : register( s3 ); +sampler g_tLightwarpSampler : register( s4 ); // 1D texture for TF NPR lighting + +sampler g_tFlashlightCookieSampler : register( s5 ); +sampler g_tFlashlightDepthSampler : register( s6 ); +sampler g_tRandomRotationSampler : register( s7 ); + +// Shaders Constants and Globals ================================================================== +const float4 g_vPackedConst0 : register( c0 ); +#define g_flDilationFactor g_vPackedConst0.x +#define g_flGlossiness g_vPackedConst0.y +#define g_flAverageAmbient g_vPackedConst0.z +#define g_flCorneaBumpStrength g_vPackedConst0.w + +const float3 g_vEyeOrigin : register( c1 ); +const float4 g_vIrisProjectionU : register( c2 ); +const float4 g_vIrisProjectionV : register( c3 ); +const float4 g_vCameraPosition : register( c4 ); +const float3 g_cAmbientOcclColor : register( c5 ); + +const float4 g_vPackedConst6 : register( c6 ); +#define g_flEyeballRadius g_vPackedConst6.y //0.51f +//#define g_bRaytraceSphere g_vPackedConst6.z //1.0f +#define g_flParallaxStrength g_vPackedConst6.w //0.25f + +// Flashlight constants +const float4 g_vFlashlightAttenuationFactors : register( c7 ); // FarZ in w +const float3 g_vFlashlightPos : register( c8 ); +const float4 g_vShadowTweaks : register( c9 ); +const float4 g_ShaderControls : register( c10 ); +#define g_fPixelFogType g_ShaderControls.x + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); + +PixelShaderLightInfo g_sLightInfo[3] : register( PSREG_LIGHT_INFO_ARRAY ); // 2 registers each - 6 registers total + +// Interpolated values ============================================================================ +struct PS_INPUT +{ + float4 vAmbientOcclUv_fallbackCorneaUv : TEXCOORD0; + float4 cVertexLight : TEXCOORD1; // w is used for the flashlight pass + float4 vTangentViewVector : TEXCOORD2; // Tangent view vector (Note: w is used for flashlight pass) + float4 vWorldPosition_ProjPosZ : TEXCOORD3; + float3 vWorldNormal : TEXCOORD4; // World-space normal + float3 vWorldTangent : TEXCOORD5; // World-space tangent + float4 vLightFalloffCosine01 : TEXCOORD6; // Light falloff and cosine terms for first two local lights + float4 vLightFalloffCosine23 : TEXCOORD7; // Light falloff and cosine terms for next two local lights + + float3 vWorldBinormal : COLOR0; // World-space normal +}; + +// Ray sphere intersect returns distance along ray to intersection ================================ +float IntersectRaySphere ( float3 cameraPos, float3 ray, float3 sphereCenter, float sphereRadius) +{ + float3 dst = cameraPos.xyz - sphereCenter.xyz; + float B = dot(dst, ray); + float C = dot(dst, dst) - (sphereRadius * sphereRadius); + float D = B*B - C; + return (D > 0) ? (-B - sqrt(D)) : 0; +} + +// Calculate both types of Fog and lerp to get result +float CalcPixelFogFactorConst( float fPixelFogType, const float4 fogParams, const float3 vEyePos, const float3 vWorldPos, const float flProjPosZ ) +{ + float fRangeFog = CalcRangeFogFactorNonFixedFunction( vWorldPos, vEyePos, fogParams.z, fogParams.x, fogParams.w ); + float fHeightFog = CalcWaterFogAlpha( fogParams.y, vEyePos.z, vWorldPos.z, flProjPosZ, fogParams.w ); + return lerp( fRangeFog, fHeightFog, fPixelFogType ); +} + +// Blend both types of Fog and lerp to get result +float3 BlendPixelFogConst( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, float fPixelFogType ) +{ + pixelFogFactor = saturate( pixelFogFactor ); + float3 fRangeResult = lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog + float3 fHeightResult = lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); + return lerp( fRangeResult, fHeightResult, fPixelFogType ); +} + +float4 FinalOutputConst( const float4 vShaderColor, float pixelFogFactor, float fPixelFogType, const int iTONEMAP_SCALE_TYPE ) +{ + float4 result = vShaderColor; + if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR ) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA ) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } + + result.rgb = BlendPixelFogConst( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType ); + result.rgb = SRGBOutput( result.rgb ); //SRGB in pixel shader conversion + + return result; +} + + +// Main =========================================================================================== +float4 main( PS_INPUT i ) : COLOR +{ + // Set bools to compile out code + bool bFlashlight = ( FLASHLIGHT != 0 ) ? true : false; + bool bDoDiffuseWarp = LIGHTWARPTEXTURE ? true : false; + int nNumLights = FLASHLIGHT ? 1 : NUM_LIGHTS; // Flashlight is considered one light, otherwise, use numlights combo + +#if !defined( SHADER_MODEL_PS_2_0 ) + bool bRayCast = RAYTRACESPHERE ? true : false; + bool bRayCastTexKill = SPHERETEXKILLCOMBO ? true : false; +#endif + + float flFlashlightNDotL = i.vTangentViewVector.w; + float4 vFlashlightTexCoord = { 0.0f, 0.0f, 0.0f, 0.0f }; + if ( bFlashlight ) + { + vFlashlightTexCoord.xyzw = i.cVertexLight.xyzw; // This was hidden in this interpolator + i.cVertexLight.rgba = float4( 0.0f, 0.0f, 0.0f, 0.0f ); + } + + // Interpolated vectors + float3 vWorldNormal = i.vWorldNormal.xyz; + float3 vWorldTangent = i.vWorldTangent.xyz; + float3 vWorldBinormal = ( i.vWorldBinormal.xyz * 2.0f ) - 1.0f; // normalize( cross( vWorldNormal.xyz, vWorldTangent.xyz ) ); + + float3 vTangentViewVector = i.vTangentViewVector.xyz; + + // World position + float3 vWorldPosition = i.vWorldPosition_ProjPosZ.xyz; + + // World view vector to pixel + float3 vWorldViewVector = normalize( vWorldPosition.xyz - g_vCameraPosition.xyz ); + + //=================// + // TF NPR lighting // + //=================// + if ( bDoDiffuseWarp ) + { + // Replace the interpolated vertex light + if ( bFlashlight == true ) + { + // Deal with this below in the flashlight section + } + else + { + if ( nNumLights > 0 ) + { + float3 cWarpedLight = 2.0f * tex1D( g_tLightwarpSampler, i.vLightFalloffCosine01.z ).rgb; + i.cVertexLight.rgb += i.vLightFalloffCosine01.x * PixelShaderGetLightColor( g_sLightInfo, 0 ) * cWarpedLight.rgb; + } + + if ( nNumLights > 1 ) + { + float3 cWarpedLight = 2.0f * tex1D( g_tLightwarpSampler, i.vLightFalloffCosine01.w ).rgb; + i.cVertexLight.rgb += i.vLightFalloffCosine01.y * PixelShaderGetLightColor( g_sLightInfo, 1 ) * cWarpedLight.rgb; + } + + if ( nNumLights > 2 ) + { + float3 cWarpedLight = 2.0f * tex1D( g_tLightwarpSampler, i.vLightFalloffCosine23.z ).rgb; + i.cVertexLight.rgb += i.vLightFalloffCosine23.x * PixelShaderGetLightColor( g_sLightInfo, 2 ) * cWarpedLight.rgb; + } + + if ( nNumLights > 3 ) + { + float3 cWarpedLight = 2.0f * tex1D( g_tLightwarpSampler, i.vLightFalloffCosine23.w ).rgb; + i.cVertexLight.rgb += i.vLightFalloffCosine23.y * PixelShaderGetLightColor( g_sLightInfo, 3 ) * cWarpedLight.rgb; + } + } + } + + //==========================================================================================================// + // Ray cast against sphere representing eyeball to reduce artifacts from non-spherical morphed eye geometry // + //==========================================================================================================// +#if !defined( SHADER_MODEL_PS_2_0 ) + if ( bRayCast ) + { + float fSphereRayCastDistance = IntersectRaySphere( g_vCameraPosition.xyz, vWorldViewVector.xyz, g_vEyeOrigin.xyz, g_flEyeballRadius ); + vWorldPosition.xyz = g_vCameraPosition.xyz + ( vWorldViewVector.xyz * fSphereRayCastDistance ); + if (fSphereRayCastDistance == 0) + { + if ( bRayCastTexKill ) + clip(-1); // texkill to get a better silhouette + vWorldPosition.xyz = g_vEyeOrigin.xyz + ( vWorldNormal.xyz * g_flEyeballRadius ); + } + } +#endif + + //=================================// + // Generate sphere and cornea uv's // + //=================================// +#if !defined( SHADER_MODEL_PS_2_0 ) + float2 vCorneaUv; // Note: Cornea texture is a cropped version of the iris texture + vCorneaUv.x = dot( g_vIrisProjectionU, float4( vWorldPosition, 1.0f ) ); + vCorneaUv.y = dot( g_vIrisProjectionV, float4( vWorldPosition, 1.0f ) ); + float2 vSphereUv = ( vCorneaUv.xy * 0.5f ) + 0.25f; +#else // ps_20 + float2 vCorneaUv = i.vAmbientOcclUv_fallbackCorneaUv.wz; // Note: Cornea texture is a cropped version of the iris texture + float2 vSphereUv = ( vCorneaUv.xy * 0.5f ) + 0.25f; +#endif + + //=================================// + // Hacked parallax mapping on iris // + //=================================// + float fIrisOffset = tex2D( g_tCorneaSampler, vCorneaUv.xy ).b; + +#if !defined( SHADER_MODEL_PS_2_0 ) + float2 vParallaxVector = ( ( vTangentViewVector.xy * fIrisOffset * g_flParallaxStrength ) / ( 1.0f - vTangentViewVector.z ) ); // Note: 0.25 is a magic number + vParallaxVector.x = -vParallaxVector.x; //Need to flip x...not sure why. + //vParallaxVector.x *= -1.0; //Need to flip x...not sure why. + //vParallaxVector = 0.0f; //Disable parallax for debugging +#else // Disable parallax effect in 2.0 version + float2 vParallaxVector = { 0.0f, 0.0f }; +#endif + + float2 vIrisUv = vSphereUv.xy - vParallaxVector.xy; + + // Note: We fetch from this texture twice right now with different uv's for the color and alpha + float2 vCorneaNoiseUv = vSphereUv.xy + ( vParallaxVector.xy * 0.5 ); + float fCorneaNoise = tex2D( g_tIrisSampler, vCorneaNoiseUv.xy ).a; + + //===============// + // Cornea normal // + //===============// + // Sample 2D normal from texture + float3 vCorneaTangentNormal = { 0.0, 0.0, 1.0 }; + float4 vCorneaSample = tex2D( g_tCorneaSampler, vCorneaUv.xy ); + vCorneaTangentNormal.xy = vCorneaSample.rg - 0.5f; // Note: This scales the bump to 50% strength + + // Scale strength of normal + vCorneaTangentNormal.xy *= g_flCorneaBumpStrength; + + // Add in surface noise and imperfections (NOTE: This should be baked into the normal map!) + vCorneaTangentNormal.xy += fCorneaNoise * 0.1f; + + // Normalize tangent vector +#if !defined( SHADER_MODEL_PS_2_0 ) + // Since this isn't used later in 2.0, skip the normalize to save shader instructions + vCorneaTangentNormal.xyz = normalize( vCorneaTangentNormal.xyz ); +#endif + + // Transform into world space + float3 vCorneaWorldNormal = Vec3TangentToWorldNormalized( vCorneaTangentNormal.xyz, vWorldNormal.xyz, vWorldTangent.xyz, vWorldBinormal.xyz ); + + //============// + // Flashlight // + //============// + float3 vFlashlightVector = { 0.0f, 0.0f, 0.0f }; + float3 cFlashlightColorFalloff = { 0.0f, 0.0f, 0.0f }; + if ( bFlashlight == true ) + { + // Flashlight vector + vFlashlightVector.xyz = normalize( g_vFlashlightPos.xyz - i.vWorldPosition_ProjPosZ.xyz ); + + // Distance attenuation for flashlight and to fade out shadow over distance + float3 vDelta = g_vFlashlightPos.xyz - i.vWorldPosition_ProjPosZ.xyz; + float flDistSquared = dot( vDelta, vDelta ); + float flDist = sqrt( flDistSquared ); + float flFlashlightAttenuation = dot( g_vFlashlightAttenuationFactors.xyz, float3( 1.0f, 1.0f/flDist, 1.0f/flDistSquared ) ); + + // Flashlight cookie +#if !defined( SHADER_MODEL_PS_2_0 ) + float3 vProjCoords = vFlashlightTexCoord.xyz / vFlashlightTexCoord.w; + float3 cFlashlightCookieColor = tex2D( g_tFlashlightCookieSampler, vProjCoords ); +#else + float3 cFlashlightCookieColor = tex2Dproj( g_tFlashlightCookieSampler, vFlashlightTexCoord.xyzw ); +#endif + + // Shadow depth map +#if FLASHLIGHTSHADOWS && !defined( SHADER_MODEL_PS_2_0 ) + int nShadowLevel = FLASHLIGHTDEPTHFILTERMODE; + float flShadow = DoFlashlightShadow( g_tFlashlightDepthSampler, g_tRandomRotationSampler, vProjCoords, float2(0,0), nShadowLevel, g_vShadowTweaks, false ); + float flAttenuated = lerp( flShadow, 1.0f, g_vShadowTweaks.y ); // Blend between fully attenuated and not attenuated + flShadow = lerp( flAttenuated, flShadow, flFlashlightAttenuation ); // Blend between shadow and above, according to light attenuation + cFlashlightCookieColor *= flShadow; // Apply shadow term to cookie color +#endif + + // Flashlight color intensity (needs to be multiplied by global flashlight color later) + cFlashlightColorFalloff.rgb = flFlashlightAttenuation * cFlashlightCookieColor.rgb; + + // Add this into the interpolated lighting + if ( bDoDiffuseWarp ) + { + //float3 cWarpedLight = 2.0f * tex1D( g_tLightwarpSampler, flFlashlightNDotL ).rgb; + //i.cVertexLight.rgb += cFlashlightColorFalloff.rgb * cFlashlightColor.rgb * cWarpedLight.rgb; + i.cVertexLight.rgb += cFlashlightColorFalloff.rgb * cFlashlightColor.rgb * flFlashlightNDotL; // No light warp for now + } + else + { + i.cVertexLight.rgb += cFlashlightColorFalloff.rgb * cFlashlightColor.rgb * flFlashlightNDotL; + } + } + + //==============// + // Dilate pupil // + //==============// +#if !defined( SHADER_MODEL_PS_2_0 ) + vIrisUv.xy -= 0.5f; // Center around (0,0) + float fPupilCenterToBorder = saturate( length( vIrisUv.xy ) / 0.2f ); //Note: 0.2 is the uv radius of the iris + float fPupilDilateFactor = g_flDilationFactor; // This value should be between 0-1 + vIrisUv.xy *= lerp (1.0f, fPupilCenterToBorder, saturate( fPupilDilateFactor ) * 2.5f - 1.25f ); + vIrisUv.xy += 0.5f; +#endif + + //============// + // Iris color // + //============// + float4 cIrisColor = tex2D( g_tIrisSampler, vIrisUv.xy ); + + //==========================// + // Iris lighting highlights // + //==========================// + float3 cIrisLighting = float3( 0.0f, 0.0f, 0.0f ); + +#if !defined( SHADER_MODEL_PS_2_0 ) + // Mask off everything but the iris pixels + float fIrisHighlightMask = tex2D( g_tCorneaSampler, vCorneaUv.xy ).a; + + // Generate the normal + float3 vIrisTangentNormal = vCorneaTangentNormal.xyz; + vIrisTangentNormal.xy *= -2.5f; // I'm not normalizing on purpose + + for ( int j=0; j < nNumLights; j++ ) + { + // World light vector + float3 vWorldLightVector; + if ( ( j == 0 ) && ( bFlashlight == true ) ) + vWorldLightVector = vFlashlightVector.xyz; + else + vWorldLightVector = PixelShaderGetLightVector( i.vWorldPosition_ProjPosZ.xyz, g_sLightInfo, j ); + + // Tangent light vector + float3 vTangentLightVector = Vec3WorldToTangent( vWorldLightVector.xyz, vWorldNormal.xyz, vWorldTangent.xyz, vWorldBinormal.xyz ); + + // Adjust the tangent light vector to generate the iris lighting + float3 tmpv = -vTangentLightVector.xyz; + tmpv.xy *= -0.5f; //Flatten tangent view + tmpv.z = max( tmpv.z, 0.5f ); //Clamp z of tangent view to help maintain highlight + tmpv.xyz = normalize( tmpv.xyz ); + + // Core iris lighting math + float fIrisFacing = pow( abs( dot( vIrisTangentNormal.xyz, tmpv.xyz ) ), 6.0f ) * 0.5f; // Yes, 6.0 and 0.5 are magic numbers + + // Cone of darkness to darken iris highlights when light falls behind eyeball past a certain point + float flConeOfDarkness = pow( 1.0f - saturate( ( -vTangentLightVector.z - 0.25f ) / 0.75f ), 4.0f ); + //float flConeOfDarkness = pow( 1.0f - saturate( ( -dot( vIrisTangentNormal.xyz, vTangentLightVector.xyz ) - 0.15f ) / 0.85f ), 8.0f ); + + // Tint by iris color and cone of darkness + float3 cIrisLightingTmp = fIrisFacing * fIrisHighlightMask * flConeOfDarkness; + + // Attenuate by light color and light falloff + if ( ( j == 0 ) && ( bFlashlight == true ) ) + cIrisLightingTmp.rgb *= cFlashlightColorFalloff.rgb * cFlashlightColor.rgb; + else if ( j == 0 ) + cIrisLightingTmp.rgb *= i.vLightFalloffCosine01.x * PixelShaderGetLightColor( g_sLightInfo, 0 ); + else if ( j == 1 ) + cIrisLightingTmp.rgb *= i.vLightFalloffCosine01.y * PixelShaderGetLightColor( g_sLightInfo, 1 ); + else if ( j == 2 ) + cIrisLightingTmp.rgb *= i.vLightFalloffCosine23.x * PixelShaderGetLightColor( g_sLightInfo, 2 ); + else + cIrisLightingTmp.rgb *= i.vLightFalloffCosine23.y * PixelShaderGetLightColor( g_sLightInfo, 3 ); + + // Sum into final variable + cIrisLighting.rgb += cIrisLightingTmp.rgb; + } + + // Add slight view dependent iris lighting based on ambient light intensity to enhance situations with no local lights (0.5f is to help keep it subtle) + cIrisLighting.rgb += saturate( dot( vIrisTangentNormal.xyz, -vTangentViewVector.xyz ) ) * g_flAverageAmbient * fIrisHighlightMask * 0.5f; +#else + // Else, intensify light over cornea to simulate the brightening that happens above + cIrisLighting.rgb += i.cVertexLight.rgb * vCorneaSample.a; +#endif + + //===================// + // Ambient occlusion // + //===================// + float3 cAmbientOcclFromTexture = tex2D( g_tEyeAmbientOcclSampler, i.vAmbientOcclUv_fallbackCorneaUv.xy ).rgb; + float3 cAmbientOcclColor = lerp( g_cAmbientOcclColor, 1.0f, cAmbientOcclFromTexture.rgb ); // Color the ambient occlusion + i.cVertexLight.rgb *= cAmbientOcclColor.rgb; + + //==========================// + // Reflection from cube map // + //==========================// + float3 vCorneaReflectionVector = reflect ( vWorldViewVector.xyz, vCorneaWorldNormal.xyz ); + + //float3 cReflection = ENV_MAP_SCALE * texCUBE( g_tEyeReflectionCubemapSampler, vCorneaReflectionVector.xyz ).rgb; + float3 cReflection = g_flGlossiness * texCUBE( g_tEyeReflectionCubemapSampler, vCorneaReflectionVector.xyz ).rgb; + + // Hack: Only add in half of the env map for the flashlight pass. This looks reasonable. + if ( bFlashlight ) + { + cReflection.rgb *= 0.5f; + } + + //===========================// + // Glint specular highlights // + //===========================// + float3 cSpecularHighlights = 0.0f; + if ( bFlashlight ) + { + cSpecularHighlights.rgb += pow( saturate( dot( vCorneaReflectionVector.xyz, vFlashlightVector.xyz ) ), 128.0f ) * cFlashlightColorFalloff.rgb * cFlashlightColor.rgb; + } + else // no flashlight + { + if ( nNumLights > 0 ) + cSpecularHighlights.rgb += pow( saturate( dot( vCorneaReflectionVector.xyz, PixelShaderGetLightVector( i.vWorldPosition_ProjPosZ.xyz, g_sLightInfo, 0 ) ) ), 128.0f ) * i.vLightFalloffCosine01.x * PixelShaderGetLightColor( g_sLightInfo, 0 ); + + if ( nNumLights > 1 ) + cSpecularHighlights.rgb += pow( saturate( dot( vCorneaReflectionVector.xyz, PixelShaderGetLightVector( i.vWorldPosition_ProjPosZ.xyz, g_sLightInfo, 1 ) ) ), 128.0f ) * i.vLightFalloffCosine01.y * PixelShaderGetLightColor( g_sLightInfo, 1 ); + + if ( nNumLights > 2 ) + cSpecularHighlights.rgb += pow( saturate( dot( vCorneaReflectionVector.xyz, PixelShaderGetLightVector( i.vWorldPosition_ProjPosZ.xyz, g_sLightInfo, 2 ) ) ), 128.0f ) * i.vLightFalloffCosine23.x * PixelShaderGetLightColor( g_sLightInfo, 2 ); + + if ( nNumLights > 3 ) + cSpecularHighlights.rgb += pow( saturate( dot( vCorneaReflectionVector.xyz, PixelShaderGetLightVector( i.vWorldPosition_ProjPosZ.xyz, g_sLightInfo, 3 ) ) ), 128.0f ) * i.vLightFalloffCosine23.y * PixelShaderGetLightColor( g_sLightInfo, 3 ); + } + + //===============// + // Combine terms // + //===============// + float4 result; + + // Unlit iris, pupil, and sclera color + result.rgb = cIrisColor.rgb; + + // Add in slight cornea noise to help define raised cornea layer for close-ups + result.rgb += fCorneaNoise * 0.1f; + + // Diffuse light (Vertex lighting + extra iris caustic lighting) + result.rgb *= i.cVertexLight.rgb + cIrisLighting.rgb; + + // Environment map + result.rgb += cReflection.rgb * i.cVertexLight.rgb; + + // Local light glints + result.rgb += cSpecularHighlights.rgb; + + // Set alpha to 1.0 by default + result.a = 1.0; + +#if !defined( SHADER_MODEL_PS_2_0 ) + float fogFactor = CalcPixelFogFactorConst( g_fPixelFogType, g_FogParams, g_vCameraPosition.xyz, i.vWorldPosition_ProjPosZ.xyz, i.vWorldPosition_ProjPosZ.w ); + return FinalOutputConst( result, fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR ); +#else + float fogFactor = CalcPixelFogFactor( PIXEL_FOG_TYPE_NONE, g_FogParams, g_vCameraPosition.xyz, i.vWorldPosition_ProjPosZ.xyz, i.vWorldPosition_ProjPosZ.w ); + return FinalOutput( result, fogFactor, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR ); +#endif + +} diff --git a/mp/src/materialsystem/stdshaders/SDK_eye_refract_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_eye_refract_vs20.fxc new file mode 100644 index 00000000..af975f99 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eye_refract_vs20.fxc @@ -0,0 +1,217 @@ +//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// + +// STATIC: "INTRO" "0..1" +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "DYNAMIC_LIGHT" "0..1" +// DYNAMIC: "STATIC_LIGHT" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..4" +// DYNAMIC: "MORPHING" "0..1" [vs30] + +#include "vortwarp_vs20_helper.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_iFogType = DOWATERFOG; +static const bool g_bHalfLambert = HALFLAMBERT ? true : false; + +const float3 g_cEyeOrigin : register( SHADER_SPECIFIC_CONST_0 ); +const float4 g_vIrisProjectionU : register( SHADER_SPECIFIC_CONST_2 ); +const float4 g_vIrisProjectionV : register( SHADER_SPECIFIC_CONST_3 ); +const float4 g_vFlashlightPosition : register( SHADER_SPECIFIC_CONST_4 ); + +#if INTRO +const float4 g_vConst4 : register( SHADER_SPECIFIC_CONST_5 ); +#define g_vModelOrigin g_vConst4.xyz +#define g_flTime g_vConst4.w +#endif + +const float4 g_vFlashlightMatrixRow1 : register( SHADER_SPECIFIC_CONST_6 ); +const float4 g_vFlashlightMatrixRow2 : register( SHADER_SPECIFIC_CONST_7 ); +const float4 g_vFlashlightMatrixRow3 : register( SHADER_SPECIFIC_CONST_8 ); +const float4 g_vFlashlightMatrixRow4 : register( SHADER_SPECIFIC_CONST_9 ); + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_10 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_11 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + float4 vPos : POSITION; // Position + float4 vBoneWeights : BLENDWEIGHT; // Skin weights + float4 vBoneIndices : BLENDINDICES; // Skin indices + float4 vTexCoord0 : TEXCOORD0; // Base (sclera) texture coordinates + + // Position deltas + float3 vPosFlex : POSITION1; + +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) + float fog : FOG; // Fixed-function fog factor +#endif + float4 vAmbientOcclUv_fallbackCorneaUv : TEXCOORD0; // Base texture coordinate + float4 cVertexLight : TEXCOORD1; // Vertex-lit color (Note: w is used for flashlight pass) + float4 vTangentViewVector : TEXCOORD2; // Tangent view vector (Note: w is used for flashlight pass) + float4 vWorldPosition_ProjPosZ : TEXCOORD3; + float3 vWorldNormal : TEXCOORD4; // World-space normal + float3 vWorldTangent : TEXCOORD5; // World-space tangent + float4 vLightFalloffCosine01 : TEXCOORD6; // Light falloff and cosine terms for first two local lights + float4 vLightFalloffCosine23 : TEXCOORD7; // Light falloff and cosine terms for next two local lights + + float3 vWorldBinormal : COLOR0; // World-space normal +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o; + + bool bDynamicLight = DYNAMIC_LIGHT ? true : false; + bool bStaticLight = STATIC_LIGHT ? true : false; + int nNumLights = NUM_LIGHTS; + + float4 vPosition = v.vPos; + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, vPosition.xyz ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, float3( 0, 0, 0 ), vPosition.xyz ); +#endif + + // Transform the position + float3 vWorldPosition; + SkinPosition( g_bSkinning, vPosition, v.vBoneWeights, v.vBoneIndices, vWorldPosition ); + + // Note: I'm relying on the iris projection vector math not changing or this will break + float3 vEyeSocketUpVector = normalize( -g_vIrisProjectionV.xyz ); + float3 vEyeSocketLeftVector = normalize( -g_vIrisProjectionU.xyz ); + +#if INTRO + float3 dummy = float3( 0.0f, 0.0f, 0.0f ); + WorldSpaceVertexProcess( g_flTime, g_vModelOrigin, vWorldPosition, dummy, dummy, dummy ); +#endif + + o.vWorldPosition_ProjPosZ.xyz = vWorldPosition.xyz; + + // Transform into projection space + //vWorldPosition -= ( vWorldPosition - g_cEyeOrigin ) * 0.9; //Debug to visualize eye origin + float4 vProjPos = mul( float4( vWorldPosition, 1.0f ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( vWorldPosition, 1.0f ), cViewProjZ ); + + + o.vWorldPosition_ProjPosZ.w = vProjPos.z; + +#if !defined( _X360 ) + // Set fixed-function fog factor + o.fog = CalcFog( vWorldPosition, vProjPos, g_iFogType ); +#endif + + // Normal = (Pos - Eye origin) + float3 vWorldNormal = normalize( vWorldPosition.xyz - g_cEyeOrigin.xyz ); + o.vWorldNormal.xyz = vWorldNormal.xyz; + + // Tangent & binormal + /* + float3 vWorldBinormal = normalize( cross( vWorldNormal.xyz, vEyeSocketLeftVector.xyz ) ); + o.vWorldBinormal.xyz = vWorldBinormal.xyz * 0.5f + 0.5f; + + float3 vWorldTangent = normalize( cross( vWorldBinormal.xyz, vWorldNormal.xyz ) ); + o.vWorldTangent.xyz = vWorldTangent.xyz; + //*/ + + //* + float3 vWorldTangent = normalize( cross( vEyeSocketUpVector.xyz, vWorldNormal.xyz ) ); + o.vWorldTangent.xyz = vWorldTangent.xyz; + + float3 vWorldBinormal = normalize( cross( vWorldNormal.xyz, vWorldTangent.xyz ) ); + o.vWorldBinormal.xyz = vWorldBinormal.xyz * 0.5f + 0.5f; + //*/ + + float3 vWorldViewVector = normalize (vWorldPosition.xyz - cEyePos.xyz); + o.vTangentViewVector.xyz = Vec3WorldToTangentNormalized (vWorldViewVector.xyz, vWorldNormal.xyz, vWorldTangent.xyz, vWorldBinormal.xyz); + + // AV - I think this will effectively make the eyeball less rounded left to right to help vertext lighting quality + // AV - Note: This probably won't look good if put on an exposed eyeball + //float vNormalDotSideVec = -dot( vWorldNormal, g_vEyeballUp ) * 0.5f; + float vNormalDotSideVec = -dot( vWorldNormal, vEyeSocketLeftVector) * 0.5f; + float3 vBentWorldNormal = normalize(vNormalDotSideVec * vEyeSocketLeftVector + vWorldNormal); + + // Compute vertex lighting + o.cVertexLight.a = 0.0f; //Only used for flashlight pass + o.cVertexLight.rgb = DoLightingUnrolled( vWorldPosition, vBentWorldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, g_bHalfLambert, nNumLights ); + + // Only interpolate ambient light for TF NPR lighting + bool bDoDiffuseWarp = LIGHTWARPTEXTURE ? true : false; + if ( bDoDiffuseWarp ) + { + if( bDynamicLight ) + { + o.cVertexLight.rgb = AmbientLight( vBentWorldNormal.xyz ); + } + else + { + o.cVertexLight.rgb = float3( 0.0f, 0.0f, 0.0f ); + } + } + +// NOTE: it appears that o.vLightFalloffCosine01 and o.vLightFalloffCosine23 are filled in even if +// we don't have enough lights, meaning we pass garbage to the pixel shader which then throws it away + + // Light falloff for first two local lights + o.vLightFalloffCosine01.x = VertexAttenInternal( vWorldPosition.xyz, 0 ); + o.vLightFalloffCosine01.y = VertexAttenInternal( vWorldPosition.xyz, 1 ); + o.vLightFalloffCosine01.z = CosineTermInternal( vWorldPosition.xyz, vWorldNormal.xyz, 0, g_bHalfLambert ); + o.vLightFalloffCosine01.w = CosineTermInternal( vWorldPosition.xyz, vWorldNormal.xyz, 1, g_bHalfLambert ); + + // Light falloff for next two local lights + o.vLightFalloffCosine23.x = VertexAttenInternal( vWorldPosition.xyz, 2 ); + o.vLightFalloffCosine23.y = VertexAttenInternal( vWorldPosition.xyz, 3 ); + o.vLightFalloffCosine23.z = CosineTermInternal( vWorldPosition.xyz, vWorldNormal.xyz, 2, g_bHalfLambert ); + o.vLightFalloffCosine23.w = CosineTermInternal( vWorldPosition.xyz, vWorldNormal.xyz, 3, g_bHalfLambert ); + + // Texture coordinates set by artists for ambient occlusion + o.vAmbientOcclUv_fallbackCorneaUv.xy = v.vTexCoord0.xy; + + // Cornea uv for ps.2.0 fallback + float2 vCorneaUv; // Note: Cornea texture is a cropped version of the iris texture + vCorneaUv.x = dot( g_vIrisProjectionU, float4( vWorldPosition, 1.0f ) ); + vCorneaUv.y = dot( g_vIrisProjectionV, float4( vWorldPosition, 1.0f ) ); + float2 vSphereUv = ( vCorneaUv.xy * 0.5f ) + 0.25f; + o.vAmbientOcclUv_fallbackCorneaUv.wz = vCorneaUv.xy; // Note: wz unpacks faster than zw in ps.2.0! + + // Step on the vertex light interpolator for the flashlight tex coords + bool bFlashlight = ( FLASHLIGHT != 0 ) ? true : false; + o.vTangentViewVector.w = 0.0f; + if ( bFlashlight ) + { + o.cVertexLight.x = dot( g_vFlashlightMatrixRow1.xyzw, float4( vWorldPosition, 1.0f ) ); + o.cVertexLight.y = dot( g_vFlashlightMatrixRow2.xyzw, float4( vWorldPosition, 1.0f ) ); + o.cVertexLight.z = dot( g_vFlashlightMatrixRow3.xyzw, float4( vWorldPosition, 1.0f ) ); + o.cVertexLight.w = dot( g_vFlashlightMatrixRow4.xyzw, float4( vWorldPosition, 1.0f ) ); + + o.vTangentViewVector.w = saturate( dot( vBentWorldNormal.xyz, normalize ( g_vFlashlightPosition.xyz - vWorldPosition.xyz ) ) ); // Flashlight N.L with modified normal + + // Half lambert version + //o.cVertexLight.z = dot( vBentWorldNormal.xyz, normalize ( g_vFlashlightPosition.xyz - vWorldPosition.xyz ) ); // Flashlight N.L with modified normal + //o.cVertexLight.z = ( o.cVertexLight.z * 0.5f ) + 0.5f; + //o.cVertexLight.z *= o.cVertexLight.z; + } + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_inc.fxc b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_inc.fxc new file mode 100644 index 00000000..69316a39 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_inc.fxc @@ -0,0 +1,92 @@ +//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "common_flashlight_fxc.h" +#include "shader_constant_register_map.h" + + +const float4 g_vShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS ); + +sampler SpotSampler : register( s0 ); +sampler BaseTextureSampler : register( s1 ); +sampler IrisSampler : register( s3 ); + +#if FLASHLIGHTSHADOWS && (!SHADER_MODEL_PS_1_1) && (!SHADER_MODEL_PS_1_4) && (!SHADER_MODEL_PS_2_0) +sampler FlashlightDepthSampler : register( s4 ); +sampler RandomRotationSampler : register( s5 ); +#endif + +#if defined( SHADER_MODEL_PS_1_1 ) || defined ( SHADER_MODEL_PS_1_4 ) + +#else + const float4 g_FogParams : register( PSREG_FOG_PARAMS ); + const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); +#endif + +struct PS_INPUT +{ + float4 spotTexCoord : TEXCOORD0; + float2 baseTexCoord : TEXCOORD1; + float2 irisTexCoord : TEXCOORD3; +#if defined( SHADER_MODEL_PS_1_1 ) || defined ( SHADER_MODEL_PS_1_4 ) + float3 vertAtten : COLOR0; +#else + float3 vertAtten : TEXCOORD4; + float3 worldPos : TEXCOORD5; + float3 projPos : TEXCOORD7; +#endif +}; + +float4 main( PS_INPUT i ) : COLOR +{ +#if defined(SHADER_MODEL_PS_2_0) + float3 spotColor = tex2Dproj( SpotSampler, i.spotTexCoord.xyzw ) * cFlashlightColor; +#elif ( defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) + float3 vProjCoords = i.spotTexCoord.xyz / i.spotTexCoord.w; + float3 spotColor = tex2D( SpotSampler, vProjCoords ) * cFlashlightColor; +#else + float3 spotColor = tex2D( SpotSampler, i.spotTexCoord ); +#endif + + float4 baseSample = tex2D( BaseTextureSampler, i.baseTexCoord ); + float4 irisSample = tex2D( IrisSampler, i.irisTexCoord ); + + float3 outcolor = float3(1,1,1); + +#if !defined( SHADER_MODEL_PS_1_1 ) && !defined( SHADER_MODEL_PS_1_4 ) + if( i.spotTexCoord.w <= 0.0f ) + { + outcolor = float3(0,0,0); + } +#endif + + // Composite the iris and sclera together +#if defined( SHADER_MODEL_PS_1_1 ) || defined ( SHADER_MODEL_PS_1_4 ) + float3 albedo = lerp( baseSample.xyz, irisSample.xyz, irisSample.a ); +#else + float3 albedo = lerp( baseSample.xyz, irisSample.xyz * 0.5f, irisSample.a ); // dim down the iris in HDR +#endif + + // Do shadow depth mapping... +#if FLASHLIGHTSHADOWS && ( defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) + float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, i.projPos.xy / i.projPos.z, FLASHLIGHTDEPTHFILTERMODE, g_vShadowTweaks, true ); + float flAttenuated = lerp( flShadow, 1.0f, g_vShadowTweaks.y ); // Blend between fully attenuated and not attenuated + flShadow = lerp( flAttenuated, flShadow, dot(i.vertAtten, float3(0.30f, 0.59f, 0.11f) ) ); // Blend between shadow and above, according to light attenuation + outcolor *= flShadow * spotColor * albedo; +#else + outcolor *= spotColor * albedo; +#endif + + // NOTE!! This has to be last to avoid loss of range. + outcolor *= i.vertAtten; +#if defined( SHADER_MODEL_PS_1_1 ) || defined ( SHADER_MODEL_PS_1_4 ) + return float4( outcolor, baseSample.a ); +#else + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos, i.projPos.z ); + return FinalOutput( float4( outcolor, 1.0f ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +#endif + +} diff --git a/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps11.fxc b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps11.fxc new file mode 100644 index 00000000..25e0702e --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps11.fxc @@ -0,0 +1,9 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#define HDRTYPE HDR_TYPE_NONE + +#include "eyes_flashlight_inc.fxc" diff --git a/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps2x.fxc new file mode 100644 index 00000000..eb00fc04 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_ps2x.fxc @@ -0,0 +1,15 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] + +#include "eyes_flashlight_inc.fxc" diff --git a/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_vs20.fxc new file mode 100644 index 00000000..e2e37dc1 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eyes_flashlight_vs20.fxc @@ -0,0 +1,145 @@ +// ------------------------------------------------------------------------------ +// $cLight0Pos = world space light position +// $SHADER_SPECIFIC_CONST_1 = spotlight projection +// $SHADER_SPECIFIC_CONST_2 = spotlight projection +// $SHADER_SPECIFIC_CONST_3 = spotlight projection +// $SHADER_SPECIFIC_CONST_4 = spotlight projection +// $SHADER_SPECIFIC_CONST_5 = far z +// $SHADER_SPECIFIC_CONST_6 = eyeball origin +// $SHADER_SPECIFIC_CONST_7 = eyeball up * 0.5 +// $SHADER_SPECIFIC_CONST_8 = iris projection U +// $SHADER_SPECIFIC_CONST_9 = iris projection V +// ------------------------------------------------------------------------------ + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; + +const float4 cLightPosition : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cSpotlightProj1 : register( SHADER_SPECIFIC_CONST_1 ); +const float4 cSpotlightProj2 : register( SHADER_SPECIFIC_CONST_2 ); +const float4 cSpotlightProj3 : register( SHADER_SPECIFIC_CONST_3 ); +const float4 cSpotlightProj4 : register( SHADER_SPECIFIC_CONST_4 ); +const float4 cFlashlighAtten : register( SHADER_SPECIFIC_CONST_5 ); // const, linear, quadratic & farZ +const float4 cIrisProjectionU : register( SHADER_SPECIFIC_CONST_8 ); +const float4 cIrisProjectionV : register( SHADER_SPECIFIC_CONST_9 ); + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_10 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_11 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + float4 vPos : POSITION; // Position + float4 vBoneWeights : BLENDWEIGHT; // Skin weights + float4 vBoneIndices : BLENDINDICES; // Skin indices + float4 vNormal : NORMAL; + float4 vTexCoord0 : TEXCOORD0; // Base (sclera) texture coordinates + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) + float fog : FOG; // Fixed-function fog factor +#endif + float4 spotTexCoord : TEXCOORD0; // Spotlight texture coordinates + float2 baseTexCoord : TEXCOORD1; // Base texture coordinates + float2 irisTexCoord : TEXCOORD3; // Iris texture coordinates + float3 vertAtten : TEXCOORD4; // vertex attenuation + float3 worldPos : TEXCOORD5; + float3 projPosXYZ : TEXCOORD7; +}; + + +float RemapValClamped_01( float val, float A, float B ) +{ + float cVal = (val - A) / (B - A); + cVal = saturate( cVal ); + return cVal; +} + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + float3 vNormal; + DecompressVertex_Normal( v.vNormal, vNormal ); + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, float3( 0, 0, 0 ), vPosition.xyz, vNormal ); +#endif + + // Perform skinning + float3 worldNormal, worldPos; + SkinPositionAndNormal( + g_bSkinning, + vPosition, vNormal, + v.vBoneWeights, v.vBoneIndices, + worldPos, worldNormal ); + + worldNormal = normalize( worldNormal ); + + // Transform into projection space + float4 projPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = projPos; + o.projPosXYZ = projPos.xyz; + o.worldPos = worldPos.xyz; + +#if !defined( _X360 ) + // Set fixed-function fog factor + o.fog = CalcFog( worldPos, o.projPos, g_FogType ); +#endif + + // Base texture coordinates + o.baseTexCoord = v.vTexCoord0; + + // Spotlight texture coordinates + o.spotTexCoord.x = dot( cSpotlightProj1, float4(worldPos, 1) ); + o.spotTexCoord.y = dot( cSpotlightProj2, float4(worldPos, 1) ); + o.spotTexCoord.z = dot( cSpotlightProj3, float4(worldPos, 1) ); + o.spotTexCoord.w = dot( cSpotlightProj4, float4(worldPos, 1) ); + + // Compute vector to light + float3 vWorldPosToLightVector = cLightPosition.xyz - worldPos; + + float3 vDistAtten = float3(1, 1, 1); + vDistAtten.z = dot( vWorldPosToLightVector, vWorldPosToLightVector ); // distsquared + vDistAtten.y = rsqrt( vDistAtten.z ); // 1 / dist + + float flDist = vDistAtten.z * vDistAtten.y; // dist + vDistAtten.z = 1.0f / vDistAtten.z; // 1 / distsquared + + float fFarZ = cFlashlighAtten.w; + + float endFalloffFactor = RemapValClamped_01( flDist, fFarZ, 0.6 * fFarZ ); + o.vertAtten.xyz = endFalloffFactor * dot( vDistAtten, cFlashlighAtten.xyz ); + + o.vertAtten *= dot( normalize( vWorldPosToLightVector ), worldNormal ); + + o.irisTexCoord.x = dot( cIrisProjectionU, float4(worldPos, 1) ); + o.irisTexCoord.y = dot( cIrisProjectionV, float4(worldPos, 1) ); + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_eyes_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_eyes_ps2x.fxc new file mode 100644 index 00000000..aad7afce --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eyes_ps2x.fxc @@ -0,0 +1,68 @@ +//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps30] +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +sampler BaseTextureSampler : register( s0 ); +sampler IrisSampler : register( s1 ); +sampler GlintSampler : register( s2 ); +const float4 cEyeScalars : register( c0 ); // { Dilation, ambient, x, x } + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; + float2 irisTexCoord : TEXCOORD1; + float2 glintTexCoord : TEXCOORD2; + float3 vertAtten : TEXCOORD3; + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + +#define fDilationFactor cEyeScalars.x +#define fGlintDamping cEyeScalars.y + +float4 main( PS_INPUT i ) : COLOR +{ + float4 baseSample = tex2D( BaseTextureSampler, i.baseTexCoord ); + float4 glintSample = tex2D( GlintSampler, i.glintTexCoord ); +/* + // Dilate the pupil/iris texture (1 is max dilation, 0 is none) + float2 biasedCoords = i.irisTexCoord * 2.0f - 1.0f; // -1 to +1 range + float fDilatability = saturate(0.8f - sqrt(dot(biasedCoords, biasedCoords) )); // 1 in the center, fading out to 0 at 0.8 from center, since irises are inset into maps + float2 scaledCoords = biasedCoords * (1 + fDilatability); // Maximal dilation + + // Blend undilated and maximally dilated based upon dilation factor + float2 dilatedCoords = lerp( scaledCoords, biasedCoords, 1.0f-saturate(cDilationFactor.x)); + dilatedCoords = dilatedCoords * 0.5f + 0.5f; // Back to 0..1 range +*/ + + float4 irisSample = tex2D( IrisSampler, i.irisTexCoord ); // Sample the iris map using dilated coordinates + + float4 result; + result.rgb = lerp( baseSample.rgb, irisSample.rgb, irisSample.a ); + result.rgb *= i.vertAtten; + result.rgb += glintSample.rgb * fGlintDamping; + result.a = baseSample.a; + + bool bWriteDepthToAlpha = false; + + // ps_2_b and beyond +#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) + bWriteDepthToAlpha = WRITE_DEPTH_TO_DESTALPHA != 0; +#endif + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( result, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, bWriteDepthToAlpha, i.worldPos_projPosZ.w ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_eyes_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_eyes_vs20.fxc new file mode 100644 index 00000000..b055eed7 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_eyes_vs20.fxc @@ -0,0 +1,145 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== +// $SHADER_SPECIFIC_CONST_0 = eyeball origin +// $SHADER_SPECIFIC_CONST_1 = eyeball up * 0.5 +// $SHADER_SPECIFIC_CONST_2 = iris projection U +// $SHADER_SPECIFIC_CONST_3 = iris projection V +// $SHADER_SPECIFIC_CONST_4 = glint projection U +// $SHADER_SPECIFIC_CONST_5 = glint projection V +//============================================================================= + +// STATIC: "INTRO" "0..1" +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "DYNAMIC_LIGHT" "0..1" +// DYNAMIC: "STATIC_LIGHT" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] + +#include "vortwarp_vs20_helper.h" + +static const int g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; +static const bool g_bHalfLambert = HALFLAMBERT ? true : false; + +const float3 cEyeOrigin : register( SHADER_SPECIFIC_CONST_0 ); +const float3 cHalfEyeballUp : register( SHADER_SPECIFIC_CONST_1 ); +const float4 cIrisProjectionU : register( SHADER_SPECIFIC_CONST_2 ); +const float4 cIrisProjectionV : register( SHADER_SPECIFIC_CONST_3 ); +const float4 cGlintProjectionU : register( SHADER_SPECIFIC_CONST_4 ); +const float4 cGlintProjectionV : register( SHADER_SPECIFIC_CONST_5 ); +#if INTRO +const float4 const4 : register( SHADER_SPECIFIC_CONST_6 ); +#define g_Time const4.w +#define modelOrigin const4.xyz +#endif + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_7 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_8 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + float4 vPos : POSITION; // Position + float4 vBoneWeights : BLENDWEIGHT; // Skin weights + float4 vBoneIndices : BLENDINDICES; // Skin indices + float4 vTexCoord0 : TEXCOORD0; // Base (sclera) texture coordinates + + float3 vPosFlex : POSITION1; // Delta positions for flexing +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) + float fog : FOG; // Fixed-function fog factor +#endif + float2 baseTC : TEXCOORD0; // Base texture coordinate + float2 irisTC : TEXCOORD1; // Iris texture coordinates + float2 glintTC : TEXCOORD2; // Glint texture coordinates + float3 vColor : TEXCOORD3; // Vertex-lit color + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog + +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o; + + bool bDynamicLight = DYNAMIC_LIGHT ? true : false; + bool bStaticLight = STATIC_LIGHT ? true : false; + + float4 vPosition = v.vPos; + float3 dummy = v.vPos.xyz; // dummy values that can't be optimized out + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, vPosition.xyz ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, dummy, vPosition.xyz ); +#endif + + // Transform the position and dummy normal (not doing the dummy normal causes invariance issues with the flashlight!) + float3 worldNormal, worldPos; + SkinPositionAndNormal( + g_bSkinning, + vPosition, dummy, + v.vBoneWeights, v.vBoneIndices, + worldPos, worldNormal ); + +#if INTRO + WorldSpaceVertexProcess( g_Time, modelOrigin, worldPos, dummy, dummy, dummy ); +#endif + + // Transform into projection space + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); + +#if !defined( _X360 ) + // Set fixed-function fog factor + o.fog = CalcFog( worldPos, vProjPos, g_FogType ); +#endif + + // Normal = (Pos - Eye origin) - just step on dummy normal created above + worldNormal = worldPos - cEyeOrigin; + + // Normal -= 0.5f * (Normal dot Eye Up) * Eye Up + float normalDotUp = -dot( worldNormal, cHalfEyeballUp) * 0.5f; + worldNormal = normalize(normalDotUp * cHalfEyeballUp + worldNormal); + + // Vertex lighting +#if ( USE_STATIC_CONTROL_FLOW || defined ( SHADER_MODEL_VS_3_0 ) ) + o.vColor = DoLighting( worldPos, worldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, g_bHalfLambert ); +#else + o.vColor = DoLightingUnrolled( worldPos, worldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, g_bHalfLambert, NUM_LIGHTS ); +#endif + + // Texture 0 is the base texture + // Texture 1 is a planar projection used for the iris + // Texture 2 is a planar projection used for the glint + o.baseTC = v.vTexCoord0; + o.irisTC.x = dot( cIrisProjectionU, float4(worldPos, 1) ); + o.irisTC.y = dot( cIrisProjectionV, float4(worldPos, 1) ); + o.glintTC.x = dot( cGlintProjectionU, float4(worldPos, 1) ); + o.glintTC.y = dot( cGlintProjectionV, float4(worldPos, 1) ); + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_flashlight_ps11.fxc b/mp/src/materialsystem/stdshaders/SDK_flashlight_ps11.fxc new file mode 100644 index 00000000..07e36a41 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_flashlight_ps11.fxc @@ -0,0 +1,69 @@ +//====== Copyright ? 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "NORMALMAP" "0..1" +// STATIC: "NOCULL" "0..1" + +#include "common_ps_fxc.h" + +sampler SpotSampler : register( s0 ); +sampler BaseTextureSampler : register( s1 ); +sampler NormalizingCubemapSampler : register( s2 ); +// use a normalizing cube map here if we aren't normal mapping +#if NORMALMAP +sampler NormalMapSampler : register( s3 ); +#else +sampler NormalizingCubemapSampler2 : register( s3 ); +#endif + +static const HALF g_OverbrightFactor = 2.0f; + +struct PS_INPUT +{ + float4 spotTexCoord : TEXCOORD0; + float2 baseTexCoord : TEXCOORD1; +#if NORMALMAP + float3 tangentPosToLightVector : TEXCOORD2; + float2 normalMapTexCoord : TEXCOORD3; +#else + float3 worldPosToLightVector : TEXCOORD2; + float3 normal : TEXCOORD3; +#endif + float4 vertAtten : COLOR0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ +#if NORMALMAP + float3 normal = tex2D( NormalMapSampler, i.normalMapTexCoord ) * 2.0f - 1.0f; +#else + float3 normal = texCUBE( NormalizingCubemapSampler2, i.normal ) * 2.0f - 1.0f; +#endif + + float3 spotColor = tex2D( SpotSampler, i.spotTexCoord ); + float4 baseSample = tex2D( BaseTextureSampler, i.baseTexCoord ); + float3 baseColor = baseSample.xyz; +#if NORMALMAP + // wrap this! + float3 tangentPosToLightVector = texCUBE( NormalizingCubemapSampler, i.tangentPosToLightVector ) * 2.0f - 1.0f; + float nDotL = saturate( dot( tangentPosToLightVector, normal ) ); +#else + float3 worldPosToLightVector = texCUBE( NormalizingCubemapSampler, i.worldPosToLightVector ) * 2.0f - 1.0f; + float nDotL = saturate( dot( worldPosToLightVector, normal ) ); +#endif + float3 outcolor; + + outcolor = spotColor * baseColor * g_OverbrightFactor; + +#if !NOCULL + outcolor *= nDotL; +#endif + + // NOTE!! This has to be last to avoid loss of range. + outcolor *= i.vertAtten; + + return float4( outcolor.xyz, baseSample.a * i.vertAtten.a ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc new file mode 100644 index 00000000..0e9d5d9f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc @@ -0,0 +1,238 @@ +//====== Copyright c 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + + + +// STATIC: "NORMALMAP" "0..2" +// STATIC: "NORMALMAP2" "0..1" +// STATIC: "WORLDVERTEXTRANSITION" "0..1" +// STATIC: "SEAMLESS" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] + +// SKIP: !$WORLDVERTEXTRANSITION && $NORMALMAP2 +// SKIP: !$NORMALMAP && $NORMALMAP2 +// SKIP: !$DETAILTEXTURE && ( $DETAIL_BLEND_MODE != 0 ) + +#include "shader_constant_register_map.h" +#include "common_flashlight_fxc.h" +#include "common_lightmappedgeneric_fxc.h" + +const float4 g_vShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS ); +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos : register( PSREG_EYEPOS_SPEC_EXPONENT ); +const float4 g_FlashlightAttenuation : register( PSREG_FLASHLIGHT_ATTENUATION ); +const float4 g_DetailConstants : register( c0 ); +const float3 g_FlashLightPos : register( PSREG_FRESNEL_SPEC_PARAMS ); + +sampler SpotSampler : register( s0 ); +sampler BaseTextureSampler : register( s1 ); +sampler NormalizingCubemapSampler : register( s2 ); + +// use a normalizing cube map here if we aren't normal mapping +sampler BumpMapSampler : register( s3 ); +sampler BaseTextureSampler2 : register( s4 ); + +#ifdef WORLDVERTEXTRANSITION +sampler NormalMap2Sampler : register( s6 ); +#endif + +#if DETAILTEXTURE +sampler DetailSampler : register( s8 ); +#endif + +#if FLASHLIGHTSHADOWS && ( defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 ) ) +sampler RandomRotationSampler : register( s5 ); // Random rotation sampler +sampler FlashlightDepthSampler : register( s7 ); +#endif + +struct PS_INPUT +{ + float4 spotTexCoord : TEXCOORD0; +#if SEAMLESS + float3 SeamlessTexCoord : TEXCOORD1; +#else + float2 baseTexCoord : TEXCOORD1; +#endif +#if NORMALMAP + float3 tangentPosToLightVector : TEXCOORD2; + float2 normalMapTexCoord : TEXCOORD3; +#else + float3 worldPosToLightVector : TEXCOORD2; + float3 normal : TEXCOORD3; +#endif + + float2 detailCoords : TEXCOORD4; + float4 worldPos_worldTransition : TEXCOORD5; + float3 projPos : TEXCOORD6; + float4 fogFactorW : TEXCOORD7; +}; + + + +float4 SampleNormal( sampler s, PS_INPUT i ) +{ +#if SEAMLESS + float4 szy=tex2D( s, i.SeamlessTexCoord.zy ); + float4 sxz=tex2D( s, i.SeamlessTexCoord.xz ); + float4 syx=tex2D( s, i.SeamlessTexCoord.xy ); + return i.fogFactorW.r*szy + i.fogFactorW.g*sxz + i.fogFactorW.b*syx; +#else +#if NORMALMAP + return tex2D( s, i.normalMapTexCoord.xy); +#else + return float4(0,0,1,1); +#endif +#endif + +} + +float4 main( PS_INPUT i ) : COLOR +{ + bool bBase2 = WORLDVERTEXTRANSITION ? true : false; + bool bBump = (NORMALMAP != 0) ? true : false; + + // Do spot stuff early since we can bail out + float3 spotColor = float3(0,0,0); + float3 vProjCoords = i.spotTexCoord.xyz / i.spotTexCoord.w; + +#if ( defined( _X360 ) ) + + float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f ); + float3 gto = vProjCoords.xyz > float3( 1.0f, 1.0f, 1.0f ); + + [branch] + if ( dot(ltz + gto, float3(1,1,1)) > 0 ) + { + clip (-1); + return float4(0,0,0,0); + } + else + { + spotColor = tex2D( SpotSampler, vProjCoords ); + + [branch] + if ( dot(spotColor.xyz, float3(1,1,1)) <= 0 ) + { + clip(-1); + return float4(0,0,0,0); + } + else + { +#else + spotColor = tex2D( SpotSampler, vProjCoords ); +#endif + + float4 baseColor = 0.0f; + float4 baseColor2 = 0.0f; + float4 vNormal = float4(0, 0, 1, 1); + float3 baseTexCoords = float3(0,0,0); + +#if SEAMLESS + baseTexCoords = i.SeamlessTexCoord.xyz; +#else + baseTexCoords.xy = i.baseTexCoord.xy; +#endif + + GetBaseTextureAndNormal( BaseTextureSampler, BaseTextureSampler2, BumpMapSampler, bBase2, bBump, baseTexCoords, i.fogFactorW.xyz, baseColor, baseColor2, vNormal ); + +#if WORLDVERTEXTRANSITION + float lerpAlpha = 1-i.worldPos_worldTransition.a; +#endif + +#if ( NORMALMAP == 0 ) + vNormal.xyz = normalize( i.normal.xyz ); +#endif + +#if ( NORMALMAP == 1 ) + vNormal.xyz = vNormal.xyz * 2.0f - 1.0f; // signed + +# if NORMALMAP2 + float3 normal2 = SampleNormal( NormalMap2Sampler, i ) * 2.0f - 1.0f; + vNormal.xyz = lerp( normal2, vNormal.xyz, lerpAlpha ); +# endif +#endif + +// ssbump +#if ( NORMALMAP == 2 ) + +# if NORMALMAP2 + float3 normal2 = SampleNormal( NormalMap2Sampler, i ); + vNormal.xyz = lerp( normal2, vNormal.xyz, lerpAlpha ); +# endif +#else + // Normalize normal after all of the lerps above (including the tri/bilinear texel fetches) + vNormal.xyz = normalize( vNormal.xyz ); +#endif + + spotColor.rgb *= cFlashlightColor.rgb; + + // Compute per-pixel distance attenuation + float3 delta = g_FlashLightPos - i.worldPos_worldTransition.xyz; + float distSquared = dot( delta, delta ); + float dist = sqrt( distSquared ); + float farZ = g_FlashlightAttenuation.w; + float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f ); + float flAtten = saturate(endFalloffFactor * dot( g_FlashlightAttenuation.xyz, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); + +#if FLASHLIGHTSHADOWS && ( defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 ) ) + float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, i.projPos.xy / i.projPos.z, FLASHLIGHTDEPTHFILTERMODE, g_vShadowTweaks, false ); + float flAttenuated = lerp( flShadow, 1.0f, g_vShadowTweaks.y ); // Blend between fully attenuated and not attenuated + flShadow = saturate(lerp( flAttenuated, flShadow, flAtten )); // Blend between shadow and above, according to light attenuation + spotColor *= flShadow; +#endif + +#if WORLDVERTEXTRANSITION && !defined( SHADER_MODEL_PS_2_0 ) + baseColor.xyz = lerp( baseColor2.xyz, baseColor.xyz, lerpAlpha ); +#endif + +#if DETAILTEXTURE + float4 detailColor = float4( g_DetailConstants.xyz, 1.0f ) * tex2D( DetailSampler, i.detailCoords ); + float4 vBase = TextureCombine( float4(baseColor.xyz, 1.0f), detailColor, DETAIL_BLEND_MODE, g_DetailConstants.w ); + baseColor.xyz = vBase.xyz; +#endif + +#if NORMALMAP == 0 + float3 worldPosToLightVector = texCUBE( NormalizingCubemapSampler, i.worldPosToLightVector ) * 2.0f - 1.0f; + float nDotL = dot( worldPosToLightVector, vNormal.xyz ); +#endif + +#if NORMALMAP == 1 + // flashlightfixme: wrap this! + float3 tangentPosToLightVector = texCUBE( NormalizingCubemapSampler, i.tangentPosToLightVector ) * 2.0f - 1.0f; + float nDotL = dot( tangentPosToLightVector, vNormal.xyz ); +#endif + +#if NORMALMAP == 2 + float3 tangentPosToLightVector = normalize( i.tangentPosToLightVector ); + + float nDotL = + vNormal.x*dot( tangentPosToLightVector, bumpBasis[0]) + + vNormal.y*dot( tangentPosToLightVector, bumpBasis[1]) + + vNormal.z*dot( tangentPosToLightVector, bumpBasis[2]); +#endif + + float3 outColor; + outColor = spotColor * baseColor.xyz * saturate( nDotL ); + outColor *= flAtten; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.z, i.worldPos_worldTransition.z, i.projPos.z ); + return FinalOutput( float4(outColor, baseColor.a) , fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); + + // so we can jump over all of the above +#if ( defined( _X360 ) ) + } + } +#endif + +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_ps2x.fxc new file mode 100644 index 00000000..3e288e56 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_ps2x.fxc @@ -0,0 +1,127 @@ +//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// +// Includes ======================================================================================= +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +#include "common_vertexlitgeneric_dx9.h" + +// Texture Samplers =============================================================================== +sampler g_tBaseSampler : register( s0 ); +sampler g_tNoiseSampler : register( s1 ); +sampler g_tBorder1DSampler : register( s2 ); +sampler g_tNormalSampler : register( s3 ); +sampler g_tSubsurfaceSampler: register( s4 ); +sampler g_tCubeSampler : register( s5 ); + +// Shaders Constants and Globals ================================================================== +const float3 g_cSubsurfaceTint : register( c0 ); +const float2 g_flBorderWidth : register( c1 ); //{ 1.0f / g_flBorderWidthFromVmt, ( 1.0f / g_flBorderWidthFromVmt ) - 1.0f }; +const float g_flBorderSoftness : register( c2 ); +const float3 g_cBorderTint : register( c3 ); +const float g_flGlobalOpacity : register( c4 ); +const float g_flGlossBrightness : register( c5 ); + +// Interpolated values ============================================================================ +struct PS_INPUT +{ + float2 vTexCoord0 : TEXCOORD0; + float2 flDistanceToEffectCenter_flFresnelEffect : TEXCOORD1; + float4 vNoiseTexCoord : TEXCOORD2; + float3 vTangentViewVector : TEXCOORD3; + float3 cVertexLight : TEXCOORD4; + float3x3 mTangentSpaceTranspose : TEXCOORD5; + // second row : TEXCOORD6; + // third row : TEXCOORD7; +}; + +// Main =========================================================================================== +float4 main( PS_INPUT i ) : COLOR +{ + // Color texture + float4 cBaseColor = tex2D( g_tBaseSampler, i.vTexCoord0.xy ); + float flFleshMaskFromTexture = cBaseColor.a; + + // Subsurface colors + float4 cSubsurfaceColor = tex2D( g_tSubsurfaceSampler, i.vTexCoord0.xy ); + cBaseColor.rgb += cBaseColor.rgb * cSubsurfaceColor.rgb * g_cSubsurfaceTint.rgb; + + // Scroll noise textures to ripple border of opening + float flNoise0 = tex2D( g_tNoiseSampler, i.vNoiseTexCoord.xy ).g; // Use green so we can DXT1 if we want + float flNoise1 = tex2D( g_tNoiseSampler, i.vNoiseTexCoord.wz ).g; // Use green so we can DXT1 if we want + float flNoise = ( flNoise0 + flNoise1 ) * 0.5f; + + // Generate 0-1 mask from distance computed in the VS + float flClampedInputMask = 0.0f; + flClampedInputMask = 1.0f - saturate( i.flDistanceToEffectCenter_flFresnelEffect.x ); + flClampedInputMask *= i.flDistanceToEffectCenter_flFresnelEffect.y; + flClampedInputMask *= flFleshMaskFromTexture; + + // Noise mask - Only apply noise around border of sphere + float flBorderMask = saturate( ( 1.0f - flClampedInputMask ) * g_flBorderWidth.x - g_flBorderWidth.y ); + float flNoiseMask = 1.0f - abs( ( flBorderMask * 2.0f ) - 1.0f ); + + // This is used to lerp in the 1D border texture over the flesh color + float flBorderMaskWithNoise = ( 1.0f - smoothstep( flNoiseMask - g_flBorderSoftness, flNoiseMask + g_flBorderSoftness, flNoise.r ) ) * flNoiseMask; + + // Border color + float vBorderUv = ( sign( flBorderMask - 0.5 ) * (1.0f - pow( flBorderMaskWithNoise, 4.0f )) * 0.5f ) + 0.5f; + float4 cBorderColor = 2.0f * tex2D( g_tBorder1DSampler, vBorderUv ); + cBorderColor.rgb *= g_cBorderTint; + cBorderColor.rgb *= flNoise; + + // Normal map + float4 vNormalMapValue = tex2D( g_tNormalSampler, i.vTexCoord0.xy ); + float3 vTangentNormal = ( vNormalMapValue.xyz * 2.0f ) - 1.0f; + vTangentNormal.xy += ( flNoise * 1.5f ) - 0.75f; // NOTE: This will denormalize the normal. + //float3 vWorldNormal = mul( i.mTangentSpaceTranspose, vTangentNormal.xyz ); + + // Specular gloss layer + float3 vTangentReflectionVector = reflect( i.vTangentViewVector.xyz, vTangentNormal.xyz ); + //vTangentReflectionVector.xy += ( flNoise * 1.5f ) - 0.75f; + float3 vWorldReflectionVector = mul( i.mTangentSpaceTranspose, vTangentReflectionVector.xyz ); + float3 cGlossLayer = ENV_MAP_SCALE * texCUBE( g_tCubeSampler, vWorldReflectionVector.xyz ).rgb; + cGlossLayer.rgb *= g_flGlossBrightness; + + // Gloss mask is just hard-coded fresnel for now + float flGlossMask = pow( saturate( dot( vTangentNormal.xyz, -i.vTangentViewVector.xyz ) ), 8.0f ); + + // Opacity + float flOpacity = 1.0f; + flOpacity = max( flBorderMaskWithNoise, step( flBorderMask, 0.5f ) ); + + // Apply global opacity + flOpacity *= g_flGlobalOpacity; + + //===============// + // Combine terms // + //===============// + float4 result; + result.rgb = cBaseColor.rgb * i.cVertexLight.rgb; + result.rgb += cGlossLayer.rgb * flGlossMask; + result.rgb *= pow( 1.0f - flBorderMaskWithNoise, 2.0f ); // Darken near border + result.rgb = lerp( result.rgb, cBorderColor.rgb, saturate( vBorderUv * 2.0f ) ); // bring in transition 1D texture + + //result.rgb = flClampedInputMask; + //result.rgb = flBorderMask; + //result.rgb = saturate( flClampedInputMask * 2.0f ); + //result.rgb = i.flDistanceToEffectCenter_flFresnelEffect.x;// * i.flDistanceToEffectCenter_flFresnelEffect.y; + //result.rgb = i.flDistanceToEffectCenter_flFresnelEffect.y * g_flBorderWidth.x - g_flBorderWidth.y; + //result.rgb = flNoiseMask; + //result.rgb = flBorderMaskWithNoise; + //result.rgb = flOpacity; + //result.rgb = flBorderUv; + //result.rgb = cBorderColor; + //result.rgb = -i.vTangentViewVector.z; + //result.rgb = vNormalMapValue.xyz; + //result.rgb = vTangentNormal.xyz; + //result.rgb = flGlossLayer; + //result.rgb = i.cVertexLight.rgb; + //result.rgb = texCUBE( g_tCubeSampler, vTangentNormal.xyz ).rgb; + //result.rgb = i.vTangentViewVector.x; + //result.rgb = cGlossLayer.rgb; + + // Set alpha for blending + result.a = flOpacity; + //result.a = 1.0f; + + return FinalOutput( result, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_vs20.fxc new file mode 100644 index 00000000..2a361b01 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_flesh_interior_blended_pass_vs20.fxc @@ -0,0 +1,155 @@ +//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// + +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "DYNAMIC_LIGHT" "0..1" +// DYNAMIC: "STATIC_LIGHT" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] + +// Includes +#include "common_vs_fxc.h" + +// Globals +static const int g_iFogType = DOWATERFOG; +static const bool g_bHalfLambert = HALFLAMBERT ? true : false; +static const bool g_bSkinning = SKINNING ? true : false; + +const float4 g_vConst0 : register( SHADER_SPECIFIC_CONST_0 ); +#define g_flTime g_vConst0.x +#define g_flNoiseUvScroll g_vConst0.y +#define g_flBorderNoiseScale g_vConst0.z +#define g_flDebugForceFleshOn g_vConst0.w + +const float4 g_vEffectCenterOoRadius1 : register( SHADER_SPECIFIC_CONST_1 ); //= { -295.0f, -5.0f, 40.0f, 1.0f/20.0f }; +const float4 g_vEffectCenterOoRadius2 : register( SHADER_SPECIFIC_CONST_2 ); //= { -295.0f, 15.0f, 40.0f, 1.0f/10.0f }; +const float4 g_vEffectCenterOoRadius3 : register( SHADER_SPECIFIC_CONST_3 ); //= { -295.0f, 35.0f, 40.0f, 1.0f/10.0f }; +const float4 g_vEffectCenterOoRadius4 : register( SHADER_SPECIFIC_CONST_4 ); //= { -295.0f, 55.0f, 40.0f, 1.0f/10.0f }; + +// Structs +struct VS_INPUT +{ + float4 vPos : POSITION; // Position + float4 vNormal : NORMAL; // Normal + float4 vBoneWeights : BLENDWEIGHT; // Skin weights + float4 vBoneIndices : BLENDINDICES; // Skin indices + float4 vTexCoord0 : TEXCOORD0; // Base texture coordinates + float3 vPosFlex : POSITION1; // Delta positions for flexing + float4 vTangent : TANGENT; +}; + +struct VS_OUTPUT +{ + float4 vProjPosition : POSITION; // Projection-space position + float2 vTexCoord0 : TEXCOORD0; + float2 flDistanceToEffectCenter_flFresnelEffect : TEXCOORD1; + float4 vNoiseTexCoord : TEXCOORD2; + float3 vTangentViewVector : TEXCOORD3; + float3 cVertexLight : TEXCOORD4; + float3x3 mTangentSpaceTranspose : TEXCOORD5; + // second row : TEXCOORD6; + // third row : TEXCOORD7; + +}; + +// Main +VS_OUTPUT main( const VS_INPUT i ) +{ + VS_OUTPUT o; + + // Flexes coming in from a separate stream (contribution masked by cFlexScale.x) + float4 vObjPosition = i.vPos; + vObjPosition.xyz += i.vPosFlex.xyz * cFlexScale.x; + + float3 vObjNormal; + float4 vObjTangent; + DecompressVertex_NormalTangent( i.vNormal, i.vTangent, vObjNormal, vObjTangent ); + + // Transform the position + float3 vWorldPosition = { 0.0f, 0.0f, 0.0f }; + float3 vWorldNormal = { 0.0f, 0.0f, 0.0f }; + float3 vWorldTangent = { 0.0f, 0.0f, 0.0f }; + float3 vWorldBinormal = { 0.0f, 0.0f, 0.0f }; + SkinPositionNormalAndTangentSpace( g_bSkinning, vObjPosition, vObjNormal, vObjTangent, i.vBoneWeights, i.vBoneIndices, vWorldPosition, vWorldNormal, vWorldTangent, vWorldBinormal ); + + // Transform into projection space + float4 vProjPosition = mul( float4( vWorldPosition, 1.0f ), cViewProj ); + o.vProjPosition = vProjPosition; + + // Pass through tex coords + o.vTexCoord0.xy = i.vTexCoord0.xy; + + // Store the closest effect intensity + o.flDistanceToEffectCenter_flFresnelEffect.x = 9999.0f; // A very large distance + o.flDistanceToEffectCenter_flFresnelEffect.x = min( o.flDistanceToEffectCenter_flFresnelEffect.x, length( vWorldPosition.xyz - g_vEffectCenterOoRadius1.xyz ) * g_vEffectCenterOoRadius1.w ); + o.flDistanceToEffectCenter_flFresnelEffect.x = min( o.flDistanceToEffectCenter_flFresnelEffect.x, length( vWorldPosition.xyz - g_vEffectCenterOoRadius2.xyz ) * g_vEffectCenterOoRadius2.w ); + o.flDistanceToEffectCenter_flFresnelEffect.x = min( o.flDistanceToEffectCenter_flFresnelEffect.x, length( vWorldPosition.xyz - g_vEffectCenterOoRadius3.xyz ) * g_vEffectCenterOoRadius3.w ); + o.flDistanceToEffectCenter_flFresnelEffect.x = min( o.flDistanceToEffectCenter_flFresnelEffect.x, length( vWorldPosition.xyz - g_vEffectCenterOoRadius4.xyz ) * g_vEffectCenterOoRadius4.w ); + + /* + // Test values for development in Alyx_interior map + o.flDistanceToEffectCenter_flFresnelEffect.x = 9999.0f; // A very large distance + float3 vTestPosition = { -295.0f, -5.0f, 40.0f }; + float flMinY = -5.0f; + float flMaxY = 66.0f; + vTestPosition.y = lerp( flMinY, flMaxY, ( abs( frac( g_flTime / 20.0f ) * 2.0 - 1.0 ) ) ); + //vTestPosition.y = lerp( flMinY, flMaxY, 0.65f ); + + //1.0f - saturate( i.flDistanceToEffectCenter_flFresnelEffect.x * 4.0f - 3.0f ) + + //o.flDistanceToEffectCenter_flFresnelEffect.x = 9999.0f; // A very large distance + + const float g_flInteriorRadius = 20.0f; + if ( g_flInteriorRadius ) + { + o.flDistanceToEffectCenter_flFresnelEffect.x = min( o.flDistanceToEffectCenter_flFresnelEffect.x, length( vWorldPosition.xyz - vTestPosition.xyz ) / g_flInteriorRadius ); + } + + const float g_flInteriorRadius2 = 14.0f; + if ( g_flInteriorRadius2 ) + { + vTestPosition.y = lerp( flMinY, flMaxY, 0.65f ); + //vTestPosition.z = lerp( 37, 45, ( abs( frac( g_flTime / 4.0f ) * 2.0 - 1.0 ) ) ); + o.flDistanceToEffectCenter_flFresnelEffect.x = min( o.flDistanceToEffectCenter_flFresnelEffect.x, length( vWorldPosition.xyz - vTestPosition.xyz ) / g_flInteriorRadius2 ); + } + //*/ + + if ( g_flDebugForceFleshOn ) + { + o.flDistanceToEffectCenter_flFresnelEffect.x = 0.0f; + } + + // Fresnel mask + float3 vWorldViewVector = normalize( vWorldPosition.xyz - cEyePos.xyz ); + o.flDistanceToEffectCenter_flFresnelEffect.y = pow( saturate( dot( -vWorldViewVector.xyz, vWorldNormal.xyz ) ), 1.5f ); + + // Noise UV + o.vNoiseTexCoord.xy = o.vTexCoord0.xy * g_flBorderNoiseScale + g_flNoiseUvScroll; + o.vNoiseTexCoord.zw = o.vTexCoord0.xy * g_flBorderNoiseScale - g_flNoiseUvScroll; // Will fetch as wz to avoid matching layers + + // Tangent view vector + o.vTangentViewVector.xyz = Vec3WorldToTangentNormalized( vWorldViewVector.xyz, vWorldNormal.xyz, vWorldTangent.xyz, vWorldBinormal.xyz ); + + // Compute vertex lighting + bool bDynamicLight = DYNAMIC_LIGHT ? true : false; + bool bStaticLight = STATIC_LIGHT ? true : false; + +#if ( USE_STATIC_CONTROL_FLOW ) || defined ( SHADER_MODEL_VS_3_0 ) + o.cVertexLight.rgb = DoLighting( vWorldPosition, vWorldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, g_bHalfLambert ); +#else + o.cVertexLight.rgb = DoLightingUnrolled( vWorldPosition, vWorldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, g_bHalfLambert, NUM_LIGHTS ); +#endif + + // Tangent space transform + o.mTangentSpaceTranspose[0] = float3( vWorldTangent.x, vWorldBinormal.x, vWorldNormal.x ); + o.mTangentSpaceTranspose[1] = float3( vWorldTangent.y, vWorldBinormal.y, vWorldNormal.y ); + o.mTangentSpaceTranspose[2] = float3( vWorldTangent.z, vWorldBinormal.z, vWorldNormal.z ); + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_ps2x.fxc new file mode 100644 index 00000000..d6680511 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_ps2x.fxc @@ -0,0 +1,59 @@ +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +sampler BaseTextureSampler : register( s0 ); +sampler LightMap0Sampler : register( s1 ); +sampler LightMap1Sampler : register( s2 ); +sampler LightMap2Sampler : register( s3 ); + +const float4 g_LightMap0Color : register( c0 ); +const float4 g_LightMap1Color : register( c1 ); +const float4 g_LightMap2Color : register( c2 ); +const float4 g_ModulationColor : register( c3 ); + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + + +struct PS_INPUT +{ + float4 vProjPos : POSITION; + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + float2 vTexCoord2 : TEXCOORD2; + float2 vTexCoord3 : TEXCOORD3; + float4 worldPos_projPosZ : TEXCOORD4; // Necessary for pixel fog + + float4 vColor : COLOR0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 resultColor; + + // output = lightmapColor[0] * ( ( N dot basis[0] )^2 ) + + // lightmapColor[1] * ( ( N dot basis[1] )^2 ) + + // lightmapColor[2] * ( ( N dot basis[2] )^2 ) + + resultColor = tex2D( LightMap0Sampler, i.vTexCoord1 ) * g_LightMap0Color; + resultColor = (tex2D( LightMap1Sampler, i.vTexCoord2 ) * g_LightMap1Color) + resultColor; + resultColor = (tex2D( LightMap2Sampler, i.vTexCoord3 ) * g_LightMap2Color) + resultColor; + + // Modulate by decal texture + float4 decalColor = tex2D( BaseTextureSampler, i.vTexCoord0 ); + resultColor.rgb = resultColor * decalColor; + resultColor.a = decalColor.a; + + // Modulate by constant color + resultColor = resultColor * g_ModulationColor; + + // Modulate by per-vertex factor + resultColor = resultColor * i.vColor; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( resultColor, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_vs20.fxc new file mode 100644 index 00000000..0b7db284 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_decal_vs20.fxc @@ -0,0 +1,74 @@ +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; + +const float4 cShaderConst0 : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cShaderConst1 : register( SHADER_SPECIFIC_CONST_1 ); + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vTexCoord0 : TEXCOORD0; + float4 vTexCoord1 : TEXCOORD1; + float2 vTexCoord2 : TEXCOORD2; + + float4 vColor : COLOR0; +}; + +struct VS_OUTPUT +{ + float4 vProjPos : POSITION; + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + float2 vTexCoord2 : TEXCOORD2; + float2 vTexCoord3 : TEXCOORD3; + + float4 worldPos_projPosZ : TEXCOORD4; // Necessary for pixel fog + + float4 vColor : COLOR0; + + float4 fogFactorW : COLOR1; + +#if !defined( _X360 ) + float fog : FOG; +#endif +}; + + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 worldPos; + worldPos = mul( v.vPos, cModel[0] ); + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.vProjPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); + + o.fogFactorW = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + + + // Compute the texture coordinates given the offset between + // each bumped lightmap + float2 offset; + offset.x = v.vTexCoord2.x; + offset.y = 0.0f; + + o.vTexCoord0.x = dot( v.vTexCoord0, cShaderConst0 ); + o.vTexCoord0.y = dot( v.vTexCoord0, cShaderConst1 ); + + o.vTexCoord1 = offset + v.vTexCoord1.xy; + o.vTexCoord2 = (offset * 2.0) + v.vTexCoord1.xy; + o.vTexCoord3 = (offset * 3.0) + v.vTexCoord1.xy; + + o.vColor = v.vColor; + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_ps2x.fxc new file mode 100644 index 00000000..d8e7e885 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_ps2x.fxc @@ -0,0 +1,330 @@ +//====== Copyright c 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + + + +// STATIC: "NORMALMAP" "0..2" +// STATIC: "NORMALMAP2" "0..1" +// STATIC: "WORLDVERTEXTRANSITION" "0..1" +// STATIC: "FANCY_BLENDING" "0..1" +// STATIC: "SEAMLESS" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [ps30] [PC] +// STATIC: "PHONG" "0..1" [ps20b] [ps30] +// STATIC: "PHONGMASK" "0..3" [ps20b] [ps30] +// STATIC: "BASETEXTURETRANSFORM2" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] [ps30] + +// SKIP: !$WORLDVERTEXTRANSITION && $NORMALMAP2 +// SKIP: !$NORMALMAP && $NORMALMAP2 +// SKIP: !$DETAILTEXTURE && ( $DETAIL_BLEND_MODE != 0 ) +// SKIP: !$PHONG && $PHONGMASK +// SKIP: $BASETEXTURETRANSFORM2 && !$WORLDVERTEXTRANSITION +// SKIP: $BASETEXTURETRANSFORM2 && $SEAMLESS +// SKIP: $BASETEXTURETRANSFORM2 && $PHONG + +#include "shader_constant_register_map.h" +#include "common_flashlight_fxc.h" +#include "common_lightmappedgeneric_fxc.h" + +const float4 g_vShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS ); +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos : register( PSREG_EYEPOS_SPEC_EXPONENT ); +const float4 g_FlashlightAttenuation : register( PSREG_FLASHLIGHT_ATTENUATION ); +const float4 g_DetailConstants : register( c0 ); +const float4 g_FlashlightPos : register( c1 ); +const float4 g_FresnelSpecParams : register( PSREG_FRESNEL_SPEC_PARAMS ); + +#define g_SpecularExponent g_EyePos.w +#define g_FresnelRanges g_FresnelSpecParams.xyz +#define g_SpecularBoost g_FresnelSpecParams.w + +#define PHONGMASK_BASEALPHA 1 +#define PHONGMASK_NORMALALPHA 2 +#define PHONGMASK_STANDALONE 3 + +sampler SpotSampler : register( s0 ); +sampler BaseTextureSampler : register( s1 ); +sampler NormalizingCubemapSampler : register( s2 ); + +// use a normalizing cube map here if we aren't normal mapping +sampler BumpMapSampler : register( s3 ); +sampler BaseTextureSampler2 : register( s4 ); + +#ifdef WORLDVERTEXTRANSITION +sampler NormalMap2Sampler : register( s6 ); +#endif + +#if DETAILTEXTURE +sampler DetailSampler : register( s8 ); +#endif + +#if FLASHLIGHTSHADOWS && ( defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 ) ) +sampler RandomRotationSampler : register( s5 ); // Random rotation sampler +sampler FlashlightDepthSampler : register( s7 ); +#endif + +#if PHONGMASK == PHONGMASK_STANDALONE +sampler PhongMaskSampler : register( s9 ); +#endif + +#if FANCY_BLENDING +sampler BlendModulationSampler : register( s10 ); +#endif + +struct PS_INPUT +{ + float4 spotTexCoord : TEXCOORD0; +#if SEAMLESS + float3 SeamlessTexCoord : TEXCOORD1; +#else +#if BASETEXTURETRANSFORM2 + // Blixibon - Using two extra floats for $basetexturetransform2 + float4 baseTexCoord : TEXCOORD1; +#else + float2 baseTexCoord : TEXCOORD1; +#endif +#endif +#if NORMALMAP +#if PHONG + float4 tangentPosToLightVector : TEXCOORD2; + float4 normalMapTexCoord : TEXCOORD3; +#else + float3 tangentPosToLightVector : TEXCOORD2; + float2 normalMapTexCoord : TEXCOORD3; +#endif +#else + float3 worldPosToLightVector : TEXCOORD2; + float3 normal : TEXCOORD3; +#endif + + float2 detailCoords : TEXCOORD4; + float4 worldPos_worldTransition : TEXCOORD5; + float3 projPos : TEXCOORD6; + float4 fogFactorW : TEXCOORD7; +}; + + + +float4 SampleNormal( sampler s, PS_INPUT i ) +{ +#if SEAMLESS + float4 szy=tex2D( s, i.SeamlessTexCoord.zy ); + float4 sxz=tex2D( s, i.SeamlessTexCoord.xz ); + float4 syx=tex2D( s, i.SeamlessTexCoord.xy ); + return i.fogFactorW.r*szy + i.fogFactorW.g*sxz + i.fogFactorW.b*syx; +#else +#if NORMALMAP + return tex2D( s, i.normalMapTexCoord.xy); +#else + return float4(0,0,1,1); +#endif +#endif + +} + +float4 main( PS_INPUT i ) : COLOR +{ + bool bBase2 = WORLDVERTEXTRANSITION ? true : false; + bool bBump = (NORMALMAP != 0) ? true : false; + + // Do spot stuff early since we can bail out + float3 spotColor = float3(0,0,0); + float3 vProjCoords = i.spotTexCoord.xyz / i.spotTexCoord.w; + +#if ( defined( _X360 ) ) + + float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f ); + float3 gto = vProjCoords.xyz > float3( 1.0f, 1.0f, 1.0f ); + + [branch] + if ( dot(ltz + gto, float3(1,1,1)) > 0 ) + { + clip (-1); + return float4(0,0,0,0); + } + else + { + spotColor = tex2D( SpotSampler, vProjCoords ); + + [branch] + if ( dot(spotColor.xyz, float3(1,1,1)) <= 0 ) + { + clip(-1); + return float4(0,0,0,0); + } + else + { +#else + clip( vProjCoords.xyz ); +#if defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 ) + clip( 1-vProjCoords.xyz ); +#endif + spotColor = tex2D( SpotSampler, vProjCoords ); +#endif + + float4 baseColor = 0.0f; + float4 baseColor2 = 0.0f; + float4 vNormal = float4(0, 0, 1, 1); + float3 baseTexCoords = float3(0,0,0); + +#if SEAMLESS + baseTexCoords = i.SeamlessTexCoord.xyz; +#else + baseTexCoords.xy = i.baseTexCoord.xy; +#endif + +#if BASETEXTURETRANSFORM2 + // Blixibon - Simpler version of GetBaseTextureAndNormal() that supports $basetexturetransform2 + // (This is duplicated in the original shader, but make this its own function in common_lightmappedgeneric_fxc.h if this becomes more widespread) + baseColor = tex2D( BaseTextureSampler, baseTexCoords.xy ); + baseColor2 = tex2D( BaseTextureSampler2, i.baseTexCoord.wz ); + if ( bBump ) + { + vNormal = tex2D( BumpMapSampler, baseTexCoords.xy ); + } +#else + GetBaseTextureAndNormal( BaseTextureSampler, BaseTextureSampler2, BumpMapSampler, bBase2, bBump, baseTexCoords, i.fogFactorW.xyz, baseColor, baseColor2, vNormal ); +#endif + +#if WORLDVERTEXTRANSITION + float lerpAlpha = i.worldPos_worldTransition.a; + + // Blixibon +#if (PIXELFOGTYPE != PIXEL_FOG_TYPE_HEIGHT) && (FANCY_BLENDING) + float4 modt=tex2D(BlendModulationSampler,baseTexCoords); + + float minb=saturate(modt.g-modt.r); + float maxb=saturate(modt.g+modt.r); + lerpAlpha=smoothstep(minb,maxb,lerpAlpha); +#endif + +#endif + +#if ( NORMALMAP == 0 ) + vNormal.xyz = normalize( i.normal.xyz ); +#endif + +#if ( NORMALMAP == 1 ) + vNormal.xyz = vNormal.xyz * 2.0f - 1.0f; // signed + +# if NORMALMAP2 + float3 normal2 = SampleNormal( NormalMap2Sampler, i ) * 2.0f - 1.0f; + vNormal.xyz = lerp( vNormal.xyz, normal2, lerpAlpha ); +# endif +#endif + +// ssbump +#if ( NORMALMAP == 2 ) + +# if NORMALMAP2 + float3 normal2 = SampleNormal( NormalMap2Sampler, i ); + vNormal.xyz = lerp( vNormal.xyz, normal2, lerpAlpha ); +# endif +#else + // Normalize normal after all of the lerps above (including the tri/bilinear texel fetches) + vNormal.xyz = normalize( vNormal.xyz ); +#endif + + spotColor.rgb *= cFlashlightColor.rgb; + + // Compute per-pixel distance attenuation + float3 delta = g_FlashlightPos.xyz - i.worldPos_worldTransition.xyz; + float distSquared = dot( delta, delta ); + float dist = sqrt( distSquared ); + float farZ = g_FlashlightAttenuation.w; + float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f ); + float flAtten = saturate(endFalloffFactor * dot( g_FlashlightAttenuation.xyz, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); + +#if FLASHLIGHTSHADOWS && ( defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 ) ) + float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, i.projPos.xy / i.projPos.z, FLASHLIGHTDEPTHFILTERMODE, g_vShadowTweaks, false ); + float flAttenuated = lerp( flShadow, 1.0f, g_vShadowTweaks.y ); // Blend between fully attenuated and not attenuated + flShadow = saturate(lerp( flAttenuated, flShadow, flAtten )); // Blend between shadow and above, according to light attenuation + spotColor *= flShadow; +#endif + +#if WORLDVERTEXTRANSITION && !defined( SHADER_MODEL_PS_2_0 ) + baseColor = lerp( baseColor, baseColor2, lerpAlpha ); +#endif + +#if PHONG + float3 vSpecMask = 1; + +# if PHONGMASK == PHONGMASK_BASEALPHA + vSpecMask = baseColor.a; +# elif PHONGMASK == PHONGMASK_NORMALALPHA + vSpecMask = vNormal.a; +# elif PHONGMASK == PHONGMASK_STANDALONE + vSpecMask = tex2D( PhongMaskSampler, baseTexCoords ).rgb; +# endif +#endif + +#if DETAILTEXTURE + float4 detailColor = float4( g_DetailConstants.xyz, 1.0f ) * tex2D( DetailSampler, i.detailCoords ); + float4 vBase = TextureCombine( float4(baseColor.xyz, 1.0f), detailColor, DETAIL_BLEND_MODE, g_DetailConstants.w ); + baseColor.xyz = vBase.xyz; +#endif + +#if NORMALMAP == 0 + float3 worldPosToLightVector = texCUBE( NormalizingCubemapSampler, i.worldPosToLightVector ) * 2.0f - 1.0f; + float nDotL = dot( worldPosToLightVector, vNormal.xyz ); +#endif + +#if NORMALMAP == 1 + // flashlightfixme: wrap this! + float3 tangentPosToLightVector = texCUBE( NormalizingCubemapSampler, i.tangentPosToLightVector.xyz ) * 2.0f - 1.0f; + float nDotL = dot( tangentPosToLightVector, vNormal.xyz ); +#endif + +#if NORMALMAP == 2 + float3 tangentPosToLightVector = texCUBE( NormalizingCubemapSampler, i.tangentPosToLightVector.xyz ) * 2.0f - 1.0f; + + float nDotL = + vNormal.x*dot( tangentPosToLightVector, bumpBasis[0]) + + vNormal.y*dot( tangentPosToLightVector, bumpBasis[1]) + + vNormal.z*dot( tangentPosToLightVector, bumpBasis[2]); +#endif + + float3 outColor; +#if PHONG == 0 + outColor = spotColor * baseColor.xyz * saturate( nDotL ); + outColor *= flAtten; +#else + outColor = spotColor * baseColor.xyz * flAtten; + + // Not using normalizing cubemap here because of pixelated specular appearance. =/ +# if NORMALMAP == 0 + float3 posToLight = normalize( i.worldPosToLightVector ); + float3 posToEye = normalize( g_EyePos.xyz - i.worldPos_worldTransition.xyz); +# else + float3 posToLight = normalize( i.tangentPosToLightVector.xyz ); + float3 posToEye = normalize( float3(i.tangentPosToLightVector.w, i.normalMapTexCoord.zw) ); + +# if NORMALMAP == 2 + vNormal.xyz = bumpBasis[0]*vNormal.x + bumpBasis[1]*vNormal.y + bumpBasis[2]*vNormal.z; + vNormal.xyz = normalize( vNormal.xyz ); +# endif +# endif + + float fFresnel = Fresnel( vNormal.xyz, posToEye, g_FresnelRanges ); + float3 specularColor = outColor * SpecularLight( vNormal.xyz, posToLight, g_SpecularExponent, posToEye, false, SpotSampler, fFresnel ); + outColor *= saturate( nDotL ); + outColor += specularColor * fFresnel * vSpecMask * g_SpecularBoost; +#endif + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.z, i.worldPos_worldTransition.z, i.projPos.z ); + return FinalOutput( float4(outColor, baseColor.a) , fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); + + // so we can jump over all of the above +#if ( defined( _X360 ) ) + } + } +#endif + +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs11.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs11.fxc new file mode 100644 index 00000000..3a0d059d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs11.fxc @@ -0,0 +1,122 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "NORMALMAP" "0..1" +// STATIC: "WORLDVERTEXTRANSITION" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +const float3 g_FlashlightPos : register( SHADER_SPECIFIC_CONST_0 ); +const float4x4 g_FlashlightWorldToTexture : register( SHADER_SPECIFIC_CONST_1 ); +const float4 g_FlashlightAttenuationFactors : register( SHADER_SPECIFIC_CONST_5 ); + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cNormalMapTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_8 ); + +static const int g_FogType = DOWATERFOG; + +struct VS_INPUT +{ + // If this is float4, and the input is float3, the w component default to one. + float4 vPos : POSITION; + float3 vNormal : NORMAL; + float2 vBaseTexCoord : TEXCOORD0; +#if NORMALMAP + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; +#endif + float4 vColor : COLOR0; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + float4 spotTexCoord : TEXCOORD0; + float2 baseTexCoord : TEXCOORD1; +#if NORMALMAP + float3 tangentPosToLightVector : TEXCOORD2; + float2 normalMapTexCoord : TEXCOORD3; +#else + float3 worldPosToLightVector : TEXCOORD2; + float3 normal : TEXCOORD3; +#endif + float4 vertAtten : COLOR0; +}; + +float RemapValClamped( float val, float A, float B ) +{ + float cVal = (val - A) / (B - A); + cVal = saturate( cVal ); + return cVal; +} + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o; + + float4 projPos; + float3 worldPos; + float3 worldNormal; + float3 eyeVector; + + projPos = mul( v.vPos, cModelViewProj ); + o.projPos = projPos; + + worldPos = mul( v.vPos, cModel[0] ); + worldNormal = mul( v.vNormal, ( float3x3 )cModel[0] ); + +#if NORMALMAP + float3 worldTangentS = mul( v.vTangentS, cModel[0] ); + float3 worldTangentT = mul( v.vTangentT, cModel[0] ); +#endif + +#if !defined( _X360 ) + o.fog = CalcFog( worldPos, projPos, g_FogType ); +#endif + + o.baseTexCoord.x = dot( v.vBaseTexCoord, cBaseTexCoordTransform[0] ) + cBaseTexCoordTransform[0].w; + o.baseTexCoord.y = dot( v.vBaseTexCoord, cBaseTexCoordTransform[1] ) + cBaseTexCoordTransform[1].w; + + float4 spotTexCoord = mul( float4( worldPos, 1.0f ), g_FlashlightWorldToTexture ); + o.spotTexCoord = spotTexCoord.xyzw; + + float3 worldPosToLightVector = g_FlashlightPos - worldPos; +#if NORMALMAP + o.normalMapTexCoord.x = dot( v.vBaseTexCoord, cNormalMapTexCoordTransform[0] ) + cNormalMapTexCoordTransform[0].w; + o.normalMapTexCoord.y = dot( v.vBaseTexCoord, cNormalMapTexCoordTransform[1] ) + cNormalMapTexCoordTransform[1].w; + + o.tangentPosToLightVector.x = dot( worldPosToLightVector, worldTangentS ); + o.tangentPosToLightVector.y = dot( worldPosToLightVector, worldTangentT ); + o.tangentPosToLightVector.z = dot( worldPosToLightVector, worldNormal ); +#else + o.worldPosToLightVector = worldPosToLightVector; + o.normal = worldNormal; +#endif + + float3 delta = worldPosToLightVector; + float distSquared = dot( delta, delta ); + float dist = sqrt( distSquared ); + float farZ = g_FlashlightAttenuationFactors.w; + float endFalloffFactor = RemapValClamped( dist, farZ, 0.6 * farZ ); + o.vertAtten.xyz = saturate( endFalloffFactor * dot( g_FlashlightAttenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); + +#if WORLDVERTEXTRANSITION + o.vertAtten.w = 1 - v.vColor.w; +#else +#if VERTEXCOLOR + o.vertAtten.w = v.vColor.w; +#else + o.vertAtten.w = 1.0f; +#endif +#endif + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs20.fxc new file mode 100644 index 00000000..19afa219 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_flashlight_vs20.fxc @@ -0,0 +1,217 @@ +//====== Copyright ? 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "NORMALMAP" "0..1" +// STATIC: "WORLDVERTEXTRANSITION" "0..1" +// STATIC: "SEAMLESS" "0..1" +// STATIC: "DETAIL" "0..1" +// STATIC: "PHONG" "0..1" +// STATIC: "BASETEXTURETRANSFORM2" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" + +// SKIP: $BASETEXTURETRANSFORM2 && !$WORLDVERTEXTRANSITION +// SKIP: $BASETEXTURETRANSFORM2 && $SEAMLESS +// SKIP: $BASETEXTURETRANSFORM2 && $PHONG + +#include "common_vs_fxc.h" + +const float3 g_FlashlightPos : register( SHADER_SPECIFIC_CONST_0 ); +const float4x4 g_FlashlightWorldToTexture : register( SHADER_SPECIFIC_CONST_1 ); +const float4 g_FlashlightAttenuationFactors : register( SHADER_SPECIFIC_CONST_5 ); + +#if SEAMLESS +const float4 SeamlessScale : register( SHADER_SPECIFIC_CONST_6 ); +#define SEAMLESS_SCALE (SeamlessScale.x) +#endif +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cNormalMapOrDetailTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_8 ); +#if PHONG +const float3 g_EyePos : register( SHADER_SPECIFIC_CONST_10 ); +#elif BASETEXTURETRANSFORM2 +const float4 cBaseTexCoordTransform2[2] : register( SHADER_SPECIFIC_CONST_10 ); // Blixibon - Support for $basetexturetransform2 +#endif + +static const int g_FogType = DOWATERFOG; + +struct VS_INPUT +{ + float3 vPos : POSITION; //This HAS to match lightmappedgeneric_vs20.fxc's position input. Otherwise depth fighting errors occur on the 360 + float4 vNormal : NORMAL; + float2 vBaseTexCoord : TEXCOORD0; +#if WORLDVERTEXTRANSITION + float2 vLightmapTexCoord : TEXCOORD1; + float4 vColor : COLOR0; +#endif +#if NORMALMAP + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + + float4 spotTexCoord : TEXCOORD0; + +#if SEAMLESS + float3 SeamlessTexCoord : TEXCOORD1; +#else +#if BASETEXTURETRANSFORM2 + // Blixibon - Using two extra floats for $basetexturetransform2 + float4 baseTexCoord : TEXCOORD1; +#else + float2 baseTexCoord : TEXCOORD1; +#endif +#endif + +#if NORMALMAP +#if PHONG + float4 tangentPosToLightVector : TEXCOORD2; + float4 normalMapTexCoord : TEXCOORD3; +#else + float3 tangentPosToLightVector : TEXCOORD2; + float2 normalMapTexCoord : TEXCOORD3; +#endif +#else + float3 worldPosToLightVector : TEXCOORD2; + float3 normal : TEXCOORD3; +#endif + + float2 detailCoords : TEXCOORD4; + float4 worldPos_worldTransition : TEXCOORD5; + float3 vProjPos : TEXCOORD6; + float4 fogFactorW : TEXCOORD7; +}; + +float RemapValClamped( float val, float A, float B, float C, float D) +{ + float cVal = (val - A) / (B - A); + cVal = saturate( cVal ); + + return C + (D - C) * cVal; +} + + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o; + + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + float4 projPos; + float3 worldPos; + float3 worldNormal; + float3 eyeVector; + + //Projection math HAS to match lightmappedgeneric_vs20.fxc's math exactly. Otherwise depth fighting errors occur on the 360 + projPos = mul( float4( v.vPos, 1 ), cModelViewProj ); + o.projPos = projPos; + o.vProjPos.xyz = projPos.xyw; + + worldPos = mul( float4( v.vPos, 1 ), cModel[0] ); + worldNormal = mul( vObjNormal, ( float3x3 )cModel[0] ); + + o.worldPos_worldTransition = float4( worldPos.xyz, 1.0f ); + + o.fogFactorW = CalcFog( worldPos, projPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW.w; +#endif + +#if NORMALMAP + float3 worldTangentS = mul( v.vTangentS, cModel[0] ); + float3 worldTangentT = mul( v.vTangentT, cModel[0] ); +#endif +#if SEAMLESS + float3 vNormal=normalize( worldNormal ); + o.fogFactorW.xyz = vNormal * vNormal; // sums to 1. + o.SeamlessTexCoord = SEAMLESS_SCALE*worldPos; + + // Generate new tangent and binormal with seamless projection + #if NORMALMAP + // Brute-force for prototype - This must match the projection in the pixel shader! + //float3 vVecX = { 1.0f, 0.0f, 0.0f }; + //float3 vVecY = { 0.0f, 1.0f, 0.0f }; + //float3 vVecZ = { 0.0f, 0.0f, 1.0f }; + //worldTangentS.xyz = normalize( ( o.fogFactorW.x * vVecZ.xyz ) + ( o.fogFactorW.y * vVecX.xyz ) + ( o.fogFactorW.z * vVecX.xyz ) ); + //worldTangentT.xyz = normalize( ( o.fogFactorW.x * vVecY.xyz ) + ( o.fogFactorW.y * vVecZ.xyz ) + ( o.fogFactorW.z * vVecY.xyz ) ); + + // Optimized version - This must match the projection in the pixel shader! + worldTangentS.xyz = normalize( float3( o.fogFactorW.y + o.fogFactorW.z, 0.0f, o.fogFactorW.x ) ); + worldTangentT.xyz = normalize( float3( 0.0f, o.fogFactorW.x + o.fogFactorW.z, o.fogFactorW.y ) ); + #endif +#else +#if (SEAMLESS == 0 ) + o.baseTexCoord.x = dot( v.vBaseTexCoord, cBaseTexCoordTransform[0] ) + cBaseTexCoordTransform[0].w; + o.baseTexCoord.y = dot( v.vBaseTexCoord, cBaseTexCoordTransform[1] ) + cBaseTexCoordTransform[1].w; +#if BASETEXTURETRANSFORM2 + o.baseTexCoord.w = dot( v.vBaseTexCoord, cBaseTexCoordTransform2[0] ) + cBaseTexCoordTransform2[0].w; + o.baseTexCoord.z = dot( v.vBaseTexCoord, cBaseTexCoordTransform2[1] ) + cBaseTexCoordTransform2[1].w; +#endif +#endif +#endif + + float4 spotTexCoord = mul( float4( worldPos, 1.0f ), g_FlashlightWorldToTexture ); + o.spotTexCoord = spotTexCoord.xyzw; + + float3 worldPosToLightVector = g_FlashlightPos - worldPos; +#if NORMALMAP + +#if (DETAIL == 0) + o.normalMapTexCoord.x = dot( v.vBaseTexCoord, cNormalMapOrDetailTexCoordTransform[0] ) + cNormalMapOrDetailTexCoordTransform[0].w; + o.normalMapTexCoord.y = dot( v.vBaseTexCoord, cNormalMapOrDetailTexCoordTransform[1] ) + cNormalMapOrDetailTexCoordTransform[1].w; +#else + +#if SEAMLESS + o.normalMapTexCoord.xy = v.vBaseTexCoord; +#else + o.normalMapTexCoord.xy = o.baseTexCoord.xy; // Blixibon - Had to change this to .xy because BASETEXTURETRANSFORM2 makes this float4 +#endif + +#endif + + o.tangentPosToLightVector.x = dot( worldPosToLightVector, worldTangentS ); + o.tangentPosToLightVector.y = dot( worldPosToLightVector, worldTangentT ); + o.tangentPosToLightVector.z = dot( worldPosToLightVector, worldNormal ); + +#if PHONG + float3 worldPosToEyeVector = g_EyePos - worldPos; + o.tangentPosToLightVector.w = dot( worldPosToEyeVector, worldTangentS ); + o.normalMapTexCoord.z = dot( worldPosToEyeVector, worldTangentT ); + o.normalMapTexCoord.w = dot( worldPosToEyeVector, worldNormal ); +#endif + +#else + o.worldPosToLightVector = worldPosToLightVector; + o.normal = worldNormal; +#endif + +#if DETAIL + o.detailCoords.x = dot( v.vBaseTexCoord, cNormalMapOrDetailTexCoordTransform[0] ) + cNormalMapOrDetailTexCoordTransform[0].w; + o.detailCoords.y = dot( v.vBaseTexCoord, cNormalMapOrDetailTexCoordTransform[1] ) + cNormalMapOrDetailTexCoordTransform[1].w; +#else + o.detailCoords = float2(0,0); +#endif + + //float3 delta = worldPosToLightVector; + //float distSquared = dot( delta, delta ); + //float dist = sqrt( distSquared ); + //float farZ = g_FlashlightAttenuationFactors.w; + //float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f ); + //o.projPos_atten.w = endFalloffFactor * dot( g_FlashlightAttenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ); + //o.projPos_atten.w = saturate( o.projPos_atten.w ); + +#if WORLDVERTEXTRANSITION + o.worldPos_worldTransition.w = v.vColor.w; +#endif + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.fxc new file mode 100644 index 00000000..1dbb8b2f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.fxc @@ -0,0 +1,12 @@ +sampler TextureSampler : register( s1 ); + +struct PS_INPUT +{ + float4 vColor0 : COLOR0; + float2 vTexCoord1 : TEXCOORD1; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + return tex2D( TextureSampler, i.vTexCoord1 ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_vs11.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_vs11.fxc new file mode 100644 index 00000000..afee6c9c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_lightingonly_vs11.fxc @@ -0,0 +1,51 @@ +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; + +struct VS_INPUT +{ + float4 vPos : POSITION; + float2 vTexCoord1 : TEXCOORD1; +}; + +struct VS_OUTPUT +{ + float4 vProjPos : POSITION; + + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + + float4 vDiffuse : COLOR0; + + float4 fogFactorW : COLOR1; + +#if !defined( _X360 ) + float fog : FOG; +#endif +}; + + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 worldPos; + worldPos = mul4x3( v.vPos, cModel[0] ); + + o.vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + + o.fogFactorW = CalcFog( worldPos, o.vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + + // YUCK! This is to make texcoords continuous for mat_softwaretl + o.vTexCoord0 = 0.0f; + o.vTexCoord1 = v.vTexCoord1; + + o.vDiffuse = 1.0f; + + return o; +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps11.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps11.fxc new file mode 100644 index 00000000..d5be9e5f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps11.fxc @@ -0,0 +1,122 @@ +// STATIC: "BASETEXTURE" "0..1" +// STATIC: "ENVMAP" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" + +// SKIP: !$ENVMAP && ( $BASEALPHAENVMAPMASK || $ENVMAPMASK ) +// SKIP: !$BASETEXTURE && $BASEALPHAENVMAPMASK +// SKIP: $BASEALPHAENVMAPMASK && $ENVMAPMASK +// SKIP: !$BASETEXTURE && $BASEALPHAENVMAPMASK +// SKIP: $SELFILLUM && $BASEALPHAENVMAPMASK +// SKIP: !$BASETEXTURE && $SELFILLUM + +const float3 g_OverbrightFactor : register( c0 ); +const float3 g_SelfIllumTint : register( c1 ); +const float3 g_EnvmapTint : register( c2 ); + +sampler BaseTextureSampler : register( s0 ); +sampler LightmapSampler : register( s1 ); +sampler EnvmapSampler : register( s2 ); +sampler EnvmapMaskSampler : register( s3 ); + +//sampler DetailSampler : register( s3 ); + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; + float2 lightmapTexCoord : TEXCOORD1; + float3 envmapTexCoord : TEXCOORD2; + float2 envmapMaskTexCoord : TEXCOORD3; + float4 vertexColor : COLOR0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + bool bBaseTexture = BASETEXTURE ? true : false; + bool bEnvmap = ENVMAP ? true : false; + bool bEnvmapMask = ENVMAPMASK ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bBaseAlphaEnvmapMask = BASEALPHAENVMAPMASK ? true : false; + +#if 1 + float4 baseColor = float4( 1.0f, 1.0f, 1.0f, 1.0f ); + if( bBaseTexture ) + { + baseColor = tex2D( BaseTextureSampler, i.baseTexCoord ); + } + + float3 specularFactor = 1.0f; + + if( bEnvmapMask ) + { + specularFactor *= tex2D( EnvmapMaskSampler, i.envmapMaskTexCoord ).xyz; + } + if( bBaseAlphaEnvmapMask ) + { + specularFactor *= 1.0 - baseColor.a; // this blows! + } + + float3 diffuseLighting = tex2D( LightmapSampler, i.lightmapTexCoord ); + + float3 albedo = float3( 1.0f, 1.0f, 1.0f ); + float alpha = 1.0f; + if( bBaseTexture ) + { + albedo *= baseColor; + if( !bBaseAlphaEnvmapMask && !bSelfIllum ) + { + alpha *= baseColor.a; + } + } + + // The vertex color contains the modulation color + vertex color combined + albedo *= i.vertexColor; + alpha *= i.vertexColor.a; // not sure about this one + + float3 diffuseComponent = ( albedo * diffuseLighting * 2.0f ) * g_OverbrightFactor; + + if( bSelfIllum ) + { + float3 selfIllumComponent = g_SelfIllumTint * albedo; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a ); + } + + float3 specularLighting = float3( 0.0f, 0.0f, 0.0f ); + + if( bEnvmap ) + { + specularLighting = tex2D( EnvmapSampler, i.envmapTexCoord ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint; + } + + float3 result = diffuseComponent + specularLighting; + return float4( result, alpha ); +#endif + +#if 0 + float4 baseColor = float4( 1.0f, 1.0f, 1.0f, 1.0f ); + float3 diffuseLighting = tex2D( LightmapSampler, i.lightmapTexCoord ); + + float3 albedo = float3( 1.0f, 1.0f, 1.0f ); + float alpha = 1.0f; + albedo *= i.vertexColor; + alpha *= i.vertexColor.a; // not sure about this one + + float3 diffuseComponent = ( albedo * diffuseLighting * 2.0f ) * g_OverbrightFactor; + float3 result = diffuseComponent; + return float4( result, alpha ); +#endif + +#if 0 + float4 result; + + result.rgb = tex2D( LightmapSampler, i.lightmapTexCoord ).rgb * i.vertexColor.rgb; + result.a = i.vertexColor.a; + result.rgb = ( result.rgb * g_OverbrightFactor ) * 2.0f; + return result; +#endif +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps20b.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps20b.fxc new file mode 100644 index 00000000..d49a33a6 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps20b.fxc @@ -0,0 +1,51 @@ +// STATIC: "MASKEDBLENDING" "0..1" +// STATIC: "BASETEXTURE2" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "BUMPMAP" "0..2" +// STATIC: "BUMPMAP2" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "NORMALMAPALPHAENVMAPMASK" "0..1" +// STATIC: "DIFFUSEBUMPMAP" "0..1" +// STATIC: "BASETEXTURENOENVMAP" "0..1" +// STATIC: "BASETEXTURE2NOENVMAP" "0..1" +// STATIC: "WARPLIGHTING" "0..1" +// STATIC: "FANCY_BLENDING" "0..1" +// STATIC: "RELIEF_MAPPING" "0..0" [ps20b] +// STATIC: "SEAMLESS" "0..1" +// STATIC: "BUMPMASK" "0..1" +// STATIC: "NORMAL_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMAL_DECODE_MODE" "0..0" [PC] +// STATIC: "NORMALMASK_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMALMASK_DECODE_MODE" "0..0" [PC] +// STATIC: "DETAIL_BLEND_MODE" "0..11" +// STATIC: "FLASHLIGHT" "0..1" [ps20b] [XBOX] +// STATIC: "BASETEXTURETRANSFORM2" "0..1" +// STATIC: "PARALLAXCORRECT" "0..1" + +// DYNAMIC: "FASTPATHENVMAPCONTRAST" "0..1" +// DYNAMIC: "FASTPATH" "0..1" +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "LIGHTING_PREVIEW" "0..2" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps30] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +// SKIP: $SEAMLESS && $RELIEF_MAPPING [ps20b] + +// SKIP: (! $DETAILTEXTURE) && ( $DETAIL_BLEND_MODE != 0 ) + +// SKIP: ($DETAIL_BLEND_MODE == 2 ) || ($DETAIL_BLEND_MODE == 3 ) || ($DETAIL_BLEND_MODE == 4 ) +// SKIP: ($DETAIL_BLEND_MODE == 5 ) || ($DETAIL_BLEND_MODE == 6 ) || ($DETAIL_BLEND_MODE == 7 ) +// SKIP: ($DETAIL_BLEND_MODE == 8 ) || ($DETAIL_BLEND_MODE == 9 ) +// SKIP ($DETAIL_BLEND_MODE == 10 ) +// SKIP ($DETAIL_BLEND_MODE == 11 ) + +// SKIP: $PARALLAXCORRECT && !$CUBEMAP + +#include "lightmappedgeneric_ps2_3_x.h" + diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps2x.fxc new file mode 100644 index 00000000..599d16a4 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_ps2x.fxc @@ -0,0 +1,59 @@ +// STATIC: "MASKEDBLENDING" "0..1" +// STATIC: "BASETEXTURE2" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "BUMPMAP" "0..2" +// STATIC: "BUMPMAP2" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "NORMALMAPALPHAENVMAPMASK" "0..1" +// STATIC: "DIFFUSEBUMPMAP" "0..1" +// STATIC: "BASETEXTURENOENVMAP" "0..1" +// STATIC: "BASETEXTURE2NOENVMAP" "0..1" +// STATIC: "WARPLIGHTING" "0..1" +// STATIC: "FANCY_BLENDING" "0..1" +// STATIC: "RELIEF_MAPPING" "0..0" [ps20b] +// STATIC: "SEAMLESS" "0..1" +// STATIC: "OUTLINE" "0..1" +// STATIC: "SOFTEDGES" "0..1" +// STATIC: "BUMPMASK" "0..1" +// STATIC: "NORMAL_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMAL_DECODE_MODE" "0..0" [PC] +// STATIC: "NORMALMASK_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMALMASK_DECODE_MODE" "0..0" [PC] +// STATIC: "DETAIL_BLEND_MODE" "0..11" +// STATIC: "FLASHLIGHT" "0..1" [ps20b] [XBOX] + +// DYNAMIC: "FASTPATHENVMAPCONTRAST" "0..1" +// DYNAMIC: "FASTPATH" "0..1" +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "LIGHTING_PREVIEW" "0..2" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +// SKIP: $SEAMLESS && $RELIEF_MAPPING [ps20b] + +// SKIP: (! $DETAILTEXTURE) && ( $DETAIL_BLEND_MODE != 0 ) + +// SKIP: $SEAMLESS && ( $OUTLINE || $SOFTEDGES) +// SKIP: $BASETEXTURE2 && ( $OUTLINE || $SOFTEDGES) +// SKIP: $BUMPMAP2 && ( $OUTLINE || $SOFTEDGES) +// SKIP: $SELFILLUM && ( $OUTLINE || $SOFTEDGES) +// SKIP: $MASKEDBLENDING && ( $OUTLINE || $SOFTEDGES) +// SKIP: $FANCY_BLENDING && ( $OUTLINE || $SOFTEDGES) +// SKIP: $LIGHTING_PREVIEW && ( $OUTLINE || $SOFTEDGES) +// SKIP: ($FASTPATH == 0) && ( $OUTLINE || $SOFTEDGES) +// SKIP: ($DETAILTEXTURE && $BUMPMAP) && ( $OUTLINE || $SOFTEDGES) +// SKIP: ($WARPLIGHTING) && ( $OUTLINE || $SOFTEDGES) +// SKIP: ($BUMPMAP) && ( $OUTLINE || $SOFTEDGES) +// SKIP: ($DETAIL_BLEND_MODE == 2 ) || ($DETAIL_BLEND_MODE == 3 ) || ($DETAIL_BLEND_MODE == 4 ) +// SKIP: ($DETAIL_BLEND_MODE == 5 ) || ($DETAIL_BLEND_MODE == 6 ) || ($DETAIL_BLEND_MODE == 7 ) +// SKIP: ($DETAIL_BLEND_MODE == 8 ) || ($DETAIL_BLEND_MODE == 9 ) +// SKIP ($DETAIL_BLEND_MODE == 10 ) && ($BUMPMAP == 0 ) +// SKIP ($DETAIL_BLEND_MODE == 11 ) && ($BUMPMAP != 0 ) + +#include "lightmappedgeneric_ps2_3_x.h" + diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_vs20.fxc new file mode 100644 index 00000000..763272fb --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedgeneric_vs20.fxc @@ -0,0 +1,268 @@ +// STATIC: "ENVMAP_MASK" "0..1" +// STATIC: "TANGENTSPACE" "0..1" +// STATIC: "BUMPMAP" "0..1" +// STATIC: "DIFFUSEBUMPMAP" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "VERTEXALPHATEXBLENDFACTOR" "0..1" +// STATIC: "RELIEF_MAPPING" "0..0" +// STATIC: "SEAMLESS" "0..1" +// STATIC: "BUMPMASK" "0..1" +// STATIC: "FLASHLIGHT" "0..1" [XBOX] +// STATIC: "BASETEXTURETRANSFORM2" "0..1" + +// DYNAMIC: "FASTPATH" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "LIGHTING_PREVIEW" "0..1" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] + +// SKIP: $SEAMLESS && $BASETEXTURETRANSFORM2 + +// This should not be a combo since I'm a moron with the tangent space and the flashlight. +// SKIP: !$BUMPMAP && $DIFFUSEBUMPMAP +// SKIP: $SEAMLESS && $RELIEF_MAPPING +// SKIP: $BUMPMASK && $RELIEF_MAPPING +// SKIP: $BUMPMASK && $SEAMLESS + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_UseSeparateEnvmapMask = ENVMAP_MASK; +static const bool g_bTangentSpace = TANGENTSPACE; +static const bool g_bBumpmap = BUMPMAP; +static const bool g_bBumpmapDiffuseLighting = DIFFUSEBUMPMAP; +static const bool g_bVertexColor = VERTEXCOLOR; +static const bool g_bVertexAlphaTexBlendFactor = VERTEXALPHATEXBLENDFACTOR; +static const bool g_BumpMask = BUMPMASK; + +#if SEAMLESS +const float4 SeamlessScale : register( SHADER_SPECIFIC_CONST_0 ); +#define SEAMLESS_SCALE (SeamlessScale.x) +#else +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +#if BASETEXTURETRANSFORM2 +const float4 cBaseTexCoordTransform2[2] : register( SHADER_SPECIFIC_CONST_8 ); // Blixibon - Support for $basetexturetransform2 +#endif +const float4 cDetailOrBumpTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_2 ); +#endif +// This should be identity if we are bump mapping, otherwise we'll screw up the lightmapTexCoordOffset. +const float4 cEnvmapMaskTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_4 ); +const float4x4 g_FlashlightWorldToTexture : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cBlendMaskTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_10 ); // not contiguous with the rest! + +struct VS_INPUT +{ + float3 vPos : POSITION; + float4 vNormal : NORMAL; + float2 vBaseTexCoord : TEXCOORD0; + float2 vLightmapTexCoord : TEXCOORD1; + float2 vLightmapTexCoordOffset : TEXCOORD2; + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; + float4 vColor : COLOR0; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) && !defined(SHADER_MODEL_VS_3_0) + float fog : FOG; +#endif + +#if SEAMLESS + float3 SeamlessTexCoord : TEXCOORD0; // x y z + float4 detailOrBumpAndEnvmapMaskTexCoord : TEXCOORD1; // envmap mask +#else +#if BASETEXTURETRANSFORM2 + // Blixibon - Using two extra floats for $basetexturetransform2 + float4 baseTexCoord : TEXCOORD0; +#else + float2 baseTexCoord : TEXCOORD0; +#endif + // detail textures and bumpmaps are mutually exclusive so that we have enough texcoords. +#if RELIEF_MAPPING + float3 TangentSpaceViewRay : TEXCOORD1; +#else + float4 detailOrBumpAndEnvmapMaskTexCoord : TEXCOORD1; +#endif +#endif + float4 lightmapTexCoord1And2 : TEXCOORD2; + float4 lightmapTexCoord3 : TEXCOORD3; // and basetexcoord*mask_scale + float4 worldPos_projPosZ : TEXCOORD4; + +#if TANGENTSPACE || (LIGHTING_PREVIEW) || defined( _X360 ) + float3x3 tangentSpaceTranspose : TEXCOORD5; // and 6 and 7 +#endif + + float4 vertexColor : COLOR; // in seamless, r g b = blend weights + float4 vertexBlendX_fogFactorW : COLOR1; + + // Extra iterators on 360, used in flashlight combo +#if defined( _X360 ) && FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD8; + float4 vProjPos : TEXCOORD9; +#endif + +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + float3 worldPos = mul( float4( v.vPos, 1 ), cModel[0] ); + + float4 vProjPos = mul( float4( v.vPos, 1 ), cModelViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( v.vPos, 1 ), cModelViewProjZ ); + + o.worldPos_projPosZ = float4( worldPos, vProjPos.z ); + + float3 worldNormal = mul( vObjNormal, ( float3x3 )cModel[0] ); + +#if TANGENTSPACE || (LIGHTING_PREVIEW) || defined( _X360 ) + float3 worldTangentS = mul( v.vTangentS, ( const float3x3 )cModel[0] ); + float3 worldTangentT = mul( v.vTangentT, ( const float3x3 )cModel[0] ); + + #if SEAMLESS && BUMPMAP && defined( _X360 ) + float3 n = normalize( worldNormal ); + float3 n2 = n * n; // sums to 1. + + o.tangentSpaceTranspose[0] = normalize( float3( n2.y + n2.z, 0.0f, n2.x ) ); + o.tangentSpaceTranspose[1] = normalize( float3( 0.0f, n2.x + n2.z, n2.y ) ); + o.tangentSpaceTranspose[2] = worldNormal; + #else + o.tangentSpaceTranspose[0] = worldTangentS; + o.tangentSpaceTranspose[1] = worldTangentT; + o.tangentSpaceTranspose[2] = worldNormal; + #endif + +#endif + + float3 worldVertToEyeVector = VSHADER_VECT_SCALE * (cEyePos - worldPos); + +#if SEAMLESS + { + // we need to fill in the texture coordinate projections + o.SeamlessTexCoord = SEAMLESS_SCALE*worldPos; + } +#else + { + if (FASTPATH) + { + o.baseTexCoord.xy = v.vBaseTexCoord; +#if BASETEXTURETRANSFORM2 + o.baseTexCoord.wz = v.vBaseTexCoord; +#endif + } + else + { + o.baseTexCoord.x = dot( v.vBaseTexCoord, cBaseTexCoordTransform[0] ) + cBaseTexCoordTransform[0].w; + o.baseTexCoord.y = dot( v.vBaseTexCoord, cBaseTexCoordTransform[1] ) + cBaseTexCoordTransform[1].w; +#if BASETEXTURETRANSFORM2 + o.baseTexCoord.w = dot( v.vBaseTexCoord, cBaseTexCoordTransform2[0] ) + cBaseTexCoordTransform2[0].w; + o.baseTexCoord.z = dot( v.vBaseTexCoord, cBaseTexCoordTransform2[1] ) + cBaseTexCoordTransform2[1].w; +#endif + } +#if ( RELIEF_MAPPING == 0 ) + { + // calculate detailorbumptexcoord + if ( FASTPATH ) + o.detailOrBumpAndEnvmapMaskTexCoord.xy = v.vBaseTexCoord.xy; + else + { + o.detailOrBumpAndEnvmapMaskTexCoord.x = dot( v.vBaseTexCoord, cDetailOrBumpTexCoordTransform[0] ) + cDetailOrBumpTexCoordTransform[0].w; + o.detailOrBumpAndEnvmapMaskTexCoord.y = dot( v.vBaseTexCoord, cDetailOrBumpTexCoordTransform[1] ) + cDetailOrBumpTexCoordTransform[1].w; + } + } +#endif + } +#endif + if ( FASTPATH ) + { + o.lightmapTexCoord3.zw = v.vBaseTexCoord; + } + else + { + o.lightmapTexCoord3.z = dot( v.vBaseTexCoord, cBlendMaskTexCoordTransform[0] ) + cBlendMaskTexCoordTransform[0].w; + o.lightmapTexCoord3.w = dot( v.vBaseTexCoord, cBlendMaskTexCoordTransform[1] ) + cBlendMaskTexCoordTransform[1].w; + } + + // compute lightmap coordinates + if( g_bBumpmap && g_bBumpmapDiffuseLighting ) + { + o.lightmapTexCoord1And2.xy = v.vLightmapTexCoord + v.vLightmapTexCoordOffset; + + float2 lightmapTexCoord2 = o.lightmapTexCoord1And2.xy + v.vLightmapTexCoordOffset; + float2 lightmapTexCoord3 = lightmapTexCoord2 + v.vLightmapTexCoordOffset; + + // reversed component order + o.lightmapTexCoord1And2.w = lightmapTexCoord2.x; + o.lightmapTexCoord1And2.z = lightmapTexCoord2.y; + + o.lightmapTexCoord3.xy = lightmapTexCoord3; + } + else + { + o.lightmapTexCoord1And2.xy = v.vLightmapTexCoord; + } + +#if ( RELIEF_MAPPING == 0) + if( g_UseSeparateEnvmapMask || g_BumpMask ) + { + // reversed component order +# if FASTPATH + o.detailOrBumpAndEnvmapMaskTexCoord.wz = v.vBaseTexCoord.xy; +# else + o.detailOrBumpAndEnvmapMaskTexCoord.w = dot( v.vBaseTexCoord, cEnvmapMaskTexCoordTransform[0] ) + cEnvmapMaskTexCoordTransform[0].w; + o.detailOrBumpAndEnvmapMaskTexCoord.z = dot( v.vBaseTexCoord, cEnvmapMaskTexCoordTransform[1] ) + cEnvmapMaskTexCoordTransform[1].w; +# endif + } +#endif + + o.vertexBlendX_fogFactorW = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) && !defined(SHADER_MODEL_VS_3_0) + o.fog = o.vertexBlendX_fogFactorW; +#endif + + if (!g_bVertexColor) + { + o.vertexColor = float4( 1.0f, 1.0f, 1.0f, cModulationColor.a ); + } + else + { +#if FASTPATH + o.vertexColor = v.vColor; +#else + if ( g_bVertexAlphaTexBlendFactor ) + { + o.vertexColor.rgb = v.vColor.rgb; + o.vertexColor.a = cModulationColor.a; + } + else + { + o.vertexColor = v.vColor; + o.vertexColor.a *= cModulationColor.a; + } +#endif + } +#if SEAMLESS + // compute belnd weights in rgb + float3 vNormal=normalize( worldNormal ); + o.vertexColor.xyz = vNormal * vNormal; // sums to 1. +#endif + +// On 360, we have extra iterators and can fold the flashlight into this shader +#if defined( _X360 ) && FLASHLIGHT + o.flashlightSpacePos = mul( float4( worldPos, 1.0f ), g_FlashlightWorldToTexture ); + o.vProjPos = vProjPos; +#endif + + if ( g_bVertexAlphaTexBlendFactor ) + { + o.vertexBlendX_fogFactorW.r = v.vColor.a; + } + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_ps2x.fxc new file mode 100644 index 00000000..6d844ddd --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_ps2x.fxc @@ -0,0 +1,189 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "BASETEXTURE" "0..1" +// STATIC: "REFLECT" "0..1" +// STATIC: "REFRACT" "0..1" +// STATIC: "ENVMAPMASK" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "common_ps_fxc.h" + +sampler RefractSampler : register( s0 ); +sampler BaseTextureSampler : register( s1 ); +sampler ReflectSampler : register( s2 ); +#if BASETEXTURE +sampler LightmapSampler : register( s3 ); +#endif +#if ENVMAPMASK +sampler EnvMapMaskSampler : register( s6 ); +#endif +sampler NormalSampler : register( s4 ); + +const HALF4 vRefractTint : register( c1 ); +const float4 g_FresnelConstants : register( c3 ); +const HALF4 vReflectTint : register( c4 ); +const float4 g_ReflectRefractScale : register( c5 ); // xy - reflect scale, zw - refract scale + +const float4 g_PixelFogParams : register( c8 ); + + +static const bool g_bReflect = REFLECT ? true : false; +static const bool g_bRefract = REFRACT ? true : false; + +struct PS_INPUT +{ + float4 vBumpTexCoordXY_vTexCoordXY : TEXCOORD0; + half3 vTangentEyeVect : TEXCOORD1; + float4 vReflectXY_vRefractYX : TEXCOORD2; + float W : TEXCOORD3; + float4 vProjPos : TEXCOORD4; + float screenCoord : TEXCOORD5; +#if BASETEXTURE +// CENTROID: TEXCOORD6 + HALF4 lightmapTexCoord1And2 : TEXCOORD6; +// CENTROID: TEXCOORD7 + HALF4 lightmapTexCoord3 : TEXCOORD7; +#endif + + float4 fogFactorW : COLOR1; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + // Load normal and expand range + HALF4 vNormalSample = tex2D( NormalSampler, i.vBumpTexCoordXY_vTexCoordXY.xy ); + HALF3 vNormal = normalize( vNormalSample * 2.0 - 1.0 ); + + // Perform division by W only once + float ooW = 1.0f / i.W; + + float2 unwarpedRefractTexCoord = i.vReflectXY_vRefractYX.wz * ooW; + + float4 reflectRefractScale = g_ReflectRefractScale; + + // Compute coordinates for sampling Reflection + float2 vReflectTexCoord; + float2 vRefractTexCoord; + + // vectorize the dependent UV calculations (reflect = .xy, refract = .wz) +#ifdef NV3X + float4 vDependentTexCoords = vNormal.xyxy * vNormalSample.a * reflectRefractScale; +#else + float4 vN; + vN.xy = vNormal.xy; + vN.w = vNormal.x; + vN.z = vNormal.y; + float4 vDependentTexCoords = vN * vNormalSample.a * reflectRefractScale; +#endif + + vDependentTexCoords += ( i.vReflectXY_vRefractYX * ooW ); + vReflectTexCoord = vDependentTexCoords.xy; + vRefractTexCoord = vDependentTexCoords.wz; + + // Sample reflection and refraction + HALF4 vReflectColor = tex2D( ReflectSampler, vReflectTexCoord ); + HALF4 vRefractColor = tex2D( RefractSampler, vRefractTexCoord ); + vReflectColor *= vReflectTint; + vRefractColor *= vRefractTint; + + half3 vEyeVect; + vEyeVect = normalize( i.vTangentEyeVect ); + + // Fresnel term + HALF fNdotV = saturate( dot( vEyeVect, vNormal ) ); + HALF fFresnelScalar = g_FresnelConstants.x * pow( 1.0 - fNdotV, g_FresnelConstants.y ) + g_FresnelConstants.z; + HALF4 fFresnel = HALF4( fFresnelScalar, fFresnelScalar, fFresnelScalar, fFresnelScalar ); + +#if BASETEXTURE + float4 baseSample = tex2D( BaseTextureSampler, i.vBumpTexCoordXY_vTexCoordXY.zw ); + HALF2 bumpCoord1; + HALF2 bumpCoord2; + HALF2 bumpCoord3; + ComputeBumpedLightmapCoordinates( i.lightmapTexCoord1And2, i.lightmapTexCoord3.xy, + bumpCoord1, bumpCoord2, bumpCoord3 ); + + HALF4 lightmapSample1 = tex2D( LightmapSampler, bumpCoord1 ); + HALF3 lightmapColor1 = lightmapSample1.rgb; + HALF3 lightmapColor2 = tex2D( LightmapSampler, bumpCoord2 ); + HALF3 lightmapColor3 = tex2D( LightmapSampler, bumpCoord3 ); + + float3 dp; + dp.x = saturate( dot( vNormal, bumpBasis[0] ) ); + dp.y = saturate( dot( vNormal, bumpBasis[1] ) ); + dp.z = saturate( dot( vNormal, bumpBasis[2] ) ); + dp *= dp; + + float3 diffuseLighting = dp.x * lightmapColor1 + + dp.y * lightmapColor2 + + dp.z * lightmapColor3; + float sum = dot( dp, float3( 1.0f, 1.0f, 1.0f ) ); + diffuseLighting *= LIGHT_MAP_SCALE / sum; + HALF3 diffuseComponent = baseSample.rgb * diffuseLighting; +#endif + + float4 flMask; +#if ENVMAPMASK + flMask = tex2D( EnvMapMaskSampler, i.vBumpTexCoordXY_vTexCoordXY.zw ); +#else + flMask = float4( 1.0f, 1.0f, 1.0f, 1.0f ); +#endif + + // NOTE: the BASETEXTURE path hasn't been tested (or really written for that matter, just copied from water) + // What I think should happen is that the alpha of base texture should be its 'translucency' + // which should indicate how much refraction to use. + // We should add an envmapmask to deal with how much reflection to use + // along with all the focus, etc. features + float4 result; + float flAlpha = 1.0f; + if( g_bReflect && g_bRefract ) + { + result = lerp( vRefractColor, vReflectColor, fFresnel ) * flMask; +#if BASETEXTURE + result += float4( diffuseComponent, 1.0f ); + flAlpha = baseSample.a; +#endif + } + else if( g_bReflect ) + { +#if BASETEXTURE + result = float4( diffuseComponent, 1.0f ) + vReflectColor * flMask; + flAlpha = baseSample.a; +#else + result = vReflectColor; +#endif + } + else if( g_bRefract ) + { +#if BASETEXTURE + result = float4( diffuseComponent, 1.0f ) + vRefractColor * flMask; + flAlpha = baseSample.a; +#else + result = vRefractColor; +#endif + } + else + { +#if BASETEXTURE + result = float4( diffuseComponent, 1.0f ); + flAlpha = baseSample.a; +#else + result = float4( 0.0f, 0.0f, 0.0f, 0.0f ); +#endif + } + + +#if ( PIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE ) + float fogFactor = CalcRangeFog( i.vProjPos.z, g_PixelFogParams.x, g_PixelFogParams.z, g_PixelFogParams.w ); +#else + float fogFactor = 0; +#endif + + return FinalOutput( float4( result.rgb, flAlpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE, (WRITE_DEPTH_TO_DESTALPHA != 0), i.vProjPos.z ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_vs20.fxc new file mode 100644 index 00000000..e7ced40b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_lightmappedreflective_vs20.fxc @@ -0,0 +1,105 @@ +// STATIC: "BASETEXTURE" "0..1" + +#include "common_vs_fxc.h" + +const float4 cBumpTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_1 ); +const float4 cBaseTextureTransform[2] : register( SHADER_SPECIFIC_CONST_3 ); + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vNormal : NORMAL; + float4 vBaseTexCoord : TEXCOORD0; + float2 vLightmapTexCoord : TEXCOORD1; + float2 vLightmapTexCoordOffset : TEXCOORD2; + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL0; +}; + +struct VS_OUTPUT +{ + float4 vProjPos_POSITION : POSITION; + float vFog : FOG; + float4 vBumpTexCoordXY_vTexCoordXY : TEXCOORD0; + float3 vTangentEyeVect : TEXCOORD1; + float4 vReflectXY_vRefractYX : TEXCOORD2; + float W : TEXCOORD3; + float4 vProjPos : TEXCOORD4; + float screenCoord : TEXCOORD5; +#if BASETEXTURE + HALF4 lightmapTexCoord1And2 : TEXCOORD6; + HALF4 lightmapTexCoord3 : TEXCOORD7; +#endif + float4 fogFactorW : COLOR1; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + + // Projected position + float4 vProjPos = mul( v.vPos, cModelViewProj ); + o.vProjPos = o.vProjPos_POSITION = vProjPos; + + // Project tangent basis + float2 vProjTangentS = mul( v.vTangentS, cViewProj ); + float2 vProjTangentT = mul( v.vTangentT, cViewProj ); + + // Map projected position to the reflection texture + float2 vReflectPos; + vReflectPos.x = -vProjPos.x; + vReflectPos.y = -vProjPos.y; // invert Y + vReflectPos = (vReflectPos + vProjPos.w) * 0.5f; + + // Map projected position to the refraction texture + float2 vRefractPos; + vRefractPos.x = vProjPos.x; + vRefractPos.y = -vProjPos.y; // invert Y + vRefractPos = (vRefractPos + vProjPos.w) * 0.5f; + + // Reflection transform + o.vReflectXY_vRefractYX = float4( vReflectPos.x, vReflectPos.y, vRefractPos.y, vRefractPos.x ); + o.W = vProjPos.w; + + o.screenCoord = vProjPos.x; + + // Compute fog based on the position + float3 vWorldPos = mul( v.vPos, cModel[0] ); + o.fogFactorW = o.vFog = CalcFog( vWorldPos, vProjPos, FOGTYPE_RANGE ); + + // Eye vector + float3 vWorldEyeVect = cEyePos - vWorldPos; + // Transform to the tangent space + o.vTangentEyeVect.x = dot( vWorldEyeVect, v.vTangentS ); + o.vTangentEyeVect.y = dot( vWorldEyeVect, v.vTangentT ); + o.vTangentEyeVect.z = dot( vWorldEyeVect, vObjNormal ); + + // Tranform bump coordinates + o.vBumpTexCoordXY_vTexCoordXY.x = dot( v.vBaseTexCoord, cBumpTexCoordTransform[0] ); + o.vBumpTexCoordXY_vTexCoordXY.y = dot( v.vBaseTexCoord, cBumpTexCoordTransform[1] ); + +#if BASETEXTURE + o.vBumpTexCoordXY_vTexCoordXY.z = dot( v.vBaseTexCoord, cBaseTextureTransform[0] ); + o.vBumpTexCoordXY_vTexCoordXY.w = dot( v.vBaseTexCoord, cBaseTextureTransform[1] ); + + o.lightmapTexCoord1And2.xy = v.vLightmapTexCoord + v.vLightmapTexCoordOffset; + + float2 lightmapTexCoord2 = o.lightmapTexCoord1And2.xy + v.vLightmapTexCoordOffset; + float2 lightmapTexCoord3 = lightmapTexCoord2 + v.vLightmapTexCoordOffset; + + // reversed component order + o.lightmapTexCoord1And2.w = lightmapTexCoord2.x; + o.lightmapTexCoord1And2.z = lightmapTexCoord2.y; + + o.lightmapTexCoord3.xy = lightmapTexCoord3; +#else + o.vBumpTexCoordXY_vTexCoordXY.z = 0.0f; + o.vBumpTexCoordXY_vTexCoordXY.w = 0.0f; +#endif + + return o; +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_monitorscreen_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_monitorscreen_ps2x.fxc new file mode 100644 index 00000000..204ceac1 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_monitorscreen_ps2x.fxc @@ -0,0 +1,55 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// +//paired with unlittwotexture_vs20 + +// STATIC: "TEXTURE2" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "shader_constant_register_map.h" +#include "common_ps_fxc.h" + +sampler BaseTextureSampler : register( s0 ); +sampler SecondaryTextureSampler : register( s1 ); + + +const float4 g_Contrast : register( c1 ); +const float4 g_Saturation : register( c2 ); +const float4 g_Tint : register( c3 ); +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +#define g_Grey float4( 0.33333f, 0.33333f, 0.33333f, 0.33333f ) + +struct PS_INPUT +{ + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + + float4 vColor : COLOR0; + + float4 worldPos_projPosZ : TEXCOORD7; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 resultColor = tex2D( BaseTextureSampler, i.vTexCoord0 ) * i.vColor; // base texture modulated with vertex color + +#if (TEXTURE2 == 1) + resultColor = tex2D( SecondaryTextureSampler, i.vTexCoord1 ) * resultColor; // modulate base color by another texture +#endif + + float3 tempColor = resultColor.rgb * resultColor.rgb; //base * base + resultColor.rgb = lerp( resultColor.rgb, tempColor.rgb, g_Contrast.rgb ); // blend between color and color * color + tempColor = dot( resultColor.rgb, g_Grey ); // color greyscaled + resultColor.rgb = lerp( tempColor.rgb, resultColor.rgb, g_Saturation.rgb ); // blend between color and greyscale + resultColor.rgb = resultColor.rgb * g_Tint.rgb; // tint + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( resultColor, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_refract_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_refract_ps2x.fxc new file mode 100644 index 00000000..0e2c92bf --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_refract_ps2x.fxc @@ -0,0 +1,250 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======= +// +//============================================================================= + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "BLUR" "0..1" +// STATIC: "FADEOUTONSILHOUETTE" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "REFRACTTINTTEXTURE" "0..1" +// STATIC: "MASKED" "0..1" +// STATIC: "COLORMODULATE" "0..1" +// STATIC: "SECONDARY_NORMAL" "0..1" +// STATIC: "NORMAL_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMAL_DECODE_MODE" "0..0" [PC] +// STATIC: "SHADER_SRGB_READ" "0..1" [ps20b] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +// SKIP: $MASKED && $BLUR + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +sampler NormalSampler2 : register( s1 ); +sampler RefractSampler : register( s2 ); +sampler NormalSampler : register( s3 ); +#if CUBEMAP +sampler EnvmapSampler : register( s4 ); +#endif +#if REFRACTTINTTEXTURE +sampler RefractTintSampler : register( s5 ); +#endif + +#if NORMAL_DECODE_MODE == NORM_DECODE_ATI2N_ALPHA +sampler AlphaMapSampler : register( s6 ); // alpha +sampler AlphaMapSampler2 : register( s7 ); +#else +#define AlphaMapSampler2 NormalSampler +#define AlphaMapSampler NormalSampler2 +#endif + +const float3 g_EnvmapTint : register( c0 ); +const float3 g_RefractTint : register( c1 ); +const float3 g_EnvmapContrast : register( c2 ); +const float3 g_EnvmapSaturation : register( c3 ); +const float4 g_c5 : register( c5 ); +#define g_RefractScale g_c5.x +#define g_flTime g_c5.w + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +static const int g_BlurCount = BLUR; +static const float g_BlurFraction = 1.0f / 512.0f; +static const float g_HalfBlurFraction = 0.5 * g_BlurFraction; +static const float4 g_BlurFractionVec = float4( g_BlurFraction, g_HalfBlurFraction, + -g_BlurFraction,-g_HalfBlurFraction ); + +struct PS_INPUT +{ + float4 vBumpTexCoord : TEXCOORD0; // NormalMap1 in xy, NormalMap2 in wz + float3 vTangentVertToEyeVector : TEXCOORD1; + float3 vWorldNormal : TEXCOORD2; + float3 vWorldTangent : TEXCOORD3; + float3 vWorldBinormal : TEXCOORD4; + float3 vRefractXYW : TEXCOORD5; + float3 vWorldViewVector : TEXCOORD6; +#if COLORMODULATE + float4 ColorModulate : COLOR0; +#endif + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog + float4 fogFactorW : COLOR1; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float3 result; + + float pixelFogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + +#if FADEOUTONSILHOUETTE + //float blend = -i.projNormal.z; + float blend = saturate( dot( -i.vWorldViewVector.xyz, i.vWorldNormal.xyz ) ); + blend = blend * blend * blend; +#else + float blend = 1.0f; +#endif + + // Decompress normal + float4 vNormal = DecompressNormal( NormalSampler, i.vBumpTexCoord.xy, NORMAL_DECODE_MODE, AlphaMapSampler ); + +#if SECONDARY_NORMAL + float3 vNormal2 = DecompressNormal( NormalSampler2, i.vBumpTexCoord.wz, NORMAL_DECODE_MODE, AlphaMapSampler2 ); + vNormal.xyz = normalize( vNormal.xyz + vNormal2.xyz ); +#endif + +#if REFRACTTINTTEXTURE + float3 refractTintColor = 2.0 * g_RefractTint * tex2D( RefractTintSampler, i.vBumpTexCoord.xy ); +#else + float3 refractTintColor = g_RefractTint; +#endif + +#if COLORMODULATE + refractTintColor *= i.ColorModulate.rgb; +#endif + + // Perform division by W only once + float ooW = 1.0f / i.vRefractXYW.z; + + // Compute coordinates for sampling refraction + float2 vRefractTexCoordNoWarp = i.vRefractXYW.xy * ooW; + float2 vRefractTexCoord = vNormal.xy; + float scale = vNormal.a * g_RefractScale; +#if COLORMODULATE + scale *= i.ColorModulate.a; +#endif + vRefractTexCoord *= scale; + vRefractTexCoord += vRefractTexCoordNoWarp; + +#if (BLUR==1) // use polyphase magic to convert 9 lookups into 4 + + // basic principle behind this transformation: + // [ A B C ] + // [ D E F ] + // [ G H I ] + // use bilinear filtering hardware to weight upper 2x2 samples evenly (0.25* [A + B + D + E]). + // scale the upper 2x2 by 4/9 (total area of kernel occupied) + // use bilinear filtering hardware to weight right 1x2 samples evenly (0.5*[C + F]) + // scale right 1x2 by 2/9 + // use bilinear filtering hardware to weight lower 2x1 samples evenly (0.5*[G + H]) + // scale bottom 2x1 by 2/9 + // fetch last sample (I) and scale by 1/9. + + float2 upper_2x2_loc = vRefractTexCoord.xy - float2(g_HalfBlurFraction, g_HalfBlurFraction); + float2 right_1x2_loc = vRefractTexCoord.xy + float2(g_BlurFraction, -g_HalfBlurFraction); + float2 lower_2x1_loc = vRefractTexCoord.xy + float2(-g_HalfBlurFraction, g_BlurFraction); + float2 singleton_loc = vRefractTexCoord.xy + float2(g_BlurFraction, g_BlurFraction); + result = tex2D(RefractSampler, upper_2x2_loc) * 0.4444444; + result += tex2D(RefractSampler, right_1x2_loc) * 0.2222222; + result += tex2D(RefractSampler, lower_2x1_loc) * 0.2222222; + result += tex2D(RefractSampler, singleton_loc) * 0.1111111; + + #if ( SHADER_SRGB_READ == 1 ) + { + // Just do this once rather than after every blur step, which is wrong, but much more efficient + result = GammaToLinear( result ); + } + #endif + + float3 unblurredColor = tex2D(RefractSampler, vRefractTexCoordNoWarp.xy); + #if ( SHADER_SRGB_READ == 1 ) + { + unblurredColor = GammaToLinear( unblurredColor ); + } + #endif + + result = lerp(unblurredColor, result * refractTintColor, blend); + +#elif (BLUR>0) // iteratively step through render target + int x, y; + + result = float3( 0.0f, 0.0f, 0.0f ); + for( x = -g_BlurCount; x <= g_BlurCount; x++ ) + { + for( y = -g_BlurCount; y <= g_BlurCount; y++ ) + { + result += tex2D( RefractSampler, vRefractTexCoord.xy + float2( g_BlurFraction * x, g_BlurFraction * y ) ); + } + } + + int width = g_BlurCount * 2 + 1; + result *= 1.0f / ( width * width ); + + #if ( SHADER_SRGB_READ == 1 ) + { + // Just do this once rather than after every blur step, which is wrong, but much more efficient + result = GammaToLinear( result ); + } + #endif + + // result is the blurred one now. . .now lerp. + float3 unblurredColor = tex2D( RefractSampler, vRefractTexCoordNoWarp.xy ); + #if ( SHADER_SRGB_READ == 1 ) + { + unblurredColor = GammaToLinear( unblurredColor ); + } + #endif + + result = lerp( unblurredColor, result * refractTintColor, blend ); +#else +# if MASKED + float4 fMaskedResult = tex2D( RefractSampler, vRefractTexCoord.xy ); + #if ( SHADER_SRGB_READ == 1 ) + { + fMaskedResult = GammaToLinear( fMaskedResult ); + } + #endif + + return FinalOutput( fMaskedResult, pixelFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE ); +# else + float3 colorWarp = tex2D( RefractSampler, vRefractTexCoord.xy ); + #if ( SHADER_SRGB_READ == 1 ) + { + colorWarp = GammaToLinear( colorWarp ); + } + #endif + float3 colorNoWarp = tex2D( RefractSampler, vRefractTexCoordNoWarp.xy ); + #if ( SHADER_SRGB_READ == 1 ) + { + colorNoWarp = GammaToLinear( colorNoWarp ); + } + #endif + + colorWarp *= refractTintColor; + result = lerp( colorNoWarp, colorWarp, blend ); +# endif +#endif + +#if CUBEMAP + float specularFactor = vNormal.a; + + float3 worldSpaceNormal = Vec3TangentToWorld( vNormal.xyz, i.vWorldNormal, i.vWorldTangent, i.vWorldBinormal ); + + float3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, i.vTangentVertToEyeVector ); + float3 specularLighting = texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint; + float3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast ); + float3 greyScale = dot( specularLighting, float3( 0.299f, 0.587f, 0.114f ) ); + specularLighting = lerp( greyScale, specularLighting, g_EnvmapSaturation ); + result += specularLighting; +#endif + +#if COLORMODULATE + float resultAlpha = i.ColorModulate.a * vNormal.a; +#else + float resultAlpha = vNormal.a; +#endif + + return FinalOutput( float4( result, resultAlpha ), pixelFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_skin_ps20b.fxc b/mp/src/materialsystem/stdshaders/SDK_skin_ps20b.fxc new file mode 100644 index 00000000..4ba9ebbf --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_skin_ps20b.fxc @@ -0,0 +1,432 @@ +//======= Copyright © 1996-2007, Valve Corporation, All rights reserved. ====== +// STATIC: "CONVERT_TO_SRGB" "0..0" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "SELFILLUMFRESNEL" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" +// STATIC: "PHONGWARPTEXTURE" "0..1" +// STATIC: "WRINKLEMAP" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..6" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "RIMLIGHT" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] +// STATIC: "FASTPATH_NOBUMP" "0..1" +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "PHONG_HALFLAMBERT" "0..1" +// STATIC: "ENVMAPMASK" "0..1" + +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..4" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps30] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] +// DYNAMIC: "PHONG_USE_EXPONENT_FACTOR" "0..0" [ps20] +// DYNAMIC: "PHONG_USE_EXPONENT_FACTOR" "0..1" [ps20b] [ps30] [PC] + + +// SKIP: ($PIXELFOGTYPE == 0) && ($WRITEWATERFOGTODESTALPHA != 0) + +// blend mode doesn't matter if we only have one texture +// SKIP: (! $DETAILTEXTURE) && ( $DETAIL_BLEND_MODE != 0 ) + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) + +// Flashlight shadow filter mode is irrelevant if there is no flashlight +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] + +// Only need self illum fresnel when self illum enabled +// SKIP: ( $SELFILLUM == 0 ) && ( $SELFILLUMFRESNEL == 1 ) +// SKIP: ( $FLASHLIGHT == 1 ) && ( $SELFILLUMFRESNEL == 1 ) +// SKIP: ( $FLASHLIGHT == 1 ) && ( $SELFILLUM == 1 ) + +// BlendTintByBaseAlpha and self illum and are opposing meanings for alpha channel +// SKIP: ( $BLENDTINTBYBASEALPHA ) && ( $SELFILLUM ) + +// SKIP: $ENVMAPMASK && !$CUBEMAP + +// fastpath means: +// no bumpmap +// basealphaenvmapmask (not inverted) +// no spec expmap +// no spectint +// no specwarp +// no rimlight +// no selfillum +// no detail +// no BlendTintByBaseAlpha + +// SKIP: $FASTPATH_NOBUMP && ( $RIMLIGHT || $DETAILTEXTURE || $PHONGWARPTEXTURE || $SELFILLUM || $BLENDTINTBYBASEALPHA ) + + + +#include "common_flashlight_fxc.h" +#include "shader_constant_register_map.h" + +const float4 g_SelfIllumTint_and_DetailBlendFactor : register( PSREG_SELFILLUMTINT ); +#if ( SELFILLUMFRESNEL == 1 ) +const float4 g_SelfIllumScaleBiasExpBrightness : register( PSREG_SELFILLUM_SCALE_BIAS_EXP ); +#endif +const float4 g_DiffuseModulation : register( PSREG_DIFFUSE_MODULATION ); +const float4 g_EnvmapTint_ShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS ); // w controls spec mask +const float3 cAmbientCube[6] : register( PSREG_AMBIENT_CUBE ); +const float4 g_EnvMapFresnel : register( PSREG_ENVMAP_FRESNEL__SELFILLUMMASK ); // x is envmap fresnel ... w is selfillummask control +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_FlashlightAttenuationFactors_RimMask : register( PSREG_FLASHLIGHT_ATTENUATION ); // On non-flashlight pass, x has rim mask control +const float4 g_FlashlightPos_RimBoost : register( PSREG_FLASHLIGHT_POSITION_RIM_BOOST ); +#if FLASHLIGHT + const float4x4 g_FlashlightWorldToTexture : register( PSREG_FLASHLIGHT_TO_WORLD_TEXTURE ); +#endif +const float4 g_FresnelSpecParams : register( PSREG_FRESNEL_SPEC_PARAMS ); // xyz are fresnel, w is specular boost +const float4 g_SpecularRimParams : register( PSREG_SPEC_RIM_PARAMS ); // xyz are specular tint color, w is rim power +PixelShaderLightInfo cLightInfo[3] : register( PSREG_LIGHT_INFO_ARRAY ); // 2 registers each - 6 registers total (4th light spread across w's) + +// TODO: give this a better name. For now, I don't want to touch shader_constant_register_map.h since I don't want to trigger a recompile of everything... +const float4 g_ShaderControls : register( PSREG_CONSTANT_27 ); // x is basemap alpgha phong mask, y is 1 - blendtintbybasealpha, z is tint overlay amount, w controls "INVERTPHONGMASK" +#define g_FlashlightPos g_FlashlightPos_RimBoost.xyz +#define g_fRimBoost g_FlashlightPos_RimBoost.w +#define g_FresnelRanges g_FresnelSpecParams.xyz +#define g_SpecularBoost g_FresnelSpecParams.w +#define g_SpecularTint g_SpecularRimParams.xyz +#define g_RimExponent g_SpecularRimParams.w +#define g_FlashlightAttenuationFactors g_FlashlightAttenuationFactors_RimMask +#define g_RimMaskControl g_FlashlightAttenuationFactors_RimMask.x +#define g_SelfIllumMaskControl g_EnvMapFresnel.w +#define g_fBaseMapAlphaPhongMask g_ShaderControls.x +#define g_fTintReplacementControl g_ShaderControls.z +#define g_fInvertPhongMask g_ShaderControls.w +#define g_fMinLighting g_ShaderControls.y + + +sampler BaseTextureSampler : register( s0 ); // Base map, selfillum in alpha +sampler SpecularWarpSampler : register( s1 ); // Specular warp sampler (for iridescence etc) +sampler DiffuseWarpSampler : register( s2 ); // Lighting warp sampler (1D texture for diffuse lighting modification) +sampler NormalMapSampler : register( s3 ); // Normal map, specular mask in alpha +sampler ShadowDepthSampler : register( s4 ); // Flashlight shadow depth map sampler +sampler NormalizeRandRotSampler : register( s5 ); // Normalization / RandomRotation samplers +sampler FlashlightSampler : register( s6 ); // Flashlight cookie +sampler SpecExponentSampler : register( s7 ); // Specular exponent map +sampler EnvmapSampler : register( s8 ); // Cubic environment map + +#if WRINKLEMAP +sampler WrinkleSampler : register( s9 ); // Compression base +sampler StretchSampler : register( s10 ); // Expansion base +sampler NormalWrinkleSampler : register( s11 ); // Compression base +sampler NormalStretchSampler : register( s12 ); // Expansion base +#endif + +#if DETAILTEXTURE +sampler DetailSampler : register( s13 ); // detail texture +#endif + +sampler SelfIllumMaskSampler : register( s14 ); // selfillummask + +#if ENVMAPMASK +sampler EnvmapMaskSampler : register( s15 ); +#endif + +struct PS_INPUT +{ + float4 baseTexCoordDetailTexCoord : TEXCOORD0; // xy=base zw=detail + float3 lightAtten : TEXCOORD1; // Scalar light attenuation factors for FOUR lights + float3 worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ : TEXCOORD2; + float3x3 tangentSpaceTranspose : TEXCOORD3; + // second row : TEXCOORD4; + // third row : TEXCOORD5; + float4 worldPos_atten3 : TEXCOORD6; + float4 projPos_fWrinkleWeight : TEXCOORD7; +}; + + + +float4 main( PS_INPUT i ) : COLOR +{ + bool bWrinkleMap = WRINKLEMAP ? true : false; + bool bDoDiffuseWarp = LIGHTWARPTEXTURE ? true : false; + bool bDoSpecularWarp = PHONGWARPTEXTURE ? true : false; + bool bDoAmbientOcclusion = false; + bool bFlashlight = (FLASHLIGHT!=0) ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bDoRimLighting = RIMLIGHT ? true : false; + bool bCubemap = CUBEMAP ? true : false; + bool bEnvmapMask = ENVMAPMASK ? true : false; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA ? true : false; + int nNumLights = NUM_LIGHTS; + + // Unpacking for convenience + float fWrinkleWeight = i.projPos_fWrinkleWeight.w; + float3 vProjPos = i.projPos_fWrinkleWeight.xyz; + float3 vWorldPos = i.worldPos_atten3.xyz; + float atten3 = i.worldPos_atten3.w; + + float4 vLightAtten = float4( i.lightAtten, atten3 ); + +#if WRINKLEMAP + float flWrinkleAmount = saturate( -fWrinkleWeight ); // One of these two is zero + float flStretchAmount = saturate( fWrinkleWeight ); // while the other is in the 0..1 range + + float flTextureAmount = 1.0f - flWrinkleAmount - flStretchAmount; // These should sum to one +#endif + + float4 baseColor = tex2D( BaseTextureSampler, i.baseTexCoordDetailTexCoord.xy ); +#if WRINKLEMAP + float4 wrinkleColor = tex2D( WrinkleSampler, i.baseTexCoordDetailTexCoord.xy ); + float4 stretchColor = tex2D( StretchSampler, i.baseTexCoordDetailTexCoord.xy ); + + // Apply wrinkle blend to only RGB. Alpha comes from the base texture + baseColor.rgb = flTextureAmount * baseColor.rgb + flWrinkleAmount * wrinkleColor.rgb + flStretchAmount * stretchColor.rgb; +#endif + +#if DETAILTEXTURE + float4 detailColor = tex2D( DetailSampler, i.baseTexCoordDetailTexCoord.zw ); + baseColor = TextureCombine( baseColor, detailColor, DETAIL_BLEND_MODE, g_SelfIllumTint_and_DetailBlendFactor.w ); +#endif + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, vWorldPos.xyz, vProjPos.z ); + + float3 vEyeDir = normalize(i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz); + float3 vRimAmbientCubeColor = PixelShaderAmbientLight(vEyeDir, cAmbientCube); + + float3 worldSpaceNormal, tangentSpaceNormal; + +#if ENVMAPMASK + float3 fSpecMask = float3( 1.0f, 1.0f, 1.0f ); +#else + float fSpecMask = 1.0f; +#endif + +#if !DETAILTEXTURE + // Blixibon - $bumpmap transform support + float4 normalTexel = tex2D( NormalMapSampler, i.baseTexCoordDetailTexCoord.zw ); +#else + float4 normalTexel = tex2D( NormalMapSampler, i.baseTexCoordDetailTexCoord.xy ); +#endif + +#if WRINKLEMAP + float4 wrinkleNormal = tex2D( NormalWrinkleSampler, i.baseTexCoordDetailTexCoord.xy ); + float4 stretchNormal = tex2D( NormalStretchSampler, i.baseTexCoordDetailTexCoord.xy ); + normalTexel = flTextureAmount * normalTexel + flWrinkleAmount * wrinkleNormal + flStretchAmount * stretchNormal; +#endif + +#if (FASTPATH_NOBUMP == 0) + tangentSpaceNormal = lerp( 2.0f * normalTexel.xyz - 1.0f, float3(0, 0, 1), g_fBaseMapAlphaPhongMask ); +#else + tangentSpaceNormal = float3(0, 0, 1); +#endif + + #if ENVMAPMASK + { + float4 envmapMaskTexel = tex2D( EnvmapMaskSampler, i.baseTexCoordDetailTexCoord.xy ); + fSpecMask = lerp( envmapMaskTexel.xyz, baseColor.aaa, g_fBaseMapAlphaPhongMask ); + } + #else + { +#if (FASTPATH_NOBUMP == 0 ) + fSpecMask = lerp( normalTexel.a, baseColor.a, g_fBaseMapAlphaPhongMask ); +#else + fSpecMask = baseColor.a; +#endif + } + #endif + + // We need a normal if we're doing any lighting + worldSpaceNormal = normalize( mul( i.tangentSpaceTranspose, tangentSpaceNormal ) ); + + float fFresnelRanges = Fresnel( worldSpaceNormal, vEyeDir, g_FresnelRanges ); + float fRimFresnel = Fresnel4( worldSpaceNormal, vEyeDir ); + + // Break down reflect so that we can share dot(worldSpaceNormal,vEyeDir) with fresnel terms + float3 vReflect = 2 * worldSpaceNormal * dot(worldSpaceNormal, vEyeDir) - vEyeDir; + + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); + float3 envMapColor = float3( 0.0f, 0.0f, 0.0f ); + if( !bFlashlight ) + { + // Summation of diffuse illumination from all local lights + diffuseLighting = PixelShaderDoLighting( vWorldPos, worldSpaceNormal, + float3( 0.0f, 0.0f, 0.0f ), false, true, vLightAtten, + cAmbientCube, NormalizeRandRotSampler, nNumLights, cLightInfo, PHONG_HALFLAMBERT, + + // These parameters aren't passed by generic shaders: + false, 1.0f, + bDoDiffuseWarp, DiffuseWarpSampler ); + + if( bCubemap ) + { + if ( bEnvmapMask ) + { + float3 fEnvMapMask = lerp( baseColor.aaa, fSpecMask, g_EnvmapTint_ShadowTweaks.w ); + envMapColor = (ENV_MAP_SCALE * + lerp(1, fFresnelRanges, g_EnvMapFresnel.x) * + lerp(fEnvMapMask, float3(1,1,1)-fEnvMapMask, g_fInvertPhongMask)) * + texCUBE( EnvmapSampler, vReflect ).xyz * + g_EnvmapTint_ShadowTweaks.xyz; + } + else + { + // Mask is either normal map alpha or base map alpha +#if ( SELFILLUMFRESNEL == 1 ) // This is to match the 2.0 version of vertexlitgeneric + float fEnvMapMask = lerp( baseColor.a, g_fInvertPhongMask, g_EnvmapTint_ShadowTweaks.w ); +#else + float fEnvMapMask = lerp( baseColor.a, fSpecMask, g_EnvmapTint_ShadowTweaks.w ); +#endif + + envMapColor = (ENV_MAP_SCALE * + lerp(1, fFresnelRanges, g_EnvMapFresnel.x) * + lerp(fEnvMapMask, 1-fEnvMapMask, g_fInvertPhongMask)) * + texCUBE( EnvmapSampler, vReflect ).xyz * + g_EnvmapTint_ShadowTweaks.xyz; + } + } + } + + float3 specularLighting = float3( 0.0f, 0.0f, 0.0f ); + float3 rimLighting = float3( 0.0f, 0.0f, 0.0f ); + + float3 vSpecularTint = 1; + float fRimMask = 0; + float fSpecExp = 1; + +#if ( FASTPATH_NOBUMP == 0 ) + float4 vSpecExpMap = tex2D( SpecExponentSampler, i.baseTexCoordDetailTexCoord.xy ); + + if ( !bFlashlight ) + { + fRimMask = lerp( 1.0f, vSpecExpMap.a, g_RimMaskControl ); // Select rim mask + } + + // If the exponent passed in as a constant is zero, use the value from the map as the exponent +#if defined( _X360 ) + [flatten] +#endif + +#if ( PHONG_USE_EXPONENT_FACTOR ) + fSpecExp = ( 1.0f + g_EyePos_SpecExponent.w * vSpecExpMap.r ); +#else + fSpecExp = (g_EyePos_SpecExponent.w >= 0.0) ? g_EyePos_SpecExponent.w : (1.0f + 149.0f * vSpecExpMap.r); +#endif + + // If constant tint is negative, tint with albedo, based upon scalar tint map +#if defined( _X360 ) + [flatten] +#endif + vSpecularTint = lerp( float3(1.0f, 1.0f, 1.0f), baseColor.rgb, vSpecExpMap.g ); + vSpecularTint = (g_SpecularTint.r >= 0.0) ? g_SpecularTint.rgb : vSpecularTint; + +#else + fSpecExp = max(g_EyePos_SpecExponent.w, 0); +#endif + + float3 albedo = baseColor.rgb; + + if ( !bFlashlight ) + { + // Summation of specular from all local lights besides the flashlight + PixelShaderDoSpecularLighting( vWorldPos, worldSpaceNormal, + fSpecExp, vEyeDir, vLightAtten, + nNumLights, cLightInfo, false, 1.0f, bDoSpecularWarp, + SpecularWarpSampler, fFresnelRanges, bDoRimLighting, g_RimExponent, + + // Outputs + specularLighting, rimLighting ); + } + else + { + #if FLASHLIGHT + float4 flashlightSpacePosition = mul( float4( vWorldPos, 1.0f ), g_FlashlightWorldToTexture ); + + DoSpecularFlashlight( g_FlashlightPos, vWorldPos, flashlightSpacePosition, worldSpaceNormal, + g_FlashlightAttenuationFactors.xyz, g_FlashlightAttenuationFactors.w, + FlashlightSampler, ShadowDepthSampler, NormalizeRandRotSampler, FLASHLIGHTDEPTHFILTERMODE, FLASHLIGHTSHADOWS, true, vProjPos.xy / vProjPos.z, + fSpecExp, vEyeDir, bDoSpecularWarp, SpecularWarpSampler, fFresnelRanges, g_EnvmapTint_ShadowTweaks, + + // These two values are output + diffuseLighting, specularLighting ); + #endif + } + + // If we didn't already apply Fresnel to specular warp, modulate the specular + if ( !bDoSpecularWarp ) + fSpecMask *= fFresnelRanges; + + // Modulate with spec mask, boost and tint + specularLighting *= fSpecMask * g_SpecularBoost; + + if (bBlendTintByBaseAlpha) + { + float3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = lerp(tintedColor, g_DiffuseModulation.rgb, g_fTintReplacementControl); + albedo = lerp(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + +#if FOGTYPE == 2 || FLASHLIGHT != 0 + float3 diffuseComponent = albedo * diffuseLighting; +#else + float flFresnelMinlight = saturate( dot( worldSpaceNormal, vEyeDir ) ); + float3 diffuseComponent = albedo * lerp( diffuseLighting, 1, g_fMinLighting * flFresnelMinlight ); +#endif + + if ( bSelfIllum && !bFlashlight ) + { +#if ( SELFILLUMFRESNEL == 1 ) // To free up the constant register...see top of file + // This will apply a Fresnel term based on the vertex normal (not the per-pixel normal!) to help fake and internal glow look + float3 vVertexNormal = normalize( float3( i.tangentSpaceTranspose[0].z, i.tangentSpaceTranspose[1].z, i.tangentSpaceTranspose[2].z ) ); + float flSelfIllumFresnel = ( pow( saturate( dot( vVertexNormal.xyz, vEyeDir.xyz ) ), g_SelfIllumScaleBiasExpBrightness.z ) * g_SelfIllumScaleBiasExpBrightness.x ) + g_SelfIllumScaleBiasExpBrightness.y; + diffuseComponent = lerp( diffuseComponent, g_SelfIllumTint_and_DetailBlendFactor.rgb * albedo * g_SelfIllumScaleBiasExpBrightness.w, baseColor.a * saturate( flSelfIllumFresnel ) ); +#else + float3 vSelfIllumMask = tex2D( SelfIllumMaskSampler, i.baseTexCoordDetailTexCoord.xy ).xyz; + vSelfIllumMask = lerp( baseColor.aaa, vSelfIllumMask, g_SelfIllumMaskControl ); + diffuseComponent = lerp( diffuseComponent, g_SelfIllumTint_and_DetailBlendFactor.rgb * albedo, vSelfIllumMask ); +#endif + + diffuseComponent = max( 0.0f, diffuseComponent ); + } + +#if DETAILTEXTURE + diffuseComponent = TextureCombinePostLighting( diffuseComponent, detailColor, + DETAIL_BLEND_MODE, g_SelfIllumTint_and_DetailBlendFactor.w ); +#endif + + if ( bDoRimLighting && !bFlashlight ) + { + float fRimMultiply = fRimMask * fRimFresnel; // both unit range: [0, 1] + + // Add in rim light modulated with tint, mask and traditional Fresnel (not using Fresnel ranges) + rimLighting *= fRimMultiply; + + // Fold rim lighting into specular term by using the max so that we don't really add light twice... + specularLighting = max( specularLighting, rimLighting ); + + // Add in view-ray lookup from ambient cube + specularLighting += (vRimAmbientCubeColor * g_fRimBoost) * saturate(fRimMultiply * worldSpaceNormal.z); + } + + float3 result = specularLighting*vSpecularTint + envMapColor + diffuseComponent; + +#if WRITEWATERFOGTODESTALPHA && ( PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT ) + float alpha = fogFactor; +#else + float alpha = g_DiffuseModulation.a; + if ( !bSelfIllum && !bBlendTintByBaseAlpha ) + { + alpha = lerp( baseColor.a * alpha, alpha, g_fBaseMapAlphaPhongMask ); + } +#endif + + bool bWriteDepthToAlpha = ( WRITE_DEPTH_TO_DESTALPHA != 0 ) && ( WRITEWATERFOGTODESTALPHA == 0 ); + + //FIXME: need to take dowaterfog into consideration + return FinalOutput( float4( result, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, bWriteDepthToAlpha, vProjPos.z ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_skin_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_skin_vs20.fxc new file mode 100644 index 00000000..9b04b7aa --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_skin_vs20.fxc @@ -0,0 +1,173 @@ +//======= Copyright (c) 1996-2007, Valve Corporation, All rights reserved. ====== + +// STATIC: "DECAL" "0..1" [vs30] +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "LIGHTING_PREVIEW" "0..1" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// DYNAMIC: "MORPHING" "0..1" [vs30] +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cDetailTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_4 ); + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + + +//----------------------------------------------------------------------------- +// Input vertex format +//----------------------------------------------------------------------------- +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float4 vColor : COLOR0; + float3 vSpecular : COLOR1; + // make these float2's and stick the [n n 0 1] in the dot math. + float4 vTexCoord0 : TEXCOORD0; + float4 vTexCoord1 : TEXCOORD1; + float4 vTexCoord2 : TEXCOORD2; + float4 vTexCoord3 : TEXCOORD3; + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; + float4 vUserData : TANGENT; + + // Position and normal/tangent deltas + float4 vPosFlex : POSITION1; + float4 vNormalFlex : NORMAL1; + +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + // Stuff that isn't seen by the pixel shader + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + // Stuff that is seen by the pixel shader + float4 baseTexCoord : TEXCOORD0; // includes detail tex coord + float3 lightAtten : TEXCOORD1; + float3 worldVertToEyeVector : TEXCOORD2; + float3x3 tangentSpaceTranspose : TEXCOORD3; + // second row : TEXCOORD4; + // third row : TEXCOORD5; + float4 worldPos_atten3 : TEXCOORD6; + float4 projPos_fWrinkleWeight : TEXCOORD7; +}; + +//----------------------------------------------------------------------------- +// Main shader entry point +//----------------------------------------------------------------------------- +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + float3 vNormal; + float4 vTangent; + DecompressVertex_NormalTangent( v.vNormal, v.vUserData, vNormal, vTangent ); + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, v.vNormalFlex, + vPosition.xyz, vNormal, vTangent.xyz, o.projPos_fWrinkleWeight.w ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, v.vTexCoord2, + vPosition.xyz, vNormal, vTangent.xyz, o.projPos_fWrinkleWeight.w ); +#endif + + // Perform skinning + float3 worldNormal, worldPos, worldTangentS, worldTangentT; + SkinPositionNormalAndTangentSpace( g_bSkinning, vPosition, vNormal, vTangent, + v.vBoneWeights, v.vBoneIndices, worldPos, + worldNormal, worldTangentS, worldTangentT ); + + // Always normalize since flex path is controlled by runtime + // constant not a shader combo and will always generate the normalization + worldNormal = normalize( worldNormal ); + worldTangentS = normalize( worldTangentS ); + worldTangentT = normalize( worldTangentT ); + +#if defined( SHADER_MODEL_VS_3_0 ) && MORPHING && DECAL + // Avoid z precision errors + worldPos += worldNormal * 0.05f * v.vTexCoord2.z; +#endif + + // Transform into projection space + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.projPos_fWrinkleWeight.xyz = vProjPos.xyz; + +#if !defined( _X360 ) + o.fog = CalcFog( worldPos, vProjPos.xyz, g_FogType ); +#endif + // Needed for water fog alpha and diffuse lighting + // FIXME: we shouldn't have to compute this all the time. + o.worldPos_atten3.xyz = worldPos; + + // Needed for specular + o.worldVertToEyeVector = VSHADER_VECT_SCALE * (cEyePos - worldPos); + + // Compute bumped lighting + // FIXME: We shouldn't have to compute this for unlit materials +#if defined ( SHADER_MODEL_VS_2_0 ) && ( !USE_STATIC_CONTROL_FLOW ) + o.lightAtten.xyz = float3(0,0,0); + o.worldPos_atten3.w = 0.0f; + #if ( NUM_LIGHTS > 0 ) + o.lightAtten.x = GetVertexAttenForLight( worldPos, 0, false ); + #endif + #if ( NUM_LIGHTS > 1 ) + o.lightAtten.y = GetVertexAttenForLight( worldPos, 1, false ); + #endif + #if ( NUM_LIGHTS > 2 ) + o.lightAtten.z = GetVertexAttenForLight( worldPos, 2, false ); + #endif + #if ( NUM_LIGHTS > 3 ) + o.worldPos_atten3.w = GetVertexAttenForLight( worldPos, 3, false ); + #endif +#else + o.lightAtten.x = GetVertexAttenForLight( worldPos, 0, true ); + o.lightAtten.y = GetVertexAttenForLight( worldPos, 1, true ); + o.lightAtten.z = GetVertexAttenForLight( worldPos, 2, true ); + o.worldPos_atten3.w = GetVertexAttenForLight( worldPos, 3, true ); +#endif + + // Base texture coordinate transform + o.baseTexCoord.x = dot( v.vTexCoord0, cBaseTexCoordTransform[0] ); + o.baseTexCoord.y = dot( v.vTexCoord0, cBaseTexCoordTransform[1] ); + o.baseTexCoord.z = dot( v.vTexCoord0, cDetailTexCoordTransform[0] ); + o.baseTexCoord.w = dot( v.vTexCoord0, cDetailTexCoordTransform[1] ); + + // Tangent space transform + o.tangentSpaceTranspose[0] = float3( worldTangentS.x, worldTangentT.x, worldNormal.x ); + o.tangentSpaceTranspose[1] = float3( worldTangentS.y, worldTangentT.y, worldNormal.y ); + o.tangentSpaceTranspose[2] = float3( worldTangentS.z, worldTangentT.z, worldNormal.z ); + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_splinerope_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_splinerope_ps2x.fxc new file mode 100644 index 00000000..174330bf --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_splinerope_ps2x.fxc @@ -0,0 +1,52 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// STATIC: "SHADER_SRGB_READ" "0..1" [XBOX] +// STATIC: "SHADER_SRGB_READ" "0..0" [PC] +// STATIC: "SHADOWDEPTH" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20] +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +#include "common_ps_fxc.h" + +float4 g_FogParams : register( c0 ); +float3 g_EyePos : register( c1 ); + +// VS_OUTPUT in a common file. +#define PIXELSHADER +#include "common_splinerope_fxc.h" + +sampler BaseTextureSampler : register( s0 ); +sampler NormalSampler : register( s1 ); + +float4 main( PS_INPUT i ) : COLOR +{ + #if ( SHADOWDEPTH == 0 ) + { + float3 vNormalMapDir = tex2D( NormalSampler, i.texCoord.xy ); // Get the 3-vector from the normal map + float4 textureColor = tex2D( BaseTextureSampler, i.texCoord.xy ); + + //Expand compacted vectors + vNormalMapDir = ( vNormalMapDir - 0.5 ) * 2.0; + float3 vLightDir = float3( 0.0f, 0.0f, 1.0f ); + + float lightDirDotNormalMap = dot( vNormalMapDir, vLightDir ); //normalMap dot dirLightDir + + // do half-lambert on the dot + lightDirDotNormalMap = lightDirDotNormalMap * 0.5 + 0.5; + lightDirDotNormalMap = lightDirDotNormalMap * lightDirDotNormalMap; + + float4 resultColor; + resultColor.xyz = lightDirDotNormalMap * ( textureColor.rgb * i.argbcolor.rgb ); + resultColor.a = textureColor.a * i.argbcolor.a; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( resultColor, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); + } + #else + { + return float4( 0.0f, 0.0f, 0.0f, 1.0f ); + } + #endif +} diff --git a/mp/src/materialsystem/stdshaders/SDK_splinerope_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_splinerope_vs20.fxc new file mode 100644 index 00000000..dd7c99a1 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_splinerope_vs20.fxc @@ -0,0 +1,78 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" +#include "spline_fxc.h" + +const float4x3 cModelView : register(SHADER_SPECIFIC_CONST_0); +const float4x4 cProj : register(SHADER_SPECIFIC_CONST_3); +const float g_MinPixelSize : register(SHADER_SPECIFIC_CONST_7); + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vTint : COLOR; + float4 vParms : POSITION; // T V side_id + float4 vSplinePt0 : TEXCOORD0; // x y z rad + float4 vSplinePt1 : TEXCOORD1; // x y z rad + float4 vSplinePt2 : TEXCOORD2; // x y z rad + float4 vSplinePt3 : TEXCOORD3; // x y z rad +}; + +// VS_OUTPUT in a common file. +#include "common_splinerope_fxc.h" + +#define P0 (v.vSplinePt0) +#define P1 (v.vSplinePt1) +#define P2 (v.vSplinePt2) +#define P3 (v.vSplinePt3) + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o; + + // posrad.xyz is worldspace position and posrad.w is worldspace diameter. + float4 posrad = CatmullRomSpline( P0, P1, P2, P3, v.vParms.x ); + + // calculate projected position here so that we can figure out how much to bloat the diameter to avoid aliasing of the sort where you skip pixels in a segment. + { + // PERF FIXME!! This could be simplified quite a bit if this ever becomes a bottleneck. I feel dirty. + // Get the view-space position for two points that are posrad.w units away from each other horizontally. + float3 viewPos1 = mul4x3( float4( posrad.xyz, 1.0f ), cModelView ); + float3 viewPos2 = viewPos1 + float3( posrad.w, 0.0f, 0.0f ); + + // Project both points. + float4 projPos1 = mul( float4( viewPos1, 1.0f ), cProj ); + float4 projPos2 = mul( float4( viewPos2, 1.0f ), cProj ); + + // Get the distance of the two points from each other in normalized screen space. + float projectedDiameterInPixels = abs( ( projPos1.x / projPos1.w ) - ( projPos2.x / projPos2.w ) ); + + // Compare the distance between the two points to the minimum allowed to keep from skipping pixels and causing aliasing. + if ( projectedDiameterInPixels < g_MinPixelSize ) + { + // Scale the radius in world space so that it is bigger than the required pixel size in screen space. + posrad.w *= ( g_MinPixelSize / projectedDiameterInPixels ); + } + } + + float3 v2p = float3( 0, 0, 1 ); + v2p = posrad.xyz - cEyePos; // screen aligned + + float3 tangent = DCatmullRomSpline3( P0, P1, P2, P3, v.vParms.x ); + float3 ofs = normalize( cross( v2p, normalize( tangent ) ) ); + posrad.xyz += ofs * ( posrad.w * ( v.vParms.z - .5 ) ); + o.projPos = mul( float4(posrad.xyz, 1.0f), cViewProj ); + o.worldPos_projPosZ.xyz = posrad.xyz; + o.worldPos_projPosZ.w = o.projPos.z; + o.texCoord.xy = float2( 1.0f - v.vParms.z, v.vParms.y ); + o.argbcolor = float4( v.vTint.rgb, v.vTint.a ); + + #if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + { + o.fog = CalcFixedFunctionFog( posrad.xyz, DOWATERFOG ); + } + #endif + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_sprite_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_sprite_ps2x.fxc new file mode 100644 index 00000000..79972038 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_sprite_ps2x.fxc @@ -0,0 +1,61 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "CONSTANTCOLOR" "0..1" +// STATIC: "HDRTYPE" "0..2" +// STATIC: "SRGB" "0..1" +// STATIC: "SRGB_OUTPUT_ADAPTER" "0..1" [ps20b] + +// DYNAMIC: "HDRENABLED" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +const HALF4 g_Color : register( c0 ); +const float g_HDRColorScale : register( c1 ); + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +sampler TexSampler : register( s0 ); + +struct PS_INPUT +{ + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate + float4 color : TEXCOORD2; // Vertex color (from lighting or unlit) + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 result, sample = tex2D( TexSampler, i.baseTexCoord ); + +#if VERTEXCOLOR + sample *= i.color; +#endif + +#if CONSTANTCOLOR + sample *= g_Color; +#endif + +#if HDRTYPE && HDRENABLED + sample.xyz *= g_HDRColorScale; +#endif + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); +#if SRGB + result = FinalOutput( sample, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +#else + result = FinalOutput( sample, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_GAMMA ); +#endif + + // On Posix, we're being forced through a linear-to-gamma curve but don't want it, so we do the opposite here first +#if SRGB_OUTPUT_ADAPTER + result = GammaToLinear( result ); +#endif + + return result; +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_sprite_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_sprite_vs20.fxc new file mode 100644 index 00000000..5e427c19 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_sprite_vs20.fxc @@ -0,0 +1,65 @@ +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "SRGB" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_bVertexColor = VERTEXCOLOR ? true : false; + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vColor : COLOR0; + // make these float2's and stick the [n n 0 1] in the dot math. + float4 vTexCoord0 : TEXCOORD0; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) + float fog : FOG; +#endif + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate + float4 color : TEXCOORD2; // Vertex color (from lighting or unlit) + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 worldPos; + worldPos = mul4x3( v.vPos, cModel[0] ); + + // Transform into projection space + float4 projPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = projPos; + projPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.worldPos_projPosZ = float4( worldPos.xyz, projPos.z ); + +#if !defined( _X360 ) + o.fog = CalcFog( worldPos, projPos, g_FogType ); +#endif + if ( g_bVertexColor ) + { + // Assume that this is unlitgeneric if you are using vertex color. +#if SRGB + o.color.rgba = GammaToLinear( v.vColor.rgba ); +#else + o.color.rgba = v.vColor.rgba; +#endif + } + + // Base texture coordinates + o.baseTexCoord.xy = v.vTexCoord0.xy; + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_bump_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_bump_ps2x.fxc new file mode 100644 index 00000000..fdcb2e42 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_bump_ps2x.fxc @@ -0,0 +1,101 @@ +//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======= + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps30][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..2" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps30] +// DYNAMIC: "AMBIENT_LIGHT" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps30] + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "shader_constant_register_map.h" + +const float3 cAmbientCube[6] : register( PSREG_AMBIENT_CUBE ); +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); +PixelShaderLightInfo cLightInfo[3] : register( PSREG_LIGHT_INFO_ARRAY ); // 2 registers each - 6 registers total + +sampler BaseTextureSampler : register( s0 ); +sampler BumpTextureSampler : register( s1 ); +sampler NormalizeSampler : register( s2 ); + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; + float4 worldVertToEyeVector_Darkening : TEXCOORD1; + float3x3 tangentSpaceTranspose : TEXCOORD2; + // second row : TEXCOORD3; + // third row : TEXCOORD4; + float4 worldPos_projPosZ : TEXCOORD5; + float2 lightAtten01 : TEXCOORD6; + float2 lightAtten23 : TEXCOORD7; +}; + + + +#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + +#define worldVertToEyeVector i.worldVertToEyeVector_Darkening.xyz +#define fDarkening i.worldVertToEyeVector_Darkening.w + +#endif + + + +float4 main( PS_INPUT i ) : COLOR +{ + bool bAmbientLight = AMBIENT_LIGHT ? true : false; + int nNumLights = NUM_LIGHTS; + + float4 vLightAtten = float4( i.lightAtten01, i.lightAtten23 ); + float4 baseSample = tex2D( BaseTextureSampler, i.baseTexCoord ); + + float3 worldSpaceNormal, tangentSpaceNormal = float3(0, 0, 1); + float fSpecExp = g_EyePos_SpecExponent.w; + + float4 normalTexel = tex2D( BumpTextureSampler, i.baseTexCoord ); + tangentSpaceNormal = 2.0f * normalTexel.xyz - 1.0f; + worldSpaceNormal = normalize( mul( i.tangentSpaceTranspose, tangentSpaceNormal ) ); + + // If the exponent passed in as a constant is zero, use the value from the map as the exponent + if ( fSpecExp == 0 ) + fSpecExp = 1.0f * ( 1.0f - normalTexel.w ) + 150.0f * normalTexel.w; + + // Summation of diffuse illumination from all local lights + float3 diffuseLighting = PixelShaderDoLighting( i.worldPos_projPosZ.xyz, worldSpaceNormal, + float3( 0.0f, 0.0f, 0.0f ), false, + bAmbientLight, vLightAtten, + cAmbientCube, NormalizeSampler, nNumLights, cLightInfo, true, + false, 0, false, NormalizeSampler ); + +#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + + float3 vDummy, specularLighting; + + // Summation of specular from all local lights + PixelShaderDoSpecularLighting( i.worldPos_projPosZ.xyz, worldSpaceNormal, fSpecExp, normalize(worldVertToEyeVector), + vLightAtten, nNumLights, cLightInfo, + false, 1.0f, false, NormalizeSampler, 1.0f, false, 1.0f, + + // Outputs + specularLighting, vDummy ); + + // Specular plus diffuse, all darkened as a function of mouth openness + float3 result = (specularLighting * baseSample.a + baseSample.rgb * diffuseLighting) * fDarkening; + +#else + float3 result = baseSample.rgb * diffuseLighting * i.worldVertToEyeVector_Darkening.w; +#endif + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( float4(result, 1.0f), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc new file mode 100644 index 00000000..1dd834b5 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc @@ -0,0 +1,152 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== + +// STATIC: "INTRO" "0..1" +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "STATIC_LIGHT" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] + +#include "vortwarp_vs20_helper.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_bSkinning = SKINNING ? true : false; + +const float4 cTeethLighting : register( SHADER_SPECIFIC_CONST_0 ); +#if INTRO +const float4 const4 : register( SHADER_SPECIFIC_CONST_1 ); +#define g_Time const4.w +#define modelOrigin const4.xyz +#endif + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float2 vTexCoord0 : TEXCOORD0; + float4 vUserData : TANGENT; // Sign for cross product in w + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + float2 baseTexCoord : TEXCOORD0; + float4 worldVertToEyeVector_Darkening : TEXCOORD1; + float3x3 tangentSpaceTranspose : TEXCOORD2; + // second row : TEXCOORD3; + // third row : TEXCOORD4; + float4 worldPos_projPosZ : TEXCOORD5; + float2 lightAtten01 : TEXCOORD6; + float2 lightAtten23 : TEXCOORD7; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + float3 vNormal; + float4 vTangent; + DecompressVertex_NormalTangent( v.vNormal, v.vUserData, vNormal, vTangent ); + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal, vTangent.xyz ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, float3( 0, 0, 0 ), + vPosition.xyz, vNormal, vTangent.xyz ); +#endif + + // Perform skinning + float3 worldNormal, worldPos, worldTangentS, worldTangentT; + SkinPositionNormalAndTangentSpace( g_bSkinning, vPosition, vNormal, vTangent, + v.vBoneWeights, v.vBoneIndices, worldPos, + worldNormal, worldTangentS, worldTangentT ); + +#if INTRO + WorldSpaceVertexProcess( g_Time, modelOrigin, worldPos, worldNormal, worldTangentS, worldTangentT ); +#endif + + // Always normalize since flex path is controlled by runtime + // constant not a shader combo and will always generate the normalization + worldNormal = normalize( worldNormal ); + worldTangentS = normalize( worldTangentS ); + worldTangentT = normalize( worldTangentT ); + + // Transform into projection space + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.worldPos_projPosZ = float4( worldPos, vProjPos.z ); +#if !defined( _X360 ) + // Set fixed-function fog factor + o.fog = CalcFog( worldPos, vProjPos, g_FogType ); +#endif + // Needed for specular + o.worldVertToEyeVector_Darkening.xyz = cEyePos - worldPos; + + // Special darkening of lights for mouth open/close + o.worldVertToEyeVector_Darkening.w = cTeethLighting.w * saturate( dot( worldNormal, cTeethLighting.xyz ) );; + + // Scalar light attenuation (mouth darkening applied in pixel shader) +#if defined( SHADER_MODEL_VS_2_0 ) && ( !USE_STATIC_CONTROL_FLOW ) + o.lightAtten01.xy = float2(0,0); + o.lightAtten23.xy = float2(0,0); + #if ( NUM_LIGHTS > 0 ) + o.lightAtten01.x = GetVertexAttenForLight( worldPos, 0, false ); + #endif + #if ( NUM_LIGHTS > 1 ) + o.lightAtten01.y = GetVertexAttenForLight( worldPos, 1, false ); + #endif + #if ( NUM_LIGHTS > 2 ) + o.lightAtten23.x = GetVertexAttenForLight( worldPos, 2, false ); + #endif + #if ( NUM_LIGHTS > 3 ) + o.lightAtten23.y = GetVertexAttenForLight( worldPos, 3, false ); + #endif +#else + o.lightAtten01.x = GetVertexAttenForLight( worldPos, 0, true ); + o.lightAtten01.y = GetVertexAttenForLight( worldPos, 1, true ); + o.lightAtten23.x = GetVertexAttenForLight( worldPos, 2, true ); + o.lightAtten23.y = GetVertexAttenForLight( worldPos, 3, true ); +#endif + + o.baseTexCoord = v.vTexCoord0; + + // Tangent space transform + o.tangentSpaceTranspose[0] = float3( worldTangentS.x, worldTangentT.x, worldNormal.x ); + o.tangentSpaceTranspose[1] = float3( worldTangentS.y, worldTangentT.y, worldNormal.y ); + o.tangentSpaceTranspose[2] = float3( worldTangentS.z, worldTangentT.z, worldNormal.z ); + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_ps2x.fxc new file mode 100644 index 00000000..524a6ba2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_ps2x.fxc @@ -0,0 +1,66 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps30][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] + +#include "common_flashlight_fxc.h" +#include "shader_constant_register_map.h" + +sampler BaseTextureSampler : register( s0 ); +sampler SpotSampler : register( s1 ); +sampler FlashlightDepthSampler : register( s2 ); +sampler RandomRotationSampler : register( s3 ); + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float3 g_EyePos : register( PSREG_EYEPOS_SPEC_EXPONENT ); +const float3 g_FlashlightPos : register( PSREG_FLASHLIGHT_POSITION_RIM_BOOST ); +const float4 g_FlashlightAtten : register( PSREG_FLASHLIGHT_ATTENUATION ); +const float4x4 g_FlashlightWorldToTexture : register( PSREG_FLASHLIGHT_TO_WORLD_TEXTURE ); +const float4 g_ShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS ); + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; // Base texture coordinates + float4 spotTexCoord : TEXCOORD1; // Spotlight texture coordinates + float3 vertAtten : TEXCOORD2; // Distance/spot attenuation + float4 projPos : TEXCOORD3; // Projective space position + float3 worldPos : TEXCOORD4; // Necessary for pixel fog +}; + +float4 main( PS_INPUT i ) : COLOR +{ +#if defined( SHADER_MODEL_PS_2_0 ) + float3 result = tex2Dproj( SpotSampler, i.spotTexCoord.xyzw ); +#else + float3 vProjCoords = i.spotTexCoord.xyz / i.spotTexCoord.w; + float3 result = tex2D( SpotSampler, vProjCoords ); +#endif + + result *= cFlashlightColor.rgb; + +#if FLASHLIGHTSHADOWS && ( defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 ) ) + result *= DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, i.projPos.xy / i.projPos.z, FLASHLIGHTDEPTHFILTERMODE, g_ShadowTweaks, true ); +#endif + result *= 0.35f; // Without this, unshadowed teeth always seem to glow + + result *= i.vertAtten; // Distance atten, NdotL and forward vector + + float4 baseSample = tex2D( BaseTextureSampler, i.baseTexCoord ); + result *= baseSample.rgb; // Multiply by base map and diffuse + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos.xyz, i.projPos.z ); + return FinalOutput( float4( result, baseSample.a ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_vs20.fxc new file mode 100644 index 00000000..8c5ce4c5 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_flashlight_vs20.fxc @@ -0,0 +1,149 @@ +//======= Copyright © 1996-2007, Valve Corporation, All rights reserved. ====== + +// STATIC: "INTRO" "0..1" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] + +#include "vortwarp_vs20_helper.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_bSkinning = SKINNING ? true : false; + +const float4 cFlashlightPosition : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cSpotlightProj1 : register( SHADER_SPECIFIC_CONST_1 ); +const float4 cSpotlightProj2 : register( SHADER_SPECIFIC_CONST_2 ); +const float4 cSpotlightProj3 : register( SHADER_SPECIFIC_CONST_3 ); +const float4 cSpotlightProj4 : register( SHADER_SPECIFIC_CONST_4 ); +const float4 cFlashlighAtten : register( SHADER_SPECIFIC_CONST_5 ); // const, linear, quadratic & farZ + +const float4 cTeethLighting : register( SHADER_SPECIFIC_CONST_8 ); +#if INTRO +const float4 const4 : register( SHADER_SPECIFIC_CONST_9 ); +#define g_Time const4.w +#define modelOrigin const4.xyz +#endif + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float2 vTexCoord0 : TEXCOORD0; + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + float2 baseTexCoord : TEXCOORD0; // Base texture coordinates + float4 spotTexCoord : TEXCOORD1; // Spotlight texture coordinates + float3 vertAtten : TEXCOORD2; // Distance/spot attenuation + float4 vProjPos : TEXCOORD3; // Projective space position + float3 worldPos : TEXCOORD4; // Necessary for pixel fog +}; + + +float RemapValClamped_01( float val, float A, float B ) +{ + float cVal = (val - A) / (B - A); + cVal = saturate( cVal ); + return cVal; +} + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + float3 vNormal; + DecompressVertex_Normal( v.vNormal, vNormal ); + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, float3( 0, 0, 0 ), vPosition.xyz, vNormal ); +#endif + + // Normalize the flexed normal + vNormal.xyz = normalize( vNormal.xyz ); + + // Transform the position + float3 worldPos, worldNormal; + SkinPositionAndNormal( g_bSkinning, vPosition, vNormal, v.vBoneWeights, v.vBoneIndices, worldPos, worldNormal ); + +#if INTRO + float3 dummy = float3( 0.0f, 0.0f, 0.0f ); + WorldSpaceVertexProcess( g_Time, modelOrigin, worldPos, worldNormal, dummy, dummy ); +#endif + + // Transform into projection space + o.projPos = mul( float4( worldPos, 1 ), cViewProj ); + o.worldPos = worldPos.xyz; + o.vProjPos = o.projPos; +#if !defined( _X360 ) + // Set fixed-function fog factor + o.fog = CalcFog( worldPos, o.projPos, g_FogType ); +#endif + // Spotlight texture coordinates + o.spotTexCoord.x = dot( cSpotlightProj1, float4(worldPos, 1) ); + o.spotTexCoord.y = dot( cSpotlightProj2, float4(worldPos, 1) ); + o.spotTexCoord.z = dot( cSpotlightProj3, float4(worldPos, 1) ); + o.spotTexCoord.w = dot( cSpotlightProj4, float4(worldPos, 1) ); + + // Compute vector to light + float3 vWorldPosToLightVector = cFlashlightPosition.xyz - worldPos; + + float3 vDistAtten = float3(1, 1, 1); + vDistAtten.z = dot( vWorldPosToLightVector, vWorldPosToLightVector ); + vDistAtten.y = rsqrt( vDistAtten.z ); + + float flDist = vDistAtten.z * vDistAtten.y; // Distance to light + vDistAtten.z = 1.0f / vDistAtten.z; // 1 / distsquared + + float fFarZ = cFlashlighAtten.w; + + float NdotL = saturate( dot( worldNormal, normalize( vWorldPosToLightVector ) ) ); + + float endFalloffFactor = RemapValClamped_01( flDist, fFarZ, 0.6 * fFarZ ); + o.vertAtten.xyz = endFalloffFactor * dot( vDistAtten, cFlashlighAtten.xyz ); + + // Final attenuation from flashlight only... + float linearAtten = NdotL * dot( vDistAtten, cFlashlighAtten.xyz ) * endFalloffFactor; + + // Forward vector + float3 vForward = cTeethLighting.xyz; + float fIllumFactor = cTeethLighting.w; + + // Modulate flashlight by mouth darkening + o.vertAtten = linearAtten * fIllumFactor * saturate( dot( worldNormal, vForward ) ); + + o.baseTexCoord = v.vTexCoord0; + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_ps2x.fxc new file mode 100644 index 00000000..5e715a4f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_ps2x.fxc @@ -0,0 +1,48 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps30][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps30] + + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +sampler BaseTextureSampler : register( s0 ); + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; + float3 vertAtten : TEXCOORD1; + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 baseSample = tex2D( BaseTextureSampler, i.baseTexCoord ); + + float4 result; + result.xyz = baseSample.xyz * i.vertAtten; + result.a = baseSample.a; + + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( result, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_vs20.fxc new file mode 100644 index 00000000..9a9ab045 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_vs20.fxc @@ -0,0 +1,127 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== + +// STATIC: "INTRO" "0..1" +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "DYNAMIC_LIGHT" "0..1" +// DYNAMIC: "STATIC_LIGHT" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] + +#include "vortwarp_vs20_helper.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_bSkinning = SKINNING ? true : false; + +const float4 cTeethLighting : register( SHADER_SPECIFIC_CONST_0 ); +#if INTRO +const float4 const4 : register( SHADER_SPECIFIC_CONST_1 ); +#define g_Time const4.w +#define modelOrigin const4.xyz +#endif + +#ifdef SHADER_MODEL_VS_3_0 +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_6 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_7 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float2 vTexCoord0 : TEXCOORD0; + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#ifdef SHADER_MODEL_VS_3_0 + float vVertexID : POSITION2; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + float2 baseTexCoord : TEXCOORD0; + float3 vertAtten : TEXCOORD1; + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + bool bDynamicLight = DYNAMIC_LIGHT ? true : false; + bool bStaticLight = STATIC_LIGHT ? true : false; + + float4 vPosition = v.vPos; + float3 vNormal; + DecompressVertex_Normal( v.vNormal, vNormal ); + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, v.vVertexID, float3( 0, 0, 0 ), vPosition.xyz, vNormal ); +#endif + + // Normalize the flexed normal + vNormal.xyz = normalize( vNormal.xyz ); + + // Transform the position + float3 worldPos, worldNormal; + SkinPositionAndNormal( g_bSkinning, vPosition, vNormal, v.vBoneWeights, v.vBoneIndices, worldPos, worldNormal ); + +#if INTRO + float3 dummy = float3( 0.0f, 0.0f, 0.0f ); + WorldSpaceVertexProcess( g_Time, modelOrigin, worldPos, worldNormal, dummy, dummy ); +#endif + + // Transform into projection space + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); +#if !defined( _X360 ) + // Set fixed-function fog factor + o.fog = CalcFog( worldPos, vProjPos, g_FogType ); +#endif + + // Compute lighting +#if ( USE_STATIC_CONTROL_FLOW ) || defined ( SHADER_MODEL_VS_3_0 ) + float3 linearColor = DoLighting( worldPos, worldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, false ); +#else + float3 linearColor = DoLightingUnrolled( worldPos, worldNormal, float3(0.0f, 0.0f, 0.0f), bStaticLight, bDynamicLight, false, NUM_LIGHTS ); +#endif + + // Forward vector + float3 vForward = cTeethLighting.xyz; + float fIllumFactor = cTeethLighting.w; + + // Darken by forward dot normal and illumination factor + linearColor *= fIllumFactor * saturate( dot( worldNormal, vForward ) ); + + o.vertAtten = linearColor; + o.baseTexCoord = v.vTexCoord0; + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps11.fxc b/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps11.fxc new file mode 100644 index 00000000..071d666c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps11.fxc @@ -0,0 +1,12 @@ +sampler TextureSampler : register( s0 ); + +struct PS_INPUT +{ + float4 vColor0 : COLOR0; + float2 vTexCoord0 : TEXCOORD0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + return i.vColor0 * tex2D( TextureSampler, i.vTexCoord0 ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps2x.fxc new file mode 100644 index 00000000..1620638f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_ps2x.fxc @@ -0,0 +1,19 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] + +#include "common_ps_fxc.h" + +sampler TextureSampler : register( s0 ); + +struct PS_INPUT +{ + float4 vColor0 : COLOR0; + float2 vTexCoord0 : TEXCOORD0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 result = i.vColor0 * tex2D( TextureSampler, i.vTexCoord0 ); + + return FinalOutput( result, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_vs20.fxc new file mode 100644 index 00000000..37249305 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_unlitgeneric_vs20.fxc @@ -0,0 +1,91 @@ +// STATIC: "VERTEXCOLOR" "0..1" +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; +static const bool g_bSkinning = SKINNING ? true : false; + +const float4 cBaseTextureTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +const float4 cMaskTextureTransform[2] : register( SHADER_SPECIFIC_CONST_2 ); +const float4 cDetailTextureTransform[2] : register( SHADER_SPECIFIC_CONST_4 ); +const float4 g_vVertexColor : register( SHADER_SPECIFIC_CONST_6 ); + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + +#if VERTEXCOLOR + float4 vColor : COLOR0; +#endif + + float4 vTexCoord0 : TEXCOORD0; +}; + +struct VS_OUTPUT +{ + float4 vProjPos : POSITION; + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + float2 vTexCoord2 : TEXCOORD2; + float2 vTexCoord3 : TEXCOORD3; + + float4 vColor : COLOR0; + float4 fogFactorW : COLOR1; + +#if !defined( _X360 ) + float fog : FOG; +#endif + + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog +}; + + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float3 worldPos; + float3 worldNormal; + + //------------------------------------------------------------------------------ + // Vertex blending + //------------------------------------------------------------------------------ + SkinPosition( g_bSkinning, v.vPos, v.vBoneWeights, v.vBoneIndices, worldPos ); + + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.vProjPos = vProjPos; + vProjPos = dot( float4( worldPos, 1 ), cViewProjZ ); + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); + + //------------------------------------------------------------------------------ + // Fog + //------------------------------------------------------------------------------ + o.fogFactorW = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + + //------------------------------------------------------------------------------ + // Texture coord transforms + //------------------------------------------------------------------------------ + o.vTexCoord0 = mul( v.vTexCoord0, (float2x4)cBaseTextureTransform ); + o.vTexCoord3 = mul( v.vTexCoord0, (float2x4)cDetailTextureTransform ); + + o.vColor = cModulationColor; + +#if VERTEXCOLOR + // 0 or 1 for g_vVertexColor.x, eliminating a bool + o.vColor = lerp( o.vColor, o.vColor * v.vColor, g_vVertexColor.x ); +#endif + + return o; +} + + + diff --git a/mp/src/materialsystem/stdshaders/SDK_unlittwotexture_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_unlittwotexture_ps2x.fxc new file mode 100644 index 00000000..4df0de86 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_unlittwotexture_ps2x.fxc @@ -0,0 +1,59 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// STATIC: "TRANSLUCENT" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +#if defined( SHADER_MODEL_PS_2_0 ) + #define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +const HALF4 g_DiffuseModulation : register( c1 ); +#if !FLASHLIGHT + // we don't use these with HDR. + const HALF3 g_EnvmapContrast : register( c2 ); + const HALF3 g_EnvmapSaturation : register( c3 ); +#endif + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +const float4 g_FlashlightAttenuationFactors : register( c22 ); +const HALF3 g_FlashlightPos : register( c23 ); +const float4x4 g_FlashlightWorldToTexture : register( c24 ); // through c27 + +sampler BaseTextureSampler : register( s0 ); +sampler BaseTextureSampler2 : register( s1 ); + +struct PS_INPUT +{ + float4 projPos : POSITION; // Projection-space position + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate + HALF2 baseTexCoord2 : TEXCOORD1; // Base texture coordinate + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for water fog dest alpha + +#if defined( _X360 ) //matching pixel shader inputs to vertex shader outputs to avoid shader patches + float4 vColor : COLOR0; +#endif +}; + +float4 main( PS_INPUT i ) : COLOR +{ + float4 baseColor = tex2D( BaseTextureSampler, i.baseTexCoord.xy ); + float4 baseColor2 = tex2D( BaseTextureSampler2, i.baseTexCoord2.xy ); + float4 result = baseColor * baseColor2 * g_DiffuseModulation; + + // This material can only get a non-opaque alpha if the material is marked as translucent +# if ( TRANSLUCENT == 0 ) + result.a = 1.0f; +# endif + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( result, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_unlittwotexture_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_unlittwotexture_vs20.fxc new file mode 100644 index 00000000..9c9976ac --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_unlittwotexture_vs20.fxc @@ -0,0 +1,66 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); // 0 & 1 +const float4 cBaseTexCoordTransform2[2] : register( SHADER_SPECIFIC_CONST_2 ); // 2 & 3 + +struct VS_INPUT +{ + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + // make these float2's and stick the [n n 0 1] in the dot math. + float4 vTexCoord0 : TEXCOORD0; +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + float fog : FOG; +#endif + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate + HALF2 baseTexCoord2 : TEXCOORD1; // Base texture coordinate + float4 worldPos_projPosZ : TEXCOORD7; // Necessary for water fog dest alpha + + float4 vColor : COLOR0; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + + // Perform skinning + float3 worldNormal, worldPos; + SkinPosition( g_bSkinning, vPosition, v.vBoneWeights, v.vBoneIndices, worldPos ); + + // Transform into projection space + float4 projPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = projPos; + +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + o.fog = CalcFixedFunctionFog( worldPos, g_FogType ); +#endif + + // Needed for water fog alpha; + o.worldPos_projPosZ = float4( worldPos.xyz, o.projPos.z ); // FIXME: we shouldn't have to compute this all thie time. + + o.baseTexCoord.x = dot( v.vTexCoord0, cBaseTexCoordTransform[0] ); // Base texture coordinates + o.baseTexCoord.y = dot( v.vTexCoord0, cBaseTexCoordTransform[1] ); + o.baseTexCoord2.x = dot( v.vTexCoord0, cBaseTexCoordTransform2[0] ); // Secondary texture coordinates + o.baseTexCoord2.y = dot( v.vTexCoord0, cBaseTexCoordTransform2[1] ); + + o.vColor = cModulationColor; + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps20b.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps20b.fxc new file mode 100644 index 00000000..90141399 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps20b.fxc @@ -0,0 +1,381 @@ +//======= Copyright © 1996-2008, Valve Corporation, All rights reserved. ====== + +// STATIC: "CUBEMAP" "0..1" +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "SELFILLUMFRESNEL" "0..1" +// STATIC: "NORMALMAPALPHAENVMAPMASK" "0..1" +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..6" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "ENVMAPMASK" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" [ps20] +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..2" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps30] +// DYNAMIC: "AMBIENT_LIGHT" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] [PC] + +// We don't use light combos when doing the flashlight +// SKIP: ( $FLASHLIGHT != 0 ) && ( $NUM_LIGHTS > 0 ) [PC] + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps30] + +// Flashlight shadow filter mode is irrelevant if there is no flashlight +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] + +// SKIP: (! $DETAILTEXTURE) && ( $DETAIL_BLEND_MODE != 0 ) + +// Don't do diffuse warp on flashlight +// SKIP: ( $FLASHLIGHT == 1 ) && ( $LIGHTWARPTEXTURE == 1 ) [PC] + +// Only warp diffuse if we have it at all +// SKIP: ( $DIFFUSELIGHTING == 0 ) && ( $LIGHTWARPTEXTURE == 1 ) + +// Skip this since it blows ps20 instruction limits +// SKIP: ( $SELFILLUMFRESNEL == 1 ) && ( $LIGHTWARPTEXTURE == 1 ) + +// Only need self illum fresnel when self illum enabled +// SKIP: ( $SELFILLUM == 0 ) && ( $SELFILLUMFRESNEL == 1 ) +// SKIP: ( $FLASHLIGHT == 1 ) && ( $SELFILLUMFRESNEL == 1 ) [PC] +// SKIP: ( $FLASHLIGHT == 1 ) && ( $SELFILLUM == 1 ) [PC] +// SKIP: ( $SELFILLUMFRESNEL == 1 ) && ( $DETAILTEXTURE == 1 ) +// SKIP: ( $SELFILLUMFRESNEL == 1 ) && ( $NORMALMAPALPHAENVMAPMASK == 1 ) + +// BlendTintByBaseAlpha is incompatible with other interpretations of alpha +// SKIP: ($BLENDTINTBYBASEALPHA) && ($SELFILLUM) + +// Only _XBOX allows flashlight and cubemap in the current implementation +// SKIP: $FLASHLIGHT && $CUBEMAP [PC] + +// Meaningless combinations +// SKIP: $NORMALMAPALPHAENVMAPMASK && !$CUBEMAP +// SKIP: $NORMALMAPALPHAENVMAPMASK && $ENVMAPMASK +// SKIP: $ENVMAPMASK && !$CUBEMAP + +#include "common_flashlight_fxc.h" +#include "common_vertexlitgeneric_dx9.h" + +const float4 g_EnvmapTint_TintReplaceFactor : register( c0 ); +const float4 g_DiffuseModulation : register( c1 ); +const float4 g_EnvmapContrast_ShadowTweaks : register( c2 ); +const float3 g_EnvmapSaturation : register( c3 ); +const float4 g_SelfIllumTint_and_BlendFactor : register( c4 ); +#define g_SelfIllumTint ( g_SelfIllumTint_and_BlendFactor.rgb) +#define g_DetailBlendFactor (g_SelfIllumTint_and_BlendFactor.w) + +const float3 cAmbientCube[6] : register( c5 ); + +// 11, 12 not used? +#if ( SELFILLUMFRESNEL == 1 ) + const float4 g_SelfIllumScaleBiasExpBrightness : register( c11 ); +#endif + +const float4 g_ShaderControls : register( c12 ); +#define g_fPixelFogType g_ShaderControls.x +#define g_fWriteDepthToAlpha g_ShaderControls.y +#define g_fWriteWaterFogToDestAlpha g_ShaderControls.z + + +// 2 registers each - 6 registers total +PixelShaderLightInfo cLightInfo[3] : register( c13 ); // through c18 + +const float4 g_EyePos_MinLight : register( c20 ); +#define g_EyePos g_EyePos_MinLight.xyz +#define g_fMinLighting g_EyePos_MinLight.w + +const float4 g_FogParams : register( c21 ); + +const float4 g_FlashlightAttenuationFactors : register( c22 ); +const float3 g_FlashlightPos : register( c23 ); +const float4x4 g_FlashlightWorldToTexture : register( c24 ); // through c27 + +sampler BaseTextureSampler : register( s0 ); +sampler EnvmapSampler : register( s1 ); +sampler DetailSampler : register( s2 ); +sampler BumpmapSampler : register( s3 ); +sampler EnvmapMaskSampler : register( s4 ); +sampler NormalizeSampler : register( s5 ); +sampler RandRotSampler : register( s6 ); // RandomRotation sampler +sampler FlashlightSampler : register( s7 ); +sampler ShadowDepthSampler : register( s8 ); // Flashlight shadow depth map sampler +sampler DiffuseWarpSampler : register( s9 ); // Lighting warp sampler (1D texture for diffuse lighting modification) + +struct PS_INPUT +{ + float4 baseTexCoord2_tangentSpaceVertToEyeVectorXY : TEXCOORD0; + float3 lightAtten : TEXCOORD1; + float4 worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ : TEXCOORD2; + float3 vWorldNormal : TEXCOORD3; // World-space normal + float4 vWorldTangent : TEXCOORD4; +#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + float4 vProjPos : TEXCOORD5; +#else + float3 vWorldBinormal : TEXCOORD5; +#endif + float4 worldPos_projPosZ : TEXCOORD6; + float3 detailTexCoord_atten3 : TEXCOORD7; + float4 fogFactorW : COLOR1; + +#if defined( _X360 ) +#if FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD8; +#endif +#endif +}; + +// Calculate both types of Fog and lerp to get result +float CalcPixelFogFactorConst( float fPixelFogType, const float4 fogParams, const float flEyePosZ, const float flWorldPosZ, const float flProjPosZ ) +{ + float fRangeFog = CalcRangeFog( flProjPosZ, fogParams.x, fogParams.z, fogParams.w ); + float fHeightFog = CalcWaterFogAlpha( fogParams.y, flEyePosZ, flWorldPosZ, flProjPosZ, fogParams.w ); + return lerp( fRangeFog, fHeightFog, fPixelFogType ); +} + +// Blend both types of Fog and lerp to get result +float3 BlendPixelFogConst( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, float fPixelFogType ) +{ + pixelFogFactor = saturate( pixelFogFactor ); + float3 fRangeResult = lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog + float3 fHeightResult = lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); + return lerp( fRangeResult, fHeightResult, fPixelFogType ); +} + +float4 FinalOutputConst( const float4 vShaderColor, float pixelFogFactor, float fPixelFogType, const int iTONEMAP_SCALE_TYPE, float fWriteDepthToDestAlpha, const float flProjZ ) +{ + float4 result = vShaderColor; + if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR ) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA ) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } + + result.a = lerp( result.a, DepthToDestAlpha( flProjZ ), fWriteDepthToDestAlpha ); + + result.rgb = BlendPixelFogConst( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType ); + result.rgb = SRGBOutput( result.rgb ); //SRGB in pixel shader conversion + + return result; +} + +float4 main( PS_INPUT i ) : COLOR +{ + bool bCubemap = CUBEMAP ? true : false; + bool bDiffuseLighting = DIFFUSELIGHTING ? true : false; + bool bDoDiffuseWarp = LIGHTWARPTEXTURE ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bSelfIllumFresnel = SELFILLUMFRESNEL ? true : false; + bool bNormalMapAlphaEnvmapMask = NORMALMAPALPHAENVMAPMASK ? true : false; + bool bHalfLambert = HALFLAMBERT ? true : false; + bool bFlashlight = (FLASHLIGHT!=0) ? true : false; + bool bAmbientLight = AMBIENT_LIGHT ? true : false; + bool bDetailTexture = DETAILTEXTURE ? true : false; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA ? true : false; + bool bEnvmapMask = ENVMAPMASK ? true : false; + int nNumLights = NUM_LIGHTS; + +#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + float3 vWorldBinormal = cross( i.vWorldNormal.xyz, i.vWorldTangent.xyz ) * i.vWorldTangent.w; +#else + float3 vWorldBinormal = i.vWorldBinormal; +#endif + + // Unpack four light attenuations + float4 vLightAtten = float4( i.lightAtten, i.detailTexCoord_atten3.z ); + + float4 baseColor = float4( 1.0f, 1.0f, 1.0f, 1.0f ); + baseColor = tex2D( BaseTextureSampler, i.baseTexCoord2_tangentSpaceVertToEyeVectorXY.xy ); + +#if DETAILTEXTURE + float4 detailColor = tex2D( DetailSampler, i.detailTexCoord_atten3.xy ); + baseColor = TextureCombine( baseColor, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor ); +#endif + +#if ENVMAPMASK + // Blixibon - $bumpmap + $envmapmask + float3 specularFactor = 1.0f; +#else + float specularFactor = 1.0f; +#endif + +#if !DETAILTEXTURE + // Blixibon - $bumpmap transform support + float4 normalTexel = tex2D( BumpmapSampler, i.detailTexCoord_atten3.xy ); +#else + float4 normalTexel = tex2D( BumpmapSampler, i.baseTexCoord2_tangentSpaceVertToEyeVectorXY.xy ); +#endif + float3 tangentSpaceNormal = normalTexel * 2.0f - 1.0f; + + if ( bNormalMapAlphaEnvmapMask ) + { + specularFactor = normalTexel.a; + } + else if( bEnvmapMask ) + { + float4 envmapMaskTexel = tex2D( EnvmapMaskSampler, i.baseTexCoord2_tangentSpaceVertToEyeVectorXY.xy ); + specularFactor *= envmapMaskTexel.xyz; + } + + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); + + float3 worldSpaceNormal = Vec3TangentToWorld( tangentSpaceNormal.xyz, i.vWorldNormal, i.vWorldTangent, vWorldBinormal ); + if ( bDiffuseLighting || bFlashlight || bCubemap || bSelfIllumFresnel ) + { +#if ( defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) + worldSpaceNormal = normalize( worldSpaceNormal ); +#else + worldSpaceNormal = NormalizeWithCubemap( NormalizeSampler, worldSpaceNormal ); +#endif + } + + if ( bDiffuseLighting ) + { + diffuseLighting = PixelShaderDoLighting( i.worldPos_projPosZ.xyz, worldSpaceNormal, + float3( 0.0f, 0.0f, 0.0f ), false, bAmbientLight, vLightAtten, + cAmbientCube, NormalizeSampler, nNumLights, cLightInfo, bHalfLambert, + false, 1.0f, bDoDiffuseWarp, DiffuseWarpSampler ); + } + + float3 albedo = baseColor; + if (bBlendTintByBaseAlpha) + { + float3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = lerp(tintedColor, g_DiffuseModulation.rgb, g_EnvmapTint_TintReplaceFactor.w); + albedo = lerp(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + float alpha = g_DiffuseModulation.a; + if ( !bSelfIllum && !bBlendTintByBaseAlpha ) + { + alpha *= baseColor.a; + } + + +#if FLASHLIGHT + if( bFlashlight ) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; + float2 vProjPos = float2(0, 0); +// On ps_2_b, we can do shadow mapping +#if ( FLASHLIGHTSHADOWS && (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) ) + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = FLASHLIGHTSHADOWS; + vProjPos = i.vProjPos.xy / i.vProjPos.w; // Screen-space position for shadow map noise +#endif + +#if defined ( _X360 ) + float4 flashlightSpacePosition = i.flashlightSpacePos; +#else + float4 flashlightSpacePosition = mul( float4( i.worldPos_projPosZ.xyz, 1.0f ), g_FlashlightWorldToTexture ); +#endif + + float3 flashlightColor = DoFlashlight( g_FlashlightPos, i.worldPos_projPosZ.xyz, flashlightSpacePosition, + worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, vProjPos, false, g_EnvmapContrast_ShadowTweaks ); + +#if defined ( _X360 ) + diffuseLighting += flashlightColor; +#else + diffuseLighting = flashlightColor; +#endif + + } +#endif + + +#if FOGTYPE == 2 || FLASHLIGHT != 0 + float3 diffuseComponent = albedo * diffuseLighting; +#else + float3 vEyeDir = normalize( i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ); + float flFresnelMinlight = saturate( dot( worldSpaceNormal, vEyeDir ) ); + + float3 diffuseComponent = albedo * lerp( diffuseLighting, 1, g_fMinLighting * flFresnelMinlight ); +#endif + + +#if !FLASHLIGHT || defined ( _X360 ) + if ( bSelfIllum ) + { + #if ( SELFILLUMFRESNEL == 1 ) // To free up the constant register...see top of file + // This will apply a fresnel term based on the vertex normal (not the per-pixel normal!) to help fake and internal glow look + #if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + float3 vVertexNormal = normalize( i.vWorldNormal.xyz ); + float flSelfIllumFresnel = ( pow( saturate( dot( vVertexNormal.xyz, normalize( i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ) ) ), g_SelfIllumScaleBiasExpBrightness.z ) * g_SelfIllumScaleBiasExpBrightness.x ) + g_SelfIllumScaleBiasExpBrightness.y; + + float3 selfIllumComponent = g_SelfIllumTint * albedo * g_SelfIllumScaleBiasExpBrightness.w; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a * saturate( flSelfIllumFresnel ) ); + #else + float3 vVertexNormal = i.vWorldNormal.xyz; + float flSelfIllumFresnel = ( pow( saturate( dot( vVertexNormal.xyz, ( i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ) ) ), g_SelfIllumScaleBiasExpBrightness.z ) * g_SelfIllumScaleBiasExpBrightness.x ) + g_SelfIllumScaleBiasExpBrightness.y; + + float3 selfIllumComponent = g_SelfIllumTint * albedo * g_SelfIllumScaleBiasExpBrightness.w; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a * saturate( flSelfIllumFresnel ) ); + #endif + #else + float3 selfIllumComponent = g_SelfIllumTint * albedo; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a ); + #endif + } +#endif + + float3 specularLighting = float3( 0.0f, 0.0f, 0.0f ); +#if !FLASHLIGHT || defined ( _X360 ) + if( bCubemap ) + { + float3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ); + + specularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint_TintReplaceFactor.rgb; + float3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast_ShadowTweaks ); + float3 greyScale = dot( specularLighting, float3( 0.299f, 0.587f, 0.114f ) ); + specularLighting = lerp( greyScale, specularLighting, g_EnvmapSaturation ); + } +#endif + + float3 result = diffuseComponent + specularLighting; + +#if defined(SHADER_MODEL_PS_2_0) + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); +#else + float fogFactor = CalcPixelFogFactor( g_fPixelFogType, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); +#endif + +#if defined( SHADER_MODEL_PS_2_0 ) + #if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + alpha = fogFactor; + #endif +#else // 2b or higher + alpha = lerp( alpha, fogFactor, g_fPixelFogType * g_fWriteWaterFogToDestAlpha ); // Use the fog factor if it's height fog +#endif + +#if defined( SHADER_MODEL_PS_2_0 ) + return FinalOutput( float4( result.rgb, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, false, i.worldPos_projPosZ.w ); +#else + return FinalOutput( float4( result.rgb, alpha ), fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, i.worldPos_projPosZ.w ); +#endif + +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps2x.fxc new file mode 100644 index 00000000..332b2572 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_ps2x.fxc @@ -0,0 +1,372 @@ +//======= Copyright © 1996-2008, Valve Corporation, All rights reserved. ====== + +// STATIC: "CUBEMAP" "0..1" +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "SELFILLUMFRESNEL" "0..1" +// STATIC: "NORMALMAPALPHAENVMAPMASK" "0..1" +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..6" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "ENVMAPMASK" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" [ps20] +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..2" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps30] +// DYNAMIC: "AMBIENT_LIGHT" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] [PC] + +// We don't use light combos when doing the flashlight +// SKIP: ( $FLASHLIGHT != 0 ) && ( $NUM_LIGHTS > 0 ) [PC] + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps30] + +// Flashlight shadow filter mode is irrelevant if there is no flashlight +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] + +// SKIP: (! $DETAILTEXTURE) && ( $DETAIL_BLEND_MODE != 0 ) + +// Don't do diffuse warp on flashlight +// SKIP: ( $FLASHLIGHT == 1 ) && ( $LIGHTWARPTEXTURE == 1 ) [PC] + +// Only warp diffuse if we have it at all +// SKIP: ( $DIFFUSELIGHTING == 0 ) && ( $LIGHTWARPTEXTURE == 1 ) + +// Skip this since it blows ps20 instruction limits +// SKIP: ( $SELFILLUMFRESNEL == 1 ) && ( $LIGHTWARPTEXTURE == 1 ) + +// Only need self illum fresnel when self illum enabled +// SKIP: ( $SELFILLUM == 0 ) && ( $SELFILLUMFRESNEL == 1 ) +// SKIP: ( $FLASHLIGHT == 1 ) && ( $SELFILLUMFRESNEL == 1 ) [PC] +// SKIP: ( $FLASHLIGHT == 1 ) && ( $SELFILLUM == 1 ) [PC] +// SKIP: ( $SELFILLUMFRESNEL == 1 ) && ( $DETAILTEXTURE == 1 ) +// SKIP: ( $SELFILLUMFRESNEL == 1 ) && ( $NORMALMAPALPHAENVMAPMASK == 1 ) + +// BlendTintByBaseAlpha is incompatible with other interpretations of alpha +// SKIP: ($BLENDTINTBYBASEALPHA) && ($SELFILLUM) + +// Only _XBOX allows flashlight and cubemap in the current implementation +// SKIP: $FLASHLIGHT && $CUBEMAP [PC] + +// Meaningless combinations +// SKIP: $NORMALMAPALPHAENVMAPMASK && !$CUBEMAP +// SKIP: $NORMALMAPALPHAENVMAPMASK && $ENVMAPMASK +// SKIP: $ENVMAPMASK && !$CUBEMAP + +#include "common_flashlight_fxc.h" +#include "common_vertexlitgeneric_dx9.h" + +const float4 g_EnvmapTint_TintReplaceFactor : register( c0 ); +const float4 g_DiffuseModulation : register( c1 ); +const float4 g_EnvmapContrast_ShadowTweaks : register( c2 ); +const float3 g_EnvmapSaturation : register( c3 ); +const float4 g_SelfIllumTint_and_BlendFactor : register( c4 ); +#define g_SelfIllumTint ( g_SelfIllumTint_and_BlendFactor.rgb) +#define g_DetailBlendFactor (g_SelfIllumTint_and_BlendFactor.w) + +const float3 cAmbientCube[6] : register( c5 ); + +// 11, 12 not used? +#if ( SELFILLUMFRESNEL == 1 ) + const float4 g_SelfIllumScaleBiasExpBrightness : register( c11 ); +#endif + +const float4 g_ShaderControls : register( c12 ); +#define g_fPixelFogType g_ShaderControls.x +#define g_fWriteDepthToAlpha g_ShaderControls.y +#define g_fWriteWaterFogToDestAlpha g_ShaderControls.z + + +// 2 registers each - 6 registers total +PixelShaderLightInfo cLightInfo[3] : register( c13 ); // through c18 + +const float3 g_EyePos : register( c20 ); +const float4 g_FogParams : register( c21 ); + +const float4 g_FlashlightAttenuationFactors : register( c22 ); +const float3 g_FlashlightPos : register( c23 ); +const float4x4 g_FlashlightWorldToTexture : register( c24 ); // through c27 + +sampler BaseTextureSampler : register( s0 ); +sampler EnvmapSampler : register( s1 ); +sampler DetailSampler : register( s2 ); +sampler BumpmapSampler : register( s3 ); +sampler EnvmapMaskSampler : register( s4 ); +sampler NormalizeSampler : register( s5 ); +sampler RandRotSampler : register( s6 ); // RandomRotation sampler +sampler FlashlightSampler : register( s7 ); +sampler ShadowDepthSampler : register( s8 ); // Flashlight shadow depth map sampler +sampler DiffuseWarpSampler : register( s9 ); // Lighting warp sampler (1D texture for diffuse lighting modification) + +struct PS_INPUT +{ + float4 baseTexCoord2_tangentSpaceVertToEyeVectorXY : TEXCOORD0; + float3 lightAtten : TEXCOORD1; + float4 worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ : TEXCOORD2; + float3 vWorldNormal : TEXCOORD3; // World-space normal + float4 vWorldTangent : TEXCOORD4; +#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + float4 vProjPos : TEXCOORD5; +#else + float3 vWorldBinormal : TEXCOORD5; +#endif + float4 worldPos_projPosZ : TEXCOORD6; + float3 detailTexCoord_atten3 : TEXCOORD7; + float4 fogFactorW : COLOR1; + +#if defined( _X360 ) +#if FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD8; +#endif +#endif +}; + +// Calculate both types of Fog and lerp to get result +float CalcPixelFogFactorConst( float fPixelFogType, const float4 fogParams, const float3 vEyePos, const float3 vWorldPos, const float flProjPosZ ) +{ + float fRangeFog = CalcRangeFogFactorNonFixedFunction( vWorldPos, vEyePos, fogParams.z, fogParams.x, fogParams.w ); + float fHeightFog = CalcWaterFogAlpha( fogParams.y, vEyePos.z, vWorldPos.z, flProjPosZ, fogParams.w ); + return lerp( fRangeFog, fHeightFog, fPixelFogType ); +} + +// Blend both types of Fog and lerp to get result +float3 BlendPixelFogConst( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, float fPixelFogType ) +{ + pixelFogFactor = saturate( pixelFogFactor ); + float3 fRangeResult = lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog + float3 fHeightResult = lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); + return lerp( fRangeResult, fHeightResult, fPixelFogType ); +} + +float4 FinalOutputConst( const float4 vShaderColor, float pixelFogFactor, float fPixelFogType, const int iTONEMAP_SCALE_TYPE, float fWriteDepthToDestAlpha, const float flProjZ ) +{ + float4 result = vShaderColor; + if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR ) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA ) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } + + result.a = lerp( result.a, DepthToDestAlpha( flProjZ ), fWriteDepthToDestAlpha ); + + result.rgb = BlendPixelFogConst( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType ); + result.rgb = SRGBOutput( result.rgb ); //SRGB in pixel shader conversion + + return result; +} + +float4 main( PS_INPUT i ) : COLOR +{ + bool bCubemap = CUBEMAP ? true : false; + bool bDiffuseLighting = DIFFUSELIGHTING ? true : false; + bool bDoDiffuseWarp = LIGHTWARPTEXTURE ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bSelfIllumFresnel = SELFILLUMFRESNEL ? true : false; + bool bNormalMapAlphaEnvmapMask = NORMALMAPALPHAENVMAPMASK ? true : false; + bool bHalfLambert = HALFLAMBERT ? true : false; + bool bFlashlight = (FLASHLIGHT!=0) ? true : false; + bool bAmbientLight = AMBIENT_LIGHT ? true : false; + bool bDetailTexture = DETAILTEXTURE ? true : false; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA ? true : false; + bool bEnvmapMask = ENVMAPMASK ? true : false; + int nNumLights = NUM_LIGHTS; + +#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + float3 vWorldBinormal = cross( i.vWorldNormal.xyz, i.vWorldTangent.xyz ) * i.vWorldTangent.w; +#else + float3 vWorldBinormal = i.vWorldBinormal; +#endif + + // Unpack four light attenuations + float4 vLightAtten = float4( i.lightAtten, i.detailTexCoord_atten3.z ); + + float4 baseColor = float4( 1.0f, 1.0f, 1.0f, 1.0f ); + baseColor = tex2D( BaseTextureSampler, i.baseTexCoord2_tangentSpaceVertToEyeVectorXY.xy ); + +#if DETAILTEXTURE + float4 detailColor = tex2D( DetailSampler, i.detailTexCoord_atten3.xy ); + baseColor = TextureCombine( baseColor, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor ); +#endif + +#if ENVMAPMASK + // Blixibon - $bumpmap + $envmapmask + float3 specularFactor = 1.0f; +#else + float specularFactor = 1.0f; +#endif + +#if !DETAILTEXTURE + // Blixibon - $bumpmap transform support + float4 normalTexel = tex2D( BumpmapSampler, i.detailTexCoord_atten3.xy ); +#else + float4 normalTexel = tex2D( BumpmapSampler, i.baseTexCoord2_tangentSpaceVertToEyeVectorXY.xy ); +#endif + float3 tangentSpaceNormal = normalTexel * 2.0f - 1.0f; + + if ( bNormalMapAlphaEnvmapMask ) + { + specularFactor = normalTexel.a; + } + else if( bEnvmapMask ) + { + float4 envmapMaskTexel = tex2D( EnvmapMaskSampler, i.baseTexCoord2_tangentSpaceVertToEyeVectorXY.xy ); + specularFactor *= envmapMaskTexel.xyz; + } + + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); + + float3 worldSpaceNormal = float3( 0.0f, 0.0f, 1.0f ); + if ( bDiffuseLighting || bFlashlight || bCubemap || bSelfIllumFresnel ) + { + worldSpaceNormal = Vec3TangentToWorld( tangentSpaceNormal, i.vWorldNormal, i.vWorldTangent, vWorldBinormal ); +#if ( defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) + worldSpaceNormal = normalize( worldSpaceNormal ); +#else + worldSpaceNormal = NormalizeWithCubemap( NormalizeSampler, worldSpaceNormal ); +#endif + } + + if ( bDiffuseLighting ) + { + diffuseLighting = PixelShaderDoLighting( i.worldPos_projPosZ.xyz, worldSpaceNormal, + float3( 0.0f, 0.0f, 0.0f ), false, bAmbientLight, vLightAtten, + cAmbientCube, NormalizeSampler, nNumLights, cLightInfo, bHalfLambert, + false, 1.0f, bDoDiffuseWarp, DiffuseWarpSampler ); + } + + float3 albedo = baseColor; + if (bBlendTintByBaseAlpha) + { + float3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = lerp(tintedColor, g_DiffuseModulation.rgb, g_EnvmapTint_TintReplaceFactor.w); + albedo = lerp(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + float alpha = g_DiffuseModulation.a; + if ( !bSelfIllum && !bBlendTintByBaseAlpha ) + { + alpha *= baseColor.a; + } + + +#if FLASHLIGHT + if( bFlashlight ) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; + float2 vProjPos = float2(0, 0); +// On ps_2_b, we can do shadow mapping +#if ( FLASHLIGHTSHADOWS && (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) ) + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = FLASHLIGHTSHADOWS; + vProjPos = i.vProjPos.xy / i.vProjPos.w; // Screen-space position for shadow map noise +#endif + +#if defined ( _X360 ) + float4 flashlightSpacePosition = i.flashlightSpacePos; +#else + float4 flashlightSpacePosition = mul( float4( i.worldPos_projPosZ.xyz, 1.0f ), g_FlashlightWorldToTexture ); +#endif + + float3 flashlightColor = DoFlashlight( g_FlashlightPos, i.worldPos_projPosZ.xyz, flashlightSpacePosition, + worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, vProjPos, false, g_EnvmapContrast_ShadowTweaks ); + +#if defined ( _X360 ) + diffuseLighting += flashlightColor; +#else + diffuseLighting = flashlightColor; +#endif + + } +#endif + + + float3 diffuseComponent = albedo * diffuseLighting; + + +#if !FLASHLIGHT || defined ( _X360 ) + if ( bSelfIllum ) + { + #if ( SELFILLUMFRESNEL == 1 ) // To free up the constant register...see top of file + // This will apply a fresnel term based on the vertex normal (not the per-pixel normal!) to help fake and internal glow look + #if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))) + float3 vVertexNormal = normalize( i.vWorldNormal.xyz ); + float flSelfIllumFresnel = ( pow( saturate( dot( vVertexNormal.xyz, normalize( i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ) ) ), g_SelfIllumScaleBiasExpBrightness.z ) * g_SelfIllumScaleBiasExpBrightness.x ) + g_SelfIllumScaleBiasExpBrightness.y; + + float3 selfIllumComponent = g_SelfIllumTint * albedo * g_SelfIllumScaleBiasExpBrightness.w; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a * saturate( flSelfIllumFresnel ) ); + #else + float3 vVertexNormal = i.vWorldNormal.xyz; + float flSelfIllumFresnel = ( pow( saturate( dot( vVertexNormal.xyz, ( i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ) ) ), g_SelfIllumScaleBiasExpBrightness.z ) * g_SelfIllumScaleBiasExpBrightness.x ) + g_SelfIllumScaleBiasExpBrightness.y; + + float3 selfIllumComponent = g_SelfIllumTint * albedo * g_SelfIllumScaleBiasExpBrightness.w; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a * saturate( flSelfIllumFresnel ) ); + #endif + #else + float3 selfIllumComponent = g_SelfIllumTint * albedo; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a ); + #endif + } +#endif + + float3 specularLighting = float3( 0.0f, 0.0f, 0.0f ); +#if !FLASHLIGHT || defined ( _X360 ) + if( bCubemap ) + { + float3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, i.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz ); + + specularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint_TintReplaceFactor.rgb; + float3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast_ShadowTweaks ); + float3 greyScale = dot( specularLighting, float3( 0.299f, 0.587f, 0.114f ) ); + specularLighting = lerp( greyScale, specularLighting, g_EnvmapSaturation ); + } +#endif + + float3 result = diffuseComponent + specularLighting; + +#if defined(SHADER_MODEL_PS_2_0) + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); +#else + float fogFactor = CalcPixelFogFactorConst( g_fPixelFogType, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); +#endif + +#if defined( SHADER_MODEL_PS_2_0 ) + #if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + alpha = fogFactor; + #endif +#else // 2b or higher + alpha = lerp( alpha, fogFactor, g_fPixelFogType * g_fWriteWaterFogToDestAlpha ); // Use the fog factor if it's height fog +#endif + +#if defined( SHADER_MODEL_PS_2_0 ) + return FinalOutput( float4( result.rgb, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, false, i.worldPos_projPosZ.w ); +#else + return FinalOutputConst( float4( result.rgb, alpha ), fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, i.worldPos_projPosZ.w ); +#endif + +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_vs20.fxc new file mode 100644 index 00000000..6095be07 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_bump_vs20.fxc @@ -0,0 +1,199 @@ +//======= Copyright (c) 1996-2009, Valve Corporation, All rights reserved. ====== +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "USE_WITH_2B" "0..1" +// STATIC: "DECAL" "0..1" [vs30] +// STATIC: "FLASHLIGHT" "0..1" [XBOX] +// STATIC: "SM30_VERTEXID" "0..1" [vs30] +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "MORPHING" "0..1" [vs30] +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] + +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); // 0 & 1 +const float4 cDetailTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_4 ); // 4 & 5 +const float4x4 g_FlashlightWorldToTexture : register( SHADER_SPECIFIC_CONST_6 ); // 6, 7, 8, 9 + +#if defined( SHADER_MODEL_VS_3_0 ) && SM30_VERTEXID +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_10 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_11 ); + +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + + +//----------------------------------------------------------------------------- +// Input vertex format +//----------------------------------------------------------------------------- +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float4 vColor : COLOR0; + float3 vSpecular : COLOR1; + // make these float2's and stick the [n n 0 1] in the dot math. + float4 vTexCoord0 : TEXCOORD0; + float4 vTexCoord1 : TEXCOORD1; + float4 vTexCoord2 : TEXCOORD2; + float4 vTexCoord3 : TEXCOORD3; + float3 vTangentS : TANGENT; + float3 vTangentT : BINORMAL; + float4 vUserData : TANGENT; + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#if defined( SHADER_MODEL_VS_3_0 ) && SM30_VERTEXID + float vVertexID : POSITION2; +#endif +}; + + +//----------------------------------------------------------------------------- +// Output vertex format +//----------------------------------------------------------------------------- +struct VS_OUTPUT +{ + // Stuff that isn't seen by the pixel shader + float4 projPos : POSITION; +#if !defined( _X360 ) + float fog : FOG; +#endif + // Stuff that is seen by the pixel shader + + float4 baseTexCoord2_tangentSpaceVertToEyeVectorXY : TEXCOORD0; + float3 lightAtten : TEXCOORD1; + float4 worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ : TEXCOORD2; + float3 vWorldNormal : TEXCOORD3; // World-space normal + float4 vWorldTangent : TEXCOORD4; +#if USE_WITH_2B + float4 vProjPos : TEXCOORD5; +#else + float3 vWorldBinormal : TEXCOORD5; +#endif + float4 worldPos_projPosZ : TEXCOORD6; + float3 detailTexCoord_atten3 : TEXCOORD7; + float4 fogFactorW : COLOR1; + +#if defined( _X360 ) && FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD8; +#endif +}; + + +//----------------------------------------------------------------------------- +// Main shader entry point +//----------------------------------------------------------------------------- +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + float4 vPosition = v.vPos; + float3 vNormal; + float4 vTangent; + DecompressVertex_NormalTangent( v.vNormal, v.vUserData, vNormal, vTangent ); + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING || !SM30_VERTEXID + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal, vTangent.xyz ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, + v.vVertexID, v.vTexCoord2, vPosition.xyz, vNormal, vTangent.xyz ); +#endif + + // Perform skinning + float3 worldNormal, worldPos, worldTangentS, worldTangentT; + SkinPositionNormalAndTangentSpace( g_bSkinning, vPosition, vNormal, vTangent, + v.vBoneWeights, v.vBoneIndices, worldPos, + worldNormal, worldTangentS, worldTangentT ); + + // Always normalize since flex path is controlled by runtime + // constant not a shader combo and will always generate the normalization + worldNormal = normalize( worldNormal ); + worldTangentS = normalize( worldTangentS ); + worldTangentT = normalize( worldTangentT ); + +#if defined( SHADER_MODEL_VS_3_0 ) && MORPHING && DECAL && SM30_VERTEXID + // Avoid z precision errors + worldPos += worldNormal * 0.05f * v.vTexCoord2.z; +#endif + + o.vWorldNormal.xyz = worldNormal.xyz; + o.vWorldTangent = float4( worldTangentS.xyz, vTangent.w ); // Propagate binormal sign in world tangent.w + + // Transform into projection space + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + +#if USE_WITH_2B + o.vProjPos = vProjPos; +#else + o.vWorldBinormal.xyz = worldTangentT.xyz; +#endif + + o.fogFactorW = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW; +#endif + + // Needed for water fog alpha and diffuse lighting + // FIXME: we shouldn't have to compute this all the time. + o.worldPos_projPosZ = float4( worldPos, vProjPos.z ); + + // Needed for cubemapping + parallax mapping + // FIXME: We shouldn't have to compute this all the time. + //o.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz = VSHADER_VECT_SCALE * (cEyePos - worldPos); + o.worldVertToEyeVectorXYZ_tangentSpaceVertToEyeVectorZ.xyz = normalize( cEyePos.xyz - worldPos.xyz ); + +#if defined( SHADER_MODEL_VS_2_0 ) && ( !USE_STATIC_CONTROL_FLOW ) + o.lightAtten.xyz = float3(0,0,0); + o.detailTexCoord_atten3.z = 0.0f; + #if ( NUM_LIGHTS > 0 ) + o.lightAtten.x = GetVertexAttenForLight( worldPos, 0, false ); + #endif + #if ( NUM_LIGHTS > 1 ) + o.lightAtten.y = GetVertexAttenForLight( worldPos, 1, false ); + #endif + #if ( NUM_LIGHTS > 2 ) + o.lightAtten.z = GetVertexAttenForLight( worldPos, 2, false ); + #endif + #if ( NUM_LIGHTS > 3 ) + o.detailTexCoord_atten3.z = GetVertexAttenForLight( worldPos, 3, false ); + #endif +#else + // Scalar light attenuation + o.lightAtten.x = GetVertexAttenForLight( worldPos, 0, true ); + o.lightAtten.y = GetVertexAttenForLight( worldPos, 1, true ); + o.lightAtten.z = GetVertexAttenForLight( worldPos, 2, true ); + o.detailTexCoord_atten3.z = GetVertexAttenForLight( worldPos, 3, true ); +#endif + + // Base texture coordinate transform + o.baseTexCoord2_tangentSpaceVertToEyeVectorXY.x = dot( v.vTexCoord0, cBaseTexCoordTransform[0] ); + o.baseTexCoord2_tangentSpaceVertToEyeVectorXY.y = dot( v.vTexCoord0, cBaseTexCoordTransform[1] ); + + // Detail texture coordinate transform + o.detailTexCoord_atten3.x = dot( v.vTexCoord0, cDetailTexCoordTransform[0] ); + o.detailTexCoord_atten3.y = dot( v.vTexCoord0, cDetailTexCoordTransform[1] ); + +#if defined( _X360 ) && FLASHLIGHT + o.flashlightSpacePos = mul( float4( worldPos, 1.0f ), g_FlashlightWorldToTexture ); +#endif + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps20b.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps20b.fxc new file mode 100644 index 00000000..e78ed5d8 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps20b.fxc @@ -0,0 +1,503 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. =======// +// +//=============================================================================// + + +//#define NEW_SHADOW_FILTERS + +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "SELFILLUM_ENVMAPMASK_ALPHA" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..9" +// STATIC: "SEAMLESS_BASE" "0..1" +// STATIC: "SEAMLESS_DETAIL" "0..1" +// STATIC: "DISTANCEALPHA" "0..1" +// STATIC: "DISTANCEALPHAFROMDETAIL" "0..1" +// STATIC: "SOFT_MASK" "0..1" +// STATIC: "OUTLINE" "0..1" +// STATIC: "OUTER_GLOW" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] +// STATIC: "DEPTHBLEND" "0..1" [ps20b] [ps30] +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "SRGB_INPUT_ADAPTER" "0..1" [ps20b] +// STATIC: "CUBEMAP_SPHERE_LEGACY" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" [ps20] +// DYNAMIC: "LIGHTING_PREVIEW" "0..2" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] + +// detail blend mode 6 = ps20b only +// SKIP: $DETAIL_BLEND_MODE == 6 [ps20] + +// SKIP: ($DETAILTEXTURE == 0 ) && ( $DETAIL_BLEND_MODE != 0 ) +// SKIP: ($DETAILTEXTURE == 0 ) && ( $SEAMLESS_DETAIL ) +// SKIP: ($ENVMAPMASK || $SELFILLUM_ENVMAPMASK_ALPHA) && ($SEAMLESS_BASE || $SEAMLESS_DETAIL) +// SKIP: $BASEALPHAENVMAPMASK && $ENVMAPMASK +// SKIP: $BASEALPHAENVMAPMASK && $SELFILLUM +// SKIP: $SELFILLUM && $SELFILLUM_ENVMAPMASK_ALPHA +// SKIP: $SELFILLUM_ENVMAPMASK_ALPHA && (! $ENVMAPMASK) +// SKIP: $ENVMAPMASK && ($FLASHLIGHT || $FLASHLIGHTSHADOWS) [PC] +// SKIP: $BASEALPHAENVMAPMASK && ($SEAMLESS_BASE || $SEAMLESS_DETAIL) +// SKIP: ($DISTANCEALPHA == 0) && ($DISTANCEALPHAFROMDETAIL || $SOFT_MASK || $OUTLINE || $OUTER_GLOW) +// SKIP: ($DETAILTEXTURE == 0) && ($DISTANCEALPHAFROMDETAIL) + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps30] + +// Flashlight shadow filter mode is irrelevant if there is no flashlight +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] + +// DISTANCEALPHA-related skips +// SKIP: ($DISTANCEALPHA) && ($ENVMAPMASK || $BASEALPHAENVMAPMASK || $SELFILLUM || $SELFILLUM_ENVMAPMASK_ALPHA ) +// SKIP: ($DISTANCEALPHA) && ($SEAMLESS_BASE || $SEAMLESS_DETAIL || $CUBEMAP || $LIGHTING_PREVIEW ) +// SKIP: ($DISTANCEALPHA) && ($WRITEWATERFOGTODESTALPHA || $PIXELFOGTYPE || $FLASHLIGHT || $FLASHLIGHTSHADOWS || $SRGB_INPUT_ADAPTER ) + +// SKIP: $SEAMLESS_BASE && $SRGB_INPUT_ADAPTER +// SKIP: $SEAMLESS_BASE && ($BLENDTINTBYBASEALPHA ) + +// BlendTintByBaseAlpha is incompatible with other interpretations of alpha +// SKIP: ($BLENDTINTBYBASEALPHA) && ($SELFILLUM || (($DISTANCEALPHA) && ($DISTANCEALPHAFROMDETAIL == 0)) || $BASEALPHAENVMAPMASK) + +// Only _XBOX allows flashlight and cubemap in the current implementation +// SKIP: $FLASHLIGHT && $CUBEMAP [PC] + +// SKIP: $CUBEMAP_SPHERE_LEGACY && ($CUBEMAP == 0) + +#include "common_flashlight_fxc.h" +#include "common_vertexlitgeneric_dx9.h" + +const float4 g_EnvmapTint_TintReplaceFactor : register( c0 ); +const float4 g_DiffuseModulation : register( c1 ); +const float4 g_EnvmapContrast_ShadowTweaks : register( c2 ); +const float4 g_EnvmapSaturation_SelfIllumMask : register( c3 ); +const float4 g_SelfIllumTint_and_BlendFactor : register( c4 ); + +const float4 g_ShaderControls : register( c12 ); +const float4 g_DepthFeatheringConstants : register( c13 ); + +const float4 g_EyePos_MinLight : register( c20 ); +#define g_EyePos g_EyePos_MinLight.xyz +#define g_fMinLighting g_EyePos_MinLight.w + +const float4 g_FogParams : register( c21 ); + +#define g_SelfIllumTint g_SelfIllumTint_and_BlendFactor.xyz +#define g_DetailBlendFactor g_SelfIllumTint_and_BlendFactor.w +#define g_EnvmapSaturation g_EnvmapSaturation_SelfIllumMask.xyz +#define g_SelfIllumMaskControl g_EnvmapSaturation_SelfIllumMask.w + +const float4 g_FlashlightAttenuationFactors : register( c22 ); +const HALF3 g_FlashlightPos : register( c23 ); +const float4x4 g_FlashlightWorldToTexture : register( c24 ); // through c27 + + +sampler BaseTextureSampler : register( s0 ); +sampler EnvmapSampler : register( s1 ); +sampler DetailSampler : register( s2 ); +sampler EnvmapMaskSampler : register( s4 ); +sampler RandRotSampler : register( s6 ); // RandomRotation sampler +sampler FlashlightSampler : register( s7 ); +sampler ShadowDepthSampler : register( s8 ); // Flashlight shadow depth map sampler +sampler DepthSampler : register( s10 ); //depth buffer sampler for depth blending +sampler SelfIllumMaskSampler : register( s11 ); // selfillummask + +struct PS_INPUT +{ +#if SEAMLESS_BASE + HALF3 baseTexCoord : TEXCOORD0; // Base texture coordinate +#else + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate +#endif +#if SEAMLESS_DETAIL + HALF3 detailTexCoord : TEXCOORD1; // Seamless texture coordinate +#else + HALF2 detailTexCoord : TEXCOORD1; // Detail texture coordinate +#endif + float4 color : TEXCOORD2; // Vertex color (from lighting or unlit) + float3 worldVertToEyeVector : TEXCOORD3; // Necessary for reflection + float3 worldSpaceNormal : TEXCOORD4; // Necessary for cubemaps and flashlight + +#if defined ( _X360 ) +#if FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD5; +#endif +#endif + + float4 projPos : TEXCOORD6; + float4 worldPos_projPosZ : TEXCOORD7; + float4 fogFactorW : COLOR1; +#if SEAMLESS_BASE || SEAMLESS_DETAIL + float3 SeamlessWeights : COLOR0; // x y z projection weights +#endif +}; + +const float4 g_GlowParameters : register( c5 ); +const float4 g_GlowColor : register( c6 ); +#define GLOW_UV_OFFSET g_GlowParameters.xy +#define OUTER_GLOW_MIN_DVALUE g_GlowParameters.z +#define OUTER_GLOW_MAX_DVALUE g_GlowParameters.w +#define OUTER_GLOW_COLOR g_GlowColor + +#define g_fPixelFogType g_ShaderControls.x +#define g_fWriteDepthToAlpha g_ShaderControls.y +#define g_fWriteWaterFogToDestAlpha g_ShaderControls.z +#define g_fVertexAlpha g_ShaderControls.w + + +const float4 g_DistanceAlphaParams : register( c7 ); +#define SOFT_MASK_MAX g_DistanceAlphaParams.x +#define SOFT_MASK_MIN g_DistanceAlphaParams.y + +const float4 g_OutlineColor : register( c8 ); +#define OUTLINE_COLOR g_OutlineColor + +const float4 g_OutlineParams : register( c9 ); +// these are ordered this way for optimal ps20 swizzling +#define OUTLINE_MIN_VALUE0 g_OutlineParams.x +#define OUTLINE_MAX_VALUE1 g_OutlineParams.y +#define OUTLINE_MAX_VALUE0 g_OutlineParams.z +#define OUTLINE_MIN_VALUE1 g_OutlineParams.w + +#if DETAILTEXTURE +const float3 g_DetailTint : register( c10 ); +#endif + + +// Calculate unified fog +float CalcPixelFogFactorConst( float fPixelFogType, const float4 fogParams, const float flEyePosZ, const float flWorldPosZ, const float flProjPosZ ) +{ + float flDepthBelowWater = fPixelFogType*fogParams.y - flWorldPosZ; // above water = negative, below water = positive + float flDepthBelowEye = fPixelFogType*flEyePosZ - flWorldPosZ; // above eye = negative, below eye = positive + // if fPixelFogType == 0, then flDepthBelowWater == flDepthBelowEye and frac will be 1 + float frac = (flDepthBelowEye == 0) ? 1 : saturate(flDepthBelowWater/flDepthBelowEye); + return saturate( min(fogParams.z, flProjPosZ * fogParams.w * frac - fogParams.x) ); +} + +// Blend both types of Fog and lerp to get result +float3 BlendPixelFogConst( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, float fPixelFogType ) +{ + //float3 fRangeResult = lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog + //float3 fHeightResult = lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); + //return lerp( fRangeResult, fHeightResult, fPixelFogType ); + pixelFogFactor = lerp( pixelFogFactor*pixelFogFactor, pixelFogFactor, fPixelFogType ); + return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor ); +} + + +float4 FinalOutputConst( const float4 vShaderColor, float pixelFogFactor, float fPixelFogType, const int iTONEMAP_SCALE_TYPE, float fWriteDepthToDestAlpha, const float flProjZ ) +{ + float4 result = vShaderColor; + if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR ) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA ) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } + + result.a = lerp( result.a, DepthToDestAlpha( flProjZ ), fWriteDepthToDestAlpha ); + + result.rgb = BlendPixelFogConst( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType ); + result.rgb = SRGBOutput( result.rgb ); //SRGB in pixel shader conversion + + return result; +} + + +#if LIGHTING_PREVIEW == 2 +LPREVIEW_PS_OUT main( PS_INPUT i ) : COLOR +#else +float4 main( PS_INPUT i ) : COLOR +#endif +{ + bool bDetailTexture = DETAILTEXTURE ? true : false; + bool bCubemap = CUBEMAP ? true : false; + bool bDiffuseLighting = DIFFUSELIGHTING ? true : false; + bool bHasNormal = bCubemap || bDiffuseLighting; + bool bEnvmapMask = ENVMAPMASK ? true : false; + bool bBaseAlphaEnvmapMask = BASEALPHAENVMAPMASK ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bVertexColor = VERTEXCOLOR ? true : false; + bool bFlashlight = FLASHLIGHT ? true : false; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA ? true : false; + + HALF4 baseColor = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); +#if SEAMLESS_BASE + baseColor = + i.SeamlessWeights.x * tex2D( BaseTextureSampler, i.baseTexCoord.yz )+ + i.SeamlessWeights.y * tex2D( BaseTextureSampler, i.baseTexCoord.zx )+ + i.SeamlessWeights.z * tex2D( BaseTextureSampler, i.baseTexCoord.xy ); +#else + baseColor = tex2D( BaseTextureSampler, i.baseTexCoord.xy ); + +#if SRGB_INPUT_ADAPTER + baseColor.rgb = GammaToLinear( baseColor.rgb ); +#endif + +#endif // !SEAMLESS_BASE + + +#if DISTANCEALPHA && (DISTANCEALPHAFROMDETAIL == 0) + float distAlphaMask = baseColor.a; +#endif + + +#if DETAILTEXTURE +#if SEAMLESS_DETAIL + float4 detailColor = + i.SeamlessWeights.x * tex2D( DetailSampler, i.detailTexCoord.yz )+ + i.SeamlessWeights.y * tex2D( DetailSampler, i.detailTexCoord.zx )+ + i.SeamlessWeights.z * tex2D( DetailSampler, i.detailTexCoord.xy ); +#else + float4 detailColor = tex2D( DetailSampler, i.detailTexCoord.xy ); +#endif + detailColor.rgb *= g_DetailTint; + +#if DISTANCEALPHA && (DISTANCEALPHAFROMDETAIL == 1) + float distAlphaMask = detailColor.a; + detailColor.a = 1.0; // make tcombine treat as 1.0 +#endif + baseColor = + TextureCombine( baseColor, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor ); +#endif + +#if DISTANCEALPHA + // now, do all distance alpha effects + //if ( OUTLINE && ( distAlphaMask >= OUTLINE_MIN_VALUE0 ) && ( distAlphaMask <= OUTLINE_MAX_VALUE1 ) ) + //{ + // float oFactor=1.0; + // if ( distAlphaMask <= OUTLINE_MIN_VALUE1 ) + // { + // oFactor=smoothstep( OUTLINE_MIN_VALUE0, OUTLINE_MIN_VALUE1, distAlphaMask ); + // } + // else + // { + // oFactor=smoothstep( OUTLINE_MAX_VALUE1, OUTLINE_MAX_VALUE0, distAlphaMask ); + // } + // baseColor = lerp( baseColor, OUTLINE_COLOR, oFactor ); + //} + if ( OUTLINE ) + { + float4 oFactors = smoothstep(g_OutlineParams.xyzw, g_OutlineParams.wzyx, distAlphaMask ); + baseColor = lerp( baseColor, g_OutlineColor, oFactors.x * oFactors.y ); + } + + float mskUsed; + if ( SOFT_MASK ) + { + mskUsed = smoothstep( SOFT_MASK_MIN, SOFT_MASK_MAX, distAlphaMask ); + baseColor.a *= mskUsed; + } + else + { + mskUsed = distAlphaMask >= 0.5; + if (DETAILTEXTURE ) + baseColor.a *= mskUsed; + else + baseColor.a = mskUsed; + } + + + if ( OUTER_GLOW ) + { +#if DISTANCEALPHAFROMDETAIL + float4 glowTexel = tex2D( DetailSampler, i.detailTexCoord.xy+GLOW_UV_OFFSET ); +#else + float4 glowTexel = tex2D( BaseTextureSampler, i.baseTexCoord.xy+GLOW_UV_OFFSET ); +#endif + float4 glowc = OUTER_GLOW_COLOR*smoothstep( OUTER_GLOW_MIN_DVALUE, OUTER_GLOW_MAX_DVALUE, glowTexel.a ); + baseColor = lerp( glowc, baseColor, mskUsed ); + } + +#endif // DISTANCEALPHA + + float3 specularFactor = 1.0f; + float4 envmapMaskTexel; + if( bEnvmapMask ) + { +#if !DETAILTEXTURE + // Blixibon - $envmapmask transform support + envmapMaskTexel = tex2D( EnvmapMaskSampler, i.detailTexCoord.xy ); +#else + envmapMaskTexel = tex2D( EnvmapMaskSampler, i.baseTexCoord.xy ); +#endif + specularFactor *= envmapMaskTexel.xyz; + } + + if( bBaseAlphaEnvmapMask ) + { + specularFactor *= 1.0 - baseColor.a; // this blows! + } + + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); + if( bDiffuseLighting || bVertexColor && !( bVertexColor && bDiffuseLighting ) ) + { + diffuseLighting = i.color.rgb; + } + + float3 albedo = baseColor; + if (bBlendTintByBaseAlpha) + { + float3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = lerp(tintedColor, g_DiffuseModulation.rgb, g_EnvmapTint_TintReplaceFactor.w); + albedo = lerp(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + float alpha = g_DiffuseModulation.a; + if ( !bBaseAlphaEnvmapMask && !bSelfIllum && !bBlendTintByBaseAlpha ) + { + alpha *= baseColor.a; + } + + + if( bFlashlight ) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; +// On ps_2_b, we can do shadow mapping +#if ( FLASHLIGHTSHADOWS && (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) ) + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = true; +#endif + +#if defined ( _X360 ) + float4 flashlightSpacePosition = i.flashlightSpacePos; +#else + float4 flashlightSpacePosition = mul( float4( i.worldPos_projPosZ.xyz, 1.0f ), g_FlashlightWorldToTexture ); +#endif + + // We want the N.L to happen on the flashlight pass, but can't afford it on ps20 + bool bUseWorldNormal = true; +#if ( defined( SHADER_MODEL_PS_2_0 ) && ( DETAILTEXTURE ) ) + bUseWorldNormal = false; +#endif + float3 flashlightColor = DoFlashlight( g_FlashlightPos, i.worldPos_projPosZ.xyz, flashlightSpacePosition, + i.worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, i.projPos.xy / i.projPos.w, false, g_EnvmapContrast_ShadowTweaks, bUseWorldNormal ); + +#if defined ( _X360 ) + diffuseLighting += flashlightColor; +#else + diffuseLighting = flashlightColor; +#endif + } + + if( bVertexColor && bDiffuseLighting ) + { + albedo *= i.color.rgb; + } + + alpha = lerp( alpha, alpha * i.color.a, g_fVertexAlpha ); + +#if FOGTYPE == 2 || FLASHLIGHT != 0 + float3 diffuseComponent = albedo * diffuseLighting; +#else + float3 vEyeDir = normalize( i.worldVertToEyeVector.xyz ); + float flFresnelMinlight = saturate( dot( i.worldSpaceNormal, vEyeDir ) ); + + float3 diffuseComponent = albedo * lerp( diffuseLighting, 1, g_fMinLighting * flFresnelMinlight ); +#endif + +#if DETAILTEXTURE + diffuseComponent = + TextureCombinePostLighting( diffuseComponent, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor ); +#endif + + HALF3 specularLighting = HALF3( 0.0f, 0.0f, 0.0f ); + +#if !FLASHLIGHT || defined ( _X360 ) + #if SELFILLUM_ENVMAPMASK_ALPHA + // range of alpha: + // 0 - 0.125 = lerp(diffuse,selfillum,alpha*8) + // 0.125-1.0 = selfillum*(1+alpha-0.125)*8 (over bright glows) + HALF3 selfIllumComponent = g_SelfIllumTint * albedo; + half Adj_Alpha=8*envmapMaskTexel.a; + diffuseComponent=( max( 0, 1-Adj_Alpha ) * diffuseComponent) + Adj_Alpha * selfIllumComponent; + #else + if ( bSelfIllum ) + { + float3 vSelfIllumMask = tex2D( SelfIllumMaskSampler, i.baseTexCoord.xy ); + vSelfIllumMask = lerp( baseColor.aaa, vSelfIllumMask, g_SelfIllumMaskControl ); + diffuseComponent = lerp( diffuseComponent, g_SelfIllumTint * albedo, vSelfIllumMask ); + } + #endif + + if( bCubemap ) + { +#if CUBEMAP_SPHERE_LEGACY + HALF3 reflectVect = normalize(CalcReflectionVectorUnnormalized( i.worldSpaceNormal, i.worldVertToEyeVector.xyz )); + + specularLighting = 0.5 * tex2D( EnvmapSampler, float2(reflectVect.x, reflectVect.y) ) * g_DiffuseModulation.rgb * diffuseLighting; +#else + HALF3 reflectVect = CalcReflectionVectorUnnormalized( i.worldSpaceNormal, i.worldVertToEyeVector.xyz ); + + specularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint_TintReplaceFactor.rgb; + HALF3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast_ShadowTweaks ); + HALF3 greyScale = dot( specularLighting, HALF3( 0.299f, 0.587f, 0.114f ) ); + specularLighting = lerp( greyScale, specularLighting, g_EnvmapSaturation ); +#endif + } +#endif + + HALF3 result = diffuseComponent + specularLighting; + +#if LIGHTING_PREVIEW +# if LIGHTING_PREVIEW == 1 + float dotprod=0.7+0.25*dot(i.worldSpaceNormal,normalize(float3(1,2,-.5))); + return FinalOutput( float4( dotprod*albedo.xyz, alpha ), 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR ); +# else + LPREVIEW_PS_OUT ret; + ret.flags=float4(1,1,1,1); + ret.color=float4( albedo.xyz, alpha ); + ret.normal=float4(i.worldSpaceNormal,alpha); + ret.position=float4(i.worldPos_projPosZ.xyz, alpha); + return FinalOutput( ret, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +# endif +#else + +# if (DEPTHBLEND == 1) + { + float2 vScreenPos; + vScreenPos.x = i.projPos.x; + vScreenPos.y = -i.projPos.y; + vScreenPos = (vScreenPos + i.projPos.w) * 0.5f; + alpha *= DepthFeathering( DepthSampler, vScreenPos / i.projPos.w, i.projPos.w - i.projPos.z, i.projPos.w, g_DepthFeatheringConstants ); + } +# endif + +#if defined( SHADER_MODEL_PS_2_0 ) + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.projPos.z ); + #if (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + alpha = lerp( alpha, fogFactor, g_fWriteWaterFogToDestAlpha ); + #endif + return FinalOutput( float4( result.rgb, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, false, i.projPos.z ); +#else // 2b or higher + float fogFactor = CalcPixelFogFactor( g_fPixelFogType, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.projPos.z ); + alpha = lerp( alpha, fogFactor, g_fWriteWaterFogToDestAlpha ); // Use the fog factor if it's height fog + return FinalOutput( float4( result.rgb, alpha ), fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, i.projPos.z ); +#endif + +#endif +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps2x.fxc new file mode 100644 index 00000000..5f6e549c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps2x.fxc @@ -0,0 +1,511 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. =======// +// +//=============================================================================// +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "SELFILLUM_ENVMAPMASK_ALPHA" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..9" +// STATIC: "SEAMLESS_BASE" "0..1" +// STATIC: "SEAMLESS_DETAIL" "0..1" +// STATIC: "DISTANCEALPHA" "0..1" +// STATIC: "DISTANCEALPHAFROMDETAIL" "0..1" +// STATIC: "SOFT_MASK" "0..1" +// STATIC: "OUTLINE" "0..1" +// STATIC: "OUTER_GLOW" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] +// STATIC: "DEPTHBLEND" "0..1" [ps20b] [ps30] +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "SRGB_INPUT_ADAPTER" "0..1" [ps20b] +// STATIC: "CUBEMAP_SPHERE_LEGACY" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" [ps20] +// DYNAMIC: "LIGHTING_PREVIEW" "0..2" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps30] +// DYNAMIC: "STATIC_LIGHT_LIGHTMAP" "0..1" [ps20b] [ps30] +// DYNAMIC: "STATIC_LIGHT_LIGHTMAP" "0..0" [ps20] +// DYNAMIC: "DEBUG_LUXELS" "0..1" [ps20b] [ps30] + +// detail blend mode 6 = ps20b only +// SKIP: $DETAIL_BLEND_MODE == 6 [ps20] + +// SKIP: ($DETAILTEXTURE == 0 ) && ( $DETAIL_BLEND_MODE != 0 ) +// SKIP: ($DETAILTEXTURE == 0 ) && ( $SEAMLESS_DETAIL ) +// SKIP: ($ENVMAPMASK || $SELFILLUM_ENVMAPMASK_ALPHA) && ($SEAMLESS_BASE || $SEAMLESS_DETAIL) +// SKIP: $BASEALPHAENVMAPMASK && $ENVMAPMASK +// SKIP: $BASEALPHAENVMAPMASK && $SELFILLUM +// SKIP: $SELFILLUM && $SELFILLUM_ENVMAPMASK_ALPHA +// SKIP: $SELFILLUM_ENVMAPMASK_ALPHA && (! $ENVMAPMASK) +// SKIP: $ENVMAPMASK && ($FLASHLIGHT || $FLASHLIGHTSHADOWS) [PC] +// SKIP: $BASEALPHAENVMAPMASK && ($SEAMLESS_BASE || $SEAMLESS_DETAIL) +// SKIP: ($DISTANCEALPHA == 0) && ($DISTANCEALPHAFROMDETAIL || $SOFT_MASK || $OUTLINE || $OUTER_GLOW) +// SKIP: ($DETAILTEXTURE == 0) && ($DISTANCEALPHAFROMDETAIL) + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps30] + +// Flashlight shadow filter mode is irrelevant if there is no flashlight +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps20b] +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] + +// DISTANCEALPHA-related skips +// SKIP: ($DISTANCEALPHA) && ($ENVMAPMASK || $BASEALPHAENVMAPMASK || $SELFILLUM || $SELFILLUM_ENVMAPMASK_ALPHA ) +// SKIP: ($DISTANCEALPHA) && ($SEAMLESS_BASE || $SEAMLESS_DETAIL || $CUBEMAP || $LIGHTING_PREVIEW ) +// SKIP: ($DISTANCEALPHA) && ($WRITEWATERFOGTODESTALPHA || $PIXELFOGTYPE || $FLASHLIGHT || $FLASHLIGHTSHADOWS || $SRGB_INPUT_ADAPTER ) + +// SKIP: $SEAMLESS_BASE && $SRGB_INPUT_ADAPTER +// SKIP: $SEAMLESS_BASE && ($BLENDTINTBYBASEALPHA ) + +// BlendTintByBaseAlpha is incompatible with other interpretations of alpha +// SKIP: ($BLENDTINTBYBASEALPHA) && ($SELFILLUM || (($DISTANCEALPHA) && ($DISTANCEALPHAFROMDETAIL == 0)) || $BASEALPHAENVMAPMASK) + +// Only _XBOX allows flashlight and cubemap in the current implementation +// SKIP: $FLASHLIGHT && $CUBEMAP [PC] + +// SKIP: $CUBEMAP_SPHERE_LEGACY && ($CUBEMAP == 0) + +// Debugging luxels only makes sense if we have lightmaps on this geometry. +// SKIP: ($STATIC_LIGHT_LIGHTMAP == 0) && ($DEBUG_LUXELS == 1) + +#include "common_flashlight_fxc.h" +#include "common_vertexlitgeneric_dx9.h" + +const float4 g_EnvmapTint_TintReplaceFactor : register( c0 ); +const float4 g_DiffuseModulation : register( c1 ); +const float4 g_EnvmapContrast_ShadowTweaks : register( c2 ); +const float4 g_EnvmapSaturation_SelfIllumMask : register( c3 ); +const float4 g_SelfIllumTint_and_BlendFactor : register( c4 ); + +const float4 g_ShaderControls : register( c12 ); +const float4 g_DepthFeatheringConstants : register( c13 ); + +const float4 g_EyePos : register( c20 ); +const float4 g_FogParams : register( c21 ); + +#define g_SelfIllumTint g_SelfIllumTint_and_BlendFactor.xyz +#define g_DetailBlendFactor g_SelfIllumTint_and_BlendFactor.w +#define g_EnvmapSaturation g_EnvmapSaturation_SelfIllumMask.xyz +#define g_SelfIllumMaskControl g_EnvmapSaturation_SelfIllumMask.w + +const float4 g_FlashlightAttenuationFactors : register( c22 ); +const HALF3 g_FlashlightPos : register( c23 ); +const float4x4 g_FlashlightWorldToTexture : register( c24 ); // through c27 + + +sampler BaseTextureSampler : register( s0 ); +sampler EnvmapSampler : register( s1 ); +sampler DetailSampler : register( s2 ); +sampler EnvmapMaskSampler : register( s4 ); +sampler RandRotSampler : register( s6 ); // RandomRotation sampler +sampler FlashlightSampler : register( s7 ); +sampler ShadowDepthSampler : register( s8 ); // Flashlight shadow depth map sampler +sampler DepthSampler : register( s10 ); //depth buffer sampler for depth blending +sampler SelfIllumMaskSampler : register( s11 ); // selfillummask +sampler LightMapSampler : register( s12 ); + +struct PS_INPUT +{ +#if SEAMLESS_BASE + HALF3 baseTexCoord : TEXCOORD0; // Base texture coordinate +#else + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate +#endif +#if SEAMLESS_DETAIL + HALF3 detailTexCoord : TEXCOORD1; // Seamless texture coordinate +#else + HALF2 detailTexCoord : TEXCOORD1; // Detail texture coordinate +#endif + float4 color : TEXCOORD2; // Vertex color (from lighting or unlit) + float3 worldVertToEyeVector : TEXCOORD3; // Necessary for reflection + float3 worldSpaceNormal : TEXCOORD4; // Necessary for cubemaps and flashlight + +#if defined ( _X360 ) +#if FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD5; +#endif +#endif + + float4 projPos : TEXCOORD6; + float4 worldPos_projPosZ : TEXCOORD7; + float4 fogFactorW : COLOR1; +#if SEAMLESS_BASE || SEAMLESS_DETAIL + float3 SeamlessWeights : COLOR0; // x y z projection weights +#endif +}; + +const float4 g_GlowParameters : register( c5 ); +const float4 g_GlowColor : register( c6 ); +#define GLOW_UV_OFFSET g_GlowParameters.xy +#define OUTER_GLOW_MIN_DVALUE g_GlowParameters.z +#define OUTER_GLOW_MAX_DVALUE g_GlowParameters.w +#define OUTER_GLOW_COLOR g_GlowColor + +#define g_fPixelFogType g_ShaderControls.x +#define g_fWriteDepthToAlpha g_ShaderControls.y +#define g_fWriteWaterFogToDestAlpha g_ShaderControls.z +#define g_fVertexAlpha g_ShaderControls.w + + +const float4 g_DistanceAlphaParams : register( c7 ); +#define SOFT_MASK_MAX g_DistanceAlphaParams.x +#define SOFT_MASK_MIN g_DistanceAlphaParams.y + +const float4 g_OutlineColor : register( c8 ); +#define OUTLINE_COLOR g_OutlineColor + +const float4 g_OutlineParams : register( c9 ); +// these are ordered this way for optimal ps20 swizzling +#define OUTLINE_MIN_VALUE0 g_OutlineParams.x +#define OUTLINE_MAX_VALUE1 g_OutlineParams.y +#define OUTLINE_MAX_VALUE0 g_OutlineParams.z +#define OUTLINE_MIN_VALUE1 g_OutlineParams.w + +#if DETAILTEXTURE +const float3 g_DetailTint : register( c10 ); +#endif + +#if DEBUG_LUXELS +const float4 g_LuxelScale : register( c11 ); +#endif + + +// Calculate unified fog +float CalcPixelFogFactorConst( float fPixelFogType, const float4 fogParams, const float3 vEyePos, const float3 vWorldPos, const float flProjPosZ ) +{ + /*float flDepthBelowWater = fPixelFogType*fogParams.y - flWorldPosZ; // above water = negative, below water = positive + float flDepthBelowEye = fPixelFogType*flEyePosZ - flWorldPosZ; // above eye = negative, below eye = positive + // if fPixelFogType == 0, then flDepthBelowWater == flDepthBelowEye and frac will be 1 + float frac = (flDepthBelowEye == 0) ? 1 : saturate(flDepthBelowWater/flDepthBelowEye); + return saturate( min(fogParams.z, flProjPosZ * fogParams.w * frac - fogParams.x) );*/ + + float fRangeFog = CalcRangeFogFactorNonFixedFunction( vWorldPos, vEyePos, fogParams.z, fogParams.x, fogParams.w ); + float fHeightFog = CalcWaterFogAlpha( fogParams.y, vEyePos.z, vWorldPos.z, flProjPosZ, fogParams.w ); + return lerp( fRangeFog, fHeightFog, fPixelFogType ); +} + +// Blend both types of Fog and lerp to get result +float3 BlendPixelFogConst( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, float fPixelFogType ) +{ + //float3 fRangeResult = lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog + //float3 fHeightResult = lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); + //return lerp( fRangeResult, fHeightResult, fPixelFogType ); + pixelFogFactor = lerp( pixelFogFactor*pixelFogFactor, pixelFogFactor, fPixelFogType ); + return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor ); +} + + +float4 FinalOutputConst( const float4 vShaderColor, float pixelFogFactor, float fPixelFogType, const int iTONEMAP_SCALE_TYPE, float fWriteDepthToDestAlpha, const float flProjZ ) +{ + float4 result = vShaderColor; + if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR ) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA ) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } + + result.a = lerp( result.a, DepthToDestAlpha( flProjZ ), fWriteDepthToDestAlpha ); + + result.rgb = BlendPixelFogConst( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType ); + result.rgb = SRGBOutput( result.rgb ); //SRGB in pixel shader conversion + + return result; +} + + +#if LIGHTING_PREVIEW == 2 +LPREVIEW_PS_OUT main( PS_INPUT i ) : COLOR +#else +float4 main( PS_INPUT i ) : COLOR +#endif +{ + bool bDetailTexture = DETAILTEXTURE ? true : false; + bool bCubemap = CUBEMAP ? true : false; + bool bDiffuseLighting = DIFFUSELIGHTING ? true : false; + bool bHasNormal = bCubemap || bDiffuseLighting; + bool bEnvmapMask = ENVMAPMASK ? true : false; + bool bBaseAlphaEnvmapMask = BASEALPHAENVMAPMASK ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bVertexColor = VERTEXCOLOR ? true : false; + bool bFlashlight = FLASHLIGHT ? true : false; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA ? true : false; + + HALF4 baseColor = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); +#if SEAMLESS_BASE + baseColor = + i.SeamlessWeights.x * tex2D( BaseTextureSampler, i.baseTexCoord.yz )+ + i.SeamlessWeights.y * tex2D( BaseTextureSampler, i.baseTexCoord.zx )+ + i.SeamlessWeights.z * tex2D( BaseTextureSampler, i.baseTexCoord.xy ); +#else + baseColor = tex2D( BaseTextureSampler, i.baseTexCoord.xy ); + +#if SRGB_INPUT_ADAPTER + baseColor.rgb = GammaToLinear( baseColor.rgb ); +#endif + +#endif // !SEAMLESS_BASE + + +#if DISTANCEALPHA && (DISTANCEALPHAFROMDETAIL == 0) + float distAlphaMask = baseColor.a; +#endif + + +#if DETAILTEXTURE +#if SEAMLESS_DETAIL + float4 detailColor = + i.SeamlessWeights.x * tex2D( DetailSampler, i.detailTexCoord.yz )+ + i.SeamlessWeights.y * tex2D( DetailSampler, i.detailTexCoord.zx )+ + i.SeamlessWeights.z * tex2D( DetailSampler, i.detailTexCoord.xy ); +#else + float4 detailColor = tex2D( DetailSampler, i.detailTexCoord.xy ); +#endif + detailColor.rgb *= g_DetailTint; + +#if DISTANCEALPHA && (DISTANCEALPHAFROMDETAIL == 1) + float distAlphaMask = detailColor.a; + detailColor.a = 1.0; // make tcombine treat as 1.0 +#endif + baseColor = + TextureCombine( baseColor, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor ); +#endif + +#if DISTANCEALPHA + // now, do all distance alpha effects + //if ( OUTLINE && ( distAlphaMask >= OUTLINE_MIN_VALUE0 ) && ( distAlphaMask <= OUTLINE_MAX_VALUE1 ) ) + //{ + // float oFactor=1.0; + // if ( distAlphaMask <= OUTLINE_MIN_VALUE1 ) + // { + // oFactor=smoothstep( OUTLINE_MIN_VALUE0, OUTLINE_MIN_VALUE1, distAlphaMask ); + // } + // else + // { + // oFactor=smoothstep( OUTLINE_MAX_VALUE1, OUTLINE_MAX_VALUE0, distAlphaMask ); + // } + // baseColor = lerp( baseColor, OUTLINE_COLOR, oFactor ); + //} + if ( OUTLINE ) + { + float4 oFactors = smoothstep(g_OutlineParams.xyzw, g_OutlineParams.wzyx, distAlphaMask ); + baseColor = lerp( baseColor, g_OutlineColor, oFactors.x * oFactors.y ); + } + + float mskUsed; + if ( SOFT_MASK ) + { + mskUsed = smoothstep( SOFT_MASK_MIN, SOFT_MASK_MAX, distAlphaMask ); + baseColor.a *= mskUsed; + } + else + { + mskUsed = distAlphaMask >= 0.5; + if (DETAILTEXTURE ) + baseColor.a *= mskUsed; + else + baseColor.a = mskUsed; + } + + + if ( OUTER_GLOW ) + { +#if DISTANCEALPHAFROMDETAIL + float4 glowTexel = tex2D( DetailSampler, i.detailTexCoord.xy+GLOW_UV_OFFSET ); +#else + float4 glowTexel = tex2D( BaseTextureSampler, i.baseTexCoord.xy+GLOW_UV_OFFSET ); +#endif + float4 glowc = OUTER_GLOW_COLOR*smoothstep( OUTER_GLOW_MIN_DVALUE, OUTER_GLOW_MAX_DVALUE, glowTexel.a ); + baseColor = lerp( glowc, baseColor, mskUsed ); + } + +#endif // DISTANCEALPHA + + float3 specularFactor = 1.0f; + float4 envmapMaskTexel; + if( bEnvmapMask ) + { + envmapMaskTexel = tex2D( EnvmapMaskSampler, i.baseTexCoord.xy ); + specularFactor *= envmapMaskTexel.xyz; + } + + if( bBaseAlphaEnvmapMask ) + { + specularFactor *= 1.0 - baseColor.a; // this blows! + } + + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); + if( bDiffuseLighting || bVertexColor ) + { + diffuseLighting = i.color.rgb; + } + +#if STATIC_LIGHT_LIGHTMAP + // This matches the behavior of vertex lighting, which multiplies by cOverbright (which is not accessible here) + // And converts from Gamma space to Linear space before being used. + float2 lightmapTexCoords = i.baseTexCoord.xy; + #if DEBUG_LUXELS + lightmapTexCoords.xy *= g_LuxelScale.xy; + #endif + float3 f3LightmapColor = GammaToLinear( 2.0f * tex2D( LightMapSampler, lightmapTexCoords ).rgb ); + diffuseLighting = f3LightmapColor; +#endif + + float3 albedo = baseColor; + + if (bBlendTintByBaseAlpha) + { + float3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = lerp(tintedColor, g_DiffuseModulation.rgb, g_EnvmapTint_TintReplaceFactor.w); + albedo = lerp(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + float alpha = g_DiffuseModulation.a; + if ( !bBaseAlphaEnvmapMask && !bSelfIllum && !bBlendTintByBaseAlpha ) + { + alpha *= baseColor.a; + } + + + if( bFlashlight ) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; +// On ps_2_b, we can do shadow mapping +#if ( FLASHLIGHTSHADOWS && (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) ) + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = true; +#endif + +#if defined ( _X360 ) + float4 flashlightSpacePosition = i.flashlightSpacePos; +#else + float4 flashlightSpacePosition = mul( float4( i.worldPos_projPosZ.xyz, 1.0f ), g_FlashlightWorldToTexture ); +#endif + + // We want the N.L to happen on the flashlight pass, but can't afford it on ps20 + bool bUseWorldNormal = true; +#if ( defined( SHADER_MODEL_PS_2_0 ) && ( DETAILTEXTURE ) ) + bUseWorldNormal = false; +#endif + float3 flashlightColor = DoFlashlight( g_FlashlightPos, i.worldPos_projPosZ.xyz, flashlightSpacePosition, + i.worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, i.projPos.xy / i.projPos.w, false, g_EnvmapContrast_ShadowTweaks, bUseWorldNormal ); + +#if defined ( _X360 ) + diffuseLighting += flashlightColor; +#else + diffuseLighting = flashlightColor; +#endif + } + + if( bVertexColor && bDiffuseLighting ) + { + albedo *= i.color.rgb; + } + + alpha = lerp( alpha, alpha * i.color.a, g_fVertexAlpha ); + + float3 diffuseComponent = albedo * diffuseLighting; + +#if DETAILTEXTURE + diffuseComponent = + TextureCombinePostLighting( diffuseComponent, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor ); +#endif + + HALF3 specularLighting = HALF3( 0.0f, 0.0f, 0.0f ); + +#if !FLASHLIGHT || defined ( _X360 ) + #if SELFILLUM_ENVMAPMASK_ALPHA + // range of alpha: + // 0 - 0.125 = lerp(diffuse,selfillum,alpha*8) + // 0.125-1.0 = selfillum*(1+alpha-0.125)*8 (over bright glows) + HALF3 selfIllumComponent = g_SelfIllumTint * albedo; + half Adj_Alpha=8*envmapMaskTexel.a; + diffuseComponent=( max( 0, 1-Adj_Alpha ) * diffuseComponent) + Adj_Alpha * selfIllumComponent; + #else + if ( bSelfIllum ) + { + float3 vSelfIllumMask = tex2D( SelfIllumMaskSampler, i.baseTexCoord.xy ); + vSelfIllumMask = lerp( baseColor.aaa, vSelfIllumMask, g_SelfIllumMaskControl ); + diffuseComponent = lerp( diffuseComponent, g_SelfIllumTint * albedo, vSelfIllumMask ); + } + #endif + + if( bCubemap ) + { +#if CUBEMAP_SPHERE_LEGACY + HALF3 reflectVect = normalize(CalcReflectionVectorUnnormalized( i.worldSpaceNormal, i.worldVertToEyeVector.xyz )); + + specularLighting = 0.5 * tex2D( EnvmapSampler, float2(reflectVect.x, reflectVect.y) ) * g_DiffuseModulation.rgb * diffuseLighting; +#else + HALF3 reflectVect = CalcReflectionVectorUnnormalized( i.worldSpaceNormal, i.worldVertToEyeVector.xyz ); + + specularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint_TintReplaceFactor.rgb; + HALF3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast_ShadowTweaks ); + HALF3 greyScale = dot( specularLighting, HALF3( 0.299f, 0.587f, 0.114f ) ); + specularLighting = lerp( greyScale, specularLighting, g_EnvmapSaturation ); +#endif + } +#endif + + HALF3 result = diffuseComponent + specularLighting; + +#if LIGHTING_PREVIEW +# if LIGHTING_PREVIEW == 1 + float dotprod=0.7+0.25*dot(i.worldSpaceNormal,normalize(float3(1,2,-.5))); + return FinalOutput( float4( dotprod*albedo.xyz, alpha ), 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR ); +# else + LPREVIEW_PS_OUT ret; + ret.flags=float4(1,1,1,1); + ret.color=float4( albedo.xyz, alpha ); + ret.normal=float4(i.worldSpaceNormal,alpha); + ret.position=float4(i.worldPos_projPosZ.xyz, alpha); + return FinalOutput( ret, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +# endif +#else + +# if (DEPTHBLEND == 1) + { + float2 vScreenPos; + vScreenPos.x = i.projPos.x; + vScreenPos.y = -i.projPos.y; + vScreenPos = (vScreenPos + i.projPos.w) * 0.5f; + alpha *= DepthFeathering( DepthSampler, vScreenPos / i.projPos.w, i.projPos.w - i.projPos.z, i.projPos.w, g_DepthFeatheringConstants ); + } +# endif + +#if defined( SHADER_MODEL_PS_2_0 ) + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.projPos.z ); + #if (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + alpha = lerp( alpha, fogFactor, g_fWriteWaterFogToDestAlpha ); + #endif + return FinalOutput( float4( result.rgb, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, false, i.projPos.z ); +#else // 2b or higher + float fogFactor = CalcPixelFogFactorConst( g_fPixelFogType, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.projPos.z ); + alpha = lerp( alpha, fogFactor, g_fWriteWaterFogToDestAlpha ); // Use the fog factor if it's height fog + return FinalOutputConst( float4( result.rgb, alpha ), fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, i.projPos.z ); +#endif + +#endif +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_vs20.fxc new file mode 100644 index 00000000..36f7a031 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_vs20.fxc @@ -0,0 +1,293 @@ +//======= Copyright © 1996-2007, Valve Corporation, All rights reserved. ====== + +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "SEAMLESS_BASE" "0..1" +// STATIC: "SEAMLESS_DETAIL" "0..1" +// STATIC: "SEPARATE_DETAIL_UVS" "0..1" +// STATIC: "DECAL" "0..1" [vs30] +// STATIC: "SM30_VERTEXID" "0..1" [vs30] +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" [vs20] +// STATIC: "DONT_GAMMA_CONVERT_VERTEX_COLOR" "0..1" +// STATIC: "TREESWAY" "0..2" +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DYNAMIC_LIGHT" "0..1" +// DYNAMIC: "STATIC_LIGHT_VERTEX" "0..1" +// DYNAMIC: "STATIC_LIGHT_LIGHTMAP" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "LIGHTING_PREVIEW" "0..1" [PC] +// DYNAMIC: "LIGHTING_PREVIEW" "0..0" [XBOX] +// DYNAMIC: "MORPHING" "0..1" [vs30] +// DYNAMIC: "NUM_LIGHTS" "0..2" [vs20] + +// If using static control flow on Direct3D, we should use the NUM_LIGHTS=0 combo +// SKIP: $USE_STATIC_CONTROL_FLOW && ( $NUM_LIGHTS > 0 ) [vs20] +// SKIP: ($SEPARATE_DETAIL_UVS) && ($SEAMLESS_DETAIL) +// SKIP: ($DONT_GAMMA_CONVERT_VERTEX_COLOR && ( ! $VERTEXCOLOR ) ) +// SKIP: ( $TREESWAY ) && ( $SEAMLESS_DETAIL || $SEAMLESS_BASE ) +#include "common_vs_fxc.h" + +static const bool g_bSkinning = SKINNING ? true : false; +static const int g_FogType = DOWATERFOG; +static const bool g_bVertexColor = VERTEXCOLOR ? true : false; +static const bool g_bCubemap = CUBEMAP ? true : false; +static const bool g_bFlashlight = FLASHLIGHT ? true : false; +static const bool g_bHalfLambert = HALFLAMBERT ? true : false; +#if (defined( SHADER_MODEL_VS_3_0 ) && MORPHING && DECAL && SM30_VERTEXID) +static const bool g_bDecalOffset = true; +#else +static const bool g_bDecalOffset = false; +#endif + +const float4 cBaseTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_0 ); +#if SEAMLESS_DETAIL || SEAMLESS_BASE +const float cSeamlessScale : register( SHADER_SPECIFIC_CONST_2); +#define SEAMLESS_SCALE cSeamlessScale.x +#endif + +const float4 cDetailTexCoordTransform[2] : register( SHADER_SPECIFIC_CONST_4 ); + +#if defined ( _X360 ) +const float4x4 g_FlashlightWorldToTexture : register( SHADER_SPECIFIC_CONST_6 ); // 6, 7, 8, 9 +#endif + +#if ( TREESWAY ) +const float4 g_vMiscParams1 : register( SHADER_SPECIFIC_CONST_6 ); +const float4 g_vMiscParams2 : register( SHADER_SPECIFIC_CONST_7 ); +const float4 g_vMiscParams3 : register( SHADER_SPECIFIC_CONST_8 ); +const float4 g_vMiscParams4 : register( SHADER_SPECIFIC_CONST_9 ); +#define g_flTime g_vMiscParams2.y +#define g_vWindDir g_vMiscParams2.zw + +#define g_flFastSwaySpeedScale g_vMiscParams1.x +#define g_flScrumbleFalloffCurve g_vMiscParams1.y +#define g_flSwayFalloffCurve g_vMiscParams1.z +#define g_flScrumbleSpeed g_vMiscParams1.w + +#define g_flHeight g_vMiscParams3.x +#define g_flStartHeight g_vMiscParams3.y +#define g_flRadius g_vMiscParams3.z +#define g_flStartRadius g_vMiscParams3.w + +#define g_flSwaySpeed g_vMiscParams4.x +#define g_flSwayIntensity g_vMiscParams4.y +#define g_flScrumbleWaveCount g_vMiscParams4.z +#define g_flScrumbleIntensity g_vMiscParams4.w + +#define g_flWindSpeedLerpStart cDetailTexCoordTransform[0].x +#define g_flWindSpeedLerpEnd cDetailTexCoordTransform[0].y +#include "tree_sway.h" +#endif + +#if defined( SHADER_MODEL_VS_3_0 ) && SM30_VERTEXID +// NOTE: cMorphTargetTextureDim.xy = target dimensions, +// cMorphTargetTextureDim.z = 4tuples/morph +const float3 cMorphTargetTextureDim : register( SHADER_SPECIFIC_CONST_10 ); +const float4 cMorphSubrect : register( SHADER_SPECIFIC_CONST_11 ); +sampler2D morphSampler : register( D3DVERTEXTEXTURESAMPLER0, s0 ); +#endif + +struct VS_INPUT +{ + // This is all of the stuff that we ever use. + float4 vPos : POSITION; + float4 vBoneWeights : BLENDWEIGHT; + float4 vBoneIndices : BLENDINDICES; + float4 vNormal : NORMAL; + float4 vColor : COLOR0; + float3 vSpecular : COLOR1; + // make these float2's and stick the [n n 0 1] in the dot math. + float4 vTexCoord0 : TEXCOORD0; + float4 vTexCoord1 : TEXCOORD1; + float4 vTexCoord2 : TEXCOORD2; + float4 vTexCoord3 : TEXCOORD3; + + // Position and normal/tangent deltas + float3 vPosFlex : POSITION1; + float3 vNormalFlex : NORMAL1; +#if defined( SHADER_MODEL_VS_3_0 ) && SM30_VERTEXID + float vVertexID : POSITION2; +#endif +}; + + +struct VS_OUTPUT +{ + float4 projPos : POSITION; // Projection-space position +#if !defined( _X360 ) + float fog : FOG; +#endif + +#if SEAMLESS_BASE + HALF3 SeamlessTexCoord : TEXCOORD0; // Base texture x/y/z (indexed by swizzle) +#else + HALF2 baseTexCoord : TEXCOORD0; // Base texture coordinate +#endif +#if SEAMLESS_DETAIL + HALF3 SeamlessDetailTexCoord : TEXCOORD1; // Detail texture coordinate +#else + HALF2 detailTexCoord : TEXCOORD1; // Detail texture coordinate +#endif + float4 color : TEXCOORD2; // Vertex color (from lighting or unlit) + +#if 1 //CUBEMAP || _X360 + float3 worldVertToEyeVector : TEXCOORD3; // Necessary for cubemaps +#endif + + float3 worldSpaceNormal : TEXCOORD4; // Necessary for cubemaps and flashlight + +#if defined ( _X360 ) && FLASHLIGHT + float4 flashlightSpacePos : TEXCOORD5; +#endif + + float4 vProjPos : TEXCOORD6; + float4 worldPos_ProjPosZ : TEXCOORD7; + float4 fogFactorW : COLOR1; +#if SEAMLESS_DETAIL || SEAMLESS_BASE + float3 SeamlessWeights : COLOR0; // x y z projection weights +#endif + +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + bool bDynamicLight = DYNAMIC_LIGHT ? true : false; + bool bStaticLight = STATIC_LIGHT_VERTEX ? true : false; + bool bDoLighting = !g_bVertexColor && (bDynamicLight || bStaticLight); + + float4 vPosition = v.vPos; + float3 vNormal = 0; + if ( bDoLighting || FLASHLIGHT || SEAMLESS_BASE || SEAMLESS_DETAIL || LIGHTING_PREVIEW || g_bDecalOffset || CUBEMAP ) + { + // The vertex only contains valid normals if they are actually needed (fetching them when absent makes D3D complain) + DecompressVertex_Normal( v.vNormal, vNormal ); + } + +#if SEAMLESS_BASE || SEAMLESS_DETAIL + // compute blend weights in rgb + float3 NNormal=normalize( vNormal ); + o.SeamlessWeights.xyz = NNormal * NNormal; // sums to 1. +#endif + +#if !defined( SHADER_MODEL_VS_3_0 ) || !MORPHING || !SM30_VERTEXID + ApplyMorph( v.vPosFlex, v.vNormalFlex, vPosition.xyz, vNormal ); +#else + ApplyMorph( morphSampler, cMorphTargetTextureDim, cMorphSubrect, + v.vVertexID, v.vTexCoord2, vPosition.xyz, vNormal ); +#endif + +#if ( TREESWAY ) + vPosition.xyz = ComputeTreeSway( vPosition.xyz, g_flTime ); +#endif + + // Perform skinning + float3 worldNormal, worldPos; + SkinPositionAndNormal( + g_bSkinning, + vPosition, vNormal, + v.vBoneWeights, v.vBoneIndices, + worldPos, worldNormal ); + + if ( !g_bVertexColor ) + { + worldNormal = normalize( worldNormal ); + } + +#if defined( SHADER_MODEL_VS_3_0 ) && MORPHING && DECAL && SM30_VERTEXID + // Avoid z precision errors + worldPos += worldNormal * 0.05f * v.vTexCoord2.z; +#endif + + o.worldSpaceNormal = worldNormal; + + // Transform into projection space + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.projPos = vProjPos; + vProjPos.z = dot( float4( worldPos, 1 ), cViewProjZ ); + + o.vProjPos = vProjPos; + o.fogFactorW.w = CalcFog( worldPos, vProjPos, g_FogType ); +#if !defined( _X360 ) + o.fog = o.fogFactorW.w; +#endif + o.worldPos_ProjPosZ.xyz = worldPos.xyz; + o.worldPos_ProjPosZ.w = vProjPos.z; + + // Needed for cubemaps +#if 1 //CUBEMAP + o.worldVertToEyeVector.xyz = VSHADER_VECT_SCALE * (cEyePos - worldPos); +#endif + +#if !defined (_X360) && FLASHLIGHT + o.color = float4( 0.0f, 0.0f, 0.0f, 0.0f ); +#else + if ( g_bVertexColor ) + { + // Assume that this is unlitgeneric if you are using vertex color. + o.color.rgb = ( DONT_GAMMA_CONVERT_VERTEX_COLOR ) ? v.vColor.rgb : GammaToLinear( v.vColor.rgb ); + o.color.a = v.vColor.a; + } + else + { + #if ( ( USE_STATIC_CONTROL_FLOW ) || defined ( SHADER_MODEL_VS_3_0 ) ) + { + o.color.xyz = DoLighting( worldPos, worldNormal, v.vSpecular, bStaticLight, bDynamicLight, g_bHalfLambert ); + } + #else + { + o.color.xyz = DoLightingUnrolled( worldPos, worldNormal, v.vSpecular, bStaticLight, bDynamicLight, g_bHalfLambert, NUM_LIGHTS ); + } + #endif + } +#endif + + +#if SEAMLESS_BASE + o.SeamlessTexCoord.xyz = SEAMLESS_SCALE * v.vPos.xyz; +#else + // Base texture coordinates + o.baseTexCoord.x = dot( v.vTexCoord0, cBaseTexCoordTransform[0] ); + o.baseTexCoord.y = dot( v.vTexCoord0, cBaseTexCoordTransform[1] ); +#endif + +#if SEAMLESS_DETAIL + // FIXME: detail texcoord as a 2d xform doesn't make much sense here, so I just do enough so + // that scale works. More smartness could allow 3d xform. + o.SeamlessDetailTexCoord.xyz = (SEAMLESS_SCALE*cDetailTexCoordTransform[0].x) * v.vPos.xyz; +#else + #if ( TREESWAY ) + { + o.detailTexCoord.xy = v.vTexCoord0; + } + #else + { + // Detail texture coordinates + // FIXME: This shouldn't have to be computed all the time. + o.detailTexCoord.x = dot( v.vTexCoord0, cDetailTexCoordTransform[0] ); + o.detailTexCoord.y = dot( v.vTexCoord0, cDetailTexCoordTransform[1] ); + } + #endif +#endif + +#if SEPARATE_DETAIL_UVS + o.detailTexCoord.xy = v.vTexCoord1.xy; +#endif + +#if LIGHTING_PREVIEW + float dot=0.5+0.5*worldNormal*float3(0.7071,0.7071,0); + o.color.xyz=float3(dot,dot,dot); +#endif + +#if defined ( _X360 ) && FLASHLIGHT + o.flashlightSpacePos = mul( float4( worldPos, 1.0f ), g_FlashlightWorldToTexture ); +#endif + + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlit_lighting_only_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlit_lighting_only_ps2x.fxc new file mode 100644 index 00000000..52812637 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_lighting_only_ps2x.fxc @@ -0,0 +1,68 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "HALFLAMBERT" "0..1" + +// DYNAMIC: "AMBIENT_LIGHT" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..2" [ps20] +// DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b] + +#define HDRTYPE HDR_TYPE_NONE +#include "common_vertexlitgeneric_dx9.h" + +const float4 g_OverbrightFactor : register( c4 ); +const float3 cAmbientCube[6] : register( c6 ); + +PixelShaderLightInfo cLightInfo[3] : register(c13); + +sampler BumpmapSampler : register( s0 ); +sampler NormalizeSampler : register( s1 ); + +struct PS_INPUT +{ + float2 baseTexCoord : TEXCOORD0; + // detail textures and bumpmaps are mutually exclusive so that we have enough texcoords. + float2 detailOrBumpTexCoord : TEXCOORD1; + // bump mapping and a separate envmap mask texture are mutually exclusive. + float2 envmapMaskTexCoord : TEXCOORD2; + float3 worldVertToEyeVector : TEXCOORD3; + float3x3 tangentSpaceTranspose : TEXCOORD4; + float4 worldPos_projPosZ : TEXCOORD5; + float2 lightAtten01 : TEXCOORD6; + float2 lightAtten23 : TEXCOORD7; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + bool bDiffuseLighting = DIFFUSELIGHTING ? true : false; + bool bHalfLambert = HALFLAMBERT ? true : false; + bool bAmbientLight = AMBIENT_LIGHT ? true : false; + int nNumLights = NUM_LIGHTS; + + float4 vLightAtten = float4( i.lightAtten01, i.lightAtten23 ); + + float3 tangentSpaceNormal = float3( 0.0f, 0.0f, 1.0f ); + float4 normalTexel = 1.0f; + float4 baseColor = float4( 1.0f, 1.0f, 1.0f, 1.0f ); + + normalTexel = tex2D( BumpmapSampler, i.detailOrBumpTexCoord ); + tangentSpaceNormal = 2.0f * normalTexel - 1.0f; + + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); + if( bDiffuseLighting ) + { + float3 worldSpaceNormal = mul( i.tangentSpaceTranspose, tangentSpaceNormal ); + float3 staticLightingColor = float3( 0.0f, 0.0f, 0.0f ); + diffuseLighting = PixelShaderDoLighting( i.worldPos_projPosZ.xyz, worldSpaceNormal, + float3( 0.0f, 0.0f, 0.0f ), false, bAmbientLight, + vLightAtten, cAmbientCube, NormalizeSampler, nNumLights, cLightInfo, bHalfLambert, + false, 0, false, NormalizeSampler ); + // multiply by .5 since we want a 50% (in gamma space) reflective surface) + diffuseLighting *= pow( 0.5f, 2.2f ); + } + + return FinalOutput( float4( diffuseLighting, 1.0f ), 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.fxc b/mp/src/materialsystem/stdshaders/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.fxc new file mode 100644 index 00000000..70ad7094 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.fxc @@ -0,0 +1,9 @@ +struct PS_INPUT +{ + float3 vColor0 : COLOR0; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + return float4( i.vColor0, 1.0 ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_water_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_water_ps2x.fxc new file mode 100644 index 00000000..adc42f36 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_water_ps2x.fxc @@ -0,0 +1,113 @@ +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b] [= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "BASETEXTURE" "0..1" +// STATIC: "MULTITEXTURE" "0..1" +// STATIC: "REFLECT" "0..1" +// STATIC: "REFRACT" "0..1" +// STATIC: "ABOVEWATER" "0..1" +// STATIC: "BLURRY_REFRACT" "0..1" [ps20b] + +// When we turn NORMAL_DECODE_MODE on, this shader only needs 0..1, not 0..2 +// STATIC: "NORMAL_DECODE_MODE" "0..0" [XBOX] +// STATIC: "NORMAL_DECODE_MODE" "0..0" [PC] + +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] + +// SKIP: $MULTITEXTURE && $BASETEXTURE + +#if defined(SHADER_MODEL_PS_2_0) +# define BLURRY_REFRACT 0 +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + +#include "water_ps2x_helper.h" + + +sampler RefractSampler : register( s0 ); +#if BASETEXTURE +sampler BaseTextureSampler : register( s1 ); +#endif +sampler ReflectSampler : register( s2 ); +#if BASETEXTURE +sampler LightmapSampler : register( s3 ); +#endif +sampler NormalSampler : register( s4 ); + +const HALF4 vRefractTint : register( c1 ); +const HALF4 vReflectTint : register( c4 ); +const float4 g_ReflectRefractScale : register( c5 ); // xy - reflect scale, zw - refract scale +const HALF4 g_WaterFogColor : register( c6 ); +const HALF4 g_WaterFogParams : register( c7 ); + +const float4 g_PixelFogParams : register( c8 ); + +const float3 g_EyePos : register( c9 ); + +#define g_WaterFogStart g_WaterFogParams.x +#define g_WaterFogEndMinusStart g_WaterFogParams.y +#define g_Reflect_OverBright g_WaterFogParams.z + +struct PS_INPUT +{ + float2 vBumpTexCoord : TEXCOORD0; + half3 vTangentEyeVect : TEXCOORD1; + float4 vReflectXY_vRefractYX : TEXCOORD2; + float4 vWorldPos_projPosW : TEXCOORD3; + float4 vProjPos : TEXCOORD4; + float screenCoord : TEXCOORD5; +#if MULTITEXTURE + float4 vExtraBumpTexCoord : TEXCOORD6; +#endif +#if BASETEXTURE +// CENTROID: TEXCOORD6 + HALF4 lightmapTexCoord1And2 : TEXCOORD6; +// CENTROID: TEXCOORD7 + HALF4 lightmapTexCoord3 : TEXCOORD7; +#endif + + float4 fogFactorW : COLOR1; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + DrawWater_params_t params; + + params.vBumpTexCoord = i.vBumpTexCoord; +#if MULTITEXTURE + params.vExtraBumpTexCoord = i.vExtraBumpTexCoord; +#endif + params.vReflectXY_vRefractYX = i.vReflectXY_vRefractYX; + params.w = i.vWorldPos_projPosW.w; + params.vReflectRefractScale = g_ReflectRefractScale; + params.fReflectOverbright = g_Reflect_OverBright; + params.vReflectTint = vReflectTint; + params.vRefractTint = vRefractTint; + params.vTangentEyeVect = i.vTangentEyeVect; + params.waterFogColor = g_WaterFogColor; +#if BASETEXTURE + params.lightmapTexCoord1And2 = i.lightmapTexCoord1And2; + params.lightmapTexCoord3 = i.lightmapTexCoord3; +#endif + params.vProjPos = i.vProjPos; + params.pixelFogParams = g_PixelFogParams; + params.fWaterFogStart = g_WaterFogStart; + params.fWaterFogEndMinusStart = g_WaterFogEndMinusStart; + params.vEyePos = g_EyePos; + params.vWorldPos = i.vWorldPos_projPosW.xyz; + + float4 result; + float fogFactor; + DrawWater( params, + // yay. . can't put sampler in a struct. +#if BASETEXTURE + BaseTextureSampler, + LightmapSampler, +#endif + NormalSampler, RefractSampler, ReflectSampler, + result, fogFactor ); + + return FinalOutput( float4( result.rgb, 1.0f ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE, (WRITE_DEPTH_TO_DESTALPHA != 0), i.vProjPos.z ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc new file mode 100644 index 00000000..fc3ea25a --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc @@ -0,0 +1,64 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// +// STATIC: "PARALLAXCORRECT" "0..1" + +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +// SKIP: $PARALLAXCORRECT [ps20] + +#include "common_ps_fxc.h" +#include "shader_constant_register_map.h" + +sampler EnvmapSampler : register( s0 ); + +const float4 g_FogParams : register( PSREG_FOG_PARAMS ); +const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT ); + +#if PARALLAXCORRECT +// Parallax cubemaps +const float3 cubemapPos : register(c0); +const float4x4 obbMatrix : register(c1); //through c4 +#endif + +struct PS_INPUT +{ + float3 eyeToVertVector : TEXCOORD0; + float4 vertexColor : COLOR; + +#if PARALLAXCORRECT + float3 worldSpaceNormal : TEXCOORD1; +#endif + + float4 worldPos_projPosZ : TEXCOORD2; // Necessary for pixel fog +}; + +float4 main( PS_INPUT i ) : COLOR +{ +#if PARALLAXCORRECT + float3 reflectVect = CalcReflectionVectorUnnormalized( i.worldSpaceNormal, g_EyePos_SpecExponent.xyz - i.worldPos_projPosZ.xyz ); //i.eyeToVertVector; //CalcReflectionVectorUnnormalized( i.worldSpaceNormal, i.eyeToVertVector ); + + //Parallax correction (2_0b and beyond) + //Adapted from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ + float3 worldPos = i.worldPos_projPosZ.xyz; + float3 positionLS = mul(float4(worldPos, 1), obbMatrix); + float3 rayLS = mul(reflectVect, (float3x3) obbMatrix); + + float3 firstPlaneIntersect = (float3(1.0f, 1.0f, 1.0f) - positionLS) / rayLS; + float3 secondPlaneIntersect = (-positionLS) / rayLS; + float3 furthestPlane = max(firstPlaneIntersect, secondPlaneIntersect); + float distance = min(furthestPlane.x, min(furthestPlane.y, furthestPlane.z)); + + // Use distance in WS directly to recover intersection + float3 intersectPositionWS = worldPos + reflectVect * distance; + reflectVect = intersectPositionWS - cubemapPos; +#else + float3 reflectVect = i.eyeToVertVector; +#endif + + HALF4 color; + color.xyz = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); + color.a = 1.0f; + color *= i.vertexColor; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + return FinalOutput( color, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR ); +} diff --git a/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc new file mode 100644 index 00000000..f67676ee --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc @@ -0,0 +1,61 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// +// +// Purpose: +// +//=========================================================================== +// STATIC: "PARALLAXCORRECT" "0..1" + +// DYNAMIC: "DOWATERFOG" "0..1" + +#include "common_vs_fxc.h" + +static const int g_FogType = DOWATERFOG; + +struct VS_INPUT +{ + float3 vPos : POSITION; +#if PARALLAXCORRECT + float4 vNormal : NORMAL; +#endif +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + float fog : FOG; +#endif + float3 eyeToVertVector : TEXCOORD0; + float4 vertexColor : COLOR; + +#if PARALLAXCORRECT + float3 worldNormal : TEXCOORD1; +#endif + + float4 worldPos_projPosZ : TEXCOORD2; // Necessary for pixel fog +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + o.projPos = mul( float4( v.vPos, 1 ), cModelViewProj ); + + float3 worldPos = mul( float4( v.vPos, 1 ), cModel[0] ); + o.worldPos_projPosZ = float4( worldPos.xyz, o.projPos.z ); + o.eyeToVertVector = worldPos - cEyePos; + +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + o.fog = CalcFixedFunctionFog( worldPos, g_FogType ); +#endif + +#if PARALLAXCORRECT + float3 vObjNormal; + DecompressVertex_Normal( v.vNormal, vObjNormal ); + o.worldNormal = mul( vObjNormal, ( float3x3 )cModel[0] ); +#endif + + o.vertexColor = cModulationColor; + return o; +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_worldtwotextureblend_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_worldtwotextureblend_ps2x.fxc new file mode 100644 index 00000000..6da0b286 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_worldtwotextureblend_ps2x.fxc @@ -0,0 +1,217 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "BUMPMAP" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "DIFFUSEBUMPMAP" "0..1" +// STATIC: "DETAIL_ALPHA_MASK_BASE_TEXTURE" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "SEAMLESS" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps20b] [PC] +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] + +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC] +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [XBOX] +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b] + +// SKIP: $DETAILTEXTURE && ( $BUMPMAP && !$DETAIL_ALPHA_MASK_BASE_TEXTURE ) +// SKIP: !$BUMPMAP && $DIFFUSEBUMPMAP +// SKIP: $VERTEXCOLOR && $BUMPMAP +// SKIP: FLASHLIGHT && $SELFILLUM +// SKIP: FLASHLIGHT && $DETAIL_ALPHA_MASK_BASE_TEXTURE +// SKIP: FLASHLIGHT && ($BUMPMAP || $DIFFUSEBUMPMAP) + +// We don't care about flashlight depth unless the flashlight is on +// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 ) [ps20b] + +#if defined( SHADER_MODEL_PS_2_0 ) +# define WRITE_DEPTH_TO_DESTALPHA 0 +#endif + + +#define HDRTYPE HDR_TYPE_NONE +#include "common_flashlight_fxc.h" +#include "common_ps_fxc.h" + +const HALF4 g_SelfIllumTint : register( c7 ); +static const HALF g_OverbrightFactor = 2.0f; + +const HALF3 g_EyePos : register( c10 ); +const HALF4 g_FogParams : register( c11 ); + +const HALF3 g_FlashlightPos : register( c15 ); +// flashlightfixme: Move this math into the vertex shader. +const float4x4 g_FlashlightWorldToTexture : register( c16 ); + +const float4 g_FlashlightAttenuationFactors : register( c20 ); + +sampler BaseTextureSampler : register( s0 ); +sampler LightmapSampler : register( s1 ); +sampler FlashlightSampler : register( s2 ); +sampler DetailSampler : register( s3 ); +sampler BumpmapSampler : register( s4 ); +sampler NormalizeSampler : register( s6 ); + +struct PS_INPUT +{ + HALF2 baseTexCoord : TEXCOORD0; + HALF4 detailOrBumpTexCoord : TEXCOORD1; + HALF4 lightmapTexCoord1And2 : TEXCOORD2; // CENTROID: TEXCOORD2 + HALF2 lightmapTexCoord3 : TEXCOORD3; // CENTROID: TEXCOORD3 + HALF4 worldPos_projPosZ : TEXCOORD4; + HALF3x3 tangentSpaceTranspose : TEXCOORD5; + // tangentSpaceTranspose : TEXCOORD6; + // tangentSpaceTranspose : TEXCOORD7; + HALF4 vertexColor : COLOR; +}; + + +float4 main( PS_INPUT i ) : COLOR +{ + bool bDetailTexture = DETAILTEXTURE ? true : false; + bool bBumpmap = BUMPMAP ? true : false; + bool bDiffuseBumpmap = DIFFUSEBUMPMAP ? true : false; + bool bVertexColor = VERTEXCOLOR ? true : false; + bool bSelfIllum = SELFILLUM ? true : false; + bool bDetailAlphaMaskBaseTexture = DETAIL_ALPHA_MASK_BASE_TEXTURE ? true : false; + bool bFlashlight = FLASHLIGHT ? true : false; + + HALF3 lightmapColor1 = HALF3( 1.0f, 1.0f, 1.0f ); + HALF3 lightmapColor2 = HALF3( 1.0f, 1.0f, 1.0f ); + HALF3 lightmapColor3 = HALF3( 1.0f, 1.0f, 1.0f ); + if( bBumpmap && bDiffuseBumpmap ) + { + HALF2 bumpCoord1; + HALF2 bumpCoord2; + HALF2 bumpCoord3; + ComputeBumpedLightmapCoordinates( i.lightmapTexCoord1And2, i.lightmapTexCoord3.xy, + bumpCoord1, bumpCoord2, bumpCoord3 ); + + HALF4 lightmapSample1 = tex2D( LightmapSampler, bumpCoord1 ); + lightmapColor1 = lightmapSample1.rgb; + lightmapColor2 = tex2D( LightmapSampler, bumpCoord2 ); + lightmapColor3 = tex2D( LightmapSampler, bumpCoord3 ); + } + else + { + if( !bFlashlight ) + { + HALF2 bumpCoord1 = ComputeLightmapCoordinates( i.lightmapTexCoord1And2, i.lightmapTexCoord3.xy ); + HALF4 lightmapSample1 = tex2D( LightmapSampler, bumpCoord1 ); + lightmapColor1 = lightmapSample1.rgb; + } + } + + HALF4 detailColor = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); + if( bDetailTexture ) + { + detailColor = tex2D( DetailSampler, i.detailOrBumpTexCoord.xy ); + } + + HALF4 baseColor = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); + baseColor = tex2D( BaseTextureSampler, i.baseTexCoord ); + if ( bDetailAlphaMaskBaseTexture ) + { + // This is what WorldTwoTextureBlend_DX6 does. + baseColor.rgb = saturate( saturate( baseColor * 2 ) * detailColor.a + (1 - detailColor.a) ); + baseColor.rgb *= detailColor; + } + else + { + baseColor.rgb = lerp( baseColor, detailColor, detailColor.a ); + } + + HALF3 normal = HALF3( 0.0f, 0.0f, 1.0f ); + if( bBumpmap ) + { + HALF3 normalTexel; + normalTexel = tex2D( BumpmapSampler, i.detailOrBumpTexCoord.xy ); + normal = 2.0 * normalTexel - 1.0; + } + + HALF3 albedo = HALF3( 1.0f, 1.0f, 1.0f ); + HALF alpha = 1.0f; + albedo *= baseColor; + if( !bSelfIllum ) + { + alpha *= baseColor.a; + } + + // The vertex color contains the modulation color + vertex color combined + albedo *= i.vertexColor; + alpha *= i.vertexColor.a; // not sure about this one + + HALF3 diffuseLighting; + if( bFlashlight ) + { + float3 worldSpaceNormal; + // Make the unbumped version not so fucking stupid and not need tangentSpaceTranspose you knob. + worldSpaceNormal = mul( normal, i.tangentSpaceTranspose ); + + int nShadowSampleLevel = 0; + bool bDoShadows = false; +// On ps_2_b, we can do shadow mapping +#if ( FLASHLIGHTSHADOWS && (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) ) ) + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = true; +#endif + float4 flashlightSpacePosition = mul( float4( i.worldPos_projPosZ.xyz, 1.0f ), g_FlashlightWorldToTexture ); + + diffuseLighting = DoFlashlight( g_FlashlightPos, i.worldPos_projPosZ.xyz, flashlightSpacePosition, + worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, FlashlightSampler, NormalizeSampler, + nShadowSampleLevel, bDoShadows, false, float2(0, 0), false ); + } + else + { + if( bBumpmap && bDiffuseBumpmap ) + { + float dot1 = saturate( dot( normal, bumpBasis[0] ) ); + float dot2 = saturate( dot( normal, bumpBasis[1] ) ); + float dot3 = saturate( dot( normal, bumpBasis[2] ) ); + + float sum = dot1 + dot2 + dot3; + diffuseLighting = dot1 * lightmapColor1 + + dot2 * lightmapColor2 + + dot3 * lightmapColor3; + diffuseLighting *= 1.0f / sum; + } + else + { + diffuseLighting = lightmapColor1; + } + + // Only scale here since the flashlight will already be scaled properly + diffuseLighting *= g_OverbrightFactor; + } + + HALF3 diffuseComponent = albedo * diffuseLighting; + + if( bSelfIllum ) + { + HALF3 selfIllumComponent = g_SelfIllumTint * albedo; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a ); + } + + HALF3 specularLighting = HALF3( 0.0f, 0.0f, 0.0f ); + HALF3 result = diffuseComponent + specularLighting; + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); + +#if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + alpha = fogFactor; +#endif + + return FinalOutput( float4( result, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, (WRITE_DEPTH_TO_DESTALPHA != 0), i.worldPos_projPosZ.w ); +} + diff --git a/mp/src/materialsystem/stdshaders/blurgaussian_3x3_ps2x.fxc b/mp/src/materialsystem/stdshaders/blurgaussian_3x3_ps2x.fxc new file mode 100644 index 00000000..5dbf655e --- /dev/null +++ b/mp/src/materialsystem/stdshaders/blurgaussian_3x3_ps2x.fxc @@ -0,0 +1,22 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +sampler g_texSampler : register( s0 ); + +struct PS_INPUT +{ + float2 uv : TEXCOORD0; +}; + +float2 g_vPsTapOffsets[2] : register( c0 ); + +float4 main( PS_INPUT i ) : COLOR +{ + float4 cOut; + + cOut = 0.25 * tex2D( g_texSampler, i.uv + g_vPsTapOffsets[0] ); + cOut += 0.25 * tex2D( g_texSampler, i.uv - g_vPsTapOffsets[0] ); + cOut += 0.25 * tex2D( g_texSampler, i.uv + g_vPsTapOffsets[1] ); + cOut += 0.25 * tex2D( g_texSampler, i.uv - g_vPsTapOffsets[1] ); + + return cOut; +} diff --git a/mp/src/materialsystem/stdshaders/buildhl2mpshaders.bat b/mp/src/materialsystem/stdshaders/buildhl2mpshaders.bat index 902292d3..0f253211 100755 --- a/mp/src/materialsystem/stdshaders/buildhl2mpshaders.bat +++ b/mp/src/materialsystem/stdshaders/buildhl2mpshaders.bat @@ -8,7 +8,7 @@ rem == Set the absolute path to your mod's game directory here == set GAMEDIR=%cd%\..\..\..\game\mod_hl2mp rem == Set the relative or absolute path to Source SDK Base 2013 Singleplayer\bin == -set SDKBINDIR=C:\SteamBetaLibrary\SteamApps\common\Source SDK Base 2013 Singleplayer\bin +set SDKBINDIR=C:\Program Files (x86)\Steam\SteamApps\common\Source SDK Base 2013 Singleplayer\bin rem == Set the Path to your mod's root source code == rem This should already be correct, accepts relative paths only! @@ -19,3 +19,6 @@ rem ==================================== call buildsdkshaders.bat + +@echo Finished building shaders +@pause diff --git a/mp/src/materialsystem/stdshaders/buildsdkshaders.bat b/mp/src/materialsystem/stdshaders/buildsdkshaders.bat index 22624938..6e287de3 100755 --- a/mp/src/materialsystem/stdshaders/buildsdkshaders.bat +++ b/mp/src/materialsystem/stdshaders/buildsdkshaders.bat @@ -4,7 +4,7 @@ setlocal rem Use dynamic shaders to build .inc files only rem set dynamic_shaders=1 rem == Setup path to nmake.exe, from vc 2005 common tools directory == -call "%VS100COMNTOOLS%vsvars32.bat" +call "%VS120COMNTOOLS%vsvars32.bat" set TTEXE=..\..\devtools\bin\timeprecise.exe diff --git a/mp/src/materialsystem/stdshaders/buildshaders.bat b/mp/src/materialsystem/stdshaders/buildshaders.bat index ad9b1852..cb2cb390 100755 --- a/mp/src/materialsystem/stdshaders/buildshaders.bat +++ b/mp/src/materialsystem/stdshaders/buildshaders.bat @@ -71,7 +71,7 @@ REM **************** :set_mod_args if not exist "%SDKBINDIR%\shadercompile.exe" goto NoShaderCompile -set ChangeToDir=%SDKBINDIR% +set ChangeToDir="%SDKBINDIR%" if /i "%4" NEQ "-source" goto NoSourceDirSpecified set SrcDirBase=%~5 diff --git a/mp/src/materialsystem/stdshaders/cable_dx9.cpp b/mp/src/materialsystem/stdshaders/cable_dx9.cpp index 5b25ed61..49df334c 100644 --- a/mp/src/materialsystem/stdshaders/cable_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/cable_dx9.cpp @@ -8,9 +8,9 @@ #include "BaseVSShader.h" -#include "cable_vs20.inc" -#include "cable_ps20.inc" -#include "cable_ps20b.inc" +#include "SDK_cable_vs20.inc" +#include "SDK_cable_ps20.inc" +#include "SDK_cable_ps20b.inc" #include "cpp_shader_constant_register_map.h" // memdbgon must be the last include file in a .cpp file!!! @@ -18,10 +18,10 @@ extern ConVar mat_fullbright; -DEFINE_FALLBACK_SHADER( Cable, Cable_DX9 ) +DEFINE_FALLBACK_SHADER( SDK_Cable, SDK_Cable_DX9 ) -BEGIN_VS_SHADER( Cable_DX9, - "Help for Cable shader" ) +BEGIN_VS_SHADER( SDK_Cable_DX9, + "Help for SDK_Cable shader" ) BEGIN_SHADER_PARAMS SHADER_PARAM( BUMPMAP, SHADER_PARAM_TYPE_TEXTURE, "cable/cablenormalmap", "bumpmap texture" ) SHADER_PARAM( MINLIGHT, SHADER_PARAM_TYPE_FLOAT, "0.1", "Minimum amount of light (0-1 value)" ) @@ -73,18 +73,18 @@ BEGIN_VS_SHADER( Cable_DX9, VERTEX_POSITION | VERTEX_COLOR | VERTEX_TANGENT_S | VERTEX_TANGENT_T, 2, tCoordDimensions, 0 ); - DECLARE_STATIC_VERTEX_SHADER( cable_vs20 ); - SET_STATIC_VERTEX_SHADER( cable_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_cable_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_cable_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( cable_ps20b ); - SET_STATIC_PIXEL_SHADER( cable_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_cable_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_cable_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( cable_ps20 ); - SET_STATIC_PIXEL_SHADER( cable_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_cable_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_cable_ps20 ); } // we are writing linear values from this shader. @@ -118,22 +118,22 @@ BEGIN_VS_SHADER( Cable_DX9, vEyePos_SpecExponent[3] = 0.0f; pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); - DECLARE_DYNAMIC_VERTEX_SHADER( cable_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_cable_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); - SET_DYNAMIC_VERTEX_SHADER( cable_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_cable_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( cable_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_cable_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( cable_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_cable_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( cable_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_cable_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( cable_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_cable_ps20 ); } } Draw(); diff --git a/mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.cpp b/mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.cpp new file mode 100644 index 00000000..a6f3084c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.cpp @@ -0,0 +1,358 @@ +//========= Copyright � 1996-2006, Valve Corporation, All rights reserved. ============// + +/* Example how to plug this into an existing shader: + + In the VMT: + // Cloak Pass + "$cloakPassEnabled" "1" + + #include "cloak_blended_pass_helper.h" + + In BEGIN_SHADER_PARAMS: + // Cloak Pass + SHADER_PARAM( CLOAKPASSENABLED, SHADER_PARAM_TYPE_BOOL, "0", "Enables cloak render in a second pass" ) + SHADER_PARAM( CLOAKFACTOR, SHADER_PARAM_TYPE_FLOAT, "0.0", "" ) + SHADER_PARAM( CLOAKCOLORTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "Cloak color tint" ) + SHADER_PARAM( REFRACTAMOUNT, SHADER_PARAM_TYPE_FLOAT, "2", "" ) + + // This should already exist + //SHADER_PARAM( BUMPMAP, SHADER_PARAM_TYPE_TEXTURE, "models/shadertest/shader1_normal", "bump map" ) + //SHADER_PARAM( BUMPFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $bumpmap" ) + //SHADER_PARAM( BUMPTRANSFORM, SHADER_PARAM_TYPE_MATRIX, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$bumpmap texcoord transform" ) + + Add this above SHADER_INIT_PARAMS() + // Cloak Pass + void SetupVarsCloakBlendedPass( CloakBlendedPassVars_t &info ) + { + info.m_nCloakFactor = CLOAKFACTOR; + info.m_nCloakColorTint = CLOAKCOLORTINT; + info.m_nRefractAmount = REFRACTAMOUNT; + + // Delete these lines if not bump mapping! + info.m_nBumpmap = BUMPMAP; + info.m_nBumpFrame = BUMPFRAME; + info.m_nBumpTransform = BUMPTRANSFORM; + } + + bool NeedsPowerOfTwoFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame ) const + { + if ( params[CLOAKPASSENABLED]->GetIntValue() ) // If material supports cloaking + { + if ( bCheckSpecificToThisFrame == false ) // For setting model flag at load time + return true; + else if ( ( params[CLOAKFACTOR]->GetFloatValue() > 0.0f ) && ( params[CLOAKFACTOR]->GetFloatValue() < 1.0f ) ) // Per-frame check + return true; + // else, not cloaking this frame, so check flag2 in case the base material still needs it + } + + // Check flag2 if not drawing cloak pass + return IS_FLAG2_SET( MATERIAL_VAR2_NEEDS_POWER_OF_TWO_FRAME_BUFFER_TEXTURE ); + } + + bool IsTranslucent( IMaterialVar **params ) const + { + if ( params[CLOAKPASSENABLED]->GetIntValue() ) // If material supports cloaking + { + if ( ( params[CLOAKFACTOR]->GetFloatValue() > 0.0f ) && ( params[CLOAKFACTOR]->GetFloatValue() < 1.0f ) ) // Per-frame check + return true; + // else, not cloaking this frame, so check flag in case the base material still needs it + } + + // Check flag if not drawing cloak pass + return IS_FLAG_SET( MATERIAL_VAR_TRANSLUCENT ); + } + + In SHADER_INIT_PARAMS() + // Cloak Pass + if ( !params[CLOAKPASSENABLED]->IsDefined() ) + { + params[CLOAKPASSENABLED]->SetIntValue( 0 ); + } + else if ( params[CLOAKPASSENABLED]->GetIntValue() ) + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + InitParamsCloakBlendedPass( this, params, pMaterialName, info ); + } + + In SHADER_INIT + // Cloak Pass + if ( params[CLOAKPASSENABLED]->GetIntValue() ) + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + InitCloakBlendedPass( this, params, info ); + } + + Modify SHADER_DRAW to look something like this: + // Skip the standard rendering if cloak pass is fully opaque + bool bDrawStandardPass = true; + if ( params[CLOAKPASSENABLED]->GetIntValue() && ( pShaderShadow == NULL ) ) // && not snapshotting + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + if ( CloakBlendedPassIsFullyOpaque( params, info ) ) + { + bDrawStandardPass = false; + } + } + + // Standard rendering pass + if ( bDrawStandardPass ) + { + Eye_Refract_Vars_t info; + SetupVarsEyeRefract( info ); + Draw_Eyes_Refract( this, params, pShaderAPI, pShaderShadow, info ); + } + else + { + // Skip this pass! + Draw( false ); + } + + // Cloak Pass + if ( params[CLOAKPASSENABLED]->GetIntValue() ) + { + // If ( snapshotting ) or ( we need to draw this frame ) + if ( ( pShaderShadow != NULL ) || ( ( params[CLOAKFACTOR]->GetFloatValue() > 0.0f ) && ( params[CLOAKFACTOR]->GetFloatValue() < 1.0f ) ) ) + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + DrawCloakBlendedPass( this, params, pShaderAPI, pShaderShadow, info ); + } + else // We're not snapshotting and we don't need to draw this frame + { + // Skip this pass! + Draw( false ); + } + } + +==================================================================================================== */ + +#include "BaseVSShader.h" +#include "mathlib/vmatrix.h" +#include "cloak_blended_pass_helper.h" +#include "convar.h" + +// Auto generated inc files +#include "SDK_cloak_blended_pass_vs20.inc" +#include "SDK_cloak_blended_pass_ps20.inc" +#include "SDK_cloak_blended_pass_ps20b.inc" + +#ifndef _X360 +#include "SDK_cloak_blended_pass_vs30.inc" +#include "SDK_cloak_blended_pass_ps30.inc" +#endif + +void InitParamsCloakBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, CloakBlendedPassVars_t &info ) +{ + // Set material flags + SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_HW_SKINNING ); + SET_FLAGS( MATERIAL_VAR_MODEL ); + SET_FLAGS2( MATERIAL_VAR2_NEEDS_TANGENT_SPACES ); + + // Set material parameter default values + if ( ( info.m_nCloakFactor != -1 ) && ( !params[info.m_nCloakFactor]->IsDefined() ) ) + { + params[info.m_nCloakFactor]->SetFloatValue( kDefaultCloakFactor ); + } + + if ( ( info.m_nRefractAmount != -1 ) && ( !params[info.m_nRefractAmount]->IsDefined() ) ) + { + params[info.m_nRefractAmount]->SetFloatValue( kDefaultRefractAmount ); + } + + if ( ( info.m_nCloakColorTint != -1 ) && ( !params[info.m_nCloakColorTint]->IsDefined() ) ) + { + params[info.m_nCloakColorTint]->SetVecValue( kDefaultCloakColorTint[0], kDefaultCloakColorTint[1], kDefaultCloakColorTint[2], kDefaultCloakColorTint[3] ); + } + + if( (info.m_nBumpFrame != -1 ) && !params[info.m_nBumpFrame]->IsDefined() ) + { + params[info.m_nBumpFrame]->SetIntValue( 0 ); + } +} + +void InitCloakBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, CloakBlendedPassVars_t &info ) +{ + // Load textures + if ( g_pConfig->UseBumpmapping() ) + { + if ( (info.m_nBumpmap != -1) && params[info.m_nBumpmap]->IsDefined() ) + { + pShader->LoadTexture( info.m_nBumpmap ); + } + } +} + +void DrawCloakBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, IShaderDynamicAPI *pShaderAPI, + IShaderShadow* pShaderShadow, CloakBlendedPassVars_t &info, VertexCompressionType_t vertexCompression ) +{ + bool bBumpMapping = ( !g_pConfig->UseBumpmapping() ) || ( info.m_nBumpmap == -1 ) || !params[info.m_nBumpmap]->IsTexture() ? 0 : 1; + + SHADOW_STATE + { + // Reset shadow state manually since we're drawing from two materials + pShader->SetInitialShadowState( ); + + // Set stream format (note that this shader supports compression) + unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_COMPRESSED; + int nTexCoordCount = 1; + int userDataSize = 0; + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, NULL, userDataSize ); + +#ifndef _X360 + if ( !g_pHardwareConfig->HasFastVertexTextures() ) +#endif + { + // Vertex Shader + DECLARE_STATIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( BUMPMAP, bBumpMapping ? 1 : 0 ); + SET_STATIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs20 ); + + // Pixel Shader + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bBumpMapping ? 1 : 0 ); + SET_STATIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bBumpMapping ? 1 : 0 ); + SET_STATIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20 ); + } + } +#ifndef _X360 + else + { + // The vertex shader uses the vertex id stream + SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + + // Vertex Shader + DECLARE_STATIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( BUMPMAP, bBumpMapping ? 1 : 0 ); + SET_STATIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs30 ); + + // Pixel Shader + DECLARE_STATIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps30 ); + SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bBumpMapping ? 1 : 0 ); + SET_STATIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps30 ); + } +#endif + + // Textures + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); // Refraction texture + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + if ( bBumpMapping ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); // Bump + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, false ); // Not sRGB + } + pShaderShadow->EnableSRGBWrite( true ); + + // Blending + pShader->EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA ); + pShaderShadow->EnableAlphaWrites( false ); + + // !!! We need to turn this back on because EnableAlphaBlending() above disables it! + pShaderShadow->EnableDepthWrites( true ); + } + DYNAMIC_STATE + { + // Reset render state manually since we're drawing from two materials + pShaderAPI->SetDefaultState(); + + // Set Vertex Shader Constants + if ( ( bBumpMapping ) && ( info.m_nBumpTransform != -1 ) ) + { + pShader->SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, info.m_nBumpTransform ); + } + +#ifndef _X360 + if ( !g_pHardwareConfig->HasFastVertexTextures() ) +#endif + { + // Set Vertex Shader Combos + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs20 ); + + // Set Pixel Shader Combos + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps20 ); + } + } +#ifndef _X360 + else + { + pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); + + // Set Vertex Shader Combos + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_cloak_blended_pass_vs30 ); + + // Set Pixel Shader Combos + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_cloak_blended_pass_ps30 ); + } +#endif + + // Bind textures + pShaderAPI->BindStandardTexture( SHADER_SAMPLER0, TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0 ); // Refraction Map + if ( bBumpMapping ) + { + pShader->BindTexture( SHADER_SAMPLER1, info.m_nBumpmap, info.m_nBumpFrame ); + } + + // Set Pixel Shader Constants + float vEyePos[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos ); + pShaderAPI->SetPixelShaderConstant( 5, vEyePos, 1 ); + + float vPackedConst1[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vPackedConst1[0] = IS_PARAM_DEFINED( info.m_nCloakFactor ) ? params[info.m_nCloakFactor]->GetFloatValue() : kDefaultCloakFactor; + vPackedConst1[1] = IS_PARAM_DEFINED( info.m_nRefractAmount ) ? params[info.m_nRefractAmount]->GetFloatValue() : kDefaultRefractAmount; + pShaderAPI->SetPixelShaderConstant( 6, vPackedConst1, 1 ); + + // Refract color tint + pShaderAPI->SetPixelShaderConstant( 7, IS_PARAM_DEFINED( info.m_nCloakColorTint ) ? params[info.m_nCloakColorTint]->GetVecValue() : kDefaultCloakColorTint, 1 ); + + // Set c0 and c1 to contain first two rows of ViewProj matrix + VMatrix mView, mProj; + pShaderAPI->GetMatrix( MATERIAL_VIEW, mView.m[0] ); + pShaderAPI->GetMatrix( MATERIAL_PROJECTION, mProj.m[0] ); + VMatrix mViewProj = mView * mProj; + mViewProj = mViewProj.Transpose3x3(); + pShaderAPI->SetPixelShaderConstant( 0, mViewProj.m[0], 2 ); + } + pShader->Draw(); +} + +bool CloakBlendedPassIsFullyOpaque ( IMaterialVar** params, CloakBlendedPassVars_t &info ) +{ + float flCloakFactor = IS_PARAM_DEFINED( info.m_nCloakFactor ) ? params[info.m_nCloakFactor]->GetFloatValue() : kDefaultCloakFactor; + //float flRefractAmount = IS_PARAM_DEFINED( info.m_nRefractAmount ) ? params[info.m_nRefractAmount]->GetFloatValue() : kDefaultRefractAmount; + + // NOTE: If this math changes, you need to update the pixel shader code! + float flFresnel = 1.0f - ( 0.0f ); // Assume V.N = 0.0f; + float flCloakLerpFactor = clamp( Lerp( clamp( flCloakFactor, 0.0f, 1.0f ), 1.0f, flFresnel - 1.35f ), 0.0f, 1.0f ); + //flCloakLerpFactor = 1.0f - smoothstep( 0.4f, 0.425f, flCloakLerpFactor ); + + if ( flCloakLerpFactor <= 0.4f ) + return true; + + return false; +} diff --git a/mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.h b/mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.h new file mode 100644 index 00000000..2d566a66 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/cloak_blended_pass_helper.h @@ -0,0 +1,46 @@ +//========= Copyright ? 1996-2006, Valve Corporation, All rights reserved. ============// + +#ifndef CLOAK_BLENDED_PASS_HELPER_H +#define CLOAK_BLENDED_PASS_HELPER_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CBaseVSShader; +class IMaterialVar; +class IShaderDynamicAPI; +class IShaderShadow; + +//----------------------------------------------------------------------------- +// Init params/ init/ draw methods +//----------------------------------------------------------------------------- +struct CloakBlendedPassVars_t +{ + CloakBlendedPassVars_t() { memset( this, 0xFF, sizeof(CloakBlendedPassVars_t) ); } + + int m_nCloakFactor; + int m_nCloakColorTint; + int m_nRefractAmount; + + int m_nBumpmap; + int m_nBumpFrame; + int m_nBumpTransform; +}; + +// Default values (Arrays should only be vec[4]) +static const float kDefaultCloakFactor = 0.0f; +static const float kDefaultCloakColorTint[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; +static const float kDefaultRefractAmount = 0.1f; + +void InitParamsCloakBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, CloakBlendedPassVars_t &info ); +void InitCloakBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, CloakBlendedPassVars_t &info ); +void DrawCloakBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, IShaderDynamicAPI *pShaderAPI, + IShaderShadow* pShaderShadow, CloakBlendedPassVars_t &info, VertexCompressionType_t vertexCompression ); +bool CloakBlendedPassIsFullyOpaque ( IMaterialVar** params, CloakBlendedPassVars_t &info ); + +#endif // CLOAK_BLENDED_PASS_HELPER_H \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/common_flashlight_fxc.h b/mp/src/materialsystem/stdshaders/common_flashlight_fxc.h index 098f1848..c14f6c03 100644 --- a/mp/src/materialsystem/stdshaders/common_flashlight_fxc.h +++ b/mp/src/materialsystem/stdshaders/common_flashlight_fxc.h @@ -10,6 +10,9 @@ #include "common_ps_fxc.h" +#if defined(SHADER_MODEL_PS_3_0) +#define NEW_SHADOW_FILTERS // Comment if you want to enable retail shadow filter. +#endif // JasonM - TODO: remove this simpleton version float DoShadow( sampler DepthSampler, float4 texCoord ) @@ -100,12 +103,16 @@ float DoShadowNvidiaCheap( sampler DepthSampler, const float4 shadowMapPos ) return dot(vTaps, float4(0.25, 0.25, 0.25, 0.25)); } +#if defined( NEW_SHADOW_FILTERS ) +float DoShadowNvidiaPCF3x3Box( sampler DepthSampler, const float3 shadowMapPos ) +#else float DoShadowNvidiaPCF3x3Box( sampler DepthSampler, const float4 shadowMapPos ) +#endif { - float fTexelEpsilon = 1.0f / 1024.0f; + float fTexelEpsilon = 1.0f / 512.0f; - float ooW = 1.0f / shadowMapPos.w; // 1 / w - float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once + //float ooW = 1.0f; //1.0f / shadowMapPos.w; // 1 / w + float3 shadowMapCenter_objDepth = shadowMapPos.xyz; // * ooW; // Do both projections at once float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space @@ -138,61 +145,80 @@ float DoShadowNvidiaPCF3x3Box( sampler DepthSampler, const float4 shadowMapPos ) // 4 20 33 20 4 // 1 4 7 4 1 // +#if defined( NEW_SHADOW_FILTERS ) +float DoShadowNvidiaPCF5x5Gaussian( sampler DepthSampler, const float3 shadowMapPos, const float2 vShadowTweaks ) +#else float DoShadowNvidiaPCF5x5Gaussian( sampler DepthSampler, const float4 shadowMapPos ) +#endif { - float fEpsilon = 1.0f / 512.0f; - float fTwoEpsilon = 2.0f * fEpsilon; +#if defined( NEW_SHADOW_FILTERS ) + float fEpsilonX = vShadowTweaks.x; + float fTwoEpsilonX = 2.0f * fEpsilonX; + float fEpsilonY = vShadowTweaks.y; + float fTwoEpsilonY = 2.0f * fEpsilonY; +#else + float fEpsilonX = 1.0 / 512.0; + float fTwoEpsilonX = 2.0f * fEpsilonX; + float fEpsilonY = fEpsilonX; + float fTwoEpsilonY = fTwoEpsilonX; +#endif +#if defined( NEW_SHADOW_FILTERS ) + // I guess we don't need this one. + // float ooW = 1.0f / shadowMapPos.w; // 1 / w + float3 shadowMapCenter_objDepth = shadowMapPos; // Do both projections at once +#else float ooW = 1.0f / shadowMapPos.w; // 1 / w float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once +#endif float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space float4 vOneTaps; - vOneTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x; - vOneTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x; - vOneTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x; - vOneTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x; + vOneTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilonX, fTwoEpsilonY ), objDepth, 1 ) ).x; + vOneTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilonX, fTwoEpsilonY ), objDepth, 1 ) ).x; + vOneTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilonX, -fTwoEpsilonY ), objDepth, 1 ) ).x; + vOneTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilonX, -fTwoEpsilonY ), objDepth, 1 ) ).x; float flOneTaps = dot( vOneTaps, float4(1.0f / 331.0f, 1.0f / 331.0f, 1.0f / 331.0f, 1.0f / 331.0f)); float4 vSevenTaps; - vSevenTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, 0 ), objDepth, 1 ) ).x; - vSevenTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, 0 ), objDepth, 1 ) ).x; - vSevenTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTwoEpsilon ), objDepth, 1 ) ).x; - vSevenTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTwoEpsilon ), objDepth, 1 ) ).x; + vSevenTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilonX, 0 ), objDepth, 1 ) ).x; + vSevenTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilonX, 0 ), objDepth, 1 ) ).x; + vSevenTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, fTwoEpsilonY ), objDepth, 1 ) ).x; + vSevenTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTwoEpsilonY ), objDepth, 1 ) ).x; float flSevenTaps = dot( vSevenTaps, float4( 7.0f / 331.0f, 7.0f / 331.0f, 7.0f / 331.0f, 7.0f / 331.0f ) ); float4 vFourTapsA, vFourTapsB; - vFourTapsA.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, fEpsilon ), objDepth, 1 ) ).x; - vFourTapsA.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x; - vFourTapsA.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x; - vFourTapsA.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, fEpsilon ), objDepth, 1 ) ).x; - vFourTapsB.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, -fEpsilon ), objDepth, 1 ) ).x; - vFourTapsB.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x; - vFourTapsB.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x; - vFourTapsB.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, -fEpsilon ), objDepth, 1 ) ).x; + vFourTapsA.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilonX, fEpsilonY ), objDepth, 1 ) ).x; + vFourTapsA.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilonX, fTwoEpsilonY ), objDepth, 1 ) ).x; + vFourTapsA.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilonX, fTwoEpsilonY ), objDepth, 1 ) ).x; + vFourTapsA.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilonX, fEpsilonY ), objDepth, 1 ) ).x; + vFourTapsB.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilonX, -fEpsilonY ), objDepth, 1 ) ).x; + vFourTapsB.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilonX, -fTwoEpsilonY ), objDepth, 1 ) ).x; + vFourTapsB.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilonX, -fTwoEpsilonY ), objDepth, 1 ) ).x; + vFourTapsB.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilonX, -fEpsilonY ), objDepth, 1 ) ).x; float flFourTapsA = dot( vFourTapsA, float4( 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f ) ); float flFourTapsB = dot( vFourTapsB, float4( 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f ) ); float4 v20Taps; - v20Taps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, fEpsilon ), objDepth, 1 ) ).x; - v20Taps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, fEpsilon ), objDepth, 1 ) ).x; - v20Taps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, -fEpsilon ), objDepth, 1 ) ).x; - v20Taps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, -fEpsilon ), objDepth, 1 ) ).x; + v20Taps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilonX, fEpsilonY ), objDepth, 1 ) ).x; + v20Taps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilonX, fEpsilonY ), objDepth, 1 ) ).x; + v20Taps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilonX, -fEpsilonY ), objDepth, 1 ) ).x; + v20Taps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilonX, -fEpsilonY ), objDepth, 1 ) ).x; float fl20Taps = dot( v20Taps, float4(20.0f / 331.0f, 20.0f / 331.0f, 20.0f / 331.0f, 20.0f / 331.0f)); float4 v33Taps; - v33Taps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, 0 ), objDepth, 1 ) ).x; - v33Taps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, 0 ), objDepth, 1 ) ).x; - v33Taps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fEpsilon ), objDepth, 1 ) ).x; - v33Taps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fEpsilon ), objDepth, 1 ) ).x; + v33Taps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilonX, 0 ), objDepth, 1 ) ).x; + v33Taps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilonX, 0 ), objDepth, 1 ) ).x; + v33Taps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, fEpsilonY ), objDepth, 1 ) ).x; + v33Taps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fEpsilonY ), objDepth, 1 ) ).x; float fl33Taps = dot( v33Taps, float4(33.0f / 331.0f, 33.0f / 331.0f, 33.0f / 331.0f, 33.0f / 331.0f)); float flCenterTap = tex2Dproj( DepthSampler, float4( shadowMapCenter, objDepth, 1 ) ).x * (55.0f / 331.0f); // Sum all 25 Taps - return flOneTaps + flSevenTaps + +flFourTapsA + flFourTapsB + fl20Taps + fl33Taps + flCenterTap; + return flOneTaps + flSevenTaps + flFourTapsA + flFourTapsB + fl20Taps + fl33Taps + flCenterTap; } @@ -601,7 +627,12 @@ float DoFlashlightShadow( sampler DepthSampler, sampler RandomRotationSampler, f #if !defined( _X360 ) //PC if( nShadowLevel == NVIDIA_PCF_POISSON ) +#if defined( NEW_SHADOW_FILTERS ) && defined( SHADER_MODEL_PS_3_0 ) + // Let's replace noise filter with gaussian blur, like in Portal 2. + flShadow = DoShadowNvidiaPCF5x5Gaussian( DepthSampler, vProjCoords, float2( vShadowTweaks.x, vShadowTweaks.x ) ); +#else flShadow = DoShadowPoisson16Sample( DepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, true, false ); +#endif else if( nShadowLevel == ATI_NOPCF ) flShadow = DoShadowPoisson16Sample( DepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, false ); else if( nShadowLevel == ATI_NO_PCF_FETCH4 ) @@ -660,6 +691,12 @@ void DoSpecularFlashlight( float3 flashlightPos, float3 worldPos, float4 flashli { float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w; float3 flashlightColor = float3(1,1,1); + + // Blixibon - Fix for flashlight textures without Clamp S/T +#if defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) + clip( vProjCoords.xyz ); + clip( 1-vProjCoords.xyz ); +#endif #if ( defined( _X360 ) ) @@ -691,6 +728,7 @@ void DoSpecularFlashlight( float3 flashlightPos, float3 worldPos, float4 flashli #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) + flashlightColor *= flashlightSpacePosition.w > 0; flashlightColor *= cFlashlightColor.xyz; // Flashlight color #endif @@ -702,7 +740,7 @@ void DoSpecularFlashlight( float3 flashlightPos, float3 worldPos, float4 flashli float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f ); // Attenuation for light and to fade out shadow over distance - float fAtten = saturate( dot( attenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); + float fAtten = saturate( endFalloffFactor * dot( attenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); // Shadowing and coloring terms #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)) @@ -722,7 +760,9 @@ void DoSpecularFlashlight( float3 flashlightPos, float3 worldPos, float4 flashli diffuseLighting *= saturate( dot( L.xyz, worldNormal.xyz ) ); // Lambertian (not Half-Lambert) term #endif diffuseLighting *= flashlightColor; - diffuseLighting *= endFalloffFactor; + + // Blixibon - Now calculating endFalloffFactor directly from fAtten + //diffuseLighting *= endFalloffFactor; // Specular term (masked by diffuse) specularLighting = diffuseLighting * SpecularLight ( worldNormal, L, fSpecularExponent, vEyeDir, bDoSpecularWarp, specularWarpSampler, fFresnel ); @@ -734,29 +774,28 @@ float3 DoFlashlight( float3 flashlightPos, float3 worldPos, float4 flashlightSpa sampler RandomRotationSampler, int nShadowLevel, bool bDoShadows, bool bAllowHighQuality, const float2 vScreenPos, bool bClip, float4 vShadowTweaks = float4(3/1024.0f, 0.0005f, 0.0f, 0.0f), bool bHasNormal = true ) { - float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w; - float3 flashlightColor = float3(1,1,1); - -#if ( defined( _X360 ) ) - - float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f ); - float3 gto = vProjCoords.xyz > float3( 1.0f, 1.0f, 1.0f ); - - [branch] - if ( dot(ltz + gto, float3(1,1,1)) > 0 ) + if ( flashlightSpacePosition.w < 0 ) { - if ( bClip ) - { - clip(-1); - } return float3(0,0,0); } else { - flashlightColor = tex2D( FlashlightSampler, vProjCoords ); + float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w; + float3 flashlightColor = float3(1,1,1); + + // Blixibon - Fix for flashlight textures without Clamp S/T +#if defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) + clip( vProjCoords.xyz ); + clip( 1-vProjCoords.xyz ); +#endif + +#if ( defined( _X360 ) ) + + float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f ); + float3 gto = vProjCoords.xyz > float3( 1.0f, 1.0f, 1.0f ); [branch] - if ( dot(flashlightColor.xyz, float3(1,1,1)) <= 0 ) + if ( dot(ltz + gto, float3(1,1,1)) > 0 ) { if ( bClip ) { @@ -764,58 +803,74 @@ float3 DoFlashlight( float3 flashlightPos, float3 worldPos, float4 flashlightSpa } return float3(0,0,0); } - } + else + { + flashlightColor = tex2D( FlashlightSampler, vProjCoords ); + + [branch] + if ( dot(flashlightColor.xyz, float3(1,1,1)) <= 0 ) + { + if ( bClip ) + { + clip(-1); + } + return float3(0,0,0); + } + } #else - flashlightColor = tex2D( FlashlightSampler, vProjCoords ); + flashlightColor = tex2D( FlashlightSampler, vProjCoords ); #endif #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) - flashlightColor *= cFlashlightColor.xyz; // Flashlight color + flashlightColor *= cFlashlightColor.xyz; // Flashlight color #endif - float3 delta = flashlightPos - worldPos; - float3 L = normalize( delta ); - float distSquared = dot( delta, delta ); - float dist = sqrt( distSquared ); + float3 delta = flashlightPos - worldPos; + float3 L = normalize( delta ); + float distSquared = dot( delta, delta ); + float dist = sqrt( distSquared ); - float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f ); + float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f ); - // Attenuation for light and to fade out shadow over distance - float fAtten = saturate( dot( attenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); + // Attenuation for light and to fade out shadow over distance + float fAtten = saturate( endFalloffFactor * dot( attenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) ); - // Shadowing and coloring terms + // Shadowing and coloring terms #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)) - if ( bDoShadows ) - { - float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality ); - float flAttenuated = lerp( flShadow, 1.0f, vShadowTweaks.y ); // Blend between fully attenuated and not attenuated - flShadow = saturate( lerp( flAttenuated, flShadow, fAtten ) ); // Blend between shadow and above, according to light attenuation - flashlightColor *= flShadow; // Shadow term - } + if ( bDoShadows ) + { + float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality ); + float flAttenuated = lerp( flShadow, 1.0f, vShadowTweaks.y ); // Blend between fully attenuated and not attenuated + flShadow = saturate( lerp( flAttenuated, flShadow, fAtten ) ); // Blend between shadow and above, according to light attenuation + flashlightColor *= flShadow; // Shadow term + } #endif - float3 diffuseLighting = fAtten; + float3 diffuseLighting = fAtten; - float flLDotWorldNormal; - if ( bHasNormal ) - { - flLDotWorldNormal = dot( L.xyz, worldNormal.xyz ); - } - else - { - flLDotWorldNormal = 1.0f; - } + float flLDotWorldNormal; + if ( bHasNormal ) + { + flLDotWorldNormal = dot( L.xyz, worldNormal.xyz ); + } + else + { + flLDotWorldNormal = 1.0f; + } #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0) - diffuseLighting *= saturate( flLDotWorldNormal + flFlashlightNoLambertValue ); // Lambertian term + diffuseLighting *= saturate( flLDotWorldNormal + flFlashlightNoLambertValue ); // Lambertian term #else - diffuseLighting *= saturate( flLDotWorldNormal ); // Lambertian (not Half-Lambert) term + diffuseLighting *= saturate( flLDotWorldNormal ); // Lambertian (not Half-Lambert) term #endif - diffuseLighting *= flashlightColor; - diffuseLighting *= endFalloffFactor; + diffuseLighting *= flashlightColor; + + // Blixibon - Now calculating endFalloffFactor directly from fAtten + //diffuseLighting *= endFalloffFactor; - return diffuseLighting; + return diffuseLighting; + } } -#endif //#ifndef COMMON_FLASHLIGHT_FXC_H_ +#endif //#ifndef COMMON_FLASHLIGHT_FXC_H_ \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/common_fxc.h b/mp/src/materialsystem/stdshaders/common_fxc.h index 2103b3f9..65f0802d 100644 --- a/mp/src/materialsystem/stdshaders/common_fxc.h +++ b/mp/src/materialsystem/stdshaders/common_fxc.h @@ -323,4 +323,18 @@ float3 Vec3TangentToWorldNormalized( float3 iTangentVector, float3 iWorldNormal, return normalize( Vec3TangentToWorld( iTangentVector, iWorldNormal, iWorldTangent, iWorldBinormal ) ); } +// returns 1.0f for no fog, 0.0f for fully fogged +float CalcRangeFogFactorFixedFunction( float3 worldPos, float3 eyePos, float flFogMaxDensity, float flFogEndOverRange, float flFogOORange ) +{ + float dist = distance( eyePos.xyz, worldPos.xyz ); + return max( flFogMaxDensity, ( -dist * flFogOORange ) + flFogEndOverRange ); +} + +// returns 0.0f for no fog, 1.0f for fully fogged which is opposite of what fixed function fog expects so that we don't have to do a "1-x" in the pixel shader. +float CalcRangeFogFactorNonFixedFunction( float3 worldPos, float3 eyePos, float flFogMaxDensity, float flFogEndOverRange, float flFogOORange ) +{ + float dist = distance( eyePos.xyz, worldPos.xyz ); + return min( flFogMaxDensity, saturate( ( dist * flFogOORange ) - flFogEndOverRange ) ); +} + #endif //#ifndef COMMON_FXC_H_ diff --git a/mp/src/materialsystem/stdshaders/common_ps_fxc.h b/mp/src/materialsystem/stdshaders/common_ps_fxc.h index fca6511f..c7a1a059 100644 --- a/mp/src/materialsystem/stdshaders/common_ps_fxc.h +++ b/mp/src/materialsystem/stdshaders/common_ps_fxc.h @@ -192,7 +192,7 @@ float4 DecompressNormal( sampler NormalSampler, float2 tc, int nDecompressionMod HALF3 NormalizeWithCubemap( sampler normalizeSampler, HALF3 input ) { // return texCUBE( normalizeSampler, input ) * 2.0f - 1.0f; - return texCUBE( normalizeSampler, input ); + return texCUBE( normalizeSampler, input ).xyz; } /* @@ -209,8 +209,17 @@ HALF4 EnvReflect( sampler envmapSampler, } */ +// Vectorized smoothstep for doing three smoothsteps at once. Used by uberlight +float3 smoothstep3( float3 edge0, float3 edge1, float3 OneOverWidth, float3 x ) +{ + x = saturate((x - edge0) * OneOverWidth); // Scale, bias and saturate x to the range of zero to one + return x*x*(3-2*x); // Evaluate polynomial +} + float CalcWaterFogAlpha( const float flWaterZ, const float flEyePosZ, const float flWorldPosZ, const float flProjPosZ, const float flFogOORange ) { +#if 0 + // This version is what you use if you want a line-integral throught he water for water fog. // float flDepthFromWater = flWaterZ - flWorldPosZ + 2.0f; // hackity hack . .this is for the DF_FUDGE_UP in view_scene.cpp float flDepthFromWater = flWaterZ - flWorldPosZ; @@ -226,6 +235,13 @@ float CalcWaterFogAlpha( const float flWaterZ, const float flEyePosZ, const floa // $tmp.w is now the distance that we see through water. return saturate(f * flProjPosZ * flFogOORange); +#else + // This version is simply using the depth of the water to determine fog factor, + // which is cheaper than doing the line integral and also fixes some problems with having + // a hard line on the shore when the water surface is viewed tangentially. + // hackity hack . .the 2.0 is for the DF_FUDGE_UP in view_scene.cpp + return saturate( ( flWaterZ - flWorldPosZ - 2.0f ) * flFogOORange ); +#endif } float CalcRangeFog( const float flProjPosZ, const float flFogStartOverRange, const float flFogMaxDensity, const float flFogOORange ) @@ -237,20 +253,24 @@ float CalcRangeFog( const float flProjPosZ, const float flFogStartOverRange, con #endif } -float CalcPixelFogFactor( int iPIXELFOGTYPE, const float4 fogParams, const float flEyePosZ, const float flWorldPosZ, const float flProjPosZ ) +float CalcPixelFogFactor( int iPIXELFOGTYPE, const float4 fogParams, const float3 vEyePos, const float3 vWorldPos, const float flProjPosZ ) { - float retVal; - if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE ) + float retVal = 0; + /*if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE ) { retVal = 0.0f; - } + }*/ if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE ) //range fog, or no fog depending on fog parameters { - retVal = CalcRangeFog( flProjPosZ, fogParams.x, fogParams.z, fogParams.w ); + //retVal = CalcRangeFog( flProjPosZ, fogParams.x, fogParams.z, fogParams.w ); + float flFogMaxDensity = fogParams.z; + float flFogEndOverRange = fogParams.x; + float flFogOORange = fogParams.w; + retVal = CalcRangeFogFactorNonFixedFunction( vWorldPos, vEyePos, flFogMaxDensity, flFogEndOverRange, flFogOORange ); } else if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT ) //height fog { - retVal = CalcWaterFogAlpha( fogParams.y, flEyePosZ, flWorldPosZ, flProjPosZ, fogParams.w ); + retVal = CalcWaterFogAlpha( fogParams.y, vEyePos.z, vWorldPos.z, flProjPosZ, fogParams.w ); } return retVal; @@ -264,23 +284,25 @@ float CalcPixelFogFactor( int iPIXELFOGTYPE, const float4 fogParams, const float float3 BlendPixelFog( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, const int iPIXELFOGTYPE ) { + float3 flRet = 0; if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE ) //either range fog or no fog depending on fog parameters and whether this is ps20 or ps2b { # if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) //Minimum requirement of ps2b pixelFogFactor = saturate( pixelFogFactor ); - return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog + flRet = lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog # else - return vShaderColor; + flRet = vShaderColor; # endif } else if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT ) { - return lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); + flRet = lerp( vShaderColor.rgb, vFogColor.rgb, saturate( pixelFogFactor ) ); } else if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE ) { - return vShaderColor; + flRet = vShaderColor; } + return flRet; } diff --git a/mp/src/materialsystem/stdshaders/common_splinerope_fxc.h b/mp/src/materialsystem/stdshaders/common_splinerope_fxc.h new file mode 100644 index 00000000..0893d1d2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/common_splinerope_fxc.h @@ -0,0 +1,26 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +#ifdef PIXELSHADER + #define VS_OUTPUT PS_INPUT +#endif + +struct VS_OUTPUT +{ +#ifndef PIXELSHADER + float4 projPos : POSITION; +#endif + + float2 texCoord : TEXCOORD0; + float4 worldPos_projPosZ : TEXCOORD1; + float4 argbcolor : COLOR; + +#ifndef PIXELSHADER + #if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) + float fog : FOG; + #endif +#endif +}; + +#ifdef PIXELSHADER + #undef VS_OUTPUT +#endif diff --git a/mp/src/materialsystem/stdshaders/common_vertexlitgeneric_dx9.h b/mp/src/materialsystem/stdshaders/common_vertexlitgeneric_dx9.h index 2eb7bb3c..c1260591 100644 --- a/mp/src/materialsystem/stdshaders/common_vertexlitgeneric_dx9.h +++ b/mp/src/materialsystem/stdshaders/common_vertexlitgeneric_dx9.h @@ -116,7 +116,7 @@ float3 DiffuseTerm(const bool bHalfLambert, const float3 worldNormal, const floa float3 fOut = float3( fResult, fResult, fResult ); if ( bDoLightingWarp ) { - fOut = 2.0f * tex1D( lightWarpSampler, fResult ); + fOut = 2.0f * tex1D( lightWarpSampler, fResult ).xyz; } return fOut; @@ -146,7 +146,7 @@ float3 PixelShaderGetLightVector( const float3 worldPos, PixelShaderLightInfo cL } else { - return normalize( cLightInfo[nLightIndex].pos - worldPos ); + return normalize( cLightInfo[nLightIndex].pos.xyz - worldPos ); } } @@ -181,7 +181,7 @@ void SpecularAndRimTerms( const float3 vWorldNormal, const float3 vLightDir, con // Optionally warp as function of scalar specular and fresnel if ( bDoSpecularWarp ) - specularLighting *= tex2D( specularWarpSampler, float2(specularLighting.x, fFresnel) ); // Sample at { (L.R)^k, fresnel } + specularLighting *= tex2D( specularWarpSampler, float2(specularLighting.x, fFresnel) ).xyz; // Sample at { (L.R)^k, fresnel } specularLighting *= saturate(dot( vWorldNormal, vLightDir )); // Mask with N.L specularLighting *= color; // Modulate with light color @@ -286,19 +286,19 @@ float3 PixelShaderDoLightingLinear( const float3 worldPos, const float3 worldNor if ( nNumLights > 0 ) { linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.x, worldPos, worldNormal, NormalizeSampler, - cLightInfo[0].pos, cLightInfo[0].color, bHalfLambert, + cLightInfo[0].pos.xyz, cLightInfo[0].color.xyz, bHalfLambert, bDoAmbientOcclusion, fAmbientOcclusion, bDoLightingWarp, lightWarpSampler ); if ( nNumLights > 1 ) { linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.y, worldPos, worldNormal, NormalizeSampler, - cLightInfo[1].pos, cLightInfo[1].color, bHalfLambert, + cLightInfo[1].pos.xyz, cLightInfo[1].color.xyz, bHalfLambert, bDoAmbientOcclusion, fAmbientOcclusion, bDoLightingWarp, lightWarpSampler ); if ( nNumLights > 2 ) { linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.z, worldPos, worldNormal, NormalizeSampler, - cLightInfo[2].pos, cLightInfo[2].color, bHalfLambert, + cLightInfo[2].pos.xyz, cLightInfo[2].color.xyz, bHalfLambert, bDoAmbientOcclusion, fAmbientOcclusion, bDoLightingWarp, lightWarpSampler ); if ( nNumLights > 3 ) diff --git a/mp/src/materialsystem/stdshaders/common_vs_fxc.h b/mp/src/materialsystem/stdshaders/common_vs_fxc.h index ac966b68..291118a4 100644 --- a/mp/src/materialsystem/stdshaders/common_vs_fxc.h +++ b/mp/src/materialsystem/stdshaders/common_vs_fxc.h @@ -91,8 +91,8 @@ const float4 cViewProjZ : register(c13); const float4 cFogParams : register(c16); #define cFogEndOverFogRange cFogParams.x -#define cFogOne cFogParams.y -#define cFogMaxDensity cFogParams.z +// cFogParams.y unused +#define cRadialFogMaxDensity cFogParams.z //radial fog max density in fractional portion. height fog max density stored in integer portion and is multiplied by 1e10 #define cOOFogRange cFogParams.w const float4x4 cViewModel : register(c17); @@ -519,8 +519,41 @@ bool ApplyMorph( sampler2D morphSampler, const float3 vMorphTargetTextureDim, co #endif // SHADER_MODEL_VS_3_0 +float CalcFixedFunctionFog( const float3 worldPos, const bool bWaterFog ) +{ + if( !bWaterFog ) + { + return CalcRangeFogFactorFixedFunction( worldPos, cEyePos, cRadialFogMaxDensity, cFogEndOverFogRange, cOOFogRange ); + } + else + { + return 0.0f; //all done in the pixel shader as of ps20 (current min-spec) + } +} -float RangeFog( const float3 projPos ) +float CalcFixedFunctionFog( const float3 worldPos, const int fogType ) +{ + return CalcFixedFunctionFog( worldPos, fogType != FOGTYPE_RANGE ); +} + +float CalcNonFixedFunctionFog( const float3 worldPos, const bool bWaterFog ) +{ + if( !bWaterFog ) + { + return CalcRangeFogFactorNonFixedFunction( worldPos, cEyePos, cRadialFogMaxDensity, cFogEndOverFogRange, cOOFogRange ); + } + else + { + return 0.0f; //all done in the pixel shader as of ps20 (current min-spec) + } +} + +float CalcNonFixedFunctionFog( const float3 worldPos, const int fogType ) +{ + return CalcNonFixedFunctionFog( worldPos, fogType != FOGTYPE_RANGE ); +} + +/*float RangeFog( const float3 projPos ) { return max( cFogMaxDensity, ( -projPos.z * cOOFogRange + cFogEndOverFogRange ) ); } @@ -547,11 +580,11 @@ float WaterFog( const float3 worldPos, const float3 projPos ) // $tmp.w is now the distance that we see through water. return max( cFogMaxDensity, ( -tmp.w * cOOFogRange + cFogOne ) ); -} +}*/ float CalcFog( const float3 worldPos, const float3 projPos, const int fogType ) { -#if defined( _X360 ) +/*#if defined( _X360 ) // 360 only does pixel fog return 1.0f; #endif @@ -568,12 +601,13 @@ float CalcFog( const float3 worldPos, const float3 projPos, const int fogType ) #else return WaterFog( worldPos, projPos ); #endif - } + }*/ + return CalcFixedFunctionFog( worldPos, fogType ); } float CalcFog( const float3 worldPos, const float3 projPos, const bool bWaterFog ) { -#if defined( _X360 ) +/*#if defined( _X360 ) // 360 only does pixel fog return 1.0f; #endif @@ -593,7 +627,8 @@ float CalcFog( const float3 worldPos, const float3 projPos, const bool bWaterFog #endif } - return flFog; + return flFog;*/ + return CalcFixedFunctionFog( worldPos, bWaterFog ); } float4 DecompressBoneWeights( const float4 weights ) diff --git a/mp/src/materialsystem/stdshaders/decalmodulate.cpp b/mp/src/materialsystem/stdshaders/decalmodulate.cpp new file mode 100644 index 00000000..6dd829b1 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/decalmodulate.cpp @@ -0,0 +1,63 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#include "shaderlib/CShader.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DEFINE_FALLBACK_SHADER( DecalModulate, DecalModulate_DX6 ) + +BEGIN_SHADER( DecalModulate_dx6, + "Help for DecalModulate_dx6" ) + + BEGIN_SHADER_PARAMS + END_SHADER_PARAMS + + SHADER_INIT_PARAMS() + { + SET_FLAGS( MATERIAL_VAR_NO_DEBUG_OVERRIDE ); + } + + SHADER_INIT + { + LoadTexture( BASETEXTURE ); + } + + SHADER_DRAW + { + SHADOW_STATE + { + pShaderShadow->EnableAlphaTest( true ); + pShaderShadow->AlphaFunc( SHADER_ALPHAFUNC_GREATER, 0.0f ); + pShaderShadow->EnableDepthWrites( false ); + pShaderShadow->EnablePolyOffset( SHADER_POLYOFFSET_DECAL ); + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableBlending( true ); + pShaderShadow->BlendFunc( SHADER_BLEND_DST_COLOR, SHADER_BLEND_SRC_COLOR ); + pShaderShadow->DrawFlags( SHADER_DRAW_POSITION | SHADER_DRAW_TEXCOORD0 ); + FogToGrey(); + } + DYNAMIC_STATE + { + // This is kinda gross. We really don't want to render anything here for the flashlight + // pass since we are multiplying by what is already flashlight lit in the framebuffer. + // There is no easy way to draw nothing conditionally, so I'll bind grey and multiply + // which shouldn't change the contents of the framebuffer much. + if( pShaderAPI->InFlashlightMode() ) + { + pShaderAPI->BindStandardTexture( SHADER_SAMPLER0, TEXTURE_GREY ); + } + else + { + BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME ); + } + } + Draw( ); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/decalmodulate_dx9.cpp b/mp/src/materialsystem/stdshaders/decalmodulate_dx9.cpp new file mode 100644 index 00000000..a4b1c170 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/decalmodulate_dx9.cpp @@ -0,0 +1,331 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#include "BaseVSShader.h" +#include "cpp_shader_constant_register_map.h" + +#include "SDK_decalmodulate_vs20.inc" +#include "SDK_decalmodulate_ps20.inc" +#include "SDK_decalmodulate_ps20b.inc" + +#ifndef _X360 +#include "SDK_decalmodulate_vs30.inc" +#include "SDK_decalmodulate_ps30.inc" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef MAPBASE +ConVar mat_decalmodulate_flashdraw( "mat_decalmodulate_flashdraw", "0" ); +#endif + +DEFINE_FALLBACK_SHADER( SDK_DecalModulate, SDK_DecalModulate_DX9 ) + +BEGIN_VS_SHADER( SDK_DecalModulate_dx9, + "Help for SDK_DecalModulate_dx9" ) + + BEGIN_SHADER_PARAMS + SHADER_PARAM( FOGEXPONENT, SHADER_PARAM_TYPE_FLOAT, "0.4", "exponent to tweak fog fade" ) + SHADER_PARAM( FOGSCALE, SHADER_PARAM_TYPE_FLOAT, "1.0", "scale to tweak fog fade" ) + END_SHADER_PARAMS + + SHADER_FALLBACK + { + return 0; + } + + SHADER_INIT_PARAMS() + { + if( !params[ FOGEXPONENT ]->IsDefined() ) + { + params[ FOGEXPONENT ]->SetFloatValue( 0.4f ); + } + + if( !params[ FOGSCALE ]->IsDefined() ) + { + params[ FOGSCALE ]->SetFloatValue( 1.0f ); + } + + SET_FLAGS( MATERIAL_VAR_NO_DEBUG_OVERRIDE ); + +#ifndef _X360 + if ( g_pHardwareConfig->HasFastVertexTextures() ) + { + // The vertex shader uses the vertex id stream + SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_HW_SKINNING ); + } +#endif + } + + SHADER_INIT + { + LoadTexture( BASETEXTURE ); + } + + SHADER_DRAW + { +#ifdef MAPBASE + // It is now believed the decals not appearing is a sorting issue. + // The flashlight part is transparent and overlaid on top of the decal. + // When a fix is found, this flashlight code could be removed. + bool bHasFlashlight = UsingFlashlight( params ); + if (bHasFlashlight && !mat_decalmodulate_flashdraw.GetBool()) + return; +#endif + SHADOW_STATE + { + pShaderShadow->EnableAlphaTest( true ); + pShaderShadow->AlphaFunc( SHADER_ALPHAFUNC_GREATER, 0.0f ); + pShaderShadow->EnableDepthWrites( false ); + pShaderShadow->EnablePolyOffset( SHADER_POLYOFFSET_DECAL ); + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + + // Be sure not to write to dest alpha + pShaderShadow->EnableAlphaWrites( false ); + + //SRGB conversions hose the blend on some hardware, so keep everything in gamma space. + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, false ); + pShaderShadow->EnableSRGBWrite( false ); + + pShaderShadow->EnableBlending( true ); + pShaderShadow->BlendFunc( SHADER_BLEND_DST_COLOR, SHADER_BLEND_SRC_COLOR ); + pShaderShadow->DisableFogGammaCorrection( true ); //fog should stay exactly middle grey + FogToGrey(); + +#ifdef MAPBASE + int userDataSize = 0; + int nShadowFilterMode = 0; + if ( bHasFlashlight ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER8, true ); // Depth texture + pShaderShadow->SetShadowDepthFiltering( SHADER_SAMPLER8 ); + pShaderShadow->EnableTexture( SHADER_SAMPLER6, true ); // Noise map + pShaderShadow->EnableTexture( SHADER_SAMPLER7, true ); // Flashlight cookie + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER7, true ); + userDataSize = 4; // tangent S + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + nShadowFilterMode = g_pHardwareConfig->GetShadowFilterMode(); // Based upon vendor and device dependent formats + } + } +#endif + + bool bHasVertexAlpha = IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR ) && IS_FLAG_SET( MATERIAL_VAR_VERTEXALPHA ); + +#ifndef _X360 + if ( !g_pHardwareConfig->HasFastVertexTextures() ) +#endif + { + DECLARE_STATIC_VERTEX_SHADER( sdk_decalmodulate_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, bHasVertexAlpha ); + SET_STATIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, false ); +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_decalmodulate_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_decalmodulate_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( VERTEXALPHA, bHasVertexAlpha ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_decalmodulate_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_decalmodulate_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( VERTEXALPHA, bHasVertexAlpha ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_decalmodulate_ps20 ); + } + } +#ifndef _X360 + else + { + DECLARE_STATIC_VERTEX_SHADER( sdk_decalmodulate_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, bHasVertexAlpha ); + SET_STATIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, false ); +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_decalmodulate_vs30 ); + + DECLARE_STATIC_PIXEL_SHADER( sdk_decalmodulate_ps30 ); + SET_STATIC_PIXEL_SHADER_COMBO( VERTEXALPHA, bHasVertexAlpha ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_decalmodulate_ps30 ); + } +#endif + + // Set stream format (note that this shader supports compression) + unsigned int flags = VERTEX_POSITION | VERTEX_FORMAT_COMPRESSED; + + if ( bHasVertexAlpha ) + { + flags |= VERTEX_COLOR; + } + +#ifndef _X360 + // The VS30 shader offsets decals along the normal (for morphed geom) + flags |= g_pHardwareConfig->HasFastVertexTextures() ? VERTEX_NORMAL : 0; +#endif + int pTexCoordDim[3] = { 2, 0, 3 }; + int nTexCoordCount = 1; +#ifndef MAPBASE + int userDataSize = 0; +#endif + +#ifndef _X360 + if ( g_pHardwareConfig->HasFastVertexTextures() ) + { +#ifdef MAPBASE + if (nTexCoordCount == 0) + nTexCoordCount = 3; +#else + nTexCoordCount = 3; +#endif + } +#endif + + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, pTexCoordDim, userDataSize ); + } + DYNAMIC_STATE + { +#ifdef MAPBASE // This fixes blood decals, etc. not showing up under flashlights. + //bHasFlashlight = pShaderAPI->InFlashlightMode(); + bool bFlashlightShadows = false; + if ( bHasFlashlight ) + { + VMatrix worldToTexture; + ITexture *pFlashlightDepthTexture; + FlashlightState_t state = pShaderAPI->GetFlashlightStateEx( worldToTexture, &pFlashlightDepthTexture ); + bFlashlightShadows = state.m_bEnableShadows && ( pFlashlightDepthTexture != NULL ); + + if( pFlashlightDepthTexture && g_pConfig->ShadowDepthTexture() && state.m_bEnableShadows ) + { + BindTexture( SHADER_SAMPLER8, pFlashlightDepthTexture, 0 ); + pShaderAPI->BindStandardTexture( SHADER_SAMPLER6, TEXTURE_SHADOW_NOISE_2D ); + } + + SetFlashLightColorFromState( state, pShaderAPI, 28 ); + + BindTexture( SHADER_SAMPLER7, state.m_pSpotlightTexture, state.m_nSpotlightTextureFrame ); + + float atten_pos[8]; + atten_pos[0] = state.m_fConstantAtten; // Set the flashlight attenuation factors + atten_pos[1] = state.m_fLinearAtten; + atten_pos[2] = state.m_fQuadraticAtten; + atten_pos[3] = state.m_FarZ; + atten_pos[4] = state.m_vecLightOrigin[0]; // Set the flashlight origin + atten_pos[5] = state.m_vecLightOrigin[1]; + atten_pos[6] = state.m_vecLightOrigin[2]; + atten_pos[7] = 1.0f; + pShaderAPI->SetPixelShaderConstant( 22, atten_pos, 2 ); + + pShaderAPI->SetPixelShaderConstant( 24, worldToTexture.Base(), 4 ); + } + + //if ( pShaderAPI->InFlashlightMode() && mat_decalmodulate_noflashdraw.GetBool() ) +#else + if ( pShaderAPI->InFlashlightMode() && !IsX360() ) + { + // Don't draw anything for the flashlight pass + Draw( false ); + return; + } +#endif + + BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME ); + + // Set an identity base texture transformation + Vector4D transformation[2]; + transformation[0].Init( 1.0f, 0.0f, 0.0f, 0.0f ); + transformation[1].Init( 0.0f, 1.0f, 0.0f, 0.0f ); + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, transformation[0].Base(), 2 ); + + pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); + + float vEyePos_SpecExponent[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent ); + vEyePos_SpecExponent[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); + + // fog tweaks + float fConsts[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + fConsts[0] = params[ FOGEXPONENT ]->GetFloatValue(); + fConsts[1] = params[ FOGSCALE ]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( 0, fConsts ); + + MaterialFogMode_t fogType = pShaderAPI->GetSceneFogMode(); + +#ifndef _X360 + if ( !g_pHardwareConfig->HasFastVertexTextures() ) +#endif + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_decalmodulate_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); +// SET_DYNAMIC_VERTEX_SHADER_COMBO( TESSELLATION, 0 ); // JasonM TODO: set this appropriately when we care about decals on subds + SET_DYNAMIC_VERTEX_SHADER( sdk_decalmodulate_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_decalmodulate_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); +#ifdef MAPBASE + SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); +#endif + SET_DYNAMIC_PIXEL_SHADER( sdk_decalmodulate_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_decalmodulate_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_decalmodulate_ps20 ); + } + } +#ifndef _X360 + else + { + SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_decalmodulate_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); +// SET_DYNAMIC_VERTEX_SHADER_COMBO( TESSELLATION, 0 ); // JasonM TODO: set this appropriately when we care about decals on subds + SET_DYNAMIC_VERTEX_SHADER( sdk_decalmodulate_vs30 ); + + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_decalmodulate_ps30 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); +#ifdef MAPBASE + SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); +#endif + SET_DYNAMIC_PIXEL_SHADER( sdk_decalmodulate_ps30 ); + + bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() }; + pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + } +#endif + } + Draw( ); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/depth_of_field_ps20b.fxc b/mp/src/materialsystem/stdshaders/depth_of_field_ps20b.fxc new file mode 100644 index 00000000..6eb5cb70 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/depth_of_field_ps20b.fxc @@ -0,0 +1,137 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// DYNAMIC: "QUALITY" "0..3" + +// Includes ======================================================================================= +#include "common_ps_fxc.h" + +// Texture Samplers =============================================================================== +sampler g_tFullFB : register( s0 ); +sampler g_tSmallFB : register( s1 ); + +// Shaders Constants and Globals ================================================================== +float4 g_vDists : register( c0 ); +#define g_flNearBlurDist g_vDists.x +#define g_flNearFocusDist g_vDists.y +#define g_flFarFocusDist g_vDists.z +#define g_flFarBlurDist g_vDists.w + +float3 g_vBlurAmounts : register( c1 ); +#define g_flMaxBlurRadius g_vBlurAmounts.x +#define g_flNearBlurStrength g_vBlurAmounts.y +#define g_flFarBlurStrength g_vBlurAmounts.z + +float3 g_vNearFarDists : register( c2 ); +#define g_flNearPlaneDist g_vNearFarDists.x +#define g_flFarPlaneDist g_vNearFarDists.y +#define g_flDepthConv g_vNearFarDists.z + +float4 g_vMagicConsts : register( c3 ); + +#if ( QUALITY == 0 ) + #define NUM_SAMPLES 8 // These must match the C code +#elif ( QUALITY == 1 ) + #define NUM_SAMPLES 16 +#elif ( QUALITY == 2 ) + #define NUM_SAMPLES 16 +#elif ( QUALITY == 3 ) + #define NUM_SAMPLES 32 +#endif + +float4 g_vPoisson[ NUM_SAMPLES/2 ] : register( c4 ); + +// Interpolated values ============================================================================ +struct PS_INPUT +{ + float2 vUv0 : TEXCOORD0; +}; + +float DestAlphaDepthToViewSpaceDepth( float flDepth ) +{ + return g_flDepthConv * flDepth + g_flNearPlaneDist; +} + +// returns blur radius from depth as a fraction of max_blur. +float BlurAmountFromDepth( float flDestAlphaDepth ) +{ + /* + dist = DestAlphaDepthToViewSpaceDepth( flDestAlphaDepth ); + float flBlur = max( g_flNearBlurStrength * saturate( (flDestAlphaDepth - g_flNearFocusDist) / ( g_flNearBlurDist - g_flNearFocusDist ) ), + g_flFarBlurStrength * saturate( (flDestAlphaDepth - g_flFarFocusDist) / ( g_flFarBlurDist - g_flFarFocusDist ) ) ); + */ + + // A more optimized version that concatenates the math above and the one in DestAlphaDepthToViewSpaceDepth to a single muladd + float flBlur = max( g_flNearBlurStrength * saturate( g_vMagicConsts.x * flDestAlphaDepth + g_vMagicConsts.y ), + g_flFarBlurStrength * saturate( g_vMagicConsts.z * flDestAlphaDepth + g_vMagicConsts.w ) ); + return flBlur; +} + +float BlurRadiusFromDepth( float flDepth ) +{ + return g_flMaxBlurRadius * BlurAmountFromDepth( flDepth ); +} + +float4 ComputeTap( float flCenterDepth, float flCenterBlurRadius, float2 vUV, float2 vPoisson ) +{ + float4 cTapSmall; + float4 cTap; + float2 vPoissonUV = vUV.xy + flCenterBlurRadius * vPoisson.xy; + + cTapSmall = tex2D( g_tSmallFB, vPoissonUV.xy ); + cTap = tex2D( g_tFullFB, vPoissonUV.xy ); + + float flTapBlur = BlurAmountFromDepth( cTap.a ); // Maybe 50/50 mix between low and high here? + + cTap = lerp( cTap, cTapSmall, saturate( 2.2 * flTapBlur ) ); // TODO: tweak blur amount. + float flLerpedTapBlur = BlurAmountFromDepth( cTap.a ); + + float weight = ( cTap.a >= flCenterDepth ) ? 1.0 : ( flLerpedTapBlur*flLerpedTapBlur ); + return float4( cTap.rgb, 1 ) * weight; +} + +float4 ComputeTapHQ( float flCenterDepth, float flCenterBlurRadius, float2 vUV, float2 vPoisson ) +{ + float4 cTap; + + cTap = tex2D( g_tFullFB, vUV.xy + flCenterBlurRadius * vPoisson.xy ); + float flTapBlur = BlurAmountFromDepth( cTap.a ); + float weight = ( cTap.a >= flCenterDepth ) ? 1.0 : ( flTapBlur * flTapBlur ); + return float4( cTap.rgb, 1 ) * weight; +} + +// Main =========================================================================================== +float4 main( PS_INPUT i ) : COLOR +{ + // TODO: BETTER DOWNSAMPLE THAT TAKES DEPTH INTO ACCOUNT? + + float4 cOut = { 0, 0, 0, 0 }; + float4 cCenterTap = tex2D( g_tFullFB, i.vUv0 ); + float flCenterBlurRadius = BlurRadiusFromDepth( cCenterTap.a ); // circle of confusion radius for current pixel + cCenterTap.a -= 0.001; // z-bias to avoid strange banding artifact on almost orthogonal walls + +#if ( QUALITY < 2 ) + // ATI's Ruby1-style algorithm + for ( int t = 0; t < NUM_SAMPLES/2; t++ ) + { + cOut.rgba += ComputeTap( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].xy ); + cOut.rgba += ComputeTap( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].wz ); + } +#else + // Less fancy, with less fetches per tap and less math. Needs more samples to look smooth. + cOut = cCenterTap; + cOut.a = 1.0; // Use the center sample we just fetched + for ( int t = 0; t < NUM_SAMPLES/2; t++ ) + { + cOut.rgba += ComputeTapHQ( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].xy ); + cOut.rgba += ComputeTapHQ( cCenterTap.a, flCenterBlurRadius, i.vUv0, g_vPoisson[t].wz ); + } +#endif + //cOut.rgb = cOut.a / float(NUM_SAMPLES+1); + //cOut = lerp( tex2D( g_tFullFB, i.vUv0 ), tex2D( g_tSmallFB, i.vUv0 ).aaaa, 0.5 ); + if ( cOut.a > 0.0 ) + cOut.rgba /= cOut.a; + else + cOut.rgba = cCenterTap.rgba; + + return cOut; +} diff --git a/mp/src/materialsystem/stdshaders/depth_of_field_vs20.fxc b/mp/src/materialsystem/stdshaders/depth_of_field_vs20.fxc new file mode 100644 index 00000000..37382c7d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/depth_of_field_vs20.fxc @@ -0,0 +1,29 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// Includes ======================================================================================= +#include "common_vs_fxc.h" + +// Input values =================================================================================== +struct VS_INPUT +{ + float3 vPos : POSITION; + float2 vBaseTexCoord : TEXCOORD0; +}; + +// Interpolated values ============================================================================ +struct VS_OUTPUT +{ + float4 projPos : POSITION; + float2 vUv0 : TEXCOORD0; +}; + +// Main =========================================================================================== +VS_OUTPUT main( const VS_INPUT i ) +{ + VS_OUTPUT o; + + o.projPos.xyzw = float4( i.vPos.xyz, 1.0f ); + o.vUv0.xy = i.vBaseTexCoord.xy; + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/depthoffield_dx9.cpp b/mp/src/materialsystem/stdshaders/depthoffield_dx9.cpp new file mode 100644 index 00000000..69650e74 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/depthoffield_dx9.cpp @@ -0,0 +1,280 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Depth of field material +// +//===========================================================================// + +#include "BaseVSShader.h" +#include "depth_of_field_vs20.inc" +#include "depth_of_field_ps20b.inc" +#include "convar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar mat_dof_max_blur_radius( "mat_dof_max_blur_radius", "50" ); +ConVar mat_dof_quality( "mat_dof_quality", "3" ); +ConVar mat_dof_constant( "mat_dof_constant", "512" ); + +// 8 samples +static const float s_flPoissonConstsQuality0[16] = { + 0.0, 0.0, + 0.527837, -0.085868, + -0.040088, 0.536087, + -0.670445, -0.179949, + -0.419418, -0.616039, + 0.440453, -0.639399, + -0.757088, 0.349334, + 0.574619, 0.685879 +}; + +// 16 samples +static const float s_flPoissonConstsQuality1[32] = { + 0.0747, -0.8341, + -0.9138, 0.3251, + 0.8667, -0.3029, + -0.4642, 0.2187, + -0.1505, 0.7320, + 0.7310, -0.6786, + 0.2859, -0.3254, + -0.1311, -0.2292, + 0.3518, 0.6470, + -0.7485, -0.6307, + 0.1687, 0.1873, + -0.3604, -0.7483, + -0.5658, -0.1521, + 0.7102, 0.0536, + -0.6056, 0.7747, + 0.7793, 0.6194 +}; + +// 32 samples +static const float s_flPoissonConstsQuality2[64] = { + 0.0854f, -0.0644f, + 0.8744f, 0.1665f, + 0.2329f, 0.3995f, + -0.7804f, 0.5482f, + -0.4577f, 0.7647f, + -0.1936f, 0.5564f, + 0.4205f, -0.5768f, + -0.0304f, -0.9050f, + -0.5215f, 0.1854f, + 0.3161f, -0.2954f, + 0.0666f, -0.5564f, + -0.2137f, -0.0072f, + -0.4112f, -0.3311f, + 0.6438f, -0.2484f, + -0.9055f, -0.0360f, + 0.8323f, 0.5268f, + 0.5592f, 0.3459f, + -0.6797f, -0.5201f, + -0.4325f, -0.8857f, + 0.8768f, -0.4197f, + 0.3090f, -0.8646f, + 0.5034f, 0.8603f, + 0.3752f, 0.0627f, + -0.0161f, 0.2627f, + 0.0969f, 0.7054f, + -0.2291f, -0.6595f, + -0.5887f, -0.1100f, + 0.7048f, -0.6528f, + -0.8438f, 0.2706f, + -0.5061f, 0.4653f, + -0.1245f, -0.3302f, + -0.1801f, 0.8486f +}; + +DEFINE_FALLBACK_SHADER( DepthOfField, DepthOfField_dx9 ) +BEGIN_VS_SHADER_FLAGS( DepthOfField_dx9, "Depth of Field", SHADER_NOT_EDITABLE ) + BEGIN_SHADER_PARAMS + SHADER_PARAM( SMALLFB, SHADER_PARAM_TYPE_TEXTURE, "_rt_SmallFB1", "Downsampled backbuffer" ) + SHADER_PARAM( NEARPLANE, SHADER_PARAM_TYPE_FLOAT, "0", "Near plane depth" ) + SHADER_PARAM( FARPLANE, SHADER_PARAM_TYPE_FLOAT, "0", "Far plane depth" ) + SHADER_PARAM( NEARBLURDEPTH, SHADER_PARAM_TYPE_FLOAT, "0", "Near blur plane depth" ) + SHADER_PARAM( NEARFOCUSDEPTH, SHADER_PARAM_TYPE_FLOAT, "0", "Near focus plane depth" ) + SHADER_PARAM( FARFOCUSDEPTH, SHADER_PARAM_TYPE_FLOAT, "0", "Far focus plane depth" ) + SHADER_PARAM( FARBLURDEPTH, SHADER_PARAM_TYPE_FLOAT, "0", "Far blur plane depth" ) + SHADER_PARAM( NEARBLURRADIUS, SHADER_PARAM_TYPE_FLOAT, "0", "Max near blur radius" ) + SHADER_PARAM( FARBLURRADIUS, SHADER_PARAM_TYPE_FLOAT, "0", "Max far blur radius" ) + SHADER_PARAM( QUALITY, SHADER_PARAM_TYPE_INTEGER, "0", "Quality level. Selects different algorithms." ) + END_SHADER_PARAMS + + SHADER_INIT_PARAMS() + { + SET_PARAM_STRING_IF_NOT_DEFINED( SMALLFB, "_rt_SmallFB1" ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( NEARPLANE, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( FARPLANE, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( NEARBLURDEPTH, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( NEARFOCUSDEPTH, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( FARFOCUSDEPTH, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( FARBLURDEPTH, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( NEARBLURRADIUS, 0.0f ); + SET_PARAM_FLOAT_IF_NOT_DEFINED( FARBLURRADIUS, 0.0f ); + SET_PARAM_INT_IF_NOT_DEFINED( QUALITY, 0 ); + } + + SHADER_FALLBACK + { + if ( g_pHardwareConfig->GetDXSupportLevel() < 92 ) + { + return "Wireframe"; + } + + return 0; + } + + SHADER_INIT + { + if ( params[BASETEXTURE]->IsDefined() ) + { + LoadTexture( BASETEXTURE ); + } + if ( params[SMALLFB]->IsDefined() ) + { + LoadTexture( SMALLFB ); + } + } + + SHADER_DRAW + { + SHADOW_STATE + { + pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 1, 0, 0 ); + + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, false ); + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, false ); + pShaderShadow->EnableSRGBWrite( false ); + + DECLARE_STATIC_VERTEX_SHADER( depth_of_field_vs20 ); + SET_STATIC_VERTEX_SHADER( depth_of_field_vs20 ); + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( depth_of_field_ps20b ); + SET_STATIC_PIXEL_SHADER( depth_of_field_ps20b ); + } + else + { + Assert( !"No ps_2_b. This shouldn't be happening" ); + } + + pShaderShadow->EnableDepthWrites( false ); + pShaderShadow->EnableAlphaWrites( false ); + } + + DYNAMIC_STATE + { + DECLARE_DYNAMIC_VERTEX_SHADER( depth_of_field_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( depth_of_field_vs20 ); + + // Bind textures + BindTexture( SHADER_SAMPLER0, BASETEXTURE ); + BindTexture( SHADER_SAMPLER1, SMALLFB ); + + // near blur = blur of stuff in front of focus range + // far blur = blur of stuff behind focus range + + // C0: set near/far blur and focus distances + // x = near blur distance + // y = near focus distance + // z = far focus distance + // w = far blur distance + // C1: + // x = blur radius for near blur (in pixels) + // y = blur radius for far blur (in pixels) + // TODO: Specifying this stuff in pixels makes blurs look smaller on high backbuffer resolutions. + // This might be a problem for tweaking these values. + float vConst[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + + vConst[0] = params[NEARBLURDEPTH]->GetFloatValue(); + vConst[1] = params[NEARFOCUSDEPTH]->GetFloatValue(); + vConst[2] = params[FARFOCUSDEPTH]->GetFloatValue(); + vConst[3] = params[FARBLURDEPTH]->GetFloatValue();; + // max blur radius will need to be set based on quality level and screen res + vConst[4] = mat_dof_max_blur_radius.GetFloat(); + vConst[5] = MIN( params[NEARBLURRADIUS]->GetFloatValue(), vConst[4] ) / vConst[4]; // near and far blur radius as fraction of max radius + vConst[6] = MIN( params[FARBLURRADIUS]->GetFloatValue(), vConst[4] ) / vConst[4]; + + vConst[8] = params[NEARPLANE]->GetFloatValue(); + vConst[9] = params[FARPLANE]->GetFloatValue(); + + vConst[10] = mat_dof_constant.GetFloat() * ( vConst[9] - vConst[8] ) / vConst[9]; + + vConst[12] = vConst[10] / ( vConst[0] - vConst[1] ); + vConst[13] = ( vConst[8] - vConst[1] ) / ( vConst[0] - vConst[1] ); + vConst[14] = vConst[10] / ( vConst[3] - vConst[2] ); + vConst[15] = ( vConst[8] - vConst[2] ) / ( vConst[3] - vConst[2] ); + + pShaderAPI->SetPixelShaderConstant( 0, vConst, 4 ); + + // set up poisson sample location constants pre-divided by screen res + int nNumPoissonSamples = 0; + const float *pPoissonSrc = NULL; + switch ( params[QUALITY]->GetIntValue() ) + { + case 0: + // NOTE: These must match the shader + nNumPoissonSamples = 8; + pPoissonSrc = s_flPoissonConstsQuality0; + break; + + case 1: + case 2: + nNumPoissonSamples = 16; + pPoissonSrc = s_flPoissonConstsQuality1; + break; + + case 3: + nNumPoissonSamples = 32; + pPoissonSrc = s_flPoissonConstsQuality2; + break; + + default: + Warning( "Invalid mat_dof_quality value. Resetting to 0.\n" ); + mat_dof_quality.SetValue( 0 ); + nNumPoissonSamples = 8; + pPoissonSrc = s_flPoissonConstsQuality0; + break; + } + + float vPoissonConst[64]; // temp table + + // Get texture dimensions + ITexture *pTex = params[BASETEXTURE]->GetTextureValue(); + Assert( pTex ); + float flInvTexWidth = 1.0f / static_cast( pTex->GetActualWidth() ); + float flInvTexHeight = 1.0f / static_cast( pTex->GetActualHeight() ); + + for ( int i = 0; i < nNumPoissonSamples; i++ ) + { + vPoissonConst[ 2*i ] = pPoissonSrc[ 2*i ] * flInvTexWidth; + vPoissonConst[ 2*i+1 ] = pPoissonSrc[ 2*i+1 ] * flInvTexHeight; + } + + // swizzle every other 2-tuple so that I can use the free .wz swizzle in the shader + for ( int i = 1; i < nNumPoissonSamples; i += 2) + { + float t = vPoissonConst[ 2*i ]; + vPoissonConst[ 2*i ] = vPoissonConst[ 2*i+1 ]; + vPoissonConst[ 2*i+1 ] = t; + } + + pShaderAPI->SetPixelShaderConstant( 4, vPoissonConst, nNumPoissonSamples / 2 ); + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( depth_of_field_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( QUALITY, params[QUALITY]->GetIntValue() ); + SET_DYNAMIC_PIXEL_SHADER( depth_of_field_ps20b ); + } + else + { + Assert( !"No ps_2_b. This shouldn't be happening" ); + } + } + + Draw(); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/depthwrite.cpp b/mp/src/materialsystem/stdshaders/depthwrite.cpp index 06d7690f..ecac59d7 100644 --- a/mp/src/materialsystem/stdshaders/depthwrite.cpp +++ b/mp/src/materialsystem/stdshaders/depthwrite.cpp @@ -8,20 +8,39 @@ #include "BaseVSShader.h" -#include "depthwrite_ps20.inc" -#include "depthwrite_ps20b.inc" -#include "depthwrite_vs20.inc" +#include "SDK_depthwrite_ps20.inc" +#include "SDK_depthwrite_ps20b.inc" +#include "SDK_depthwrite_vs20.inc" #if !defined( _X360 ) -#include "depthwrite_ps30.inc" -#include "depthwrite_vs30.inc" +#include "SDK_depthwrite_ps30.inc" +#include "SDK_depthwrite_vs30.inc" #endif -BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) +BEGIN_VS_SHADER_FLAGS( SDK_DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) BEGIN_SHADER_PARAMS SHADER_PARAM( ALPHATESTREFERENCE, SHADER_PARAM_TYPE_FLOAT, "", "Alpha reference value" ) SHADER_PARAM( COLOR_DEPTH, SHADER_PARAM_TYPE_BOOL, "0", "Write depth as color") + + // vertexlitgeneric tree sway animation control + SHADER_PARAM( TREESWAY, SHADER_PARAM_TYPE_INTEGER, "0", "" ) + SHADER_PARAM( TREESWAYHEIGHT, SHADER_PARAM_TYPE_FLOAT, "1000", "" ) + SHADER_PARAM( TREESWAYSTARTHEIGHT, SHADER_PARAM_TYPE_FLOAT, "0.2", "" ) + SHADER_PARAM( TREESWAYRADIUS, SHADER_PARAM_TYPE_FLOAT, "300", "" ) + SHADER_PARAM( TREESWAYSTARTRADIUS, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSPEED, SHADER_PARAM_TYPE_FLOAT, "1", "" ) + SHADER_PARAM( TREESWAYSPEEDHIGHWINDMULTIPLIER, SHADER_PARAM_TYPE_FLOAT, "2", "" ) + SHADER_PARAM( TREESWAYSTRENGTH, SHADER_PARAM_TYPE_FLOAT, "10", "" ) + SHADER_PARAM( TREESWAYSCRUMBLESPEED, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSCRUMBLESTRENGTH, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSCRUMBLEFREQUENCY, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYFALLOFFEXP, SHADER_PARAM_TYPE_FLOAT, "1.5", "" ) + SHADER_PARAM( TREESWAYSCRUMBLEFALLOFFEXP, SHADER_PARAM_TYPE_FLOAT, "1.0", "" ) + SHADER_PARAM( TREESWAYSPEEDLERPSTART, SHADER_PARAM_TYPE_FLOAT, "3", "" ) + SHADER_PARAM( TREESWAYSPEEDLERPEND, SHADER_PARAM_TYPE_FLOAT, "6", "" ) + SHADER_PARAM( TREESWAYSTATIC, SHADER_PARAM_TYPE_BOOL, "0", "" ) + SHADER_PARAM( TREESWAYSTATICVALUES, SHADER_PARAM_TYPE_VEC2, "[0.5 0.5]", "" ) END_SHADER_PARAMS SHADER_INIT_PARAMS() @@ -46,6 +65,7 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) { bool bAlphaClip = IS_FLAG_SET( MATERIAL_VAR_ALPHATEST ); int nColorDepth = GetIntParam( COLOR_DEPTH, params, 0 ); + int nTreeSwayMode = clamp( GetIntParam( TREESWAY, params, 0 ), 0, 2 ); SHADOW_STATE { @@ -76,10 +96,11 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_STATIC_VERTEX_SHADER( depthwrite_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_depthwrite_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( ONLY_PROJECT_POSITION, !bAlphaClip && IsX360() && !nColorDepth ); //360 needs to know if it *shouldn't* output texture coordinates to avoid shader patches SET_STATIC_VERTEX_SHADER_COMBO( COLOR_DEPTH, nColorDepth ); - SET_STATIC_VERTEX_SHADER( depthwrite_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( TREESWAY, nTreeSwayMode ); + SET_STATIC_VERTEX_SHADER( sdk_depthwrite_vs20 ); if ( bAlphaClip || g_pHardwareConfig->PlatformRequiresNonNullPixelShaders() || nColorDepth ) { @@ -91,15 +112,15 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( depthwrite_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_depthwrite_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( COLOR_DEPTH, nColorDepth ); - SET_STATIC_PIXEL_SHADER( depthwrite_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_depthwrite_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( depthwrite_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_depthwrite_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( COLOR_DEPTH, nColorDepth ); - SET_STATIC_PIXEL_SHADER( depthwrite_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_depthwrite_ps20 ); } } } @@ -108,17 +129,18 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) { SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( depthwrite_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_depthwrite_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( ONLY_PROJECT_POSITION, 0 ); //360 only combo, and this is a PC path SET_STATIC_VERTEX_SHADER_COMBO( COLOR_DEPTH, nColorDepth ); - SET_STATIC_VERTEX_SHADER( depthwrite_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( TREESWAY, nTreeSwayMode ); + SET_STATIC_VERTEX_SHADER( sdk_depthwrite_vs30 ); pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); - DECLARE_STATIC_PIXEL_SHADER( depthwrite_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_depthwrite_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( COLOR_DEPTH, nColorDepth ); - SET_STATIC_PIXEL_SHADER( depthwrite_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_depthwrite_ps30 ); } #endif } @@ -129,7 +151,7 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - depthwrite_vs20_Dynamic_Index vshIndex; + sdk_depthwrite_vs20_Dynamic_Index vshIndex; vshIndex.SetSKINNING( pShaderAPI->GetCurrentNumBones() > 0 ); vshIndex.SetCOMPRESSED_VERTS( (int)vertexCompression ); pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); @@ -149,15 +171,15 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( depthwrite_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_depthwrite_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( ALPHACLIP, bAlphaClip ); - SET_DYNAMIC_PIXEL_SHADER( depthwrite_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_depthwrite_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( depthwrite_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_depthwrite_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( ALPHACLIP, bAlphaClip ); - SET_DYNAMIC_PIXEL_SHADER( depthwrite_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_depthwrite_ps20 ); } } #ifndef _X360 @@ -165,7 +187,7 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) { SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); - depthwrite_vs30_Dynamic_Index vshIndex; + sdk_depthwrite_vs30_Dynamic_Index vshIndex; vshIndex.SetSKINNING( pShaderAPI->GetCurrentNumBones() > 0 ); vshIndex.SetMORPHING( pShaderAPI->IsHWMorphingEnabled() ); vshIndex.SetCOMPRESSED_VERTS( (int)vertexCompression ); @@ -184,12 +206,56 @@ BEGIN_VS_SHADER_FLAGS( DepthWrite, "Help for Depth Write", SHADER_NOT_EDITABLE ) pShaderAPI->SetPixelShaderConstant( 0, vAlphaThreshold, 1 ); } - DECLARE_DYNAMIC_PIXEL_SHADER( depthwrite_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_depthwrite_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( ALPHACLIP, bAlphaClip ); - SET_DYNAMIC_PIXEL_SHADER( depthwrite_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_depthwrite_ps30 ); } #endif + if ( nTreeSwayMode != 0 ) + { + float flParams[4]; + + flParams[0] = pShaderAPI->CurrentTime(); + if (params[TREESWAYSTATIC]->GetIntValue() == 0) + { + const Vector& windDir = pShaderAPI->GetVectorRenderingParameter( VECTOR_RENDERPARM_WIND_DIRECTION ); + flParams[1] = windDir.x; + flParams[2] = windDir.y; + } + else + { + // Use a static value instead of the env_wind value. + params[TREESWAYSTATICVALUES]->GetVecValue( flParams + 1, 2 ); + } + flParams[3] = 0.0f; + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, flParams ); + + flParams[0] = GetFloatParam( TREESWAYSCRUMBLEFALLOFFEXP, params, 1.0f ); + flParams[1] = GetFloatParam( TREESWAYFALLOFFEXP, params, 1.0f ); + flParams[2] = GetFloatParam( TREESWAYSCRUMBLESPEED, params, 3.0f ); + flParams[3] = GetFloatParam( TREESWAYSPEEDHIGHWINDMULTIPLIER, params, 2.0f ); + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_3, flParams ); + + flParams[0] = GetFloatParam( TREESWAYHEIGHT, params, 1000.0f ); + flParams[1] = GetFloatParam( TREESWAYSTARTHEIGHT, params, 0.1f ); + flParams[2] = GetFloatParam( TREESWAYRADIUS, params, 300.0f ); + flParams[3] = GetFloatParam( TREESWAYSTARTRADIUS, params, 0.2f ); + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, flParams ); + + flParams[0] = GetFloatParam( TREESWAYSPEED, params, 1.0f ); + flParams[1] = GetFloatParam( TREESWAYSTRENGTH, params, 10.0f ); + flParams[2] = GetFloatParam( TREESWAYSCRUMBLEFREQUENCY, params, 12.0f ); + flParams[3] = GetFloatParam( TREESWAYSCRUMBLESTRENGTH, params, 10.0f ); + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_5, flParams ); + + flParams[0] = GetFloatParam( TREESWAYSPEEDLERPSTART, params, 3.0f ); + flParams[1] = GetFloatParam( TREESWAYSPEEDLERPEND, params, 6.0f ); + flParams[2] = 0.0f; + flParams[3] = 0.0f; + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_9, flParams ); + } + Vector4D vParms; // set up arbitrary far planes, as the real ones are too far ( 30,000 ) diff --git a/mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.cpp b/mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.cpp new file mode 100644 index 00000000..a9fcce42 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.cpp @@ -0,0 +1,278 @@ +//========= Copyright � 1996-2006, Valve Corporation, All rights reserved. ============// + +/* Example how to plug this into an existing shader: + + In the VMT: + // Emissive Scroll Pass + "$emissiveBlendEnabled" "1" // Enables effect + "$emissiveBlendTexture" "models/vortigaunt/vortigaunt_illum" + "$emissiveBlendBaseTexture" "Models/Vortigaunt/vortigaunt_blue" + "$emissiveBlendFlowTexture" "models/vortigaunt/vortigaunt_flow" + "$emissiveBlendTint" "[10 10 10]" + "$emissiveBlendStrength" "1.0" // Set by game code + "$emissiveBlendScrollVector" "[0.11 0.124]" + "Proxies" + { + "VortEmissive" // For setting $selfillumstrength + { + } + } + + #include "emissive_scroll_blended_pass_helper.h" + + In BEGIN_SHADER_PARAMS: + // Emissive Scroll Pass + SHADER_PARAM( EMISSIVEBLENDENABLED, SHADER_PARAM_TYPE_BOOL, "0", "Enable emissive blend pass" ) + SHADER_PARAM( EMISSIVEBLENDBASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "self-illumination map" ) + SHADER_PARAM( EMISSIVEBLENDSCROLLVECTOR, SHADER_PARAM_TYPE_VEC2, "[0.11 0.124]", "Emissive scroll vec" ) + SHADER_PARAM( EMISSIVEBLENDSTRENGTH, SHADER_PARAM_TYPE_FLOAT, "1.0", "Emissive blend strength" ) + SHADER_PARAM( EMISSIVEBLENDTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "self-illumination map" ) + SHADER_PARAM( EMISSIVEBLENDTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "Self-illumination tint" ) + SHADER_PARAM( EMISSIVEBLENDFLOWTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "flow map" ) + + Add this above SHADER_INIT_PARAMS() + // Emissive Scroll Pass + void SetupVarsEmissiveScrollBlendedPass( EmissiveScrollBlendedPassVars_t &info ) + { + info.m_nBlendStrength = EMISSIVEBLENDSTRENGTH; + info.m_nBaseTexture = EMISSIVEBLENDBASETEXTURE; + info.m_nFlowTexture = EMISSIVEBLENDFLOWTEXTURE; + info.m_nEmissiveTexture = EMISSIVEBLENDTEXTURE; + info.m_nEmissiveTint = EMISSIVEBLENDTINT; + info.m_nEmissiveScrollVector = EMISSIVEBLENDSCROLLVECTOR; + } + + In SHADER_INIT_PARAMS() + // Emissive Scroll Pass + if ( !params[EMISSIVEBLENDENABLED]->IsDefined() ) + { + params[EMISSIVEBLENDENABLED]->SetIntValue( 0 ); + } + else if ( params[EMISSIVEBLENDENABLED]->GetIntValue() ) + { + EmissiveScrollBlendedPassVars_t info; + SetupVarsEmissiveScrollBlendedPass( info ); + InitParamsEmissiveScrollBlendedPass( this, params, pMaterialName, info ); + } + + In SHADER_INIT + // Emissive Scroll Pass + if ( params[EMISSIVEBLENDENABLED]->GetIntValue() ) + { + EmissiveScrollBlendedPassVars_t info; + SetupVarsEmissiveScrollBlendedPass( info ); + InitEmissiveScrollBlendedPass( this, params, info ); + } + + At the very end of SHADER_DRAW + // Emissive Scroll Pass + if ( params[EMISSIVEBLENDENABLED]->GetIntValue() ) + { + // If ( snapshotting ) or ( we need to draw this frame ) + if ( ( pShaderShadow != NULL ) || ( params[EMISSIVEBLENDSTRENGTH]->GetFloatValue() > 0.0f ) ) + { + EmissiveScrollBlendedPassVars_t info; + SetupVarsEmissiveScrollBlendedPass( info ); + DrawEmissiveScrollBlendedPass( this, params, pShaderAPI, pShaderShadow, info ); + } + else // We're not snapshotting and we don't need to draw this frame + { + // Skip this pass! + Draw( false ); + } + } + +==================================================================================================== */ + +#include "BaseVSShader.h" +#include "mathlib/vmatrix.h" +#include "emissive_scroll_blended_pass_helper.h" +#include "convar.h" + +// Auto generated inc files +#include "SDK_emissive_scroll_blended_pass_vs20.inc" +#include "SDK_emissive_scroll_blended_pass_ps20.inc" +#include "SDK_emissive_scroll_blended_pass_ps20b.inc" + +#ifndef _X360 +#include "SDK_emissive_scroll_blended_pass_vs30.inc" +#include "SDK_emissive_scroll_blended_pass_ps30.inc" +#endif + +void InitParamsEmissiveScrollBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, EmissiveScrollBlendedPassVars_t &info ) +{ + SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_HW_SKINNING ); + + if ( ( info.m_nEmissiveScrollVector != -1 ) && ( !params[info.m_nEmissiveScrollVector]->IsDefined() ) ) + { + params[info.m_nEmissiveScrollVector]->SetVecValue( kDefaultEmissiveScrollVector, 4 ); + } + + if ( ( info.m_nBlendStrength != -1 ) && ( !params[info.m_nBlendStrength]->IsDefined() ) ) + { + params[info.m_nBlendStrength]->SetFloatValue( kDefaultEmissiveBlendStrength ); + } + + if ( ( info.m_nEmissiveTint != -1 ) && ( !params[info.m_nEmissiveTint]->IsDefined() ) ) + { + params[info.m_nEmissiveTint]->SetVecValue( kDefaultEmissiveTint, 4 ); + } + + SET_PARAM_FLOAT_IF_NOT_DEFINED( info.m_nTime, 0.0f ); +} + +void InitEmissiveScrollBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, EmissiveScrollBlendedPassVars_t &info ) +{ + // Load textures + pShader->LoadTexture( info.m_nBaseTexture ); + pShader->LoadTexture( info.m_nFlowTexture ); + pShader->LoadTexture( info.m_nEmissiveTexture ); +} + +void DrawEmissiveScrollBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, IShaderDynamicAPI *pShaderAPI, + IShaderShadow* pShaderShadow, EmissiveScrollBlendedPassVars_t &info, VertexCompressionType_t vertexCompression ) +{ + SHADOW_STATE + { + // Reset shadow state manually since we're drawing from two materials + pShader->SetInitialShadowState(); + + // Set stream format (note that this shader supports compression) + unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_COMPRESSED; + int nTexCoordCount = 1; + int userDataSize = 0; + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, NULL, userDataSize ); + +#ifndef _X360 + if ( !g_pHardwareConfig->HasFastVertexTextures() ) +#endif + { + // Vertex Shader + DECLARE_STATIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs20 ); + + // Pixel Shader + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20 ); + } + } +#ifndef _X360 + else + { + // The vertex shader uses the vertex id stream + SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs30 ); + + DECLARE_STATIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps30 ); + } +#endif + + // Textures + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, false ); // Flow texture not sRGB + pShaderShadow->EnableTexture( SHADER_SAMPLER2, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER2, true ); + pShaderShadow->EnableSRGBWrite( true ); + + // Blending + pShader->EnableAlphaBlending( SHADER_BLEND_ONE, SHADER_BLEND_ONE ); + pShaderShadow->EnableAlphaWrites( false ); + } + DYNAMIC_STATE + { + // Reset render state manually since we're drawing from two materials + pShaderAPI->SetDefaultState(); + +#ifndef _X360 + if ( !g_pHardwareConfig->HasFastVertexTextures() ) +#endif + { + // Set Vertex Shader Combos + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs20 ); + + // Set Vertex Shader Constants + // None? + + // Set Pixel Shader Combos + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps20 ); + } + } +#ifndef _X360 + else + { + pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); + + // Set Vertex Shader Combos + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_emissive_scroll_blended_pass_vs30 ); + + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_emissive_scroll_blended_pass_ps30 ); + } +#endif + + // Bind textures + pShader->BindTexture( SHADER_SAMPLER0, info.m_nBaseTexture ); + pShader->BindTexture( SHADER_SAMPLER1, info.m_nFlowTexture ); + pShader->BindTexture( SHADER_SAMPLER2, info.m_nEmissiveTexture ); + + // Set Pixel Shader Constants + //float vConstZero[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + // This brings in the electricity and the second base texture when the second base texture is present + float vPsConst0[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + if (1) + { + // Overall blend strength + vPsConst0[0] = IS_PARAM_DEFINED( info.m_nBlendStrength ) ? params[info.m_nBlendStrength]->GetFloatValue() : kDefaultEmissiveBlendStrength; + if ( vPsConst0[0] < 0.0f ) + vPsConst0[0] = 0.0f; + if ( vPsConst0[0] > 1.0f ) + vPsConst0[0] = 1.0f; + + // Time % 1000 for scrolling + vPsConst0[1] = IS_PARAM_DEFINED( info.m_nTime ) && params[info.m_nTime]->GetFloatValue() > 0.0f ? params[info.m_nTime]->GetFloatValue() : pShaderAPI->CurrentTime(); + vPsConst0[1] -= (float)( (int)( vPsConst0[1] / 1000.0f ) ) * 1000.0f; + + // Dest alpha value for warping mask - NOTE: If we want to use this, we have to modify the blending mode above! + //if ( ( params[info.m_nWarpParam]->GetFloatValue() > 0.0f ) && ( params[info.m_nWarpParam]->GetFloatValue() < 1.0f ) ) + // tmpVec[2] = 1.0f; + //else + // tmpVec[2] = 0.0f; + } + pShaderAPI->SetPixelShaderConstant( 0, vPsConst0, 1 ); + + // Scroll vector + pShaderAPI->SetPixelShaderConstant( 1, IS_PARAM_DEFINED( info.m_nEmissiveScrollVector ) ? params[info.m_nEmissiveScrollVector]->GetVecValue() : kDefaultEmissiveScrollVector, 1 ); + + // Self illum tint + pShaderAPI->SetPixelShaderConstant( 2, IS_PARAM_DEFINED( info.m_nEmissiveTint ) ? params[info.m_nEmissiveTint]->GetVecValue() : kDefaultEmissiveTint, 1 ); + } + pShader->Draw(); +} diff --git a/mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.h b/mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.h new file mode 100644 index 00000000..8617fa6a --- /dev/null +++ b/mp/src/materialsystem/stdshaders/emissive_scroll_blended_pass_helper.h @@ -0,0 +1,45 @@ +//========= Copyright ? 1996-2006, Valve Corporation, All rights reserved. ============// + +#ifndef EMISSIVE_SCROLL_BLENDED_PASS_HELPER_H +#define EMISSIVE_SCROLL_BLENDED_PASS_HELPER_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CBaseVSShader; +class IMaterialVar; +class IShaderDynamicAPI; +class IShaderShadow; + +//----------------------------------------------------------------------------- +// Init params/ init/ draw methods +//----------------------------------------------------------------------------- +struct EmissiveScrollBlendedPassVars_t +{ + EmissiveScrollBlendedPassVars_t() { memset( this, 0xFF, sizeof(EmissiveScrollBlendedPassVars_t) ); } + + int m_nBlendStrength; // Amount this layer is blended in globally + int m_nBaseTexture; + int m_nFlowTexture; + int m_nEmissiveTexture; + int m_nEmissiveTint; + int m_nEmissiveScrollVector; + int m_nTime; +}; + +// Default values (Arrays should only be vec[4]) +static const float kDefaultEmissiveBlendStrength = 0.0f; +static const float kDefaultEmissiveTint[4] = { 1.0f, 1.0f, 1.0f, 0.0f } ; +static const float kDefaultEmissiveScrollVector[4] = { 0.11f, 0.124f, 0.0f, 0.0f } ; + +void InitParamsEmissiveScrollBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, EmissiveScrollBlendedPassVars_t &info ); +void InitEmissiveScrollBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, EmissiveScrollBlendedPassVars_t &info ); +void DrawEmissiveScrollBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, IShaderDynamicAPI *pShaderAPI, + IShaderShadow* pShaderShadow, EmissiveScrollBlendedPassVars_t &info, VertexCompressionType_t vertexCompression ); + +#endif // EMISSIVE_SCROLL_BLENDED_PASS_HELPER_H \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/engine_post_dx9.cpp b/mp/src/materialsystem/stdshaders/engine_post_dx9.cpp new file mode 100644 index 00000000..1c3cdc2b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/engine_post_dx9.cpp @@ -0,0 +1,636 @@ +//========= Copyright © 1996-2007, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "BaseVSShader.h" + +#include "SDK_screenspaceeffect_vs20.inc" +#include "SDK_engine_post_ps20b.inc" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +ConVar mat_screen_blur_override( "mat_screen_blur_override", "-1.0" ); +ConVar mat_depth_blur_focal_distance_override( "mat_depth_blur_focal_distance_override", "-1.0" ); +ConVar mat_depth_blur_strength_override( "mat_depth_blur_strength_override", "-1.0" ); +ConVar mat_grain_scale_override( "mat_grain_scale_override", "-1.0" ); +ConVar mat_local_contrast_scale_override( "mat_local_contrast_scale_override", "0.0" ); +ConVar mat_local_contrast_midtone_mask_override( "mat_local_contrast_midtone_mask_override", "-1.0" ); +ConVar mat_local_contrast_vignette_start_override( "mat_local_contrast_vignette_start_override", "-1.0" ); +ConVar mat_local_contrast_vignette_end_override( "mat_local_contrast_vignette_end_override", "-1.0" ); +ConVar mat_local_contrast_edge_scale_override( "mat_local_contrast_edge_scale_override", "-1000.0" ); + +DEFINE_FALLBACK_SHADER( SDK_Engine_Post, SDK_Engine_Post_dx9 ) +BEGIN_VS_SHADER_FLAGS( SDK_Engine_Post_dx9, "Engine post-processing effects (software anti-aliasing, bloom, color-correction", SHADER_NOT_EDITABLE ) + BEGIN_SHADER_PARAMS + SHADER_PARAM( FBTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_FullFrameFB", "Full framebuffer texture" ) + SHADER_PARAM( AAENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Enable software anti-aliasing" ) + SHADER_PARAM( AAINTERNAL1, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0]", "Internal anti-aliasing values set via material proxy" ) + SHADER_PARAM( AAINTERNAL2, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0]", "Internal anti-aliasing values set via material proxy" ) + SHADER_PARAM( AAINTERNAL3, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0]", "Internal anti-aliasing values set via material proxy" ) + SHADER_PARAM( BLOOMENABLE, SHADER_PARAM_TYPE_BOOL, "1", "Enable bloom" ) + SHADER_PARAM( BLOOMAMOUNT, SHADER_PARAM_TYPE_FLOAT, "1.0", "Bloom scale factor" ) + SHADER_PARAM( SCREENEFFECTTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "used for paint or vomit screen effect" ) + SHADER_PARAM( DEPTHBLURENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Inexpensive depth-of-field substitute" ) + + SHADER_PARAM( ALLOWVIGNETTE, SHADER_PARAM_TYPE_BOOL, "0", "Allow vignette" ) + SHADER_PARAM( VIGNETTEENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Enable vignette" ) + SHADER_PARAM( INTERNAL_VIGNETTETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "dev/vignette", "" ) + + SHADER_PARAM( ALLOWNOISE, SHADER_PARAM_TYPE_BOOL, "0", "Allow noise" ) + SHADER_PARAM( NOISEENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Enable noise" ) + SHADER_PARAM( NOISESCALE, SHADER_PARAM_TYPE_FLOAT, "0", "Noise scale" ) + SHADER_PARAM( NOISETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "Noise texture" ) + + SHADER_PARAM( ALLOWLOCALCONTRAST, SHADER_PARAM_TYPE_BOOL, "0", "Enable local contrast enhancement" ) + SHADER_PARAM( LOCALCONTRASTENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Enable local contrast enhancement" ) + SHADER_PARAM( LOCALCONTRASTSCALE, SHADER_PARAM_TYPE_FLOAT, "0", "Local contrast scale" ) + SHADER_PARAM( LOCALCONTRASTMIDTONEMASK, SHADER_PARAM_TYPE_FLOAT, "0", "Local contrast midtone mask" ) + SHADER_PARAM( LOCALCONTRASTVIGNETTESTART, SHADER_PARAM_TYPE_BOOL, "0", "Enable local contrast enhancement" ) + SHADER_PARAM( LOCALCONTRASTVIGNETTEEND, SHADER_PARAM_TYPE_FLOAT, "0", "Local contrast scale" ) + SHADER_PARAM( LOCALCONTRASTEDGESCALE, SHADER_PARAM_TYPE_FLOAT, "0", "Local contrast midtone mask" ) + + SHADER_PARAM( BLURREDVIGNETTEENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Enable blurred vignette" ) + SHADER_PARAM( BLURREDVIGNETTESCALE, SHADER_PARAM_TYPE_FLOAT, "0", "blurred vignette strength" ) + SHADER_PARAM( FADETOBLACKSCALE, SHADER_PARAM_TYPE_FLOAT, "0", "fade strength" ) + + SHADER_PARAM( DEPTHBLURFOCALDISTANCE, SHADER_PARAM_TYPE_FLOAT, "0", "Distance in dest-alpha space [0,1] of focal plane." ) + SHADER_PARAM( DEPTHBLURSTRENGTH, SHADER_PARAM_TYPE_FLOAT, "0", "Strength of depth-blur effect" ) + SHADER_PARAM( SCREENBLURSTRENGTH, SHADER_PARAM_TYPE_FLOAT, "0", "Full-screen blur factor" ) + + SHADER_PARAM( VOMITCOLOR1, SHADER_PARAM_TYPE_VEC3, "[0 0 0 0]", "1st vomit blend color" ) + SHADER_PARAM( VOMITCOLOR2, SHADER_PARAM_TYPE_VEC3, "[0 0 0 0]", "2st vomit blend color" ) + SHADER_PARAM( VOMITREFRACTSCALE, SHADER_PARAM_TYPE_FLOAT, "0.15", "vomit refract strength" ) + SHADER_PARAM( VOMITENABLE, SHADER_PARAM_TYPE_BOOL, "0", "Enable vomit refract" ) + + SHADER_PARAM( FADECOLOR, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0]", "viewfade color" ) + SHADER_PARAM( FADE, SHADER_PARAM_TYPE_INTEGER, "0", "fade type. 0 = off, 1 = lerp, 2 = modulate" ) + + SHADER_PARAM( TV_GAMMA, SHADER_PARAM_TYPE_INTEGER, "0", "0 default, 1 used for laying off 360 movies" ) + SHADER_PARAM( DESATURATEENABLE, SHADER_PARAM_TYPE_INTEGER, "0", "Desaturate with math, turns off color correction" ) + SHADER_PARAM( DESATURATION, SHADER_PARAM_TYPE_FLOAT, "0", "Desaturation Amount" ) + + // Tool color correction setup + SHADER_PARAM( TOOLMODE, SHADER_PARAM_TYPE_BOOL, "1", "tool mode" ) + SHADER_PARAM( TOOLCOLORCORRECTION, SHADER_PARAM_TYPE_FLOAT, "1", "tool color correction override" ) + SHADER_PARAM( WEIGHT_DEFAULT, SHADER_PARAM_TYPE_FLOAT, "1", "weight default" ) + SHADER_PARAM( WEIGHT0, SHADER_PARAM_TYPE_FLOAT, "1", "weight0" ) + SHADER_PARAM( WEIGHT1, SHADER_PARAM_TYPE_FLOAT, "1", "weight1" ) + SHADER_PARAM( WEIGHT2, SHADER_PARAM_TYPE_FLOAT, "1", "weight2" ) + SHADER_PARAM( WEIGHT3, SHADER_PARAM_TYPE_FLOAT, "1", "weight3" ) + SHADER_PARAM( NUM_LOOKUPS, SHADER_PARAM_TYPE_FLOAT, "0", "num_lookups" ) + SHADER_PARAM( TOOLTIME, SHADER_PARAM_TYPE_FLOAT, "0", "tooltime" ) + END_SHADER_PARAMS + + SHADER_INIT_PARAMS() + { + if ( !params[ INTERNAL_VIGNETTETEXTURE ]->IsDefined() ) + { + params[ INTERNAL_VIGNETTETEXTURE ]->SetStringValue( "dev/vignette" ); + } + if ( !params[ AAENABLE ]->IsDefined() ) + { + params[ AAENABLE ]->SetIntValue( 0 ); + } + if ( !params[ AAINTERNAL1 ]->IsDefined() ) + { + params[ AAINTERNAL1 ]->SetVecValue( 0, 0, 0, 0 ); + } + if ( !params[ AAINTERNAL2 ]->IsDefined() ) + { + params[ AAINTERNAL2 ]->SetVecValue( 0, 0, 0, 0 ); + } + if ( !params[ AAINTERNAL3 ]->IsDefined() ) + { + params[ AAINTERNAL3 ]->SetVecValue( 0, 0, 0, 0 ); + } + if ( !params[ BLOOMENABLE ]->IsDefined() ) + { + params[ BLOOMENABLE ]->SetIntValue( 1 ); + } + if ( !params[ BLOOMAMOUNT ]->IsDefined() ) + { + params[ BLOOMAMOUNT ]->SetFloatValue( 1.0f ); + } + if ( !params[ DEPTHBLURENABLE ]->IsDefined() ) + { + params[ DEPTHBLURENABLE ]->SetIntValue( 0 ); + } + if ( !params[ ALLOWNOISE ]->IsDefined() ) + { + params[ ALLOWNOISE ]->SetIntValue( 1 ); + } + if ( !params[ NOISESCALE ]->IsDefined() ) + { + params[ NOISESCALE ]->SetFloatValue( 1.0f ); + } + if ( !params[ NOISEENABLE ]->IsDefined() ) + { + params[ NOISEENABLE ]->SetIntValue( 0 ); + } + if ( !params[ ALLOWVIGNETTE ]->IsDefined() ) + { + params[ ALLOWVIGNETTE ]->SetIntValue( 1 ); + } + if ( !params[ VIGNETTEENABLE ]->IsDefined() ) + { + params[ VIGNETTEENABLE ]->SetIntValue( 0 ); + } + if ( !params[ ALLOWLOCALCONTRAST ]->IsDefined() ) + { + params[ ALLOWLOCALCONTRAST ]->SetIntValue( 1 ); + } + if ( !params[ LOCALCONTRASTSCALE ]->IsDefined() ) + { + params[ LOCALCONTRASTSCALE ]->SetFloatValue( 1.0f ); + } + if ( !params[ LOCALCONTRASTMIDTONEMASK ]->IsDefined() ) + { + params[ LOCALCONTRASTMIDTONEMASK ]->SetFloatValue( 1000.0f ); + } + if ( !params[ LOCALCONTRASTENABLE ]->IsDefined() ) + { + params[ LOCALCONTRASTENABLE ]->SetIntValue( 0 ); + } + if ( !params[ LOCALCONTRASTVIGNETTESTART ]->IsDefined() ) + { + params[ LOCALCONTRASTVIGNETTESTART ]->SetFloatValue( 0.7f ); + } + if ( !params[ LOCALCONTRASTVIGNETTEEND ]->IsDefined() ) + { + params[ LOCALCONTRASTVIGNETTEEND ]->SetFloatValue( 1.0f ); + } + if ( !params[ LOCALCONTRASTEDGESCALE ]->IsDefined() ) + { + params[ LOCALCONTRASTEDGESCALE ]->SetFloatValue( 0.0f ); + } + if ( !params[ BLURREDVIGNETTEENABLE ]->IsDefined() ) + { + params[ BLURREDVIGNETTEENABLE ]->SetIntValue( 0 ); + } + if ( !params[ BLURREDVIGNETTESCALE ]->IsDefined() ) + { + params[ BLURREDVIGNETTESCALE ]->SetFloatValue( 0.0f ); + } + if ( !params[ FADETOBLACKSCALE ]->IsDefined() ) + { + params[ FADETOBLACKSCALE ]->SetFloatValue( 0.0f ); + } + if ( !params[ DEPTHBLURFOCALDISTANCE ]->IsDefined() ) + { + params[ DEPTHBLURFOCALDISTANCE ]->SetFloatValue( 0.0f ); + } + if ( !params[ DEPTHBLURSTRENGTH ]->IsDefined() ) + { + params[ DEPTHBLURSTRENGTH ]->SetFloatValue( 0.0f ); + } + if ( !params[ SCREENBLURSTRENGTH ]->IsDefined() ) + { + params[ SCREENBLURSTRENGTH ]->SetFloatValue( 0.0f ); + } + if ( !params[ TOOLMODE ]->IsDefined() ) + { + params[ TOOLMODE ]->SetIntValue( 0 ); + } + if ( !params[ TOOLCOLORCORRECTION ]->IsDefined() ) + { + params[ TOOLCOLORCORRECTION ]->SetFloatValue( 0.0f ); + } + if ( !params[ VOMITENABLE ]->IsDefined() ) + { + params[ VOMITENABLE ]->SetIntValue( 0 ); + } + if ( !params[ VOMITREFRACTSCALE ]->IsDefined() ) + { + params[ VOMITREFRACTSCALE ]->SetFloatValue( 0.15f ); + } + if ( !params[ VOMITCOLOR1 ]->IsDefined() ) + { + params[ VOMITCOLOR1 ]->SetVecValue( 1.0, 1.0, 0.0 ); + } + if ( !params[ VOMITCOLOR2 ]->IsDefined() ) + { + params[ VOMITCOLOR2 ]->SetVecValue( 0.0, 1.0, 0.0 ); + } + if ( !params[ FADE ]->IsDefined() ) + { + params[ FADE ]->SetIntValue( 0 ); + } + if ( !params[ FADECOLOR ]->IsDefined() ) + { + params[ FADECOLOR ]->SetVecValue( 0.0f, 0.0f, 0.0f, 0.0f ); + } + if ( !params[ TV_GAMMA ]->IsDefined() ) + { + params[ TV_GAMMA ]->SetIntValue( 0 ); + } + if ( !params[ DESATURATEENABLE ]->IsDefined() ) + { + params[ DESATURATEENABLE ]->SetIntValue( 0 ); + } + if ( !params[ DESATURATION ]->IsDefined() ) + { + params[ DESATURATION ]->SetFloatValue( 0.0f ); + } + + SET_FLAGS2( MATERIAL_VAR2_NEEDS_FULL_FRAME_BUFFER_TEXTURE ); + } + + SHADER_FALLBACK + { + // This shader should not be *used* unless we're >= DX9 (bloomadd.vmt/screenspace_general_dx8 should be used for DX8) + return 0; + } + + SHADER_INIT + { + if ( params[BASETEXTURE]->IsDefined() ) + { + LoadTexture( BASETEXTURE ); + } + + if ( params[FBTEXTURE]->IsDefined() ) + { + LoadTexture( FBTEXTURE ); + } + + if ( params[SCREENEFFECTTEXTURE]->IsDefined() ) + { + LoadTexture( SCREENEFFECTTEXTURE ); + } + + if ( params[NOISETEXTURE]->IsDefined() ) + { + LoadTexture( NOISETEXTURE ); + } + + if ( params[INTERNAL_VIGNETTETEXTURE]->IsDefined() ) + { + LoadTexture( INTERNAL_VIGNETTETEXTURE ); + } + } + + SHADER_DRAW + { + bool bToolMode = params[TOOLMODE]->GetIntValue() != 0; + bool bDepthBlurEnable = params[ DEPTHBLURENABLE ]->GetIntValue() != 0; + + SHADOW_STATE + { + // This shader uses opaque blending, but needs to match the behaviour of bloom_add/screen_spacegeneral, + // which uses additive blending (and is used when bloom is enabled but col-correction and AA are not). + // BUT! + // Hardware sRGB blending is incorrect (on pre-DX10 cards, sRGB values are added directly). + // SO... + // When doing the bloom addition in the pixel shader, we need to emulate that incorrect + // behaviour - by turning sRGB read OFF for the FB texture and by turning sRGB write OFF + // (which is fine, since the AA process works better on an sRGB framebuffer than a linear + // one; gamma colours more closely match luminance perception. The color-correction process + // has always taken gamma-space values as input anyway). + + pShaderShadow->EnableBlending( false ); + + // The (sRGB) bloom texture is bound to sampler 0 + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, false ); + pShaderShadow->EnableSRGBWrite( false ); + + // The (sRGB) full framebuffer texture is bound to sampler 1: + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, false ); + + // Up to 4 (sRGB) color-correction lookup textures are bound to samplers 2-5: + pShaderShadow->EnableTexture( SHADER_SAMPLER2, true ); + pShaderShadow->EnableTexture( SHADER_SAMPLER3, true ); + pShaderShadow->EnableTexture( SHADER_SAMPLER4, true ); + pShaderShadow->EnableTexture( SHADER_SAMPLER5, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER2, false ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER3, false ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER4, false ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER5, false ); + + // Noise + pShaderShadow->EnableTexture( SHADER_SAMPLER6, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER6, false ); + + // Vignette + pShaderShadow->EnableTexture( SHADER_SAMPLER7, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER7, false ); + + // Screen effect texture + pShaderShadow->EnableTexture( SHADER_SAMPLER8, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER8, false ); + + pShaderShadow->EnableSRGBWrite( false ); + + int format = VERTEX_POSITION; + int numTexCoords = 1; + int * pTexCoordDimensions = NULL; + int userDataSize = 0; + pShaderShadow->VertexShaderVertexFormat( format, numTexCoords, pTexCoordDimensions, userDataSize ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_screenspaceeffect_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_screenspaceeffect_vs20 ); + + DECLARE_STATIC_PIXEL_SHADER( sdk_engine_post_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( TOOL_MODE, bToolMode ); + SET_STATIC_PIXEL_SHADER_COMBO( DEPTH_BLUR_ENABLE, bDepthBlurEnable ); + SET_STATIC_PIXEL_SHADER( sdk_engine_post_ps20b ); + } + DYNAMIC_STATE + { + BindTexture( SHADER_SAMPLER0, BASETEXTURE, -1 ); + + // FIXME: need to set FBTEXTURE to be point-sampled (will speed up this shader significantly on 360) + // and assert that it's set to SHADER_TEXWRAPMODE_CLAMP (since the shader will sample offscreen) + BindTexture( SHADER_SAMPLER1, FBTEXTURE, -1 ); + + ShaderColorCorrectionInfo_t ccInfo = { false, 0, 1.0f, { 1.0f, 1.0f, 1.0f, 1.0f } }; + + float flTime; + if ( bToolMode ) + { + flTime = params[TOOLTIME]->GetFloatValue(); + } + else + { + flTime = pShaderAPI->CurrentTime(); + } + + // PC, ps20b has a desaturation control that overrides color correction + bool bDesaturateEnable = bToolMode && ( params[DESATURATEENABLE]->GetIntValue() != 0 ) && g_pHardwareConfig->SupportsPixelShaders_2_b(); + + float vPsConst16[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vPsConst16[0] = params[ DESATURATION ]->GetFloatValue(); + vPsConst16[1] = ( params[FADE]->GetIntValue() == 2 ) ? 1.0f : 0.0f; // Enable lerping to ( color * fadecolor ) for FADE_COLOR=2 + pShaderAPI->SetPixelShaderConstant( 16, vPsConst16, 1 ); + + if ( params[FADE]->GetIntValue() == 0 ) + { + // Not fading, so set the constant to cause nothing to change about the pixel color + float vConst[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + pShaderAPI->SetPixelShaderConstant( 15, vConst ); + } + else + { + pShaderAPI->SetPixelShaderConstant( 15, params[ FADECOLOR ]->GetVecValue(), 1 ); + } + + if ( !bDesaturateEnable ) // set up color correction + { + bool bToolColorCorrection = params[TOOLCOLORCORRECTION]->GetIntValue() != 0; + if ( bToolColorCorrection ) + { + ccInfo.m_bIsEnabled = true; + + ccInfo.m_nLookupCount = (int) params[NUM_LOOKUPS]->GetFloatValue(); + ccInfo.m_flDefaultWeight = params[WEIGHT_DEFAULT]->GetFloatValue(); + ccInfo.m_pLookupWeights[0] = params[WEIGHT0]->GetFloatValue(); + ccInfo.m_pLookupWeights[1] = params[WEIGHT1]->GetFloatValue(); + ccInfo.m_pLookupWeights[2] = params[WEIGHT2]->GetFloatValue(); + ccInfo.m_pLookupWeights[3] = params[WEIGHT3]->GetFloatValue(); + } + else + { + pShaderAPI->GetCurrentColorCorrection( &ccInfo ); + } + } + + int colCorrectNumLookups = MIN( ccInfo.m_nLookupCount, 3 ); + for ( int i = 0; i < colCorrectNumLookups; i++ ) + { + pShaderAPI->BindStandardTexture( (Sampler_t)(SHADER_SAMPLER2 + i), (StandardTextureId_t)(TEXTURE_COLOR_CORRECTION_VOLUME_0 + i) ); + } + + // Upload 1-pixel X&Y offsets [ (+dX,0,+dY,-dX) is chosen to work with the allowed ps20 swizzles ] + // The shader will sample in a cross (up/down/left/right from the current sample), for 5-tap + // (quality 0) mode and add another 4 samples in a diagonal cross, for 9-tap (quality 1) mode + ITexture * pTarget = params[FBTEXTURE]->GetTextureValue(); + int width = pTarget->GetActualWidth(); + int height = pTarget->GetActualHeight(); + float dX = 1.0f / width; + float dY = 1.0f / height; + float offsets[4] = { +dX, 0, +dY, -dX }; + pShaderAPI->SetPixelShaderConstant( 0, &offsets[0], 1 ); + + // Upload AA tweakables: + // x - strength (this can be used to toggle the AA off, or to weaken it where pathological cases are showing) + // y - reduction of 1-pixel-line blurring (blurring of 1-pixel lines causes issues, so it's tunable) + // z - edge threshold multiplier (default 1.0, < 1.0 => more edges softened, > 1.0 => fewer edges softened) + // w - tap offset multiplier (default 1.0, < 1.0 => sharper image, > 1.0 => blurrier image) + float tweakables[4] = { params[ AAINTERNAL1 ]->GetVecValue()[0], + params[ AAINTERNAL1 ]->GetVecValue()[1], + params[ AAINTERNAL3 ]->GetVecValue()[0], + params[ AAINTERNAL3 ]->GetVecValue()[1] }; + pShaderAPI->SetPixelShaderConstant( 1, &tweakables[0], 1 ); + + // Upload AA UV transform (converts bloom texture UVs to framebuffer texture UVs) + // NOTE: we swap the order of the z and w components since 'wz' is an allowed ps20 swizzle, but 'zw' is not: + float uvTrans[4] = { params[ AAINTERNAL2 ]->GetVecValue()[0], + params[ AAINTERNAL2 ]->GetVecValue()[1], + params[ AAINTERNAL2 ]->GetVecValue()[3], + params[ AAINTERNAL2 ]->GetVecValue()[2] }; + pShaderAPI->SetPixelShaderConstant( 2, &uvTrans[0], 1 ); + + // Upload color-correction weights: + pShaderAPI->SetPixelShaderConstant( 3, &ccInfo.m_flDefaultWeight ); + pShaderAPI->SetPixelShaderConstant( 4, ccInfo.m_pLookupWeights ); + + int aaEnabled = 0; + int bloomEnabled = ( params[ BLOOMENABLE ]->GetIntValue() == 0 ) ? 0 : 1; + int colCorrectEnabled = ccInfo.m_bIsEnabled; + + float flBloomFactor = bloomEnabled ? 1.0f : 0.0f; + flBloomFactor *= params[BLOOMAMOUNT]->GetFloatValue(); + float bloomConstant[4] = + { + flBloomFactor, + params[ SCREENBLURSTRENGTH ]->GetFloatValue(), + params[ DEPTHBLURFOCALDISTANCE ]->GetFloatValue(), + params[ DEPTHBLURSTRENGTH ]->GetFloatValue() + }; + + if ( mat_screen_blur_override.GetFloat() >= 0.0f ) + { + bloomConstant[1] = mat_screen_blur_override.GetFloat(); + } + if ( mat_depth_blur_focal_distance_override.GetFloat() >= 0.0f ) + { + bloomConstant[2] = mat_depth_blur_focal_distance_override.GetFloat(); + } + if ( mat_depth_blur_strength_override.GetFloat() >= 0.0f ) + { + bloomConstant[3] = mat_depth_blur_strength_override.GetFloat(); + } + + pShaderAPI->SetPixelShaderConstant( 5, bloomConstant ); + + // Vignette + bool bVignetteEnable = ( params[ VIGNETTEENABLE ]->GetIntValue() != 0 ) && ( params[ ALLOWVIGNETTE ]->GetIntValue() != 0 ); + if ( bVignetteEnable ) + { + BindTexture( SHADER_SAMPLER7, INTERNAL_VIGNETTETEXTURE ); + } + + // Noise + bool bNoiseEnable = ( params[ NOISEENABLE ]->GetIntValue() != 0 ) && ( params[ ALLOWNOISE ]->GetIntValue() != 0 ); + + int nFbTextureHeight = params[FBTEXTURE]->GetTextureValue()->GetActualHeight(); + if ( nFbTextureHeight < 720 ) + { + // Disable noise at low resolutions + bNoiseEnable = false; + } + + if ( bNoiseEnable ) + { + BindTexture( SHADER_SAMPLER6, NOISETEXTURE ); + + // Noise scale + float vPsConst6[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vPsConst6[0] = params[ NOISESCALE ]->GetFloatValue(); + if ( mat_grain_scale_override.GetFloat() != -1.0f ) + { + vPsConst6[0] = mat_grain_scale_override.GetFloat(); + } + + if ( vPsConst6[0] <= 0.0f ) + { + bNoiseEnable = false; + } + + pShaderAPI->SetPixelShaderConstant( 6, vPsConst6 ); + + // Time % 1000 for scrolling + float vPsConst[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vPsConst[0] = flTime; + vPsConst[0] -= (float)( (int)( vPsConst[0] / 1000.0f ) ) * 1000.0f; + pShaderAPI->SetPixelShaderConstant( 7, vPsConst, 1 ); + } + + // Local Contrast + bool bLocalContrastEnable = ( params[ LOCALCONTRASTENABLE ]->GetIntValue() != 0 ) && ( params[ ALLOWLOCALCONTRAST ]->GetIntValue() != 0 ); + bool bBlurredVignetteEnable = ( bLocalContrastEnable ) && ( params[ BLURREDVIGNETTEENABLE ]->GetIntValue() != 0 ); + + if ( bLocalContrastEnable ) + { + // Contrast scale + float vPsConst[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vPsConst[0] = params[ LOCALCONTRASTSCALE ]->GetFloatValue(); + if ( mat_local_contrast_scale_override.GetFloat() != 0.0f ) + { + vPsConst[0] = mat_local_contrast_scale_override.GetFloat(); + } + vPsConst[1] = params[ LOCALCONTRASTMIDTONEMASK ]->GetFloatValue(); + if ( mat_local_contrast_midtone_mask_override.GetFloat() >= 0.0f ) + { + vPsConst[1] = mat_local_contrast_midtone_mask_override.GetFloat(); + } + vPsConst[2] = params[ BLURREDVIGNETTESCALE ]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( 8, vPsConst, 1 ); + + vPsConst[0] = params[ LOCALCONTRASTVIGNETTESTART ]->GetFloatValue(); + if ( mat_local_contrast_vignette_start_override.GetFloat() >= 0.0f ) + { + vPsConst[0] = mat_local_contrast_vignette_start_override.GetFloat(); + } + vPsConst[1] = params[ LOCALCONTRASTVIGNETTEEND ]->GetFloatValue(); + if ( mat_local_contrast_vignette_end_override.GetFloat() >= 0.0f ) + { + vPsConst[1] = mat_local_contrast_vignette_end_override.GetFloat(); + } + vPsConst[2] = params[ LOCALCONTRASTEDGESCALE ]->GetFloatValue(); + if ( mat_local_contrast_edge_scale_override.GetFloat() >= -1.0f ) + { + vPsConst[2] = mat_local_contrast_edge_scale_override.GetFloat(); + } + pShaderAPI->SetPixelShaderConstant( 9, vPsConst, 1 ); + } + + // fade to black + float vPsConst[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vPsConst[0] = params[ FADETOBLACKSCALE ]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( 10, vPsConst, 1 ); + + bool bVomitEnable = ( params[ VOMITENABLE ]->GetIntValue() != 0 ); + if ( bVomitEnable ) + { + BindTexture( SHADER_SAMPLER8, SCREENEFFECTTEXTURE ); + + params[ VOMITCOLOR1 ]->GetVecValue( vPsConst, 3 ); + vPsConst[3] = params[ VOMITREFRACTSCALE ]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( 11, vPsConst, 1 ); + params[ VOMITCOLOR2 ]->GetVecValue( vPsConst, 3 ); + vPsConst[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( 12, vPsConst, 1 ); + + // Get viewport and render target dimensions and set shader constant to do a 2D mad + ShaderViewport_t vp; + pShaderAPI->GetViewports( &vp, 1 ); + int nViewportX = vp.m_nTopLeftX; + int nViewportY = vp.m_nTopLeftY; + int nViewportWidth = vp.m_nWidth; + int nViewportHeight = vp.m_nHeight; + + int nRtWidth, nRtHeight; + ITexture *pRenderTarget = pShaderAPI->GetRenderTargetEx( 0 ); + if( pRenderTarget != nullptr ) + { + nRtWidth = pRenderTarget->GetActualWidth(); + nRtHeight = pRenderTarget->GetActualHeight(); + } + else + { + pShaderAPI->GetBackBufferDimensions( nRtWidth, nRtHeight ); + } + + float vViewportMad[4]; + + // screen->viewport transform + vViewportMad[0] = ( float )nRtWidth / ( float )nViewportWidth; + vViewportMad[1] = ( float )nRtHeight / ( float )nViewportHeight; + vViewportMad[2] = -( float )nViewportX / ( float )nViewportWidth; + vViewportMad[3] = -( float )nViewportY / ( float )nViewportHeight; + pShaderAPI->SetPixelShaderConstant( 13, vViewportMad, 1 ); + + // viewport->screen transform + vViewportMad[0] = ( float )nViewportWidth / ( float )nRtWidth; + vViewportMad[1] = ( float )nViewportHeight / ( float )nRtHeight; + vViewportMad[2] = ( float )nViewportX / ( float )nRtWidth; + vViewportMad[3] = ( float )nViewportY / ( float )nRtHeight; + pShaderAPI->SetPixelShaderConstant( 14, vViewportMad, 1 ); + } + + if ( !colCorrectEnabled ) + { + colCorrectNumLookups = 0; + } + + bool bConvertFromLinear = ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ); + + // JasonM - double check this if the SFM needs to use the engine post FX clip in main + bool bConvertToLinear = bToolMode && bConvertFromLinear && ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ); + + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_engine_post_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( AA_ENABLE, aaEnabled ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( COL_CORRECT_NUM_LOOKUPS, colCorrectNumLookups ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( CONVERT_FROM_LINEAR, bConvertFromLinear ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( CONVERT_TO_LINEAR, bConvertToLinear ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( NOISE_ENABLE, bNoiseEnable ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( VIGNETTE_ENABLE, bVignetteEnable ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( LOCAL_CONTRAST_ENABLE, bLocalContrastEnable ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( BLURRED_VIGNETTE_ENABLE, bBlurredVignetteEnable ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( VOMIT_ENABLE, bVomitEnable ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( TV_GAMMA, params[TV_GAMMA]->GetIntValue() && bToolMode ? 1 : 0 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( DESATURATEENABLE, bDesaturateEnable ); + SET_DYNAMIC_PIXEL_SHADER( sdk_engine_post_ps20b ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_screenspaceeffect_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_screenspaceeffect_vs20 ); + } + Draw(); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/example_model_ps20b.fxc b/mp/src/materialsystem/stdshaders/example_model_ps20b.fxc index 29ed629a..00ff3499 100644 --- a/mp/src/materialsystem/stdshaders/example_model_ps20b.fxc +++ b/mp/src/materialsystem/stdshaders/example_model_ps20b.fxc @@ -80,7 +80,7 @@ float4 main( PS_INPUT i ) : COLOR float3 result = baseColor.rgb * g_DiffuseModulation.rgb * diffuseLighting; float alpha = g_DiffuseModulation.a * baseColor.a; - float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.z, i.worldPos.z, i.projPos.z ); + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos, i.projPos.z ); #if WRITEWATERFOGTODESTALPHA && ( PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT ) alpha = fogFactor; diff --git a/mp/src/materialsystem/stdshaders/eye_refract.cpp b/mp/src/materialsystem/stdshaders/eye_refract.cpp index 87528028..9e1e870c 100644 --- a/mp/src/materialsystem/stdshaders/eye_refract.cpp +++ b/mp/src/materialsystem/stdshaders/eye_refract.cpp @@ -8,8 +8,8 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -DEFINE_FALLBACK_SHADER( EyeRefract, EyeRefract_dx9 ) -BEGIN_VS_SHADER( EyeRefract_dx9, "Help for Eyes" ) +DEFINE_FALLBACK_SHADER( SDK_EyeRefract, SDK_EyeRefract_dx9 ) +BEGIN_VS_SHADER( SDK_EyeRefract_dx9, "Help for SDK_EyeRefract" ) BEGIN_SHADER_PARAMS SHADER_PARAM( IRIS, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "iris texture" ) SHADER_PARAM( IRISFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "frame for the iris texture" ) diff --git a/mp/src/materialsystem/stdshaders/eye_refract_helper.cpp b/mp/src/materialsystem/stdshaders/eye_refract_helper.cpp index b7aa4caf..6601c93d 100644 --- a/mp/src/materialsystem/stdshaders/eye_refract_helper.cpp +++ b/mp/src/materialsystem/stdshaders/eye_refract_helper.cpp @@ -6,16 +6,16 @@ #include "cpp_shader_constant_register_map.h" -#include "eyes_flashlight_vs11.inc" -#include "eyes_flashlight_ps11.inc" +//#include "eyes_flashlight_vs11.inc" +//#include "eyes_flashlight_ps11.inc" -#include "eye_refract_vs20.inc" -#include "eye_refract_ps20.inc" -#include "eye_refract_ps20b.inc" +#include "SDK_eye_refract_vs20.inc" +#include "SDK_eye_refract_ps20.inc" +#include "SDK_eye_refract_ps20b.inc" #ifndef _X360 -#include "eye_refract_vs30.inc" -#include "eye_refract_ps30.inc" +#include "SDK_eye_refract_vs30.inc" +#include "SDK_eye_refract_ps30.inc" #endif #include "convar.h" @@ -145,25 +145,25 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_STATIC_VERTEX_SHADER( eye_refract_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eye_refract_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, IS_FLAG_SET( MATERIAL_VAR_HALFLAMBERT ) ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, bIntro ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, bDrawFlashlightAdditivePass ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( LIGHTWARPTEXTURE, bDiffuseWarp ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( eye_refract_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_eye_refract_vs20 ); if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { bool bSphereTexKillCombo = IS_PARAM_DEFINED( info.m_nSphereTexKillCombo ) ? ( params[info.m_nSphereTexKillCombo]->GetIntValue() ? true : false ) : ( kDefaultSphereTexKillCombo ? true : false ); bool bRayTraceSphere = IS_PARAM_DEFINED( info.m_nRaytraceSphere ) ? ( params[info.m_nRaytraceSphere]->GetIntValue() ? true : false ) : ( kDefaultRaytraceSphere ? true : false ); - DECLARE_STATIC_PIXEL_SHADER( eye_refract_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eye_refract_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( SPHERETEXKILLCOMBO, bSphereTexKillCombo ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( RAYTRACESPHERE, bRayTraceSphere ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bDrawFlashlightAdditivePass ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bDiffuseWarp ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( eye_refract_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_eye_refract_ps20b ); if ( bDrawFlashlightAdditivePass == true ) { @@ -174,10 +174,10 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, } else { - DECLARE_STATIC_PIXEL_SHADER( eye_refract_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eye_refract_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bDrawFlashlightAdditivePass ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bDiffuseWarp ? 1 : 0 ); - SET_STATIC_PIXEL_SHADER( eye_refract_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_eye_refract_ps20 ); } } #ifndef _X360 @@ -186,23 +186,23 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, // The vertex shader uses the vertex id stream SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( eye_refract_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eye_refract_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, IS_FLAG_SET( MATERIAL_VAR_HALFLAMBERT ) ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, bIntro ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, bDrawFlashlightAdditivePass ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( LIGHTWARPTEXTURE, bDiffuseWarp ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( eye_refract_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_eye_refract_vs30 ); bool bSphereTexKillCombo = IS_PARAM_DEFINED( info.m_nSphereTexKillCombo ) ? ( params[info.m_nSphereTexKillCombo]->GetIntValue() ? true : false ) : ( kDefaultSphereTexKillCombo ? true : false ); bool bRayTraceSphere = IS_PARAM_DEFINED( info.m_nRaytraceSphere ) ? ( params[info.m_nRaytraceSphere]->GetIntValue() ? true : false ) : ( kDefaultRaytraceSphere ? true : false ); - DECLARE_STATIC_PIXEL_SHADER( eye_refract_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eye_refract_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( SPHERETEXKILLCOMBO, bSphereTexKillCombo ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( RAYTRACESPHERE, bRayTraceSphere ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bDrawFlashlightAdditivePass ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bDiffuseWarp ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( eye_refract_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_eye_refract_ps30 ); if ( bDrawFlashlightAdditivePass == true ) { @@ -284,21 +284,21 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_DYNAMIC_VERTEX_SHADER( eye_refract_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eye_refract_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( eye_refract_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eye_refract_vs20 ); } #ifndef _X360 else { pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0 ); - DECLARE_DYNAMIC_VERTEX_SHADER( eye_refract_vs30 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eye_refract_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); @@ -306,7 +306,7 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( eye_refract_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eye_refract_vs30 ); } #endif @@ -361,25 +361,25 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, { if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( eye_refract_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eye_refract_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); - SET_DYNAMIC_PIXEL_SHADER( eye_refract_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eye_refract_ps20b ); } else // ps.2.0 { - DECLARE_DYNAMIC_PIXEL_SHADER( eye_refract_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eye_refract_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); - SET_DYNAMIC_PIXEL_SHADER( eye_refract_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eye_refract_ps20 ); } } #ifndef _X360 else { - DECLARE_DYNAMIC_PIXEL_SHADER( eye_refract_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eye_refract_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); - SET_DYNAMIC_PIXEL_SHADER( eye_refract_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eye_refract_ps30 ); } #endif @@ -437,7 +437,7 @@ void Draw_Eyes_Refract_Internal( CBaseVSShader *pShader, IMaterialVar** params, { params[info.m_nEntityOrigin]->GetVecValue( timeVec, 3 ); } - pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, timeVec, 1 ); + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_5, timeVec, 1 ); } } pShader->Draw(); diff --git a/mp/src/materialsystem/stdshaders/eyeball.cpp b/mp/src/materialsystem/stdshaders/eyeball.cpp index 7113585d..f1ec2117 100644 --- a/mp/src/materialsystem/stdshaders/eyeball.cpp +++ b/mp/src/materialsystem/stdshaders/eyeball.cpp @@ -9,7 +9,7 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -BEGIN_VS_SHADER( Eyeball, "Help for EyeBall" ) +BEGIN_VS_SHADER( SDK_Eyeball, "Help for SDK_EyeBall" ) BEGIN_SHADER_PARAMS SHADER_PARAM_OVERRIDE( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "models/alyx/pupil_l", "iris texture", 0 ) diff --git a/mp/src/materialsystem/stdshaders/eyeglint_dx9.cpp b/mp/src/materialsystem/stdshaders/eyeglint_dx9.cpp index d2662e6d..33499ccd 100644 --- a/mp/src/materialsystem/stdshaders/eyeglint_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/eyeglint_dx9.cpp @@ -9,12 +9,12 @@ #include "BaseVSShader.h" #include "shaderlib/cshader.h" -#include "eyeglint_vs20.inc" -#include "eyeglint_ps20.inc" -#include "eyeglint_ps20b.inc" +#include "SDK_eyeglint_vs20.inc" +#include "SDK_eyeglint_ps20.inc" +#include "SDK_eyeglint_ps20b.inc" -DEFINE_FALLBACK_SHADER( EyeGlint, EyeGlint_dx9 ) -BEGIN_VS_SHADER( EyeGlint_dx9, "Help for EyeGlint" ) +DEFINE_FALLBACK_SHADER( SDK_EyeGlint, SDK_EyeGlint_dx9 ) +BEGIN_VS_SHADER( SDK_EyeGlint_dx9, "Help for SDK_EyeGlint" ) BEGIN_SHADER_PARAMS END_SHADER_PARAMS @@ -48,18 +48,18 @@ SHADER_DRAW pShaderShadow->EnableSRGBWrite( false ); // linear texture - DECLARE_STATIC_VERTEX_SHADER( eyeglint_vs20 ); - SET_STATIC_VERTEX_SHADER( eyeglint_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eyeglint_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_eyeglint_vs20 ); - SET_STATIC_PS2X_PIXEL_SHADER_NO_COMBOS( eyeglint ); + SET_STATIC_PS2X_PIXEL_SHADER_NO_COMBOS( sdk_eyeglint ); } DYNAMIC_STATE { - DECLARE_DYNAMIC_VERTEX_SHADER( eyeglint_vs20 ); - SET_DYNAMIC_VERTEX_SHADER( eyeglint_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eyeglint_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eyeglint_vs20 ); - SET_DYNAMIC_PS2X_PIXEL_SHADER_NO_COMBOS( eyeglint ); + SET_DYNAMIC_PS2X_PIXEL_SHADER_NO_COMBOS( sdk_eyeglint ); } Draw(); } diff --git a/mp/src/materialsystem/stdshaders/eyes.cpp b/mp/src/materialsystem/stdshaders/eyes.cpp index 7aa4738f..2a0c5908 100644 --- a/mp/src/materialsystem/stdshaders/eyes.cpp +++ b/mp/src/materialsystem/stdshaders/eyes.cpp @@ -13,10 +13,10 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -DEFINE_FALLBACK_SHADER( eyes, Eyes_dx8 ) +DEFINE_FALLBACK_SHADER( SDK_Eyes, SDK_Eyes_dx8 ) -BEGIN_VS_SHADER( Eyes_dx8, - "Help for Eyes" ) +BEGIN_VS_SHADER( SDK_Eyes_dx8, + "Help for SDK_Eyes" ) BEGIN_SHADER_PARAMS SHADER_PARAM_OVERRIDE( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "models/alyx/eyeball_l", "iris texture", 0 ) diff --git a/mp/src/materialsystem/stdshaders/eyes_dx8_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/eyes_dx8_dx9_helper.cpp index e1edda0d..526ae685 100644 --- a/mp/src/materialsystem/stdshaders/eyes_dx8_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/eyes_dx8_dx9_helper.cpp @@ -9,24 +9,24 @@ #include "mathlib/vmatrix.h" #include "eyes_dx8_dx9_helper.h" #include "cpp_shader_constant_register_map.h" -#include "Eyes.inc" -#include "eyes_flashlight_vs11.inc" -#include "eyes_flashlight_ps11.inc" +#include "SDK_Eyes.inc" +#include "SDK_eyes_flashlight_vs11.inc" +#include "SDK_eyes_flashlight_ps11.inc" #ifdef STDSHADER_DX9_DLL_EXPORT -#include "eyes_vs20.inc" -#include "eyes_ps20.inc" -#include "eyes_ps20b.inc" -#include "eyes_flashlight_vs20.inc" -#include "eyes_flashlight_ps20.inc" -#include "eyes_flashlight_ps20b.inc" +#include "SDK_eyes_vs20.inc" +#include "SDK_eyes_ps20.inc" +#include "SDK_eyes_ps20b.inc" +#include "SDK_eyes_flashlight_vs20.inc" +#include "SDK_eyes_flashlight_ps20.inc" +#include "SDK_eyes_flashlight_ps20b.inc" #ifndef _X360 -#include "eyes_vs30.inc" -#include "eyes_ps30.inc" -#include "eyes_flashlight_vs30.inc" -#include "eyes_flashlight_ps30.inc" +#include "SDK_eyes_vs30.inc" +#include "SDK_eyes_ps30.inc" +#include "SDK_eyes_flashlight_vs30.inc" +#include "SDK_eyes_flashlight_ps30.inc" #endif #endif @@ -137,19 +137,19 @@ static void DrawFlashlight( bool bDX9, CBaseVSShader *pShader, IMaterialVar** pa if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_STATIC_VERTEX_SHADER( eyes_flashlight_vs20 ); - SET_STATIC_VERTEX_SHADER( eyes_flashlight_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eyes_flashlight_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_eyes_flashlight_vs20 ); if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( eyes_flashlight_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( eyes_flashlight_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( eyes_flashlight_ps20 ); - SET_STATIC_PIXEL_SHADER( eyes_flashlight_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20 ); } } #ifndef _X360 @@ -158,12 +158,12 @@ static void DrawFlashlight( bool bDX9, CBaseVSShader *pShader, IMaterialVar** pa // The vertex shader uses the vertex id stream SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( eyes_flashlight_vs30 ); - SET_STATIC_VERTEX_SHADER( eyes_flashlight_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eyes_flashlight_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_eyes_flashlight_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( eyes_flashlight_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eyes_flashlight_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( eyes_flashlight_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_eyes_flashlight_ps30 ); } #endif @@ -184,11 +184,11 @@ static void DrawFlashlight( bool bDX9, CBaseVSShader *pShader, IMaterialVar** pa #endif { // DX8 uses old asm shaders - eyes_flashlight_vs11_Static_Index vshIndex; - pShaderShadow->SetVertexShader( "eyes_flashlight_vs11", vshIndex.GetIndex() ); + sdk_eyes_flashlight_vs11_Static_Index vshIndex; + pShaderShadow->SetVertexShader( "sdk_eyes_flashlight_vs11", vshIndex.GetIndex() ); - eyes_flashlight_ps11_Static_Index pshIndex; - pShaderShadow->SetPixelShader( "eyes_flashlight_ps11", pshIndex.GetIndex() ); + sdk_eyes_flashlight_ps11_Static_Index pshIndex; + pShaderShadow->SetPixelShader( "sdk_eyes_flashlight_ps11", pshIndex.GetIndex() ); } pShader->FogToBlack(); @@ -220,23 +220,23 @@ static void DrawFlashlight( bool bDX9, CBaseVSShader *pShader, IMaterialVar** pa if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_DYNAMIC_VERTEX_SHADER( eyes_flashlight_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eyes_flashlight_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( eyes_flashlight_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eyes_flashlight_vs20 ); } #ifndef _X360 else { pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0 ); - DECLARE_DYNAMIC_VERTEX_SHADER( eyes_flashlight_vs30 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eyes_flashlight_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( eyes_flashlight_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eyes_flashlight_vs30 ); } #endif @@ -267,27 +267,27 @@ static void DrawFlashlight( bool bDX9, CBaseVSShader *pShader, IMaterialVar** pa { if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( eyes_flashlight_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, flashlightState.m_bEnableShadows && ( pFlashlightDepthTexture != NULL ) ); - SET_DYNAMIC_PIXEL_SHADER( eyes_flashlight_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20b ); SetDepthFlashlightParams( pShader, pShaderAPI, worldToTexture, flashlightState ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( eyes_flashlight_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( eyes_flashlight_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eyes_flashlight_ps20 ); } } #ifndef _X360 else { - DECLARE_DYNAMIC_PIXEL_SHADER( eyes_flashlight_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eyes_flashlight_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, flashlightState.m_bEnableShadows && ( pFlashlightDepthTexture != NULL ) ); - SET_DYNAMIC_PIXEL_SHADER( eyes_flashlight_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eyes_flashlight_ps30 ); SetDepthFlashlightParams( pShader, pShaderAPI, worldToTexture, flashlightState ); } @@ -296,12 +296,12 @@ static void DrawFlashlight( bool bDX9, CBaseVSShader *pShader, IMaterialVar** pa else // older asm shaders for DX8 #endif { - eyes_flashlight_vs11_Dynamic_Index vshIndex; + sdk_eyes_flashlight_vs11_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); vshIndex.SetSKINNING( pShaderAPI->GetCurrentNumBones() > 0 ); pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); - eyes_flashlight_ps11_Dynamic_Index pshIndex; + sdk_eyes_flashlight_ps11_Dynamic_Index pshIndex; pShaderAPI->SetPixelShaderIndex( pshIndex.GetIndex() ); } @@ -343,21 +343,21 @@ static void DrawUsingVertexShader( bool bDX9, CBaseVSShader *pShader, IMaterialV { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_STATIC_VERTEX_SHADER( eyes_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eyes_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, IS_FLAG_SET( MATERIAL_VAR_HALFLAMBERT ) ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[info.m_nIntro]->GetIntValue() ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); - SET_STATIC_VERTEX_SHADER( eyes_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_eyes_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( eyes_ps20b ); - SET_STATIC_PIXEL_SHADER( eyes_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eyes_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_eyes_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( eyes_ps20 ); - SET_STATIC_PIXEL_SHADER( eyes_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eyes_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_eyes_ps20 ); } } #ifndef _X360 @@ -366,13 +366,13 @@ static void DrawUsingVertexShader( bool bDX9, CBaseVSShader *pShader, IMaterialV // The vertex shader uses the vertex id stream SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( eyes_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_eyes_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, IS_FLAG_SET( MATERIAL_VAR_HALFLAMBERT ) ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[info.m_nIntro]->GetIntValue() ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( eyes_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_eyes_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( eyes_ps30 ); - SET_STATIC_PIXEL_SHADER( eyes_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_eyes_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_eyes_ps30 ); } #endif // On DX9, get the gamma read and write correct @@ -383,7 +383,7 @@ static void DrawUsingVertexShader( bool bDX9, CBaseVSShader *pShader, IMaterialV else #endif { - eyes_Static_Index vshIndex; + sdk_eyes_Static_Index vshIndex; vshIndex.SetHALF_LAMBERT( IS_FLAG_SET( MATERIAL_VAR_HALFLAMBERT ) ); pShaderShadow->SetVertexShader( "Eyes", vshIndex.GetIndex() ); @@ -417,28 +417,28 @@ static void DrawUsingVertexShader( bool bDX9, CBaseVSShader *pShader, IMaterialV { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_DYNAMIC_VERTEX_SHADER( eyes_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eyes_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER( eyes_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eyes_vs20 ); } #ifndef _X360 else { pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, VERTEX_SHADER_SHADER_SPECIFIC_CONST_8, SHADER_VERTEXTEXTURE_SAMPLER0 ); - DECLARE_DYNAMIC_VERTEX_SHADER( eyes_vs30 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_eyes_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( eyes_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_eyes_vs30 ); } #endif @@ -469,25 +469,25 @@ static void DrawUsingVertexShader( bool bDX9, CBaseVSShader *pShader, IMaterialV { if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( eyes_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eyes_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( eyes_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eyes_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( eyes_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eyes_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( eyes_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eyes_ps20 ); } } #ifndef _X360 else { - DECLARE_DYNAMIC_PIXEL_SHADER( eyes_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_eyes_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( eyes_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_eyes_ps30 ); } #endif @@ -498,13 +498,13 @@ static void DrawUsingVertexShader( bool bDX9, CBaseVSShader *pShader, IMaterialV float timeVec[4] = { 0.0f, 0.0f, 0.0f, curTime }; Assert( params[info.m_nEntityOrigin]->IsDefined() ); params[info.m_nEntityOrigin]->GetVecValue( timeVec, 3 ); - pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, timeVec, 1 ); + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_5, timeVec, 1 ); } } else #endif { - eyes_Dynamic_Index vshIndex; + sdk_eyes_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); vshIndex.SetSKINNING( pShaderAPI->GetCurrentNumBones() > 0 ); vshIndex.SetLIGHT_COMBO( pShaderAPI->GetCurrentLightCombo() ); diff --git a/mp/src/materialsystem/stdshaders/eyes_dx9.cpp b/mp/src/materialsystem/stdshaders/eyes_dx9.cpp index b33ec804..a032ef69 100644 --- a/mp/src/materialsystem/stdshaders/eyes_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/eyes_dx9.cpp @@ -12,9 +12,9 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -DEFINE_FALLBACK_SHADER( eyes, Eyes_dx9 ) +DEFINE_FALLBACK_SHADER( SDK_Eyes, SDK_Eyes_dx9 ) -BEGIN_VS_SHADER( Eyes_dx9, "Help for Eyes" ) +BEGIN_VS_SHADER( SDK_Eyes_dx9, "Help for SDK_Eyes" ) BEGIN_SHADER_PARAMS SHADER_PARAM( IRIS, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "iris texture" ) diff --git a/mp/src/materialsystem/stdshaders/flesh_interior_blended_pass_helper.cpp b/mp/src/materialsystem/stdshaders/flesh_interior_blended_pass_helper.cpp index 885a4cf6..e171507c 100644 --- a/mp/src/materialsystem/stdshaders/flesh_interior_blended_pass_helper.cpp +++ b/mp/src/materialsystem/stdshaders/flesh_interior_blended_pass_helper.cpp @@ -124,9 +124,9 @@ #include "flesh_interior_blended_pass_helper.h" // Auto generated inc files -#include "flesh_interior_blended_pass_vs20.inc" -#include "flesh_interior_blended_pass_ps20.inc" -#include "flesh_interior_blended_pass_ps20b.inc" +#include "SDK_flesh_interior_blended_pass_vs20.inc" +#include "SDK_flesh_interior_blended_pass_ps20.inc" +#include "SDK_flesh_interior_blended_pass_ps20b.inc" void InitParamsFleshInteriorBlendedPass( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, FleshInteriorBlendedPassVars_t &info ) { @@ -177,21 +177,21 @@ void DrawFleshInteriorBlendedPass( CBaseVSShader *pShader, IMaterialVar** params bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); // Vertex Shader - DECLARE_STATIC_VERTEX_SHADER( flesh_interior_blended_pass_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_flesh_interior_blended_pass_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, IS_FLAG_SET( MATERIAL_VAR_HALFLAMBERT ) ); SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); - SET_STATIC_VERTEX_SHADER( flesh_interior_blended_pass_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_flesh_interior_blended_pass_vs20 ); // Pixel Shader if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20b ); - SET_STATIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20 ); - SET_STATIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20 ); } // Textures @@ -224,14 +224,14 @@ void DrawFleshInteriorBlendedPass( CBaseVSShader *pShader, IMaterialVar** params // Set Vertex Shader Combos LightState_t lightState = { 0, false, false }; pShaderAPI->GetDX9LightState( &lightState ); - DECLARE_DYNAMIC_VERTEX_SHADER( flesh_interior_blended_pass_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_flesh_interior_blended_pass_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER( flesh_interior_blended_pass_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_flesh_interior_blended_pass_vs20 ); // Set Vertex Shader Constants pShader->SetAmbientCubeDynamicStateVertexShader(); @@ -300,13 +300,13 @@ void DrawFleshInteriorBlendedPass( CBaseVSShader *pShader, IMaterialVar** params // Set Pixel Shader Combos if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20b ); - SET_DYNAMIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20 ); - SET_DYNAMIC_PIXEL_SHADER( flesh_interior_blended_pass_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_flesh_interior_blended_pass_ps20 ); } // Bind textures diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Refract_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Refract_vs20.inc new file mode 100644 index 00000000..58adb397 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Refract_vs20.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_refract_vs20_Static_Index +{ +private: + int m_nMODEL; +#ifdef _DEBUG + bool m_bMODEL; +#endif +public: + void SetMODEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMODEL = i; +#ifdef _DEBUG + m_bMODEL = true; +#endif + } + void SetMODEL( bool i ) + { + m_nMODEL = i ? 1 : 0; +#ifdef _DEBUG + m_bMODEL = true; +#endif + } +private: + int m_nCOLORMODULATE; +#ifdef _DEBUG + bool m_bCOLORMODULATE; +#endif +public: + void SetCOLORMODULATE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLORMODULATE = i; +#ifdef _DEBUG + m_bCOLORMODULATE = true; +#endif + } + void SetCOLORMODULATE( bool i ) + { + m_nCOLORMODULATE = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLORMODULATE = true; +#endif + } +public: + sdk_refract_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bMODEL = false; +#endif // _DEBUG + m_nMODEL = 0; +#ifdef _DEBUG + m_bCOLORMODULATE = false; +#endif // _DEBUG + m_nCOLORMODULATE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bMODEL && m_bCOLORMODULATE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nMODEL ) + ( 8 * m_nCOLORMODULATE ) + 0; + } +}; +#define shaderStaticTest_sdk_refract_vs20 vsh_forgot_to_set_static_MODEL + vsh_forgot_to_set_static_COLORMODULATE + 0 +class sdk_refract_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_refract_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_refract_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20.inc new file mode 100644 index 00000000..161c4c79 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_shatteredglass_ps20_Static_Index +{ +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nHDRTYPE; +#ifdef _DEBUG + bool m_bHDRTYPE; +#endif +public: + void SetHDRTYPE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nHDRTYPE = i; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } + void SetHDRTYPE( bool i ) + { + m_nHDRTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_shatteredglass_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bHDRTYPE = false; +#endif // _DEBUG + m_nHDRTYPE = 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCUBEMAP && m_bVERTEXCOLOR && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bHDRTYPE && m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCUBEMAP ) + ( 4 * m_nVERTEXCOLOR ) + ( 8 * m_nENVMAPMASK ) + ( 16 * m_nBASEALPHAENVMAPMASK ) + ( 32 * m_nHDRTYPE ) + ( 96 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_shatteredglass_ps20 psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_HDRTYPE + psh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_shatteredglass_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_shatteredglass_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_shatteredglass_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20b.inc new file mode 100644 index 00000000..5c79bd11 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_ps20b.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_shatteredglass_ps20b_Static_Index +{ +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nHDRTYPE; +#ifdef _DEBUG + bool m_bHDRTYPE; +#endif +public: + void SetHDRTYPE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nHDRTYPE = i; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } + void SetHDRTYPE( bool i ) + { + m_nHDRTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_shatteredglass_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bHDRTYPE = false; +#endif // _DEBUG + m_nHDRTYPE = 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCUBEMAP && m_bVERTEXCOLOR && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bHDRTYPE && m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCUBEMAP ) + ( 4 * m_nVERTEXCOLOR ) + ( 8 * m_nENVMAPMASK ) + ( 16 * m_nBASEALPHAENVMAPMASK ) + ( 32 * m_nHDRTYPE ) + ( 96 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_shatteredglass_ps20b psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_HDRTYPE + psh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_shatteredglass_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_shatteredglass_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_shatteredglass_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_vs20.inc new file mode 100644 index 00000000..a3e18d53 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_ShatteredGlass_vs20.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_shatteredglass_vs20_Static_Index +{ +private: + int m_nENVMAP_MASK; +#ifdef _DEBUG + bool m_bENVMAP_MASK; +#endif +public: + void SetENVMAP_MASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAP_MASK = i; +#ifdef _DEBUG + m_bENVMAP_MASK = true; +#endif + } + void SetENVMAP_MASK( bool i ) + { + m_nENVMAP_MASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAP_MASK = true; +#endif + } +public: + sdk_shatteredglass_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bENVMAP_MASK = false; +#endif // _DEBUG + m_nENVMAP_MASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bENVMAP_MASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nENVMAP_MASK ) + 0; + } +}; +#define shaderStaticTest_sdk_shatteredglass_vs20 vsh_forgot_to_set_static_ENVMAP_MASK + 0 +class sdk_shatteredglass_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_shatteredglass_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_shatteredglass_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20.inc new file mode 100644 index 00000000..49475ba9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20.inc @@ -0,0 +1,237 @@ +#include "shaderlib/cshader.h" +class sdk_watercheap_ps20_Static_Index +{ +private: + int m_nMULTITEXTURE; +#ifdef _DEBUG + bool m_bMULTITEXTURE; +#endif +public: + void SetMULTITEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMULTITEXTURE = i; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } + void SetMULTITEXTURE( bool i ) + { + m_nMULTITEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } +private: + int m_nFRESNEL; +#ifdef _DEBUG + bool m_bFRESNEL; +#endif +public: + void SetFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFRESNEL = i; +#ifdef _DEBUG + m_bFRESNEL = true; +#endif + } + void SetFRESNEL( bool i ) + { + m_nFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bFRESNEL = true; +#endif + } +private: + int m_nBLEND; +#ifdef _DEBUG + bool m_bBLEND; +#endif +public: + void SetBLEND( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLEND = i; +#ifdef _DEBUG + m_bBLEND = true; +#endif + } + void SetBLEND( bool i ) + { + m_nBLEND = i ? 1 : 0; +#ifdef _DEBUG + m_bBLEND = true; +#endif + } +private: + int m_nREFRACTALPHA; +#ifdef _DEBUG + bool m_bREFRACTALPHA; +#endif +public: + void SetREFRACTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACTALPHA = i; +#ifdef _DEBUG + m_bREFRACTALPHA = true; +#endif + } + void SetREFRACTALPHA( bool i ) + { + m_nREFRACTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACTALPHA = true; +#endif + } +private: + int m_nHDRTYPE; +#ifdef _DEBUG + bool m_bHDRTYPE; +#endif +public: + void SetHDRTYPE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nHDRTYPE = i; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } + void SetHDRTYPE( bool i ) + { + m_nHDRTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +public: + sdk_watercheap_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bMULTITEXTURE = false; +#endif // _DEBUG + m_nMULTITEXTURE = 0; +#ifdef _DEBUG + m_bFRESNEL = false; +#endif // _DEBUG + m_nFRESNEL = 0; +#ifdef _DEBUG + m_bBLEND = false; +#endif // _DEBUG + m_nBLEND = 0; +#ifdef _DEBUG + m_bREFRACTALPHA = false; +#endif // _DEBUG + m_nREFRACTALPHA = 0; +#ifdef _DEBUG + m_bHDRTYPE = false; +#endif // _DEBUG + m_nHDRTYPE = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bMULTITEXTURE && m_bFRESNEL && m_bBLEND && m_bREFRACTALPHA && m_bHDRTYPE && m_bNORMAL_DECODE_MODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nMULTITEXTURE ) + ( 8 * m_nFRESNEL ) + ( 16 * m_nBLEND ) + ( 32 * m_nREFRACTALPHA ) + ( 64 * m_nHDRTYPE ) + ( 192 * m_nNORMAL_DECODE_MODE ) + 0; + } +}; +#define shaderStaticTest_sdk_watercheap_ps20 psh_forgot_to_set_static_MULTITEXTURE + psh_forgot_to_set_static_FRESNEL + psh_forgot_to_set_static_BLEND + psh_forgot_to_set_static_REFRACTALPHA + psh_forgot_to_set_static_HDRTYPE + psh_forgot_to_set_static_NORMAL_DECODE_MODE + 0 +class sdk_watercheap_ps20_Dynamic_Index +{ +private: + int m_nHDRENABLED; +#ifdef _DEBUG + bool m_bHDRENABLED; +#endif +public: + void SetHDRENABLED( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHDRENABLED = i; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } + void SetHDRENABLED( bool i ) + { + m_nHDRENABLED = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_watercheap_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bHDRENABLED = false; +#endif // _DEBUG + m_nHDRENABLED = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bHDRENABLED && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nHDRENABLED ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_watercheap_ps20 psh_forgot_to_set_dynamic_HDRENABLED + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20b.inc new file mode 100644 index 00000000..6ddfc76f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_ps20b.inc @@ -0,0 +1,262 @@ +#include "shaderlib/cshader.h" +class sdk_watercheap_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nMULTITEXTURE; +#ifdef _DEBUG + bool m_bMULTITEXTURE; +#endif +public: + void SetMULTITEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMULTITEXTURE = i; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } + void SetMULTITEXTURE( bool i ) + { + m_nMULTITEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } +private: + int m_nFRESNEL; +#ifdef _DEBUG + bool m_bFRESNEL; +#endif +public: + void SetFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFRESNEL = i; +#ifdef _DEBUG + m_bFRESNEL = true; +#endif + } + void SetFRESNEL( bool i ) + { + m_nFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bFRESNEL = true; +#endif + } +private: + int m_nBLEND; +#ifdef _DEBUG + bool m_bBLEND; +#endif +public: + void SetBLEND( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLEND = i; +#ifdef _DEBUG + m_bBLEND = true; +#endif + } + void SetBLEND( bool i ) + { + m_nBLEND = i ? 1 : 0; +#ifdef _DEBUG + m_bBLEND = true; +#endif + } +private: + int m_nREFRACTALPHA; +#ifdef _DEBUG + bool m_bREFRACTALPHA; +#endif +public: + void SetREFRACTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACTALPHA = i; +#ifdef _DEBUG + m_bREFRACTALPHA = true; +#endif + } + void SetREFRACTALPHA( bool i ) + { + m_nREFRACTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACTALPHA = true; +#endif + } +private: + int m_nHDRTYPE; +#ifdef _DEBUG + bool m_bHDRTYPE; +#endif +public: + void SetHDRTYPE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nHDRTYPE = i; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } + void SetHDRTYPE( bool i ) + { + m_nHDRTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +public: + sdk_watercheap_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bMULTITEXTURE = false; +#endif // _DEBUG + m_nMULTITEXTURE = 0; +#ifdef _DEBUG + m_bFRESNEL = false; +#endif // _DEBUG + m_nFRESNEL = 0; +#ifdef _DEBUG + m_bBLEND = false; +#endif // _DEBUG + m_nBLEND = 0; +#ifdef _DEBUG + m_bREFRACTALPHA = false; +#endif // _DEBUG + m_nREFRACTALPHA = 0; +#ifdef _DEBUG + m_bHDRTYPE = false; +#endif // _DEBUG + m_nHDRTYPE = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bMULTITEXTURE && m_bFRESNEL && m_bBLEND && m_bREFRACTALPHA && m_bHDRTYPE && m_bNORMAL_DECODE_MODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nMULTITEXTURE ) + ( 16 * m_nFRESNEL ) + ( 32 * m_nBLEND ) + ( 64 * m_nREFRACTALPHA ) + ( 128 * m_nHDRTYPE ) + ( 384 * m_nNORMAL_DECODE_MODE ) + 0; + } +}; +#define shaderStaticTest_sdk_watercheap_ps20b psh_forgot_to_set_static_MULTITEXTURE + psh_forgot_to_set_static_FRESNEL + psh_forgot_to_set_static_BLEND + psh_forgot_to_set_static_REFRACTALPHA + psh_forgot_to_set_static_HDRTYPE + psh_forgot_to_set_static_NORMAL_DECODE_MODE + 0 +class sdk_watercheap_ps20b_Dynamic_Index +{ +private: + int m_nHDRENABLED; +#ifdef _DEBUG + bool m_bHDRENABLED; +#endif +public: + void SetHDRENABLED( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHDRENABLED = i; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } + void SetHDRENABLED( bool i ) + { + m_nHDRENABLED = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_watercheap_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bHDRENABLED = false; +#endif // _DEBUG + m_nHDRENABLED = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bHDRENABLED && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nHDRENABLED ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_watercheap_ps20b psh_forgot_to_set_dynamic_HDRENABLED + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_vs20.inc new file mode 100644 index 00000000..49244f88 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WaterCheap_vs20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_watercheap_vs20_Static_Index +{ +private: + int m_nBLEND; +#ifdef _DEBUG + bool m_bBLEND; +#endif +public: + void SetBLEND( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLEND = i; +#ifdef _DEBUG + m_bBLEND = true; +#endif + } + void SetBLEND( bool i ) + { + m_nBLEND = i ? 1 : 0; +#ifdef _DEBUG + m_bBLEND = true; +#endif + } +public: + sdk_watercheap_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bBLEND = false; +#endif // _DEBUG + m_nBLEND = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBLEND; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nBLEND ) + 0; + } +}; +#define shaderStaticTest_sdk_watercheap_vs20 vsh_forgot_to_set_static_BLEND + 0 +class sdk_watercheap_vs20_Dynamic_Index +{ +public: + sdk_watercheap_vs20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_watercheap_vs20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20.inc new file mode 100644 index 00000000..4929b59c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_water_ps20_Static_Index +{ +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +private: + int m_nMULTITEXTURE; +#ifdef _DEBUG + bool m_bMULTITEXTURE; +#endif +public: + void SetMULTITEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMULTITEXTURE = i; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } + void SetMULTITEXTURE( bool i ) + { + m_nMULTITEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } +private: + int m_nREFLECT; +#ifdef _DEBUG + bool m_bREFLECT; +#endif +public: + void SetREFLECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFLECT = i; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } + void SetREFLECT( bool i ) + { + m_nREFLECT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } +private: + int m_nREFRACT; +#ifdef _DEBUG + bool m_bREFRACT; +#endif +public: + void SetREFRACT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACT = i; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } + void SetREFRACT( bool i ) + { + m_nREFRACT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } +private: + int m_nABOVEWATER; +#ifdef _DEBUG + bool m_bABOVEWATER; +#endif +public: + void SetABOVEWATER( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nABOVEWATER = i; +#ifdef _DEBUG + m_bABOVEWATER = true; +#endif + } + void SetABOVEWATER( bool i ) + { + m_nABOVEWATER = i ? 1 : 0; +#ifdef _DEBUG + m_bABOVEWATER = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +public: + sdk_water_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; +#ifdef _DEBUG + m_bMULTITEXTURE = false; +#endif // _DEBUG + m_nMULTITEXTURE = 0; +#ifdef _DEBUG + m_bREFLECT = false; +#endif // _DEBUG + m_nREFLECT = 0; +#ifdef _DEBUG + m_bREFRACT = false; +#endif // _DEBUG + m_nREFRACT = 0; +#ifdef _DEBUG + m_bABOVEWATER = false; +#endif // _DEBUG + m_nABOVEWATER = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBASETEXTURE && m_bMULTITEXTURE && m_bREFLECT && m_bREFRACT && m_bABOVEWATER && m_bNORMAL_DECODE_MODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nBASETEXTURE ) + ( 4 * m_nMULTITEXTURE ) + ( 8 * m_nREFLECT ) + ( 16 * m_nREFRACT ) + ( 32 * m_nABOVEWATER ) + ( 64 * m_nNORMAL_DECODE_MODE ) + 0; + } +}; +#define shaderStaticTest_sdk_water_ps20 psh_forgot_to_set_static_BASETEXTURE + psh_forgot_to_set_static_MULTITEXTURE + psh_forgot_to_set_static_REFLECT + psh_forgot_to_set_static_REFRACT + psh_forgot_to_set_static_ABOVEWATER + psh_forgot_to_set_static_NORMAL_DECODE_MODE + 0 +class sdk_water_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_water_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_water_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20b.inc new file mode 100644 index 00000000..675efa7a --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_ps20b.inc @@ -0,0 +1,287 @@ +#include "shaderlib/cshader.h" +class sdk_water_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +private: + int m_nMULTITEXTURE; +#ifdef _DEBUG + bool m_bMULTITEXTURE; +#endif +public: + void SetMULTITEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMULTITEXTURE = i; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } + void SetMULTITEXTURE( bool i ) + { + m_nMULTITEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } +private: + int m_nREFLECT; +#ifdef _DEBUG + bool m_bREFLECT; +#endif +public: + void SetREFLECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFLECT = i; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } + void SetREFLECT( bool i ) + { + m_nREFLECT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } +private: + int m_nREFRACT; +#ifdef _DEBUG + bool m_bREFRACT; +#endif +public: + void SetREFRACT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACT = i; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } + void SetREFRACT( bool i ) + { + m_nREFRACT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } +private: + int m_nABOVEWATER; +#ifdef _DEBUG + bool m_bABOVEWATER; +#endif +public: + void SetABOVEWATER( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nABOVEWATER = i; +#ifdef _DEBUG + m_bABOVEWATER = true; +#endif + } + void SetABOVEWATER( bool i ) + { + m_nABOVEWATER = i ? 1 : 0; +#ifdef _DEBUG + m_bABOVEWATER = true; +#endif + } +private: + int m_nBLURRY_REFRACT; +#ifdef _DEBUG + bool m_bBLURRY_REFRACT; +#endif +public: + void SetBLURRY_REFRACT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLURRY_REFRACT = i; +#ifdef _DEBUG + m_bBLURRY_REFRACT = true; +#endif + } + void SetBLURRY_REFRACT( bool i ) + { + m_nBLURRY_REFRACT = i ? 1 : 0; +#ifdef _DEBUG + m_bBLURRY_REFRACT = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +public: + sdk_water_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; +#ifdef _DEBUG + m_bMULTITEXTURE = false; +#endif // _DEBUG + m_nMULTITEXTURE = 0; +#ifdef _DEBUG + m_bREFLECT = false; +#endif // _DEBUG + m_nREFLECT = 0; +#ifdef _DEBUG + m_bREFRACT = false; +#endif // _DEBUG + m_nREFRACT = 0; +#ifdef _DEBUG + m_bABOVEWATER = false; +#endif // _DEBUG + m_nABOVEWATER = 0; +#ifdef _DEBUG + m_bBLURRY_REFRACT = false; +#endif // _DEBUG + m_nBLURRY_REFRACT = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bBASETEXTURE && m_bMULTITEXTURE && m_bREFLECT && m_bREFRACT && m_bABOVEWATER && m_bBLURRY_REFRACT && m_bNORMAL_DECODE_MODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nBASETEXTURE ) + ( 16 * m_nMULTITEXTURE ) + ( 32 * m_nREFLECT ) + ( 64 * m_nREFRACT ) + ( 128 * m_nABOVEWATER ) + ( 256 * m_nBLURRY_REFRACT ) + ( 512 * m_nNORMAL_DECODE_MODE ) + 0; + } +}; +#define shaderStaticTest_sdk_water_ps20b psh_forgot_to_set_static_BASETEXTURE + psh_forgot_to_set_static_MULTITEXTURE + psh_forgot_to_set_static_REFLECT + psh_forgot_to_set_static_REFRACT + psh_forgot_to_set_static_ABOVEWATER + psh_forgot_to_set_static_BLURRY_REFRACT + psh_forgot_to_set_static_NORMAL_DECODE_MODE + 0 +class sdk_water_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_water_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_water_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_vs20.inc new file mode 100644 index 00000000..67e8f134 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_Water_vs20.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_water_vs20_Static_Index +{ +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +private: + int m_nMULTITEXTURE; +#ifdef _DEBUG + bool m_bMULTITEXTURE; +#endif +public: + void SetMULTITEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMULTITEXTURE = i; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } + void SetMULTITEXTURE( bool i ) + { + m_nMULTITEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bMULTITEXTURE = true; +#endif + } +public: + sdk_water_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; +#ifdef _DEBUG + m_bMULTITEXTURE = false; +#endif // _DEBUG + m_nMULTITEXTURE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBASETEXTURE && m_bMULTITEXTURE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nBASETEXTURE ) + ( 2 * m_nMULTITEXTURE ) + 0; + } +}; +#define shaderStaticTest_sdk_water_vs20 vsh_forgot_to_set_static_BASETEXTURE + vsh_forgot_to_set_static_MULTITEXTURE + 0 +class sdk_water_vs20_Dynamic_Index +{ +public: + sdk_water_vs20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_water_vs20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20.inc new file mode 100644 index 00000000..67613947 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_worldvertextransition_ps20_Static_Index +{ +private: + int m_nMACROS; +#ifdef _DEBUG + bool m_bMACROS; +#endif +public: + void SetMACROS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMACROS = i; +#ifdef _DEBUG + m_bMACROS = true; +#endif + } + void SetMACROS( bool i ) + { + m_nMACROS = i ? 1 : 0; +#ifdef _DEBUG + m_bMACROS = true; +#endif + } +public: + sdk_worldvertextransition_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bMACROS = false; +#endif // _DEBUG + m_nMACROS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bMACROS; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nMACROS ) + 0; + } +}; +#define shaderStaticTest_sdk_worldvertextransition_ps20 psh_forgot_to_set_static_MACROS + 0 +class sdk_worldvertextransition_ps20_Dynamic_Index +{ +public: + sdk_worldvertextransition_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_worldvertextransition_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20b.inc new file mode 100644 index 00000000..cd00eaf5 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_ps20b.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_worldvertextransition_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nMACROS; +#ifdef _DEBUG + bool m_bMACROS; +#endif +public: + void SetMACROS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMACROS = i; +#ifdef _DEBUG + m_bMACROS = true; +#endif + } + void SetMACROS( bool i ) + { + m_nMACROS = i ? 1 : 0; +#ifdef _DEBUG + m_bMACROS = true; +#endif + } +public: + sdk_worldvertextransition_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bMACROS = false; +#endif // _DEBUG + m_nMACROS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bMACROS; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + ( 2 * m_nMACROS ) + 0; + } +}; +#define shaderStaticTest_sdk_worldvertextransition_ps20b psh_forgot_to_set_static_MACROS + 0 +class sdk_worldvertextransition_ps20b_Dynamic_Index +{ +public: + sdk_worldvertextransition_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_worldvertextransition_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_vs20.inc new file mode 100644 index 00000000..fbf30e08 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_WorldVertexTransition_vs20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_worldvertextransition_vs20_Static_Index +{ +public: + sdk_worldvertextransition_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_worldvertextransition_vs20 0 +class sdk_worldvertextransition_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_worldvertextransition_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_worldvertextransition_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20.inc new file mode 100644 index 00000000..6d6f0122 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_cable_ps20_Static_Index +{ +public: + sdk_cable_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_cable_ps20 0 +class sdk_cable_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_cable_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_cable_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20b.inc new file mode 100644 index 00000000..0e73dbdf --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_ps20b.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_cable_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_cable_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_cable_ps20b 0 +class sdk_cable_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_cable_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_cable_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_vs20.inc new file mode 100644 index 00000000..d2ef4642 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cable_vs20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_cable_vs20_Static_Index +{ +public: + sdk_cable_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_cable_vs20 0 +class sdk_cable_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_cable_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_cable_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20.inc new file mode 100644 index 00000000..5d52f92f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_cloak_blended_pass_ps20_Static_Index +{ +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +public: + sdk_cloak_blended_pass_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBUMPMAP; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nBUMPMAP ) + 0; + } +}; +#define shaderStaticTest_sdk_cloak_blended_pass_ps20 psh_forgot_to_set_static_BUMPMAP + 0 +class sdk_cloak_blended_pass_ps20_Dynamic_Index +{ +public: + sdk_cloak_blended_pass_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_cloak_blended_pass_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20b.inc new file mode 100644 index 00000000..c724bbd2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps20b.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_cloak_blended_pass_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +public: + sdk_cloak_blended_pass_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bBUMPMAP; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + ( 2 * m_nBUMPMAP ) + 0; + } +}; +#define shaderStaticTest_sdk_cloak_blended_pass_ps20b psh_forgot_to_set_static_BUMPMAP + 0 +class sdk_cloak_blended_pass_ps20b_Dynamic_Index +{ +public: + sdk_cloak_blended_pass_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_cloak_blended_pass_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps30.inc new file mode 100644 index 00000000..778aaf04 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_ps30.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_cloak_blended_pass_ps30_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +public: + sdk_cloak_blended_pass_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bBUMPMAP; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + ( 2 * m_nBUMPMAP ) + 0; + } +}; +#define shaderStaticTest_sdk_cloak_blended_pass_ps30 psh_forgot_to_set_static_BUMPMAP + 0 +class sdk_cloak_blended_pass_ps30_Dynamic_Index +{ +public: + sdk_cloak_blended_pass_ps30_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_cloak_blended_pass_ps30 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs20.inc new file mode 100644 index 00000000..7f431299 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs20.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_cloak_blended_pass_vs20_Static_Index +{ +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +public: + sdk_cloak_blended_pass_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBUMPMAP; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nBUMPMAP ) + 0; + } +}; +#define shaderStaticTest_sdk_cloak_blended_pass_vs20 vsh_forgot_to_set_static_BUMPMAP + 0 +class sdk_cloak_blended_pass_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_cloak_blended_pass_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_cloak_blended_pass_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs30.inc new file mode 100644 index 00000000..f3121788 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_cloak_blended_pass_vs30.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_cloak_blended_pass_vs30_Static_Index +{ +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +public: + sdk_cloak_blended_pass_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBUMPMAP; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 8 * m_nBUMPMAP ) + 0; + } +}; +#define shaderStaticTest_sdk_cloak_blended_pass_vs30 vsh_forgot_to_set_static_BUMPMAP + 0 +class sdk_cloak_blended_pass_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_cloak_blended_pass_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_cloak_blended_pass_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20.inc new file mode 100644 index 00000000..cc1e6113 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_decalmodulate_ps20_Static_Index +{ +private: + int m_nVERTEXALPHA; +#ifdef _DEBUG + bool m_bVERTEXALPHA; +#endif +public: + void SetVERTEXALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXALPHA = i; +#ifdef _DEBUG + m_bVERTEXALPHA = true; +#endif + } + void SetVERTEXALPHA( bool i ) + { + m_nVERTEXALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXALPHA = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +public: + sdk_decalmodulate_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXALPHA = false; +#endif // _DEBUG + m_nVERTEXALPHA = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXALPHA && m_bFLASHLIGHT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nVERTEXALPHA ) + ( 4 * m_nFLASHLIGHT ) + 0; + } +}; +#define shaderStaticTest_sdk_decalmodulate_ps20 psh_forgot_to_set_static_VERTEXALPHA + psh_forgot_to_set_static_FLASHLIGHT + 0 +class sdk_decalmodulate_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_decalmodulate_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_decalmodulate_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20b.inc new file mode 100644 index 00000000..e35c8a54 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps20b.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_decalmodulate_ps20b_Static_Index +{ +private: + int m_nVERTEXALPHA; +#ifdef _DEBUG + bool m_bVERTEXALPHA; +#endif +public: + void SetVERTEXALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXALPHA = i; +#ifdef _DEBUG + m_bVERTEXALPHA = true; +#endif + } + void SetVERTEXALPHA( bool i ) + { + m_nVERTEXALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXALPHA = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_decalmodulate_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXALPHA = false; +#endif // _DEBUG + m_nVERTEXALPHA = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXALPHA && m_bFLASHLIGHT && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nVERTEXALPHA ) + ( 8 * m_nFLASHLIGHT ) + ( 16 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_decalmodulate_ps20b psh_forgot_to_set_static_VERTEXALPHA + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_decalmodulate_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_decalmodulate_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_decalmodulate_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps30.inc new file mode 100644 index 00000000..db6747a2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_ps30.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_decalmodulate_ps30_Static_Index +{ +private: + int m_nVERTEXALPHA; +#ifdef _DEBUG + bool m_bVERTEXALPHA; +#endif +public: + void SetVERTEXALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXALPHA = i; +#ifdef _DEBUG + m_bVERTEXALPHA = true; +#endif + } + void SetVERTEXALPHA( bool i ) + { + m_nVERTEXALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXALPHA = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_decalmodulate_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXALPHA = false; +#endif // _DEBUG + m_nVERTEXALPHA = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXALPHA && m_bFLASHLIGHT && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nVERTEXALPHA ) + ( 8 * m_nFLASHLIGHT ) + ( 16 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_decalmodulate_ps30 psh_forgot_to_set_static_VERTEXALPHA + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_decalmodulate_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_decalmodulate_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_decalmodulate_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs20.inc new file mode 100644 index 00000000..0f571fd7 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs20.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_decalmodulate_vs20_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +public: + sdk_decalmodulate_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR && m_bLIGHTING_PREVIEW && m_bFLASHLIGHT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nVERTEXCOLOR ) + ( 8 * m_nLIGHTING_PREVIEW ) + ( 16 * m_nFLASHLIGHT ) + 0; + } +}; +#define shaderStaticTest_sdk_decalmodulate_vs20 vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_LIGHTING_PREVIEW + vsh_forgot_to_set_static_FLASHLIGHT + 0 +class sdk_decalmodulate_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_decalmodulate_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG && m_bCOMPRESSED_VERTS && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + ( 2 * m_nCOMPRESSED_VERTS ) + ( 4 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_decalmodulate_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs30.inc new file mode 100644 index 00000000..38617b8c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_decalmodulate_vs30.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_decalmodulate_vs30_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +public: + sdk_decalmodulate_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR && m_bLIGHTING_PREVIEW && m_bFLASHLIGHT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 16 * m_nVERTEXCOLOR ) + ( 32 * m_nLIGHTING_PREVIEW ) + ( 64 * m_nFLASHLIGHT ) + 0; + } +}; +#define shaderStaticTest_sdk_decalmodulate_vs30 vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_LIGHTING_PREVIEW + vsh_forgot_to_set_static_FLASHLIGHT + 0 +class sdk_decalmodulate_vs30_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_decalmodulate_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG && m_bCOMPRESSED_VERTS && m_bSKINNING && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + ( 2 * m_nCOMPRESSED_VERTS ) + ( 4 * m_nSKINNING ) + ( 8 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_decalmodulate_vs30 vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20.inc new file mode 100644 index 00000000..938ab9bd --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_depthwrite_ps20_Static_Index +{ +private: + int m_nCOLOR_DEPTH; +#ifdef _DEBUG + bool m_bCOLOR_DEPTH; +#endif +public: + void SetCOLOR_DEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLOR_DEPTH = i; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } + void SetCOLOR_DEPTH( bool i ) + { + m_nCOLOR_DEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } +public: + sdk_depthwrite_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bCOLOR_DEPTH = false; +#endif // _DEBUG + m_nCOLOR_DEPTH = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCOLOR_DEPTH; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCOLOR_DEPTH ) + 0; + } +}; +#define shaderStaticTest_sdk_depthwrite_ps20 psh_forgot_to_set_static_COLOR_DEPTH + 0 +class sdk_depthwrite_ps20_Dynamic_Index +{ +private: + int m_nALPHACLIP; +#ifdef _DEBUG + bool m_bALPHACLIP; +#endif +public: + void SetALPHACLIP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nALPHACLIP = i; +#ifdef _DEBUG + m_bALPHACLIP = true; +#endif + } + void SetALPHACLIP( bool i ) + { + m_nALPHACLIP = i ? 1 : 0; +#ifdef _DEBUG + m_bALPHACLIP = true; +#endif + } +public: + sdk_depthwrite_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bALPHACLIP = false; +#endif // _DEBUG + m_nALPHACLIP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bALPHACLIP; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nALPHACLIP ) + 0; + } +}; +#define shaderDynamicTest_sdk_depthwrite_ps20 psh_forgot_to_set_dynamic_ALPHACLIP + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20b.inc new file mode 100644 index 00000000..fc639347 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps20b.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_depthwrite_ps20b_Static_Index +{ +private: + int m_nCOLOR_DEPTH; +#ifdef _DEBUG + bool m_bCOLOR_DEPTH; +#endif +public: + void SetCOLOR_DEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLOR_DEPTH = i; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } + void SetCOLOR_DEPTH( bool i ) + { + m_nCOLOR_DEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } +public: + sdk_depthwrite_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCOLOR_DEPTH = false; +#endif // _DEBUG + m_nCOLOR_DEPTH = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCOLOR_DEPTH; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCOLOR_DEPTH ) + 0; + } +}; +#define shaderStaticTest_sdk_depthwrite_ps20b psh_forgot_to_set_static_COLOR_DEPTH + 0 +class sdk_depthwrite_ps20b_Dynamic_Index +{ +private: + int m_nALPHACLIP; +#ifdef _DEBUG + bool m_bALPHACLIP; +#endif +public: + void SetALPHACLIP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nALPHACLIP = i; +#ifdef _DEBUG + m_bALPHACLIP = true; +#endif + } + void SetALPHACLIP( bool i ) + { + m_nALPHACLIP = i ? 1 : 0; +#ifdef _DEBUG + m_bALPHACLIP = true; +#endif + } +public: + sdk_depthwrite_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bALPHACLIP = false; +#endif // _DEBUG + m_nALPHACLIP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bALPHACLIP; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nALPHACLIP ) + 0; + } +}; +#define shaderDynamicTest_sdk_depthwrite_ps20b psh_forgot_to_set_dynamic_ALPHACLIP + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps30.inc new file mode 100644 index 00000000..7ebec65d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_ps30.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_depthwrite_ps30_Static_Index +{ +private: + int m_nCOLOR_DEPTH; +#ifdef _DEBUG + bool m_bCOLOR_DEPTH; +#endif +public: + void SetCOLOR_DEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLOR_DEPTH = i; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } + void SetCOLOR_DEPTH( bool i ) + { + m_nCOLOR_DEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } +public: + sdk_depthwrite_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCOLOR_DEPTH = false; +#endif // _DEBUG + m_nCOLOR_DEPTH = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCOLOR_DEPTH; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCOLOR_DEPTH ) + 0; + } +}; +#define shaderStaticTest_sdk_depthwrite_ps30 psh_forgot_to_set_static_COLOR_DEPTH + 0 +class sdk_depthwrite_ps30_Dynamic_Index +{ +private: + int m_nALPHACLIP; +#ifdef _DEBUG + bool m_bALPHACLIP; +#endif +public: + void SetALPHACLIP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nALPHACLIP = i; +#ifdef _DEBUG + m_bALPHACLIP = true; +#endif + } + void SetALPHACLIP( bool i ) + { + m_nALPHACLIP = i ? 1 : 0; +#ifdef _DEBUG + m_bALPHACLIP = true; +#endif + } +public: + sdk_depthwrite_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bALPHACLIP = false; +#endif // _DEBUG + m_nALPHACLIP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bALPHACLIP; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nALPHACLIP ) + 0; + } +}; +#define shaderDynamicTest_sdk_depthwrite_ps30 psh_forgot_to_set_dynamic_ALPHACLIP + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs20.inc new file mode 100644 index 00000000..09a93ed2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs20.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_depthwrite_vs20_Static_Index +{ +private: + int m_nONLY_PROJECT_POSITION; +#ifdef _DEBUG + bool m_bONLY_PROJECT_POSITION; +#endif +public: + void SetONLY_PROJECT_POSITION( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nONLY_PROJECT_POSITION = i; +#ifdef _DEBUG + m_bONLY_PROJECT_POSITION = true; +#endif + } + void SetONLY_PROJECT_POSITION( bool i ) + { + m_nONLY_PROJECT_POSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bONLY_PROJECT_POSITION = true; +#endif + } +private: + int m_nCOLOR_DEPTH; +#ifdef _DEBUG + bool m_bCOLOR_DEPTH; +#endif +public: + void SetCOLOR_DEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLOR_DEPTH = i; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } + void SetCOLOR_DEPTH( bool i ) + { + m_nCOLOR_DEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } +private: + int m_nTREESWAY; +#ifdef _DEBUG + bool m_bTREESWAY; +#endif +public: + void SetTREESWAY( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nTREESWAY = i; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } + void SetTREESWAY( bool i ) + { + m_nTREESWAY = i ? 1 : 0; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } +public: + sdk_depthwrite_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bONLY_PROJECT_POSITION = false; +#endif // _DEBUG + m_nONLY_PROJECT_POSITION = 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = false; +#endif // _DEBUG + m_nCOLOR_DEPTH = 0; +#ifdef _DEBUG + m_bTREESWAY = false; +#endif // _DEBUG + m_nTREESWAY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bONLY_PROJECT_POSITION && m_bCOLOR_DEPTH && m_bTREESWAY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nONLY_PROJECT_POSITION ) + ( 4 * m_nCOLOR_DEPTH ) + ( 8 * m_nTREESWAY ) + 0; + } +}; +#define shaderStaticTest_sdk_depthwrite_vs20 vsh_forgot_to_set_static_ONLY_PROJECT_POSITION + vsh_forgot_to_set_static_COLOR_DEPTH + vsh_forgot_to_set_static_TREESWAY + 0 +class sdk_depthwrite_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_depthwrite_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_depthwrite_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs30.inc new file mode 100644 index 00000000..d208700a --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_depthwrite_vs30.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_depthwrite_vs30_Static_Index +{ +private: + int m_nONLY_PROJECT_POSITION; +#ifdef _DEBUG + bool m_bONLY_PROJECT_POSITION; +#endif +public: + void SetONLY_PROJECT_POSITION( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nONLY_PROJECT_POSITION = i; +#ifdef _DEBUG + m_bONLY_PROJECT_POSITION = true; +#endif + } + void SetONLY_PROJECT_POSITION( bool i ) + { + m_nONLY_PROJECT_POSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bONLY_PROJECT_POSITION = true; +#endif + } +private: + int m_nCOLOR_DEPTH; +#ifdef _DEBUG + bool m_bCOLOR_DEPTH; +#endif +public: + void SetCOLOR_DEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLOR_DEPTH = i; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } + void SetCOLOR_DEPTH( bool i ) + { + m_nCOLOR_DEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = true; +#endif + } +private: + int m_nTREESWAY; +#ifdef _DEBUG + bool m_bTREESWAY; +#endif +public: + void SetTREESWAY( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nTREESWAY = i; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } + void SetTREESWAY( bool i ) + { + m_nTREESWAY = i ? 1 : 0; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } +public: + sdk_depthwrite_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bONLY_PROJECT_POSITION = false; +#endif // _DEBUG + m_nONLY_PROJECT_POSITION = 0; +#ifdef _DEBUG + m_bCOLOR_DEPTH = false; +#endif // _DEBUG + m_nCOLOR_DEPTH = 0; +#ifdef _DEBUG + m_bTREESWAY = false; +#endif // _DEBUG + m_nTREESWAY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bONLY_PROJECT_POSITION && m_bCOLOR_DEPTH && m_bTREESWAY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 8 * m_nONLY_PROJECT_POSITION ) + ( 8 * m_nCOLOR_DEPTH ) + ( 16 * m_nTREESWAY ) + 0; + } +}; +#define shaderStaticTest_sdk_depthwrite_vs30 vsh_forgot_to_set_static_ONLY_PROJECT_POSITION + vsh_forgot_to_set_static_COLOR_DEPTH + vsh_forgot_to_set_static_TREESWAY + 0 +class sdk_depthwrite_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_depthwrite_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_depthwrite_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20.inc new file mode 100644 index 00000000..c415f0a5 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_emissive_scroll_blended_pass_ps20_Static_Index +{ +public: + sdk_emissive_scroll_blended_pass_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_emissive_scroll_blended_pass_ps20 0 +class sdk_emissive_scroll_blended_pass_ps20_Dynamic_Index +{ +public: + sdk_emissive_scroll_blended_pass_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_emissive_scroll_blended_pass_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20b.inc new file mode 100644 index 00000000..42dd5742 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps20b.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_emissive_scroll_blended_pass_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_emissive_scroll_blended_pass_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_emissive_scroll_blended_pass_ps20b 0 +class sdk_emissive_scroll_blended_pass_ps20b_Dynamic_Index +{ +public: + sdk_emissive_scroll_blended_pass_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_emissive_scroll_blended_pass_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps30.inc new file mode 100644 index 00000000..f0e08917 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_ps30.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_emissive_scroll_blended_pass_ps30_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_emissive_scroll_blended_pass_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_emissive_scroll_blended_pass_ps30 0 +class sdk_emissive_scroll_blended_pass_ps30_Dynamic_Index +{ +public: + sdk_emissive_scroll_blended_pass_ps30_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_emissive_scroll_blended_pass_ps30 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs20.inc new file mode 100644 index 00000000..a7f2793f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs20.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_emissive_scroll_blended_pass_vs20_Static_Index +{ +public: + sdk_emissive_scroll_blended_pass_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_emissive_scroll_blended_pass_vs20 0 +class sdk_emissive_scroll_blended_pass_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_emissive_scroll_blended_pass_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_emissive_scroll_blended_pass_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs30.inc new file mode 100644 index 00000000..5abcbad5 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_emissive_scroll_blended_pass_vs30.inc @@ -0,0 +1,110 @@ +#include "shaderlib/cshader.h" +class sdk_emissive_scroll_blended_pass_vs30_Static_Index +{ +public: + sdk_emissive_scroll_blended_pass_vs30_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_emissive_scroll_blended_pass_vs30 0 +class sdk_emissive_scroll_blended_pass_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_emissive_scroll_blended_pass_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_emissive_scroll_blended_pass_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_engine_post_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_engine_post_ps20b.inc new file mode 100644 index 00000000..8d65f79a --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_engine_post_ps20b.inc @@ -0,0 +1,362 @@ +#include "shaderlib/cshader.h" +class sdk_engine_post_ps20b_Static_Index +{ +private: + int m_nTOOL_MODE; +#ifdef _DEBUG + bool m_bTOOL_MODE; +#endif +public: + void SetTOOL_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTOOL_MODE = i; +#ifdef _DEBUG + m_bTOOL_MODE = true; +#endif + } + void SetTOOL_MODE( bool i ) + { + m_nTOOL_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bTOOL_MODE = true; +#endif + } +private: + int m_nDEPTH_BLUR_ENABLE; +#ifdef _DEBUG + bool m_bDEPTH_BLUR_ENABLE; +#endif +public: + void SetDEPTH_BLUR_ENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDEPTH_BLUR_ENABLE = i; +#ifdef _DEBUG + m_bDEPTH_BLUR_ENABLE = true; +#endif + } + void SetDEPTH_BLUR_ENABLE( bool i ) + { + m_nDEPTH_BLUR_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bDEPTH_BLUR_ENABLE = true; +#endif + } +public: + sdk_engine_post_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bTOOL_MODE = false; +#endif // _DEBUG + m_nTOOL_MODE = 0; +#ifdef _DEBUG + m_bDEPTH_BLUR_ENABLE = false; +#endif // _DEBUG + m_nDEPTH_BLUR_ENABLE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bTOOL_MODE && m_bDEPTH_BLUR_ENABLE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2048 * m_nTOOL_MODE ) + ( 4096 * m_nDEPTH_BLUR_ENABLE ) + 0; + } +}; +#define shaderStaticTest_sdk_engine_post_ps20b psh_forgot_to_set_static_TOOL_MODE + psh_forgot_to_set_static_DEPTH_BLUR_ENABLE + 0 +class sdk_engine_post_ps20b_Dynamic_Index +{ +private: + int m_nAA_ENABLE; +#ifdef _DEBUG + bool m_bAA_ENABLE; +#endif +public: + void SetAA_ENABLE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nAA_ENABLE = i; +#ifdef _DEBUG + m_bAA_ENABLE = true; +#endif + } + void SetAA_ENABLE( bool i ) + { + m_nAA_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bAA_ENABLE = true; +#endif + } +private: + int m_nCOL_CORRECT_NUM_LOOKUPS; +#ifdef _DEBUG + bool m_bCOL_CORRECT_NUM_LOOKUPS; +#endif +public: + void SetCOL_CORRECT_NUM_LOOKUPS( int i ) + { + Assert( i >= 0 && i <= 3 ); + m_nCOL_CORRECT_NUM_LOOKUPS = i; +#ifdef _DEBUG + m_bCOL_CORRECT_NUM_LOOKUPS = true; +#endif + } + void SetCOL_CORRECT_NUM_LOOKUPS( bool i ) + { + m_nCOL_CORRECT_NUM_LOOKUPS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOL_CORRECT_NUM_LOOKUPS = true; +#endif + } +private: + int m_nCONVERT_FROM_LINEAR; +#ifdef _DEBUG + bool m_bCONVERT_FROM_LINEAR; +#endif +public: + void SetCONVERT_FROM_LINEAR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_FROM_LINEAR = i; +#ifdef _DEBUG + m_bCONVERT_FROM_LINEAR = true; +#endif + } + void SetCONVERT_FROM_LINEAR( bool i ) + { + m_nCONVERT_FROM_LINEAR = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_FROM_LINEAR = true; +#endif + } +private: + int m_nCONVERT_TO_LINEAR; +#ifdef _DEBUG + bool m_bCONVERT_TO_LINEAR; +#endif +public: + void SetCONVERT_TO_LINEAR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_LINEAR = i; +#ifdef _DEBUG + m_bCONVERT_TO_LINEAR = true; +#endif + } + void SetCONVERT_TO_LINEAR( bool i ) + { + m_nCONVERT_TO_LINEAR = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_LINEAR = true; +#endif + } +private: + int m_nNOISE_ENABLE; +#ifdef _DEBUG + bool m_bNOISE_ENABLE; +#endif +public: + void SetNOISE_ENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNOISE_ENABLE = i; +#ifdef _DEBUG + m_bNOISE_ENABLE = true; +#endif + } + void SetNOISE_ENABLE( bool i ) + { + m_nNOISE_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bNOISE_ENABLE = true; +#endif + } +private: + int m_nVIGNETTE_ENABLE; +#ifdef _DEBUG + bool m_bVIGNETTE_ENABLE; +#endif +public: + void SetVIGNETTE_ENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVIGNETTE_ENABLE = i; +#ifdef _DEBUG + m_bVIGNETTE_ENABLE = true; +#endif + } + void SetVIGNETTE_ENABLE( bool i ) + { + m_nVIGNETTE_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bVIGNETTE_ENABLE = true; +#endif + } +private: + int m_nLOCAL_CONTRAST_ENABLE; +#ifdef _DEBUG + bool m_bLOCAL_CONTRAST_ENABLE; +#endif +public: + void SetLOCAL_CONTRAST_ENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLOCAL_CONTRAST_ENABLE = i; +#ifdef _DEBUG + m_bLOCAL_CONTRAST_ENABLE = true; +#endif + } + void SetLOCAL_CONTRAST_ENABLE( bool i ) + { + m_nLOCAL_CONTRAST_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bLOCAL_CONTRAST_ENABLE = true; +#endif + } +private: + int m_nBLURRED_VIGNETTE_ENABLE; +#ifdef _DEBUG + bool m_bBLURRED_VIGNETTE_ENABLE; +#endif +public: + void SetBLURRED_VIGNETTE_ENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLURRED_VIGNETTE_ENABLE = i; +#ifdef _DEBUG + m_bBLURRED_VIGNETTE_ENABLE = true; +#endif + } + void SetBLURRED_VIGNETTE_ENABLE( bool i ) + { + m_nBLURRED_VIGNETTE_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bBLURRED_VIGNETTE_ENABLE = true; +#endif + } +private: + int m_nVOMIT_ENABLE; +#ifdef _DEBUG + bool m_bVOMIT_ENABLE; +#endif +public: + void SetVOMIT_ENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVOMIT_ENABLE = i; +#ifdef _DEBUG + m_bVOMIT_ENABLE = true; +#endif + } + void SetVOMIT_ENABLE( bool i ) + { + m_nVOMIT_ENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bVOMIT_ENABLE = true; +#endif + } +private: + int m_nTV_GAMMA; +#ifdef _DEBUG + bool m_bTV_GAMMA; +#endif +public: + void SetTV_GAMMA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTV_GAMMA = i; +#ifdef _DEBUG + m_bTV_GAMMA = true; +#endif + } + void SetTV_GAMMA( bool i ) + { + m_nTV_GAMMA = i ? 1 : 0; +#ifdef _DEBUG + m_bTV_GAMMA = true; +#endif + } +private: + int m_nDESATURATEENABLE; +#ifdef _DEBUG + bool m_bDESATURATEENABLE; +#endif +public: + void SetDESATURATEENABLE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDESATURATEENABLE = i; +#ifdef _DEBUG + m_bDESATURATEENABLE = true; +#endif + } + void SetDESATURATEENABLE( bool i ) + { + m_nDESATURATEENABLE = i ? 1 : 0; +#ifdef _DEBUG + m_bDESATURATEENABLE = true; +#endif + } +public: + sdk_engine_post_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bAA_ENABLE = false; +#endif // _DEBUG + m_nAA_ENABLE = 0; +#ifdef _DEBUG + m_bCOL_CORRECT_NUM_LOOKUPS = false; +#endif // _DEBUG + m_nCOL_CORRECT_NUM_LOOKUPS = 0; +#ifdef _DEBUG + m_bCONVERT_FROM_LINEAR = false; +#endif // _DEBUG + m_nCONVERT_FROM_LINEAR = 0; +#ifdef _DEBUG + m_bCONVERT_TO_LINEAR = false; +#endif // _DEBUG + m_nCONVERT_TO_LINEAR = 0; +#ifdef _DEBUG + m_bNOISE_ENABLE = false; +#endif // _DEBUG + m_nNOISE_ENABLE = 0; +#ifdef _DEBUG + m_bVIGNETTE_ENABLE = false; +#endif // _DEBUG + m_nVIGNETTE_ENABLE = 0; +#ifdef _DEBUG + m_bLOCAL_CONTRAST_ENABLE = false; +#endif // _DEBUG + m_nLOCAL_CONTRAST_ENABLE = 0; +#ifdef _DEBUG + m_bBLURRED_VIGNETTE_ENABLE = false; +#endif // _DEBUG + m_nBLURRED_VIGNETTE_ENABLE = 0; +#ifdef _DEBUG + m_bVOMIT_ENABLE = false; +#endif // _DEBUG + m_nVOMIT_ENABLE = 0; +#ifdef _DEBUG + m_bTV_GAMMA = false; +#endif // _DEBUG + m_nTV_GAMMA = 0; +#ifdef _DEBUG + m_bDESATURATEENABLE = false; +#endif // _DEBUG + m_nDESATURATEENABLE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bAA_ENABLE && m_bCOL_CORRECT_NUM_LOOKUPS && m_bCONVERT_FROM_LINEAR && m_bCONVERT_TO_LINEAR && m_bNOISE_ENABLE && m_bVIGNETTE_ENABLE && m_bLOCAL_CONTRAST_ENABLE && m_bBLURRED_VIGNETTE_ENABLE && m_bVOMIT_ENABLE && m_bTV_GAMMA && m_bDESATURATEENABLE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nAA_ENABLE ) + ( 1 * m_nCOL_CORRECT_NUM_LOOKUPS ) + ( 4 * m_nCONVERT_FROM_LINEAR ) + ( 8 * m_nCONVERT_TO_LINEAR ) + ( 16 * m_nNOISE_ENABLE ) + ( 32 * m_nVIGNETTE_ENABLE ) + ( 64 * m_nLOCAL_CONTRAST_ENABLE ) + ( 128 * m_nBLURRED_VIGNETTE_ENABLE ) + ( 256 * m_nVOMIT_ENABLE ) + ( 512 * m_nTV_GAMMA ) + ( 1024 * m_nDESATURATEENABLE ) + 0; + } +}; +#define shaderDynamicTest_sdk_engine_post_ps20b psh_forgot_to_set_dynamic_AA_ENABLE + psh_forgot_to_set_dynamic_COL_CORRECT_NUM_LOOKUPS + psh_forgot_to_set_dynamic_CONVERT_FROM_LINEAR + psh_forgot_to_set_dynamic_CONVERT_TO_LINEAR + psh_forgot_to_set_dynamic_NOISE_ENABLE + psh_forgot_to_set_dynamic_VIGNETTE_ENABLE + psh_forgot_to_set_dynamic_LOCAL_CONTRAST_ENABLE + psh_forgot_to_set_dynamic_BLURRED_VIGNETTE_ENABLE + psh_forgot_to_set_dynamic_VOMIT_ENABLE + psh_forgot_to_set_dynamic_TV_GAMMA + psh_forgot_to_set_dynamic_DESATURATEENABLE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20.inc new file mode 100644 index 00000000..850964b3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_eye_refract_ps20_Static_Index +{ +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +public: + sdk_eye_refract_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 3 * m_nFLASHLIGHT ) + ( 6 * m_nLIGHTWARPTEXTURE ) + 0; + } +}; +#define shaderStaticTest_sdk_eye_refract_ps20 psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_LIGHTWARPTEXTURE + 0 +class sdk_eye_refract_ps20_Dynamic_Index +{ +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_eye_refract_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eye_refract_ps20 psh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20b.inc new file mode 100644 index 00000000..93308c81 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps20b.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_eye_refract_ps20b_Static_Index +{ +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nSPHERETEXKILLCOMBO; +#ifdef _DEBUG + bool m_bSPHERETEXKILLCOMBO; +#endif +public: + void SetSPHERETEXKILLCOMBO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSPHERETEXKILLCOMBO = i; +#ifdef _DEBUG + m_bSPHERETEXKILLCOMBO = true; +#endif + } + void SetSPHERETEXKILLCOMBO( bool i ) + { + m_nSPHERETEXKILLCOMBO = i ? 1 : 0; +#ifdef _DEBUG + m_bSPHERETEXKILLCOMBO = true; +#endif + } +private: + int m_nRAYTRACESPHERE; +#ifdef _DEBUG + bool m_bRAYTRACESPHERE; +#endif +public: + void SetRAYTRACESPHERE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nRAYTRACESPHERE = i; +#ifdef _DEBUG + m_bRAYTRACESPHERE = true; +#endif + } + void SetRAYTRACESPHERE( bool i ) + { + m_nRAYTRACESPHERE = i ? 1 : 0; +#ifdef _DEBUG + m_bRAYTRACESPHERE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_eye_refract_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bSPHERETEXKILLCOMBO = false; +#endif // _DEBUG + m_nSPHERETEXKILLCOMBO = 0; +#ifdef _DEBUG + m_bRAYTRACESPHERE = false; +#endif // _DEBUG + m_nRAYTRACESPHERE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE && m_bSPHERETEXKILLCOMBO && m_bRAYTRACESPHERE && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 10 * m_nFLASHLIGHT ) + ( 20 * m_nLIGHTWARPTEXTURE ) + ( 40 * m_nSPHERETEXKILLCOMBO ) + ( 80 * m_nRAYTRACESPHERE ) + ( 160 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_eye_refract_ps20b psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_SPHERETEXKILLCOMBO + psh_forgot_to_set_static_RAYTRACESPHERE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_eye_refract_ps20b_Dynamic_Index +{ +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_eye_refract_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bNUM_LIGHTS && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nNUM_LIGHTS ) + ( 5 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eye_refract_ps20b psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps30.inc new file mode 100644 index 00000000..76c39918 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_ps30.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_eye_refract_ps30_Static_Index +{ +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nSPHERETEXKILLCOMBO; +#ifdef _DEBUG + bool m_bSPHERETEXKILLCOMBO; +#endif +public: + void SetSPHERETEXKILLCOMBO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSPHERETEXKILLCOMBO = i; +#ifdef _DEBUG + m_bSPHERETEXKILLCOMBO = true; +#endif + } + void SetSPHERETEXKILLCOMBO( bool i ) + { + m_nSPHERETEXKILLCOMBO = i ? 1 : 0; +#ifdef _DEBUG + m_bSPHERETEXKILLCOMBO = true; +#endif + } +private: + int m_nRAYTRACESPHERE; +#ifdef _DEBUG + bool m_bRAYTRACESPHERE; +#endif +public: + void SetRAYTRACESPHERE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nRAYTRACESPHERE = i; +#ifdef _DEBUG + m_bRAYTRACESPHERE = true; +#endif + } + void SetRAYTRACESPHERE( bool i ) + { + m_nRAYTRACESPHERE = i ? 1 : 0; +#ifdef _DEBUG + m_bRAYTRACESPHERE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_eye_refract_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bSPHERETEXKILLCOMBO = false; +#endif // _DEBUG + m_nSPHERETEXKILLCOMBO = 0; +#ifdef _DEBUG + m_bRAYTRACESPHERE = false; +#endif // _DEBUG + m_nRAYTRACESPHERE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE && m_bSPHERETEXKILLCOMBO && m_bRAYTRACESPHERE && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 10 * m_nFLASHLIGHT ) + ( 20 * m_nLIGHTWARPTEXTURE ) + ( 40 * m_nSPHERETEXKILLCOMBO ) + ( 80 * m_nRAYTRACESPHERE ) + ( 160 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_eye_refract_ps30 psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_SPHERETEXKILLCOMBO + psh_forgot_to_set_static_RAYTRACESPHERE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_eye_refract_ps30_Dynamic_Index +{ +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_eye_refract_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bNUM_LIGHTS && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nNUM_LIGHTS ) + ( 5 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eye_refract_ps30 psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs20.inc new file mode 100644 index 00000000..ec10825b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs20.inc @@ -0,0 +1,287 @@ +#include "shaderlib/cshader.h" +class sdk_eye_refract_vs20_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +public: + sdk_eye_refract_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 160 * m_nINTRO ) + ( 320 * m_nHALFLAMBERT ) + ( 640 * m_nFLASHLIGHT ) + ( 1280 * m_nLIGHTWARPTEXTURE ) + 0; + } +}; +#define shaderStaticTest_sdk_eye_refract_vs20 vsh_forgot_to_set_static_INTRO + vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_FLASHLIGHT + vsh_forgot_to_set_static_LIGHTWARPTEXTURE + 0 +class sdk_eye_refract_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_eye_refract_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eye_refract_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs30.inc new file mode 100644 index 00000000..4729f2ab --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eye_refract_vs30.inc @@ -0,0 +1,312 @@ +#include "shaderlib/cshader.h" +class sdk_eye_refract_vs30_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +public: + sdk_eye_refract_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 320 * m_nINTRO ) + ( 640 * m_nHALFLAMBERT ) + ( 1280 * m_nFLASHLIGHT ) + ( 2560 * m_nLIGHTWARPTEXTURE ) + 0; + } +}; +#define shaderStaticTest_sdk_eye_refract_vs30 vsh_forgot_to_set_static_INTRO + vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_FLASHLIGHT + vsh_forgot_to_set_static_LIGHTWARPTEXTURE + 0 +class sdk_eye_refract_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_eye_refract_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bNUM_LIGHTS && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nNUM_LIGHTS ) + ( 160 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_eye_refract_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_NUM_LIGHTS + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20.inc new file mode 100644 index 00000000..3c83a6ca --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_eyeglint_ps20_Static_Index +{ +public: + sdk_eyeglint_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyeglint_ps20 0 +class sdk_eyeglint_ps20_Dynamic_Index +{ +public: + sdk_eyeglint_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_eyeglint_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20b.inc new file mode 100644 index 00000000..2dbb669b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_ps20b.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_eyeglint_ps20b_Static_Index +{ +public: + sdk_eyeglint_ps20b_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyeglint_ps20b 0 +class sdk_eyeglint_ps20b_Dynamic_Index +{ +public: + sdk_eyeglint_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_eyeglint_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_vs20.inc new file mode 100644 index 00000000..05087222 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyeglint_vs20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_eyeglint_vs20_Static_Index +{ +public: + sdk_eyeglint_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyeglint_vs20 0 +class sdk_eyeglint_vs20_Dynamic_Index +{ +public: + sdk_eyeglint_vs20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_eyeglint_vs20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps11.inc new file mode 100644 index 00000000..721122e3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps11.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_flashlight_ps11_Static_Index +{ +public: + sdk_eyes_flashlight_ps11_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_flashlight_ps11 0 +class sdk_eyes_flashlight_ps11_Dynamic_Index +{ +public: + sdk_eyes_flashlight_ps11_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_eyes_flashlight_ps11 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20.inc new file mode 100644 index 00000000..9e576f91 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_flashlight_ps20_Static_Index +{ +public: + sdk_eyes_flashlight_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_flashlight_ps20 0 +class sdk_eyes_flashlight_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_eyes_flashlight_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_flashlight_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20b.inc new file mode 100644 index 00000000..d156ca25 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps20b.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_flashlight_ps20b_Static_Index +{ +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_eyes_flashlight_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_eyes_flashlight_ps20b psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_eyes_flashlight_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_eyes_flashlight_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_flashlight_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps30.inc new file mode 100644 index 00000000..57ddecae --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_ps30.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_flashlight_ps30_Static_Index +{ +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_eyes_flashlight_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_eyes_flashlight_ps30 psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_eyes_flashlight_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_eyes_flashlight_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_flashlight_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs20.inc new file mode 100644 index 00000000..c93c72de --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs20.inc @@ -0,0 +1,110 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_flashlight_vs20_Static_Index +{ +public: + sdk_eyes_flashlight_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_flashlight_vs20 0 +class sdk_eyes_flashlight_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_eyes_flashlight_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_flashlight_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs30.inc new file mode 100644 index 00000000..1244088b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_flashlight_vs30.inc @@ -0,0 +1,135 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_flashlight_vs30_Static_Index +{ +public: + sdk_eyes_flashlight_vs30_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_flashlight_vs30 0 +class sdk_eyes_flashlight_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_eyes_flashlight_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + ( 8 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_flashlight_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20.inc new file mode 100644 index 00000000..cbbb103d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_ps20_Static_Index +{ +public: + sdk_eyes_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_ps20 0 +class sdk_eyes_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_eyes_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20b.inc new file mode 100644 index 00000000..f10f16d4 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps20b.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_ps20b_Static_Index +{ +public: + sdk_eyes_ps20b_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_ps20b 0 +class sdk_eyes_ps20b_Dynamic_Index +{ +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_eyes_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITE_DEPTH_TO_DESTALPHA && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_ps20b psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps30.inc new file mode 100644 index 00000000..2353f65d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_ps30.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_ps30_Static_Index +{ +public: + sdk_eyes_ps30_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_eyes_ps30 0 +class sdk_eyes_ps30_Dynamic_Index +{ +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_eyes_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITE_DEPTH_TO_DESTALPHA && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_ps30 psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs20.inc new file mode 100644 index 00000000..5b896790 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs20.inc @@ -0,0 +1,262 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_vs20_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +public: + sdk_eyes_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO && m_bHALFLAMBERT && m_bUSE_STATIC_CONTROL_FLOW; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 96 * m_nINTRO ) + ( 192 * m_nHALFLAMBERT ) + ( 384 * m_nUSE_STATIC_CONTROL_FLOW ) + 0; + } +}; +#define shaderStaticTest_sdk_eyes_vs20 vsh_forgot_to_set_static_INTRO + vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + 0 +class sdk_eyes_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_eyes_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs30.inc new file mode 100644 index 00000000..d14fa273 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_eyes_vs30.inc @@ -0,0 +1,237 @@ +#include "shaderlib/cshader.h" +class sdk_eyes_vs30_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +public: + sdk_eyes_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO && m_bHALFLAMBERT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 64 * m_nINTRO ) + ( 128 * m_nHALFLAMBERT ) + 0; + } +}; +#define shaderStaticTest_sdk_eyes_vs30 vsh_forgot_to_set_static_INTRO + vsh_forgot_to_set_static_HALFLAMBERT + 0 +class sdk_eyes_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_eyes_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_eyes_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps11.inc new file mode 100644 index 00000000..fc6f849d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps11.inc @@ -0,0 +1,85 @@ +#include "shaderlib/cshader.h" +class sdk_flashlight_ps11_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNOCULL; +#ifdef _DEBUG + bool m_bNOCULL; +#endif +public: + void SetNOCULL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNOCULL = i; +#ifdef _DEBUG + m_bNOCULL = true; +#endif + } + void SetNOCULL( bool i ) + { + m_nNOCULL = i ? 1 : 0; +#ifdef _DEBUG + m_bNOCULL = true; +#endif + } +public: + sdk_flashlight_ps11_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNOCULL = false; +#endif // _DEBUG + m_nNOCULL = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNOCULL; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nNORMALMAP ) + ( 2 * m_nNOCULL ) + 0; + } +}; +#define shaderStaticTest_sdk_flashlight_ps11 psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NOCULL + 0 +class sdk_flashlight_ps11_Dynamic_Index +{ +public: + sdk_flashlight_ps11_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_flashlight_ps11 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20.inc new file mode 100644 index 00000000..3d25ff86 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_flashlight_ps20_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNORMALMAP2; +#ifdef _DEBUG + bool m_bNORMALMAP2; +#endif +public: + void SetNORMALMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP2 = i; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } + void SetNORMALMAP2( bool i ) + { + m_nNORMALMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +public: + sdk_flashlight_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNORMALMAP2 = false; +#endif // _DEBUG + m_nNORMALMAP2 = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNORMALMAP2 && m_bWORLDVERTEXTRANSITION && m_bSEAMLESS && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nNORMALMAP ) + ( 6 * m_nNORMALMAP2 ) + ( 12 * m_nWORLDVERTEXTRANSITION ) + ( 24 * m_nSEAMLESS ) + ( 48 * m_nDETAILTEXTURE ) + ( 96 * m_nDETAIL_BLEND_MODE ) + 0; + } +}; +#define shaderStaticTest_sdk_flashlight_ps20 psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NORMALMAP2 + psh_forgot_to_set_static_WORLDVERTEXTRANSITION + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + 0 +class sdk_flashlight_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_flashlight_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_flashlight_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20b.inc new file mode 100644 index 00000000..0e2bf822 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps20b.inc @@ -0,0 +1,262 @@ +#include "shaderlib/cshader.h" +class sdk_flashlight_ps20b_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNORMALMAP2; +#ifdef _DEBUG + bool m_bNORMALMAP2; +#endif +public: + void SetNORMALMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP2 = i; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } + void SetNORMALMAP2( bool i ) + { + m_nNORMALMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_flashlight_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNORMALMAP2 = false; +#endif // _DEBUG + m_nNORMALMAP2 = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNORMALMAP2 && m_bWORLDVERTEXTRANSITION && m_bSEAMLESS && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nNORMALMAP ) + ( 12 * m_nNORMALMAP2 ) + ( 24 * m_nWORLDVERTEXTRANSITION ) + ( 48 * m_nSEAMLESS ) + ( 96 * m_nDETAILTEXTURE ) + ( 192 * m_nDETAIL_BLEND_MODE ) + ( 384 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_flashlight_ps20b psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NORMALMAP2 + psh_forgot_to_set_static_WORLDVERTEXTRANSITION + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_flashlight_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_flashlight_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_flashlight_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps30.inc new file mode 100644 index 00000000..88331a5b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flashlight_ps30.inc @@ -0,0 +1,262 @@ +#include "shaderlib/cshader.h" +class sdk_flashlight_ps30_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNORMALMAP2; +#ifdef _DEBUG + bool m_bNORMALMAP2; +#endif +public: + void SetNORMALMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP2 = i; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } + void SetNORMALMAP2( bool i ) + { + m_nNORMALMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_flashlight_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNORMALMAP2 = false; +#endif // _DEBUG + m_nNORMALMAP2 = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNORMALMAP2 && m_bWORLDVERTEXTRANSITION && m_bSEAMLESS && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nNORMALMAP ) + ( 12 * m_nNORMALMAP2 ) + ( 24 * m_nWORLDVERTEXTRANSITION ) + ( 48 * m_nSEAMLESS ) + ( 96 * m_nDETAILTEXTURE ) + ( 192 * m_nDETAIL_BLEND_MODE ) + ( 384 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_flashlight_ps30 psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NORMALMAP2 + psh_forgot_to_set_static_WORLDVERTEXTRANSITION + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_flashlight_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_flashlight_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_flashlight_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20.inc new file mode 100644 index 00000000..0b73f03b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_flesh_interior_blended_pass_ps20_Static_Index +{ +public: + sdk_flesh_interior_blended_pass_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_flesh_interior_blended_pass_ps20 0 +class sdk_flesh_interior_blended_pass_ps20_Dynamic_Index +{ +public: + sdk_flesh_interior_blended_pass_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_flesh_interior_blended_pass_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20b.inc new file mode 100644 index 00000000..dd85606f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_ps20b.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_flesh_interior_blended_pass_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_flesh_interior_blended_pass_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_flesh_interior_blended_pass_ps20b 0 +class sdk_flesh_interior_blended_pass_ps20b_Dynamic_Index +{ +public: + sdk_flesh_interior_blended_pass_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_flesh_interior_blended_pass_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_vs20.inc new file mode 100644 index 00000000..e2241b31 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_flesh_interior_blended_pass_vs20.inc @@ -0,0 +1,237 @@ +#include "shaderlib/cshader.h" +class sdk_flesh_interior_blended_pass_vs20_Static_Index +{ +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +public: + sdk_flesh_interior_blended_pass_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bHALFLAMBERT && m_bUSE_STATIC_CONTROL_FLOW; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 96 * m_nHALFLAMBERT ) + ( 192 * m_nUSE_STATIC_CONTROL_FLOW ) + 0; + } +}; +#define shaderStaticTest_sdk_flesh_interior_blended_pass_vs20 vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + 0 +class sdk_flesh_interior_blended_pass_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_flesh_interior_blended_pass_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bSKINNING && m_bDOWATERFOG && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nSKINNING ) + ( 4 * m_nDOWATERFOG ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_flesh_interior_blended_pass_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20.inc new file mode 100644 index 00000000..91c81ef9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_decal_ps20_Static_Index +{ +public: + sdk_lightmappedgeneric_decal_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_decal_ps20 0 +class sdk_lightmappedgeneric_decal_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_lightmappedgeneric_decal_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_decal_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20b.inc new file mode 100644 index 00000000..e11d31ca --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_ps20b.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_decal_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_lightmappedgeneric_decal_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_decal_ps20b 0 +class sdk_lightmappedgeneric_decal_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_lightmappedgeneric_decal_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_decal_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_vs20.inc new file mode 100644 index 00000000..a53c8769 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_decal_vs20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_decal_vs20_Static_Index +{ +public: + sdk_lightmappedgeneric_decal_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_decal_vs20 0 +class sdk_lightmappedgeneric_decal_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_lightmappedgeneric_decal_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_decal_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20.inc new file mode 100644 index 00000000..e24bcc01 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20.inc @@ -0,0 +1,262 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_flashlight_ps20_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNORMALMAP2; +#ifdef _DEBUG + bool m_bNORMALMAP2; +#endif +public: + void SetNORMALMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP2 = i; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } + void SetNORMALMAP2( bool i ) + { + m_nNORMALMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nFANCY_BLENDING; +#ifdef _DEBUG + bool m_bFANCY_BLENDING; +#endif +public: + void SetFANCY_BLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFANCY_BLENDING = i; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } + void SetFANCY_BLENDING( bool i ) + { + m_nFANCY_BLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNORMALMAP2 = false; +#endif // _DEBUG + m_nNORMALMAP2 = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = false; +#endif // _DEBUG + m_nFANCY_BLENDING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNORMALMAP2 && m_bWORLDVERTEXTRANSITION && m_bFANCY_BLENDING && m_bSEAMLESS && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nNORMALMAP ) + ( 6 * m_nNORMALMAP2 ) + ( 12 * m_nWORLDVERTEXTRANSITION ) + ( 24 * m_nFANCY_BLENDING ) + ( 48 * m_nSEAMLESS ) + ( 96 * m_nDETAILTEXTURE ) + ( 192 * m_nDETAIL_BLEND_MODE ) + ( 384 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_flashlight_ps20 psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NORMALMAP2 + psh_forgot_to_set_static_WORLDVERTEXTRANSITION + psh_forgot_to_set_static_FANCY_BLENDING + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_flashlight_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_flashlight_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20b.inc new file mode 100644 index 00000000..e4ba14aa --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps20b.inc @@ -0,0 +1,362 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_flashlight_ps20b_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNORMALMAP2; +#ifdef _DEBUG + bool m_bNORMALMAP2; +#endif +public: + void SetNORMALMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP2 = i; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } + void SetNORMALMAP2( bool i ) + { + m_nNORMALMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nFANCY_BLENDING; +#ifdef _DEBUG + bool m_bFANCY_BLENDING; +#endif +public: + void SetFANCY_BLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFANCY_BLENDING = i; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } + void SetFANCY_BLENDING( bool i ) + { + m_nFANCY_BLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nPHONG; +#ifdef _DEBUG + bool m_bPHONG; +#endif +public: + void SetPHONG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG = i; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } + void SetPHONG( bool i ) + { + m_nPHONG = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } +private: + int m_nPHONGMASK; +#ifdef _DEBUG + bool m_bPHONGMASK; +#endif +public: + void SetPHONGMASK( int i ) + { + Assert( i >= 0 && i <= 3 ); + m_nPHONGMASK = i; +#ifdef _DEBUG + m_bPHONGMASK = true; +#endif + } + void SetPHONGMASK( bool i ) + { + m_nPHONGMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONGMASK = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNORMALMAP2 = false; +#endif // _DEBUG + m_nNORMALMAP2 = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = false; +#endif // _DEBUG + m_nFANCY_BLENDING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bPHONG = false; +#endif // _DEBUG + m_nPHONG = 0; +#ifdef _DEBUG + m_bPHONGMASK = false; +#endif // _DEBUG + m_nPHONGMASK = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNORMALMAP2 && m_bWORLDVERTEXTRANSITION && m_bFANCY_BLENDING && m_bSEAMLESS && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bFLASHLIGHTDEPTHFILTERMODE && m_bPHONG && m_bPHONGMASK && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nNORMALMAP ) + ( 12 * m_nNORMALMAP2 ) + ( 24 * m_nWORLDVERTEXTRANSITION ) + ( 48 * m_nFANCY_BLENDING ) + ( 96 * m_nSEAMLESS ) + ( 192 * m_nDETAILTEXTURE ) + ( 384 * m_nDETAIL_BLEND_MODE ) + ( 768 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 2304 * m_nPHONG ) + ( 4608 * m_nPHONGMASK ) + ( 18432 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_flashlight_ps20b psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NORMALMAP2 + psh_forgot_to_set_static_WORLDVERTEXTRANSITION + psh_forgot_to_set_static_FANCY_BLENDING + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_PHONG + psh_forgot_to_set_static_PHONGMASK + psh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_flashlight_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_flashlight_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps30.inc new file mode 100644 index 00000000..a9734f15 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_ps30.inc @@ -0,0 +1,362 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_flashlight_ps30_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nNORMALMAP2; +#ifdef _DEBUG + bool m_bNORMALMAP2; +#endif +public: + void SetNORMALMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP2 = i; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } + void SetNORMALMAP2( bool i ) + { + m_nNORMALMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP2 = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nFANCY_BLENDING; +#ifdef _DEBUG + bool m_bFANCY_BLENDING; +#endif +public: + void SetFANCY_BLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFANCY_BLENDING = i; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } + void SetFANCY_BLENDING( bool i ) + { + m_nFANCY_BLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nPHONG; +#ifdef _DEBUG + bool m_bPHONG; +#endif +public: + void SetPHONG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG = i; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } + void SetPHONG( bool i ) + { + m_nPHONG = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } +private: + int m_nPHONGMASK; +#ifdef _DEBUG + bool m_bPHONGMASK; +#endif +public: + void SetPHONGMASK( int i ) + { + Assert( i >= 0 && i <= 3 ); + m_nPHONGMASK = i; +#ifdef _DEBUG + m_bPHONGMASK = true; +#endif + } + void SetPHONGMASK( bool i ) + { + m_nPHONGMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONGMASK = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bNORMALMAP2 = false; +#endif // _DEBUG + m_nNORMALMAP2 = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = false; +#endif // _DEBUG + m_nFANCY_BLENDING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bPHONG = false; +#endif // _DEBUG + m_nPHONG = 0; +#ifdef _DEBUG + m_bPHONGMASK = false; +#endif // _DEBUG + m_nPHONGMASK = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bNORMALMAP2 && m_bWORLDVERTEXTRANSITION && m_bFANCY_BLENDING && m_bSEAMLESS && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bFLASHLIGHTDEPTHFILTERMODE && m_bPHONG && m_bPHONGMASK && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nNORMALMAP ) + ( 12 * m_nNORMALMAP2 ) + ( 24 * m_nWORLDVERTEXTRANSITION ) + ( 48 * m_nFANCY_BLENDING ) + ( 96 * m_nSEAMLESS ) + ( 192 * m_nDETAILTEXTURE ) + ( 384 * m_nDETAIL_BLEND_MODE ) + ( 768 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 2304 * m_nPHONG ) + ( 4608 * m_nPHONGMASK ) + ( 18432 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_flashlight_ps30 psh_forgot_to_set_static_NORMALMAP + psh_forgot_to_set_static_NORMALMAP2 + psh_forgot_to_set_static_WORLDVERTEXTRANSITION + psh_forgot_to_set_static_FANCY_BLENDING + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_PHONG + psh_forgot_to_set_static_PHONGMASK + psh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_flashlight_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_flashlight_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs11.inc new file mode 100644 index 00000000..94142c09 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs11.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_flashlight_vs11_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_vs11_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bWORLDVERTEXTRANSITION && m_bVERTEXCOLOR; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nNORMALMAP ) + ( 4 * m_nWORLDVERTEXTRANSITION ) + ( 8 * m_nVERTEXCOLOR ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_flashlight_vs11 vsh_forgot_to_set_static_NORMALMAP + vsh_forgot_to_set_static_WORLDVERTEXTRANSITION + vsh_forgot_to_set_static_VERTEXCOLOR + 0 +class sdk_lightmappedgeneric_flashlight_vs11_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_vs11_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_flashlight_vs11 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs20.inc new file mode 100644 index 00000000..95edefe2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs20.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_flashlight_vs20_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAIL; +#ifdef _DEBUG + bool m_bDETAIL; +#endif +public: + void SetDETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL = i; +#ifdef _DEBUG + m_bDETAIL = true; +#endif + } + void SetDETAIL( bool i ) + { + m_nDETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL = true; +#endif + } +private: + int m_nPHONG; +#ifdef _DEBUG + bool m_bPHONG; +#endif +public: + void SetPHONG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG = i; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } + void SetPHONG( bool i ) + { + m_nPHONG = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAIL = false; +#endif // _DEBUG + m_nDETAIL = 0; +#ifdef _DEBUG + m_bPHONG = false; +#endif // _DEBUG + m_nPHONG = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bWORLDVERTEXTRANSITION && m_bSEAMLESS && m_bDETAIL && m_bPHONG && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nNORMALMAP ) + ( 4 * m_nWORLDVERTEXTRANSITION ) + ( 8 * m_nSEAMLESS ) + ( 16 * m_nDETAIL ) + ( 32 * m_nPHONG ) + ( 64 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_flashlight_vs20 vsh_forgot_to_set_static_NORMALMAP + vsh_forgot_to_set_static_WORLDVERTEXTRANSITION + vsh_forgot_to_set_static_SEAMLESS + vsh_forgot_to_set_static_DETAIL + vsh_forgot_to_set_static_PHONG + vsh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_flashlight_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_flashlight_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs30.inc new file mode 100644 index 00000000..376110e3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_flashlight_vs30.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_flashlight_vs30_Static_Index +{ +private: + int m_nNORMALMAP; +#ifdef _DEBUG + bool m_bNORMALMAP; +#endif +public: + void SetNORMALMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAP = i; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } + void SetNORMALMAP( bool i ) + { + m_nNORMALMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAP = true; +#endif + } +private: + int m_nWORLDVERTEXTRANSITION; +#ifdef _DEBUG + bool m_bWORLDVERTEXTRANSITION; +#endif +public: + void SetWORLDVERTEXTRANSITION( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWORLDVERTEXTRANSITION = i; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } + void SetWORLDVERTEXTRANSITION( bool i ) + { + m_nWORLDVERTEXTRANSITION = i ? 1 : 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nDETAIL; +#ifdef _DEBUG + bool m_bDETAIL; +#endif +public: + void SetDETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL = i; +#ifdef _DEBUG + m_bDETAIL = true; +#endif + } + void SetDETAIL( bool i ) + { + m_nDETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL = true; +#endif + } +private: + int m_nPHONG; +#ifdef _DEBUG + bool m_bPHONG; +#endif +public: + void SetPHONG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG = i; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } + void SetPHONG( bool i ) + { + m_nPHONG = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bNORMALMAP = false; +#endif // _DEBUG + m_nNORMALMAP = 0; +#ifdef _DEBUG + m_bWORLDVERTEXTRANSITION = false; +#endif // _DEBUG + m_nWORLDVERTEXTRANSITION = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bDETAIL = false; +#endif // _DEBUG + m_nDETAIL = 0; +#ifdef _DEBUG + m_bPHONG = false; +#endif // _DEBUG + m_nPHONG = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bNORMALMAP && m_bWORLDVERTEXTRANSITION && m_bSEAMLESS && m_bDETAIL && m_bPHONG && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nNORMALMAP ) + ( 4 * m_nWORLDVERTEXTRANSITION ) + ( 8 * m_nSEAMLESS ) + ( 16 * m_nDETAIL ) + ( 32 * m_nPHONG ) + ( 64 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_flashlight_vs30 vsh_forgot_to_set_static_NORMALMAP + vsh_forgot_to_set_static_WORLDVERTEXTRANSITION + vsh_forgot_to_set_static_SEAMLESS + vsh_forgot_to_set_static_DETAIL + vsh_forgot_to_set_static_PHONG + vsh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_flashlight_vs30_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_lightmappedgeneric_flashlight_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_flashlight_vs30 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.inc new file mode 100644 index 00000000..d11eaeb9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_overbright2_ps11.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_lightingonly_overbright2_ps11_Static_Index +{ +public: + sdk_lightmappedgeneric_lightingonly_overbright2_ps11_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_lightingonly_overbright2_ps11 0 +class sdk_lightmappedgeneric_lightingonly_overbright2_ps11_Dynamic_Index +{ +public: + sdk_lightmappedgeneric_lightingonly_overbright2_ps11_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_lightingonly_overbright2_ps11 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_vs11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_vs11.inc new file mode 100644 index 00000000..5bb3dc94 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_lightingonly_vs11.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_lightingonly_vs11_Static_Index +{ +public: + sdk_lightmappedgeneric_lightingonly_vs11_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_lightingonly_vs11 0 +class sdk_lightmappedgeneric_lightingonly_vs11_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_lightmappedgeneric_lightingonly_vs11_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_lightingonly_vs11 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps11.inc new file mode 100644 index 00000000..30f358a1 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps11.inc @@ -0,0 +1,160 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_ps11_Static_Index +{ +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +private: + int m_nENVMAP; +#ifdef _DEBUG + bool m_bENVMAP; +#endif +public: + void SetENVMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAP = i; +#ifdef _DEBUG + m_bENVMAP = true; +#endif + } + void SetENVMAP( bool i ) + { + m_nENVMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAP = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +public: + sdk_lightmappedgeneric_ps11_Static_Index( ) + { +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; +#ifdef _DEBUG + m_bENVMAP = false; +#endif // _DEBUG + m_nENVMAP = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBASETEXTURE && m_bENVMAP && m_bENVMAPMASK && m_bSELFILLUM && m_bBASEALPHAENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nBASETEXTURE ) + ( 2 * m_nENVMAP ) + ( 4 * m_nENVMAPMASK ) + ( 8 * m_nSELFILLUM ) + ( 16 * m_nBASEALPHAENVMAPMASK ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_ps11 psh_forgot_to_set_static_BASETEXTURE + psh_forgot_to_set_static_ENVMAP + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + 0 +class sdk_lightmappedgeneric_ps11_Dynamic_Index +{ +public: + sdk_lightmappedgeneric_ps11_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_ps11 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps20b.inc new file mode 100644 index 00000000..4acdc5be --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps20b.inc @@ -0,0 +1,762 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_ps20b_Static_Index +{ +private: + int m_nMASKEDBLENDING; +#ifdef _DEBUG + bool m_bMASKEDBLENDING; +#endif +public: + void SetMASKEDBLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMASKEDBLENDING = i; +#ifdef _DEBUG + m_bMASKEDBLENDING = true; +#endif + } + void SetMASKEDBLENDING( bool i ) + { + m_nMASKEDBLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bMASKEDBLENDING = true; +#endif + } +private: + int m_nBASETEXTURE2; +#ifdef _DEBUG + bool m_bBASETEXTURE2; +#endif +public: + void SetBASETEXTURE2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE2 = i; +#ifdef _DEBUG + m_bBASETEXTURE2 = true; +#endif + } + void SetBASETEXTURE2( bool i ) + { + m_nBASETEXTURE2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE2 = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +private: + int m_nBUMPMAP2; +#ifdef _DEBUG + bool m_bBUMPMAP2; +#endif +public: + void SetBUMPMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP2 = i; +#ifdef _DEBUG + m_bBUMPMAP2 = true; +#endif + } + void SetBUMPMAP2( bool i ) + { + m_nBUMPMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP2 = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nNORMALMAPALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bNORMALMAPALPHAENVMAPMASK; +#endif +public: + void SetNORMALMAPALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAPALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } + void SetNORMALMAPALPHAENVMAPMASK( bool i ) + { + m_nNORMALMAPALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } +private: + int m_nDIFFUSEBUMPMAP; +#ifdef _DEBUG + bool m_bDIFFUSEBUMPMAP; +#endif +public: + void SetDIFFUSEBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSEBUMPMAP = i; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } + void SetDIFFUSEBUMPMAP( bool i ) + { + m_nDIFFUSEBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } +private: + int m_nBASETEXTURENOENVMAP; +#ifdef _DEBUG + bool m_bBASETEXTURENOENVMAP; +#endif +public: + void SetBASETEXTURENOENVMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURENOENVMAP = i; +#ifdef _DEBUG + m_bBASETEXTURENOENVMAP = true; +#endif + } + void SetBASETEXTURENOENVMAP( bool i ) + { + m_nBASETEXTURENOENVMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURENOENVMAP = true; +#endif + } +private: + int m_nBASETEXTURE2NOENVMAP; +#ifdef _DEBUG + bool m_bBASETEXTURE2NOENVMAP; +#endif +public: + void SetBASETEXTURE2NOENVMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE2NOENVMAP = i; +#ifdef _DEBUG + m_bBASETEXTURE2NOENVMAP = true; +#endif + } + void SetBASETEXTURE2NOENVMAP( bool i ) + { + m_nBASETEXTURE2NOENVMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE2NOENVMAP = true; +#endif + } +private: + int m_nWARPLIGHTING; +#ifdef _DEBUG + bool m_bWARPLIGHTING; +#endif +public: + void SetWARPLIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWARPLIGHTING = i; +#ifdef _DEBUG + m_bWARPLIGHTING = true; +#endif + } + void SetWARPLIGHTING( bool i ) + { + m_nWARPLIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bWARPLIGHTING = true; +#endif + } +private: + int m_nFANCY_BLENDING; +#ifdef _DEBUG + bool m_bFANCY_BLENDING; +#endif +public: + void SetFANCY_BLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFANCY_BLENDING = i; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } + void SetFANCY_BLENDING( bool i ) + { + m_nFANCY_BLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } +private: + int m_nRELIEF_MAPPING; +#ifdef _DEBUG + bool m_bRELIEF_MAPPING; +#endif +public: + void SetRELIEF_MAPPING( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nRELIEF_MAPPING = i; +#ifdef _DEBUG + m_bRELIEF_MAPPING = true; +#endif + } + void SetRELIEF_MAPPING( bool i ) + { + m_nRELIEF_MAPPING = i ? 1 : 0; +#ifdef _DEBUG + m_bRELIEF_MAPPING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nBUMPMASK; +#ifdef _DEBUG + bool m_bBUMPMASK; +#endif +public: + void SetBUMPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMASK = i; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } + void SetBUMPMASK( bool i ) + { + m_nBUMPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +private: + int m_nNORMALMASK_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMALMASK_DECODE_MODE; +#endif +public: + void SetNORMALMASK_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMALMASK_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMALMASK_DECODE_MODE = true; +#endif + } + void SetNORMALMASK_DECODE_MODE( bool i ) + { + m_nNORMALMASK_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMASK_DECODE_MODE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 11 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_lightmappedgeneric_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bMASKEDBLENDING = false; +#endif // _DEBUG + m_nMASKEDBLENDING = 0; +#ifdef _DEBUG + m_bBASETEXTURE2 = false; +#endif // _DEBUG + m_nBASETEXTURE2 = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; +#ifdef _DEBUG + m_bBUMPMAP2 = false; +#endif // _DEBUG + m_nBUMPMAP2 = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nNORMALMAPALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = false; +#endif // _DEBUG + m_nDIFFUSEBUMPMAP = 0; +#ifdef _DEBUG + m_bBASETEXTURENOENVMAP = false; +#endif // _DEBUG + m_nBASETEXTURENOENVMAP = 0; +#ifdef _DEBUG + m_bBASETEXTURE2NOENVMAP = false; +#endif // _DEBUG + m_nBASETEXTURE2NOENVMAP = 0; +#ifdef _DEBUG + m_bWARPLIGHTING = false; +#endif // _DEBUG + m_nWARPLIGHTING = 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = false; +#endif // _DEBUG + m_nFANCY_BLENDING = 0; +#ifdef _DEBUG + m_bRELIEF_MAPPING = false; +#endif // _DEBUG + m_nRELIEF_MAPPING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bBUMPMASK = false; +#endif // _DEBUG + m_nBUMPMASK = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; +#ifdef _DEBUG + m_bNORMALMASK_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMALMASK_DECODE_MODE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bMASKEDBLENDING && m_bBASETEXTURE2 && m_bDETAILTEXTURE && m_bBUMPMAP && m_bBUMPMAP2 && m_bCUBEMAP && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bSELFILLUM && m_bNORMALMAPALPHAENVMAPMASK && m_bDIFFUSEBUMPMAP && m_bBASETEXTURENOENVMAP && m_bBASETEXTURE2NOENVMAP && m_bWARPLIGHTING && m_bFANCY_BLENDING && m_bRELIEF_MAPPING && m_bSEAMLESS && m_bBUMPMASK && m_bNORMAL_DECODE_MODE && m_bNORMALMASK_DECODE_MODE && m_bDETAIL_BLEND_MODE && m_bBASETEXTURETRANSFORM2 && m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 96 * m_nMASKEDBLENDING ) + ( 192 * m_nBASETEXTURE2 ) + ( 384 * m_nDETAILTEXTURE ) + ( 768 * m_nBUMPMAP ) + ( 2304 * m_nBUMPMAP2 ) + ( 4608 * m_nCUBEMAP ) + ( 9216 * m_nENVMAPMASK ) + ( 18432 * m_nBASEALPHAENVMAPMASK ) + ( 36864 * m_nSELFILLUM ) + ( 73728 * m_nNORMALMAPALPHAENVMAPMASK ) + ( 147456 * m_nDIFFUSEBUMPMAP ) + ( 294912 * m_nBASETEXTURENOENVMAP ) + ( 589824 * m_nBASETEXTURE2NOENVMAP ) + ( 1179648 * m_nWARPLIGHTING ) + ( 2359296 * m_nFANCY_BLENDING ) + ( 4718592 * m_nRELIEF_MAPPING ) + ( 4718592 * m_nSEAMLESS ) + ( 9437184 * m_nBUMPMASK ) + ( 18874368 * m_nNORMAL_DECODE_MODE ) + ( 18874368 * m_nNORMALMASK_DECODE_MODE ) + ( 18874368 * m_nDETAIL_BLEND_MODE ) + ( 226492416 * m_nBASETEXTURETRANSFORM2 ) + ( 452984832 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_ps20b psh_forgot_to_set_static_MASKEDBLENDING + psh_forgot_to_set_static_BASETEXTURE2 + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_BUMPMAP + psh_forgot_to_set_static_BUMPMAP2 + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_NORMALMAPALPHAENVMAPMASK + psh_forgot_to_set_static_DIFFUSEBUMPMAP + psh_forgot_to_set_static_BASETEXTURENOENVMAP + psh_forgot_to_set_static_BASETEXTURE2NOENVMAP + psh_forgot_to_set_static_WARPLIGHTING + psh_forgot_to_set_static_FANCY_BLENDING + psh_forgot_to_set_static_RELIEF_MAPPING + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_BUMPMASK + psh_forgot_to_set_static_NORMAL_DECODE_MODE + psh_forgot_to_set_static_NORMALMASK_DECODE_MODE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_BASETEXTURETRANSFORM2 + psh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_lightmappedgeneric_ps20b_Dynamic_Index +{ +private: + int m_nFASTPATHENVMAPCONTRAST; +#ifdef _DEBUG + bool m_bFASTPATHENVMAPCONTRAST; +#endif +public: + void SetFASTPATHENVMAPCONTRAST( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATHENVMAPCONTRAST = i; +#ifdef _DEBUG + m_bFASTPATHENVMAPCONTRAST = true; +#endif + } + void SetFASTPATHENVMAPCONTRAST( bool i ) + { + m_nFASTPATHENVMAPCONTRAST = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATHENVMAPCONTRAST = true; +#endif + } +private: + int m_nFASTPATH; +#ifdef _DEBUG + bool m_bFASTPATH; +#endif +public: + void SetFASTPATH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATH = i; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } + void SetFASTPATH( bool i ) + { + m_nFASTPATH = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_lightmappedgeneric_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bFASTPATHENVMAPCONTRAST = false; +#endif // _DEBUG + m_nFASTPATHENVMAPCONTRAST = 0; +#ifdef _DEBUG + m_bFASTPATH = false; +#endif // _DEBUG + m_nFASTPATH = 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bFASTPATHENVMAPCONTRAST && m_bFASTPATH && m_bWRITEWATERFOGTODESTALPHA && m_bPIXELFOGTYPE && m_bLIGHTING_PREVIEW && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nFASTPATHENVMAPCONTRAST ) + ( 2 * m_nFASTPATH ) + ( 4 * m_nWRITEWATERFOGTODESTALPHA ) + ( 8 * m_nPIXELFOGTYPE ) + ( 16 * m_nLIGHTING_PREVIEW ) + ( 48 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_ps20b psh_forgot_to_set_dynamic_FASTPATHENVMAPCONTRAST + psh_forgot_to_set_dynamic_FASTPATH + psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_LIGHTING_PREVIEW + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps30.inc new file mode 100644 index 00000000..205c6bda --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_ps30.inc @@ -0,0 +1,737 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_ps30_Static_Index +{ +private: + int m_nMASKEDBLENDING; +#ifdef _DEBUG + bool m_bMASKEDBLENDING; +#endif +public: + void SetMASKEDBLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMASKEDBLENDING = i; +#ifdef _DEBUG + m_bMASKEDBLENDING = true; +#endif + } + void SetMASKEDBLENDING( bool i ) + { + m_nMASKEDBLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bMASKEDBLENDING = true; +#endif + } +private: + int m_nBASETEXTURE2; +#ifdef _DEBUG + bool m_bBASETEXTURE2; +#endif +public: + void SetBASETEXTURE2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE2 = i; +#ifdef _DEBUG + m_bBASETEXTURE2 = true; +#endif + } + void SetBASETEXTURE2( bool i ) + { + m_nBASETEXTURE2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE2 = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +private: + int m_nBUMPMAP2; +#ifdef _DEBUG + bool m_bBUMPMAP2; +#endif +public: + void SetBUMPMAP2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP2 = i; +#ifdef _DEBUG + m_bBUMPMAP2 = true; +#endif + } + void SetBUMPMAP2( bool i ) + { + m_nBUMPMAP2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP2 = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nNORMALMAPALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bNORMALMAPALPHAENVMAPMASK; +#endif +public: + void SetNORMALMAPALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAPALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } + void SetNORMALMAPALPHAENVMAPMASK( bool i ) + { + m_nNORMALMAPALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } +private: + int m_nDIFFUSEBUMPMAP; +#ifdef _DEBUG + bool m_bDIFFUSEBUMPMAP; +#endif +public: + void SetDIFFUSEBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSEBUMPMAP = i; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } + void SetDIFFUSEBUMPMAP( bool i ) + { + m_nDIFFUSEBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } +private: + int m_nBASETEXTURENOENVMAP; +#ifdef _DEBUG + bool m_bBASETEXTURENOENVMAP; +#endif +public: + void SetBASETEXTURENOENVMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURENOENVMAP = i; +#ifdef _DEBUG + m_bBASETEXTURENOENVMAP = true; +#endif + } + void SetBASETEXTURENOENVMAP( bool i ) + { + m_nBASETEXTURENOENVMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURENOENVMAP = true; +#endif + } +private: + int m_nBASETEXTURE2NOENVMAP; +#ifdef _DEBUG + bool m_bBASETEXTURE2NOENVMAP; +#endif +public: + void SetBASETEXTURE2NOENVMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE2NOENVMAP = i; +#ifdef _DEBUG + m_bBASETEXTURE2NOENVMAP = true; +#endif + } + void SetBASETEXTURE2NOENVMAP( bool i ) + { + m_nBASETEXTURE2NOENVMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE2NOENVMAP = true; +#endif + } +private: + int m_nWARPLIGHTING; +#ifdef _DEBUG + bool m_bWARPLIGHTING; +#endif +public: + void SetWARPLIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWARPLIGHTING = i; +#ifdef _DEBUG + m_bWARPLIGHTING = true; +#endif + } + void SetWARPLIGHTING( bool i ) + { + m_nWARPLIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bWARPLIGHTING = true; +#endif + } +private: + int m_nFANCY_BLENDING; +#ifdef _DEBUG + bool m_bFANCY_BLENDING; +#endif +public: + void SetFANCY_BLENDING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFANCY_BLENDING = i; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } + void SetFANCY_BLENDING( bool i ) + { + m_nFANCY_BLENDING = i ? 1 : 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nBUMPMASK; +#ifdef _DEBUG + bool m_bBUMPMASK; +#endif +public: + void SetBUMPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMASK = i; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } + void SetBUMPMASK( bool i ) + { + m_nBUMPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +private: + int m_nNORMALMASK_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMALMASK_DECODE_MODE; +#endif +public: + void SetNORMALMASK_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMALMASK_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMALMASK_DECODE_MODE = true; +#endif + } + void SetNORMALMASK_DECODE_MODE( bool i ) + { + m_nNORMALMASK_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMASK_DECODE_MODE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 11 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_lightmappedgeneric_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bMASKEDBLENDING = false; +#endif // _DEBUG + m_nMASKEDBLENDING = 0; +#ifdef _DEBUG + m_bBASETEXTURE2 = false; +#endif // _DEBUG + m_nBASETEXTURE2 = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; +#ifdef _DEBUG + m_bBUMPMAP2 = false; +#endif // _DEBUG + m_nBUMPMAP2 = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nNORMALMAPALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = false; +#endif // _DEBUG + m_nDIFFUSEBUMPMAP = 0; +#ifdef _DEBUG + m_bBASETEXTURENOENVMAP = false; +#endif // _DEBUG + m_nBASETEXTURENOENVMAP = 0; +#ifdef _DEBUG + m_bBASETEXTURE2NOENVMAP = false; +#endif // _DEBUG + m_nBASETEXTURE2NOENVMAP = 0; +#ifdef _DEBUG + m_bWARPLIGHTING = false; +#endif // _DEBUG + m_nWARPLIGHTING = 0; +#ifdef _DEBUG + m_bFANCY_BLENDING = false; +#endif // _DEBUG + m_nFANCY_BLENDING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bBUMPMASK = false; +#endif // _DEBUG + m_nBUMPMASK = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; +#ifdef _DEBUG + m_bNORMALMASK_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMALMASK_DECODE_MODE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bMASKEDBLENDING && m_bBASETEXTURE2 && m_bDETAILTEXTURE && m_bBUMPMAP && m_bBUMPMAP2 && m_bCUBEMAP && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bSELFILLUM && m_bNORMALMAPALPHAENVMAPMASK && m_bDIFFUSEBUMPMAP && m_bBASETEXTURENOENVMAP && m_bBASETEXTURE2NOENVMAP && m_bWARPLIGHTING && m_bFANCY_BLENDING && m_bSEAMLESS && m_bBUMPMASK && m_bNORMAL_DECODE_MODE && m_bNORMALMASK_DECODE_MODE && m_bDETAIL_BLEND_MODE && m_bBASETEXTURETRANSFORM2 && m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 96 * m_nMASKEDBLENDING ) + ( 192 * m_nBASETEXTURE2 ) + ( 384 * m_nDETAILTEXTURE ) + ( 768 * m_nBUMPMAP ) + ( 2304 * m_nBUMPMAP2 ) + ( 4608 * m_nCUBEMAP ) + ( 9216 * m_nENVMAPMASK ) + ( 18432 * m_nBASEALPHAENVMAPMASK ) + ( 36864 * m_nSELFILLUM ) + ( 73728 * m_nNORMALMAPALPHAENVMAPMASK ) + ( 147456 * m_nDIFFUSEBUMPMAP ) + ( 294912 * m_nBASETEXTURENOENVMAP ) + ( 589824 * m_nBASETEXTURE2NOENVMAP ) + ( 1179648 * m_nWARPLIGHTING ) + ( 2359296 * m_nFANCY_BLENDING ) + ( 4718592 * m_nSEAMLESS ) + ( 9437184 * m_nBUMPMASK ) + ( 18874368 * m_nNORMAL_DECODE_MODE ) + ( 18874368 * m_nNORMALMASK_DECODE_MODE ) + ( 18874368 * m_nDETAIL_BLEND_MODE ) + ( 226492416 * m_nBASETEXTURETRANSFORM2 ) + ( 452984832 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_ps30 psh_forgot_to_set_static_MASKEDBLENDING + psh_forgot_to_set_static_BASETEXTURE2 + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_BUMPMAP + psh_forgot_to_set_static_BUMPMAP2 + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_NORMALMAPALPHAENVMAPMASK + psh_forgot_to_set_static_DIFFUSEBUMPMAP + psh_forgot_to_set_static_BASETEXTURENOENVMAP + psh_forgot_to_set_static_BASETEXTURE2NOENVMAP + psh_forgot_to_set_static_WARPLIGHTING + psh_forgot_to_set_static_FANCY_BLENDING + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_BUMPMASK + psh_forgot_to_set_static_NORMAL_DECODE_MODE + psh_forgot_to_set_static_NORMALMASK_DECODE_MODE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_BASETEXTURETRANSFORM2 + psh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_lightmappedgeneric_ps30_Dynamic_Index +{ +private: + int m_nFASTPATHENVMAPCONTRAST; +#ifdef _DEBUG + bool m_bFASTPATHENVMAPCONTRAST; +#endif +public: + void SetFASTPATHENVMAPCONTRAST( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATHENVMAPCONTRAST = i; +#ifdef _DEBUG + m_bFASTPATHENVMAPCONTRAST = true; +#endif + } + void SetFASTPATHENVMAPCONTRAST( bool i ) + { + m_nFASTPATHENVMAPCONTRAST = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATHENVMAPCONTRAST = true; +#endif + } +private: + int m_nFASTPATH; +#ifdef _DEBUG + bool m_bFASTPATH; +#endif +public: + void SetFASTPATH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATH = i; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } + void SetFASTPATH( bool i ) + { + m_nFASTPATH = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_lightmappedgeneric_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bFASTPATHENVMAPCONTRAST = false; +#endif // _DEBUG + m_nFASTPATHENVMAPCONTRAST = 0; +#ifdef _DEBUG + m_bFASTPATH = false; +#endif // _DEBUG + m_nFASTPATH = 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bFASTPATHENVMAPCONTRAST && m_bFASTPATH && m_bWRITEWATERFOGTODESTALPHA && m_bPIXELFOGTYPE && m_bLIGHTING_PREVIEW && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nFASTPATHENVMAPCONTRAST ) + ( 2 * m_nFASTPATH ) + ( 4 * m_nWRITEWATERFOGTODESTALPHA ) + ( 8 * m_nPIXELFOGTYPE ) + ( 16 * m_nLIGHTING_PREVIEW ) + ( 48 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_ps30 psh_forgot_to_set_dynamic_FASTPATHENVMAPCONTRAST + psh_forgot_to_set_dynamic_FASTPATH + psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_LIGHTING_PREVIEW + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs20.inc new file mode 100644 index 00000000..90c3dbb9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs20.inc @@ -0,0 +1,362 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_vs20_Static_Index +{ +private: + int m_nENVMAP_MASK; +#ifdef _DEBUG + bool m_bENVMAP_MASK; +#endif +public: + void SetENVMAP_MASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAP_MASK = i; +#ifdef _DEBUG + m_bENVMAP_MASK = true; +#endif + } + void SetENVMAP_MASK( bool i ) + { + m_nENVMAP_MASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAP_MASK = true; +#endif + } +private: + int m_nTANGENTSPACE; +#ifdef _DEBUG + bool m_bTANGENTSPACE; +#endif +public: + void SetTANGENTSPACE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTANGENTSPACE = i; +#ifdef _DEBUG + m_bTANGENTSPACE = true; +#endif + } + void SetTANGENTSPACE( bool i ) + { + m_nTANGENTSPACE = i ? 1 : 0; +#ifdef _DEBUG + m_bTANGENTSPACE = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +private: + int m_nDIFFUSEBUMPMAP; +#ifdef _DEBUG + bool m_bDIFFUSEBUMPMAP; +#endif +public: + void SetDIFFUSEBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSEBUMPMAP = i; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } + void SetDIFFUSEBUMPMAP( bool i ) + { + m_nDIFFUSEBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nVERTEXALPHATEXBLENDFACTOR; +#ifdef _DEBUG + bool m_bVERTEXALPHATEXBLENDFACTOR; +#endif +public: + void SetVERTEXALPHATEXBLENDFACTOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXALPHATEXBLENDFACTOR = i; +#ifdef _DEBUG + m_bVERTEXALPHATEXBLENDFACTOR = true; +#endif + } + void SetVERTEXALPHATEXBLENDFACTOR( bool i ) + { + m_nVERTEXALPHATEXBLENDFACTOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXALPHATEXBLENDFACTOR = true; +#endif + } +private: + int m_nRELIEF_MAPPING; +#ifdef _DEBUG + bool m_bRELIEF_MAPPING; +#endif +public: + void SetRELIEF_MAPPING( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nRELIEF_MAPPING = i; +#ifdef _DEBUG + m_bRELIEF_MAPPING = true; +#endif + } + void SetRELIEF_MAPPING( bool i ) + { + m_nRELIEF_MAPPING = i ? 1 : 0; +#ifdef _DEBUG + m_bRELIEF_MAPPING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nBUMPMASK; +#ifdef _DEBUG + bool m_bBUMPMASK; +#endif +public: + void SetBUMPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMASK = i; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } + void SetBUMPMASK( bool i ) + { + m_nBUMPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bENVMAP_MASK = false; +#endif // _DEBUG + m_nENVMAP_MASK = 0; +#ifdef _DEBUG + m_bTANGENTSPACE = false; +#endif // _DEBUG + m_nTANGENTSPACE = 0; +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = false; +#endif // _DEBUG + m_nDIFFUSEBUMPMAP = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bVERTEXALPHATEXBLENDFACTOR = false; +#endif // _DEBUG + m_nVERTEXALPHATEXBLENDFACTOR = 0; +#ifdef _DEBUG + m_bRELIEF_MAPPING = false; +#endif // _DEBUG + m_nRELIEF_MAPPING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bBUMPMASK = false; +#endif // _DEBUG + m_nBUMPMASK = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bENVMAP_MASK && m_bTANGENTSPACE && m_bBUMPMAP && m_bDIFFUSEBUMPMAP && m_bVERTEXCOLOR && m_bVERTEXALPHATEXBLENDFACTOR && m_bRELIEF_MAPPING && m_bSEAMLESS && m_bBUMPMASK && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 8 * m_nENVMAP_MASK ) + ( 16 * m_nTANGENTSPACE ) + ( 32 * m_nBUMPMAP ) + ( 64 * m_nDIFFUSEBUMPMAP ) + ( 128 * m_nVERTEXCOLOR ) + ( 256 * m_nVERTEXALPHATEXBLENDFACTOR ) + ( 512 * m_nRELIEF_MAPPING ) + ( 512 * m_nSEAMLESS ) + ( 1024 * m_nBUMPMASK ) + ( 2048 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_vs20 vsh_forgot_to_set_static_ENVMAP_MASK + vsh_forgot_to_set_static_TANGENTSPACE + vsh_forgot_to_set_static_BUMPMAP + vsh_forgot_to_set_static_DIFFUSEBUMPMAP + vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_VERTEXALPHATEXBLENDFACTOR + vsh_forgot_to_set_static_RELIEF_MAPPING + vsh_forgot_to_set_static_SEAMLESS + vsh_forgot_to_set_static_BUMPMASK + vsh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_vs20_Dynamic_Index +{ +private: + int m_nFASTPATH; +#ifdef _DEBUG + bool m_bFASTPATH; +#endif +public: + void SetFASTPATH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATH = i; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } + void SetFASTPATH( bool i ) + { + m_nFASTPATH = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +public: + sdk_lightmappedgeneric_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bFASTPATH = false; +#endif // _DEBUG + m_nFASTPATH = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bFASTPATH && m_bDOWATERFOG && m_bLIGHTING_PREVIEW; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nFASTPATH ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nLIGHTING_PREVIEW ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_vs20 vsh_forgot_to_set_dynamic_FASTPATH + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_LIGHTING_PREVIEW + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs30.inc new file mode 100644 index 00000000..5fda5413 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedgeneric_vs30.inc @@ -0,0 +1,362 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedgeneric_vs30_Static_Index +{ +private: + int m_nENVMAP_MASK; +#ifdef _DEBUG + bool m_bENVMAP_MASK; +#endif +public: + void SetENVMAP_MASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAP_MASK = i; +#ifdef _DEBUG + m_bENVMAP_MASK = true; +#endif + } + void SetENVMAP_MASK( bool i ) + { + m_nENVMAP_MASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAP_MASK = true; +#endif + } +private: + int m_nTANGENTSPACE; +#ifdef _DEBUG + bool m_bTANGENTSPACE; +#endif +public: + void SetTANGENTSPACE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTANGENTSPACE = i; +#ifdef _DEBUG + m_bTANGENTSPACE = true; +#endif + } + void SetTANGENTSPACE( bool i ) + { + m_nTANGENTSPACE = i ? 1 : 0; +#ifdef _DEBUG + m_bTANGENTSPACE = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +private: + int m_nDIFFUSEBUMPMAP; +#ifdef _DEBUG + bool m_bDIFFUSEBUMPMAP; +#endif +public: + void SetDIFFUSEBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSEBUMPMAP = i; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } + void SetDIFFUSEBUMPMAP( bool i ) + { + m_nDIFFUSEBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nVERTEXALPHATEXBLENDFACTOR; +#ifdef _DEBUG + bool m_bVERTEXALPHATEXBLENDFACTOR; +#endif +public: + void SetVERTEXALPHATEXBLENDFACTOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXALPHATEXBLENDFACTOR = i; +#ifdef _DEBUG + m_bVERTEXALPHATEXBLENDFACTOR = true; +#endif + } + void SetVERTEXALPHATEXBLENDFACTOR( bool i ) + { + m_nVERTEXALPHATEXBLENDFACTOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXALPHATEXBLENDFACTOR = true; +#endif + } +private: + int m_nRELIEF_MAPPING; +#ifdef _DEBUG + bool m_bRELIEF_MAPPING; +#endif +public: + void SetRELIEF_MAPPING( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nRELIEF_MAPPING = i; +#ifdef _DEBUG + m_bRELIEF_MAPPING = true; +#endif + } + void SetRELIEF_MAPPING( bool i ) + { + m_nRELIEF_MAPPING = i ? 1 : 0; +#ifdef _DEBUG + m_bRELIEF_MAPPING = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nBUMPMASK; +#ifdef _DEBUG + bool m_bBUMPMASK; +#endif +public: + void SetBUMPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMASK = i; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } + void SetBUMPMASK( bool i ) + { + m_nBUMPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMASK = true; +#endif + } +private: + int m_nBASETEXTURETRANSFORM2; +#ifdef _DEBUG + bool m_bBASETEXTURETRANSFORM2; +#endif +public: + void SetBASETEXTURETRANSFORM2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURETRANSFORM2 = i; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } + void SetBASETEXTURETRANSFORM2( bool i ) + { + m_nBASETEXTURETRANSFORM2 = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = true; +#endif + } +public: + sdk_lightmappedgeneric_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bENVMAP_MASK = false; +#endif // _DEBUG + m_nENVMAP_MASK = 0; +#ifdef _DEBUG + m_bTANGENTSPACE = false; +#endif // _DEBUG + m_nTANGENTSPACE = 0; +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = false; +#endif // _DEBUG + m_nDIFFUSEBUMPMAP = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bVERTEXALPHATEXBLENDFACTOR = false; +#endif // _DEBUG + m_nVERTEXALPHATEXBLENDFACTOR = 0; +#ifdef _DEBUG + m_bRELIEF_MAPPING = false; +#endif // _DEBUG + m_nRELIEF_MAPPING = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bBUMPMASK = false; +#endif // _DEBUG + m_nBUMPMASK = 0; +#ifdef _DEBUG + m_bBASETEXTURETRANSFORM2 = false; +#endif // _DEBUG + m_nBASETEXTURETRANSFORM2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bENVMAP_MASK && m_bTANGENTSPACE && m_bBUMPMAP && m_bDIFFUSEBUMPMAP && m_bVERTEXCOLOR && m_bVERTEXALPHATEXBLENDFACTOR && m_bRELIEF_MAPPING && m_bSEAMLESS && m_bBUMPMASK && m_bBASETEXTURETRANSFORM2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 8 * m_nENVMAP_MASK ) + ( 16 * m_nTANGENTSPACE ) + ( 32 * m_nBUMPMAP ) + ( 64 * m_nDIFFUSEBUMPMAP ) + ( 128 * m_nVERTEXCOLOR ) + ( 256 * m_nVERTEXALPHATEXBLENDFACTOR ) + ( 512 * m_nRELIEF_MAPPING ) + ( 512 * m_nSEAMLESS ) + ( 1024 * m_nBUMPMASK ) + ( 2048 * m_nBASETEXTURETRANSFORM2 ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedgeneric_vs30 vsh_forgot_to_set_static_ENVMAP_MASK + vsh_forgot_to_set_static_TANGENTSPACE + vsh_forgot_to_set_static_BUMPMAP + vsh_forgot_to_set_static_DIFFUSEBUMPMAP + vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_VERTEXALPHATEXBLENDFACTOR + vsh_forgot_to_set_static_RELIEF_MAPPING + vsh_forgot_to_set_static_SEAMLESS + vsh_forgot_to_set_static_BUMPMASK + vsh_forgot_to_set_static_BASETEXTURETRANSFORM2 + 0 +class sdk_lightmappedgeneric_vs30_Dynamic_Index +{ +private: + int m_nFASTPATH; +#ifdef _DEBUG + bool m_bFASTPATH; +#endif +public: + void SetFASTPATH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATH = i; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } + void SetFASTPATH( bool i ) + { + m_nFASTPATH = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATH = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +public: + sdk_lightmappedgeneric_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bFASTPATH = false; +#endif // _DEBUG + m_nFASTPATH = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bFASTPATH && m_bDOWATERFOG && m_bLIGHTING_PREVIEW; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nFASTPATH ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nLIGHTING_PREVIEW ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedgeneric_vs30 vsh_forgot_to_set_dynamic_FASTPATH + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_LIGHTING_PREVIEW + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20.inc new file mode 100644 index 00000000..469d8062 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedreflective_ps20_Static_Index +{ +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +private: + int m_nREFLECT; +#ifdef _DEBUG + bool m_bREFLECT; +#endif +public: + void SetREFLECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFLECT = i; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } + void SetREFLECT( bool i ) + { + m_nREFLECT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } +private: + int m_nREFRACT; +#ifdef _DEBUG + bool m_bREFRACT; +#endif +public: + void SetREFRACT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACT = i; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } + void SetREFRACT( bool i ) + { + m_nREFRACT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_lightmappedreflective_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; +#ifdef _DEBUG + m_bREFLECT = false; +#endif // _DEBUG + m_nREFLECT = 0; +#ifdef _DEBUG + m_bREFRACT = false; +#endif // _DEBUG + m_nREFRACT = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBASETEXTURE && m_bREFLECT && m_bREFRACT && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nBASETEXTURE ) + ( 4 * m_nREFLECT ) + ( 8 * m_nREFRACT ) + ( 16 * m_nENVMAPMASK ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedreflective_ps20 psh_forgot_to_set_static_BASETEXTURE + psh_forgot_to_set_static_REFLECT + psh_forgot_to_set_static_REFRACT + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_lightmappedreflective_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_lightmappedreflective_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedreflective_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20b.inc new file mode 100644 index 00000000..e5a00725 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_ps20b.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedreflective_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +private: + int m_nREFLECT; +#ifdef _DEBUG + bool m_bREFLECT; +#endif +public: + void SetREFLECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFLECT = i; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } + void SetREFLECT( bool i ) + { + m_nREFLECT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFLECT = true; +#endif + } +private: + int m_nREFRACT; +#ifdef _DEBUG + bool m_bREFRACT; +#endif +public: + void SetREFRACT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACT = i; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } + void SetREFRACT( bool i ) + { + m_nREFRACT = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACT = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_lightmappedreflective_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; +#ifdef _DEBUG + m_bREFLECT = false; +#endif // _DEBUG + m_nREFLECT = 0; +#ifdef _DEBUG + m_bREFRACT = false; +#endif // _DEBUG + m_nREFRACT = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bBASETEXTURE && m_bREFLECT && m_bREFRACT && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nBASETEXTURE ) + ( 16 * m_nREFLECT ) + ( 32 * m_nREFRACT ) + ( 64 * m_nENVMAPMASK ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedreflective_ps20b psh_forgot_to_set_static_BASETEXTURE + psh_forgot_to_set_static_REFLECT + psh_forgot_to_set_static_REFRACT + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_lightmappedreflective_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_lightmappedreflective_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedreflective_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_vs20.inc new file mode 100644 index 00000000..4321a47b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_lightmappedreflective_vs20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_lightmappedreflective_vs20_Static_Index +{ +private: + int m_nBASETEXTURE; +#ifdef _DEBUG + bool m_bBASETEXTURE; +#endif +public: + void SetBASETEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASETEXTURE = i; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } + void SetBASETEXTURE( bool i ) + { + m_nBASETEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bBASETEXTURE = true; +#endif + } +public: + sdk_lightmappedreflective_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bBASETEXTURE = false; +#endif // _DEBUG + m_nBASETEXTURE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBASETEXTURE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nBASETEXTURE ) + 0; + } +}; +#define shaderStaticTest_sdk_lightmappedreflective_vs20 vsh_forgot_to_set_static_BASETEXTURE + 0 +class sdk_lightmappedreflective_vs20_Dynamic_Index +{ +public: + sdk_lightmappedreflective_vs20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_lightmappedreflective_vs20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20.inc new file mode 100644 index 00000000..12b6a1b6 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_monitorscreen_ps20_Static_Index +{ +private: + int m_nTEXTURE2; +#ifdef _DEBUG + bool m_bTEXTURE2; +#endif +public: + void SetTEXTURE2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTEXTURE2 = i; +#ifdef _DEBUG + m_bTEXTURE2 = true; +#endif + } + void SetTEXTURE2( bool i ) + { + m_nTEXTURE2 = i ? 1 : 0; +#ifdef _DEBUG + m_bTEXTURE2 = true; +#endif + } +public: + sdk_monitorscreen_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bTEXTURE2 = false; +#endif // _DEBUG + m_nTEXTURE2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bTEXTURE2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nTEXTURE2 ) + 0; + } +}; +#define shaderStaticTest_sdk_monitorscreen_ps20 psh_forgot_to_set_static_TEXTURE2 + 0 +class sdk_monitorscreen_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_monitorscreen_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_monitorscreen_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20b.inc new file mode 100644 index 00000000..39daea2f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_monitorscreen_ps20b.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_monitorscreen_ps20b_Static_Index +{ +private: + int m_nTEXTURE2; +#ifdef _DEBUG + bool m_bTEXTURE2; +#endif +public: + void SetTEXTURE2( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTEXTURE2 = i; +#ifdef _DEBUG + m_bTEXTURE2 = true; +#endif + } + void SetTEXTURE2( bool i ) + { + m_nTEXTURE2 = i ? 1 : 0; +#ifdef _DEBUG + m_bTEXTURE2 = true; +#endif + } +public: + sdk_monitorscreen_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bTEXTURE2 = false; +#endif // _DEBUG + m_nTEXTURE2 = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bTEXTURE2; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nTEXTURE2 ) + 0; + } +}; +#define shaderStaticTest_sdk_monitorscreen_ps20b psh_forgot_to_set_static_TEXTURE2 + 0 +class sdk_monitorscreen_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_monitorscreen_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_monitorscreen_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20.inc new file mode 100644 index 00000000..839d80a2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20.inc @@ -0,0 +1,262 @@ +#include "shaderlib/cshader.h" +class sdk_refract_ps20_Static_Index +{ +private: + int m_nBLUR; +#ifdef _DEBUG + bool m_bBLUR; +#endif +public: + void SetBLUR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLUR = i; +#ifdef _DEBUG + m_bBLUR = true; +#endif + } + void SetBLUR( bool i ) + { + m_nBLUR = i ? 1 : 0; +#ifdef _DEBUG + m_bBLUR = true; +#endif + } +private: + int m_nFADEOUTONSILHOUETTE; +#ifdef _DEBUG + bool m_bFADEOUTONSILHOUETTE; +#endif +public: + void SetFADEOUTONSILHOUETTE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFADEOUTONSILHOUETTE = i; +#ifdef _DEBUG + m_bFADEOUTONSILHOUETTE = true; +#endif + } + void SetFADEOUTONSILHOUETTE( bool i ) + { + m_nFADEOUTONSILHOUETTE = i ? 1 : 0; +#ifdef _DEBUG + m_bFADEOUTONSILHOUETTE = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nREFRACTTINTTEXTURE; +#ifdef _DEBUG + bool m_bREFRACTTINTTEXTURE; +#endif +public: + void SetREFRACTTINTTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACTTINTTEXTURE = i; +#ifdef _DEBUG + m_bREFRACTTINTTEXTURE = true; +#endif + } + void SetREFRACTTINTTEXTURE( bool i ) + { + m_nREFRACTTINTTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACTTINTTEXTURE = true; +#endif + } +private: + int m_nMASKED; +#ifdef _DEBUG + bool m_bMASKED; +#endif +public: + void SetMASKED( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMASKED = i; +#ifdef _DEBUG + m_bMASKED = true; +#endif + } + void SetMASKED( bool i ) + { + m_nMASKED = i ? 1 : 0; +#ifdef _DEBUG + m_bMASKED = true; +#endif + } +private: + int m_nCOLORMODULATE; +#ifdef _DEBUG + bool m_bCOLORMODULATE; +#endif +public: + void SetCOLORMODULATE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLORMODULATE = i; +#ifdef _DEBUG + m_bCOLORMODULATE = true; +#endif + } + void SetCOLORMODULATE( bool i ) + { + m_nCOLORMODULATE = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLORMODULATE = true; +#endif + } +private: + int m_nSECONDARY_NORMAL; +#ifdef _DEBUG + bool m_bSECONDARY_NORMAL; +#endif +public: + void SetSECONDARY_NORMAL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSECONDARY_NORMAL = i; +#ifdef _DEBUG + m_bSECONDARY_NORMAL = true; +#endif + } + void SetSECONDARY_NORMAL( bool i ) + { + m_nSECONDARY_NORMAL = i ? 1 : 0; +#ifdef _DEBUG + m_bSECONDARY_NORMAL = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +public: + sdk_refract_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bBLUR = false; +#endif // _DEBUG + m_nBLUR = 0; +#ifdef _DEBUG + m_bFADEOUTONSILHOUETTE = false; +#endif // _DEBUG + m_nFADEOUTONSILHOUETTE = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bREFRACTTINTTEXTURE = false; +#endif // _DEBUG + m_nREFRACTTINTTEXTURE = 0; +#ifdef _DEBUG + m_bMASKED = false; +#endif // _DEBUG + m_nMASKED = 0; +#ifdef _DEBUG + m_bCOLORMODULATE = false; +#endif // _DEBUG + m_nCOLORMODULATE = 0; +#ifdef _DEBUG + m_bSECONDARY_NORMAL = false; +#endif // _DEBUG + m_nSECONDARY_NORMAL = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bBLUR && m_bFADEOUTONSILHOUETTE && m_bCUBEMAP && m_bREFRACTTINTTEXTURE && m_bMASKED && m_bCOLORMODULATE && m_bSECONDARY_NORMAL && m_bNORMAL_DECODE_MODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nBLUR ) + ( 4 * m_nFADEOUTONSILHOUETTE ) + ( 8 * m_nCUBEMAP ) + ( 16 * m_nREFRACTTINTTEXTURE ) + ( 32 * m_nMASKED ) + ( 64 * m_nCOLORMODULATE ) + ( 128 * m_nSECONDARY_NORMAL ) + ( 256 * m_nNORMAL_DECODE_MODE ) + 0; + } +}; +#define shaderStaticTest_sdk_refract_ps20 psh_forgot_to_set_static_BLUR + psh_forgot_to_set_static_FADEOUTONSILHOUETTE + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_REFRACTTINTTEXTURE + psh_forgot_to_set_static_MASKED + psh_forgot_to_set_static_COLORMODULATE + psh_forgot_to_set_static_SECONDARY_NORMAL + psh_forgot_to_set_static_NORMAL_DECODE_MODE + 0 +class sdk_refract_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_refract_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_refract_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20b.inc new file mode 100644 index 00000000..3d877ae0 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_refract_ps20b.inc @@ -0,0 +1,337 @@ +#include "shaderlib/cshader.h" +class sdk_refract_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nBLUR; +#ifdef _DEBUG + bool m_bBLUR; +#endif +public: + void SetBLUR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLUR = i; +#ifdef _DEBUG + m_bBLUR = true; +#endif + } + void SetBLUR( bool i ) + { + m_nBLUR = i ? 1 : 0; +#ifdef _DEBUG + m_bBLUR = true; +#endif + } +private: + int m_nFADEOUTONSILHOUETTE; +#ifdef _DEBUG + bool m_bFADEOUTONSILHOUETTE; +#endif +public: + void SetFADEOUTONSILHOUETTE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFADEOUTONSILHOUETTE = i; +#ifdef _DEBUG + m_bFADEOUTONSILHOUETTE = true; +#endif + } + void SetFADEOUTONSILHOUETTE( bool i ) + { + m_nFADEOUTONSILHOUETTE = i ? 1 : 0; +#ifdef _DEBUG + m_bFADEOUTONSILHOUETTE = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nREFRACTTINTTEXTURE; +#ifdef _DEBUG + bool m_bREFRACTTINTTEXTURE; +#endif +public: + void SetREFRACTTINTTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nREFRACTTINTTEXTURE = i; +#ifdef _DEBUG + m_bREFRACTTINTTEXTURE = true; +#endif + } + void SetREFRACTTINTTEXTURE( bool i ) + { + m_nREFRACTTINTTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bREFRACTTINTTEXTURE = true; +#endif + } +private: + int m_nMASKED; +#ifdef _DEBUG + bool m_bMASKED; +#endif +public: + void SetMASKED( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMASKED = i; +#ifdef _DEBUG + m_bMASKED = true; +#endif + } + void SetMASKED( bool i ) + { + m_nMASKED = i ? 1 : 0; +#ifdef _DEBUG + m_bMASKED = true; +#endif + } +private: + int m_nCOLORMODULATE; +#ifdef _DEBUG + bool m_bCOLORMODULATE; +#endif +public: + void SetCOLORMODULATE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOLORMODULATE = i; +#ifdef _DEBUG + m_bCOLORMODULATE = true; +#endif + } + void SetCOLORMODULATE( bool i ) + { + m_nCOLORMODULATE = i ? 1 : 0; +#ifdef _DEBUG + m_bCOLORMODULATE = true; +#endif + } +private: + int m_nSECONDARY_NORMAL; +#ifdef _DEBUG + bool m_bSECONDARY_NORMAL; +#endif +public: + void SetSECONDARY_NORMAL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSECONDARY_NORMAL = i; +#ifdef _DEBUG + m_bSECONDARY_NORMAL = true; +#endif + } + void SetSECONDARY_NORMAL( bool i ) + { + m_nSECONDARY_NORMAL = i ? 1 : 0; +#ifdef _DEBUG + m_bSECONDARY_NORMAL = true; +#endif + } +private: + int m_nNORMAL_DECODE_MODE; +#ifdef _DEBUG + bool m_bNORMAL_DECODE_MODE; +#endif +public: + void SetNORMAL_DECODE_MODE( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nNORMAL_DECODE_MODE = i; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } + void SetNORMAL_DECODE_MODE( bool i ) + { + m_nNORMAL_DECODE_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = true; +#endif + } +private: + int m_nSHADER_SRGB_READ; +#ifdef _DEBUG + bool m_bSHADER_SRGB_READ; +#endif +public: + void SetSHADER_SRGB_READ( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSHADER_SRGB_READ = i; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } + void SetSHADER_SRGB_READ( bool i ) + { + m_nSHADER_SRGB_READ = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } +public: + sdk_refract_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bBLUR = false; +#endif // _DEBUG + m_nBLUR = 0; +#ifdef _DEBUG + m_bFADEOUTONSILHOUETTE = false; +#endif // _DEBUG + m_nFADEOUTONSILHOUETTE = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bREFRACTTINTTEXTURE = false; +#endif // _DEBUG + m_nREFRACTTINTTEXTURE = 0; +#ifdef _DEBUG + m_bMASKED = false; +#endif // _DEBUG + m_nMASKED = 0; +#ifdef _DEBUG + m_bCOLORMODULATE = false; +#endif // _DEBUG + m_nCOLORMODULATE = 0; +#ifdef _DEBUG + m_bSECONDARY_NORMAL = false; +#endif // _DEBUG + m_nSECONDARY_NORMAL = 0; +#ifdef _DEBUG + m_bNORMAL_DECODE_MODE = false; +#endif // _DEBUG + m_nNORMAL_DECODE_MODE = 0; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = false; +#endif // _DEBUG + m_nSHADER_SRGB_READ = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bBLUR && m_bFADEOUTONSILHOUETTE && m_bCUBEMAP && m_bREFRACTTINTTEXTURE && m_bMASKED && m_bCOLORMODULATE && m_bSECONDARY_NORMAL && m_bNORMAL_DECODE_MODE && m_bSHADER_SRGB_READ; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nBLUR ) + ( 16 * m_nFADEOUTONSILHOUETTE ) + ( 32 * m_nCUBEMAP ) + ( 64 * m_nREFRACTTINTTEXTURE ) + ( 128 * m_nMASKED ) + ( 256 * m_nCOLORMODULATE ) + ( 512 * m_nSECONDARY_NORMAL ) + ( 1024 * m_nNORMAL_DECODE_MODE ) + ( 1024 * m_nSHADER_SRGB_READ ) + 0; + } +}; +#define shaderStaticTest_sdk_refract_ps20b psh_forgot_to_set_static_BLUR + psh_forgot_to_set_static_FADEOUTONSILHOUETTE + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_REFRACTTINTTEXTURE + psh_forgot_to_set_static_MASKED + psh_forgot_to_set_static_COLORMODULATE + psh_forgot_to_set_static_SECONDARY_NORMAL + psh_forgot_to_set_static_NORMAL_DECODE_MODE + psh_forgot_to_set_static_SHADER_SRGB_READ + 0 +class sdk_refract_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_refract_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_refract_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps20b.inc new file mode 100644 index 00000000..eee79b75 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps20b.inc @@ -0,0 +1,587 @@ +#include "shaderlib/cshader.h" +class sdk_skin_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nSELFILLUMFRESNEL; +#ifdef _DEBUG + bool m_bSELFILLUMFRESNEL; +#endif +public: + void SetSELFILLUMFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUMFRESNEL = i; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } + void SetSELFILLUMFRESNEL( bool i ) + { + m_nSELFILLUMFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nPHONGWARPTEXTURE; +#ifdef _DEBUG + bool m_bPHONGWARPTEXTURE; +#endif +public: + void SetPHONGWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONGWARPTEXTURE = i; +#ifdef _DEBUG + m_bPHONGWARPTEXTURE = true; +#endif + } + void SetPHONGWARPTEXTURE( bool i ) + { + m_nPHONGWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONGWARPTEXTURE = true; +#endif + } +private: + int m_nWRINKLEMAP; +#ifdef _DEBUG + bool m_bWRINKLEMAP; +#endif +public: + void SetWRINKLEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRINKLEMAP = i; +#ifdef _DEBUG + m_bWRINKLEMAP = true; +#endif + } + void SetWRINKLEMAP( bool i ) + { + m_nWRINKLEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bWRINKLEMAP = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 6 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nRIMLIGHT; +#ifdef _DEBUG + bool m_bRIMLIGHT; +#endif +public: + void SetRIMLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nRIMLIGHT = i; +#ifdef _DEBUG + m_bRIMLIGHT = true; +#endif + } + void SetRIMLIGHT( bool i ) + { + m_nRIMLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bRIMLIGHT = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nFASTPATH_NOBUMP; +#ifdef _DEBUG + bool m_bFASTPATH_NOBUMP; +#endif +public: + void SetFASTPATH_NOBUMP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATH_NOBUMP = i; +#ifdef _DEBUG + m_bFASTPATH_NOBUMP = true; +#endif + } + void SetFASTPATH_NOBUMP( bool i ) + { + m_nFASTPATH_NOBUMP = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATH_NOBUMP = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nPHONG_HALFLAMBERT; +#ifdef _DEBUG + bool m_bPHONG_HALFLAMBERT; +#endif +public: + void SetPHONG_HALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG_HALFLAMBERT = i; +#ifdef _DEBUG + m_bPHONG_HALFLAMBERT = true; +#endif + } + void SetPHONG_HALFLAMBERT( bool i ) + { + m_nPHONG_HALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG_HALFLAMBERT = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_skin_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = false; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = false; +#endif // _DEBUG + m_nSELFILLUMFRESNEL = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bPHONGWARPTEXTURE = false; +#endif // _DEBUG + m_nPHONGWARPTEXTURE = 0; +#ifdef _DEBUG + m_bWRINKLEMAP = false; +#endif // _DEBUG + m_nWRINKLEMAP = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bRIMLIGHT = false; +#endif // _DEBUG + m_nRIMLIGHT = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bFASTPATH_NOBUMP = false; +#endif // _DEBUG + m_nFASTPATH_NOBUMP = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bPHONG_HALFLAMBERT = false; +#endif // _DEBUG + m_nPHONG_HALFLAMBERT = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bCUBEMAP && m_bSELFILLUM && m_bSELFILLUMFRESNEL && m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE && m_bPHONGWARPTEXTURE && m_bWRINKLEMAP && m_bDETAIL_BLEND_MODE && m_bDETAILTEXTURE && m_bRIMLIGHT && m_bFLASHLIGHTDEPTHFILTERMODE && m_bFASTPATH_NOBUMP && m_bBLENDTINTBYBASEALPHA && m_bPHONG_HALFLAMBERT && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 160 * m_nCONVERT_TO_SRGB ) + ( 160 * m_nCUBEMAP ) + ( 320 * m_nSELFILLUM ) + ( 640 * m_nSELFILLUMFRESNEL ) + ( 1280 * m_nFLASHLIGHT ) + ( 2560 * m_nLIGHTWARPTEXTURE ) + ( 5120 * m_nPHONGWARPTEXTURE ) + ( 10240 * m_nWRINKLEMAP ) + ( 20480 * m_nDETAIL_BLEND_MODE ) + ( 143360 * m_nDETAILTEXTURE ) + ( 286720 * m_nRIMLIGHT ) + ( 573440 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 1720320 * m_nFASTPATH_NOBUMP ) + ( 3440640 * m_nBLENDTINTBYBASEALPHA ) + ( 6881280 * m_nPHONG_HALFLAMBERT ) + 0; + } +}; +#define shaderStaticTest_sdk_skin_ps20b psh_forgot_to_set_static_CONVERT_TO_SRGB + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_SELFILLUMFRESNEL + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_PHONGWARPTEXTURE + psh_forgot_to_set_static_WRINKLEMAP + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_RIMLIGHT + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_FASTPATH_NOBUMP + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_PHONG_HALFLAMBERT + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_skin_ps20b_Dynamic_Index +{ +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +private: + int m_nPHONG_USE_EXPONENT_FACTOR; +#ifdef _DEBUG + bool m_bPHONG_USE_EXPONENT_FACTOR; +#endif +public: + void SetPHONG_USE_EXPONENT_FACTOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG_USE_EXPONENT_FACTOR = i; +#ifdef _DEBUG + m_bPHONG_USE_EXPONENT_FACTOR = true; +#endif + } + void SetPHONG_USE_EXPONENT_FACTOR( bool i ) + { + m_nPHONG_USE_EXPONENT_FACTOR = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG_USE_EXPONENT_FACTOR = true; +#endif + } +public: + sdk_skin_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; +#ifdef _DEBUG + m_bPHONG_USE_EXPONENT_FACTOR = false; +#endif // _DEBUG + m_nPHONG_USE_EXPONENT_FACTOR = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITEWATERFOGTODESTALPHA && m_bPIXELFOGTYPE && m_bNUM_LIGHTS && m_bWRITE_DEPTH_TO_DESTALPHA && m_bFLASHLIGHTSHADOWS && m_bPHONG_USE_EXPONENT_FACTOR; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITEWATERFOGTODESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + ( 4 * m_nNUM_LIGHTS ) + ( 20 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 40 * m_nFLASHLIGHTSHADOWS ) + ( 80 * m_nPHONG_USE_EXPONENT_FACTOR ) + 0; + } +}; +#define shaderDynamicTest_sdk_skin_ps20b psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + psh_forgot_to_set_dynamic_PHONG_USE_EXPONENT_FACTOR + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps30.inc new file mode 100644 index 00000000..89094e70 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_ps30.inc @@ -0,0 +1,587 @@ +#include "shaderlib/cshader.h" +class sdk_skin_ps30_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nSELFILLUMFRESNEL; +#ifdef _DEBUG + bool m_bSELFILLUMFRESNEL; +#endif +public: + void SetSELFILLUMFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUMFRESNEL = i; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } + void SetSELFILLUMFRESNEL( bool i ) + { + m_nSELFILLUMFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nPHONGWARPTEXTURE; +#ifdef _DEBUG + bool m_bPHONGWARPTEXTURE; +#endif +public: + void SetPHONGWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONGWARPTEXTURE = i; +#ifdef _DEBUG + m_bPHONGWARPTEXTURE = true; +#endif + } + void SetPHONGWARPTEXTURE( bool i ) + { + m_nPHONGWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONGWARPTEXTURE = true; +#endif + } +private: + int m_nWRINKLEMAP; +#ifdef _DEBUG + bool m_bWRINKLEMAP; +#endif +public: + void SetWRINKLEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRINKLEMAP = i; +#ifdef _DEBUG + m_bWRINKLEMAP = true; +#endif + } + void SetWRINKLEMAP( bool i ) + { + m_nWRINKLEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bWRINKLEMAP = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 6 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nRIMLIGHT; +#ifdef _DEBUG + bool m_bRIMLIGHT; +#endif +public: + void SetRIMLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nRIMLIGHT = i; +#ifdef _DEBUG + m_bRIMLIGHT = true; +#endif + } + void SetRIMLIGHT( bool i ) + { + m_nRIMLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bRIMLIGHT = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nFASTPATH_NOBUMP; +#ifdef _DEBUG + bool m_bFASTPATH_NOBUMP; +#endif +public: + void SetFASTPATH_NOBUMP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFASTPATH_NOBUMP = i; +#ifdef _DEBUG + m_bFASTPATH_NOBUMP = true; +#endif + } + void SetFASTPATH_NOBUMP( bool i ) + { + m_nFASTPATH_NOBUMP = i ? 1 : 0; +#ifdef _DEBUG + m_bFASTPATH_NOBUMP = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nPHONG_HALFLAMBERT; +#ifdef _DEBUG + bool m_bPHONG_HALFLAMBERT; +#endif +public: + void SetPHONG_HALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG_HALFLAMBERT = i; +#ifdef _DEBUG + m_bPHONG_HALFLAMBERT = true; +#endif + } + void SetPHONG_HALFLAMBERT( bool i ) + { + m_nPHONG_HALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG_HALFLAMBERT = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_skin_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = false; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = false; +#endif // _DEBUG + m_nSELFILLUMFRESNEL = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bPHONGWARPTEXTURE = false; +#endif // _DEBUG + m_nPHONGWARPTEXTURE = 0; +#ifdef _DEBUG + m_bWRINKLEMAP = false; +#endif // _DEBUG + m_nWRINKLEMAP = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bRIMLIGHT = false; +#endif // _DEBUG + m_nRIMLIGHT = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bFASTPATH_NOBUMP = false; +#endif // _DEBUG + m_nFASTPATH_NOBUMP = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bPHONG_HALFLAMBERT = false; +#endif // _DEBUG + m_nPHONG_HALFLAMBERT = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bCUBEMAP && m_bSELFILLUM && m_bSELFILLUMFRESNEL && m_bFLASHLIGHT && m_bLIGHTWARPTEXTURE && m_bPHONGWARPTEXTURE && m_bWRINKLEMAP && m_bDETAIL_BLEND_MODE && m_bDETAILTEXTURE && m_bRIMLIGHT && m_bFLASHLIGHTDEPTHFILTERMODE && m_bFASTPATH_NOBUMP && m_bBLENDTINTBYBASEALPHA && m_bPHONG_HALFLAMBERT && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 160 * m_nCONVERT_TO_SRGB ) + ( 160 * m_nCUBEMAP ) + ( 320 * m_nSELFILLUM ) + ( 640 * m_nSELFILLUMFRESNEL ) + ( 1280 * m_nFLASHLIGHT ) + ( 2560 * m_nLIGHTWARPTEXTURE ) + ( 5120 * m_nPHONGWARPTEXTURE ) + ( 10240 * m_nWRINKLEMAP ) + ( 20480 * m_nDETAIL_BLEND_MODE ) + ( 143360 * m_nDETAILTEXTURE ) + ( 286720 * m_nRIMLIGHT ) + ( 573440 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 1720320 * m_nFASTPATH_NOBUMP ) + ( 3440640 * m_nBLENDTINTBYBASEALPHA ) + ( 6881280 * m_nPHONG_HALFLAMBERT ) + 0; + } +}; +#define shaderStaticTest_sdk_skin_ps30 psh_forgot_to_set_static_CONVERT_TO_SRGB + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_SELFILLUMFRESNEL + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_PHONGWARPTEXTURE + psh_forgot_to_set_static_WRINKLEMAP + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_RIMLIGHT + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_FASTPATH_NOBUMP + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_PHONG_HALFLAMBERT + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_skin_ps30_Dynamic_Index +{ +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +private: + int m_nPHONG_USE_EXPONENT_FACTOR; +#ifdef _DEBUG + bool m_bPHONG_USE_EXPONENT_FACTOR; +#endif +public: + void SetPHONG_USE_EXPONENT_FACTOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPHONG_USE_EXPONENT_FACTOR = i; +#ifdef _DEBUG + m_bPHONG_USE_EXPONENT_FACTOR = true; +#endif + } + void SetPHONG_USE_EXPONENT_FACTOR( bool i ) + { + m_nPHONG_USE_EXPONENT_FACTOR = i ? 1 : 0; +#ifdef _DEBUG + m_bPHONG_USE_EXPONENT_FACTOR = true; +#endif + } +public: + sdk_skin_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; +#ifdef _DEBUG + m_bPHONG_USE_EXPONENT_FACTOR = false; +#endif // _DEBUG + m_nPHONG_USE_EXPONENT_FACTOR = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITEWATERFOGTODESTALPHA && m_bPIXELFOGTYPE && m_bNUM_LIGHTS && m_bWRITE_DEPTH_TO_DESTALPHA && m_bFLASHLIGHTSHADOWS && m_bPHONG_USE_EXPONENT_FACTOR; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITEWATERFOGTODESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + ( 4 * m_nNUM_LIGHTS ) + ( 20 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 40 * m_nFLASHLIGHTSHADOWS ) + ( 80 * m_nPHONG_USE_EXPONENT_FACTOR ) + 0; + } +}; +#define shaderDynamicTest_sdk_skin_ps30 psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + psh_forgot_to_set_dynamic_PHONG_USE_EXPONENT_FACTOR + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs20.inc new file mode 100644 index 00000000..22a29bf7 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs20.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_skin_vs20_Static_Index +{ +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +public: + sdk_skin_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bUSE_STATIC_CONTROL_FLOW; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 48 * m_nUSE_STATIC_CONTROL_FLOW ) + 0; + } +}; +#define shaderStaticTest_sdk_skin_vs20 vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + 0 +class sdk_skin_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_skin_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bLIGHTING_PREVIEW && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nLIGHTING_PREVIEW ) + ( 16 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_skin_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_LIGHTING_PREVIEW + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs30.inc new file mode 100644 index 00000000..fb6a1125 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_skin_vs30.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_skin_vs30_Static_Index +{ +private: + int m_nDECAL; +#ifdef _DEBUG + bool m_bDECAL; +#endif +public: + void SetDECAL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDECAL = i; +#ifdef _DEBUG + m_bDECAL = true; +#endif + } + void SetDECAL( bool i ) + { + m_nDECAL = i ? 1 : 0; +#ifdef _DEBUG + m_bDECAL = true; +#endif + } +public: + sdk_skin_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bDECAL = false; +#endif // _DEBUG + m_nDECAL = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bDECAL; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 32 * m_nDECAL ) + 0; + } +}; +#define shaderStaticTest_sdk_skin_vs30 vsh_forgot_to_set_static_DECAL + 0 +class sdk_skin_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_skin_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bLIGHTING_PREVIEW && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nLIGHTING_PREVIEW ) + ( 16 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_skin_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_LIGHTING_PREVIEW + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20.inc new file mode 100644 index 00000000..f43cdb2f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_splinerope_ps20_Static_Index +{ +private: + int m_nSHADER_SRGB_READ; +#ifdef _DEBUG + bool m_bSHADER_SRGB_READ; +#endif +public: + void SetSHADER_SRGB_READ( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nSHADER_SRGB_READ = i; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } + void SetSHADER_SRGB_READ( bool i ) + { + m_nSHADER_SRGB_READ = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } +private: + int m_nSHADOWDEPTH; +#ifdef _DEBUG + bool m_bSHADOWDEPTH; +#endif +public: + void SetSHADOWDEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSHADOWDEPTH = i; +#ifdef _DEBUG + m_bSHADOWDEPTH = true; +#endif + } + void SetSHADOWDEPTH( bool i ) + { + m_nSHADOWDEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADOWDEPTH = true; +#endif + } +public: + sdk_splinerope_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bSHADER_SRGB_READ = false; +#endif // _DEBUG + m_nSHADER_SRGB_READ = 0; +#ifdef _DEBUG + m_bSHADOWDEPTH = false; +#endif // _DEBUG + m_nSHADOWDEPTH = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bSHADER_SRGB_READ && m_bSHADOWDEPTH; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nSHADER_SRGB_READ ) + ( 2 * m_nSHADOWDEPTH ) + 0; + } +}; +#define shaderStaticTest_sdk_splinerope_ps20 psh_forgot_to_set_static_SHADER_SRGB_READ + psh_forgot_to_set_static_SHADOWDEPTH + 0 +class sdk_splinerope_ps20_Dynamic_Index +{ +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_splinerope_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITE_DEPTH_TO_DESTALPHA && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_splinerope_ps20 psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20b.inc new file mode 100644 index 00000000..75ce64e9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps20b.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_splinerope_ps20b_Static_Index +{ +private: + int m_nSHADER_SRGB_READ; +#ifdef _DEBUG + bool m_bSHADER_SRGB_READ; +#endif +public: + void SetSHADER_SRGB_READ( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nSHADER_SRGB_READ = i; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } + void SetSHADER_SRGB_READ( bool i ) + { + m_nSHADER_SRGB_READ = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } +private: + int m_nSHADOWDEPTH; +#ifdef _DEBUG + bool m_bSHADOWDEPTH; +#endif +public: + void SetSHADOWDEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSHADOWDEPTH = i; +#ifdef _DEBUG + m_bSHADOWDEPTH = true; +#endif + } + void SetSHADOWDEPTH( bool i ) + { + m_nSHADOWDEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADOWDEPTH = true; +#endif + } +public: + sdk_splinerope_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bSHADER_SRGB_READ = false; +#endif // _DEBUG + m_nSHADER_SRGB_READ = 0; +#ifdef _DEBUG + m_bSHADOWDEPTH = false; +#endif // _DEBUG + m_nSHADOWDEPTH = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bSHADER_SRGB_READ && m_bSHADOWDEPTH; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nSHADER_SRGB_READ ) + ( 4 * m_nSHADOWDEPTH ) + 0; + } +}; +#define shaderStaticTest_sdk_splinerope_ps20b psh_forgot_to_set_static_SHADER_SRGB_READ + psh_forgot_to_set_static_SHADOWDEPTH + 0 +class sdk_splinerope_ps20b_Dynamic_Index +{ +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_splinerope_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITE_DEPTH_TO_DESTALPHA && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_splinerope_ps20b psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps30.inc new file mode 100644 index 00000000..6427d06f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_ps30.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_splinerope_ps30_Static_Index +{ +private: + int m_nSHADER_SRGB_READ; +#ifdef _DEBUG + bool m_bSHADER_SRGB_READ; +#endif +public: + void SetSHADER_SRGB_READ( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nSHADER_SRGB_READ = i; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } + void SetSHADER_SRGB_READ( bool i ) + { + m_nSHADER_SRGB_READ = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADER_SRGB_READ = true; +#endif + } +private: + int m_nSHADOWDEPTH; +#ifdef _DEBUG + bool m_bSHADOWDEPTH; +#endif +public: + void SetSHADOWDEPTH( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSHADOWDEPTH = i; +#ifdef _DEBUG + m_bSHADOWDEPTH = true; +#endif + } + void SetSHADOWDEPTH( bool i ) + { + m_nSHADOWDEPTH = i ? 1 : 0; +#ifdef _DEBUG + m_bSHADOWDEPTH = true; +#endif + } +public: + sdk_splinerope_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bSHADER_SRGB_READ = false; +#endif // _DEBUG + m_nSHADER_SRGB_READ = 0; +#ifdef _DEBUG + m_bSHADOWDEPTH = false; +#endif // _DEBUG + m_nSHADOWDEPTH = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bSHADER_SRGB_READ && m_bSHADOWDEPTH; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nSHADER_SRGB_READ ) + ( 2 * m_nSHADOWDEPTH ) + 0; + } +}; +#define shaderStaticTest_sdk_splinerope_ps30 psh_forgot_to_set_static_SHADER_SRGB_READ + psh_forgot_to_set_static_SHADOWDEPTH + 0 +class sdk_splinerope_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_splinerope_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_splinerope_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs20.inc new file mode 100644 index 00000000..ba584299 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_splinerope_vs20_Static_Index +{ +public: + sdk_splinerope_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_splinerope_vs20 0 +class sdk_splinerope_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_splinerope_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_splinerope_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs30.inc new file mode 100644 index 00000000..97c31b1b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_splinerope_vs30.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_splinerope_vs30_Static_Index +{ +public: + sdk_splinerope_vs30_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_splinerope_vs30 0 +class sdk_splinerope_vs30_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_splinerope_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_splinerope_vs30 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20.inc new file mode 100644 index 00000000..ac161fb8 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_sprite_ps20_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nCONSTANTCOLOR; +#ifdef _DEBUG + bool m_bCONSTANTCOLOR; +#endif +public: + void SetCONSTANTCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONSTANTCOLOR = i; +#ifdef _DEBUG + m_bCONSTANTCOLOR = true; +#endif + } + void SetCONSTANTCOLOR( bool i ) + { + m_nCONSTANTCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bCONSTANTCOLOR = true; +#endif + } +private: + int m_nHDRTYPE; +#ifdef _DEBUG + bool m_bHDRTYPE; +#endif +public: + void SetHDRTYPE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nHDRTYPE = i; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } + void SetHDRTYPE( bool i ) + { + m_nHDRTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } +private: + int m_nSRGB; +#ifdef _DEBUG + bool m_bSRGB; +#endif +public: + void SetSRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSRGB = i; +#ifdef _DEBUG + m_bSRGB = true; +#endif + } + void SetSRGB( bool i ) + { + m_nSRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bSRGB = true; +#endif + } +public: + sdk_sprite_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bCONSTANTCOLOR = false; +#endif // _DEBUG + m_nCONSTANTCOLOR = 0; +#ifdef _DEBUG + m_bHDRTYPE = false; +#endif // _DEBUG + m_nHDRTYPE = 0; +#ifdef _DEBUG + m_bSRGB = false; +#endif // _DEBUG + m_nSRGB = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR && m_bCONSTANTCOLOR && m_bHDRTYPE && m_bSRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nVERTEXCOLOR ) + ( 8 * m_nCONSTANTCOLOR ) + ( 16 * m_nHDRTYPE ) + ( 48 * m_nSRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_sprite_ps20 psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_CONSTANTCOLOR + psh_forgot_to_set_static_HDRTYPE + psh_forgot_to_set_static_SRGB + 0 +class sdk_sprite_ps20_Dynamic_Index +{ +private: + int m_nHDRENABLED; +#ifdef _DEBUG + bool m_bHDRENABLED; +#endif +public: + void SetHDRENABLED( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHDRENABLED = i; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } + void SetHDRENABLED( bool i ) + { + m_nHDRENABLED = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_sprite_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bHDRENABLED = false; +#endif // _DEBUG + m_nHDRENABLED = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bHDRENABLED && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nHDRENABLED ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_sprite_ps20 psh_forgot_to_set_dynamic_HDRENABLED + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20b.inc new file mode 100644 index 00000000..35d69c88 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_ps20b.inc @@ -0,0 +1,237 @@ +#include "shaderlib/cshader.h" +class sdk_sprite_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nCONSTANTCOLOR; +#ifdef _DEBUG + bool m_bCONSTANTCOLOR; +#endif +public: + void SetCONSTANTCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONSTANTCOLOR = i; +#ifdef _DEBUG + m_bCONSTANTCOLOR = true; +#endif + } + void SetCONSTANTCOLOR( bool i ) + { + m_nCONSTANTCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bCONSTANTCOLOR = true; +#endif + } +private: + int m_nHDRTYPE; +#ifdef _DEBUG + bool m_bHDRTYPE; +#endif +public: + void SetHDRTYPE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nHDRTYPE = i; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } + void SetHDRTYPE( bool i ) + { + m_nHDRTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRTYPE = true; +#endif + } +private: + int m_nSRGB; +#ifdef _DEBUG + bool m_bSRGB; +#endif +public: + void SetSRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSRGB = i; +#ifdef _DEBUG + m_bSRGB = true; +#endif + } + void SetSRGB( bool i ) + { + m_nSRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bSRGB = true; +#endif + } +private: + int m_nSRGB_OUTPUT_ADAPTER; +#ifdef _DEBUG + bool m_bSRGB_OUTPUT_ADAPTER; +#endif +public: + void SetSRGB_OUTPUT_ADAPTER( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSRGB_OUTPUT_ADAPTER = i; +#ifdef _DEBUG + m_bSRGB_OUTPUT_ADAPTER = true; +#endif + } + void SetSRGB_OUTPUT_ADAPTER( bool i ) + { + m_nSRGB_OUTPUT_ADAPTER = i ? 1 : 0; +#ifdef _DEBUG + m_bSRGB_OUTPUT_ADAPTER = true; +#endif + } +public: + sdk_sprite_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bCONSTANTCOLOR = false; +#endif // _DEBUG + m_nCONSTANTCOLOR = 0; +#ifdef _DEBUG + m_bHDRTYPE = false; +#endif // _DEBUG + m_nHDRTYPE = 0; +#ifdef _DEBUG + m_bSRGB = false; +#endif // _DEBUG + m_nSRGB = 0; +#ifdef _DEBUG + m_bSRGB_OUTPUT_ADAPTER = false; +#endif // _DEBUG + m_nSRGB_OUTPUT_ADAPTER = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bVERTEXCOLOR && m_bCONSTANTCOLOR && m_bHDRTYPE && m_bSRGB && m_bSRGB_OUTPUT_ADAPTER; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nVERTEXCOLOR ) + ( 16 * m_nCONSTANTCOLOR ) + ( 32 * m_nHDRTYPE ) + ( 96 * m_nSRGB ) + ( 192 * m_nSRGB_OUTPUT_ADAPTER ) + 0; + } +}; +#define shaderStaticTest_sdk_sprite_ps20b psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_CONSTANTCOLOR + psh_forgot_to_set_static_HDRTYPE + psh_forgot_to_set_static_SRGB + psh_forgot_to_set_static_SRGB_OUTPUT_ADAPTER + 0 +class sdk_sprite_ps20b_Dynamic_Index +{ +private: + int m_nHDRENABLED; +#ifdef _DEBUG + bool m_bHDRENABLED; +#endif +public: + void SetHDRENABLED( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHDRENABLED = i; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } + void SetHDRENABLED( bool i ) + { + m_nHDRENABLED = i ? 1 : 0; +#ifdef _DEBUG + m_bHDRENABLED = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_sprite_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bHDRENABLED = false; +#endif // _DEBUG + m_nHDRENABLED = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bHDRENABLED && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nHDRENABLED ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_sprite_ps20b psh_forgot_to_set_dynamic_HDRENABLED + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_vs20.inc new file mode 100644 index 00000000..25ac196d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_sprite_vs20.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_sprite_vs20_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nSRGB; +#ifdef _DEBUG + bool m_bSRGB; +#endif +public: + void SetSRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSRGB = i; +#ifdef _DEBUG + m_bSRGB = true; +#endif + } + void SetSRGB( bool i ) + { + m_nSRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bSRGB = true; +#endif + } +public: + sdk_sprite_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bSRGB = false; +#endif // _DEBUG + m_nSRGB = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR && m_bSRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nVERTEXCOLOR ) + ( 4 * m_nSRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_sprite_vs20 vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_SRGB + 0 +class sdk_sprite_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_sprite_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_sprite_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20.inc new file mode 100644 index 00000000..bd2b6155 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20.inc @@ -0,0 +1,110 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_bump_ps20_Static_Index +{ +public: + sdk_teeth_bump_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_teeth_bump_ps20 0 +class sdk_teeth_bump_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +public: + sdk_teeth_bump_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bNUM_LIGHTS && m_bAMBIENT_LIGHT; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nNUM_LIGHTS ) + ( 6 * m_nAMBIENT_LIGHT ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_bump_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_AMBIENT_LIGHT + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20b.inc new file mode 100644 index 00000000..8d774cf4 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps20b.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_bump_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_teeth_bump_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 40 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_bump_ps20b 0 +class sdk_teeth_bump_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_teeth_bump_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bNUM_LIGHTS && m_bAMBIENT_LIGHT && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nNUM_LIGHTS ) + ( 10 * m_nAMBIENT_LIGHT ) + ( 20 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_bump_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_AMBIENT_LIGHT + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps30.inc new file mode 100644 index 00000000..b3ae4c8f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_ps30.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_bump_ps30_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_teeth_bump_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 40 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_bump_ps30 0 +class sdk_teeth_bump_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_teeth_bump_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bNUM_LIGHTS && m_bAMBIENT_LIGHT && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nNUM_LIGHTS ) + ( 10 * m_nAMBIENT_LIGHT ) + ( 20 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_bump_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_AMBIENT_LIGHT + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs20.inc new file mode 100644 index 00000000..95700dc2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs20.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_bump_vs20_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +public: + sdk_teeth_bump_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO && m_bUSE_STATIC_CONTROL_FLOW; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 48 * m_nINTRO ) + ( 96 * m_nUSE_STATIC_CONTROL_FLOW ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_bump_vs20 vsh_forgot_to_set_static_INTRO + vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + 0 +class sdk_teeth_bump_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_teeth_bump_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bSTATIC_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nSTATIC_LIGHT ) + ( 16 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_bump_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs30.inc new file mode 100644 index 00000000..61d78dbe --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_bump_vs30.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_bump_vs30_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +public: + sdk_teeth_bump_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 32 * m_nINTRO ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_bump_vs30 vsh_forgot_to_set_static_INTRO + 0 +class sdk_teeth_bump_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_teeth_bump_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bSTATIC_LIGHT && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nSTATIC_LIGHT ) + ( 16 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_bump_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20.inc new file mode 100644 index 00000000..773cf54a --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_flashlight_ps20_Static_Index +{ +public: + sdk_teeth_flashlight_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_teeth_flashlight_ps20 0 +class sdk_teeth_flashlight_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_teeth_flashlight_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_flashlight_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20b.inc new file mode 100644 index 00000000..209eb19f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps20b.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_flashlight_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_teeth_flashlight_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_flashlight_ps20b psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_teeth_flashlight_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_teeth_flashlight_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_flashlight_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps30.inc new file mode 100644 index 00000000..3e08d8f2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_ps30.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_flashlight_ps30_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_teeth_flashlight_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + ( 8 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_flashlight_ps30 psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_teeth_flashlight_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_teeth_flashlight_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_flashlight_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs20.inc new file mode 100644 index 00000000..b3728827 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs20.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_flashlight_vs20_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +public: + sdk_teeth_flashlight_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 8 * m_nINTRO ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_flashlight_vs20 vsh_forgot_to_set_static_INTRO + 0 +class sdk_teeth_flashlight_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_teeth_flashlight_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_flashlight_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs30.inc new file mode 100644 index 00000000..e69d95bf --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_flashlight_vs30.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_flashlight_vs30_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +public: + sdk_teeth_flashlight_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 16 * m_nINTRO ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_flashlight_vs30 vsh_forgot_to_set_static_INTRO + 0 +class sdk_teeth_flashlight_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_teeth_flashlight_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_flashlight_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20.inc new file mode 100644 index 00000000..b7eae9b2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_ps20_Static_Index +{ +public: + sdk_teeth_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_teeth_ps20 0 +class sdk_teeth_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_teeth_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20b.inc new file mode 100644 index 00000000..1fcf1c6b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps20b.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_teeth_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_ps20b 0 +class sdk_teeth_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_teeth_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps30.inc new file mode 100644 index 00000000..d2cc52f3 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_ps30.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_ps30_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_teeth_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_ps30 0 +class sdk_teeth_ps30_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_teeth_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_ps30 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs20.inc new file mode 100644 index 00000000..18cc3990 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs20.inc @@ -0,0 +1,237 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_vs20_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +public: + sdk_teeth_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO && m_bUSE_STATIC_CONTROL_FLOW; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 96 * m_nINTRO ) + ( 192 * m_nUSE_STATIC_CONTROL_FLOW ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_vs20 vsh_forgot_to_set_static_INTRO + vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + 0 +class sdk_teeth_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_teeth_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs30.inc new file mode 100644 index 00000000..985f57e8 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_teeth_vs30.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_teeth_vs30_Static_Index +{ +private: + int m_nINTRO; +#ifdef _DEBUG + bool m_bINTRO; +#endif +public: + void SetINTRO( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nINTRO = i; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } + void SetINTRO( bool i ) + { + m_nINTRO = i ? 1 : 0; +#ifdef _DEBUG + m_bINTRO = true; +#endif + } +public: + sdk_teeth_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bINTRO = false; +#endif // _DEBUG + m_nINTRO = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bINTRO; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 64 * m_nINTRO ) + 0; + } +}; +#define shaderStaticTest_sdk_teeth_vs30 vsh_forgot_to_set_static_INTRO + 0 +class sdk_teeth_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT; +#endif +public: + void SetSTATIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } + void SetSTATIC_LIGHT( bool i ) + { + m_nSTATIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_teeth_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT = false; +#endif // _DEBUG + m_nSTATIC_LIGHT = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nDYNAMIC_LIGHT ) + ( 16 * m_nSTATIC_LIGHT ) + ( 32 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_teeth_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps11.inc new file mode 100644 index 00000000..30c5c529 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps11.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_unlitgeneric_ps11_Static_Index +{ +public: + sdk_unlitgeneric_ps11_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_unlitgeneric_ps11 0 +class sdk_unlitgeneric_ps11_Dynamic_Index +{ +public: + sdk_unlitgeneric_ps11_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_unlitgeneric_ps11 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20.inc new file mode 100644 index 00000000..96088dec --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_unlitgeneric_ps20_Static_Index +{ +public: + sdk_unlitgeneric_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_unlitgeneric_ps20 0 +class sdk_unlitgeneric_ps20_Dynamic_Index +{ +public: + sdk_unlitgeneric_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_unlitgeneric_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20b.inc new file mode 100644 index 00000000..5f0347a2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_ps20b.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class sdk_unlitgeneric_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +public: + sdk_unlitgeneric_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCONVERT_TO_SRGB ) + 0; + } +}; +#define shaderStaticTest_sdk_unlitgeneric_ps20b 0 +class sdk_unlitgeneric_ps20b_Dynamic_Index +{ +public: + sdk_unlitgeneric_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_unlitgeneric_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_vs20.inc new file mode 100644 index 00000000..ea58edbc --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlitgeneric_vs20.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_unlitgeneric_vs20_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +public: + sdk_unlitgeneric_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 8 * m_nVERTEXCOLOR ) + 0; + } +}; +#define shaderStaticTest_sdk_unlitgeneric_vs20 vsh_forgot_to_set_static_VERTEXCOLOR + 0 +class sdk_unlitgeneric_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_unlitgeneric_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_unlitgeneric_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20.inc new file mode 100644 index 00000000..47ecb668 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_unlittwotexture_ps20_Static_Index +{ +private: + int m_nTRANSLUCENT; +#ifdef _DEBUG + bool m_bTRANSLUCENT; +#endif +public: + void SetTRANSLUCENT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTRANSLUCENT = i; +#ifdef _DEBUG + m_bTRANSLUCENT = true; +#endif + } + void SetTRANSLUCENT( bool i ) + { + m_nTRANSLUCENT = i ? 1 : 0; +#ifdef _DEBUG + m_bTRANSLUCENT = true; +#endif + } +public: + sdk_unlittwotexture_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bTRANSLUCENT = false; +#endif // _DEBUG + m_nTRANSLUCENT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bTRANSLUCENT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nTRANSLUCENT ) + 0; + } +}; +#define shaderStaticTest_sdk_unlittwotexture_ps20 psh_forgot_to_set_static_TRANSLUCENT + 0 +class sdk_unlittwotexture_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_unlittwotexture_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_unlittwotexture_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20b.inc new file mode 100644 index 00000000..52b755c8 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_ps20b.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_unlittwotexture_ps20b_Static_Index +{ +private: + int m_nTRANSLUCENT; +#ifdef _DEBUG + bool m_bTRANSLUCENT; +#endif +public: + void SetTRANSLUCENT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nTRANSLUCENT = i; +#ifdef _DEBUG + m_bTRANSLUCENT = true; +#endif + } + void SetTRANSLUCENT( bool i ) + { + m_nTRANSLUCENT = i ? 1 : 0; +#ifdef _DEBUG + m_bTRANSLUCENT = true; +#endif + } +public: + sdk_unlittwotexture_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bTRANSLUCENT = false; +#endif // _DEBUG + m_nTRANSLUCENT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bTRANSLUCENT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nTRANSLUCENT ) + 0; + } +}; +#define shaderStaticTest_sdk_unlittwotexture_ps20b psh_forgot_to_set_static_TRANSLUCENT + 0 +class sdk_unlittwotexture_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +public: + sdk_unlittwotexture_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITE_DEPTH_TO_DESTALPHA ) + 0; + } +}; +#define shaderDynamicTest_sdk_unlittwotexture_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_vs20.inc new file mode 100644 index 00000000..a9757e65 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_unlittwotexture_vs20.inc @@ -0,0 +1,110 @@ +#include "shaderlib/cshader.h" +class sdk_unlittwotexture_vs20_Static_Index +{ +public: + sdk_unlittwotexture_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_unlittwotexture_vs20 0 +class sdk_unlittwotexture_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +public: + sdk_unlittwotexture_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + 0; + } +}; +#define shaderDynamicTest_sdk_unlittwotexture_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20.inc new file mode 100644 index 00000000..b6cbcf56 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20.inc @@ -0,0 +1,437 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_bump_ps20_Static_Index +{ +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nSELFILLUMFRESNEL; +#ifdef _DEBUG + bool m_bSELFILLUMFRESNEL; +#endif +public: + void SetSELFILLUMFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUMFRESNEL = i; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } + void SetSELFILLUMFRESNEL( bool i ) + { + m_nSELFILLUMFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } +private: + int m_nNORMALMAPALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bNORMALMAPALPHAENVMAPMASK; +#endif +public: + void SetNORMALMAPALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAPALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } + void SetNORMALMAPALPHAENVMAPMASK( bool i ) + { + m_nNORMALMAPALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 6 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = false; +#endif // _DEBUG + m_nSELFILLUMFRESNEL = 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nNORMALMAPALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCUBEMAP && m_bDIFFUSELIGHTING && m_bLIGHTWARPTEXTURE && m_bSELFILLUM && m_bSELFILLUMFRESNEL && m_bNORMALMAPALPHAENVMAPMASK && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bBLENDTINTBYBASEALPHA && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 24 * m_nCUBEMAP ) + ( 48 * m_nDIFFUSELIGHTING ) + ( 96 * m_nLIGHTWARPTEXTURE ) + ( 192 * m_nSELFILLUM ) + ( 384 * m_nSELFILLUMFRESNEL ) + ( 768 * m_nNORMALMAPALPHAENVMAPMASK ) + ( 1536 * m_nHALFLAMBERT ) + ( 3072 * m_nFLASHLIGHT ) + ( 6144 * m_nDETAILTEXTURE ) + ( 12288 * m_nDETAIL_BLEND_MODE ) + ( 86016 * m_nBLENDTINTBYBASEALPHA ) + ( 172032 * m_nENVMAPMASK ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_bump_ps20 psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_SELFILLUMFRESNEL + psh_forgot_to_set_static_NORMALMAPALPHAENVMAPMASK + psh_forgot_to_set_static_HALFLAMBERT + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_vertexlit_and_unlit_generic_bump_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bWRITEWATERFOGTODESTALPHA && m_bNUM_LIGHTS && m_bAMBIENT_LIGHT; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nWRITEWATERFOGTODESTALPHA ) + ( 4 * m_nNUM_LIGHTS ) + ( 12 * m_nAMBIENT_LIGHT ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_bump_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_AMBIENT_LIGHT + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20b.inc new file mode 100644 index 00000000..7c81e32f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps20b.inc @@ -0,0 +1,437 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_bump_ps20b_Static_Index +{ +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nSELFILLUMFRESNEL; +#ifdef _DEBUG + bool m_bSELFILLUMFRESNEL; +#endif +public: + void SetSELFILLUMFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUMFRESNEL = i; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } + void SetSELFILLUMFRESNEL( bool i ) + { + m_nSELFILLUMFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } +private: + int m_nNORMALMAPALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bNORMALMAPALPHAENVMAPMASK; +#endif +public: + void SetNORMALMAPALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAPALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } + void SetNORMALMAPALPHAENVMAPMASK( bool i ) + { + m_nNORMALMAPALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 6 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = false; +#endif // _DEBUG + m_nSELFILLUMFRESNEL = 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nNORMALMAPALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCUBEMAP && m_bDIFFUSELIGHTING && m_bLIGHTWARPTEXTURE && m_bSELFILLUM && m_bSELFILLUMFRESNEL && m_bNORMALMAPALPHAENVMAPMASK && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bFLASHLIGHTDEPTHFILTERMODE && m_bBLENDTINTBYBASEALPHA && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 20 * m_nCUBEMAP ) + ( 40 * m_nDIFFUSELIGHTING ) + ( 80 * m_nLIGHTWARPTEXTURE ) + ( 160 * m_nSELFILLUM ) + ( 320 * m_nSELFILLUMFRESNEL ) + ( 640 * m_nNORMALMAPALPHAENVMAPMASK ) + ( 1280 * m_nHALFLAMBERT ) + ( 2560 * m_nFLASHLIGHT ) + ( 5120 * m_nDETAILTEXTURE ) + ( 10240 * m_nDETAIL_BLEND_MODE ) + ( 71680 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 215040 * m_nBLENDTINTBYBASEALPHA ) + ( 430080 * m_nENVMAPMASK ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_bump_ps20b psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_SELFILLUMFRESNEL + psh_forgot_to_set_static_NORMALMAPALPHAENVMAPMASK + psh_forgot_to_set_static_HALFLAMBERT + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_vertexlit_and_unlit_generic_bump_ps20b_Dynamic_Index +{ +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bNUM_LIGHTS && m_bAMBIENT_LIGHT && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nNUM_LIGHTS ) + ( 5 * m_nAMBIENT_LIGHT ) + ( 10 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_bump_ps20b psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_AMBIENT_LIGHT + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps30.inc new file mode 100644 index 00000000..1cb70971 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_ps30.inc @@ -0,0 +1,437 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_bump_ps30_Static_Index +{ +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nLIGHTWARPTEXTURE; +#ifdef _DEBUG + bool m_bLIGHTWARPTEXTURE; +#endif +public: + void SetLIGHTWARPTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTWARPTEXTURE = i; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } + void SetLIGHTWARPTEXTURE( bool i ) + { + m_nLIGHTWARPTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nSELFILLUMFRESNEL; +#ifdef _DEBUG + bool m_bSELFILLUMFRESNEL; +#endif +public: + void SetSELFILLUMFRESNEL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUMFRESNEL = i; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } + void SetSELFILLUMFRESNEL( bool i ) + { + m_nSELFILLUMFRESNEL = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = true; +#endif + } +private: + int m_nNORMALMAPALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bNORMALMAPALPHAENVMAPMASK; +#endif +public: + void SetNORMALMAPALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nNORMALMAPALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } + void SetNORMALMAPALPHAENVMAPMASK( bool i ) + { + m_nNORMALMAPALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 6 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bLIGHTWARPTEXTURE = false; +#endif // _DEBUG + m_nLIGHTWARPTEXTURE = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bSELFILLUMFRESNEL = false; +#endif // _DEBUG + m_nSELFILLUMFRESNEL = 0; +#ifdef _DEBUG + m_bNORMALMAPALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nNORMALMAPALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCUBEMAP && m_bDIFFUSELIGHTING && m_bLIGHTWARPTEXTURE && m_bSELFILLUM && m_bSELFILLUMFRESNEL && m_bNORMALMAPALPHAENVMAPMASK && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bDETAILTEXTURE && m_bDETAIL_BLEND_MODE && m_bFLASHLIGHTDEPTHFILTERMODE && m_bBLENDTINTBYBASEALPHA && m_bENVMAPMASK; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 20 * m_nCUBEMAP ) + ( 40 * m_nDIFFUSELIGHTING ) + ( 80 * m_nLIGHTWARPTEXTURE ) + ( 160 * m_nSELFILLUM ) + ( 320 * m_nSELFILLUMFRESNEL ) + ( 640 * m_nNORMALMAPALPHAENVMAPMASK ) + ( 1280 * m_nHALFLAMBERT ) + ( 2560 * m_nFLASHLIGHT ) + ( 5120 * m_nDETAILTEXTURE ) + ( 10240 * m_nDETAIL_BLEND_MODE ) + ( 71680 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 215040 * m_nBLENDTINTBYBASEALPHA ) + ( 430080 * m_nENVMAPMASK ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_bump_ps30 psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_LIGHTWARPTEXTURE + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_SELFILLUMFRESNEL + psh_forgot_to_set_static_NORMALMAPALPHAENVMAPMASK + psh_forgot_to_set_static_HALFLAMBERT + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_ENVMAPMASK + 0 +class sdk_vertexlit_and_unlit_generic_bump_ps30_Dynamic_Index +{ +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bNUM_LIGHTS && m_bAMBIENT_LIGHT && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nNUM_LIGHTS ) + ( 5 * m_nAMBIENT_LIGHT ) + ( 10 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_bump_ps30 psh_forgot_to_set_dynamic_NUM_LIGHTS + psh_forgot_to_set_dynamic_AMBIENT_LIGHT + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs20.inc new file mode 100644 index 00000000..7c9fe088 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs20.inc @@ -0,0 +1,212 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_bump_vs20_Static_Index +{ +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nUSE_WITH_2B; +#ifdef _DEBUG + bool m_bUSE_WITH_2B; +#endif +public: + void SetUSE_WITH_2B( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_WITH_2B = i; +#ifdef _DEBUG + m_bUSE_WITH_2B = true; +#endif + } + void SetUSE_WITH_2B( bool i ) + { + m_nUSE_WITH_2B = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_WITH_2B = true; +#endif + } +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bUSE_WITH_2B = false; +#endif // _DEBUG + m_nUSE_WITH_2B = 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bHALFLAMBERT && m_bUSE_WITH_2B && m_bUSE_STATIC_CONTROL_FLOW; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 24 * m_nHALFLAMBERT ) + ( 48 * m_nUSE_WITH_2B ) + ( 96 * m_nUSE_STATIC_CONTROL_FLOW ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_bump_vs20 vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_USE_WITH_2B + vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + 0 +class sdk_vertexlit_and_unlit_generic_bump_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_bump_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs30.inc new file mode 100644 index 00000000..06841a47 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_bump_vs30.inc @@ -0,0 +1,237 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_bump_vs30_Static_Index +{ +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nUSE_WITH_2B; +#ifdef _DEBUG + bool m_bUSE_WITH_2B; +#endif +public: + void SetUSE_WITH_2B( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_WITH_2B = i; +#ifdef _DEBUG + m_bUSE_WITH_2B = true; +#endif + } + void SetUSE_WITH_2B( bool i ) + { + m_nUSE_WITH_2B = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_WITH_2B = true; +#endif + } +private: + int m_nDECAL; +#ifdef _DEBUG + bool m_bDECAL; +#endif +public: + void SetDECAL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDECAL = i; +#ifdef _DEBUG + m_bDECAL = true; +#endif + } + void SetDECAL( bool i ) + { + m_nDECAL = i ? 1 : 0; +#ifdef _DEBUG + m_bDECAL = true; +#endif + } +private: + int m_nSM30_VERTEXID; +#ifdef _DEBUG + bool m_bSM30_VERTEXID; +#endif +public: + void SetSM30_VERTEXID( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSM30_VERTEXID = i; +#ifdef _DEBUG + m_bSM30_VERTEXID = true; +#endif + } + void SetSM30_VERTEXID( bool i ) + { + m_nSM30_VERTEXID = i ? 1 : 0; +#ifdef _DEBUG + m_bSM30_VERTEXID = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bUSE_WITH_2B = false; +#endif // _DEBUG + m_nUSE_WITH_2B = 0; +#ifdef _DEBUG + m_bDECAL = false; +#endif // _DEBUG + m_nDECAL = 0; +#ifdef _DEBUG + m_bSM30_VERTEXID = false; +#endif // _DEBUG + m_nSM30_VERTEXID = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bHALFLAMBERT && m_bUSE_WITH_2B && m_bDECAL && m_bSM30_VERTEXID; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 16 * m_nHALFLAMBERT ) + ( 32 * m_nUSE_WITH_2B ) + ( 64 * m_nDECAL ) + ( 128 * m_nSM30_VERTEXID ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_bump_vs30 vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_USE_WITH_2B + vsh_forgot_to_set_static_DECAL + vsh_forgot_to_set_static_SM30_VERTEXID + 0 +class sdk_vertexlit_and_unlit_generic_bump_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_bump_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDOWATERFOG && m_bSKINNING && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDOWATERFOG ) + ( 4 * m_nSKINNING ) + ( 8 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_bump_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20.inc new file mode 100644 index 00000000..49caddbb --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20.inc @@ -0,0 +1,587 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_ps20_Static_Index +{ +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSELFILLUM_ENVMAPMASK_ALPHA; +#ifdef _DEBUG + bool m_bSELFILLUM_ENVMAPMASK_ALPHA; +#endif +public: + void SetSELFILLUM_ENVMAPMASK_ALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM_ENVMAPMASK_ALPHA = i; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = true; +#endif + } + void SetSELFILLUM_ENVMAPMASK_ALPHA( bool i ) + { + m_nSELFILLUM_ENVMAPMASK_ALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 9 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nSEAMLESS_BASE; +#ifdef _DEBUG + bool m_bSEAMLESS_BASE; +#endif +public: + void SetSEAMLESS_BASE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_BASE = i; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } + void SetSEAMLESS_BASE( bool i ) + { + m_nSEAMLESS_BASE = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } +private: + int m_nSEAMLESS_DETAIL; +#ifdef _DEBUG + bool m_bSEAMLESS_DETAIL; +#endif +public: + void SetSEAMLESS_DETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_DETAIL = i; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } + void SetSEAMLESS_DETAIL( bool i ) + { + m_nSEAMLESS_DETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } +private: + int m_nDISTANCEALPHA; +#ifdef _DEBUG + bool m_bDISTANCEALPHA; +#endif +public: + void SetDISTANCEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDISTANCEALPHA = i; +#ifdef _DEBUG + m_bDISTANCEALPHA = true; +#endif + } + void SetDISTANCEALPHA( bool i ) + { + m_nDISTANCEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bDISTANCEALPHA = true; +#endif + } +private: + int m_nDISTANCEALPHAFROMDETAIL; +#ifdef _DEBUG + bool m_bDISTANCEALPHAFROMDETAIL; +#endif +public: + void SetDISTANCEALPHAFROMDETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDISTANCEALPHAFROMDETAIL = i; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = true; +#endif + } + void SetDISTANCEALPHAFROMDETAIL( bool i ) + { + m_nDISTANCEALPHAFROMDETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = true; +#endif + } +private: + int m_nSOFT_MASK; +#ifdef _DEBUG + bool m_bSOFT_MASK; +#endif +public: + void SetSOFT_MASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSOFT_MASK = i; +#ifdef _DEBUG + m_bSOFT_MASK = true; +#endif + } + void SetSOFT_MASK( bool i ) + { + m_nSOFT_MASK = i ? 1 : 0; +#ifdef _DEBUG + m_bSOFT_MASK = true; +#endif + } +private: + int m_nOUTLINE; +#ifdef _DEBUG + bool m_bOUTLINE; +#endif +public: + void SetOUTLINE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nOUTLINE = i; +#ifdef _DEBUG + m_bOUTLINE = true; +#endif + } + void SetOUTLINE( bool i ) + { + m_nOUTLINE = i ? 1 : 0; +#ifdef _DEBUG + m_bOUTLINE = true; +#endif + } +private: + int m_nOUTER_GLOW; +#ifdef _DEBUG + bool m_bOUTER_GLOW; +#endif +public: + void SetOUTER_GLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nOUTER_GLOW = i; +#ifdef _DEBUG + m_bOUTER_GLOW = true; +#endif + } + void SetOUTER_GLOW( bool i ) + { + m_nOUTER_GLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bOUTER_GLOW = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nCUBEMAP_SPHERE_LEGACY; +#ifdef _DEBUG + bool m_bCUBEMAP_SPHERE_LEGACY; +#endif +public: + void SetCUBEMAP_SPHERE_LEGACY( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP_SPHERE_LEGACY = i; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = true; +#endif + } + void SetCUBEMAP_SPHERE_LEGACY( bool i ) + { + m_nCUBEMAP_SPHERE_LEGACY = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = false; +#endif // _DEBUG + m_nSELFILLUM_ENVMAPMASK_ALPHA = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = false; +#endif // _DEBUG + m_nSEAMLESS_BASE = 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = false; +#endif // _DEBUG + m_nSEAMLESS_DETAIL = 0; +#ifdef _DEBUG + m_bDISTANCEALPHA = false; +#endif // _DEBUG + m_nDISTANCEALPHA = 0; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = false; +#endif // _DEBUG + m_nDISTANCEALPHAFROMDETAIL = 0; +#ifdef _DEBUG + m_bSOFT_MASK = false; +#endif // _DEBUG + m_nSOFT_MASK = 0; +#ifdef _DEBUG + m_bOUTLINE = false; +#endif // _DEBUG + m_nOUTLINE = 0; +#ifdef _DEBUG + m_bOUTER_GLOW = false; +#endif // _DEBUG + m_nOUTER_GLOW = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = false; +#endif // _DEBUG + m_nCUBEMAP_SPHERE_LEGACY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bDETAILTEXTURE && m_bCUBEMAP && m_bDIFFUSELIGHTING && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bSELFILLUM && m_bVERTEXCOLOR && m_bFLASHLIGHT && m_bSELFILLUM_ENVMAPMASK_ALPHA && m_bDETAIL_BLEND_MODE && m_bSEAMLESS_BASE && m_bSEAMLESS_DETAIL && m_bDISTANCEALPHA && m_bDISTANCEALPHAFROMDETAIL && m_bSOFT_MASK && m_bOUTLINE && m_bOUTER_GLOW && m_bBLENDTINTBYBASEALPHA && m_bCUBEMAP_SPHERE_LEGACY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 6 * m_nDETAILTEXTURE ) + ( 12 * m_nCUBEMAP ) + ( 24 * m_nDIFFUSELIGHTING ) + ( 48 * m_nENVMAPMASK ) + ( 96 * m_nBASEALPHAENVMAPMASK ) + ( 192 * m_nSELFILLUM ) + ( 384 * m_nVERTEXCOLOR ) + ( 768 * m_nFLASHLIGHT ) + ( 1536 * m_nSELFILLUM_ENVMAPMASK_ALPHA ) + ( 3072 * m_nDETAIL_BLEND_MODE ) + ( 30720 * m_nSEAMLESS_BASE ) + ( 61440 * m_nSEAMLESS_DETAIL ) + ( 122880 * m_nDISTANCEALPHA ) + ( 245760 * m_nDISTANCEALPHAFROMDETAIL ) + ( 491520 * m_nSOFT_MASK ) + ( 983040 * m_nOUTLINE ) + ( 1966080 * m_nOUTER_GLOW ) + ( 3932160 * m_nBLENDTINTBYBASEALPHA ) + ( 7864320 * m_nCUBEMAP_SPHERE_LEGACY ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_ps20 psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_SELFILLUM_ENVMAPMASK_ALPHA + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_SEAMLESS_BASE + psh_forgot_to_set_static_SEAMLESS_DETAIL + psh_forgot_to_set_static_DISTANCEALPHA + psh_forgot_to_set_static_DISTANCEALPHAFROMDETAIL + psh_forgot_to_set_static_SOFT_MASK + psh_forgot_to_set_static_OUTLINE + psh_forgot_to_set_static_OUTER_GLOW + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_CUBEMAP_SPHERE_LEGACY + 0 +class sdk_vertexlit_and_unlit_generic_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nSTATIC_LIGHT_LIGHTMAP; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_LIGHTMAP; +#endif +public: + void SetSTATIC_LIGHT_LIGHTMAP( int i ) + { + Assert( i >= 0 && i <= 0 ); + m_nSTATIC_LIGHT_LIGHTMAP = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } + void SetSTATIC_LIGHT_LIGHTMAP( bool i ) + { + m_nSTATIC_LIGHT_LIGHTMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_LIGHTMAP = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE && m_bLIGHTING_PREVIEW && m_bSTATIC_LIGHT_LIGHTMAP; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + ( 2 * m_nLIGHTING_PREVIEW ) + ( 6 * m_nSTATIC_LIGHT_LIGHTMAP ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_LIGHTING_PREVIEW + psh_forgot_to_set_dynamic_STATIC_LIGHT_LIGHTMAP + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20b.inc new file mode 100644 index 00000000..f9e6eef0 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps20b.inc @@ -0,0 +1,687 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_ps20b_Static_Index +{ +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSELFILLUM_ENVMAPMASK_ALPHA; +#ifdef _DEBUG + bool m_bSELFILLUM_ENVMAPMASK_ALPHA; +#endif +public: + void SetSELFILLUM_ENVMAPMASK_ALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM_ENVMAPMASK_ALPHA = i; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = true; +#endif + } + void SetSELFILLUM_ENVMAPMASK_ALPHA( bool i ) + { + m_nSELFILLUM_ENVMAPMASK_ALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 9 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nSEAMLESS_BASE; +#ifdef _DEBUG + bool m_bSEAMLESS_BASE; +#endif +public: + void SetSEAMLESS_BASE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_BASE = i; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } + void SetSEAMLESS_BASE( bool i ) + { + m_nSEAMLESS_BASE = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } +private: + int m_nSEAMLESS_DETAIL; +#ifdef _DEBUG + bool m_bSEAMLESS_DETAIL; +#endif +public: + void SetSEAMLESS_DETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_DETAIL = i; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } + void SetSEAMLESS_DETAIL( bool i ) + { + m_nSEAMLESS_DETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } +private: + int m_nDISTANCEALPHA; +#ifdef _DEBUG + bool m_bDISTANCEALPHA; +#endif +public: + void SetDISTANCEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDISTANCEALPHA = i; +#ifdef _DEBUG + m_bDISTANCEALPHA = true; +#endif + } + void SetDISTANCEALPHA( bool i ) + { + m_nDISTANCEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bDISTANCEALPHA = true; +#endif + } +private: + int m_nDISTANCEALPHAFROMDETAIL; +#ifdef _DEBUG + bool m_bDISTANCEALPHAFROMDETAIL; +#endif +public: + void SetDISTANCEALPHAFROMDETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDISTANCEALPHAFROMDETAIL = i; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = true; +#endif + } + void SetDISTANCEALPHAFROMDETAIL( bool i ) + { + m_nDISTANCEALPHAFROMDETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = true; +#endif + } +private: + int m_nSOFT_MASK; +#ifdef _DEBUG + bool m_bSOFT_MASK; +#endif +public: + void SetSOFT_MASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSOFT_MASK = i; +#ifdef _DEBUG + m_bSOFT_MASK = true; +#endif + } + void SetSOFT_MASK( bool i ) + { + m_nSOFT_MASK = i ? 1 : 0; +#ifdef _DEBUG + m_bSOFT_MASK = true; +#endif + } +private: + int m_nOUTLINE; +#ifdef _DEBUG + bool m_bOUTLINE; +#endif +public: + void SetOUTLINE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nOUTLINE = i; +#ifdef _DEBUG + m_bOUTLINE = true; +#endif + } + void SetOUTLINE( bool i ) + { + m_nOUTLINE = i ? 1 : 0; +#ifdef _DEBUG + m_bOUTLINE = true; +#endif + } +private: + int m_nOUTER_GLOW; +#ifdef _DEBUG + bool m_bOUTER_GLOW; +#endif +public: + void SetOUTER_GLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nOUTER_GLOW = i; +#ifdef _DEBUG + m_bOUTER_GLOW = true; +#endif + } + void SetOUTER_GLOW( bool i ) + { + m_nOUTER_GLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bOUTER_GLOW = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nDEPTHBLEND; +#ifdef _DEBUG + bool m_bDEPTHBLEND; +#endif +public: + void SetDEPTHBLEND( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDEPTHBLEND = i; +#ifdef _DEBUG + m_bDEPTHBLEND = true; +#endif + } + void SetDEPTHBLEND( bool i ) + { + m_nDEPTHBLEND = i ? 1 : 0; +#ifdef _DEBUG + m_bDEPTHBLEND = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nSRGB_INPUT_ADAPTER; +#ifdef _DEBUG + bool m_bSRGB_INPUT_ADAPTER; +#endif +public: + void SetSRGB_INPUT_ADAPTER( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSRGB_INPUT_ADAPTER = i; +#ifdef _DEBUG + m_bSRGB_INPUT_ADAPTER = true; +#endif + } + void SetSRGB_INPUT_ADAPTER( bool i ) + { + m_nSRGB_INPUT_ADAPTER = i ? 1 : 0; +#ifdef _DEBUG + m_bSRGB_INPUT_ADAPTER = true; +#endif + } +private: + int m_nCUBEMAP_SPHERE_LEGACY; +#ifdef _DEBUG + bool m_bCUBEMAP_SPHERE_LEGACY; +#endif +public: + void SetCUBEMAP_SPHERE_LEGACY( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP_SPHERE_LEGACY = i; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = true; +#endif + } + void SetCUBEMAP_SPHERE_LEGACY( bool i ) + { + m_nCUBEMAP_SPHERE_LEGACY = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = false; +#endif // _DEBUG + m_nSELFILLUM_ENVMAPMASK_ALPHA = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = false; +#endif // _DEBUG + m_nSEAMLESS_BASE = 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = false; +#endif // _DEBUG + m_nSEAMLESS_DETAIL = 0; +#ifdef _DEBUG + m_bDISTANCEALPHA = false; +#endif // _DEBUG + m_nDISTANCEALPHA = 0; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = false; +#endif // _DEBUG + m_nDISTANCEALPHAFROMDETAIL = 0; +#ifdef _DEBUG + m_bSOFT_MASK = false; +#endif // _DEBUG + m_nSOFT_MASK = 0; +#ifdef _DEBUG + m_bOUTLINE = false; +#endif // _DEBUG + m_nOUTLINE = 0; +#ifdef _DEBUG + m_bOUTER_GLOW = false; +#endif // _DEBUG + m_nOUTER_GLOW = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bDEPTHBLEND = false; +#endif // _DEBUG + m_nDEPTHBLEND = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bSRGB_INPUT_ADAPTER = false; +#endif // _DEBUG + m_nSRGB_INPUT_ADAPTER = 0; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = false; +#endif // _DEBUG + m_nCUBEMAP_SPHERE_LEGACY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bDETAILTEXTURE && m_bCUBEMAP && m_bDIFFUSELIGHTING && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bSELFILLUM && m_bVERTEXCOLOR && m_bFLASHLIGHT && m_bSELFILLUM_ENVMAPMASK_ALPHA && m_bDETAIL_BLEND_MODE && m_bSEAMLESS_BASE && m_bSEAMLESS_DETAIL && m_bDISTANCEALPHA && m_bDISTANCEALPHAFROMDETAIL && m_bSOFT_MASK && m_bOUTLINE && m_bOUTER_GLOW && m_bFLASHLIGHTDEPTHFILTERMODE && m_bDEPTHBLEND && m_bBLENDTINTBYBASEALPHA && m_bSRGB_INPUT_ADAPTER && m_bCUBEMAP_SPHERE_LEGACY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 24 * m_nDETAILTEXTURE ) + ( 48 * m_nCUBEMAP ) + ( 96 * m_nDIFFUSELIGHTING ) + ( 192 * m_nENVMAPMASK ) + ( 384 * m_nBASEALPHAENVMAPMASK ) + ( 768 * m_nSELFILLUM ) + ( 1536 * m_nVERTEXCOLOR ) + ( 3072 * m_nFLASHLIGHT ) + ( 6144 * m_nSELFILLUM_ENVMAPMASK_ALPHA ) + ( 12288 * m_nDETAIL_BLEND_MODE ) + ( 122880 * m_nSEAMLESS_BASE ) + ( 245760 * m_nSEAMLESS_DETAIL ) + ( 491520 * m_nDISTANCEALPHA ) + ( 983040 * m_nDISTANCEALPHAFROMDETAIL ) + ( 1966080 * m_nSOFT_MASK ) + ( 3932160 * m_nOUTLINE ) + ( 7864320 * m_nOUTER_GLOW ) + ( 15728640 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 47185920 * m_nDEPTHBLEND ) + ( 94371840 * m_nBLENDTINTBYBASEALPHA ) + ( 188743680 * m_nSRGB_INPUT_ADAPTER ) + ( 377487360 * m_nCUBEMAP_SPHERE_LEGACY ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_ps20b psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_SELFILLUM_ENVMAPMASK_ALPHA + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_SEAMLESS_BASE + psh_forgot_to_set_static_SEAMLESS_DETAIL + psh_forgot_to_set_static_DISTANCEALPHA + psh_forgot_to_set_static_DISTANCEALPHAFROMDETAIL + psh_forgot_to_set_static_SOFT_MASK + psh_forgot_to_set_static_OUTLINE + psh_forgot_to_set_static_OUTER_GLOW + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_DEPTHBLEND + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_SRGB_INPUT_ADAPTER + psh_forgot_to_set_static_CUBEMAP_SPHERE_LEGACY + 0 +class sdk_vertexlit_and_unlit_generic_ps20b_Dynamic_Index +{ +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +private: + int m_nSTATIC_LIGHT_LIGHTMAP; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_LIGHTMAP; +#endif +public: + void SetSTATIC_LIGHT_LIGHTMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT_LIGHTMAP = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } + void SetSTATIC_LIGHT_LIGHTMAP( bool i ) + { + m_nSTATIC_LIGHT_LIGHTMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } +private: + int m_nDEBUG_LUXELS; +#ifdef _DEBUG + bool m_bDEBUG_LUXELS; +#endif +public: + void SetDEBUG_LUXELS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDEBUG_LUXELS = i; +#ifdef _DEBUG + m_bDEBUG_LUXELS = true; +#endif + } + void SetDEBUG_LUXELS( bool i ) + { + m_nDEBUG_LUXELS = i ? 1 : 0; +#ifdef _DEBUG + m_bDEBUG_LUXELS = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_LIGHTMAP = 0; +#ifdef _DEBUG + m_bDEBUG_LUXELS = false; +#endif // _DEBUG + m_nDEBUG_LUXELS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bLIGHTING_PREVIEW && m_bFLASHLIGHTSHADOWS && m_bSTATIC_LIGHT_LIGHTMAP && m_bDEBUG_LUXELS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nLIGHTING_PREVIEW ) + ( 3 * m_nFLASHLIGHTSHADOWS ) + ( 6 * m_nSTATIC_LIGHT_LIGHTMAP ) + ( 12 * m_nDEBUG_LUXELS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_ps20b psh_forgot_to_set_dynamic_LIGHTING_PREVIEW + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + psh_forgot_to_set_dynamic_STATIC_LIGHT_LIGHTMAP + psh_forgot_to_set_dynamic_DEBUG_LUXELS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps30.inc new file mode 100644 index 00000000..2414d77f --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_ps30.inc @@ -0,0 +1,662 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_ps30_Static_Index +{ +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nENVMAPMASK; +#ifdef _DEBUG + bool m_bENVMAPMASK; +#endif +public: + void SetENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nENVMAPMASK = i; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } + void SetENVMAPMASK( bool i ) + { + m_nENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bENVMAPMASK = true; +#endif + } +private: + int m_nBASEALPHAENVMAPMASK; +#ifdef _DEBUG + bool m_bBASEALPHAENVMAPMASK; +#endif +public: + void SetBASEALPHAENVMAPMASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBASEALPHAENVMAPMASK = i; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } + void SetBASEALPHAENVMAPMASK( bool i ) + { + m_nBASEALPHAENVMAPMASK = i ? 1 : 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSELFILLUM_ENVMAPMASK_ALPHA; +#ifdef _DEBUG + bool m_bSELFILLUM_ENVMAPMASK_ALPHA; +#endif +public: + void SetSELFILLUM_ENVMAPMASK_ALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM_ENVMAPMASK_ALPHA = i; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = true; +#endif + } + void SetSELFILLUM_ENVMAPMASK_ALPHA( bool i ) + { + m_nSELFILLUM_ENVMAPMASK_ALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = true; +#endif + } +private: + int m_nDETAIL_BLEND_MODE; +#ifdef _DEBUG + bool m_bDETAIL_BLEND_MODE; +#endif +public: + void SetDETAIL_BLEND_MODE( int i ) + { + Assert( i >= 0 && i <= 9 ); + m_nDETAIL_BLEND_MODE = i; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } + void SetDETAIL_BLEND_MODE( bool i ) + { + m_nDETAIL_BLEND_MODE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = true; +#endif + } +private: + int m_nSEAMLESS_BASE; +#ifdef _DEBUG + bool m_bSEAMLESS_BASE; +#endif +public: + void SetSEAMLESS_BASE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_BASE = i; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } + void SetSEAMLESS_BASE( bool i ) + { + m_nSEAMLESS_BASE = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } +private: + int m_nSEAMLESS_DETAIL; +#ifdef _DEBUG + bool m_bSEAMLESS_DETAIL; +#endif +public: + void SetSEAMLESS_DETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_DETAIL = i; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } + void SetSEAMLESS_DETAIL( bool i ) + { + m_nSEAMLESS_DETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } +private: + int m_nDISTANCEALPHA; +#ifdef _DEBUG + bool m_bDISTANCEALPHA; +#endif +public: + void SetDISTANCEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDISTANCEALPHA = i; +#ifdef _DEBUG + m_bDISTANCEALPHA = true; +#endif + } + void SetDISTANCEALPHA( bool i ) + { + m_nDISTANCEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bDISTANCEALPHA = true; +#endif + } +private: + int m_nDISTANCEALPHAFROMDETAIL; +#ifdef _DEBUG + bool m_bDISTANCEALPHAFROMDETAIL; +#endif +public: + void SetDISTANCEALPHAFROMDETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDISTANCEALPHAFROMDETAIL = i; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = true; +#endif + } + void SetDISTANCEALPHAFROMDETAIL( bool i ) + { + m_nDISTANCEALPHAFROMDETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = true; +#endif + } +private: + int m_nSOFT_MASK; +#ifdef _DEBUG + bool m_bSOFT_MASK; +#endif +public: + void SetSOFT_MASK( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSOFT_MASK = i; +#ifdef _DEBUG + m_bSOFT_MASK = true; +#endif + } + void SetSOFT_MASK( bool i ) + { + m_nSOFT_MASK = i ? 1 : 0; +#ifdef _DEBUG + m_bSOFT_MASK = true; +#endif + } +private: + int m_nOUTLINE; +#ifdef _DEBUG + bool m_bOUTLINE; +#endif +public: + void SetOUTLINE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nOUTLINE = i; +#ifdef _DEBUG + m_bOUTLINE = true; +#endif + } + void SetOUTLINE( bool i ) + { + m_nOUTLINE = i ? 1 : 0; +#ifdef _DEBUG + m_bOUTLINE = true; +#endif + } +private: + int m_nOUTER_GLOW; +#ifdef _DEBUG + bool m_bOUTER_GLOW; +#endif +public: + void SetOUTER_GLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nOUTER_GLOW = i; +#ifdef _DEBUG + m_bOUTER_GLOW = true; +#endif + } + void SetOUTER_GLOW( bool i ) + { + m_nOUTER_GLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bOUTER_GLOW = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +private: + int m_nDEPTHBLEND; +#ifdef _DEBUG + bool m_bDEPTHBLEND; +#endif +public: + void SetDEPTHBLEND( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDEPTHBLEND = i; +#ifdef _DEBUG + m_bDEPTHBLEND = true; +#endif + } + void SetDEPTHBLEND( bool i ) + { + m_nDEPTHBLEND = i ? 1 : 0; +#ifdef _DEBUG + m_bDEPTHBLEND = true; +#endif + } +private: + int m_nBLENDTINTBYBASEALPHA; +#ifdef _DEBUG + bool m_bBLENDTINTBYBASEALPHA; +#endif +public: + void SetBLENDTINTBYBASEALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBLENDTINTBYBASEALPHA = i; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } + void SetBLENDTINTBYBASEALPHA( bool i ) + { + m_nBLENDTINTBYBASEALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = true; +#endif + } +private: + int m_nCUBEMAP_SPHERE_LEGACY; +#ifdef _DEBUG + bool m_bCUBEMAP_SPHERE_LEGACY; +#endif +public: + void SetCUBEMAP_SPHERE_LEGACY( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP_SPHERE_LEGACY = i; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = true; +#endif + } + void SetCUBEMAP_SPHERE_LEGACY( bool i ) + { + m_nCUBEMAP_SPHERE_LEGACY = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_ps30_Static_Index( ) + { +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bENVMAPMASK = false; +#endif // _DEBUG + m_nENVMAPMASK = 0; +#ifdef _DEBUG + m_bBASEALPHAENVMAPMASK = false; +#endif // _DEBUG + m_nBASEALPHAENVMAPMASK = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSELFILLUM_ENVMAPMASK_ALPHA = false; +#endif // _DEBUG + m_nSELFILLUM_ENVMAPMASK_ALPHA = 0; +#ifdef _DEBUG + m_bDETAIL_BLEND_MODE = false; +#endif // _DEBUG + m_nDETAIL_BLEND_MODE = 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = false; +#endif // _DEBUG + m_nSEAMLESS_BASE = 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = false; +#endif // _DEBUG + m_nSEAMLESS_DETAIL = 0; +#ifdef _DEBUG + m_bDISTANCEALPHA = false; +#endif // _DEBUG + m_nDISTANCEALPHA = 0; +#ifdef _DEBUG + m_bDISTANCEALPHAFROMDETAIL = false; +#endif // _DEBUG + m_nDISTANCEALPHAFROMDETAIL = 0; +#ifdef _DEBUG + m_bSOFT_MASK = false; +#endif // _DEBUG + m_nSOFT_MASK = 0; +#ifdef _DEBUG + m_bOUTLINE = false; +#endif // _DEBUG + m_nOUTLINE = 0; +#ifdef _DEBUG + m_bOUTER_GLOW = false; +#endif // _DEBUG + m_nOUTER_GLOW = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; +#ifdef _DEBUG + m_bDEPTHBLEND = false; +#endif // _DEBUG + m_nDEPTHBLEND = 0; +#ifdef _DEBUG + m_bBLENDTINTBYBASEALPHA = false; +#endif // _DEBUG + m_nBLENDTINTBYBASEALPHA = 0; +#ifdef _DEBUG + m_bCUBEMAP_SPHERE_LEGACY = false; +#endif // _DEBUG + m_nCUBEMAP_SPHERE_LEGACY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bDETAILTEXTURE && m_bCUBEMAP && m_bDIFFUSELIGHTING && m_bENVMAPMASK && m_bBASEALPHAENVMAPMASK && m_bSELFILLUM && m_bVERTEXCOLOR && m_bFLASHLIGHT && m_bSELFILLUM_ENVMAPMASK_ALPHA && m_bDETAIL_BLEND_MODE && m_bSEAMLESS_BASE && m_bSEAMLESS_DETAIL && m_bDISTANCEALPHA && m_bDISTANCEALPHAFROMDETAIL && m_bSOFT_MASK && m_bOUTLINE && m_bOUTER_GLOW && m_bFLASHLIGHTDEPTHFILTERMODE && m_bDEPTHBLEND && m_bBLENDTINTBYBASEALPHA && m_bCUBEMAP_SPHERE_LEGACY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 24 * m_nDETAILTEXTURE ) + ( 48 * m_nCUBEMAP ) + ( 96 * m_nDIFFUSELIGHTING ) + ( 192 * m_nENVMAPMASK ) + ( 384 * m_nBASEALPHAENVMAPMASK ) + ( 768 * m_nSELFILLUM ) + ( 1536 * m_nVERTEXCOLOR ) + ( 3072 * m_nFLASHLIGHT ) + ( 6144 * m_nSELFILLUM_ENVMAPMASK_ALPHA ) + ( 12288 * m_nDETAIL_BLEND_MODE ) + ( 122880 * m_nSEAMLESS_BASE ) + ( 245760 * m_nSEAMLESS_DETAIL ) + ( 491520 * m_nDISTANCEALPHA ) + ( 983040 * m_nDISTANCEALPHAFROMDETAIL ) + ( 1966080 * m_nSOFT_MASK ) + ( 3932160 * m_nOUTLINE ) + ( 7864320 * m_nOUTER_GLOW ) + ( 15728640 * m_nFLASHLIGHTDEPTHFILTERMODE ) + ( 47185920 * m_nDEPTHBLEND ) + ( 94371840 * m_nBLENDTINTBYBASEALPHA ) + ( 188743680 * m_nCUBEMAP_SPHERE_LEGACY ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_ps30 psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_ENVMAPMASK + psh_forgot_to_set_static_BASEALPHAENVMAPMASK + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_SELFILLUM_ENVMAPMASK_ALPHA + psh_forgot_to_set_static_DETAIL_BLEND_MODE + psh_forgot_to_set_static_SEAMLESS_BASE + psh_forgot_to_set_static_SEAMLESS_DETAIL + psh_forgot_to_set_static_DISTANCEALPHA + psh_forgot_to_set_static_DISTANCEALPHAFROMDETAIL + psh_forgot_to_set_static_SOFT_MASK + psh_forgot_to_set_static_OUTLINE + psh_forgot_to_set_static_OUTER_GLOW + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + psh_forgot_to_set_static_DEPTHBLEND + psh_forgot_to_set_static_BLENDTINTBYBASEALPHA + psh_forgot_to_set_static_CUBEMAP_SPHERE_LEGACY + 0 +class sdk_vertexlit_and_unlit_generic_ps30_Dynamic_Index +{ +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +private: + int m_nSTATIC_LIGHT_LIGHTMAP; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_LIGHTMAP; +#endif +public: + void SetSTATIC_LIGHT_LIGHTMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT_LIGHTMAP = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } + void SetSTATIC_LIGHT_LIGHTMAP( bool i ) + { + m_nSTATIC_LIGHT_LIGHTMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } +private: + int m_nDEBUG_LUXELS; +#ifdef _DEBUG + bool m_bDEBUG_LUXELS; +#endif +public: + void SetDEBUG_LUXELS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDEBUG_LUXELS = i; +#ifdef _DEBUG + m_bDEBUG_LUXELS = true; +#endif + } + void SetDEBUG_LUXELS( bool i ) + { + m_nDEBUG_LUXELS = i ? 1 : 0; +#ifdef _DEBUG + m_bDEBUG_LUXELS = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_ps30_Dynamic_Index() + { +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_LIGHTMAP = 0; +#ifdef _DEBUG + m_bDEBUG_LUXELS = false; +#endif // _DEBUG + m_nDEBUG_LUXELS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bLIGHTING_PREVIEW && m_bFLASHLIGHTSHADOWS && m_bSTATIC_LIGHT_LIGHTMAP && m_bDEBUG_LUXELS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nLIGHTING_PREVIEW ) + ( 3 * m_nFLASHLIGHTSHADOWS ) + ( 6 * m_nSTATIC_LIGHT_LIGHTMAP ) + ( 12 * m_nDEBUG_LUXELS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_ps30 psh_forgot_to_set_dynamic_LIGHTING_PREVIEW + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + psh_forgot_to_set_dynamic_STATIC_LIGHT_LIGHTMAP + psh_forgot_to_set_dynamic_DEBUG_LUXELS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs20.inc new file mode 100644 index 00000000..fc50b9fc --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs20.inc @@ -0,0 +1,487 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_vs20_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSEAMLESS_BASE; +#ifdef _DEBUG + bool m_bSEAMLESS_BASE; +#endif +public: + void SetSEAMLESS_BASE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_BASE = i; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } + void SetSEAMLESS_BASE( bool i ) + { + m_nSEAMLESS_BASE = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } +private: + int m_nSEAMLESS_DETAIL; +#ifdef _DEBUG + bool m_bSEAMLESS_DETAIL; +#endif +public: + void SetSEAMLESS_DETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_DETAIL = i; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } + void SetSEAMLESS_DETAIL( bool i ) + { + m_nSEAMLESS_DETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } +private: + int m_nSEPARATE_DETAIL_UVS; +#ifdef _DEBUG + bool m_bSEPARATE_DETAIL_UVS; +#endif +public: + void SetSEPARATE_DETAIL_UVS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEPARATE_DETAIL_UVS = i; +#ifdef _DEBUG + m_bSEPARATE_DETAIL_UVS = true; +#endif + } + void SetSEPARATE_DETAIL_UVS( bool i ) + { + m_nSEPARATE_DETAIL_UVS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEPARATE_DETAIL_UVS = true; +#endif + } +private: + int m_nUSE_STATIC_CONTROL_FLOW; +#ifdef _DEBUG + bool m_bUSE_STATIC_CONTROL_FLOW; +#endif +public: + void SetUSE_STATIC_CONTROL_FLOW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nUSE_STATIC_CONTROL_FLOW = i; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } + void SetUSE_STATIC_CONTROL_FLOW( bool i ) + { + m_nUSE_STATIC_CONTROL_FLOW = i ? 1 : 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = true; +#endif + } +private: + int m_nDONT_GAMMA_CONVERT_VERTEX_COLOR; +#ifdef _DEBUG + bool m_bDONT_GAMMA_CONVERT_VERTEX_COLOR; +#endif +public: + void SetDONT_GAMMA_CONVERT_VERTEX_COLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDONT_GAMMA_CONVERT_VERTEX_COLOR = i; +#ifdef _DEBUG + m_bDONT_GAMMA_CONVERT_VERTEX_COLOR = true; +#endif + } + void SetDONT_GAMMA_CONVERT_VERTEX_COLOR( bool i ) + { + m_nDONT_GAMMA_CONVERT_VERTEX_COLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bDONT_GAMMA_CONVERT_VERTEX_COLOR = true; +#endif + } +private: + int m_nTREESWAY; +#ifdef _DEBUG + bool m_bTREESWAY; +#endif +public: + void SetTREESWAY( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nTREESWAY = i; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } + void SetTREESWAY( bool i ) + { + m_nTREESWAY = i ? 1 : 0; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = false; +#endif // _DEBUG + m_nSEAMLESS_BASE = 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = false; +#endif // _DEBUG + m_nSEAMLESS_DETAIL = 0; +#ifdef _DEBUG + m_bSEPARATE_DETAIL_UVS = false; +#endif // _DEBUG + m_nSEPARATE_DETAIL_UVS = 0; +#ifdef _DEBUG + m_bUSE_STATIC_CONTROL_FLOW = false; +#endif // _DEBUG + m_nUSE_STATIC_CONTROL_FLOW = 0; +#ifdef _DEBUG + m_bDONT_GAMMA_CONVERT_VERTEX_COLOR = false; +#endif // _DEBUG + m_nDONT_GAMMA_CONVERT_VERTEX_COLOR = 0; +#ifdef _DEBUG + m_bTREESWAY = false; +#endif // _DEBUG + m_nTREESWAY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR && m_bCUBEMAP && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bSEAMLESS_BASE && m_bSEAMLESS_DETAIL && m_bSEPARATE_DETAIL_UVS && m_bUSE_STATIC_CONTROL_FLOW && m_bDONT_GAMMA_CONVERT_VERTEX_COLOR && m_bTREESWAY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 384 * m_nVERTEXCOLOR ) + ( 768 * m_nCUBEMAP ) + ( 1536 * m_nHALFLAMBERT ) + ( 3072 * m_nFLASHLIGHT ) + ( 6144 * m_nSEAMLESS_BASE ) + ( 12288 * m_nSEAMLESS_DETAIL ) + ( 24576 * m_nSEPARATE_DETAIL_UVS ) + ( 49152 * m_nUSE_STATIC_CONTROL_FLOW ) + ( 98304 * m_nDONT_GAMMA_CONVERT_VERTEX_COLOR ) + ( 196608 * m_nTREESWAY ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_vs20 vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_CUBEMAP + vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_FLASHLIGHT + vsh_forgot_to_set_static_SEAMLESS_BASE + vsh_forgot_to_set_static_SEAMLESS_DETAIL + vsh_forgot_to_set_static_SEPARATE_DETAIL_UVS + vsh_forgot_to_set_static_USE_STATIC_CONTROL_FLOW + vsh_forgot_to_set_static_DONT_GAMMA_CONVERT_VERTEX_COLOR + vsh_forgot_to_set_static_TREESWAY + 0 +class sdk_vertexlit_and_unlit_generic_vs20_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT_VERTEX; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_VERTEX; +#endif +public: + void SetSTATIC_LIGHT_VERTEX( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT_VERTEX = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_VERTEX = true; +#endif + } + void SetSTATIC_LIGHT_VERTEX( bool i ) + { + m_nSTATIC_LIGHT_VERTEX = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_VERTEX = true; +#endif + } +private: + int m_nSTATIC_LIGHT_LIGHTMAP; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_LIGHTMAP; +#endif +public: + void SetSTATIC_LIGHT_LIGHTMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT_LIGHTMAP = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } + void SetSTATIC_LIGHT_LIGHTMAP( bool i ) + { + m_nSTATIC_LIGHT_LIGHTMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_VERTEX = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_VERTEX = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_LIGHTMAP = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT_VERTEX && m_bSTATIC_LIGHT_LIGHTMAP && m_bDOWATERFOG && m_bSKINNING && m_bLIGHTING_PREVIEW && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDYNAMIC_LIGHT ) + ( 4 * m_nSTATIC_LIGHT_VERTEX ) + ( 8 * m_nSTATIC_LIGHT_LIGHTMAP ) + ( 16 * m_nDOWATERFOG ) + ( 32 * m_nSKINNING ) + ( 64 * m_nLIGHTING_PREVIEW ) + ( 128 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT_VERTEX + vsh_forgot_to_set_dynamic_STATIC_LIGHT_LIGHTMAP + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_LIGHTING_PREVIEW + vsh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs30.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs30.inc new file mode 100644 index 00000000..5bd1e3ba --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_and_unlit_generic_vs30.inc @@ -0,0 +1,512 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_and_unlit_generic_vs30_Static_Index +{ +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nCUBEMAP; +#ifdef _DEBUG + bool m_bCUBEMAP; +#endif +public: + void SetCUBEMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCUBEMAP = i; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } + void SetCUBEMAP( bool i ) + { + m_nCUBEMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bCUBEMAP = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSEAMLESS_BASE; +#ifdef _DEBUG + bool m_bSEAMLESS_BASE; +#endif +public: + void SetSEAMLESS_BASE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_BASE = i; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } + void SetSEAMLESS_BASE( bool i ) + { + m_nSEAMLESS_BASE = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = true; +#endif + } +private: + int m_nSEAMLESS_DETAIL; +#ifdef _DEBUG + bool m_bSEAMLESS_DETAIL; +#endif +public: + void SetSEAMLESS_DETAIL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS_DETAIL = i; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } + void SetSEAMLESS_DETAIL( bool i ) + { + m_nSEAMLESS_DETAIL = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = true; +#endif + } +private: + int m_nSEPARATE_DETAIL_UVS; +#ifdef _DEBUG + bool m_bSEPARATE_DETAIL_UVS; +#endif +public: + void SetSEPARATE_DETAIL_UVS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEPARATE_DETAIL_UVS = i; +#ifdef _DEBUG + m_bSEPARATE_DETAIL_UVS = true; +#endif + } + void SetSEPARATE_DETAIL_UVS( bool i ) + { + m_nSEPARATE_DETAIL_UVS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEPARATE_DETAIL_UVS = true; +#endif + } +private: + int m_nDECAL; +#ifdef _DEBUG + bool m_bDECAL; +#endif +public: + void SetDECAL( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDECAL = i; +#ifdef _DEBUG + m_bDECAL = true; +#endif + } + void SetDECAL( bool i ) + { + m_nDECAL = i ? 1 : 0; +#ifdef _DEBUG + m_bDECAL = true; +#endif + } +private: + int m_nSM30_VERTEXID; +#ifdef _DEBUG + bool m_bSM30_VERTEXID; +#endif +public: + void SetSM30_VERTEXID( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSM30_VERTEXID = i; +#ifdef _DEBUG + m_bSM30_VERTEXID = true; +#endif + } + void SetSM30_VERTEXID( bool i ) + { + m_nSM30_VERTEXID = i ? 1 : 0; +#ifdef _DEBUG + m_bSM30_VERTEXID = true; +#endif + } +private: + int m_nDONT_GAMMA_CONVERT_VERTEX_COLOR; +#ifdef _DEBUG + bool m_bDONT_GAMMA_CONVERT_VERTEX_COLOR; +#endif +public: + void SetDONT_GAMMA_CONVERT_VERTEX_COLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDONT_GAMMA_CONVERT_VERTEX_COLOR = i; +#ifdef _DEBUG + m_bDONT_GAMMA_CONVERT_VERTEX_COLOR = true; +#endif + } + void SetDONT_GAMMA_CONVERT_VERTEX_COLOR( bool i ) + { + m_nDONT_GAMMA_CONVERT_VERTEX_COLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bDONT_GAMMA_CONVERT_VERTEX_COLOR = true; +#endif + } +private: + int m_nTREESWAY; +#ifdef _DEBUG + bool m_bTREESWAY; +#endif +public: + void SetTREESWAY( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nTREESWAY = i; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } + void SetTREESWAY( bool i ) + { + m_nTREESWAY = i ? 1 : 0; +#ifdef _DEBUG + m_bTREESWAY = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_vs30_Static_Index( ) + { +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSEAMLESS_BASE = false; +#endif // _DEBUG + m_nSEAMLESS_BASE = 0; +#ifdef _DEBUG + m_bSEAMLESS_DETAIL = false; +#endif // _DEBUG + m_nSEAMLESS_DETAIL = 0; +#ifdef _DEBUG + m_bSEPARATE_DETAIL_UVS = false; +#endif // _DEBUG + m_nSEPARATE_DETAIL_UVS = 0; +#ifdef _DEBUG + m_bDECAL = false; +#endif // _DEBUG + m_nDECAL = 0; +#ifdef _DEBUG + m_bSM30_VERTEXID = false; +#endif // _DEBUG + m_nSM30_VERTEXID = 0; +#ifdef _DEBUG + m_bDONT_GAMMA_CONVERT_VERTEX_COLOR = false; +#endif // _DEBUG + m_nDONT_GAMMA_CONVERT_VERTEX_COLOR = 0; +#ifdef _DEBUG + m_bTREESWAY = false; +#endif // _DEBUG + m_nTREESWAY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bVERTEXCOLOR && m_bCUBEMAP && m_bHALFLAMBERT && m_bFLASHLIGHT && m_bSEAMLESS_BASE && m_bSEAMLESS_DETAIL && m_bSEPARATE_DETAIL_UVS && m_bDECAL && m_bSM30_VERTEXID && m_bDONT_GAMMA_CONVERT_VERTEX_COLOR && m_bTREESWAY; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 256 * m_nVERTEXCOLOR ) + ( 512 * m_nCUBEMAP ) + ( 1024 * m_nHALFLAMBERT ) + ( 2048 * m_nFLASHLIGHT ) + ( 4096 * m_nSEAMLESS_BASE ) + ( 8192 * m_nSEAMLESS_DETAIL ) + ( 16384 * m_nSEPARATE_DETAIL_UVS ) + ( 32768 * m_nDECAL ) + ( 65536 * m_nSM30_VERTEXID ) + ( 131072 * m_nDONT_GAMMA_CONVERT_VERTEX_COLOR ) + ( 262144 * m_nTREESWAY ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_and_unlit_generic_vs30 vsh_forgot_to_set_static_VERTEXCOLOR + vsh_forgot_to_set_static_CUBEMAP + vsh_forgot_to_set_static_HALFLAMBERT + vsh_forgot_to_set_static_FLASHLIGHT + vsh_forgot_to_set_static_SEAMLESS_BASE + vsh_forgot_to_set_static_SEAMLESS_DETAIL + vsh_forgot_to_set_static_SEPARATE_DETAIL_UVS + vsh_forgot_to_set_static_DECAL + vsh_forgot_to_set_static_SM30_VERTEXID + vsh_forgot_to_set_static_DONT_GAMMA_CONVERT_VERTEX_COLOR + vsh_forgot_to_set_static_TREESWAY + 0 +class sdk_vertexlit_and_unlit_generic_vs30_Dynamic_Index +{ +private: + int m_nCOMPRESSED_VERTS; +#ifdef _DEBUG + bool m_bCOMPRESSED_VERTS; +#endif +public: + void SetCOMPRESSED_VERTS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCOMPRESSED_VERTS = i; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } + void SetCOMPRESSED_VERTS( bool i ) + { + m_nCOMPRESSED_VERTS = i ? 1 : 0; +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = true; +#endif + } +private: + int m_nDYNAMIC_LIGHT; +#ifdef _DEBUG + bool m_bDYNAMIC_LIGHT; +#endif +public: + void SetDYNAMIC_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDYNAMIC_LIGHT = i; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } + void SetDYNAMIC_LIGHT( bool i ) + { + m_nDYNAMIC_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = true; +#endif + } +private: + int m_nSTATIC_LIGHT_VERTEX; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_VERTEX; +#endif +public: + void SetSTATIC_LIGHT_VERTEX( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT_VERTEX = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_VERTEX = true; +#endif + } + void SetSTATIC_LIGHT_VERTEX( bool i ) + { + m_nSTATIC_LIGHT_VERTEX = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_VERTEX = true; +#endif + } +private: + int m_nSTATIC_LIGHT_LIGHTMAP; +#ifdef _DEBUG + bool m_bSTATIC_LIGHT_LIGHTMAP; +#endif +public: + void SetSTATIC_LIGHT_LIGHTMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSTATIC_LIGHT_LIGHTMAP = i; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } + void SetSTATIC_LIGHT_LIGHTMAP( bool i ) + { + m_nSTATIC_LIGHT_LIGHTMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = true; +#endif + } +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +private: + int m_nSKINNING; +#ifdef _DEBUG + bool m_bSKINNING; +#endif +public: + void SetSKINNING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSKINNING = i; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } + void SetSKINNING( bool i ) + { + m_nSKINNING = i ? 1 : 0; +#ifdef _DEBUG + m_bSKINNING = true; +#endif + } +private: + int m_nLIGHTING_PREVIEW; +#ifdef _DEBUG + bool m_bLIGHTING_PREVIEW; +#endif +public: + void SetLIGHTING_PREVIEW( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nLIGHTING_PREVIEW = i; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } + void SetLIGHTING_PREVIEW( bool i ) + { + m_nLIGHTING_PREVIEW = i ? 1 : 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = true; +#endif + } +private: + int m_nMORPHING; +#ifdef _DEBUG + bool m_bMORPHING; +#endif +public: + void SetMORPHING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nMORPHING = i; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } + void SetMORPHING( bool i ) + { + m_nMORPHING = i ? 1 : 0; +#ifdef _DEBUG + m_bMORPHING = true; +#endif + } +public: + sdk_vertexlit_and_unlit_generic_vs30_Dynamic_Index() + { +#ifdef _DEBUG + m_bCOMPRESSED_VERTS = false; +#endif // _DEBUG + m_nCOMPRESSED_VERTS = 0; +#ifdef _DEBUG + m_bDYNAMIC_LIGHT = false; +#endif // _DEBUG + m_nDYNAMIC_LIGHT = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_VERTEX = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_VERTEX = 0; +#ifdef _DEBUG + m_bSTATIC_LIGHT_LIGHTMAP = false; +#endif // _DEBUG + m_nSTATIC_LIGHT_LIGHTMAP = 0; +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; +#ifdef _DEBUG + m_bSKINNING = false; +#endif // _DEBUG + m_nSKINNING = 0; +#ifdef _DEBUG + m_bLIGHTING_PREVIEW = false; +#endif // _DEBUG + m_nLIGHTING_PREVIEW = 0; +#ifdef _DEBUG + m_bMORPHING = false; +#endif // _DEBUG + m_nMORPHING = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bCOMPRESSED_VERTS && m_bDYNAMIC_LIGHT && m_bSTATIC_LIGHT_VERTEX && m_bSTATIC_LIGHT_LIGHTMAP && m_bDOWATERFOG && m_bSKINNING && m_bLIGHTING_PREVIEW && m_bMORPHING; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nCOMPRESSED_VERTS ) + ( 2 * m_nDYNAMIC_LIGHT ) + ( 4 * m_nSTATIC_LIGHT_VERTEX ) + ( 8 * m_nSTATIC_LIGHT_LIGHTMAP ) + ( 16 * m_nDOWATERFOG ) + ( 32 * m_nSKINNING ) + ( 64 * m_nLIGHTING_PREVIEW ) + ( 128 * m_nMORPHING ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_and_unlit_generic_vs30 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_DYNAMIC_LIGHT + vsh_forgot_to_set_dynamic_STATIC_LIGHT_VERTEX + vsh_forgot_to_set_dynamic_STATIC_LIGHT_LIGHTMAP + vsh_forgot_to_set_dynamic_DOWATERFOG + vsh_forgot_to_set_dynamic_SKINNING + vsh_forgot_to_set_dynamic_LIGHTING_PREVIEW + vsh_forgot_to_set_dynamic_MORPHING + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20.inc new file mode 100644 index 00000000..365fb68e --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20.inc @@ -0,0 +1,137 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_lighting_only_ps20_Static_Index +{ +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +public: + sdk_vertexlit_lighting_only_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bDIFFUSELIGHTING && m_bHALFLAMBERT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 6 * m_nDIFFUSELIGHTING ) + ( 12 * m_nHALFLAMBERT ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_lighting_only_ps20 psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_HALFLAMBERT + 0 +class sdk_vertexlit_lighting_only_ps20_Dynamic_Index +{ +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_vertexlit_lighting_only_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bAMBIENT_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nAMBIENT_LIGHT ) + ( 2 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_lighting_only_ps20 psh_forgot_to_set_dynamic_AMBIENT_LIGHT + psh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20b.inc new file mode 100644 index 00000000..a9d3beaa --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlit_lighting_only_ps20b.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlit_lighting_only_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nDIFFUSELIGHTING; +#ifdef _DEBUG + bool m_bDIFFUSELIGHTING; +#endif +public: + void SetDIFFUSELIGHTING( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSELIGHTING = i; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } + void SetDIFFUSELIGHTING( bool i ) + { + m_nDIFFUSELIGHTING = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSELIGHTING = true; +#endif + } +private: + int m_nHALFLAMBERT; +#ifdef _DEBUG + bool m_bHALFLAMBERT; +#endif +public: + void SetHALFLAMBERT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nHALFLAMBERT = i; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } + void SetHALFLAMBERT( bool i ) + { + m_nHALFLAMBERT = i ? 1 : 0; +#ifdef _DEBUG + m_bHALFLAMBERT = true; +#endif + } +public: + sdk_vertexlit_lighting_only_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bDIFFUSELIGHTING = false; +#endif // _DEBUG + m_nDIFFUSELIGHTING = 0; +#ifdef _DEBUG + m_bHALFLAMBERT = false; +#endif // _DEBUG + m_nHALFLAMBERT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bDIFFUSELIGHTING && m_bHALFLAMBERT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 10 * m_nCONVERT_TO_SRGB ) + ( 20 * m_nDIFFUSELIGHTING ) + ( 40 * m_nHALFLAMBERT ) + 0; + } +}; +#define shaderStaticTest_sdk_vertexlit_lighting_only_ps20b psh_forgot_to_set_static_DIFFUSELIGHTING + psh_forgot_to_set_static_HALFLAMBERT + 0 +class sdk_vertexlit_lighting_only_ps20b_Dynamic_Index +{ +private: + int m_nAMBIENT_LIGHT; +#ifdef _DEBUG + bool m_bAMBIENT_LIGHT; +#endif +public: + void SetAMBIENT_LIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nAMBIENT_LIGHT = i; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } + void SetAMBIENT_LIGHT( bool i ) + { + m_nAMBIENT_LIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bAMBIENT_LIGHT = true; +#endif + } +private: + int m_nNUM_LIGHTS; +#ifdef _DEBUG + bool m_bNUM_LIGHTS; +#endif +public: + void SetNUM_LIGHTS( int i ) + { + Assert( i >= 0 && i <= 4 ); + m_nNUM_LIGHTS = i; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } + void SetNUM_LIGHTS( bool i ) + { + m_nNUM_LIGHTS = i ? 1 : 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = true; +#endif + } +public: + sdk_vertexlit_lighting_only_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bAMBIENT_LIGHT = false; +#endif // _DEBUG + m_nAMBIENT_LIGHT = 0; +#ifdef _DEBUG + m_bNUM_LIGHTS = false; +#endif // _DEBUG + m_nNUM_LIGHTS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bAMBIENT_LIGHT && m_bNUM_LIGHTS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nAMBIENT_LIGHT ) + ( 2 * m_nNUM_LIGHTS ) + 0; + } +}; +#define shaderDynamicTest_sdk_vertexlit_lighting_only_ps20b psh_forgot_to_set_dynamic_AMBIENT_LIGHT + psh_forgot_to_set_dynamic_NUM_LIGHTS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.inc new file mode 100644 index 00000000..9f9d2b41 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_vertexlitgeneric_lightingonly_overbright2_ps11.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class sdk_vertexlitgeneric_lightingonly_overbright2_ps11_Static_Index +{ +public: + sdk_vertexlitgeneric_lightingonly_overbright2_ps11_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_sdk_vertexlitgeneric_lightingonly_overbright2_ps11 0 +class sdk_vertexlitgeneric_lightingonly_overbright2_ps11_Dynamic_Index +{ +public: + sdk_vertexlitgeneric_lightingonly_overbright2_ps11_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_sdk_vertexlitgeneric_lightingonly_overbright2_ps11 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20.inc new file mode 100644 index 00000000..7c34f6ee --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_windowimposter_ps20_Static_Index +{ +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_windowimposter_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_windowimposter_ps20 psh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_windowimposter_ps20_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_windowimposter_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_windowimposter_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20b.inc new file mode 100644 index 00000000..e94fc94d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_ps20b.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_windowimposter_ps20b_Static_Index +{ +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_windowimposter_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_windowimposter_ps20b psh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_windowimposter_ps20b_Dynamic_Index +{ +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_windowimposter_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_windowimposter_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_vs20.inc new file mode 100644 index 00000000..faa096a2 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_windowimposter_vs20.inc @@ -0,0 +1,87 @@ +#include "shaderlib/cshader.h" +class sdk_windowimposter_vs20_Static_Index +{ +private: + int m_nPARALLAXCORRECT; +#ifdef _DEBUG + bool m_bPARALLAXCORRECT; +#endif +public: + void SetPARALLAXCORRECT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPARALLAXCORRECT = i; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } + void SetPARALLAXCORRECT( bool i ) + { + m_nPARALLAXCORRECT = i ? 1 : 0; +#ifdef _DEBUG + m_bPARALLAXCORRECT = true; +#endif + } +public: + sdk_windowimposter_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bPARALLAXCORRECT = false; +#endif // _DEBUG + m_nPARALLAXCORRECT = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bPARALLAXCORRECT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nPARALLAXCORRECT ) + 0; + } +}; +#define shaderStaticTest_sdk_windowimposter_vs20 vsh_forgot_to_set_static_PARALLAXCORRECT + 0 +class sdk_windowimposter_vs20_Dynamic_Index +{ +private: + int m_nDOWATERFOG; +#ifdef _DEBUG + bool m_bDOWATERFOG; +#endif +public: + void SetDOWATERFOG( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDOWATERFOG = i; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } + void SetDOWATERFOG( bool i ) + { + m_nDOWATERFOG = i ? 1 : 0; +#ifdef _DEBUG + m_bDOWATERFOG = true; +#endif + } +public: + sdk_windowimposter_vs20_Dynamic_Index() + { +#ifdef _DEBUG + m_bDOWATERFOG = false; +#endif // _DEBUG + m_nDOWATERFOG = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bDOWATERFOG; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nDOWATERFOG ) + 0; + } +}; +#define shaderDynamicTest_sdk_windowimposter_vs20 vsh_forgot_to_set_dynamic_DOWATERFOG + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20.inc new file mode 100644 index 00000000..eeef1cae --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20.inc @@ -0,0 +1,287 @@ +#include "shaderlib/cshader.h" +class sdk_worldtwotextureblend_ps20_Static_Index +{ +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nDIFFUSEBUMPMAP; +#ifdef _DEBUG + bool m_bDIFFUSEBUMPMAP; +#endif +public: + void SetDIFFUSEBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSEBUMPMAP = i; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } + void SetDIFFUSEBUMPMAP( bool i ) + { + m_nDIFFUSEBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } +private: + int m_nDETAIL_ALPHA_MASK_BASE_TEXTURE; +#ifdef _DEBUG + bool m_bDETAIL_ALPHA_MASK_BASE_TEXTURE; +#endif +public: + void SetDETAIL_ALPHA_MASK_BASE_TEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_ALPHA_MASK_BASE_TEXTURE = i; +#ifdef _DEBUG + m_bDETAIL_ALPHA_MASK_BASE_TEXTURE = true; +#endif + } + void SetDETAIL_ALPHA_MASK_BASE_TEXTURE( bool i ) + { + m_nDETAIL_ALPHA_MASK_BASE_TEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_ALPHA_MASK_BASE_TEXTURE = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +public: + sdk_worldtwotextureblend_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = false; +#endif // _DEBUG + m_nDIFFUSEBUMPMAP = 0; +#ifdef _DEBUG + m_bDETAIL_ALPHA_MASK_BASE_TEXTURE = false; +#endif // _DEBUG + m_nDETAIL_ALPHA_MASK_BASE_TEXTURE = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bDETAILTEXTURE && m_bBUMPMAP && m_bVERTEXCOLOR && m_bSELFILLUM && m_bDIFFUSEBUMPMAP && m_bDETAIL_ALPHA_MASK_BASE_TEXTURE && m_bFLASHLIGHT && m_bSEAMLESS; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nDETAILTEXTURE ) + ( 8 * m_nBUMPMAP ) + ( 16 * m_nVERTEXCOLOR ) + ( 32 * m_nSELFILLUM ) + ( 64 * m_nDIFFUSEBUMPMAP ) + ( 128 * m_nDETAIL_ALPHA_MASK_BASE_TEXTURE ) + ( 256 * m_nFLASHLIGHT ) + ( 512 * m_nSEAMLESS ) + 0; + } +}; +#define shaderStaticTest_sdk_worldtwotextureblend_ps20 psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_BUMPMAP + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_DIFFUSEBUMPMAP + psh_forgot_to_set_static_DETAIL_ALPHA_MASK_BASE_TEXTURE + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_SEAMLESS + 0 +class sdk_worldtwotextureblend_ps20_Dynamic_Index +{ +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +public: + sdk_worldtwotextureblend_ps20_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITEWATERFOGTODESTALPHA && m_bPIXELFOGTYPE; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITEWATERFOGTODESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + 0; + } +}; +#define shaderDynamicTest_sdk_worldtwotextureblend_ps20 psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20b.inc new file mode 100644 index 00000000..5663a738 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_worldtwotextureblend_ps20b.inc @@ -0,0 +1,387 @@ +#include "shaderlib/cshader.h" +class sdk_worldtwotextureblend_ps20b_Static_Index +{ +private: + int m_nCONVERT_TO_SRGB; +#ifdef _DEBUG + bool m_bCONVERT_TO_SRGB; +#endif +public: + void SetCONVERT_TO_SRGB( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCONVERT_TO_SRGB = i; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } + void SetCONVERT_TO_SRGB( bool i ) + { + m_nCONVERT_TO_SRGB = i ? 1 : 0; +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif + } +private: + int m_nDETAILTEXTURE; +#ifdef _DEBUG + bool m_bDETAILTEXTURE; +#endif +public: + void SetDETAILTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAILTEXTURE = i; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } + void SetDETAILTEXTURE( bool i ) + { + m_nDETAILTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAILTEXTURE = true; +#endif + } +private: + int m_nBUMPMAP; +#ifdef _DEBUG + bool m_bBUMPMAP; +#endif +public: + void SetBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nBUMPMAP = i; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } + void SetBUMPMAP( bool i ) + { + m_nBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bBUMPMAP = true; +#endif + } +private: + int m_nVERTEXCOLOR; +#ifdef _DEBUG + bool m_bVERTEXCOLOR; +#endif +public: + void SetVERTEXCOLOR( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nVERTEXCOLOR = i; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } + void SetVERTEXCOLOR( bool i ) + { + m_nVERTEXCOLOR = i ? 1 : 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = true; +#endif + } +private: + int m_nSELFILLUM; +#ifdef _DEBUG + bool m_bSELFILLUM; +#endif +public: + void SetSELFILLUM( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSELFILLUM = i; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } + void SetSELFILLUM( bool i ) + { + m_nSELFILLUM = i ? 1 : 0; +#ifdef _DEBUG + m_bSELFILLUM = true; +#endif + } +private: + int m_nDIFFUSEBUMPMAP; +#ifdef _DEBUG + bool m_bDIFFUSEBUMPMAP; +#endif +public: + void SetDIFFUSEBUMPMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDIFFUSEBUMPMAP = i; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } + void SetDIFFUSEBUMPMAP( bool i ) + { + m_nDIFFUSEBUMPMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = true; +#endif + } +private: + int m_nDETAIL_ALPHA_MASK_BASE_TEXTURE; +#ifdef _DEBUG + bool m_bDETAIL_ALPHA_MASK_BASE_TEXTURE; +#endif +public: + void SetDETAIL_ALPHA_MASK_BASE_TEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nDETAIL_ALPHA_MASK_BASE_TEXTURE = i; +#ifdef _DEBUG + m_bDETAIL_ALPHA_MASK_BASE_TEXTURE = true; +#endif + } + void SetDETAIL_ALPHA_MASK_BASE_TEXTURE( bool i ) + { + m_nDETAIL_ALPHA_MASK_BASE_TEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bDETAIL_ALPHA_MASK_BASE_TEXTURE = true; +#endif + } +private: + int m_nFLASHLIGHT; +#ifdef _DEBUG + bool m_bFLASHLIGHT; +#endif +public: + void SetFLASHLIGHT( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHT = i; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } + void SetFLASHLIGHT( bool i ) + { + m_nFLASHLIGHT = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHT = true; +#endif + } +private: + int m_nSEAMLESS; +#ifdef _DEBUG + bool m_bSEAMLESS; +#endif +public: + void SetSEAMLESS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nSEAMLESS = i; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } + void SetSEAMLESS( bool i ) + { + m_nSEAMLESS = i ? 1 : 0; +#ifdef _DEBUG + m_bSEAMLESS = true; +#endif + } +private: + int m_nFLASHLIGHTDEPTHFILTERMODE; +#ifdef _DEBUG + bool m_bFLASHLIGHTDEPTHFILTERMODE; +#endif +public: + void SetFLASHLIGHTDEPTHFILTERMODE( int i ) + { + Assert( i >= 0 && i <= 2 ); + m_nFLASHLIGHTDEPTHFILTERMODE = i; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } + void SetFLASHLIGHTDEPTHFILTERMODE( bool i ) + { + m_nFLASHLIGHTDEPTHFILTERMODE = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = true; +#endif + } +public: + sdk_worldtwotextureblend_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bDETAILTEXTURE = false; +#endif // _DEBUG + m_nDETAILTEXTURE = 0; +#ifdef _DEBUG + m_bBUMPMAP = false; +#endif // _DEBUG + m_nBUMPMAP = 0; +#ifdef _DEBUG + m_bVERTEXCOLOR = false; +#endif // _DEBUG + m_nVERTEXCOLOR = 0; +#ifdef _DEBUG + m_bSELFILLUM = false; +#endif // _DEBUG + m_nSELFILLUM = 0; +#ifdef _DEBUG + m_bDIFFUSEBUMPMAP = false; +#endif // _DEBUG + m_nDIFFUSEBUMPMAP = 0; +#ifdef _DEBUG + m_bDETAIL_ALPHA_MASK_BASE_TEXTURE = false; +#endif // _DEBUG + m_nDETAIL_ALPHA_MASK_BASE_TEXTURE = 0; +#ifdef _DEBUG + m_bFLASHLIGHT = false; +#endif // _DEBUG + m_nFLASHLIGHT = 0; +#ifdef _DEBUG + m_bSEAMLESS = false; +#endif // _DEBUG + m_nSEAMLESS = 0; +#ifdef _DEBUG + m_bFLASHLIGHTDEPTHFILTERMODE = false; +#endif // _DEBUG + m_nFLASHLIGHTDEPTHFILTERMODE = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllStaticVarsDefined = m_bCONVERT_TO_SRGB && m_bDETAILTEXTURE && m_bBUMPMAP && m_bVERTEXCOLOR && m_bSELFILLUM && m_bDIFFUSEBUMPMAP && m_bDETAIL_ALPHA_MASK_BASE_TEXTURE && m_bFLASHLIGHT && m_bSEAMLESS && m_bFLASHLIGHTDEPTHFILTERMODE; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 16 * m_nCONVERT_TO_SRGB ) + ( 32 * m_nDETAILTEXTURE ) + ( 64 * m_nBUMPMAP ) + ( 128 * m_nVERTEXCOLOR ) + ( 256 * m_nSELFILLUM ) + ( 512 * m_nDIFFUSEBUMPMAP ) + ( 1024 * m_nDETAIL_ALPHA_MASK_BASE_TEXTURE ) + ( 2048 * m_nFLASHLIGHT ) + ( 4096 * m_nSEAMLESS ) + ( 8192 * m_nFLASHLIGHTDEPTHFILTERMODE ) + 0; + } +}; +#define shaderStaticTest_sdk_worldtwotextureblend_ps20b psh_forgot_to_set_static_DETAILTEXTURE + psh_forgot_to_set_static_BUMPMAP + psh_forgot_to_set_static_VERTEXCOLOR + psh_forgot_to_set_static_SELFILLUM + psh_forgot_to_set_static_DIFFUSEBUMPMAP + psh_forgot_to_set_static_DETAIL_ALPHA_MASK_BASE_TEXTURE + psh_forgot_to_set_static_FLASHLIGHT + psh_forgot_to_set_static_SEAMLESS + psh_forgot_to_set_static_FLASHLIGHTDEPTHFILTERMODE + 0 +class sdk_worldtwotextureblend_ps20b_Dynamic_Index +{ +private: + int m_nWRITEWATERFOGTODESTALPHA; +#ifdef _DEBUG + bool m_bWRITEWATERFOGTODESTALPHA; +#endif +public: + void SetWRITEWATERFOGTODESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITEWATERFOGTODESTALPHA = i; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } + void SetWRITEWATERFOGTODESTALPHA( bool i ) + { + m_nWRITEWATERFOGTODESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = true; +#endif + } +private: + int m_nPIXELFOGTYPE; +#ifdef _DEBUG + bool m_bPIXELFOGTYPE; +#endif +public: + void SetPIXELFOGTYPE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nPIXELFOGTYPE = i; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } + void SetPIXELFOGTYPE( bool i ) + { + m_nPIXELFOGTYPE = i ? 1 : 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = true; +#endif + } +private: + int m_nWRITE_DEPTH_TO_DESTALPHA; +#ifdef _DEBUG + bool m_bWRITE_DEPTH_TO_DESTALPHA; +#endif +public: + void SetWRITE_DEPTH_TO_DESTALPHA( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nWRITE_DEPTH_TO_DESTALPHA = i; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } + void SetWRITE_DEPTH_TO_DESTALPHA( bool i ) + { + m_nWRITE_DEPTH_TO_DESTALPHA = i ? 1 : 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = true; +#endif + } +private: + int m_nFLASHLIGHTSHADOWS; +#ifdef _DEBUG + bool m_bFLASHLIGHTSHADOWS; +#endif +public: + void SetFLASHLIGHTSHADOWS( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLASHLIGHTSHADOWS = i; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } + void SetFLASHLIGHTSHADOWS( bool i ) + { + m_nFLASHLIGHTSHADOWS = i ? 1 : 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = true; +#endif + } +public: + sdk_worldtwotextureblend_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bWRITEWATERFOGTODESTALPHA = false; +#endif // _DEBUG + m_nWRITEWATERFOGTODESTALPHA = 0; +#ifdef _DEBUG + m_bPIXELFOGTYPE = false; +#endif // _DEBUG + m_nPIXELFOGTYPE = 0; +#ifdef _DEBUG + m_bWRITE_DEPTH_TO_DESTALPHA = false; +#endif // _DEBUG + m_nWRITE_DEPTH_TO_DESTALPHA = 0; +#ifdef _DEBUG + m_bFLASHLIGHTSHADOWS = false; +#endif // _DEBUG + m_nFLASHLIGHTSHADOWS = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bWRITEWATERFOGTODESTALPHA && m_bPIXELFOGTYPE && m_bWRITE_DEPTH_TO_DESTALPHA && m_bFLASHLIGHTSHADOWS; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nWRITEWATERFOGTODESTALPHA ) + ( 2 * m_nPIXELFOGTYPE ) + ( 4 * m_nWRITE_DEPTH_TO_DESTALPHA ) + ( 8 * m_nFLASHLIGHTSHADOWS ) + 0; + } +}; +#define shaderDynamicTest_sdk_worldtwotextureblend_ps20b psh_forgot_to_set_dynamic_WRITEWATERFOGTODESTALPHA + psh_forgot_to_set_dynamic_PIXELFOGTYPE + psh_forgot_to_set_dynamic_WRITE_DEPTH_TO_DESTALPHA + psh_forgot_to_set_dynamic_FLASHLIGHTSHADOWS + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20.inc new file mode 100644 index 00000000..01b921ca --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class blurgaussian_3x3_ps20_Static_Index +{ +public: + blurgaussian_3x3_ps20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_blurgaussian_3x3_ps20 0 +class blurgaussian_3x3_ps20_Dynamic_Index +{ +public: + blurgaussian_3x3_ps20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_blurgaussian_3x3_ps20 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20b.inc new file mode 100644 index 00000000..d5298ae8 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/blurgaussian_3x3_ps20b.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class blurgaussian_3x3_ps20b_Static_Index +{ +public: + blurgaussian_3x3_ps20b_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_blurgaussian_3x3_ps20b 0 +class blurgaussian_3x3_ps20b_Dynamic_Index +{ +public: + blurgaussian_3x3_ps20b_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_blurgaussian_3x3_ps20b 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_ps20b.inc new file mode 100644 index 00000000..99961231 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_ps20b.inc @@ -0,0 +1,60 @@ +#include "shaderlib/cshader.h" +class depth_of_field_ps20b_Static_Index +{ +public: + depth_of_field_ps20b_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_depth_of_field_ps20b 0 +class depth_of_field_ps20b_Dynamic_Index +{ +private: + int m_nQUALITY; +#ifdef _DEBUG + bool m_bQUALITY; +#endif +public: + void SetQUALITY( int i ) + { + Assert( i >= 0 && i <= 3 ); + m_nQUALITY = i; +#ifdef _DEBUG + m_bQUALITY = true; +#endif + } + void SetQUALITY( bool i ) + { + m_nQUALITY = i ? 1 : 0; +#ifdef _DEBUG + m_bQUALITY = true; +#endif + } +public: + depth_of_field_ps20b_Dynamic_Index() + { +#ifdef _DEBUG + m_bQUALITY = false; +#endif // _DEBUG + m_nQUALITY = 0; + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG + bool bAllDynamicVarsDefined = m_bQUALITY; + Assert( bAllDynamicVarsDefined ); +#endif // _DEBUG + return ( 1 * m_nQUALITY ) + 0; + } +}; +#define shaderDynamicTest_depth_of_field_ps20b psh_forgot_to_set_dynamic_QUALITY + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_vs20.inc new file mode 100644 index 00000000..a014d1b9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/depth_of_field_vs20.inc @@ -0,0 +1,33 @@ +#include "shaderlib/cshader.h" +class depth_of_field_vs20_Static_Index +{ +public: + depth_of_field_vs20_Static_Index( ) + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderStaticTest_depth_of_field_vs20 0 +class depth_of_field_vs20_Dynamic_Index +{ +public: + depth_of_field_vs20_Dynamic_Index() + { + } + int GetIndex() + { + // Asserts to make sure that we aren't using any skipped combinations. + // Asserts to make sure that we are setting all of the combination vars. +#ifdef _DEBUG +#endif // _DEBUG + return 0; + } +}; +#define shaderDynamicTest_depth_of_field_vs20 0 diff --git a/mp/src/materialsystem/stdshaders/game_shader_dx9_base.vpc b/mp/src/materialsystem/stdshaders/game_shader_dx9_base.vpc index b89578e8..5e6c047c 100644 --- a/mp/src/materialsystem/stdshaders/game_shader_dx9_base.vpc +++ b/mp/src/materialsystem/stdshaders/game_shader_dx9_base.vpc @@ -8,6 +8,9 @@ $Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin" $Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" +// Mapbase shaders +$Include "$SRCDIR\materialsystem\stdshaders\game_shader_dx9_mapbase.vpc" [$MAPBASE] + $Configuration "Debug" { $General @@ -34,7 +37,7 @@ $Configuration $AdditionalIncludeDirectories "$BASE;fxctmp9;vshtmp9;" [$WIN32||$POSIX] // $AdditionalIncludeDirectories "$BASE;..\..\dx9sdk\include" [$WIN32] $AdditionalIncludeDirectories "$BASE;fxctmp9_360;vshtmp9_360" [$X360] - $PreprocessorDefinitions "$BASE;STDSHADER_DX9_DLL_EXPORT;FAST_MATERIALVAR_ACCESS;GAME_SHADER_DLL" + $PreprocessorDefinitions "$BASE;STDSHADER_DX9_DLL_EXPORT;FAST_MATERIALVAR_ACCESS" $PreprocessorDefinitions "$BASE;USE_ACTUAL_DX" [($WIN32||$X360) && !$GL] } diff --git a/mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc b/mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc new file mode 100644 index 00000000..40d64143 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// game_shader_dx9_mapbase.vpc +// +// Project Script for Mapbase shader changes +//----------------------------------------------------------------------------- + +$Configuration +{ + $Compiler + { + // https://developer.valvesoftware.com/wiki/Parallax_Corrected_Cubemaps + $PreprocessorDefinitions "$BASE;PARALLAX_CORRECTED_CUBEMAPS" + } +} + +$Project +{ + $Folder "Source Files" + { + $File "cloak_blended_pass_helper.cpp" + $File "emissive_scroll_blended_pass_helper.cpp" + $File "flesh_interior_blended_pass_helper.cpp" + $File "lightmappedgeneric_dx9.cpp" + $File "lightmappedgeneric_dx9_helper.cpp" + $File "lightmappedreflective.cpp" + $File "skin_dx9_helper.cpp" + $File "unlitgeneric_dx9.cpp" + $File "vertexlitgeneric_dx9.cpp" + $File "vertexlitgeneric_dx9_helper.cpp" + + $File "common_flashlight_fxc.h" + $File "lightmappedgeneric_dx9_helper.h" + $File "lightmappedgeneric_ps2_3_x.h" + + $File "worldtwotextureblend.cpp" + $File "worldvertextransition.cpp" + $File "worldvertextransition_dx8_helper.cpp" + $File "refract.cpp" + $File "refract_dx9_helper.cpp" + + $File "water.cpp" + $File "depthwrite.cpp" + //$File "cable_dx9.cpp" + $File "splinerope.cpp" + + //$File "eyes.cpp" + $File "eyes_dx9.cpp" + $File "eyes_dx8_dx9_helper.cpp" + $File "eyeball.cpp" + $File "eyeglint_dx9.cpp" + $File "eye_refract.cpp" + $File "eye_refract_helper.cpp" + $File "teeth.cpp" + + //$File "sprite.cpp" + $File "sprite_dx9.cpp" + + $File "decalmodulate_dx9.cpp" + + $File "unlittwotexture_dx9.cpp" + $File "MonitorScreen_dx9.cpp" + $File "shatteredglass.cpp" + $File "windowimposter_dx90.cpp" + + $File "engine_post_dx9.cpp" + $File "depthoffield_dx9.cpp" + } + + //$Shaders "mapbase_dx9_20b.txt" + //$Shaders "mapbase_dx9_30.txt" +} diff --git a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9.cpp b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9.cpp index bac124a8..e5d2916c 100644 --- a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9.cpp @@ -9,12 +9,11 @@ #include "BaseVSShader.h" #include "convar.h" #include "lightmappedgeneric_dx9_helper.h" +#include "SDK_lightmappedgeneric_ps20b.inc" +#include "SDK_lightmappedgeneric_vs20.inc" -static LightmappedGeneric_DX9_Vars_t s_info; - - -BEGIN_VS_SHADER( LightmappedGeneric, - "Help for LightmappedGeneric" ) +BEGIN_VS_SHADER( SDK_LightmappedGeneric, + "Help for SDK_LightmappedGeneric" ) BEGIN_SHADER_PARAMS SHADER_PARAM( ALBEDO, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "albedo (Base texture with no baked lighting)" ) @@ -22,8 +21,9 @@ BEGIN_VS_SHADER( LightmappedGeneric, SHADER_PARAM( DETAIL, SHADER_PARAM_TYPE_TEXTURE, "shadertest/detail", "detail texture" ) SHADER_PARAM( DETAILFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $detail" ) SHADER_PARAM( DETAILSCALE, SHADER_PARAM_TYPE_FLOAT, "4", "scale of the detail texture" ) - - SHADER_PARAM( ALPHA2, SHADER_PARAM_TYPE_FLOAT, "1", "" ) + SHADER_PARAM( TRANSLUCENT, SHADER_PARAM_TYPE_BOOL, "0", "" ) + SHADER_PARAM( ALPHATEST, SHADER_PARAM_TYPE_BOOL, "0", "" ) + SHADER_PARAM( ALPHATESTREFERENCE, SHADER_PARAM_TYPE_FLOAT, "0.5", "" ) // detail (multi-) texturing SHADER_PARAM( DETAILBLENDMODE, SHADER_PARAM_TYPE_INTEGER, "0", "mode for combining detail texture with base. 0=normal, 1= additive, 2=alpha blend detail over base, 3=crossfade" ) @@ -49,6 +49,10 @@ BEGIN_VS_SHADER( LightmappedGeneric, SHADER_PARAM( BUMPMASK, SHADER_PARAM_TYPE_TEXTURE, "models/shadertest/shader3_normal", "bump map" ) SHADER_PARAM( BASETEXTURE2, SHADER_PARAM_TYPE_TEXTURE, "shadertest/lightmappedtexture", "Blended texture" ) SHADER_PARAM( FRAME2, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $basetexture2" ) +#ifdef MAPBASE + // This needs to be a SHADER_PARAM_TYPE_STRING so it isn't considered "defined" by default. + SHADER_PARAM( BASETEXTURETRANSFORM2, SHADER_PARAM_TYPE_STRING, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$basetexture2 texcoord transform" ) +#endif SHADER_PARAM( BASETEXTURENOENVMAP, SHADER_PARAM_TYPE_BOOL, "0", "" ) SHADER_PARAM( BASETEXTURE2NOENVMAP, SHADER_PARAM_TYPE_BOOL, "0", "" ) SHADER_PARAM( DETAIL_ALPHA_MASK_BASE_TEXTURE, SHADER_PARAM_TYPE_BOOL, "0", @@ -56,15 +60,10 @@ BEGIN_VS_SHADER( LightmappedGeneric, "detail alpha=1, you get detail*base*lightmap" ) SHADER_PARAM( LIGHTWARPTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "light munging lookup texture" ) SHADER_PARAM( BLENDMODULATETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "texture to use r/g channels for blend range for" ) - SHADER_PARAM( MASKEDBLENDING, SHADER_PARAM_TYPE_INTEGER, "0", "blend using texture with no vertex alpha. For using texture blending on non-displacements" ) SHADER_PARAM( BLENDMASKTRANSFORM, SHADER_PARAM_TYPE_MATRIX, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$blendmodulatetexture texcoord transform" ) + SHADER_PARAM( MASKEDBLENDING, SHADER_PARAM_TYPE_INTEGER, "0", "blend using texture with no vertex alpha. For using texture blending on non-displacements" ) SHADER_PARAM( SSBUMP, SHADER_PARAM_TYPE_INTEGER, "0", "whether or not to use alternate bumpmap format with height" ) SHADER_PARAM( SEAMLESS_SCALE, SHADER_PARAM_TYPE_FLOAT, "0", "Scale factor for 'seamless' texture mapping. 0 means to use ordinary mapping" ) - SHADER_PARAM( ALPHATESTREFERENCE, SHADER_PARAM_TYPE_FLOAT, "0.0", "" ) - - SHADER_PARAM( SOFTEDGES, SHADER_PARAM_TYPE_BOOL, "0", "Enable soft edges to distance coded textures.") - SHADER_PARAM( EDGESOFTNESSSTART, SHADER_PARAM_TYPE_FLOAT, "0.6", "Start value for soft edges for distancealpha."); - SHADER_PARAM( EDGESOFTNESSEND, SHADER_PARAM_TYPE_FLOAT, "0.5", "End value for soft edges for distancealpha."); SHADER_PARAM( OUTLINE, SHADER_PARAM_TYPE_BOOL, "0", "Enable outline for distance coded textures.") SHADER_PARAM( OUTLINECOLOR, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "color of outline for distance coded images." ) @@ -73,6 +72,24 @@ BEGIN_VS_SHADER( LightmappedGeneric, SHADER_PARAM( OUTLINESTART1, SHADER_PARAM_TYPE_FLOAT, "0.0", "inner start value for outline") SHADER_PARAM( OUTLINEEND0, SHADER_PARAM_TYPE_FLOAT, "0.0", "inner end value for outline") SHADER_PARAM( OUTLINEEND1, SHADER_PARAM_TYPE_FLOAT, "0.0", "outer end value for outline") + + SHADER_PARAM( SOFTEDGES, SHADER_PARAM_TYPE_BOOL, "0", "Enable soft edges to distance coded textures.") + SHADER_PARAM( EDGESOFTNESSSTART, SHADER_PARAM_TYPE_FLOAT, "0.6", "Start value for soft edges for distancealpha.") + SHADER_PARAM( EDGESOFTNESSEND, SHADER_PARAM_TYPE_FLOAT, "0.5", "End value for soft edges for distancealpha.") + + SHADER_PARAM( PHONG, SHADER_PARAM_TYPE_BOOL, "0", "enables phong lighting" ) + SHADER_PARAM( PHONGBOOST, SHADER_PARAM_TYPE_FLOAT, "1.0", "Phong overbrightening factor (specular mask channel should be authored to account for this)" ) + SHADER_PARAM( PHONGFRESNELRANGES, SHADER_PARAM_TYPE_VEC3, "[0 0.5 1]", "Parameters for remapping fresnel output" ) + SHADER_PARAM( PHONGEXPONENT, SHADER_PARAM_TYPE_FLOAT, "5.0", "Phong exponent for local specular lights" ) + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + SHADER_PARAM( ENVMAPPARALLAX, SHADER_PARAM_TYPE_BOOL, "0", "Enables parallax correction code for env_cubemaps" ) + SHADER_PARAM( ENVMAPPARALLAXOBB1, SHADER_PARAM_TYPE_VEC4, "[1 0 0 0]", "The first line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB2, SHADER_PARAM_TYPE_VEC4, "[0 1 0 0]", "The second line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB3, SHADER_PARAM_TYPE_VEC4, "[0 0 1 0]", "The third line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPORIGIN, SHADER_PARAM_TYPE_VEC3, "[0 0 0]", "The world space position of the env_cubemap being corrected" ) +#endif END_SHADER_PARAMS void SetupVars( LightmappedGeneric_DX9_Vars_t& info ) @@ -83,8 +100,6 @@ END_SHADER_PARAMS info.m_nAlbedo = ALBEDO; info.m_nSelfIllumTint = SELFILLUMTINT; - info.m_nAlpha2 = ALPHA2; - info.m_nDetail = DETAIL; info.m_nDetailFrame = DETAILFRAME; info.m_nDetailScale = DETAILSCALE; @@ -111,6 +126,9 @@ END_SHADER_PARAMS info.m_nBumpMask = BUMPMASK; info.m_nBaseTexture2 = BASETEXTURE2; info.m_nBaseTexture2Frame = FRAME2; +#ifdef MAPBASE + info.m_nBaseTexture2Transform = BASETEXTURETRANSFORM2; +#endif info.m_nBaseTextureNoEnvmap = BASETEXTURENOENVMAP; info.m_nBaseTexture2NoEnvmap = BASETEXTURE2NOENVMAP; info.m_nDetailAlphaMaskBaseTexture = DETAIL_ALPHA_MASK_BASE_TEXTURE; @@ -134,6 +152,20 @@ END_SHADER_PARAMS info.m_nOutlineStart1 = OUTLINESTART1; info.m_nOutlineEnd0 = OUTLINEEND0; info.m_nOutlineEnd1 = OUTLINEEND1; + + info.m_nPhong = PHONG; + info.m_nPhongBoost = PHONGBOOST; + info.m_nPhongFresnelRanges = PHONGFRESNELRANGES; + info.m_nPhongExponent = PHONGEXPONENT; + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + info.m_nEnvmapParallax = ENVMAPPARALLAX; + info.m_nEnvmapParallaxObb1 = ENVMAPPARALLAXOBB1; + info.m_nEnvmapParallaxObb2 = ENVMAPPARALLAXOBB2; + info.m_nEnvmapParallaxObb3 = ENVMAPPARALLAXOBB3; + info.m_nEnvmapOrigin = ENVMAPORIGIN; +#endif } SHADER_FALLBACK @@ -147,18 +179,22 @@ END_SHADER_PARAMS // Set up anything that is necessary to make decisions in SHADER_FALLBACK. SHADER_INIT_PARAMS() { - SetupVars( s_info ); - InitParamsLightmappedGeneric_DX9( this, params, pMaterialName, s_info ); + LightmappedGeneric_DX9_Vars_t info; + SetupVars( info ); + InitParamsLightmappedGeneric_DX9( this, params, pMaterialName, info ); } SHADER_INIT { - SetupVars( s_info ); - InitLightmappedGeneric_DX9( this, params, s_info ); + LightmappedGeneric_DX9_Vars_t info; + SetupVars( info ); + InitLightmappedGeneric_DX9( this, params, info ); } SHADER_DRAW { - DrawLightmappedGeneric_DX9( this, params, pShaderAPI, pShaderShadow, s_info, pContextDataPtr ); + LightmappedGeneric_DX9_Vars_t info; + SetupVars( info ); + DrawLightmappedGeneric_DX9( this, params, pShaderAPI, pShaderShadow, info, pContextDataPtr ); } -END_SHADER +END_SHADER \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp index 9da6716d..b2a8ddcc 100644 --- a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp @@ -9,10 +9,19 @@ #include "lightmappedgeneric_dx9_helper.h" #include "BaseVSShader.h" #include "commandbuilder.h" +#include "cpp_shader_constant_register_map.h" #include "convar.h" -#include "lightmappedgeneric_ps20.inc" -#include "lightmappedgeneric_vs20.inc" -#include "lightmappedgeneric_ps20b.inc" +#include "SDK_lightmappedgeneric_ps20b.inc" +#include "SDK_lightmappedgeneric_vs20.inc" +#include "SDK_lightmappedgeneric_ps30.inc" +#include "SDK_lightmappedgeneric_vs30.inc" + +#include "SDK_lightmappedgeneric_flashlight_vs20.inc" +#include "SDK_lightmappedgeneric_flashlight_vs30.inc" +#include "SDK_lightmappedgeneric_flashlight_ps20.inc" +#include "SDK_lightmappedgeneric_flashlight_ps20b.inc" +#include "SDK_lightmappedgeneric_flashlight_ps30.inc" + #include "tier0/memdbgon.h" @@ -20,7 +29,11 @@ ConVar mat_disable_lightwarp( "mat_disable_lightwarp", "0" ); ConVar mat_disable_fancy_blending( "mat_disable_fancy_blending", "0" ); ConVar mat_fullbright( "mat_fullbright","0", FCVAR_CHEAT ); ConVar my_mat_fullbright( "mat_fullbright","0", FCVAR_CHEAT ); -extern ConVar r_flashlight_version2; + +ConVar mat_enable_lightmapped_phong( "mat_enable_lightmapped_phong", "1", FCVAR_ARCHIVE, "If 1, allow phong on world brushes. If 0, disallow. mat_force_lightmapped_phong does not work if this value is 0." ); +ConVar mat_force_lightmapped_phong( "mat_force_lightmapped_phong", "0", FCVAR_CHEAT, "Forces the use of phong on all LightmappedAdv textures, regardless of setting in VMT." ); +ConVar mat_force_lightmapped_phong_boost( "mat_force_lightmapped_phong_boost", "5.0", FCVAR_CHEAT ); +ConVar mat_force_lightmapped_phong_exp( "mat_force_lightmapped_phong_exp", "50.0", FCVAR_CHEAT ); class CLightmappedGeneric_DX9_Context : public CBasePerMaterialContextData { @@ -164,6 +177,9 @@ void InitParamsLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** pa if( !g_pConfig->UseSpecular() && params[info.m_nEnvmap]->IsDefined() && params[info.m_nBaseTexture]->IsDefined() ) { params[info.m_nEnvmap]->SetUndefined(); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + params[info.m_nEnvmapParallax]->SetUndefined(); +#endif } if( !params[info.m_nBaseTextureNoEnvmap]->IsDefined() ) @@ -185,8 +201,61 @@ void InitParamsLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** pa InitFloatParam( info.m_nEdgeSoftnessStart, params, 0.5 ); InitFloatParam( info.m_nEdgeSoftnessEnd, params, 0.5 ); InitFloatParam( info.m_nOutlineAlpha, params, 1.0 ); + + if ( !params[info.m_nPhong]->IsDefined() || !mat_enable_lightmapped_phong.GetBool() ) + { + params[info.m_nPhong]->SetIntValue( 0 ); + } + if ( !params[info.m_nPhongBoost]->IsDefined() ) + { + params[info.m_nPhongBoost]->SetFloatValue( 1.0 ); + } + if ( !params[info.m_nPhongFresnelRanges]->IsDefined() ) + { + params[info.m_nPhongFresnelRanges]->SetVecValue( 0.0, 0.5, 1.0 ); + } + if ( !params[info.m_nPhongExponent]->IsDefined() ) + { + params[info.m_nPhongExponent]->SetFloatValue( 5.0 ); + } + + if ( params[info.m_nPhong]->GetIntValue() && mat_enable_lightmapped_phong.GetBool() ) + { + if ( pShader->CanUseEditorMaterials() ) + { + params[info.m_nPhong]->SetIntValue( 0 ); + } + else if ( !params[info.m_nEnvmapMaskTransform]->MatrixIsIdentity() ) + { + Warning( "Warning! material %s: $envmapmasktransform and $phong are mutually exclusive. Disabling phong..\n", pMaterialName ); + params[info.m_nPhong]->SetIntValue( 0 ); + } + } + else if ( mat_force_lightmapped_phong.GetBool() && mat_enable_lightmapped_phong.GetBool() && + params[info.m_nEnvmapMaskTransform]->MatrixIsIdentity() ) + { + params[info.m_nPhong]->SetIntValue( 1 ); + params[info.m_nPhongBoost]->SetFloatValue( mat_force_lightmapped_phong_boost.GetFloat() ); + params[info.m_nPhongFresnelRanges]->SetVecValue( 0.0, 0.5, 1.0 ); + params[info.m_nPhongExponent]->SetFloatValue( mat_force_lightmapped_phong_exp.GetFloat() ); + } } +#ifdef MAPBASE +// Created for the missing cubemap solution below +void LoadLightmappedGenericEnvmap( CBaseVSShader *pShader, IMaterialVar** params, LightmappedGeneric_DX9_Vars_t &info ) +{ + if ( !IS_FLAG_SET(MATERIAL_VAR_ENVMAPSPHERE) ) + { + pShader->LoadCubeMap( info.m_nEnvmap, g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ? TEXTUREFLAGS_SRGB : 0 ); + } + else + { + pShader->LoadTexture( info.m_nEnvmap ); + } +} +#endif + void InitLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, LightmappedGeneric_DX9_Vars_t &info ) { if ( g_pConfig->UseBumpmapping() && params[info.m_nBumpmap]->IsDefined() ) @@ -249,6 +318,20 @@ void InitLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, if (params[info.m_nEnvmap]->IsDefined()) { +#ifdef MAPBASE + LoadLightmappedGenericEnvmap( pShader, params, info ); + + if (mat_specular_disable_on_missing.GetBool()) + { + // Revert to defaultcubemap when the envmap texture is missing + // (should be equivalent to toolsblack in Mapbase) + if (params[info.m_nEnvmap]->GetTextureValue()->IsError()) + { + params[info.m_nEnvmap]->SetStringValue( "engine/defaultcubemap" ); + LoadLightmappedGenericEnvmap( pShader, params, info ); + } + } +#else if ( !IS_FLAG_SET(MATERIAL_VAR_ENVMAPSPHERE) ) { pShader->LoadCubeMap( info.m_nEnvmap, g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ? TEXTUREFLAGS_SRGB : 0 ); @@ -257,6 +340,7 @@ void InitLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, { pShader->LoadTexture( info.m_nEnvmap ); } +#endif if ( !g_pHardwareConfig->SupportsCubeMaps() ) { @@ -277,6 +361,412 @@ void InitLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, SET_FLAGS2( MATERIAL_VAR2_NEEDS_TANGENT_SPACES ); } +void DrawLightmappedGenericFlashlight_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShaderDynamicAPI *pShaderAPI, + IShaderShadow* pShaderShadow, LightmappedGenericFlashlight_DX9_Vars_t &vars ) +{ + Assert( vars.m_bLightmappedGeneric ); + + bool bBump2 = vars.m_bWorldVertexTransition && vars.m_bBump && vars.m_nBumpmap2Var != -1 && params[vars.m_nBumpmap2Var]->IsTexture(); + bool bSeamless = vars.m_fSeamlessScale != 0.0; + bool bDetail = (vars.m_nDetailVar != -1) && params[vars.m_nDetailVar]->IsDefined() && (vars.m_nDetailScale != -1); + bool bPhong = (vars.m_nPhong != -1) && (params[vars.m_nPhong]->GetIntValue() != 0); +#ifdef MAPBASE + bool hasBaseTextureTransform2 = (vars.m_nBaseTexture2TransformVar != -1) && params[vars.m_nBaseTexture2TransformVar]->IsDefined() && params[vars.m_nBaseTexture2Var]->IsTexture(); + bool bHasBlendModulateTexture = + (vars.m_nBlendModulateTexture != -1) && + (params[vars.m_nBlendModulateTexture]->IsTexture()); +#endif + + int nDetailBlendMode = 0; + if ( bDetail ) + { + nDetailBlendMode = GetIntParam( vars.m_nDetailTextureCombineMode, params ); + nDetailBlendMode = nDetailBlendMode > 1 ? 1 : nDetailBlendMode; + } + + PhongMaskVariant_t nPhongMaskVariant = PHONGMASK_NONE; + if ( bPhong ) + { + if ( IS_FLAG_SET(MATERIAL_VAR_BASEALPHAENVMAPMASK) ) + { + nPhongMaskVariant = PHONGMASK_BASEALPHA; + } + else if ( IS_FLAG_SET(MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK) ) + { + nPhongMaskVariant = PHONGMASK_NORMALALPHA; + } + else if ( params[vars.m_nPhongMask]->IsDefined() ) + { + nPhongMaskVariant = PHONGMASK_STANDALONE; + } + } + + if( pShaderShadow ) + { + pShader->SetInitialShadowState(); + pShaderShadow->EnableDepthWrites( false ); + pShaderShadow->EnableAlphaWrites( false ); + + // Alpha blend + pShader->SetAdditiveBlendingShadowState( BASETEXTURE, true ); + + // Alpha test + pShaderShadow->EnableAlphaTest( IS_FLAG_SET( MATERIAL_VAR_ALPHATEST ) ); + if ( vars.m_nAlphaTestReference != -1 && params[vars.m_nAlphaTestReference]->GetFloatValue() > 0.0f ) + { + pShaderShadow->AlphaFunc( SHADER_ALPHAFUNC_GEQUAL, params[vars.m_nAlphaTestReference]->GetFloatValue() ); + } + + // Spot sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + + // Base sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, true ); + + // Normalizing cubemap sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER2, true ); + + // Normalizing cubemap sampler2 or normal map sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER3, true ); + + // RandomRotation sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER5, true ); + + // Flashlight depth sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER7, true ); + pShaderShadow->SetShadowDepthFiltering( SHADER_SAMPLER7 ); + + if( vars.m_bWorldVertexTransition ) + { + // $basetexture2 + pShaderShadow->EnableTexture( SHADER_SAMPLER4, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER4, true ); + } + if( bBump2 ) + { + // Normalmap2 sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER6, true ); + } + if( bDetail ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER8, true ); // detail sampler + if ( nDetailBlendMode != 0 ) //Not Mod2X + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER8, true ); + } + if( nPhongMaskVariant == PHONGMASK_STANDALONE ) + { + // phong mask sampler + pShaderShadow->EnableTexture( SHADER_SAMPLER9, true ); + } + +#ifdef MAPBASE + if ( bHasBlendModulateTexture ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER10, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER10, false ); + } +#endif + + pShaderShadow->EnableSRGBWrite( true ); + + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( WORLDVERTEXTRANSITION, vars.m_bWorldVertexTransition ); + SET_STATIC_VERTEX_SHADER_COMBO( NORMALMAP, vars.m_bBump ); + SET_STATIC_VERTEX_SHADER_COMBO( SEAMLESS, bSeamless ); + SET_STATIC_VERTEX_SHADER_COMBO( DETAIL, bDetail ); + SET_STATIC_VERTEX_SHADER_COMBO( PHONG, bPhong ); +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs30 ); + } + else + { + DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( WORLDVERTEXTRANSITION, vars.m_bWorldVertexTransition ); + SET_STATIC_VERTEX_SHADER_COMBO( NORMALMAP, vars.m_bBump ); + SET_STATIC_VERTEX_SHADER_COMBO( SEAMLESS, bSeamless ); + SET_STATIC_VERTEX_SHADER_COMBO( DETAIL, bDetail ); + SET_STATIC_VERTEX_SHADER_COMBO( PHONG, bPhong ); +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs20 ); + } + + unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL; + if( vars.m_bBump ) + { + flags |= VERTEX_TANGENT_S | VERTEX_TANGENT_T; + } + int numTexCoords = 1; + if( vars.m_bWorldVertexTransition ) + { + flags |= VERTEX_COLOR; + numTexCoords = 2; // need lightmap texcoords to get alpha. + } + pShaderShadow->VertexShaderVertexFormat( flags, numTexCoords, 0, 0 ); + + int nBumpMapVariant = 0; + if ( vars.m_bBump ) + { + nBumpMapVariant = ( vars.m_bSSBump ) ? 2 : 1; + } + + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps30 ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAP, nBumpMapVariant ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAP2, bBump2 ); + SET_STATIC_PIXEL_SHADER_COMBO( WORLDVERTEXTRANSITION, vars.m_bWorldVertexTransition ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( FANCY_BLENDING, bHasBlendModulateTexture ); +#endif + SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamless ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bDetail ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, g_pHardwareConfig->GetShadowFilterMode() ); + SET_STATIC_PIXEL_SHADER_COMBO( PHONG, bPhong ); + SET_STATIC_PIXEL_SHADER_COMBO( PHONGMASK, nPhongMaskVariant ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps30 ); + } + else if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAP, nBumpMapVariant ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAP2, bBump2 ); + SET_STATIC_PIXEL_SHADER_COMBO( WORLDVERTEXTRANSITION, vars.m_bWorldVertexTransition ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( FANCY_BLENDING, bHasBlendModulateTexture ); +#endif + SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamless ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bDetail ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, g_pHardwareConfig->GetShadowFilterMode() ); + SET_STATIC_PIXEL_SHADER_COMBO( PHONG, bPhong ); + SET_STATIC_PIXEL_SHADER_COMBO( PHONGMASK, nPhongMaskVariant ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAP, nBumpMapVariant ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAP2, bBump2 ); + SET_STATIC_PIXEL_SHADER_COMBO( WORLDVERTEXTRANSITION, vars.m_bWorldVertexTransition ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( FANCY_BLENDING, bHasBlendModulateTexture ); +#endif + SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamless ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bDetail ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20 ); + } + pShader->FogToBlack(); + } + else + { + VMatrix worldToTexture; + ITexture *pFlashlightDepthTexture; + FlashlightState_t flashlightState = pShaderAPI->GetFlashlightStateEx( worldToTexture, &pFlashlightDepthTexture ); + + if ( pFlashlightDepthTexture == NULL ) + { + const int iFlashlightShadowIndex = ( flashlightState.m_nShadowQuality >> 16 ) - 1; + + if ( iFlashlightShadowIndex >= 0 + && iFlashlightShadowIndex <= ( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_LAST - INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST ) ) + { + pFlashlightDepthTexture = (ITexture*)pShaderAPI->GetIntRenderingParameter( INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST + iFlashlightShadowIndex ); + } + } + + SetFlashLightColorFromState( flashlightState, pShaderAPI ); + + pShader->BindTexture( SHADER_SAMPLER0, flashlightState.m_pSpotlightTexture, flashlightState.m_nSpotlightTextureFrame ); + + if( pFlashlightDepthTexture && g_pConfig->ShadowDepthTexture() && flashlightState.m_bEnableShadows ) + { + pShader->BindTexture( SHADER_SAMPLER7, pFlashlightDepthTexture, 0 ); + pShaderAPI->BindStandardTexture( SHADER_SAMPLER5, TEXTURE_SHADOW_NOISE_2D ); + + // Tweaks associated with a given flashlight + float tweaks[4]; + tweaks[0] = ShadowFilterFromState( flashlightState ); + tweaks[1] = ShadowAttenFromState( flashlightState ); + pShader->HashShadow2DJitter( flashlightState.m_flShadowJitterSeed, &tweaks[2], &tweaks[3] ); + pShaderAPI->SetPixelShaderConstant( PSREG_ENVMAP_TINT__SHADOW_TWEAKS, tweaks, 1 ); + + // Dimensions of screen, used for screen-space noise map sampling + float vScreenScale[4] = {1280.0f / 32.0f, 720.0f / 32.0f, 0, 0}; + int nWidth, nHeight; + pShaderAPI->GetBackBufferDimensions( nWidth, nHeight ); + vScreenScale[0] = (float) nWidth / 32.0f; + vScreenScale[1] = (float) nHeight / 32.0f; + pShaderAPI->SetPixelShaderConstant( PSREG_FLASHLIGHT_SCREEN_SCALE, vScreenScale, 1 ); + } + + if( params[BASETEXTURE]->IsTexture() && mat_fullbright.GetInt() != 2 ) + { + pShader->BindTexture( SHADER_SAMPLER1, BASETEXTURE, FRAME ); + } + else + { + pShaderAPI->BindStandardTexture( SHADER_SAMPLER1, TEXTURE_GREY ); + } + if( vars.m_bWorldVertexTransition ) + { + Assert( vars.m_nBaseTexture2Var >= 0 && vars.m_nBaseTexture2FrameVar >= 0 ); + pShader->BindTexture( SHADER_SAMPLER4, vars.m_nBaseTexture2Var, vars.m_nBaseTexture2FrameVar ); + } + pShaderAPI->BindStandardTexture( SHADER_SAMPLER2, TEXTURE_NORMALIZATION_CUBEMAP ); + if( vars.m_bBump ) + { + pShader->BindTexture( SHADER_SAMPLER3, vars.m_nBumpmapVar, vars.m_nBumpmapFrame ); + } + else + { + pShaderAPI->BindStandardTexture( SHADER_SAMPLER3, TEXTURE_NORMALIZATION_CUBEMAP ); + } + + if( bDetail ) + { + pShader->BindTexture( SHADER_SAMPLER8, vars.m_nDetailVar ); + } + + if( bBump2 ) + { + pShader->BindTexture( SHADER_SAMPLER6, vars.m_nBumpmap2Var, vars.m_nBumpmap2Frame ); + } + + if( nPhongMaskVariant == PHONGMASK_STANDALONE ) + { + pShader->BindTexture( SHADER_SAMPLER9, vars.m_nPhongMask, vars.m_nPhongMaskFrame ); + } + +#ifdef MAPBASE + if ( bHasBlendModulateTexture ) + { + pShader->BindTexture( SHADER_SAMPLER10, vars.m_nBlendModulateTexture ); + } +#endif + + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs30 ); + } + else + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_flashlight_vs20 ); + } + + if ( bSeamless ) + { + float const0[4]={ vars.m_fSeamlessScale,0,0,0}; + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, const0 ); + } + + if ( bDetail ) + { + float vDetailConstants[4] = {1,1,1,1}; + + if ( vars.m_nDetailTint != -1 ) + { + params[vars.m_nDetailTint]->GetVecValue( vDetailConstants, 3 ); + } + + if ( vars.m_nDetailTextureBlendFactor != -1 ) + { + vDetailConstants[3] = params[vars.m_nDetailTextureBlendFactor]->GetFloatValue(); + } + + pShaderAPI->SetPixelShaderConstant( 0, vDetailConstants, 1 ); + } + + if ( bPhong ) + { + float vEyePos[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos ); + vEyePos[3] = 0.0f; + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, vEyePos ); + } + +#ifdef MAPBASE + else if ( hasBaseTextureTransform2 ) + { + pShader->SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, vars.m_nBaseTexture2TransformVar ); + } +#endif + + pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); + + float vEyePos_SpecExponent[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent ); + vEyePos_SpecExponent[3] = params[vars.m_nPhongExponent]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); + + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps30 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, flashlightState.m_bEnableShadows ); + SET_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps30 ); + } + else if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, flashlightState.m_bEnableShadows ); + SET_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_flashlight_ps20 ); + } + + float atten[4]; // Set the flashlight attenuation factors + atten[0] = flashlightState.m_fConstantAtten; + atten[1] = flashlightState.m_fLinearAtten; + atten[2] = flashlightState.m_fQuadraticAtten; + atten[3] = flashlightState.m_FarZ; + /*atten[3] = flashlightState.m_FarZAtten;*/ + pShaderAPI->SetPixelShaderConstant( PSREG_FLASHLIGHT_ATTENUATION, atten, 1 ); + + float lightPos[4]; + lightPos[0] = flashlightState.m_vecLightOrigin[0]; + lightPos[1] = flashlightState.m_vecLightOrigin[1]; + lightPos[2] = flashlightState.m_vecLightOrigin[2]; + lightPos[3] = 1.0f; + pShaderAPI->SetPixelShaderConstant( 1, lightPos, 1 ); + + float specParams[4]; + params[vars.m_nPhongFresnelRanges]->GetVecValue( specParams, 3 ); + specParams[3] = params[vars.m_nPhongBoost]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( PSREG_FRESNEL_SPEC_PARAMS, specParams, 1 ); + + pShader->SetFlashlightVertexShaderConstants( vars.m_bBump, vars.m_nBumpTransform, bDetail, vars.m_nDetailScale, bSeamless ? false : true ); + } + pShader->Draw(); +} + void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** params, bool hasFlashlight, IShaderDynamicAPI *pShaderAPI, IShaderShadow* pShaderShadow, LightmappedGeneric_DX9_Vars_t &info, @@ -303,21 +793,26 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** bool hasBump = ( params[info.m_nBumpmap]->IsTexture() ) && ( !g_pHardwareConfig->PreferReducedFillrate() ); bool hasSSBump = hasBump && (info.m_nSelfShadowedBumpFlag != -1) && ( params[info.m_nSelfShadowedBumpFlag]->GetIntValue() ); bool hasBaseTexture2 = hasBaseTexture && params[info.m_nBaseTexture2]->IsTexture(); +#ifdef MAPBASE + bool hasBaseTextureTransform2 = (info.m_nBaseTexture2Transform != -1) && params[info.m_nBaseTexture2Transform]->IsDefined() && hasBaseTexture2; +#endif bool hasLightWarpTexture = params[info.m_nLightWarpTexture]->IsTexture(); bool hasBump2 = hasBump && params[info.m_nBumpmap2]->IsTexture(); bool hasDetailTexture = params[info.m_nDetail]->IsTexture(); bool hasSelfIllum = IS_FLAG_SET( MATERIAL_VAR_SELFILLUM ); - bool hasBumpMask = hasBump && hasBump2 && params[info.m_nBumpMask]->IsTexture() && !hasSelfIllum && - !hasDetailTexture && !hasBaseTexture2 && (params[info.m_nBaseTextureNoEnvmap]->GetIntValue() == 0); bool bHasBlendModulateTexture = (info.m_nBlendModulateTexture != -1) && (params[info.m_nBlendModulateTexture]->IsTexture() ); bool hasNormalMapAlphaEnvmapMask = IS_FLAG_SET( MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + bool hasParallaxCorrection = params[info.m_nEnvmapParallax]->GetIntValue() > 0; +#endif if ( hasFlashlight && !IsX360() ) { // !!speed!! do this in the caller so we don't build struct every time - CBaseVSShader::DrawFlashlight_dx90_Vars_t vars; + LightmappedGenericFlashlight_DX9_Vars_t vars; vars.m_bBump = hasBump; vars.m_nBumpmapVar = info.m_nBumpmap; vars.m_nBumpmapFrame = info.m_nBumpFrame; @@ -328,6 +823,9 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** vars.m_bWorldVertexTransition = hasBaseTexture2; vars.m_nBaseTexture2Var = info.m_nBaseTexture2; vars.m_nBaseTexture2FrameVar = info.m_nBaseTexture2Frame; +#ifdef MAPBASE + vars.m_nBaseTexture2TransformVar = info.m_nBaseTexture2Transform; +#endif vars.m_nBumpmap2Var = info.m_nBumpmap2; vars.m_nBumpmap2Frame = info.m_nBumpFrame2; vars.m_nBump2Transform = info.m_nBumpTransform2; @@ -343,7 +841,19 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** vars.m_fSeamlessScale = params[info.m_nSeamlessMappingScale]->GetFloatValue(); else vars.m_fSeamlessScale = 0.0; - pShader->DrawFlashlight_dx90( params, pShaderAPI, pShaderShadow, vars ); + + vars.m_nPhong = info.m_nPhong; + vars.m_nPhongBoost = info.m_nPhongBoost; + vars.m_nPhongFresnelRanges = info.m_nPhongFresnelRanges; + vars.m_nPhongExponent = info.m_nPhongExponent; + vars.m_nPhongMask = info.m_nEnvmapMask; + vars.m_nPhongMaskFrame = info.m_nEnvmapMaskFrame; + +#ifdef MAPBASE + vars.m_nBlendModulateTexture = info.m_nBlendModulateTexture; +#endif + + DrawLightmappedGenericFlashlight_DX9_Internal( pShader, params, pShaderAPI, pShaderShadow, vars ); return; } @@ -369,6 +879,8 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** } } } + const bool hasBumpMask = hasBump && hasBump2 && params[info.m_nBumpMask]->IsTexture() && !hasSelfIllum && + !hasDetailTexture && !hasBaseTexture2 && (params[info.m_nBaseTextureNoEnvmap]->GetIntValue() == 0); int nNormalMaskDecodeMode = 0; if ( hasBumpMask && g_pHardwareConfig->SupportsNormalMapCompression() && g_pHardwareConfig->SupportsPixelShaders_2_b() ) @@ -380,9 +892,9 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** } } - bool bHasOutline = IsBoolSet( info.m_nOutline, params ); + const bool bHasOutline = false; //IsBoolSet( info.m_nOutline, params ); pContextData->m_bPixelShaderForceFastPathBecauseOutline = bHasOutline; - bool bHasSoftEdges = IsBoolSet( info.m_nSoftEdges, params ); + const bool bHasSoftEdges = false; //IsBoolSet( info.m_nSoftEdges, params ); bool hasEnvmapMask = params[info.m_nEnvmapMask]->IsTexture(); @@ -424,7 +936,7 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, params[info.m_nSeamlessMappingScale]->GetFloatValue(),0,0,0 ); } - staticCmdsBuf.StoreEyePosInPixelShaderConstant( 10 ); + //staticCmdsBuf.StoreEyePosInPixelShaderConstant( 10 ); staticCmdsBuf.SetPixelShaderFogParams( 11 ); staticCmdsBuf.End(); // now, copy buf @@ -546,6 +1058,13 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** pShaderShadow->SetShadowDepthFiltering( SHADER_SAMPLER14 ); pShaderShadow->EnableTexture( SHADER_SAMPLER15, true ); } + else if ( !hasFlashlight ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER14, true ); + pShaderShadow->SetShadowDepthFiltering( SHADER_SAMPLER14 ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER14, false ); + pShaderShadow->EnableTexture( SHADER_SAMPLER15, true ); + } if( hasVertexColor || hasBaseTexture2 || hasBump2 ) { @@ -570,26 +1089,94 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** bool bMaskedBlending=( (info.m_nMaskedBlending != -1) && (params[info.m_nMaskedBlending]->GetIntValue() != 0) ); - DECLARE_STATIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); - SET_STATIC_VERTEX_SHADER_COMBO( ENVMAP_MASK, hasEnvmapMask ); - SET_STATIC_VERTEX_SHADER_COMBO( TANGENTSPACE, params[info.m_nEnvmap]->IsTexture() ); - SET_STATIC_VERTEX_SHADER_COMBO( BUMPMAP, hasBump ); - SET_STATIC_VERTEX_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); - SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR ) ); - SET_STATIC_VERTEX_SHADER_COMBO( VERTEXALPHATEXBLENDFACTOR, hasBaseTexture2 || hasBump2 ); - SET_STATIC_VERTEX_SHADER_COMBO( BUMPMASK, hasBumpMask ); - bool bReliefMapping = false; //( bumpmap_variant == 2 ) && ( ! bSeamlessMapping ); - SET_STATIC_VERTEX_SHADER_COMBO( RELIEF_MAPPING, false );//bReliefMapping ); - SET_STATIC_VERTEX_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); -#ifdef _X360 - SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, hasFlashlight); -#endif - SET_STATIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) { - DECLARE_STATIC_PIXEL_SHADER( lightmappedgeneric_ps20b ); + DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( ENVMAP_MASK, hasEnvmapMask ); + SET_STATIC_VERTEX_SHADER_COMBO( TANGENTSPACE, 1 ); // // GSTRINGMIGRATION params[info.m_nEnvmap]->IsTexture() ); + SET_STATIC_VERTEX_SHADER_COMBO( BUMPMAP, hasBump ); + SET_STATIC_VERTEX_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); + SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR ) ); + SET_STATIC_VERTEX_SHADER_COMBO( VERTEXALPHATEXBLENDFACTOR, hasBaseTexture2 || hasBump2 ); + SET_STATIC_VERTEX_SHADER_COMBO( BUMPMASK, hasBumpMask ); + SET_STATIC_VERTEX_SHADER_COMBO( RELIEF_MAPPING, false );//bReliefMapping ); + SET_STATIC_VERTEX_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); +#ifdef _X360 + SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, hasFlashlight); +#endif +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs30 ); + } + else + { + DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( ENVMAP_MASK, hasEnvmapMask ); + SET_STATIC_VERTEX_SHADER_COMBO( TANGENTSPACE, 1 ); // // GSTRINGMIGRATION params[info.m_nEnvmap]->IsTexture() ); + SET_STATIC_VERTEX_SHADER_COMBO( BUMPMAP, hasBump ); + SET_STATIC_VERTEX_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); + SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR ) ); + SET_STATIC_VERTEX_SHADER_COMBO( VERTEXALPHATEXBLENDFACTOR, hasBaseTexture2 || hasBump2 ); + SET_STATIC_VERTEX_SHADER_COMBO( BUMPMASK, hasBumpMask ); + SET_STATIC_VERTEX_SHADER_COMBO( RELIEF_MAPPING, false );//bReliefMapping ); + SET_STATIC_VERTEX_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); +#ifdef _X360 + SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, hasFlashlight); +#endif +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); + } + + + //if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps30 ); + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2, hasBaseTexture2 ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, hasDetailTexture ); + SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bumpmap_variant ); + SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP2, hasBump2 ); + SET_STATIC_PIXEL_SHADER_COMBO( BUMPMASK, hasBumpMask ); + SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); + SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, hasEnvmap ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, hasEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, hasSelfIllum ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAPALPHAENVMAPMASK, hasNormalMapAlphaEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURENOENVMAP, params[info.m_nBaseTextureNoEnvmap]->GetIntValue() ); + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2NOENVMAP, params[info.m_nBaseTexture2NoEnvmap]->GetIntValue() ); + SET_STATIC_PIXEL_SHADER_COMBO( WARPLIGHTING, hasLightWarpTexture ); + SET_STATIC_PIXEL_SHADER_COMBO( FANCY_BLENDING, bHasBlendModulateTexture ); + SET_STATIC_PIXEL_SHADER_COMBO( MASKEDBLENDING, bMaskedBlending); + //SET_STATIC_PIXEL_SHADER_COMBO( RELIEF_MAPPING, bReliefMapping ); + SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); + //SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bHasOutline ); + //SET_STATIC_PIXEL_SHADER_COMBO( SOFTEDGES, bHasSoftEdges ); + SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); + SET_STATIC_PIXEL_SHADER_COMBO( NORMALMASK_DECODE_MODE, (int) nNormalMaskDecodeMode ); +#ifdef _X360 + SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, hasFlashlight); +#endif +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps enabled for 2_0b and onwards + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, hasParallaxCorrection ); +#else + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, false ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps30 ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2, hasBaseTexture2 ); SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, hasDetailTexture ); SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bumpmap_variant ); @@ -608,43 +1195,52 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** SET_STATIC_PIXEL_SHADER_COMBO( MASKEDBLENDING, bMaskedBlending); SET_STATIC_PIXEL_SHADER_COMBO( RELIEF_MAPPING, bReliefMapping ); SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); - SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bHasOutline ); - SET_STATIC_PIXEL_SHADER_COMBO( SOFTEDGES, bHasSoftEdges ); + //SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bHasOutline ); + //SET_STATIC_PIXEL_SHADER_COMBO( SOFTEDGES, bHasSoftEdges ); SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); SET_STATIC_PIXEL_SHADER_COMBO( NORMALMASK_DECODE_MODE, (int) nNormalMaskDecodeMode ); #ifdef _X360 SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, hasFlashlight); #endif - SET_STATIC_PIXEL_SHADER( lightmappedgeneric_ps20b ); - } - else - { - DECLARE_STATIC_PIXEL_SHADER( lightmappedgeneric_ps20 ); - SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2, hasBaseTexture2 ); - SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, hasDetailTexture ); - SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bumpmap_variant ); - SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP2, hasBump2 ); - SET_STATIC_PIXEL_SHADER_COMBO( BUMPMASK, hasBumpMask ); - SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); - SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, hasEnvmap ); - SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, hasEnvmapMask ); - SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); - SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, hasSelfIllum ); - SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAPALPHAENVMAPMASK, hasNormalMapAlphaEnvmapMask ); - SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURENOENVMAP, params[info.m_nBaseTextureNoEnvmap]->GetIntValue() ); - SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2NOENVMAP, params[info.m_nBaseTexture2NoEnvmap]->GetIntValue() ); - SET_STATIC_PIXEL_SHADER_COMBO( WARPLIGHTING, hasLightWarpTexture ); - SET_STATIC_PIXEL_SHADER_COMBO( FANCY_BLENDING, bHasBlendModulateTexture ); - SET_STATIC_PIXEL_SHADER_COMBO( MASKEDBLENDING, bMaskedBlending); - SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); - SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bHasOutline ); - SET_STATIC_PIXEL_SHADER_COMBO( SOFTEDGES, bHasSoftEdges ); - SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); - SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, 0 ); // No normal compression with ps_2_0 (yikes!) - SET_STATIC_PIXEL_SHADER_COMBO( NORMALMASK_DECODE_MODE, 0 ); // No normal compression with ps_2_0 - SET_STATIC_PIXEL_SHADER( lightmappedgeneric_ps20 ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURETRANSFORM2, hasBaseTextureTransform2 ); +#endif +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps enabled for 2_0b and onwards + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, hasParallaxCorrection ); +#else + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, false ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20b ); } + //else + //{ + // DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20 ); + // SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2, hasBaseTexture2 ); + // SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, hasDetailTexture ); + // SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, bumpmap_variant ); + // SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP2, hasBump2 ); + // SET_STATIC_PIXEL_SHADER_COMBO( BUMPMASK, hasBumpMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); + // SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, hasEnvmap ); + // SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, hasEnvmapMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, hasSelfIllum ); + // SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAPALPHAENVMAPMASK, hasNormalMapAlphaEnvmapMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURENOENVMAP, params[info.m_nBaseTextureNoEnvmap]->GetIntValue() ); + // SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE2NOENVMAP, params[info.m_nBaseTexture2NoEnvmap]->GetIntValue() ); + // SET_STATIC_PIXEL_SHADER_COMBO( WARPLIGHTING, hasLightWarpTexture ); + // SET_STATIC_PIXEL_SHADER_COMBO( FANCY_BLENDING, bHasBlendModulateTexture ); + // SET_STATIC_PIXEL_SHADER_COMBO( MASKEDBLENDING, bMaskedBlending); + // SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); + // SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bHasOutline ); + // SET_STATIC_PIXEL_SHADER_COMBO( SOFTEDGES, bHasSoftEdges ); + // SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); + // SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, 0 ); // No normal compression with ps_2_0 (yikes!) + // SET_STATIC_PIXEL_SHADER_COMBO( NORMALMASK_DECODE_MODE, 0 ); // No normal compression with ps_2_0 + // SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20 ); + //} // HACK HACK HACK - enable alpha writes all the time so that we have them for // underwater stuff and writing depth to dest alpha // But only do it if we're not using the alpha already for translucency @@ -674,6 +1270,9 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** // for the texture transform. bool bHasTextureTransform = !( params[info.m_nBaseTextureTransform]->MatrixIsIdentity() && +#ifdef MAPBASE + (!hasBaseTextureTransform2 || params[info.m_nBaseTexture2Transform]->MatrixIsIdentity()) && +#endif params[info.m_nBumpTransform]->MatrixIsIdentity() && params[info.m_nBumpTransform2]->MatrixIsIdentity() && params[info.m_nEnvmapMaskTransform]->MatrixIsIdentity() ); @@ -712,6 +1311,12 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** { pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nBumpTransform2 ); } +#ifdef MAPBASE + if ( hasBaseTextureTransform2 ) + { + pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_8, info.m_nBaseTexture2Transform ); + } +#endif } pContextData->m_SemiStaticCmdsOut.SetEnvMapTintPixelShaderDynamicState( 0, info.m_nEnvmapTint ); // set up shader modulation color @@ -749,6 +1354,10 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** float fresnelReflection = params[info.m_nFresnelReflection]->GetFloatValue(); bool hasEnvmap = params[info.m_nEnvmap]->IsTexture(); +#ifdef MAPBASE + bool bEditorBlend = (hasBaseTexture2 && pShader->UsingEditor( params )); // Mapbase - For fixing editor blending +#endif + pContextData->m_bPixelShaderFastPath = true; bool bUsingContrast = hasEnvmap && ( (envmapContrast != 0.0f) && (envmapContrast != 1.0f) ) && (envmapSaturation != 1.0f); bool bUsingFresnel = hasEnvmap && (fresnelReflection != 1.0f); @@ -921,6 +1530,58 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** pContextData->m_SemiStaticCmdsOut.BindTexture( pShader, SHADER_SAMPLER3, info.m_nBlendModulateTexture, -1 ); } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + if (hasParallaxCorrection) + { + float envMapOrigin[4] = {0,0,0,0}; + params[info.m_nEnvmapOrigin]->GetVecValue( envMapOrigin, 3 ); +#ifdef MAPBASE + envMapOrigin[4] = bEditorBlend ? 1.0f : 0.0f; +#endif + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 21, envMapOrigin ); + + float* vecs[3]; + vecs[0] = const_cast(params[info.m_nEnvmapParallaxObb1]->GetVecValue()); + vecs[1] = const_cast(params[info.m_nEnvmapParallaxObb2]->GetVecValue()); + vecs[2] = const_cast(params[info.m_nEnvmapParallaxObb3]->GetVecValue()); + float matrix[4][4]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 4; j++) + { + matrix[i][j] = vecs[i][j]; + } + } + matrix[3][0] = matrix[3][1] = matrix[3][2] = 0; + matrix[3][3] = 1; + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 22, &matrix[0][0], 4 ); + } +#endif + +#ifdef MAPBASE + // Hammer apparently has a bug that causes the vertex blend to get swapped. + // Hammer uses a special internal shader to nullify this, but it doesn't work with custom shaders. + // Downfall got around this by swapping around the base textures in the DLL code when drawn by the editor. + // Doing it here in the shader itself allows us to retain other properties, like FANCY_BLENDING. + else + { + // m_SemiStaticCmdsOut wasn't being sent correctly, so we have to assign this to the API directly + float editorBlend = bEditorBlend ? 1.0f : 0.0f; + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 21, &editorBlend, 1 ); + /* + if (bEditorBlend) + { + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 35, 1.0f ); + } + else + { + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 35, 0.0f ); + } + */ + } +#endif + pContextData->m_SemiStaticCmdsOut.End(); } } @@ -949,14 +1610,29 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** } MaterialFogMode_t fogType = pShaderAPI->GetSceneFogMode(); - DECLARE_DYNAMIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); - SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); - SET_DYNAMIC_VERTEX_SHADER_COMBO( FASTPATH, bVertexShaderFastPath ); - SET_DYNAMIC_VERTEX_SHADER_COMBO( - LIGHTING_PREVIEW, - (nFixedLightingMode)?1:0 - ); - SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, lightmappedgeneric_vs20 ); + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( FASTPATH, bVertexShaderFastPath ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( + LIGHTING_PREVIEW, + (nFixedLightingMode)?1:0 + ); + SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, sdk_lightmappedgeneric_vs30 ); + } + else + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( FASTPATH, bVertexShaderFastPath ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( + LIGHTING_PREVIEW, + (nFixedLightingMode)?1:0 + ); + SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, sdk_lightmappedgeneric_vs20 ); + } + bool bPixelShaderFastPath = pContextData->m_bPixelShaderFastPath; if( nFixedLightingMode !=0 ) @@ -979,9 +1655,24 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** } float envmapContrast = params[info.m_nEnvmapContrast]->GetFloatValue(); - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + //if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + if ( g_pHardwareConfig->SupportsShaderModel_3_0() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( lightmappedgeneric_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps30 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATH, bPixelShaderFastPath || pContextData->m_bPixelShaderForceFastPathBecauseOutline ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATHENVMAPCONTRAST, bPixelShaderFastPath && envmapContrast == 1.0f ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + + // Don't write fog to alpha if we're using translucency + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bWriteDepthToAlpha ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, nFixedLightingMode ); + + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_lightmappedgeneric_ps30 ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATH, bPixelShaderFastPath || pContextData->m_bPixelShaderForceFastPathBecauseOutline ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATHENVMAPCONTRAST, bPixelShaderFastPath && envmapContrast == 1.0f ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); @@ -991,21 +1682,21 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, nFixedLightingMode ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, lightmappedgeneric_ps20b ); - } - else - { - DECLARE_DYNAMIC_PIXEL_SHADER( lightmappedgeneric_ps20 ); - SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATH, bPixelShaderFastPath ); - SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATHENVMAPCONTRAST, bPixelShaderFastPath && envmapContrast == 1.0f ); - SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - - // Don't write fog to alpha if we're using translucency - SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); - SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, nFixedLightingMode ); - - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, lightmappedgeneric_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_lightmappedgeneric_ps20b ); } + //else + //{ + // DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20 ); + // SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATH, bPixelShaderFastPath ); + // SET_DYNAMIC_PIXEL_SHADER_COMBO( FASTPATHENVMAPCONTRAST, bPixelShaderFastPath && envmapContrast == 1.0f ); + // SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + // + // // Don't write fog to alpha if we're using translucency + // SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); + // SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, nFixedLightingMode ); + // + // SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_lightmappedgeneric_ps20 ); + //} if( hasFlashlight && IsX360() ) { @@ -1053,6 +1744,11 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** } } + float eyePos[4]; + pShaderAPI->GetWorldSpaceCameraPosition( eyePos ); + eyePos[3] = pShaderAPI->GetFloatRenderingParameter( FLOAT_RENDERPARM_MINIMUMLIGHTING ); + DynamicCmdsOut.SetPixelShaderConstant( 10, eyePos ); + DynamicCmdsOut.End(); pShaderAPI->ExecuteCommandBuffer( DynamicCmdsOut.Base() ); } @@ -1075,11 +1771,16 @@ void DrawLightmappedGeneric_DX9(CBaseVSShader *pShader, IMaterialVar** params, CBasePerMaterialContextData **pContextDataPtr ) { bool hasFlashlight = pShader->UsingFlashlight( params ); - if ( !IsX360() && !r_flashlight_version2.GetInt() ) - { - DrawLightmappedGeneric_DX9_Internal( pShader, params, hasFlashlight, pShaderAPI, pShaderShadow, info, pContextDataPtr ); - return; - } - + DrawLightmappedGeneric_DX9_Internal( pShader, params, hasFlashlight, pShaderAPI, pShaderShadow, info, pContextDataPtr ); + + //ConVarRef r_flashlight_version2 = ConVarRef( "r_flashlight_version2" ); + // + //if ( !IsX360() && !r_flashlight_version2.GetInt() ) + //{ + // DrawLightmappedGeneric_DX9_Internal( pShader, params, hasFlashlight, pShaderAPI, pShaderShadow, info, pContextDataPtr ); + // return; + //} + // + //DrawLightmappedGeneric_DX9_Internal( pShader, params, hasFlashlight, pShaderAPI, pShaderShadow, info, pContextDataPtr ); } diff --git a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.h b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.h index 00375b9f..1b79ca18 100644 --- a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.h +++ b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.h @@ -11,7 +11,6 @@ #include #include "BaseVSShader.h" - //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- @@ -62,6 +61,9 @@ struct LightmappedGeneric_DX9_Vars_t int m_nBumpMask; int m_nBaseTexture2; int m_nBaseTexture2Frame; +#ifdef MAPBASE + int m_nBaseTexture2Transform; +#endif int m_nBaseTextureNoEnvmap; int m_nBaseTexture2NoEnvmap; int m_nDetailAlphaMaskBaseTexture; @@ -87,8 +89,55 @@ struct LightmappedGeneric_DX9_Vars_t int m_nOutlineEnd0; int m_nOutlineEnd1; + int m_nPhong; + int m_nPhongBoost; + int m_nPhongFresnelRanges; + int m_nPhongExponent; + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + int m_nEnvmapParallax; // Needed for editor + int m_nEnvmapParallaxObb1; + int m_nEnvmapParallaxObb2; + int m_nEnvmapParallaxObb3; + int m_nEnvmapOrigin; +#endif }; + +enum PhongMaskVariant_t +{ + PHONGMASK_NONE, + PHONGMASK_BASEALPHA, + PHONGMASK_NORMALALPHA, + PHONGMASK_STANDALONE, +}; + +struct LightmappedGenericFlashlight_DX9_Vars_t : public CBaseVSShader::DrawFlashlight_dx90_Vars_t +{ + LightmappedGenericFlashlight_DX9_Vars_t() + : m_nPhong( -1 ) + , m_nPhongBoost( -1 ) + , m_nPhongFresnelRanges( -1 ) + , m_nPhongExponent( -1 ) + , m_nPhongMask( -1 ) + , m_nPhongMaskFrame( -1 ) + { + } + int m_nPhong; + int m_nPhongBoost; + int m_nPhongFresnelRanges; + int m_nPhongExponent; + int m_nPhongMask; + int m_nPhongMaskFrame; + +#ifdef MAPBASE + // Fix for displacements not showing $blendmodulatetexture under a flashlight + int m_nBlendModulateTexture; +#endif +}; + + void InitParamsLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, LightmappedGeneric_DX9_Vars_t &info ); void InitLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, LightmappedGeneric_DX9_Vars_t &info ); void DrawLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, @@ -96,4 +145,4 @@ void DrawLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, LightmappedGeneric_DX9_Vars_t &info, CBasePerMaterialContextData **pContextDataPtr ); -#endif // LIGHTMAPPEDGENERIC_DX9_HELPER_H +#endif // LIGHTMAPPEDGENERIC_DX9_HELPER_H \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/lightmappedgeneric_ps2_3_x.h b/mp/src/materialsystem/stdshaders/lightmappedgeneric_ps2_3_x.h index 585ea1f6..ff4a296e 100644 --- a/mp/src/materialsystem/stdshaders/lightmappedgeneric_ps2_3_x.h +++ b/mp/src/materialsystem/stdshaders/lightmappedgeneric_ps2_3_x.h @@ -10,6 +10,7 @@ // SKIP: !$FASTPATH && $FASTPATHENVMAPTINT // SKIP: !$BUMPMAP && $DIFFUSEBUMPMAP // SKIP: !$BUMPMAP && $BUMPMAP2 +// SKIP: !$BUMPMAP2 && $BUMPMASK // SKIP: $ENVMAPMASK && $BUMPMAP2 // SKIP: $BASETEXTURENOENVMAP && ( !$BASETEXTURE2 || !$CUBEMAP ) // SKIP: $BASETEXTURE2NOENVMAP && ( !$BASETEXTURE2 || !$CUBEMAP ) @@ -23,10 +24,19 @@ // SKIP: !$BUMPMAP && ($NORMAL_DECODE_MODE == 2) // SKIP: !$BUMPMAP && ($NORMALMASK_DECODE_MODE == 1) // SKIP: !$BUMPMAP && ($NORMALMASK_DECODE_MODE == 2) -// NOSKIP: $FANCY_BLENDING && (!$FASTPATH) // 360 compiler craps out on some combo in this family. Content doesn't use blendmode 10 anyway -// SKIP: $FASTPATH && $PIXELFOGTYPE && $BASETEXTURE2 && $DETAILTEXTURE && $CUBEMAP && ($DETAIL_BLEND_MODE == 10 ) [XBOX] +// SKIP: $FASTPATH && $PIXELFOGTYPE && $BASETEXTURE2 && $DETAILTEXTURE && $CUBEMAP && ($DETAIL_BLEND_MODE == 10 ) + +// Too many instructions to do this all at once: +// SKIP: $FANCY_BLENDING && $BUMPMAP && $DETAILTEXTURE + +// SKIP: $BASETEXTURETRANSFORM2 && !$BASETEXTURE2 +// SKIP: $BASETEXTURETRANSFORM2 && $SEAMLESS + +// SKIP: $SWAP_VERTEX_BLEND && !$BASETEXTURE2 + +// SKIP: !$FANCY_BLENDING && $MASKEDBLENDING // debug crap: // NOSKIP: $DETAILTEXTURE @@ -90,7 +100,11 @@ const float4 g_DetailTint_and_BlendFactor : register( c8 ); #define g_DetailTint (g_DetailTint_and_BlendFactor.rgb) #define g_DetailBlendFactor (g_DetailTint_and_BlendFactor.w) -const HALF3 g_EyePos : register( c10 ); +const float4 g_EyePos_MinLight : register( c10 ); +#define g_EyePos g_EyePos_MinLight.xyz +#define g_fMinLighting g_EyePos_MinLight.w + + const HALF4 g_FogParams : register( c11 ); const float4 g_TintValuesAndLightmapScale : register( c12 ); @@ -101,6 +115,20 @@ const float3 g_FlashlightPos : register( c14 ); const float4x4 g_FlashlightWorldToTexture : register( c15 ); // through c18 const float4 g_ShadowTweaks : register( c19 ); +#if PARALLAXCORRECT +// Parallax cubemaps +const float4 cubemapPos : register(c21); +const float4x4 obbMatrix : register(c22); //through c25 +#define g_BlendInverted cubemapPos.w +#else +// Blixibon - Hammer apparently has a bug that causes the vertex blend to get swapped. +// Hammer uses a special internal shader to nullify this, but it doesn't work with custom shaders. +// Downfall got around this by swapping around the base textures in the DLL code when drawn by the editor. +// Doing it here in the shader itself allows us to retain other properties, like FANCY_BLENDING. +// TODO: This may be inefficent usage of a constant +const HALF g_BlendInverted : register(c21); +#endif + sampler BaseTextureSampler : register( s0 ); sampler LightmapSampler : register( s1 ); @@ -157,8 +185,13 @@ struct PS_INPUT #if SEAMLESS float3 SeamlessTexCoord : TEXCOORD0; // zy xz float4 detailOrBumpAndEnvmapMaskTexCoord : TEXCOORD1; // envmap mask +#else +#if BASETEXTURETRANSFORM2 + // Blixibon - Using two extra floats for $basetexturetransform2 + HALF4 baseTexCoord : TEXCOORD0; #else HALF2 baseTexCoord : TEXCOORD0; +#endif // detail textures and bumpmaps are mutually exclusive so that we have enough texcoords. #if ( RELIEF_MAPPING == 0 ) HALF4 detailOrBumpAndEnvmapMaskTexCoord : TEXCOORD1; @@ -211,8 +244,23 @@ HALF4 main( PS_INPUT i ) : COLOR baseTexCoords.xy = i.baseTexCoord.xy; #endif +#if BASETEXTURETRANSFORM2 + // Blixibon - Simpler version of GetBaseTextureAndNormal() that supports $basetexturetransform2 + // (make this its own function in common_lightmappedgeneric_fxc.h if this becomes more widespread) + // + // Also, not sure where else to put this, but we're using an entire BASETEXTURETRANSFORM2 combo + // because in DX9, $basetexture2 would update from the original $basetexturetransform, so + // keeping this code separate retains that original behavior. + baseColor = tex2D( BaseTextureSampler, baseTexCoords.xy ); + baseColor2 = tex2D( BaseTextureSampler2, i.baseTexCoord.wz ); + if ( bBumpmap || bNormalMapAlphaEnvmapMask ) + { + vNormal = tex2D( BumpmapSampler, baseTexCoords.xy ); + } +#else GetBaseTextureAndNormal( BaseTextureSampler, BaseTextureSampler2, BumpmapSampler, bBaseTexture2, bBumpmap || bNormalMapAlphaEnvmapMask, baseTexCoords, i.vertexColor.rgb, baseColor, baseColor2, vNormal ); +#endif #if BUMPMAP == 1 // not ssbump vNormal.xyz = vNormal.xyz * 2.0f - 1.0f; // make signed if we're not ssbump @@ -311,26 +359,31 @@ HALF4 main( PS_INPUT i ) : COLOR #if MASKEDBLENDING float blendfactor=0.5; #else + float blendfactor=i.vertexBlendX_fogFactorW.r; + + // See g_BlendInverted's declaration for more info on this + if (g_BlendInverted > 0.0) + { + blendfactor=1.0f-blendfactor; + } + #endif if( bBaseTexture2 ) { #if (SELFILLUM == 0) && (PIXELFOGTYPE != PIXEL_FOG_TYPE_HEIGHT) && (FANCY_BLENDING) - float4 modt=tex2D(BlendModulationSampler,i.lightmapTexCoord3.zw); + float4 modt=tex2D(BlendModulationSampler,baseTexCoords); #if MASKEDBLENDING - // FXC is unable to optimize this, despite blendfactor=0.5 above - //float minb=modt.g-modt.r; - //float maxb=modt.g+modt.r; - //blendfactor=smoothstep(minb,maxb,blendfactor); - blendfactor=modt.g; + float minb=modt.g-modt.r; + float maxb=modt.g+modt.r; #else - float minb=saturate(modt.g-modt.r); - float maxb=saturate(modt.g+modt.r); + float minb=max(0,modt.g-modt.r); + float maxb=min(1,modt.g+modt.r); +#endif blendfactor=smoothstep(minb,maxb,blendfactor); #endif -#endif - baseColor.rgb = lerp( baseColor, baseColor2.rgb, blendfactor ); + baseColor.rgb = lerp( baseColor.rgb, baseColor2.rgb, blendfactor ); blendedAlpha = lerp( baseColor.a, baseColor2.a, blendfactor ); } @@ -414,7 +467,7 @@ HALF4 main( PS_INPUT i ) : COLOR albedo *= baseColor; if( !bBaseAlphaEnvmapMask && !bSelfIllum ) { - alpha *= baseColor.a; + alpha *= blendedAlpha; // Blixibon - Replaced baseColor.a with blendedAlpha } if( bDetailTexture ) @@ -424,7 +477,7 @@ HALF4 main( PS_INPUT i ) : COLOR // The vertex color contains the modulation color + vertex color combined #if ( SEAMLESS == 0 ) - albedo.xyz *= i.vertexColor; + albedo.xyz *= i.vertexColor.xyz; #endif alpha *= i.vertexColor.a * g_flAlpha2; // not sure about this one @@ -447,9 +500,9 @@ HALF4 main( PS_INPUT i ) : COLOR vNormal.xyz = normalize( bumpBasis[0]*vNormal.x + bumpBasis[1]*vNormal.y + bumpBasis[2]*vNormal.z); #else float3 dp; - dp.x = saturate( dot( vNormal, bumpBasis[0] ) ); - dp.y = saturate( dot( vNormal, bumpBasis[1] ) ); - dp.z = saturate( dot( vNormal, bumpBasis[2] ) ); + dp.x = saturate( dot( vNormal.xyz, bumpBasis[0] ) ); + dp.y = saturate( dot( vNormal.xyz, bumpBasis[1] ) ); + dp.z = saturate( dot( vNormal.xyz, bumpBasis[2] ) ); dp *= dp; #if ( DETAIL_BLEND_MODE == TCOMBINE_SSBUMP_BUMP ) @@ -475,11 +528,23 @@ HALF4 main( PS_INPUT i ) : COLOR diffuseLighting *= 2.0*tex2D(WarpLightingSampler,float2(len,0)); #endif -#if CUBEMAP || LIGHTING_PREVIEW || ( defined( _X360 ) && FLASHLIGHT ) - float3 worldSpaceNormal = mul( vNormal, i.tangentSpaceTranspose ); +#if 1 //CUBEMAP || LIGHTING_PREVIEW || ( defined( _X360 ) && FLASHLIGHT ) + float3x3 tangentSpaceTranspose = i.tangentSpaceTranspose; + + float3 worldSpaceNormal = mul( vNormal.xyz, i.tangentSpaceTranspose ); #endif + float3 worldVertToEyeVector = g_EyePos - i.worldPos_projPosZ.xyz; + +#if FOGTYPE == 2 || FLASHLIGHT != 0 float3 diffuseComponent = albedo.xyz * diffuseLighting; +#else + float3 vEyeDir = normalize( worldVertToEyeVector ); + float flFresnelMinlight = saturate( dot( worldSpaceNormal, vEyeDir ) ); + + float3 diffuseComponent = albedo.xyz * lerp( diffuseLighting, 1, g_fMinLighting * flFresnelMinlight ); +#endif + #if defined( _X360 ) && FLASHLIGHT @@ -490,9 +555,9 @@ HALF4 main( PS_INPUT i ) : COLOR float3 worldPosToLightVector = g_FlashlightPos - i.worldPos_projPosZ.xyz; float3 tangentPosToLightVector; - tangentPosToLightVector.x = dot( worldPosToLightVector, i.tangentSpaceTranspose[0] ); - tangentPosToLightVector.y = dot( worldPosToLightVector, i.tangentSpaceTranspose[1] ); - tangentPosToLightVector.z = dot( worldPosToLightVector, i.tangentSpaceTranspose[2] ); + tangentPosToLightVector.x = dot( worldPosToLightVector, tangentSpaceTranspose[0] ); + tangentPosToLightVector.y = dot( worldPosToLightVector, tangentSpaceTranspose[1] ); + tangentPosToLightVector.z = dot( worldPosToLightVector, tangentSpaceTranspose[2] ); tangentPosToLightVector = normalize( tangentPosToLightVector ); @@ -514,15 +579,15 @@ HALF4 main( PS_INPUT i ) : COLOR if( bSelfIllum ) { - float3 selfIllumComponent = g_SelfIllumTint * albedo.xyz; - diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a ); + float3 selfIllumComponent = g_SelfIllumTint.xyz * albedo.xyz; + diffuseComponent = lerp( diffuseComponent, selfIllumComponent, blendedAlpha ); // Blixibon - Replaced baseColor.a with blendedAlpha } HALF3 specularLighting = HALF3( 0.0f, 0.0f, 0.0f ); #if CUBEMAP if( bCubemap ) { - float3 worldVertToEyeVector = g_EyePos - i.worldPos_projPosZ.xyz; + //float3 worldVertToEyeVector = g_EyePos - i.worldPos_projPosZ.xyz; float3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, worldVertToEyeVector ); // Calc Fresnel factor @@ -531,10 +596,27 @@ HALF4 main( PS_INPUT i ) : COLOR fresnel = pow( fresnel, 5.0 ); fresnel = fresnel * g_OneMinusFresnelReflection + g_FresnelReflection; +#if PARALLAXCORRECT + //Parallax correction (2_0b and beyond) + //Adapted from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ + float3 worldPos = i.worldPos_projPosZ.xyz; + float3 positionLS = mul(float4(worldPos, 1), obbMatrix); + float3 rayLS = mul(reflectVect, (float3x3) obbMatrix); + + float3 firstPlaneIntersect = (float3(1.0f, 1.0f, 1.0f) - positionLS) / rayLS; + float3 secondPlaneIntersect = (-positionLS) / rayLS; + float3 furthestPlane = max(firstPlaneIntersect, secondPlaneIntersect); + float distance = min(furthestPlane.x, min(furthestPlane.y, furthestPlane.z)); + + // Use distance in WS directly to recover intersection + float3 intersectPositionWS = worldPos + reflectVect * distance; + reflectVect = intersectPositionWS - cubemapPos; +#endif + specularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, reflectVect ); specularLighting *= specularFactor; - specularLighting *= g_EnvmapTint; + specularLighting *= g_EnvmapTint.rgb; #if FANCY_BLENDING == 0 HALF3 specularLightingSquared = specularLighting * specularLighting; specularLighting = lerp( specularLighting, specularLightingSquared, g_EnvmapContrast ); @@ -548,7 +630,7 @@ HALF4 main( PS_INPUT i ) : COLOR HALF3 result = diffuseComponent + specularLighting; #if LIGHTING_PREVIEW - worldSpaceNormal = mul( vNormal, i.tangentSpaceTranspose ); + worldSpaceNormal = mul( vNormal, tangentSpaceTranspose ); # if LIGHTING_PREVIEW == 1 float dotprod = 0.7+0.25 * dot( worldSpaceNormal, normalize( float3( 1, 2, -.5 ) ) ); return FinalOutput( HALF4( dotprod*albedo.xyz, alpha ), 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); @@ -570,7 +652,7 @@ HALF4 main( PS_INPUT i ) : COLOR bWriteDepthToAlpha = ( WRITE_DEPTH_TO_DESTALPHA != 0 ) && ( WRITEWATERFOGTODESTALPHA == 0 ); #endif - float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.z, i.worldPos_projPosZ.z, i.worldPos_projPosZ.w ); + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w ); #if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) alpha = fogFactor; diff --git a/mp/src/materialsystem/stdshaders/lightmappedreflective.cpp b/mp/src/materialsystem/stdshaders/lightmappedreflective.cpp new file mode 100644 index 00000000..8c8efb9e --- /dev/null +++ b/mp/src/materialsystem/stdshaders/lightmappedreflective.cpp @@ -0,0 +1,282 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "BaseVSShader.h" +#include "mathlib/vmatrix.h" +#include "common_hlsl_cpp_consts.h" // hack hack hack! + +#include "SDK_lightmappedreflective_vs20.inc" +#include "SDK_lightmappedreflective_ps20.inc" +#include "SDK_lightmappedreflective_ps20b.inc" + + +DEFINE_FALLBACK_SHADER( SDK_LightmappedReflective, SDK_LightmappedReflective_DX90 ) + +BEGIN_VS_SHADER( SDK_LightmappedReflective_DX90, "Help for SDK_Lightmapped Reflective" ) + + BEGIN_SHADER_PARAMS + SHADER_PARAM( REFRACTTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_WaterRefraction", "" ) + SHADER_PARAM( REFLECTTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_WaterReflection", "" ) + SHADER_PARAM( REFRACTAMOUNT, SHADER_PARAM_TYPE_FLOAT, "0", "" ) + SHADER_PARAM( REFRACTTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "refraction tint" ) + SHADER_PARAM( REFLECTAMOUNT, SHADER_PARAM_TYPE_FLOAT, "0.8", "" ) + SHADER_PARAM( REFLECTTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "reflection tint" ) + SHADER_PARAM( NORMALMAP, SHADER_PARAM_TYPE_TEXTURE, "dev/water_normal", "normal map" ) + SHADER_PARAM( BUMPFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $bumpmap" ) + SHADER_PARAM( BUMPTRANSFORM, SHADER_PARAM_TYPE_MATRIX, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$bumpmap texcoord transform" ) + SHADER_PARAM( ENVMAPMASK, SHADER_PARAM_TYPE_TEXTURE, "shadertest/shadertest_envmask", "envmap mask" ) + SHADER_PARAM( ENVMAPMASKFRAME, SHADER_PARAM_TYPE_INTEGER, "", "" ) + SHADER_PARAM( FRESNELPOWER, SHADER_PARAM_TYPE_FLOAT, "5", "" ) + SHADER_PARAM( MAXREFLECTIVITY, SHADER_PARAM_TYPE_FLOAT, "1", "" ) + SHADER_PARAM( MINREFLECTIVITY, SHADER_PARAM_TYPE_FLOAT, "0", "" ) + END_SHADER_PARAMS + + SHADER_INIT_PARAMS() + { + if ( !params[FRESNELPOWER]->IsDefined() ) + { + params[FRESNELPOWER]->SetFloatValue( 5.0f ); + } + if ( !params[MAXREFLECTIVITY]->IsDefined() ) + { + params[MAXREFLECTIVITY]->SetFloatValue( 1.0f ); + } + + SET_FLAGS2( MATERIAL_VAR2_NEEDS_TANGENT_SPACES ); + if ( params[BASETEXTURE]->IsDefined() ) + { + SET_FLAGS2( MATERIAL_VAR2_LIGHTING_LIGHTMAP ); + if( g_pConfig->UseBumpmapping() && params[NORMALMAP]->IsDefined() ) + { + SET_FLAGS2( MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ); + } + } + } + + SHADER_FALLBACK + { + // FIXME: Create dx8 level fallback if we use this feature out of the SFM + return 0; + } + + SHADER_INIT + { + if( params[REFRACTTEXTURE]->IsDefined() ) + { + LoadTexture( REFRACTTEXTURE ); + } + if( params[REFLECTTEXTURE]->IsDefined() ) + { + LoadTexture( REFLECTTEXTURE ); + } + if ( params[NORMALMAP]->IsDefined() ) + { + LoadBumpMap( NORMALMAP ); + } + if( params[BASETEXTURE]->IsDefined() ) + { + LoadTexture( BASETEXTURE ); + if( params[ENVMAPMASK]->IsDefined() ) + { + LoadTexture( ENVMAPMASK ); + } + } + else + { + params[ENVMAPMASK]->SetUndefined(); + } + } + + inline void DrawReflectionRefraction( IMaterialVar **params, IShaderShadow* pShaderShadow, + IShaderDynamicAPI* pShaderAPI, bool bReflection, bool bRefraction ) + { + BlendType_t nBlendType = EvaluateBlendRequirements( BASETEXTURE, true ); + bool bFullyOpaque = (nBlendType != BT_BLENDADD) && (nBlendType != BT_BLEND) && !IS_FLAG_SET(MATERIAL_VAR_ALPHATEST); //dest alpha is free for special use + + SHADOW_STATE + { + SetInitialShadowState( ); + if( bRefraction ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + } + } + if( bReflection ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER2, true ); + pShaderShadow->EnableTexture( SHADER_SAMPLER3, true ); + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER2, true ); + } + } + if( params[BASETEXTURE]->IsTexture() ) + { + // BASETEXTURE + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + // LIGHTMAP + pShaderShadow->EnableTexture( SHADER_SAMPLER3, true ); + + if ( params[ENVMAPMASK]->IsTexture() ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER6, true ); + } + } + + // normal map + pShaderShadow->EnableTexture( SHADER_SAMPLER4, true ); + + int fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_TANGENT_S | VERTEX_TANGENT_T; + + // texcoord0 : base texcoord + // texcoord1 : lightmap texcoord + // texcoord2 : lightmap texcoord offset + int numTexCoords = 1; + if( params[BASETEXTURE]->IsTexture() ) + { + numTexCoords = 3; + } + pShaderShadow->VertexShaderVertexFormat( fmt, numTexCoords, 0, 0 ); + + if ( IS_FLAG_SET(MATERIAL_VAR_TRANSLUCENT ) ) + { + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA ); + } + + DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedreflective_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURE, params[BASETEXTURE]->IsTexture() ); + SET_STATIC_VERTEX_SHADER( sdk_lightmappedreflective_vs20 ); + + // "REFLECT" "0..1" + // "REFRACT" "0..1" + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( REFLECT, bReflection ); + SET_STATIC_PIXEL_SHADER_COMBO( REFRACT, bRefraction ); + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE, params[BASETEXTURE]->IsTexture() ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, params[ENVMAPMASK]->IsTexture() && params[BASETEXTURE]->IsTexture() ); + SET_STATIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( REFLECT, bReflection ); + SET_STATIC_PIXEL_SHADER_COMBO( REFRACT, bRefraction ); + SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE, params[BASETEXTURE]->IsTexture() ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, params[ENVMAPMASK]->IsTexture() && params[BASETEXTURE]->IsTexture() ); + SET_STATIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20 ); + } + + FogToFogColor(); + + if( g_pHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + { + // we are writing linear values from this shader. + pShaderShadow->EnableSRGBWrite( true ); + } + + pShaderShadow->EnableAlphaWrites( bFullyOpaque ); + } + DYNAMIC_STATE + { + if( bRefraction ) + { + // HDRFIXME: add comment about binding.. Specify the number of MRTs in the enable + BindTexture( SHADER_SAMPLER0, REFRACTTEXTURE, -1 ); + } + if( bReflection ) + { + BindTexture( SHADER_SAMPLER2, REFLECTTEXTURE, -1 ); + } + BindTexture( SHADER_SAMPLER4, NORMALMAP, BUMPFRAME ); + if( params[BASETEXTURE]->IsTexture() ) + { + BindTexture( SHADER_SAMPLER1, BASETEXTURE, FRAME ); + pShaderAPI->BindStandardTexture( SHADER_SAMPLER3, TEXTURE_LIGHTMAP ); + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_3, BASETEXTURETRANSFORM ); + + if ( params[ENVMAPMASK]->IsTexture() ) + { + BindTexture( SHADER_SAMPLER6, ENVMAPMASK, ENVMAPMASKFRAME ); + } + } + + // Refraction tint + if( bRefraction ) + { + SetPixelShaderConstantGammaToLinear( 1, REFRACTTINT ); + } + // Reflection tint + if( bReflection ) + { + SetPixelShaderConstantGammaToLinear( 4, REFLECTTINT ); + } + + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_1, BUMPTRANSFORM ); + + float c0[4] = { 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f, 0.0f }; + pShaderAPI->SetPixelShaderConstant( 0, c0, 1 ); + + float c2[4] = { 0.5f, 0.5f, 0.5f, 0.5f }; + pShaderAPI->SetPixelShaderConstant( 2, c2, 1 ); + + // fresnel constants + float flFresnelFactor = params[MAXREFLECTIVITY]->GetFloatValue() - params[MINREFLECTIVITY]->GetFloatValue(); + float c3[4] = { flFresnelFactor, params[FRESNELPOWER]->GetFloatValue(), params[MINREFLECTIVITY]->GetFloatValue(), 0.0f }; + pShaderAPI->SetPixelShaderConstant( 3, c3, 1 ); + + float c5[4] = { params[REFLECTAMOUNT]->GetFloatValue(), params[REFLECTAMOUNT]->GetFloatValue(), + params[REFRACTAMOUNT]->GetFloatValue(), params[REFRACTAMOUNT]->GetFloatValue() }; + pShaderAPI->SetPixelShaderConstant( 5, c5, 1 ); + + pShaderAPI->SetPixelShaderFogParams( 8 ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedreflective_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_lightmappedreflective_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_lightmappedreflective_ps20 ); + } + } + Draw(); + } + + SHADER_DRAW + { + bool bRefraction = params[REFRACTTEXTURE]->IsTexture(); + bool bReflection = params[REFLECTTEXTURE]->IsTexture(); + bool bDrewSomething = false; + if ( bReflection || bRefraction ) + { + bDrewSomething = true; + DrawReflectionRefraction( params, pShaderShadow, pShaderAPI, bReflection, bRefraction ); + } + + if( !bDrewSomething ) + { + // We are likely here because of the tools. . . draw something so that + // we won't go into wireframe-land. + Draw(); + } + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/refract.cpp b/mp/src/materialsystem/stdshaders/refract.cpp index 852e93a5..4ed22b1d 100644 --- a/mp/src/materialsystem/stdshaders/refract.cpp +++ b/mp/src/materialsystem/stdshaders/refract.cpp @@ -9,9 +9,9 @@ #include "convar.h" #include "refract_dx9_helper.h" -DEFINE_FALLBACK_SHADER( Refract, Refract_DX90 ) +DEFINE_FALLBACK_SHADER( SDK_Refract, SDK_Refract_DX90 ) -BEGIN_VS_SHADER( Refract_DX90, "Help for Refract" ) +BEGIN_VS_SHADER( SDK_Refract_DX90, "Help for SDK_Refract" ) BEGIN_SHADER_PARAMS SHADER_PARAM_OVERRIDE( COLOR, SHADER_PARAM_TYPE_COLOR, "{255 255 255}", "unused", SHADER_PARAM_NOT_EDITABLE ) diff --git a/mp/src/materialsystem/stdshaders/refract_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/refract_dx9_helper.cpp index f436e62a..b40b4454 100644 --- a/mp/src/materialsystem/stdshaders/refract_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/refract_dx9_helper.cpp @@ -8,9 +8,9 @@ #include "BaseVSShader.h" #include "refract_dx9_helper.h" #include "convar.h" -#include "Refract_vs20.inc" -#include "Refract_ps20.inc" -#include "Refract_ps20b.inc" +#include "SDK_Refract_vs20.inc" +#include "SDK_refract_ps20.inc" +#include "SDK_refract_ps20b.inc" #include "cpp_shader_constant_register_map.h" #define MAXBLUR 1 @@ -76,6 +76,19 @@ void InitRefract_DX9( CBaseVSShader *pShader, IMaterialVar** params, Refract_DX9 if( params[info.m_nEnvmap]->IsDefined() ) { pShader->LoadCubeMap( info.m_nEnvmap, TEXTUREFLAGS_SRGB ); + +#ifdef MAPBASE + if (mat_specular_disable_on_missing.GetBool()) + { + // Revert to defaultcubemap when the envmap texture is missing + // (should be equivalent to toolsblack in Mapbase) + if (!IS_FLAG_SET( MATERIAL_VAR_MODEL ) && params[info.m_nEnvmap]->GetTextureValue()->IsError()) + { + params[info.m_nEnvmap]->SetStringValue( "engine/defaultcubemap" ); + pShader->LoadCubeMap( info.m_nEnvmap, TEXTUREFLAGS_SRGB ); + } + } +#endif } if( params[info.m_nRefractTintTexture]->IsDefined() ) { @@ -206,16 +219,16 @@ void DrawRefract_DX9( CBaseVSShader *pShader, IMaterialVar** params, IShaderDyna pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, NULL, userDataSize ); - DECLARE_STATIC_VERTEX_SHADER( refract_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_refract_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( MODEL, bIsModel ); SET_STATIC_VERTEX_SHADER_COMBO( COLORMODULATE, bColorModulate ); - SET_STATIC_VERTEX_SHADER( refract_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_refract_vs20 ); // We have to do this in the shader on R500 or Leopard bool bShaderSRGBConvert = IsOSX() && ( g_pHardwareConfig->FakeSRGBWrite() || !g_pHardwareConfig->CanDoSRGBReadFromRTs() ); if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // always send OpenGL down the ps2b path { - DECLARE_STATIC_PIXEL_SHADER( refract_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_refract_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( BLUR, blurAmount ); SET_STATIC_PIXEL_SHADER_COMBO( FADEOUTONSILHOUETTE, bFadeOutOnSilhouette ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); @@ -225,11 +238,11 @@ void DrawRefract_DX9( CBaseVSShader *pShader, IMaterialVar** params, IShaderDyna SET_STATIC_PIXEL_SHADER_COMBO( SECONDARY_NORMAL, bSecondaryNormal ); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); SET_STATIC_PIXEL_SHADER_COMBO( SHADER_SRGB_READ, bShaderSRGBConvert ); - SET_STATIC_PIXEL_SHADER( refract_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_refract_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( refract_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_refract_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( BLUR, blurAmount ); SET_STATIC_PIXEL_SHADER_COMBO( FADEOUTONSILHOUETTE, bFadeOutOnSilhouette ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); @@ -238,7 +251,7 @@ void DrawRefract_DX9( CBaseVSShader *pShader, IMaterialVar** params, IShaderDyna SET_STATIC_PIXEL_SHADER_COMBO( COLORMODULATE, bColorModulate ); SET_STATIC_PIXEL_SHADER_COMBO( SECONDARY_NORMAL, bSecondaryNormal ); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); - SET_STATIC_PIXEL_SHADER( refract_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_refract_ps20 ); } pShader->DefaultFog(); if( bMasked ) @@ -293,23 +306,23 @@ void DrawRefract_DX9( CBaseVSShader *pShader, IMaterialVar** params, IShaderDyna pShader->BindTexture( SHADER_SAMPLER5, info.m_nRefractTintTexture, info.m_nRefractTintTextureFrame ); } - DECLARE_DYNAMIC_VERTEX_SHADER( refract_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_refract_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( refract_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_refract_vs20 ); if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // always send Posix down the ps2b path { - DECLARE_DYNAMIC_PIXEL_SHADER( refract_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_refract_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bWriteZ && bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( refract_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_refract_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( refract_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_refract_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( refract_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_refract_ps20 ); } pShader->SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_1, info.m_nBumpTransform ); // 1 & 2 diff --git a/mp/src/materialsystem/stdshaders/sdk_eyeglint_ps2x.fxc b/mp/src/materialsystem/stdshaders/sdk_eyeglint_ps2x.fxc new file mode 100644 index 00000000..bc39f079 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/sdk_eyeglint_ps2x.fxc @@ -0,0 +1,32 @@ +// ======= Copyright © 1996-2007, Valve Corporation, All rights reserved. ====== +// +// Run procedural glint generation inner loop in pixel shader (ps_2_0) +// +// ============================================================================= + +struct PS_INPUT +{ + float2 tc : TEXCOORD0; // Interpolated coordinate of current texel + float2 glintCenter : TEXCOORD1; // Uniform value containing center of glint + float3 glintColor : TEXCOORD2; // Uniform value of color of glint +}; + +float GlintGaussSpotCoefficient( float2 d ) +{ + return saturate( exp( -25.0f * dot(d, d) ) ); +} + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.tc - i.glintCenter; // This texel relative to glint center + + float intensity = GlintGaussSpotCoefficient( uv + float2(-0.25f, -0.25f) ) + + GlintGaussSpotCoefficient( uv + float2( 0.25f, -0.25f) ) + + 5 * GlintGaussSpotCoefficient( uv ) + + GlintGaussSpotCoefficient( uv + float2(-0.25f, 0.25f) ) + + GlintGaussSpotCoefficient( uv + float2( 0.25f, 0.25f) ); + + intensity *= 4.0f/9.0f; + + return float4( intensity * i.glintColor, 1.0f ); +} \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/sdk_eyeglint_vs20.fxc b/mp/src/materialsystem/stdshaders/sdk_eyeglint_vs20.fxc new file mode 100644 index 00000000..cf1ccb9b --- /dev/null +++ b/mp/src/materialsystem/stdshaders/sdk_eyeglint_vs20.fxc @@ -0,0 +1,38 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Vertex shader to pass through texcoords needed to run the +// procedural glint generation inner loop in the pixel shader +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#include "common_vs_fxc.h" + +struct VS_INPUT +{ + float3 vPos : POSITION; + float2 tc : TEXCOORD0; // Interpolated coordinate of current texel in 3x3 quad + float2 glintCenter : TEXCOORD1; // Uniform value containing center of glint in local 3x3 quad + float3 glintColor : TEXCOORD2; // Uniform value of color of glint +}; + +struct VS_OUTPUT +{ + float4 projPos : POSITION; + float2 tc : TEXCOORD0; // Interpolated coordinate of current texel in 3x3 quad + float2 glintCenter : TEXCOORD1; // Uniform value containing center of glint in local 3x3 quad + float3 glintColor : TEXCOORD2; // Uniform value of color of glint +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + o.projPos = float4( v.vPos, 1.0f ); + o.tc = v.tc; + o.glintCenter = v.glintCenter; + o.glintColor = v.glintColor; + return o; +} + + diff --git a/mp/src/materialsystem/stdshaders/shatteredglass.cpp b/mp/src/materialsystem/stdshaders/shatteredglass.cpp new file mode 100644 index 00000000..e98889ea --- /dev/null +++ b/mp/src/materialsystem/stdshaders/shatteredglass.cpp @@ -0,0 +1,349 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Lightmap only shader +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#include "BaseVSShader.h" + +#include "SDK_ShatteredGlass_ps20.inc" +#include "SDK_ShatteredGlass_ps20b.inc" +#include "SDK_ShatteredGlass_vs20.inc" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +BEGIN_VS_SHADER( SDK_ShatteredGlass, + "Help for SDK_ShatteredGlass" ) + + BEGIN_SHADER_PARAMS + SHADER_PARAM_OVERRIDE( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "Glass/glasswindowbreak070b", "unused", SHADER_PARAM_NOT_EDITABLE ) + SHADER_PARAM( DETAIL, SHADER_PARAM_TYPE_TEXTURE, "Glass/glasswindowbreak070b", "detail" ) + SHADER_PARAM( DETAILSCALE, SHADER_PARAM_TYPE_FLOAT, "1.0", "detail scale" ) + SHADER_PARAM( ENVMAP, SHADER_PARAM_TYPE_TEXTURE, "shadertest/shadertest_env", "envmap" ) + SHADER_PARAM( ENVMAPFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "" ) + SHADER_PARAM( ENVMAPMASK, SHADER_PARAM_TYPE_TEXTURE, "glass/glasswindowbreak070b_mask", "envmap mask" ) + SHADER_PARAM( ENVMAPMASKFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "" ) + SHADER_PARAM( ENVMAPMASKTRANSFORM, SHADER_PARAM_TYPE_MATRIX, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$envmapmask texcoord transform" ) + SHADER_PARAM( ENVMAPTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "envmap tint" ) + SHADER_PARAM( ENVMAPCONTRAST, SHADER_PARAM_TYPE_FLOAT, "0.0", "contrast 0 == normal 1 == color*color" ) + SHADER_PARAM( ENVMAPSATURATION, SHADER_PARAM_TYPE_FLOAT, "1.0", "saturation 0 == greyscale 1 == normal" ) + SHADER_PARAM( FRESNELREFLECTION, SHADER_PARAM_TYPE_FLOAT, "1.0", "1.0 == mirror, 0.0 == water" ) + SHADER_PARAM( UNLITFACTOR, SHADER_PARAM_TYPE_FLOAT, "0.7", "0.0 == multiply by lightmap, 1.0 == multiply by 1" ) +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + SHADER_PARAM( ENVMAPPARALLAX, SHADER_PARAM_TYPE_BOOL, "0", "Enables parallax correction code for env_cubemaps" ) + SHADER_PARAM( ENVMAPPARALLAXOBB1, SHADER_PARAM_TYPE_VEC4, "[1 0 0 0]", "The first line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB2, SHADER_PARAM_TYPE_VEC4, "[0 1 0 0]", "The second line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB3, SHADER_PARAM_TYPE_VEC4, "[0 0 1 0]", "The third line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPORIGIN, SHADER_PARAM_TYPE_VEC3, "[0 0 0]", "The world space position of the env_cubemap being corrected" ) +#endif + END_SHADER_PARAMS + + SHADER_INIT_PARAMS() + { + if( !params[DETAILSCALE]->IsDefined() ) + params[DETAILSCALE]->SetFloatValue( 1.0f ); + + if( !params[ENVMAPTINT]->IsDefined() ) + params[ENVMAPTINT]->SetVecValue( 1.0f, 1.0f, 1.0f ); + + if( !params[ENVMAPCONTRAST]->IsDefined() ) + params[ENVMAPCONTRAST]->SetFloatValue( 0.0f ); + + if( !params[ENVMAPSATURATION]->IsDefined() ) + params[ENVMAPSATURATION]->SetFloatValue( 1.0f ); + + if( !params[UNLITFACTOR]->IsDefined() ) + params[UNLITFACTOR]->SetFloatValue( 0.3f ); + + if( !params[FRESNELREFLECTION]->IsDefined() ) + params[FRESNELREFLECTION]->SetFloatValue( 1.0f ); + + if( !params[ENVMAPMASKFRAME]->IsDefined() ) + params[ENVMAPMASKFRAME]->SetIntValue( 0 ); + + if( !params[ENVMAPFRAME]->IsDefined() ) + params[ENVMAPFRAME]->SetIntValue( 0 ); + + // No texture means no self-illum or env mask in base alpha + if ( !params[BASETEXTURE]->IsDefined() ) + { + CLEAR_FLAGS( MATERIAL_VAR_BASEALPHAENVMAPMASK ); + } + + // If in decal mode, no debug override... + if (IS_FLAG_SET(MATERIAL_VAR_DECAL)) + { + SET_FLAGS( MATERIAL_VAR_NO_DEBUG_OVERRIDE ); + } + } + + SHADER_FALLBACK + { + return 0; + } + + SHADER_INIT + { + if (params[BASETEXTURE]->IsDefined()) + { + LoadTexture( BASETEXTURE ); + + if ( !params[BASETEXTURE]->GetTextureValue()->IsTranslucent() ) + { + if ( IS_FLAG_SET( MATERIAL_VAR_BASEALPHAENVMAPMASK ) ) + CLEAR_FLAGS( MATERIAL_VAR_BASEALPHAENVMAPMASK ); + } + } + + if ( params[DETAIL]->IsDefined() ) + { + LoadTexture( DETAIL ); + } + + // Don't alpha test if the alpha channel is used for other purposes + if ( IS_FLAG_SET(MATERIAL_VAR_BASEALPHAENVMAPMASK) ) + CLEAR_FLAGS( MATERIAL_VAR_ALPHATEST ); + + if (params[ENVMAP]->IsDefined()) + { + LoadCubeMap( ENVMAP ); +#ifdef MAPBASE + if (mat_specular_disable_on_missing.GetBool()) + { + // Revert to defaultcubemap when the envmap texture is missing + // (should be equivalent to toolsblack in Mapbase) + if (params[ENVMAP]->GetTextureValue()->IsError()) + { + params[ENVMAP]->SetStringValue( "engine/defaultcubemap" ); + LoadCubeMap( ENVMAP ); + } + } +#endif + + if ( params[ENVMAPMASK]->IsDefined() ) + { + LoadTexture( ENVMAPMASK ); + } + } + } + + SHADER_DRAW + { + bool bHasEnvmapMask = false; + bool bHasEnvmap = false; +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + bool hasParallaxCorrection = false; +#endif + if ( params[ENVMAP]->IsTexture() ) + { + bHasEnvmap = true; +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + hasParallaxCorrection = params[ENVMAPPARALLAX]->GetIntValue() > 0; +#endif + if ( params[ENVMAPMASK]->IsTexture() ) + { + bHasEnvmapMask = true; + } + } + bool bHasVertexColor = IS_FLAG_SET(MATERIAL_VAR_VERTEXCOLOR); + bool bHasBaseAlphaEnvmapMask = IS_FLAG_SET(MATERIAL_VAR_BASEALPHAENVMAPMASK); + + // Base + SHADOW_STATE + { + // alpha test + pShaderShadow->EnableAlphaTest( IS_FLAG_SET(MATERIAL_VAR_ALPHATEST) ); + + // Alpha blending, enable alpha blending if the detail texture is translucent + bool detailIsTranslucent = TextureIsTranslucent( DETAIL, false ); + if ( detailIsTranslucent ) + { + if ( IS_FLAG_SET( MATERIAL_VAR_ADDITIVE ) ) + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE ); + else + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA ); + } + else + { + SetDefaultBlendingShadowState( BASETEXTURE, true ); + } + + pShaderShadow->EnableSRGBWrite( true ); + + // Base texture + unsigned int flags = VERTEX_POSITION; + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + + // Lightmap + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, true ); + } + else + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, false ); + } + + // Detail texture + pShaderShadow->EnableTexture( SHADER_SAMPLER3, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER3, true ); + + // Envmap + if ( bHasEnvmap ) + { + flags |= VERTEX_NORMAL; + pShaderShadow->EnableTexture( SHADER_SAMPLER2, true ); + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER2, true ); + } + + if( bHasEnvmapMask ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER5, true ); + } + } + + // Normalizing cube map + pShaderShadow->EnableTexture( SHADER_SAMPLER6, true ); + + if ( bHasVertexColor ) + { + flags |= VERTEX_COLOR; + } + + pShaderShadow->VertexShaderVertexFormat( flags, 3, 0, 0 ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_shatteredglass_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( ENVMAP_MASK, bHasEnvmapMask ); + SET_STATIC_VERTEX_SHADER( sdk_shatteredglass_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_shatteredglass_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); + SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR, bHasVertexColor ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, bHasBaseAlphaEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( HDRTYPE, g_pHardwareConfig->GetHDRType() ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps enabled for 2_0b and onwards + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, hasParallaxCorrection ); +#else + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, false ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_shatteredglass_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_shatteredglass_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); + SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR, bHasVertexColor ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, bHasBaseAlphaEnvmapMask ); + SET_STATIC_PIXEL_SHADER_COMBO( HDRTYPE, g_pHardwareConfig->GetHDRType() ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, 0 ); // No parallax cubemaps with ps_2_0 :( +#endif + SET_STATIC_PIXEL_SHADER( sdk_shatteredglass_ps20 ); + } + + DefaultFog(); + } + DYNAMIC_STATE + { + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, BASETEXTURETRANSFORM ); + SetVertexShaderTextureScale( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, DETAILSCALE ); + + BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME ); + pShaderAPI->BindStandardTexture( SHADER_SAMPLER1, TEXTURE_LIGHTMAP ); + BindTexture( SHADER_SAMPLER3, DETAIL ); + + if( bHasEnvmap ) + { + BindTexture( SHADER_SAMPLER2, ENVMAP, ENVMAPFRAME ); + if( bHasEnvmapMask ) + { + BindTexture( SHADER_SAMPLER5, ENVMAPMASK, ENVMAPMASKFRAME ); + } + } + + pShaderAPI->BindStandardTexture( SHADER_SAMPLER6, TEXTURE_NORMALIZATION_CUBEMAP_SIGNED ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_shatteredglass_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, ( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ); + SET_DYNAMIC_VERTEX_SHADER( sdk_shatteredglass_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_shatteredglass_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_shatteredglass_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_shatteredglass_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_shatteredglass_ps20 ); + } + + SetEnvMapTintPixelShaderDynamicState( 0, ENVMAPTINT, -1 ); + SetModulationPixelShaderDynamicState( 1 ); + SetPixelShaderConstant( 2, ENVMAPCONTRAST ); + SetPixelShaderConstant( 3, ENVMAPSATURATION ); + + // [ 0, 0 ,0, R(0) ] + float fresnel[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + fresnel[3] = params[FRESNELREFLECTION]->GetFloatValue(); + fresnel[0] = fresnel[1] = fresnel[2] = 1.0f - fresnel[3]; + pShaderAPI->SetPixelShaderConstant( 4, fresnel ); + + float eyePos[4]; + pShaderAPI->GetWorldSpaceCameraPosition( eyePos ); + pShaderAPI->SetPixelShaderConstant( 5, eyePos, 1 ); + + pShaderAPI->SetPixelShaderFogParams( 12 ); + + float overbright[4]; + overbright[0] = OVERBRIGHT; + overbright[1] = params[UNLITFACTOR]->GetFloatValue(); + overbright[2] = overbright[3] = 1.0f - params[UNLITFACTOR]->GetFloatValue(); + pShaderAPI->SetPixelShaderConstant( 6, overbright ); + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + if (hasParallaxCorrection) + { + pShaderAPI->SetPixelShaderConstant( 7, params[ENVMAPORIGIN]->GetVecValue() ); + + float* vecs[3]; + vecs[0] = const_cast(params[ENVMAPPARALLAXOBB1]->GetVecValue()); + vecs[1] = const_cast(params[ENVMAPPARALLAXOBB2]->GetVecValue()); + vecs[2] = const_cast(params[ENVMAPPARALLAXOBB3]->GetVecValue()); + float matrix[4][4]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 4; j++) + { + matrix[i][j] = vecs[i][j]; + } + } + matrix[3][0] = matrix[3][1] = matrix[3][2] = 0; + matrix[3][3] = 1; + pShaderAPI->SetPixelShaderConstant( 8, &matrix[0][0], 4 ); + } +#endif + } + Draw(); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/skin_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/skin_dx9_helper.cpp index cc999788..1174cb1b 100644 --- a/mp/src/materialsystem/stdshaders/skin_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/skin_dx9_helper.cpp @@ -9,13 +9,13 @@ #include "skin_dx9_helper.h" #include "convar.h" #include "cpp_shader_constant_register_map.h" -#include "skin_vs20.inc" -#include "skin_ps20b.inc" +#include "SDK_skin_vs20.inc" +#include "SDK_skin_ps20b.inc" #include "commandbuilder.h" #ifndef _X360 -#include "skin_vs30.inc" -#include "skin_ps30.inc" +#include "SDK_skin_vs30.inc" +#include "SDK_skin_ps30.inc" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -115,6 +115,10 @@ void InitParamsSkin_DX9( CBaseVSShader *pShader, IMaterialVar** params, const ch { params[info.m_nEnvmapFresnel]->SetFloatValue( 0 ); } + +#ifdef MAPBASE + InitIntParam( info.m_nPhongDisableHalfLambert, params, 0 ); +#endif } //----------------------------------------------------------------------------- @@ -207,6 +211,19 @@ void InitSkin_DX9( CBaseVSShader *pShader, IMaterialVar** params, VertexLitGener pShader->LoadCubeMap( info.m_nEnvmap, g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ? TEXTUREFLAGS_SRGB : 0 ); } +#ifdef MAPBASE + // This is crashing for currently unknown reasons. + // As a result, $envmapmask support is not yet functional. + /* + if ( info.m_nEnvmapMask != -1 && params[info.m_nEnvmapMask]->IsDefined() ) + { + pShader->LoadTexture( info.m_nEnvmapMask ); + + CLEAR_FLAGS( MATERIAL_VAR_BASEALPHAENVMAPMASK ); + } + */ +#endif + if ( bHasSelfIllumMask ) { pShader->LoadTexture( info.m_nSelfIllumMask ); @@ -254,6 +271,10 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad bool bHasPhongWarp = (info.m_nPhongWarpTexture != -1) && params[info.m_nPhongWarpTexture]->IsTexture(); bool bHasNormalMapAlphaEnvmapMask = IS_FLAG_SET( MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ); +#ifdef MAPBASE + bool bHasEnvmapMask = (!bHasFlashlight || IsX360()) && info.m_nEnvmapMask != -1 && params[info.m_nEnvmapMask]->IsTexture(); +#endif + #if !defined( _X360 ) bool bIsDecal = IS_FLAG_SET( MATERIAL_VAR_DECAL ); #endif @@ -273,6 +294,15 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad float flPhongExponentFactor = ( info.m_nPhongExponentFactor != -1 ) ? GetFloatParam( info.m_nPhongExponentFactor, params ) : 0.0f; const bool bHasPhongExponentFactor = flPhongExponentFactor != 0.0f; +#ifdef MAPBASE + // This option was ported from Alien Swarm for Source 2013's "skin shader" implementation. + // Original comment from Alien Swarm SDK: + // "This is to allow phong materials to disable half lambert. Half lambert has always been forced on in phong, + // so the only safe way to allow artists to disable half lambert is to create this param that disables the + // default behavior of forcing half lambert on." + bool bPhongHalfLambert = IS_PARAM_DEFINED( info.m_nPhongDisableHalfLambert ) ? (params[info.m_nPhongDisableHalfLambert]->GetIntValue() == 0) : true; +#endif + BlendType_t nBlendType= pShader->EvaluateBlendRequirements( bBlendTintByBaseAlpha ? -1 : info.m_nBaseTexture, true ); bool bFullyOpaque = (nBlendType != BT_BLENDADD) && (nBlendType != BT_BLEND) && !bIsAlphaTested && !bHasFlashlight; //dest alpha is free for special use @@ -421,6 +451,13 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad pShaderShadow->EnableTexture( SHADER_SAMPLER14, true ); } +#ifdef MAPBASE + if ( bHasEnvmapMask ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER15, true ); + } +#endif + if( bHasVertexColor || bHasVertexAlpha ) { flags |= VERTEX_COLOR; @@ -447,17 +484,17 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad #ifndef _X360 - if ( !g_pHardwareConfig->HasFastVertexTextures() ) + if ( !g_pHardwareConfig->SupportsShaderModel_3_0() ) #endif { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_STATIC_VERTEX_SHADER( skin_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_skin_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); - SET_STATIC_VERTEX_SHADER( skin_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_skin_vs20 ); // Assume we're only going to get in here if we support 2b - DECLARE_STATIC_PIXEL_SHADER( skin_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_skin_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, bHasSelfIllum && !bHasFlashlight ); SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUMFRESNEL, bHasSelfIllumFresnel && !bHasFlashlight ); @@ -472,19 +509,26 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad SET_STATIC_PIXEL_SHADER_COMBO( CONVERT_TO_SRGB, 0 ); SET_STATIC_PIXEL_SHADER_COMBO( FASTPATH_NOBUMP, pContextData->m_bFastPath ); SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( skin_ps20b ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( PHONG_HALFLAMBERT, bPhongHalfLambert ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_skin_ps20b ); } #ifndef _X360 else { + const bool bFastVertexTextures = g_pHardwareConfig->HasFastVertexTextures(); + // The vertex shader uses the vertex id stream - SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + if ( bFastVertexTextures ) + SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( skin_vs30 ); - SET_STATIC_VERTEX_SHADER_COMBO( DECAL, bIsDecal ); - SET_STATIC_VERTEX_SHADER( skin_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_skin_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( DECAL, bIsDecal && bFastVertexTextures ); + SET_STATIC_VERTEX_SHADER( sdk_skin_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( skin_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_skin_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, bHasSelfIllum && !bHasFlashlight ); SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUMFRESNEL, bHasSelfIllumFresnel && !bHasFlashlight ); @@ -499,7 +543,11 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad SET_STATIC_PIXEL_SHADER_COMBO( CONVERT_TO_SRGB, 0 ); SET_STATIC_PIXEL_SHADER_COMBO( FASTPATH_NOBUMP, pContextData->m_bFastPath ); SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( skin_ps30 ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( PHONG_HALFLAMBERT, bPhongHalfLambert ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); +#endif + SET_STATIC_PIXEL_SHADER( sdk_skin_ps30 ); } #endif @@ -597,6 +645,13 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad } } +#ifdef MAPBASE + if ( bHasEnvmapMask ) + { + pContextData->m_SemiStaticCmdsOut.BindTexture( pShader, SHADER_SAMPLER15, info.m_nEnvmapMask, info.m_nEnvmapMaskFrame ); + } +#endif + if ( hasDetailTexture ) { pShader->BindTexture( SHADER_SAMPLER13, info.m_nDetail, info.m_nDetailFrame ); @@ -660,61 +715,78 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad } #ifndef _X360 - if ( !g_pHardwareConfig->HasFastVertexTextures() ) + if ( !g_pHardwareConfig->SupportsShaderModel_3_0() ) #endif { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_DYNAMIC_VERTEX_SHADER( skin_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_skin_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING)!=0); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER( skin_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_skin_vs20 ); - DECLARE_DYNAMIC_PIXEL_SHADER( skin_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_skin_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bWriteDepthToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PHONG_USE_EXPONENT_FACTOR, bHasPhongExponentFactor ); - SET_DYNAMIC_PIXEL_SHADER( skin_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_skin_ps20b ); } #ifndef _X360 else { - pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); + const bool bFastVertexTextures = g_pHardwareConfig->HasFastVertexTextures(); - DECLARE_DYNAMIC_VERTEX_SHADER( skin_vs30 ); + if ( bFastVertexTextures ) + pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_skin_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING)!=0); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( skin_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_skin_vs30 ); - DECLARE_DYNAMIC_PIXEL_SHADER( skin_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_skin_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bWriteDepthToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PHONG_USE_EXPONENT_FACTOR, bHasPhongExponentFactor ); - SET_DYNAMIC_PIXEL_SHADER( skin_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_skin_ps30 ); - bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() || !bIsDecal }; - pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + if ( bFastVertexTextures ) + { + bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() || !bIsDecal }; + pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + } } #endif pShader->SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, info.m_nBaseTextureTransform ); +#ifdef MAPBASE + // The original code makes it seem like we have the opportunity to support both $bumptransform and $detail at the same time, + // and that may or may not have been Valve's intention, but we'd need to add another texcoord for this and it's already + // a limitation with the non-skin shader anyway. + if ( bHasBump ) + { + pShader->SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nBumpTransform ); + } + else +#else if( bHasBump ) { pShader->SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, info.m_nBumpTransform ); } +#endif if ( hasDetailTexture ) { @@ -736,7 +808,10 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad bool bHasBaseAlphaPhongMask = (info.m_nBaseMapAlphaPhongMask != -1) && ( params[info.m_nBaseMapAlphaPhongMask]->GetIntValue() != 0 ); float fHasBaseAlphaPhongMask = bHasBaseAlphaPhongMask ? 1 : 0; // Controls for lerp-style paths through shader code - float vShaderControls[4] = { fHasBaseAlphaPhongMask, 0.0f/*unused*/, flTintReplacementAmount, fInvertPhongMask }; + + const float flMinLighting = pShaderAPI->GetFloatRenderingParameter( FLOAT_RENDERPARM_MINIMUMLIGHTING ); + + float vShaderControls[4] = { fHasBaseAlphaPhongMask, flMinLighting, flTintReplacementAmount, fInvertPhongMask }; pShaderAPI->SetPixelShaderConstant( PSREG_CONSTANT_27, vShaderControls, 1 ); if ( hasDetailTexture ) @@ -969,22 +1044,23 @@ void DrawSkin_DX9_Internal( CBaseVSShader *pShader, IMaterialVar** params, IShad //----------------------------------------------------------------------------- // Draws the shader //----------------------------------------------------------------------------- -extern ConVar r_flashlight_version2; void DrawSkin_DX9( CBaseVSShader *pShader, IMaterialVar** params, IShaderDynamicAPI *pShaderAPI, IShaderShadow* pShaderShadow, VertexLitGeneric_DX9_Vars_t &info, VertexCompressionType_t vertexCompression, CBasePerMaterialContextData **pContextDataPtr ) { + //ConVarRef r_flashlight_version2 = ConVarRef( "r_flashlight_version2" ); + bool bHasFlashlight = pShader->UsingFlashlight( params ); - if ( bHasFlashlight && ( IsX360() || r_flashlight_version2.GetInt() ) ) - { - DrawSkin_DX9_Internal( pShader, params, pShaderAPI, - pShaderShadow, false, info, vertexCompression, pContextDataPtr++ ); - if ( pShaderShadow ) - { - pShader->SetInitialShadowState( ); - } - } + //if ( bHasFlashlight && ( IsX360() || r_flashlight_version2.GetBool() ) ) + //{ + // DrawSkin_DX9_Internal( pShader, params, pShaderAPI, + // pShaderShadow, false, info, vertexCompression, pContextDataPtr++ ); + // if ( pShaderShadow ) + // { + // pShader->SetInitialShadowState( ); + // } + //} DrawSkin_DX9_Internal( pShader, params, pShaderAPI, pShaderShadow, bHasFlashlight, info, vertexCompression, pContextDataPtr ); } diff --git a/mp/src/materialsystem/stdshaders/spline_fxc.h b/mp/src/materialsystem/stdshaders/spline_fxc.h new file mode 100644 index 00000000..bc9fb428 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/spline_fxc.h @@ -0,0 +1,19 @@ +//========== Copyright (c) Valve Corporation, All rights reserved. ==========// + +// derivative of catmull rom spline courtesy of calc +float4 DCatmullRomSpline ( float4 a, float4 b, float4 c, float4 d, float t ) +{ + return 0.5 *( c - a + t * ( 2 * a - 5 * b + 4 * c - d + t * (3 * b - a - 3 * c + d ) ) + + t * ( 2 * a - 5 * b + 4 * c - d + 2 * ( t * ( 3 * b - a - 3 * c + d ) ) ) ); +} + +float3 DCatmullRomSpline3 ( float3 a, float3 b, float3 c, float3 d, float t ) +{ + return 0.5 *( c - a + t * ( 2 * a - 5 * b + 4 * c - d + t * (3 * b - a - 3 * c + d ) ) + + t * ( 2 * a - 5 * b + 4 * c - d + 2 * ( t * ( 3 * b - a - 3 * c + d ) ) ) ); +} + +float4 CatmullRomSpline( float4 a, float4 b, float4 c, float4 d, float t ) +{ + return b + 0.5 * t * ( c - a + t * ( 2 * a - 5 * b + 4 * c - d + t * ( -a + 3 * b -3 * c + d ) ) ); +} diff --git a/mp/src/materialsystem/stdshaders/splinerope.cpp b/mp/src/materialsystem/stdshaders/splinerope.cpp new file mode 100644 index 00000000..f4618021 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/splinerope.cpp @@ -0,0 +1,169 @@ +//===== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======// + +#include "BaseVSShader.h" +#include "convar.h" + +#include "SDK_splinerope_ps20.inc" +#include "SDK_splinerope_ps20b.inc" +#include "SDK_splinerope_vs20.inc" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static ConVar rope_min_pixel_diameter( "rope_min_pixel_diameter", "2.0", FCVAR_CHEAT ); + +DEFINE_FALLBACK_SHADER( SDK_Cable, SDK_Cable_DX9 ) + +BEGIN_VS_SHADER( SDK_Cable_DX9, "Help for SplineRope" ) + BEGIN_SHADER_PARAMS + SHADER_PARAM( SHADERSRGBREAD360, SHADER_PARAM_TYPE_BOOL, "0", "Simulate srgb read in shader code") + SHADER_PARAM( SHADOWDEPTH, SHADER_PARAM_TYPE_INTEGER, "0", "writing to a shadow depth buffer" ) + SHADER_PARAM( BUMPMAP, SHADER_PARAM_TYPE_TEXTURE, "cable/cablenormalmap", "normal map" ) + END_SHADER_PARAMS + + SHADER_INIT_PARAMS() + { + // srgb read 360 + InitIntParam( SHADERSRGBREAD360, params, 0 ); + InitIntParam( SHADOWDEPTH, params, 0 ); + if ( !params[BUMPMAP]->IsDefined() ) + { + params[BUMPMAP]->SetStringValue( "cable/cablenormalmap" ); + } + SET_FLAGS2( MATERIAL_VAR2_IS_SPRITECARD ); // What's this for? + } + + SHADER_FALLBACK + { + return 0; + } + + SHADER_INIT + { + SET_FLAGS2( MATERIAL_VAR2_LIGHTING_VERTEX_LIT ); + LoadTexture( BASETEXTURE ); + LoadBumpMap( BUMPMAP ); + } + + SHADER_DRAW + { + bool bShaderSrgbRead = ( IsX360() && params[SHADERSRGBREAD360]->GetIntValue() ); + bool bShadowDepth = ( params[SHADOWDEPTH]->GetIntValue() != 0 ); + SHADOW_STATE + { + // draw back-facing because of yaw spin + pShaderShadow->EnableCulling( false ); + + if ( bShadowDepth ) + { + // don't write color and alpha since we only interested in depth for shadow maps. + pShaderShadow->EnableColorWrites( false ); + pShaderShadow->EnableAlphaWrites( false ); + + // polyoffset for shadow maps. + pShaderShadow->EnablePolyOffset( SHADER_POLYOFFSET_SHADOW_BIAS ); + } + else + { + // We need to write to dest alpha for depth feathering. + pShaderShadow->EnableAlphaWrites( true ); + + // base texture + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, !bShaderSrgbRead ); + + // normal map + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + + pShaderShadow->EnableSRGBWrite( true ); + + FogToFogColor(); + } + + static int s_TexCoordSize[]={ 4, // (worldspace xyz) (radius (diameter?) of spline at this point) for first control point + 4, // (worldspace xyz) (radius of spline at this point) for second control point + 4, // (worldspace xyz) (radius of spline at this point) for third control point + 4, // (worldspace xyz) (radius of spline at this point) for fourth control point + }; + + unsigned int flags = VERTEX_POSITION | VERTEX_COLOR; + + int numTexCoords = 4; + pShaderShadow->VertexShaderVertexFormat( flags, numTexCoords, s_TexCoordSize, 0 ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_splinerope_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_splinerope_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_splinerope_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( SHADER_SRGB_READ, bShaderSrgbRead ); + SET_STATIC_PIXEL_SHADER_COMBO( SHADOWDEPTH, bShadowDepth ); + SET_STATIC_PIXEL_SHADER( sdk_splinerope_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_splinerope_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( SHADER_SRGB_READ, bShaderSrgbRead ); + SET_STATIC_PIXEL_SHADER_COMBO( SHADOWDEPTH, bShadowDepth ); + SET_STATIC_PIXEL_SHADER( sdk_splinerope_ps20 ); + } + } + DYNAMIC_STATE + { + // We need these only when screen-orienting, which we are always in this shader. + LoadModelViewMatrixIntoVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0 ); + LoadProjectionMatrixIntoVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_3 ); + + // Get viewport and render target dimensions and set shader constant to do a 2D mad + ShaderViewport_t viewport; + pShaderAPI->GetViewports( &viewport, 1 ); + + float c7[4]={ 0.0f, 0.0f, 0.0f, 0.0f }; + if ( !g_pHardwareConfig->IsAAEnabled() ) + { + float flMinPixelDiameter = rope_min_pixel_diameter.GetFloat() / ( float )viewport.m_nWidth; + c7[0]= c7[1] = c7[2] = c7[3] = flMinPixelDiameter; + } + pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, c7, 1 ); + + // bind base texture + BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME ); + + // normal map + BindTexture( SHADER_SAMPLER1, BUMPMAP ); + + if ( !bShadowDepth ) + { + pShaderAPI->SetPixelShaderFogParams( 0 ); + + float vEyePos[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos ); + vEyePos[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( 1, vEyePos, 1 ); + } + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_splinerope_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER( sdk_splinerope_vs20 ); + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_splinerope_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, pShaderAPI->ShouldWriteDepthToDestAlpha() ); + //SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_splinerope_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_splinerope_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, 0 ); + //SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_splinerope_ps20 ); + } + } + Draw( ); + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/sprite_dx9.cpp b/mp/src/materialsystem/stdshaders/sprite_dx9.cpp index 0a0bb9f4..7c8f3b99 100644 --- a/mp/src/materialsystem/stdshaders/sprite_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/sprite_dx9.cpp @@ -15,9 +15,9 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#include "sprite_vs20.inc" -#include "sprite_ps20.inc" -#include "sprite_ps20b.inc" +#include "SDK_sprite_vs20.inc" +#include "SDK_sprite_ps20.inc" +#include "SDK_sprite_ps20b.inc" // WARNING! Change these in engine/SpriteGn.h if you change them here! #define SPR_VP_PARALLEL_UPRIGHT 0 @@ -27,9 +27,9 @@ #define SPR_VP_PARALLEL_ORIENTED 4 -DEFINE_FALLBACK_SHADER( Sprite, Sprite_DX9 ) +DEFINE_FALLBACK_SHADER( SDK_Sprite, SDK_Sprite_DX9 ) -BEGIN_VS_SHADER( Sprite_DX9, +BEGIN_VS_SHADER( SDK_Sprite_DX9, "Help for Sprite_DX9" ) BEGIN_SHADER_PARAMS @@ -134,29 +134,29 @@ BEGIN_VS_SHADER( Sprite_DX9, int numTexCoords = 1; s_pShaderShadow->VertexShaderVertexFormat( flags, numTexCoords, 0, 0 ); - DECLARE_STATIC_VERTEX_SHADER( sprite_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_sprite_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, ( shaderFlags & SHADER_USE_VERTEX_COLOR ) ? true : false ); SET_STATIC_VERTEX_SHADER_COMBO( SRGB, bSRGB ); - SET_STATIC_VERTEX_SHADER( sprite_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_sprite_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path { - DECLARE_STATIC_PIXEL_SHADER( sprite_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_sprite_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR, ( shaderFlags & SHADER_USE_VERTEX_COLOR ) ? true : false ); SET_STATIC_PIXEL_SHADER_COMBO( CONSTANTCOLOR, ( shaderFlags & SHADER_USE_CONSTANT_COLOR ) ? true : false ); SET_STATIC_PIXEL_SHADER_COMBO( HDRTYPE, g_pHardwareConfig->GetHDRType() ); SET_STATIC_PIXEL_SHADER_COMBO( SRGB, bSRGB ); SET_STATIC_PIXEL_SHADER_COMBO( SRGB_OUTPUT_ADAPTER, bSRGBOutputAdapter ); - SET_STATIC_PIXEL_SHADER( sprite_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_sprite_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( sprite_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_sprite_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR, ( shaderFlags & SHADER_USE_VERTEX_COLOR ) ? true : false ); SET_STATIC_PIXEL_SHADER_COMBO( CONSTANTCOLOR, ( shaderFlags & SHADER_USE_CONSTANT_COLOR ) ? true : false ); SET_STATIC_PIXEL_SHADER_COMBO( HDRTYPE, g_pHardwareConfig->GetHDRType() ); SET_STATIC_PIXEL_SHADER_COMBO( SRGB, bSRGB ); - SET_STATIC_PIXEL_SHADER( sprite_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_sprite_ps20 ); } // OSX always has to sRGB write (don't do this on Linux/Win GL - it causes glow sprites to be way too dark) @@ -172,23 +172,23 @@ BEGIN_VS_SHADER( Sprite_DX9, MaterialFogMode_t fogType = s_pShaderAPI->GetSceneFogMode(); int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0; - DECLARE_DYNAMIC_VERTEX_SHADER( sprite_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_sprite_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); - SET_DYNAMIC_VERTEX_SHADER( sprite_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_sprite_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path { - DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( sprite_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( sprite_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20 ); } pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); @@ -380,23 +380,23 @@ BEGIN_VS_SHADER( Sprite_DX9, MaterialFogMode_t fogType = s_pShaderAPI->GetSceneFogMode(); int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0; - DECLARE_DYNAMIC_VERTEX_SHADER( sprite_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_sprite_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); - SET_DYNAMIC_VERTEX_SHADER( sprite_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_sprite_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path { - DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( sprite_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( sprite_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20 ); } pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); @@ -437,23 +437,23 @@ BEGIN_VS_SHADER( Sprite_DX9, MaterialFogMode_t fogType = s_pShaderAPI->GetSceneFogMode(); int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0; - DECLARE_DYNAMIC_VERTEX_SHADER( sprite_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_sprite_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); - SET_DYNAMIC_VERTEX_SHADER( sprite_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_sprite_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path { - DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( sprite_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( sprite_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_sprite_ps20 ); } pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); diff --git a/mp/src/materialsystem/stdshaders/stdshader_dx9_20b.txt b/mp/src/materialsystem/stdshaders/stdshader_dx9_20b.txt index a24577ec..47b148d0 100644 --- a/mp/src/materialsystem/stdshaders/stdshader_dx9_20b.txt +++ b/mp/src/materialsystem/stdshaders/stdshader_dx9_20b.txt @@ -7,10 +7,20 @@ // _vs20.vcs // -example_model_ps20b.fxc -example_model_vs20.fxc +//example_model_ps20b.fxc +//example_model_vs20.fxc -SDK_Bloom_ps2x.fxc -SDK_screenspaceeffect_vs20.fxc +SDK_vertexlit_and_unlit_generic_ps2x.fxc +SDK_vertexlit_and_unlit_generic_ps20b.fxc +SDK_vertexlit_and_unlit_generic_vs20.fxc +//SDK_skin_ps20b.fxc +//SDK_skin_vs20.fxc +SDK_windowimposter_ps2x.fxc +SDK_windowimposter_vs20.fxc -SDK_bloomadd_ps2x.fxc +SDK_engine_post_ps20b.fxc + +depth_of_field_ps20b.fxc +depth_of_field_vs20.fxc + +blurgaussian_3x3_ps2x.fxc diff --git a/mp/src/materialsystem/stdshaders/stdshader_dx9_30.txt b/mp/src/materialsystem/stdshaders/stdshader_dx9_30.txt index 071b6a1a..c013ff8d 100644 --- a/mp/src/materialsystem/stdshaders/stdshader_dx9_30.txt +++ b/mp/src/materialsystem/stdshaders/stdshader_dx9_30.txt @@ -7,4 +7,7 @@ // _vs30.vcs // -// There are no examples of such shaders in the SDK, but add yours here. +SDK_vertexlit_and_unlit_generic_ps2x.fxc +SDK_vertexlit_and_unlit_generic_vs20.fxc +SDK_skin_ps20b.fxc +SDK_skin_vs20.fxc diff --git a/mp/src/materialsystem/stdshaders/teeth.cpp b/mp/src/materialsystem/stdshaders/teeth.cpp index b3dc9ff9..6307a5f9 100644 --- a/mp/src/materialsystem/stdshaders/teeth.cpp +++ b/mp/src/materialsystem/stdshaders/teeth.cpp @@ -7,32 +7,32 @@ #include "BaseVSShader.h" #include "cpp_shader_constant_register_map.h" -#include "teeth_vs20.inc" -#include "teeth_flashlight_vs20.inc" -#include "teeth_bump_vs20.inc" -#include "teeth_ps20.inc" -#include "teeth_ps20b.inc" -#include "teeth_flashlight_ps20.inc" -#include "teeth_flashlight_ps20b.inc" -#include "teeth_bump_ps20.inc" -#include "teeth_bump_ps20b.inc" +#include "SDK_teeth_vs20.inc" +#include "SDK_teeth_flashlight_vs20.inc" +#include "SDK_teeth_bump_vs20.inc" +#include "SDK_teeth_ps20.inc" +#include "SDK_teeth_ps20b.inc" +#include "SDK_teeth_flashlight_ps20.inc" +#include "SDK_teeth_flashlight_ps20b.inc" +#include "SDK_teeth_bump_ps20.inc" +#include "SDK_teeth_bump_ps20b.inc" #ifndef _X360 -#include "teeth_vs30.inc" -#include "teeth_ps30.inc" -#include "teeth_bump_vs30.inc" -#include "teeth_bump_ps30.inc" -#include "teeth_flashlight_vs30.inc" -#include "teeth_flashlight_ps30.inc" +#include "SDK_teeth_vs30.inc" +#include "SDK_teeth_ps30.inc" +#include "SDK_teeth_bump_vs30.inc" +#include "SDK_teeth_bump_ps30.inc" +#include "SDK_teeth_flashlight_vs30.inc" +#include "SDK_teeth_flashlight_ps30.inc" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -DEFINE_FALLBACK_SHADER( Teeth, Teeth_DX9 ) +DEFINE_FALLBACK_SHADER( SDK_Teeth, SDK_Teeth_DX9 ) extern ConVar r_flashlight_version2; -BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) +BEGIN_VS_SHADER( SDK_Teeth_DX9, "Help for SDK_Teeth_DX9" ) BEGIN_SHADER_PARAMS SHADER_PARAM( ILLUMFACTOR, SHADER_PARAM_TYPE_FLOAT, "1", "Amount to darken or brighten the teeth" ) @@ -119,21 +119,21 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_STATIC_VERTEX_SHADER( teeth_bump_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_teeth_bump_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[INTRO]->GetIntValue() ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); - SET_STATIC_VERTEX_SHADER( teeth_bump_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_teeth_bump_vs20 ); // ps_2_b version which does phong if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( teeth_bump_ps20b ); - SET_STATIC_PIXEL_SHADER( teeth_bump_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_bump_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_bump_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( teeth_bump_ps20 ); - SET_STATIC_PIXEL_SHADER( teeth_bump_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_bump_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_bump_ps20 ); } } #ifndef _X360 @@ -142,12 +142,12 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) // The vertex shader uses the vertex id stream SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( teeth_bump_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_teeth_bump_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[INTRO]->GetIntValue() ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( teeth_bump_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_teeth_bump_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( teeth_bump_ps30 ); - SET_STATIC_PIXEL_SHADER( teeth_bump_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_bump_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_bump_ps30 ); } #endif } @@ -159,20 +159,20 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_STATIC_VERTEX_SHADER( teeth_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_teeth_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[INTRO]->GetIntValue() ? 1 : 0 ); SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); - SET_STATIC_VERTEX_SHADER( teeth_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_teeth_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( teeth_ps20b ); - SET_STATIC_PIXEL_SHADER( teeth_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( teeth_ps20 ); - SET_STATIC_PIXEL_SHADER( teeth_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_ps20 ); } } #ifndef _X360 @@ -181,12 +181,12 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) // The vertex shader uses the vertex id stream SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( teeth_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_teeth_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[INTRO]->GetIntValue() ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( teeth_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_teeth_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( teeth_ps30 ); - SET_STATIC_PIXEL_SHADER( teeth_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_ps30 ); } #endif } @@ -233,13 +233,13 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_DYNAMIC_VERTEX_SHADER( teeth_bump_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_teeth_bump_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER( teeth_bump_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_teeth_bump_vs20 ); // ps_2_b version which does Phong if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) @@ -249,20 +249,20 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vSpecExponent.Base(), 1 ); - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_bump_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_bump_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( AMBIENT_LIGHT, lightState.m_bAmbientLight ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( teeth_bump_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_bump_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_bump_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_bump_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( AMBIENT_LIGHT, lightState.m_bAmbientLight ? 1 : 0 ); - SET_DYNAMIC_PIXEL_SHADER( teeth_bump_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_bump_ps20 ); } } #ifndef _X360 @@ -270,24 +270,24 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); - DECLARE_DYNAMIC_VERTEX_SHADER( teeth_bump_vs30 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_teeth_bump_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( teeth_bump_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_teeth_bump_vs30 ); Vector4D vSpecExponent; vSpecExponent[3] = params[PHONGEXPONENT]->GetFloatValue(); pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vSpecExponent.Base(), 1 ); - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_bump_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_bump_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( AMBIENT_LIGHT, lightState.m_bAmbientLight ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( teeth_bump_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_bump_ps30 ); } #endif } @@ -302,27 +302,27 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_DYNAMIC_VERTEX_SHADER( teeth_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_teeth_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER( teeth_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_teeth_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( teeth_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( teeth_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_ps20 ); } } #ifndef _X360 @@ -330,19 +330,19 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); - DECLARE_DYNAMIC_VERTEX_SHADER( teeth_vs30 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_teeth_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( teeth_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_teeth_vs30 ); - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( teeth_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_ps30 ); } #endif } @@ -396,20 +396,20 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_STATIC_VERTEX_SHADER( teeth_flashlight_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_teeth_flashlight_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[INTRO]->GetIntValue() ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( teeth_flashlight_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_teeth_flashlight_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( teeth_flashlight_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( teeth_flashlight_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( teeth_flashlight_ps20 ); - SET_STATIC_PIXEL_SHADER( teeth_flashlight_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20 ); } } #ifndef _X360 @@ -418,13 +418,13 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) // The vertex shader uses the vertex id stream SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); - DECLARE_STATIC_VERTEX_SHADER( teeth_flashlight_vs30 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_teeth_flashlight_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( INTRO, params[INTRO]->GetIntValue() ? 1 : 0 ); - SET_STATIC_VERTEX_SHADER( teeth_flashlight_vs30 ); + SET_STATIC_VERTEX_SHADER( sdk_teeth_flashlight_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( teeth_flashlight_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_teeth_flashlight_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( teeth_flashlight_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_teeth_flashlight_ps30 ); } #endif // On DX9, do sRGB @@ -504,24 +504,24 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) if ( !g_pHardwareConfig->HasFastVertexTextures() ) #endif { - DECLARE_DYNAMIC_VERTEX_SHADER( teeth_flashlight_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_teeth_flashlight_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( teeth_flashlight_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_teeth_flashlight_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_flashlight_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); - SET_DYNAMIC_PIXEL_SHADER( teeth_flashlight_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_flashlight_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( teeth_flashlight_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_flashlight_ps20 ); } } #ifndef _X360 @@ -529,17 +529,17 @@ BEGIN_VS_SHADER( Teeth_DX9, "Help for Teeth_DX9" ) { SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, SHADER_VERTEXTEXTURE_SAMPLER0 ); - DECLARE_DYNAMIC_VERTEX_SHADER( teeth_flashlight_vs30 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_teeth_flashlight_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( teeth_flashlight_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_teeth_flashlight_vs30 ); - DECLARE_DYNAMIC_PIXEL_SHADER( teeth_flashlight_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_teeth_flashlight_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); - SET_DYNAMIC_PIXEL_SHADER( teeth_flashlight_ps30 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_teeth_flashlight_ps30 ); } #endif diff --git a/mp/src/materialsystem/stdshaders/tree_sway.h b/mp/src/materialsystem/stdshaders/tree_sway.h new file mode 100644 index 00000000..2bc998ad --- /dev/null +++ b/mp/src/materialsystem/stdshaders/tree_sway.h @@ -0,0 +1,89 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ + +#ifndef _TREE_SWAY_H +#define _TREE_SWAY_H + +// Tree sway vertex animation function. Requires a number of global variables to be defined. See vertexlit_and_unlit_generic_vs20.fxc or depthwrite_vs20.fxc for details. + +// Tree sway mode 2: +// Hacks to use tree sway code on rectangular sheets of plastic/tarp attached at the four corners. +// Inverts the sway scale radius to be 1 at (0,0,0) in model space and fall off radially towards the edges of the model. +// The model is expected to be build lying in the xy plane in model space, with its center at the origin. +// Treeswaystrength should be 0 in the vmt. + +#if ( TREESWAY ) + float3 ComputeTreeSway( float3 vPositionOS, float flTime ) + { + static const float g_flWindOffsetScale = 19; + + float flWindIntensity = length( g_vWindDir ); + + // Model root position is the translation component of the model to world matrix + float3 vModelRoot = float3( cModel[0][3].x, cModel[0][3].y, cModel[0][3].z ); + + // Transform the wind direction into model space + float3 vWindDirAndIntensityOS = mul( ( float3x3 )cModel[0], float3( g_vWindDir, 0 ) ); + + float flSwayScaleHeight = saturate( ( vPositionOS.z - g_flHeight * g_flStartHeight ) / + ( ( 1.0 - g_flStartHeight ) * g_flHeight ) ); + + float flSwayScaleRadius = saturate( length( ( vPositionOS.xy ) - g_flRadius * g_flStartRadius ) / + ( ( 1.0 - g_flStartRadius ) * g_flRadius ) ); + + // Used to turn off branch sway and scrumble below the minimum sway height + float flHeightThreshold = step( 0, vPositionOS.z - g_flHeight * g_flStartHeight ); + + #if ( TREESWAY == 2 ) + { + // Works better for hanging vines + flHeightThreshold = step( vPositionOS.z - g_flHeight * g_flStartHeight, 0 ); + } + #endif + + // Scale branch motion based on how orthogonal they are + // This is what I want to compute: + // float flOrthoBranchScale = 1.0 - abs( dot( normalize( vWindDirAndIntensityOS.xyz ), float3( normalize( vPositionOS.xy ), 0 ) ) ); + // Some NV hardware (7800) will do bad things when normalizing a 0 length vector. Instead, I'm doing the dot product unnormalized + // and divide by the length of the vectors, making sure to avoid divide by 0. + float flOrthoBranchScale = abs( dot( vWindDirAndIntensityOS.xyz, float3( vPositionOS.xy, 0 ) ) ); + flOrthoBranchScale = 1.0 - saturate( flOrthoBranchScale / ( max( length( vWindDirAndIntensityOS.xyz ), 0.0001 ) * max( length( vPositionOS.xy ), 0.0001 ) ) ); + + float flSwayScaleTrunk = g_flSwayIntensity * pow( flSwayScaleHeight, g_flSwayFalloffCurve ); + float flSwayScaleBranches = g_flSwayIntensity * flOrthoBranchScale * flSwayScaleRadius * flHeightThreshold; + #if ( TREESWAY == 2 ) + { + // Looks stupid on vines + flSwayScaleBranches = 0.0; + } + #endif + float flWindTimeOffset = dot( vModelRoot.xyz, float3( 1, 1, 1 ) ) * g_flWindOffsetScale; + float flSlowSwayTime = ( flTime + flWindTimeOffset ) * g_flSwaySpeed; + + float3 vSwayPosOS = normalize( vPositionOS.xyz ); + float3 vScrumblePosOS = vSwayPosOS * g_flScrumbleWaveCount; + float flScrumbleScale = pow( flSwayScaleRadius, g_flScrumbleFalloffCurve ) * g_flScrumbleIntensity * flHeightThreshold; + + float3 vPositionOffset = float3( 0, 0, 0 ); + + // lerp between slow and fast sines based on wind speed + float flSpeedLerp = smoothstep( g_flWindSpeedLerpStart, g_flWindSpeedLerpEnd, flWindIntensity ); + float4 vABunchOfSines = sin( float4( 1.0, 2.31, g_flFastSwaySpeedScale, 2.14 * g_flFastSwaySpeedScale ) * flSlowSwayTime.xxxx ); + vABunchOfSines.xy = lerp( vABunchOfSines.xy, vABunchOfSines.zw, flSpeedLerp ); + + vPositionOffset.xyz = vWindDirAndIntensityOS * flSwayScaleTrunk * ( vABunchOfSines.x + 0.1 ); + vPositionOffset.xyz += vWindDirAndIntensityOS * flSwayScaleBranches * ( vABunchOfSines.y + 0.4 ); + + float3 vScrumbleScale = flScrumbleScale.xxx; + #if ( TREESWAY == 2 ) + { + vScrumbleScale *= float3( 0.5, 0.5, 1.0 ); + } + #endif + + vPositionOffset.xyz += flWindIntensity * ( vScrumbleScale.xyz * sin( g_flScrumbleSpeed * flTime.xxx + vScrumblePosOS.yzx + flWindTimeOffset.xxx ) ); + + return vPositionOS.xyz + vPositionOffset.xyz; + } +#endif + +#endif // _TREE_SWAY_H \ No newline at end of file diff --git a/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp b/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp index b57474a8..f0c30116 100644 --- a/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp @@ -11,7 +11,7 @@ extern ConVar r_flashlight_version2; -BEGIN_VS_SHADER( UnlitGeneric, "Help for UnlitGeneric" ) +BEGIN_VS_SHADER( SDK_UnlitGeneric, "Help for SDK_UnlitGeneric" ) BEGIN_SHADER_PARAMS SHADER_PARAM( ALBEDO, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "albedo (Base texture with no baked lighting)" ) @@ -78,6 +78,25 @@ BEGIN_VS_SHADER( UnlitGeneric, "Help for UnlitGeneric" ) SHADER_PARAM( DEPTHBLENDSCALE, SHADER_PARAM_TYPE_FLOAT, "50.0", "Amplify or reduce DEPTHBLEND fading. Lower values make harder edges." ) SHADER_PARAM( RECEIVEFLASHLIGHT, SHADER_PARAM_TYPE_INTEGER, "0", "Forces this material to receive flashlights." ) + // vertexlitgeneric tree sway animation control (on unlitgeneric) + SHADER_PARAM( TREESWAY, SHADER_PARAM_TYPE_INTEGER, "0", "" ) + SHADER_PARAM( TREESWAYHEIGHT, SHADER_PARAM_TYPE_FLOAT, "1000", "" ) + SHADER_PARAM( TREESWAYSTARTHEIGHT, SHADER_PARAM_TYPE_FLOAT, "0.2", "" ) + SHADER_PARAM( TREESWAYRADIUS, SHADER_PARAM_TYPE_FLOAT, "300", "" ) + SHADER_PARAM( TREESWAYSTARTRADIUS, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSPEED, SHADER_PARAM_TYPE_FLOAT, "1", "" ) + SHADER_PARAM( TREESWAYSPEEDHIGHWINDMULTIPLIER, SHADER_PARAM_TYPE_FLOAT, "2", "" ) + SHADER_PARAM( TREESWAYSTRENGTH, SHADER_PARAM_TYPE_FLOAT, "10", "" ) + SHADER_PARAM( TREESWAYSCRUMBLESPEED, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSCRUMBLESTRENGTH, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSCRUMBLEFREQUENCY, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYFALLOFFEXP, SHADER_PARAM_TYPE_FLOAT, "1.5", "" ) + SHADER_PARAM( TREESWAYSCRUMBLEFALLOFFEXP, SHADER_PARAM_TYPE_FLOAT, "1.0", "" ) + SHADER_PARAM( TREESWAYSPEEDLERPSTART, SHADER_PARAM_TYPE_FLOAT, "3", "" ) + SHADER_PARAM( TREESWAYSPEEDLERPEND, SHADER_PARAM_TYPE_FLOAT, "6", "" ) + SHADER_PARAM( TREESWAYSTATIC, SHADER_PARAM_TYPE_BOOL, "0", "" ) + SHADER_PARAM( TREESWAYSTATICVALUES, SHADER_PARAM_TYPE_VEC2, "[0.5 0.5]", "" ) + END_SHADER_PARAMS void SetupVars( VertexLitGeneric_DX9_Vars_t& info ) @@ -157,6 +176,24 @@ BEGIN_VS_SHADER( UnlitGeneric, "Help for UnlitGeneric" ) info.m_nDepthBlend = DEPTHBLEND; info.m_nDepthBlendScale = DEPTHBLENDSCALE; info.m_nReceiveFlashlight = RECEIVEFLASHLIGHT; + + info.m_nTreeSway = TREESWAY; + info.m_nTreeSwayHeight = TREESWAYHEIGHT; + info.m_nTreeSwayStartHeight = TREESWAYSTARTHEIGHT; + info.m_nTreeSwayRadius = TREESWAYRADIUS; + info.m_nTreeSwayStartRadius = TREESWAYSTARTRADIUS; + info.m_nTreeSwaySpeed = TREESWAYSPEED; + info.m_nTreeSwaySpeedHighWindMultiplier = TREESWAYSPEEDHIGHWINDMULTIPLIER; + info.m_nTreeSwayStrength = TREESWAYSTRENGTH; + info.m_nTreeSwayScrumbleSpeed = TREESWAYSCRUMBLESPEED; + info.m_nTreeSwayScrumbleStrength = TREESWAYSCRUMBLESTRENGTH; + info.m_nTreeSwayScrumbleFrequency = TREESWAYSCRUMBLEFREQUENCY; + info.m_nTreeSwayFalloffExp = TREESWAYFALLOFFEXP; + info.m_nTreeSwayScrumbleFalloffExp = TREESWAYSCRUMBLEFALLOFFEXP; + info.m_nTreeSwaySpeedLerpStart = TREESWAYSPEEDLERPSTART; + info.m_nTreeSwaySpeedLerpEnd = TREESWAYSPEEDLERPEND; + info.m_nTreeSwayStatic = TREESWAYSTATIC; + info.m_nTreeSwayStaticValues = TREESWAYSTATICVALUES; } SHADER_INIT_PARAMS() @@ -186,9 +223,11 @@ BEGIN_VS_SHADER( UnlitGeneric, "Help for UnlitGeneric" ) { VertexLitGeneric_DX9_Vars_t vars; SetupVars( vars ); + + //ConVarRef r_flashlight_version2 = ConVarRef( "r_flashlight_version2" ); - bool bNewFlashlightPath = IsX360() || ( r_flashlight_version2.GetInt() != 0 ); - if ( ( pShaderShadow == NULL ) && ( pShaderAPI != NULL ) && !bNewFlashlightPath && pShaderAPI->InFlashlightMode() ) // Not snapshotting && flashlight pass + //bool bNewFlashlightPath = IsX360() || ( r_flashlight_version2.GetInt() != 0 ); + if ( ( pShaderShadow == NULL ) && ( pShaderAPI != NULL ) && /*!bNewFlashlightPath &&*/ pShaderAPI->InFlashlightMode() ) // Not snapshotting && flashlight pass { Draw( false ); } diff --git a/mp/src/materialsystem/stdshaders/unlittwotexture_dx9.cpp b/mp/src/materialsystem/stdshaders/unlittwotexture_dx9.cpp new file mode 100644 index 00000000..49650622 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/unlittwotexture_dx9.cpp @@ -0,0 +1,265 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#include "BaseVSShader.h" +#include "cloak_blended_pass_helper.h" +#include "cpp_shader_constant_register_map.h" + +#include "SDK_unlittwotexture_vs20.inc" +#include "SDK_unlittwotexture_ps20.inc" +#include "SDK_unlittwotexture_ps20b.inc" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DEFINE_FALLBACK_SHADER( SDK_UnlitTwoTexture, SDK_UnlitTwoTexture_DX9 ) +BEGIN_VS_SHADER( SDK_UnlitTwoTexture_DX9, "Help for SDK_UnlitTwoTexture_DX9" ) + BEGIN_SHADER_PARAMS + SHADER_PARAM( TEXTURE2, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "second texture" ) + SHADER_PARAM( FRAME2, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $texture2" ) + SHADER_PARAM( TEXTURE2TRANSFORM, SHADER_PARAM_TYPE_MATRIX, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$texture2 texcoord transform" ) + + // Cloak Pass + SHADER_PARAM( CLOAKPASSENABLED, SHADER_PARAM_TYPE_BOOL, "0", "Enables cloak render in a second pass" ) + SHADER_PARAM( CLOAKFACTOR, SHADER_PARAM_TYPE_FLOAT, "0.0", "" ) + SHADER_PARAM( CLOAKCOLORTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "Cloak color tint" ) + SHADER_PARAM( REFRACTAMOUNT, SHADER_PARAM_TYPE_FLOAT, "2", "" ) + END_SHADER_PARAMS + + SHADER_FALLBACK + { + return 0; + } + + // Cloak Pass + void SetupVarsCloakBlendedPass( CloakBlendedPassVars_t &info ) + { + info.m_nCloakFactor = CLOAKFACTOR; + info.m_nCloakColorTint = CLOAKCOLORTINT; + info.m_nRefractAmount = REFRACTAMOUNT; + } + + bool NeedsPowerOfTwoFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame ) const + { + if ( params[CLOAKPASSENABLED]->GetIntValue() ) // If material supports cloaking + { + if ( bCheckSpecificToThisFrame == false ) // For setting model flag at load time + return true; + else if ( ( params[CLOAKFACTOR]->GetFloatValue() > 0.0f ) && ( params[CLOAKFACTOR]->GetFloatValue() < 1.0f ) ) // Per-frame check + return true; + // else, not cloaking this frame, so check flag2 in case the base material still needs it + } + + // Check flag2 if not drawing cloak pass + return IS_FLAG2_SET( MATERIAL_VAR2_NEEDS_POWER_OF_TWO_FRAME_BUFFER_TEXTURE ); + } + + bool IsTranslucent( IMaterialVar **params ) const + { + if ( params[CLOAKPASSENABLED]->GetIntValue() ) // If material supports cloaking + { + if ( ( params[CLOAKFACTOR]->GetFloatValue() > 0.0f ) && ( params[CLOAKFACTOR]->GetFloatValue() < 1.0f ) ) // Per-frame check + return true; + // else, not cloaking this frame, so check flag in case the base material still needs it + } + + // Check flag if not drawing cloak pass + return IS_FLAG_SET( MATERIAL_VAR_TRANSLUCENT ); + } + + SHADER_INIT_PARAMS() + { + SET_FLAGS2( MATERIAL_VAR2_SUPPORTS_HW_SKINNING ); + + // Cloak Pass + if ( !params[CLOAKPASSENABLED]->IsDefined() ) + { + params[CLOAKPASSENABLED]->SetIntValue( 0 ); + } + else if ( params[CLOAKPASSENABLED]->GetIntValue() ) + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + InitParamsCloakBlendedPass( this, params, pMaterialName, info ); + } + } + + SHADER_INIT + { + if (params[BASETEXTURE]->IsDefined()) + LoadTexture( BASETEXTURE ); + if (params[TEXTURE2]->IsDefined()) + LoadTexture( TEXTURE2 ); + + // Cloak Pass + if ( params[CLOAKPASSENABLED]->GetIntValue() ) + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + InitCloakBlendedPass( this, params, info ); + } + } + + SHADER_DRAW + { + // Skip the standard rendering if cloak pass is fully opaque + bool bDrawStandardPass = true; + if ( params[CLOAKPASSENABLED]->GetIntValue() && ( pShaderShadow == NULL ) ) // && not snapshotting + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + if ( CloakBlendedPassIsFullyOpaque( params, info ) ) + { + bDrawStandardPass = false; + } + } + + // Skip flashlight pass for unlit stuff + bool bNewFlashlightPath = IsX360(); + if ( bDrawStandardPass && ( pShaderShadow == NULL ) && ( pShaderAPI != NULL ) && + !bNewFlashlightPath && ( pShaderAPI->InFlashlightMode() ) ) // not snapshotting && flashlight pass) + { + bDrawStandardPass = false; + } + + // Standard rendering pass + if ( bDrawStandardPass ) + { + BlendType_t nBlendType = EvaluateBlendRequirements( BASETEXTURE, true ); + bool bFullyOpaque = (nBlendType != BT_BLENDADD) && (nBlendType != BT_BLEND) && !IS_FLAG_SET(MATERIAL_VAR_ALPHATEST); //dest alpha is free for special use + + SHADOW_STATE + { + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, true ); + + pShaderShadow->EnableTexture( SHADER_SAMPLER1, true ); + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER1, true ); + + s_pShaderShadow->EnableSRGBWrite( true ); + + // Either we've got a constant modulation or we've got a texture alpha on either texture + if ( IsAlphaModulating() || IS_FLAG_SET( MATERIAL_VAR_TRANSLUCENT ) || TextureIsTranslucent( BASETEXTURE, true ) || TextureIsTranslucent( TEXTURE2, true ) ) + { + if ( IS_FLAG_SET(MATERIAL_VAR_ADDITIVE) ) + { + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE ); + } + else + { + EnableAlphaBlending( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA ); + } + } + else + { + if ( IS_FLAG_SET(MATERIAL_VAR_ADDITIVE) ) + { + EnableAlphaBlending( SHADER_BLEND_ONE, SHADER_BLEND_ONE ); + } + else + { + DisableAlphaBlending( ); + } + } + + // Set stream format (note that this shader supports compression) + unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_COMPRESSED; + int nTexCoordCount = 1; + int userDataSize = 0; + if (IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR )) + { + flags |= VERTEX_COLOR; + } + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, NULL, userDataSize ); + + // If this is set, blend with the alpha channels of the textures and modulation color + bool bTranslucent = IsAlphaModulating() || IS_FLAG_SET( MATERIAL_VAR_TRANSLUCENT ) || TextureIsTranslucent( BASETEXTURE, true ) || TextureIsTranslucent( TEXTURE2, true ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_unlittwotexture_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( TRANSLUCENT, bTranslucent ); + SET_STATIC_PIXEL_SHADER( sdk_unlittwotexture_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_unlittwotexture_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( TRANSLUCENT, bTranslucent ); + SET_STATIC_PIXEL_SHADER( sdk_unlittwotexture_ps20 ); + } + + DefaultFog(); + + pShaderShadow->EnableAlphaWrites( bFullyOpaque ); + } + DYNAMIC_STATE + { + BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME ); + BindTexture( SHADER_SAMPLER1, TEXTURE2, FRAME2 ); + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_0, BASETEXTURETRANSFORM ); + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_2, TEXTURE2TRANSFORM ); + SetModulationPixelShaderDynamicState_LinearColorSpace( 1 ); + + pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); + + float vEyePos_SpecExponent[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent ); + vEyePos_SpecExponent[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); + + int numBones = pShaderAPI->GetCurrentNumBones(); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_unlittwotexture_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_unlittwotexture_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bFullyOpaque && pShaderAPI->ShouldWriteDepthToDestAlpha() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_unlittwotexture_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_unlittwotexture_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_unlittwotexture_ps20 ); + } + } + Draw(); + } + else + { + // Skip this pass! + Draw( false ); + } + + // Cloak Pass + if ( params[CLOAKPASSENABLED]->GetIntValue() ) + { + // If ( snapshotting ) or ( we need to draw this frame ) + if ( ( pShaderShadow != NULL ) || ( ( params[CLOAKFACTOR]->GetFloatValue() > 0.0f ) && ( params[CLOAKFACTOR]->GetFloatValue() < 1.0f ) ) ) + { + CloakBlendedPassVars_t info; + SetupVarsCloakBlendedPass( info ); + DrawCloakBlendedPass( this, params, pShaderAPI, pShaderShadow, info, vertexCompression ); + } + else // We're not snapshotting and we don't need to draw this frame + { + // Skip this pass! + Draw( false ); + } + } + } +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp index 5e7e61e9..4252e823 100644 --- a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp @@ -11,10 +11,13 @@ #include "emissive_scroll_blended_pass_helper.h" #include "cloak_blended_pass_helper.h" #include "flesh_interior_blended_pass_helper.h" + +#ifdef GAME_SHADER_DLL #include "weapon_sheen_pass_helper.h" +#endif -BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) +BEGIN_VS_SHADER( SDK_VertexLitGeneric, "Help for SDK_VertexLitGeneric" ) BEGIN_SHADER_PARAMS SHADER_PARAM( ALBEDO, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "albedo (Base texture with no baked lighting)" ) SHADER_PARAM( COMPRESS, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "compression wrinklemap" ) @@ -94,6 +97,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) SHADER_PARAM( CLOAKCOLORTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "Cloak color tint" ) SHADER_PARAM( REFRACTAMOUNT, SHADER_PARAM_TYPE_FLOAT, "2", "" ) +#ifdef GAME_SHADER_DLL // Weapon Sheen Pass SHADER_PARAM( SHEENPASSENABLED, SHADER_PARAM_TYPE_BOOL, "0", "Enables weapon sheen render in a second pass" ) SHADER_PARAM( SHEENMAP, SHADER_PARAM_TYPE_TEXTURE, "shadertest/shadertest_env", "sheenmap" ) @@ -106,6 +110,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) SHADER_PARAM( SHEENMAPMASKOFFSETY, SHADER_PARAM_TYPE_FLOAT, "0", "Y Offset of the mask relative to model space coords of target" ) SHADER_PARAM( SHEENMAPMASKDIRECTION, SHADER_PARAM_TYPE_INTEGER, "0", "The direction the sheen should move (length direction of weapon) XYZ, 0,1,2" ) SHADER_PARAM( SHEENINDEX, SHADER_PARAM_TYPE_INTEGER, "0", "Index of the Effect Type (Color Additive, Override etc...)" ) +#endif // Flesh Interior Pass SHADER_PARAM( FLESHINTERIORENABLED, SHADER_PARAM_TYPE_BOOL, "0", "Enable Flesh interior blend pass" ) @@ -136,6 +141,29 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) SHADER_PARAM( BLENDTINTBYBASEALPHA, SHADER_PARAM_TYPE_BOOL, "0", "Use the base alpha to blend in the $color modulation") SHADER_PARAM( BLENDTINTCOLOROVERBASE, SHADER_PARAM_TYPE_FLOAT, "0", "blend between tint acting as a multiplication versus a replace" ) + +#ifdef MAPBASE + SHADER_PARAM( PHONGDISABLEHALFLAMBERT, SHADER_PARAM_TYPE_BOOL, "0", "Disable half lambert for phong" ) +#endif + + // vertexlitgeneric tree sway animation control + SHADER_PARAM( TREESWAY, SHADER_PARAM_TYPE_INTEGER, "0", "" ) + SHADER_PARAM( TREESWAYHEIGHT, SHADER_PARAM_TYPE_FLOAT, "1000", "" ) + SHADER_PARAM( TREESWAYSTARTHEIGHT, SHADER_PARAM_TYPE_FLOAT, "0.2", "" ) + SHADER_PARAM( TREESWAYRADIUS, SHADER_PARAM_TYPE_FLOAT, "300", "" ) + SHADER_PARAM( TREESWAYSTARTRADIUS, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSPEED, SHADER_PARAM_TYPE_FLOAT, "1", "" ) + SHADER_PARAM( TREESWAYSPEEDHIGHWINDMULTIPLIER, SHADER_PARAM_TYPE_FLOAT, "2", "" ) + SHADER_PARAM( TREESWAYSTRENGTH, SHADER_PARAM_TYPE_FLOAT, "10", "" ) + SHADER_PARAM( TREESWAYSCRUMBLESPEED, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSCRUMBLESTRENGTH, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYSCRUMBLEFREQUENCY, SHADER_PARAM_TYPE_FLOAT, "0.1", "" ) + SHADER_PARAM( TREESWAYFALLOFFEXP, SHADER_PARAM_TYPE_FLOAT, "1.5", "" ) + SHADER_PARAM( TREESWAYSCRUMBLEFALLOFFEXP, SHADER_PARAM_TYPE_FLOAT, "1.0", "" ) + SHADER_PARAM( TREESWAYSPEEDLERPSTART, SHADER_PARAM_TYPE_FLOAT, "3", "" ) + SHADER_PARAM( TREESWAYSPEEDLERPEND, SHADER_PARAM_TYPE_FLOAT, "6", "" ) + SHADER_PARAM( TREESWAYSTATIC, SHADER_PARAM_TYPE_BOOL, "0", "" ) + SHADER_PARAM( TREESWAYSTATICVALUES, SHADER_PARAM_TYPE_VEC2, "[0.5 0.5]", "" ) END_SHADER_PARAMS void SetupVars( VertexLitGeneric_DX9_Vars_t& info ) @@ -213,6 +241,28 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) info.m_nSelfIllumMask = SELFILLUMMASK; info.m_nBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA; info.m_nTintReplacesBaseColor = BLENDTINTCOLOROVERBASE; + +#ifdef MAPBASE + info.m_nPhongDisableHalfLambert = PHONGDISABLEHALFLAMBERT; +#endif + + info.m_nTreeSway = TREESWAY; + info.m_nTreeSwayHeight = TREESWAYHEIGHT; + info.m_nTreeSwayStartHeight = TREESWAYSTARTHEIGHT; + info.m_nTreeSwayRadius = TREESWAYRADIUS; + info.m_nTreeSwayStartRadius = TREESWAYSTARTRADIUS; + info.m_nTreeSwaySpeed = TREESWAYSPEED; + info.m_nTreeSwaySpeedHighWindMultiplier = TREESWAYSPEEDHIGHWINDMULTIPLIER; + info.m_nTreeSwayStrength = TREESWAYSTRENGTH; + info.m_nTreeSwayScrumbleSpeed = TREESWAYSCRUMBLESPEED; + info.m_nTreeSwayScrumbleStrength = TREESWAYSCRUMBLESTRENGTH; + info.m_nTreeSwayScrumbleFrequency = TREESWAYSCRUMBLEFREQUENCY; + info.m_nTreeSwayFalloffExp = TREESWAYFALLOFFEXP; + info.m_nTreeSwayScrumbleFalloffExp = TREESWAYSCRUMBLEFALLOFFEXP; + info.m_nTreeSwaySpeedLerpStart = TREESWAYSPEEDLERPSTART; + info.m_nTreeSwaySpeedLerpEnd = TREESWAYSPEEDLERPEND; + info.m_nTreeSwayStatic = TREESWAYSTATIC; + info.m_nTreeSwayStaticValues = TREESWAYSTATICVALUES; } // Cloak Pass @@ -228,6 +278,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) info.m_nBumpTransform = BUMPTRANSFORM; } +#ifdef GAME_SHADER_DLL // Weapon Sheen Pass void SetupVarsWeaponSheenPass( WeaponSheenPassVars_t &info ) { @@ -246,6 +297,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) info.m_nBumpFrame = BUMPFRAME; info.m_nBumpTransform = BUMPTRANSFORM; } +#endif bool NeedsPowerOfTwoFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame ) const { @@ -257,8 +309,11 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) return true; // else, not cloaking this frame, so check flag2 in case the base material still needs it } + +#ifdef GAME_SHADER_DLL if ( params[SHEENPASSENABLED]->GetIntValue() ) // If material supports weapon sheen return true; +#endif // Check flag2 if not drawing cloak pass return IS_FLAG2_SET( MATERIAL_VAR2_NEEDS_POWER_OF_TWO_FRAME_BUFFER_TEXTURE ); @@ -335,6 +390,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) InitParamsCloakBlendedPass( this, params, pMaterialName, info ); } +#ifdef GAME_SHADER_DLL // Sheen Pass if ( !params[SHEENPASSENABLED]->IsDefined() ) { @@ -346,6 +402,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) SetupVarsWeaponSheenPass( info ); InitParamsWeaponSheenPass( this, params, pMaterialName, info ); } +#endif // Emissive Scroll Pass if ( !params[EMISSIVEBLENDENABLED]->IsDefined() ) @@ -400,6 +457,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) InitCloakBlendedPass( this, params, info ); } +#ifdef GAME_SHADER_DLL // TODO : Only do this if we're in range of the camera // Weapon Sheen if ( params[SHEENPASSENABLED]->GetIntValue() ) @@ -408,6 +466,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) SetupVarsWeaponSheenPass( info ); InitWeaponSheenPass( this, params, info ); } +#endif // Emissive Scroll Pass if ( params[EMISSIVEBLENDENABLED]->GetIntValue() ) @@ -453,6 +512,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) Draw( false ); } +#ifdef GAME_SHADER_DLL // Weapon sheen pass // only if doing standard as well (don't do it if cloaked) if ( params[SHEENPASSENABLED]->GetIntValue() ) @@ -469,6 +529,7 @@ BEGIN_VS_SHADER( VertexLitGeneric, "Help for VertexLitGeneric" ) Draw( false ); } } +#endif // Cloak Pass if ( params[CLOAKPASSENABLED]->GetIntValue() ) diff --git a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp index 431d0de5..4fded702 100644 --- a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp @@ -9,19 +9,19 @@ #include "vertexlitgeneric_dx9_helper.h" #include "skin_dx9_helper.h" -#include "VertexLit_and_unlit_Generic_vs20.inc" -#include "VertexLit_and_unlit_Generic_bump_vs20.inc" +#include "SDK_vertexlit_and_unlit_generic_vs20.inc" +#include "SDK_vertexlit_and_unlit_generic_bump_vs20.inc" -#include "vertexlit_and_unlit_generic_ps20.inc" -#include "vertexlit_and_unlit_generic_ps20b.inc" -#include "vertexlit_and_unlit_generic_bump_ps20.inc" -#include "vertexlit_and_unlit_generic_bump_ps20b.inc" +//#include "SDK_vertexlit_and_unlit_generic_ps20.inc" +#include "SDK_vertexlit_and_unlit_generic_ps20b.inc" +//#include "SDK_vertexlit_and_unlit_generic_bump_ps20.inc" +#include "SDK_vertexlit_and_unlit_generic_bump_ps20b.inc" #ifndef _X360 -#include "vertexlit_and_unlit_generic_vs30.inc" -#include "vertexlit_and_unlit_generic_ps30.inc" -#include "vertexlit_and_unlit_generic_bump_vs30.inc" -#include "vertexlit_and_unlit_generic_bump_ps30.inc" +#include "SDK_vertexlit_and_unlit_generic_vs30.inc" +#include "SDK_vertexlit_and_unlit_generic_ps30.inc" +#include "SDK_vertexlit_and_unlit_generic_bump_vs30.inc" +#include "SDK_vertexlit_and_unlit_generic_bump_ps30.inc" #endif #include "commandbuilder.h" @@ -194,6 +194,7 @@ void InitParamsVertexLitGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** para CLEAR_FLAGS( MATERIAL_VAR_BASEALPHAENVMAPMASK ); } +#ifndef MAPBASE if ( IS_FLAG_SET( MATERIAL_VAR_BASEALPHAENVMAPMASK ) && info.m_nBumpmap != -1 && params[info.m_nBumpmap]->IsDefined() && !hasNormalMapAlphaEnvmapMask ) { @@ -210,6 +211,7 @@ void InitParamsVertexLitGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** para params[info.m_nEnvmap]->SetUndefined(); } } +#endif // If mat_specular 0, then get rid of envmap if ( !g_pConfig->UseSpecular() && info.m_nEnvmap != -1 && params[info.m_nEnvmap]->IsDefined() && params[info.m_nBaseTexture]->IsDefined() ) @@ -224,6 +226,22 @@ void InitParamsVertexLitGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** para InitIntParam( info.m_nDepthBlend, params, 0 ); InitFloatParam( info.m_nDepthBlendScale, params, 50.0f ); + + InitIntParam( info.m_nTreeSway, params, 0 ); + InitFloatParam( info.m_nTreeSwayHeight, params, 1000.0f ); + InitFloatParam( info.m_nTreeSwayStartHeight, params, 0.1f ); + InitFloatParam( info.m_nTreeSwayRadius, params, 300.0f ); + InitFloatParam( info.m_nTreeSwayStartRadius, params, 0.2f ); + InitFloatParam( info.m_nTreeSwaySpeed, params, 1.0f ); + InitFloatParam( info.m_nTreeSwaySpeedHighWindMultiplier, params, 2.0f ); + InitFloatParam( info.m_nTreeSwayStrength, params, 10.0f ); + InitFloatParam( info.m_nTreeSwayScrumbleSpeed, params, 5.0f ); + InitFloatParam( info.m_nTreeSwayScrumbleStrength, params, 10.0f ); + InitFloatParam( info.m_nTreeSwayScrumbleFrequency, params, 12.0f ); + InitFloatParam( info.m_nTreeSwayFalloffExp, params, 1.5f ); + InitFloatParam( info.m_nTreeSwayScrumbleFalloffExp, params, 1.0f ); + InitFloatParam( info.m_nTreeSwaySpeedLerpStart, params, 3.0f ); + InitFloatParam( info.m_nTreeSwaySpeedLerpEnd, params, 6.0f ); } @@ -393,6 +411,8 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial bFlashlightNoLambert = true; } + int nTreeSwayMode = clamp( GetIntParam( info.m_nTreeSway, params, 0 ), 0, 2 ); + bool bTreeSway = nTreeSwayMode != 0; bool bAmbientOnly = IsBoolSet( info.m_nAmbientOnly, params ); float fBlendFactor = GetFloatParam( info.m_nDetailTextureBlendFactor, params, 1.0 ); @@ -431,14 +451,11 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial bool bHasVertexColor = bVertexLitGeneric ? false : IS_FLAG_SET( MATERIAL_VAR_VERTEXCOLOR ); bool bHasVertexAlpha = bVertexLitGeneric ? false : IS_FLAG_SET( MATERIAL_VAR_VERTEXALPHA ); -/*^*/ // printf("\t\t[%d] bHasVertexColor\n",(int)bHasVertexColor); -/*^*/ // printf("\t\t[%d] bHasVertexAlpha\n",(int)bHasVertexAlpha); if ( pShader->IsSnapshotting() || (! pContextData ) || ( pContextData->m_bMaterialVarsChanged ) ) { -/*^*/ // printf("\t\t[1] snapshotting=%d pContextData=%08x pContextData->m_bMaterialVarsChanged=%d \n",(int)pShader->IsSnapshotting(), (int)pContextData, pContextData ? (int)pContextData->m_bMaterialVarsChanged : -1 ); - bool bSeamlessBase = IsBoolSet( info.m_nSeamlessBase, params ); - bool bSeamlessDetail = IsBoolSet( info.m_nSeamlessDetail, params ); + bool bSeamlessBase = IsBoolSet( info.m_nSeamlessBase, params ) && !bTreeSway; + bool bSeamlessDetail = IsBoolSet( info.m_nSeamlessDetail, params ) && !bTreeSway; bool bDistanceAlpha = IsBoolSet( info.m_nDistanceAlpha, params ); bool bHasSelfIllum = (!bHasFlashlight || IsX360() ) && IS_FLAG_SET( MATERIAL_VAR_SELFILLUM ); bool bHasEnvmapMask = (!bHasFlashlight || IsX360() ) && info.m_nEnvmapMask != -1 && params[info.m_nEnvmapMask]->IsTexture(); @@ -670,23 +687,23 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial if ( bHasBump || bHasDiffuseWarp ) { #ifndef _X360 - if ( !g_pHardwareConfig->HasFastVertexTextures() ) + if ( !g_pHardwareConfig->SupportsShaderModel_3_0() ) #endif { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, bHalfLambert); - SET_STATIC_VERTEX_SHADER_COMBO( USE_WITH_2B, g_pHardwareConfig->SupportsPixelShaders_2_b() ); + SET_STATIC_VERTEX_SHADER_COMBO( USE_WITH_2B, 1 ); #ifdef _X360 SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); #endif SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); - SET_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs20 ); - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL this way - { - DECLARE_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps20b ); + //if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL this way + //{ + DECLARE_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bHasDiffuseWarp && !bHasSelfIllumFresnel ); @@ -699,38 +716,43 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps20b ); - } - else // ps_2_0 - { - DECLARE_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps20 ); - SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); - SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); - SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bHasDiffuseWarp && !bHasSelfIllumFresnel ); - SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, bHasSelfIllum ); - SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUMFRESNEL, bHasSelfIllumFresnel ); - SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAPALPHAENVMAPMASK, hasNormalMapAlphaEnvmapMask && bHasEnvmap ); - SET_STATIC_PIXEL_SHADER_COMBO( HALFLAMBERT, bHalfLambert); - SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); - SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bHasDetailTexture ); - SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); - SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps20 ); - } + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); + SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps20b ); + //} + //else // ps_2_0 + //{ + // DECLARE_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps20 ); + // SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); + // SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); + // SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bHasDiffuseWarp && !bHasSelfIllumFresnel ); + // SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, bHasSelfIllum ); + // SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUMFRESNEL, bHasSelfIllumFresnel ); + // SET_STATIC_PIXEL_SHADER_COMBO( NORMALMAPALPHAENVMAPMASK, hasNormalMapAlphaEnvmapMask && bHasEnvmap ); + // SET_STATIC_PIXEL_SHADER_COMBO( HALFLAMBERT, bHalfLambert); + // SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); + // SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bHasDetailTexture ); + // SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); + // SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); + // SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps20 ); + //} } #ifndef _X360 else { - // The vertex shader uses the vertex id stream - SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + const bool bFastVertexTextures = g_pHardwareConfig->HasFastVertexTextures(); - DECLARE_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs30 ); + // The vertex shader uses the vertex id stream + if ( bFastVertexTextures ) + SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, bHalfLambert); SET_STATIC_VERTEX_SHADER_COMBO( USE_WITH_2B, true ); SET_STATIC_VERTEX_SHADER_COMBO( DECAL, bIsDecal ); - SET_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( SM30_VERTEXID, bFastVertexTextures ); + SET_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); SET_STATIC_PIXEL_SHADER_COMBO( LIGHTWARPTEXTURE, bHasDiffuseWarp && !bHasSelfIllumFresnel ); @@ -743,7 +765,8 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps30 ); + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); + SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps30 ); } #endif } @@ -766,12 +789,12 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial } #ifndef _X360 - if ( !g_pHardwareConfig->HasFastVertexTextures() ) + if ( !g_pHardwareConfig->SupportsShaderModel_3_0() ) #endif { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, bHasVertexColor || bHasVertexAlpha ); SET_STATIC_VERTEX_SHADER_COMBO( CUBEMAP, bHasEnvmap ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, bHalfLambert ); @@ -781,11 +804,12 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_STATIC_VERTEX_SHADER_COMBO( SEPARATE_DETAIL_UVS, IsBoolSet( info.m_nSeparateDetailUVs, params ) ); SET_STATIC_VERTEX_SHADER_COMBO( USE_STATIC_CONTROL_FLOW, bUseStaticControlFlow ); SET_STATIC_VERTEX_SHADER_COMBO( DONT_GAMMA_CONVERT_VERTEX_COLOR, (! bSRGBWrite ) && bHasVertexColor ); - SET_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( TREESWAY, bTreeSway ? nTreeSwayMode : 0 ); + SET_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_vs20 ); - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send Gl this way - { - DECLARE_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps20b ); + //if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send Gl this way + //{ + DECLARE_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM_ENVMAPMASK_ALPHA, ( hasSelfIllumInEnvMapMask && ( bHasEnvmapMask ) ) ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP_SPHERE_LEGACY, bHasLegacyEnvSphereMap ); @@ -808,40 +832,43 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_STATIC_PIXEL_SHADER_COMBO( DEPTHBLEND, bDoDepthBlend ); SET_STATIC_PIXEL_SHADER_COMBO( SRGB_INPUT_ADAPTER, bSRGBInputAdapter ? 1 : 0 ); SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps20b ); - } - else // ps_2_0 - { - DECLARE_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps20 ); - SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM_ENVMAPMASK_ALPHA, ( hasSelfIllumInEnvMapMask && ( bHasEnvmapMask ) ) ); - SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); - SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP_SPHERE_LEGACY, bHasLegacyEnvSphereMap ); - SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); - SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); - SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); - SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, bHasSelfIllum ); - SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR, bHasVertexColor ); - SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); - SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bHasDetailTexture ); - SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); - SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS_BASE, bSeamlessBase ); - SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS_DETAIL, bSeamlessDetail ); - SET_STATIC_PIXEL_SHADER_COMBO( DISTANCEALPHA, bDistanceAlpha ); - SET_STATIC_PIXEL_SHADER_COMBO( DISTANCEALPHAFROMDETAIL, bDistanceAlphaFromDetail ); - SET_STATIC_PIXEL_SHADER_COMBO( SOFT_MASK, bSoftMask ); - SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bOutline ); - SET_STATIC_PIXEL_SHADER_COMBO( OUTER_GLOW, bGlow ); - SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps20 ); - } + SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps20b ); + //} + //else // ps_2_0 + //{ + // DECLARE_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps20 ); + // SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM_ENVMAPMASK_ALPHA, ( hasSelfIllumInEnvMapMask && ( bHasEnvmapMask ) ) ); + // SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); + // SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP_SPHERE_LEGACY, bHasLegacyEnvSphereMap ); + // SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); + // SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM, bHasSelfIllum ); + // SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR, bHasVertexColor ); + // SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, bHasFlashlight ); + // SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, bHasDetailTexture ); + // SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_BLEND_MODE, nDetailBlendMode ); + // SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS_BASE, bSeamlessBase ); + // SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS_DETAIL, bSeamlessDetail ); + // SET_STATIC_PIXEL_SHADER_COMBO( DISTANCEALPHA, bDistanceAlpha ); + // SET_STATIC_PIXEL_SHADER_COMBO( DISTANCEALPHAFROMDETAIL, bDistanceAlphaFromDetail ); + // SET_STATIC_PIXEL_SHADER_COMBO( SOFT_MASK, bSoftMask ); + // SET_STATIC_PIXEL_SHADER_COMBO( OUTLINE, bOutline ); + // SET_STATIC_PIXEL_SHADER_COMBO( OUTER_GLOW, bGlow ); + // SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); + // SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps20 ); + //} } #ifndef _X360 else { - // The vertex shader uses the vertex id stream - SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + const bool bFastVertexTextures = g_pHardwareConfig->HasFastVertexTextures(); - DECLARE_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_vs30 ); + // The vertex shader uses the vertex id stream + if ( bFastVertexTextures ) + SET_FLAGS2( MATERIAL_VAR2_USES_VERTEXID ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_vs30 ); SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR, bHasVertexColor || bHasVertexAlpha ); SET_STATIC_VERTEX_SHADER_COMBO( CUBEMAP, bHasEnvmap ); SET_STATIC_VERTEX_SHADER_COMBO( HALFLAMBERT, bHalfLambert ); @@ -850,10 +877,12 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_STATIC_VERTEX_SHADER_COMBO( SEAMLESS_DETAIL, bSeamlessDetail ); SET_STATIC_VERTEX_SHADER_COMBO( SEPARATE_DETAIL_UVS, IsBoolSet( info.m_nSeparateDetailUVs, params ) ); SET_STATIC_VERTEX_SHADER_COMBO( DECAL, bIsDecal ); + SET_STATIC_VERTEX_SHADER_COMBO( SM30_VERTEXID, bFastVertexTextures ); SET_STATIC_VERTEX_SHADER_COMBO( DONT_GAMMA_CONVERT_VERTEX_COLOR, bSRGBWrite ? 0 : 1 ); - SET_STATIC_VERTEX_SHADER( vertexlit_and_unlit_generic_vs30 ); + SET_STATIC_VERTEX_SHADER_COMBO( TREESWAY, bTreeSway ? nTreeSwayMode : 0 ); + SET_STATIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_vs30 ); - DECLARE_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps30 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps30 ); SET_STATIC_PIXEL_SHADER_COMBO( SELFILLUM_ENVMAPMASK_ALPHA, ( hasSelfIllumInEnvMapMask && ( bHasEnvmapMask ) ) ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap ); SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP_SPHERE_LEGACY, bHasLegacyEnvSphereMap ); @@ -875,7 +904,7 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); SET_STATIC_PIXEL_SHADER_COMBO( DEPTHBLEND, bDoDepthBlend ); SET_STATIC_PIXEL_SHADER_COMBO( BLENDTINTBYBASEALPHA, bBlendTintByBaseAlpha ); - SET_STATIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps30 ); + SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps30 ); } #endif } @@ -956,10 +985,13 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial if ( bHasDetailTexture ) { - if ( IS_PARAM_DEFINED( info.m_nDetailTextureTransform ) ) - pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureScaledTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nDetailTextureTransform, info.m_nDetailScale ); - else - pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureScaledTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nBaseTextureTransform, info.m_nDetailScale ); + if ( !bTreeSway ) + { + if ( IS_PARAM_DEFINED( info.m_nDetailTextureTransform ) ) + pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureScaledTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nDetailTextureTransform, info.m_nDetailScale ); + else + pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureScaledTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nBaseTextureTransform, info.m_nDetailScale ); + } //Assert( !bHasBump ); if ( info.m_nDetailTint != -1 ) pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstantGammaToLinear( 10, info.m_nDetailTint ); @@ -968,6 +1000,18 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant4( 10, 1, 1, 1, 1 ); } } +#ifdef MAPBASE + else if ( bHasEnvmapMask && info.m_nEnvmapMaskTransform != -1 ) + { + // Use $envmapmasktransform when there is no detail texture taking up this space + pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nEnvmapMaskTransform ); + } + else if ( bHasBump && info.m_nBumpTransform != -1 ) + { + // Use $bumptransform when there is no detail texture taking up this space + pContextData->m_SemiStaticCmdsOut.SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, info.m_nBumpTransform ); + } +#endif if ( bDistanceAlpha ) { float flSoftStart = GetFloatParam( info.m_nEdgeSoftnessStart, params ); @@ -1160,6 +1204,36 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial } pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant_W( 4, info.m_nSelfIllumTint, fBlendFactor ); pContextData->m_SemiStaticCmdsOut.SetAmbientCubeDynamicStateVertexShader(); + + if ( bTreeSway ) + { + float flParams[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + flParams[0] = GetFloatParam( info.m_nTreeSwaySpeedHighWindMultiplier, params, 2.0f ); + flParams[1] = GetFloatParam( info.m_nTreeSwayScrumbleFalloffExp, params, 1.0f ); + flParams[2] = GetFloatParam( info.m_nTreeSwayFalloffExp, params, 1.0f ); + flParams[3] = GetFloatParam( info.m_nTreeSwayScrumbleSpeed, params, 3.0f ); + pContextData->m_SemiStaticCmdsOut.SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_6, flParams ); + + flParams[0] = GetFloatParam( info.m_nTreeSwaySpeedLerpStart, params, 3.0f ); + flParams[1] = GetFloatParam( info.m_nTreeSwaySpeedLerpEnd, params, 6.0f ); + flParams[2] = 0; + flParams[3] = 0; + pContextData->m_SemiStaticCmdsOut.SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_4, flParams ); + + flParams[0] = GetFloatParam( info.m_nTreeSwayHeight, params, 1000.0f ); + flParams[1] = GetFloatParam( info.m_nTreeSwayStartHeight, params, 0.1f ); + flParams[2] = GetFloatParam( info.m_nTreeSwayRadius, params, 300.0f ); + flParams[3] = GetFloatParam( info.m_nTreeSwayStartRadius, params, 0.2f ); + pContextData->m_SemiStaticCmdsOut.SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_8, flParams ); + + flParams[0] = GetFloatParam( info.m_nTreeSwaySpeed, params, 1.0f ); + flParams[1] = GetFloatParam( info.m_nTreeSwayStrength, params, 10.0f ); + flParams[2] = GetFloatParam( info.m_nTreeSwayScrumbleFrequency, params, 12.0f ); + flParams[3] = GetFloatParam( info.m_nTreeSwayScrumbleStrength, params, 10.0f ); + pContextData->m_SemiStaticCmdsOut.SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_9, flParams ); + } + pContextData->m_SemiStaticCmdsOut.End(); } } @@ -1243,59 +1317,65 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial if ( bHasBump || bHasDiffuseWarp ) { #ifndef _X360 - if ( !g_pHardwareConfig->HasFastVertexTextures() ) + if ( !g_pHardwareConfig->SupportsShaderModel_3_0() ) #endif { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_DYNAMIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_bump_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_bump_vs20 ); // Bind ps_2_b shader so we can get shadow mapping... - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL this way - { - DECLARE_DYNAMIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps20b ); + //if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL this way + //{ + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( AMBIENT_LIGHT, lightState.m_bAmbientLight ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); // SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_bump_ps20b ); - } - else + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_bump_ps20b ); + //} + /*else { - DECLARE_DYNAMIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( AMBIENT_LIGHT, lightState.m_bAmbientLight ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_bump_ps20 ); - } + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_bump_ps20 ); + }*/ } #ifndef _X360 else { - pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0 ); + const bool bFastVertexTextures = g_pHardwareConfig->HasFastVertexTextures(); - DECLARE_DYNAMIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs30 ); + if ( bFastVertexTextures ) + pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0 ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogIndex ); SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 ); - SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() && bFastVertexTextures ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER( vertexlit_and_unlit_generic_bump_vs30 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_bump_vs30 ); - DECLARE_DYNAMIC_PIXEL_SHADER( vertexlit_and_unlit_generic_bump_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_bump_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); SET_DYNAMIC_PIXEL_SHADER_COMBO( AMBIENT_LIGHT, lightState.m_bAmbientLight ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); // SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_bump_ps30 ); + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_bump_ps30 ); - bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() || !bIsDecal }; - pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + if ( bFastVertexTextures ) + { + bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() || !bIsDecal }; + pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + } } #endif } @@ -1309,12 +1389,12 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial } #ifndef _X360 - if ( !g_pHardwareConfig->HasFastVertexTextures() ) + if ( !g_pHardwareConfig->SupportsShaderModel_3_0() ) #endif { bool bUseStaticControlFlow = g_pHardwareConfig->SupportsStaticControlFlow(); - DECLARE_DYNAMIC_VERTEX_SHADER( vertexlit_and_unlit_generic_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT_VERTEX, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT_LIGHTMAP, lightState.m_bStaticLightTexel ? 1 : 0); @@ -1325,12 +1405,12 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING)!=0); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); SET_DYNAMIC_VERTEX_SHADER_COMBO( NUM_LIGHTS, bUseStaticControlFlow ? 0 : lightState.m_nNumLights ); - SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_vs20 ); // Bind ps_2_b shader so we can get shadow mapping - if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL this way - { - DECLARE_DYNAMIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps20b ); + //if ( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL this way + //{ + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps20b ); // SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); @@ -1339,25 +1419,28 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING) ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_ps20b ); - } - else + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_ps20b ); + //} + /*else { - DECLARE_DYNAMIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( STATIC_LIGHT_LIGHTMAP, lightState.m_bStaticLightTexel ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING) ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_ps20 ); - } + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_ps20 ); + }*/ } #ifndef _X360 else { - pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0 ); + const bool bFastVertexTextures = g_pHardwareConfig->HasFastVertexTextures(); - DECLARE_DYNAMIC_VERTEX_SHADER( vertexlit_and_unlit_generic_vs30 ); + if ( bFastVertexTextures ) + pShader->SetHWMorphVertexShaderState( VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0 ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_vertexlit_and_unlit_generic_vs30 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DYNAMIC_LIGHT, lightState.HasDynamicLight() ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT_VERTEX, lightState.m_bStaticLightVertex ? 1 : 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( STATIC_LIGHT_LIGHTMAP, lightState.m_bStaticLightTexel ? 1 : 0 ); @@ -1365,21 +1448,24 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, numBones > 0 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING)!=0); - SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( MORPHING, pShaderAPI->IsHWMorphingEnabled() && bFastVertexTextures ); SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); - SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_vs30 ); + SET_DYNAMIC_VERTEX_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_vs30 ); - DECLARE_DYNAMIC_PIXEL_SHADER( vertexlit_and_unlit_generic_ps30 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps30 ); // SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); SET_DYNAMIC_PIXEL_SHADER_COMBO( STATIC_LIGHT_LIGHTMAP, lightState.m_bStaticLightTexel ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( DEBUG_LUXELS, bHasMatLuxel ? 1 : 0 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING) ); - SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, vertexlit_and_unlit_generic_ps30 ); + SET_DYNAMIC_PIXEL_SHADER_CMD( DynamicCmdsOut, sdk_vertexlit_and_unlit_generic_ps30 ); - bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() || !bIsDecal }; - pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + if ( bFastVertexTextures ) + { + bool bUnusedTexCoords[3] = { false, false, !pShaderAPI->IsHWMorphingEnabled() || !bIsDecal }; + pShaderAPI->MarkUnusedVertexFields( 0, 3, bUnusedTexCoords ); + } } #endif } @@ -1395,6 +1481,7 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial float eyePos[4]; pShaderAPI->GetWorldSpaceCameraPosition( eyePos ); + eyePos[3] = pShaderAPI->GetFloatRenderingParameter( FLOAT_RENDERPARM_MINIMUMLIGHTING ); DynamicCmdsOut.SetPixelShaderConstant( 20, eyePos ); // Non-bump case does its own depth feathering work @@ -1436,6 +1523,25 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial DynamicCmdsOut.SetPixelShaderConstant( 24, worldToTexture.Base(), 4 ); } + + if ( bTreeSway ) + { + float fTempConst[4]; + fTempConst[1] = pShaderAPI->CurrentTime(); + if ( params[info.m_nTreeSwayStatic]->GetIntValue() == 0 ) + { + const Vector& windDir = pShaderAPI->GetVectorRenderingParameter( VECTOR_RENDERPARM_WIND_DIRECTION ); + fTempConst[2] = windDir.x; + fTempConst[3] = windDir.y; + } + else + { + // Use a static value instead of the env_wind value. + params[info.m_nTreeSwayStaticValues]->GetVecValue( fTempConst+2, 2 ); + } + DynamicCmdsOut.SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_7, fTempConst ); + } + DynamicCmdsOut.End(); pShaderAPI->ExecuteCommandBuffer( DynamicCmdsOut.Base() ); } @@ -1449,7 +1555,7 @@ void DrawVertexLitGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, IS IShaderShadow* pShaderShadow, bool bVertexLitGeneric, VertexLitGeneric_DX9_Vars_t &info, VertexCompressionType_t vertexCompression, CBasePerMaterialContextData **pContextDataPtr ) { - if ( WantsSkinShader( params, info ) && g_pHardwareConfig->SupportsPixelShaders_2_b() && g_pConfig->UseBumpmapping() && g_pConfig->UsePhong() ) + if ( WantsSkinShader( params, info ) /*&& g_pHardwareConfig->SupportsPixelShaders_2_b()*/ && g_pConfig->UseBumpmapping() && g_pConfig->UsePhong() ) { DrawSkin_DX9( pShader, params, pShaderAPI, pShaderShadow, info, vertexCompression, pContextDataPtr ); return; diff --git a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h index 5820dbeb..10bbbedd 100644 --- a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h +++ b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h @@ -132,6 +132,29 @@ struct VertexLitGeneric_DX9_Vars_t int m_nBlendTintByBaseAlpha; int m_nTintReplacesBaseColor; + +#ifdef MAPBASE + // Parameter ported from Alien Swarm. See bPhongHalfLambert in DrawSkin_DX9_Internal() for more info. + int m_nPhongDisableHalfLambert; +#endif + + int m_nTreeSway; + int m_nTreeSwayHeight; + int m_nTreeSwayStartHeight; + int m_nTreeSwayRadius; + int m_nTreeSwayStartRadius; + int m_nTreeSwaySpeed; + int m_nTreeSwaySpeedHighWindMultiplier; + int m_nTreeSwayStrength; + int m_nTreeSwayScrumbleSpeed; + int m_nTreeSwayScrumbleStrength; + int m_nTreeSwayScrumbleFrequency; + int m_nTreeSwayFalloffExp; + int m_nTreeSwayScrumbleFalloffExp; + int m_nTreeSwaySpeedLerpStart; + int m_nTreeSwaySpeedLerpEnd; + int m_nTreeSwayStatic; + int m_nTreeSwayStaticValues; }; void InitParamsVertexLitGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, bool bVertexLitGeneric, VertexLitGeneric_DX9_Vars_t &info ); diff --git a/mp/src/materialsystem/stdshaders/vortwarp_vs20_helper.h b/mp/src/materialsystem/stdshaders/vortwarp_vs20_helper.h new file mode 100644 index 00000000..20631692 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/vortwarp_vs20_helper.h @@ -0,0 +1,41 @@ +#include "common_vs_fxc.h" + +float Sine( float min, float max, float t ) +{ + return ( sin( t ) * 0.5f + 0.5f ) * ( max - min ) + min; +} + +float3 QuadraticBezier( float3 A, float3 B, float3 C, float t ) +{ + return lerp( lerp( A, B, t ), lerp( B, C, t ), t ); +} + +float3 CubicBezier( float3 A, float3 B, float3 C, float3 D, float t ) +{ + return QuadraticBezier( lerp( A, B, t ), lerp( B, C, t ), lerp( C, D, t ), t ); +} + +void WorldSpaceVertexProcess( in float time, in float3 modelOrigin, inout float3 worldPos, inout float3 worldNormal, inout float3 worldTangentS, inout float3 worldTangentT ) +{ + float myTime = time; + myTime = saturate( 1.0f - myTime ); + myTime *= myTime; + myTime *= myTime; + myTime *= myTime; +// worldPos.z += 72.0f * myTime; + + // end + float3 A = float3( 0.0f, 0.0f, 1.0f ); + float3 B = float3( 1.0f, 1.0f, 1.0f ); + float3 C = float3( 0.0f, 0.0f, 1.0f ); + float3 D = float3( 0.0f, 0.0f, 1.0f ); + // start + +// float3 modelOrigin = float3( 70.0f, -14.0f, 0.0f ); + + float t = worldPos.z * ( 1.0f / ( 72.0f ) ); // about 72 inches tall + t = saturate( t ); + float3 worldPosDelta = ( worldPos - modelOrigin ) * CubicBezier( A, B, C, D, t ); + worldPosDelta.z += Sine( 0.0f, 10.0, worldPos.z ); + worldPos = lerp( worldPos, worldPosDelta + modelOrigin, myTime ); +} diff --git a/mp/src/materialsystem/stdshaders/water.cpp b/mp/src/materialsystem/stdshaders/water.cpp index 11353577..4b5a1d6c 100644 --- a/mp/src/materialsystem/stdshaders/water.cpp +++ b/mp/src/materialsystem/stdshaders/water.cpp @@ -10,21 +10,21 @@ #include "common_hlsl_cpp_consts.h" // hack hack hack! #include "convar.h" -#include "WaterCheap_vs20.inc" -#include "WaterCheap_ps20.inc" -#include "WaterCheap_ps20b.inc" -#include "Water_vs20.inc" -#include "Water_ps20.inc" -#include "water_ps20b.inc" +#include "SDK_WaterCheap_vs20.inc" +#include "SDK_WaterCheap_ps20.inc" +#include "SDK_WaterCheap_ps20b.inc" +#include "SDK_Water_vs20.inc" +#include "SDK_Water_ps20.inc" +#include "SDK_Water_ps20b.inc" #ifndef _X360 static ConVar r_waterforceexpensive( "r_waterforceexpensive", "0", FCVAR_ARCHIVE ); #endif -DEFINE_FALLBACK_SHADER( Water, Water_DX9_HDR ) +DEFINE_FALLBACK_SHADER( SDK_Water, SDK_Water_DX9_HDR ) -BEGIN_VS_SHADER( Water_DX90, - "Help for Water" ) +BEGIN_VS_SHADER( SDK_Water_DX90, + "Help for SDK_Water" ) BEGIN_SHADER_PARAMS SHADER_PARAM( REFRACTTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_WaterRefraction", "" ) @@ -151,6 +151,19 @@ BEGIN_VS_SHADER( Water_DX90, if ( params[ENVMAP]->IsDefined() ) { LoadCubeMap( ENVMAP, TEXTUREFLAGS_SRGB ); + +#ifdef MAPBASE + if (mat_specular_disable_on_missing.GetBool()) + { + // Revert to defaultcubemap when the envmap texture is missing + // (should be equivalent to toolsblack in Mapbase) + if (params[ENVMAP]->GetTextureValue()->IsError()) + { + params[ENVMAP]->SetStringValue( "engine/defaultcubemap" ); + LoadCubeMap( ENVMAP, TEXTUREFLAGS_SRGB ); + } + } +#endif } if ( params[NORMALMAP]->IsDefined() ) { @@ -236,17 +249,17 @@ BEGIN_VS_SHADER( Water_DX90, } } - DECLARE_STATIC_VERTEX_SHADER( water_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_water_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( MULTITEXTURE,fabs(Scroll1.x) > 0.0); SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURE, params[BASETEXTURE]->IsTexture() ); - SET_STATIC_VERTEX_SHADER( water_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_water_vs20 ); // "REFLECT" "0..1" // "REFRACT" "0..1" if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( water_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_water_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( REFLECT, bReflection ); SET_STATIC_PIXEL_SHADER_COMBO( REFRACT, bRefraction ); SET_STATIC_PIXEL_SHADER_COMBO( ABOVEWATER, params[ABOVEWATER]->GetIntValue() ); @@ -254,18 +267,18 @@ BEGIN_VS_SHADER( Water_DX90, SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE, params[BASETEXTURE]->IsTexture() ); SET_STATIC_PIXEL_SHADER_COMBO( BLURRY_REFRACT, params[BLURREFRACT]->GetIntValue() ); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); - SET_STATIC_PIXEL_SHADER( water_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_water_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( water_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_water_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( REFLECT, bReflection ); SET_STATIC_PIXEL_SHADER_COMBO( REFRACT, bRefraction ); SET_STATIC_PIXEL_SHADER_COMBO( ABOVEWATER, params[ABOVEWATER]->GetIntValue() ); SET_STATIC_PIXEL_SHADER_COMBO( MULTITEXTURE,fabs(Scroll1.x) > 0.0); SET_STATIC_PIXEL_SHADER_COMBO( BASETEXTURE, params[BASETEXTURE]->IsTexture() ); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); - SET_STATIC_PIXEL_SHADER( water_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_water_ps20 ); } FogToFogColor(); @@ -368,21 +381,26 @@ BEGIN_VS_SHADER( Water_DX90, pShaderAPI->SetPixelShaderFogParams( 8 ); - DECLARE_DYNAMIC_VERTEX_SHADER( water_vs20 ); - SET_DYNAMIC_VERTEX_SHADER( water_vs20 ); + float vEyePos[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos ); + vEyePos[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( 9, vEyePos ); + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_water_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_water_vs20 ); if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( water_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_water_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, pShaderAPI->ShouldWriteDepthToDestAlpha() ); - SET_DYNAMIC_PIXEL_SHADER( water_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_water_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( water_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_water_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( water_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_water_ps20 ); } } Draw(); @@ -430,13 +448,13 @@ BEGIN_VS_SHADER( Water_DX90, } } - DECLARE_STATIC_VERTEX_SHADER( watercheap_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_watercheap_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( BLEND, bBlend && bRefraction ); - SET_STATIC_VERTEX_SHADER( watercheap_vs20 ); + SET_STATIC_VERTEX_SHADER( sdk_watercheap_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( watercheap_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_watercheap_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( FRESNEL, params[NOFRESNEL]->GetIntValue() == 0 ); SET_STATIC_PIXEL_SHADER_COMBO( BLEND, bBlend ); SET_STATIC_PIXEL_SHADER_COMBO( REFRACTALPHA, bRefraction ); @@ -445,11 +463,11 @@ BEGIN_VS_SHADER( Water_DX90, params[SCROLL1]->GetVecValue( Scroll1.Base(), 4 ); SET_STATIC_PIXEL_SHADER_COMBO( MULTITEXTURE,fabs(Scroll1.x) > 0.0); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); - SET_STATIC_PIXEL_SHADER( watercheap_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_watercheap_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( watercheap_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_watercheap_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( FRESNEL, params[NOFRESNEL]->GetIntValue() == 0 ); SET_STATIC_PIXEL_SHADER_COMBO( BLEND, bBlend ); SET_STATIC_PIXEL_SHADER_COMBO( REFRACTALPHA, bRefraction ); @@ -458,7 +476,7 @@ BEGIN_VS_SHADER( Water_DX90, params[SCROLL1]->GetVecValue( Scroll1.Base(), 4 ); SET_STATIC_PIXEL_SHADER_COMBO( MULTITEXTURE,fabs(Scroll1.x) > 0.0); SET_STATIC_PIXEL_SHADER_COMBO( NORMAL_DECODE_MODE, (int) nNormalDecodeMode ); - SET_STATIC_PIXEL_SHADER( watercheap_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_watercheap_ps20 ); } // HDRFIXME: test cheap water! @@ -495,6 +513,11 @@ BEGIN_VS_SHADER( Water_DX90, }; pShaderAPI->SetPixelShaderConstant( 1, cheapWaterParams ); + float vEyePos[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos ); + vEyePos[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( 4, vEyePos ); + if( g_pConfig->bShowSpecular ) { SetPixelShaderConstant( 2, REFLECTTINT, REFLECTBLENDFACTOR ); @@ -521,22 +544,22 @@ BEGIN_VS_SHADER( Water_DX90, pShaderAPI->SetVertexShaderConstant( VERTEX_SHADER_SHADER_SPECIFIC_CONST_3, vc0, 1 ); } - DECLARE_DYNAMIC_VERTEX_SHADER( watercheap_vs20 ); - SET_DYNAMIC_VERTEX_SHADER( watercheap_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_watercheap_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_watercheap_vs20 ); if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( watercheap_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_watercheap_ps20b ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( watercheap_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_watercheap_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( watercheap_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_watercheap_ps20 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( watercheap_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_watercheap_ps20 ); } } Draw(); @@ -599,14 +622,14 @@ END_SHADER //----------------------------------------------------------------------------- // This allows us to use a block labelled 'Water_DX9_HDR' in the water materials //----------------------------------------------------------------------------- -BEGIN_INHERITED_SHADER( Water_DX9_HDR, Water_DX90, - "Help for Water_DX9_HDR" ) +BEGIN_INHERITED_SHADER( SDK_Water_DX9_HDR, SDK_Water_DX90, + "Help for SDK_Water_DX9_HDR" ) SHADER_FALLBACK { if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) { - return "WATER_DX90"; + return "SDK_WATER_DX90"; } return 0; } diff --git a/mp/src/materialsystem/stdshaders/water_ps2x_helper.h b/mp/src/materialsystem/stdshaders/water_ps2x_helper.h index b15b9986..ba5f214b 100644 --- a/mp/src/materialsystem/stdshaders/water_ps2x_helper.h +++ b/mp/src/materialsystem/stdshaders/water_ps2x_helper.h @@ -23,6 +23,8 @@ struct DrawWater_params_t float4 pixelFogParams; float fWaterFogStart; float fWaterFogEndMinusStart; + float3 vEyePos; + float3 vWorldPos; }; void DrawWater( in DrawWater_params_t i, @@ -232,7 +234,7 @@ void DrawWater( in DrawWater_params_t i, } #if (PIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE) - fogFactor = CalcRangeFog( i.vProjPos.z, i.pixelFogParams.x, i.pixelFogParams.z, i.pixelFogParams.w ); + fogFactor = CalcRangeFogFactorNonFixedFunction( i.vWorldPos, i.vEyePos, i.pixelFogParams.z, i.pixelFogParams.x, i.pixelFogParams.w ); #else fogFactor = 0; #endif diff --git a/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp b/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp new file mode 100644 index 00000000..99ac7914 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp @@ -0,0 +1,181 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BaseVSShader.h" +#include "cpp_shader_constant_register_map.h" + +#include "sdk_windowimposter_vs20.inc" +#include "sdk_windowimposter_ps20.inc" +#include "sdk_windowimposter_ps20b.inc" + + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DEFINE_FALLBACK_SHADER( sdk_windowimposter, sdk_windowimposter_DX90 ) + +BEGIN_VS_SHADER( sdk_windowimposter_DX90, + "Help for WindowImposter_DX90" ) + + BEGIN_SHADER_PARAMS + SHADER_PARAM( ENVMAP, SHADER_PARAM_TYPE_TEXTURE, "shadertest/shadertest_env", "envmap" ) + +#ifdef MAPBASE + SHADER_PARAM( ENVMAPFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "" ) +#endif + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + SHADER_PARAM( ENVMAPPARALLAX, SHADER_PARAM_TYPE_BOOL, "0", "Enables parallax correction code for env_cubemaps" ) + SHADER_PARAM( ENVMAPPARALLAXOBB1, SHADER_PARAM_TYPE_VEC4, "[1 0 0 0]", "The first line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB2, SHADER_PARAM_TYPE_VEC4, "[0 1 0 0]", "The second line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB3, SHADER_PARAM_TYPE_VEC4, "[0 0 1 0]", "The third line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPORIGIN, SHADER_PARAM_TYPE_VEC3, "[0 0 0]", "The world space position of the env_cubemap being corrected" ) +#endif + END_SHADER_PARAMS + + // Set up anything that is necessary to make decisions in SHADER_FALLBACK. + SHADER_INIT_PARAMS() + { +#ifdef MAPBASE + if( !params[ENVMAPFRAME]->IsDefined() ) + params[ENVMAPFRAME]->SetIntValue( 0 ); +#endif + } + + SHADER_FALLBACK + { + return NULL; + } + + SHADER_INIT + { + LoadCubeMap( ENVMAP ); +#ifdef MAPBASE + if (mat_specular_disable_on_missing.GetBool()) + { + // Revert to defaultcubemap when the envmap texture is missing + // (should be equivalent to toolsblack in Mapbase) + if (params[ENVMAP]->GetTextureValue()->IsError()) + { + params[ENVMAP]->SetStringValue( "engine/defaultcubemap" ); + LoadCubeMap( ENVMAP ); + } + } +#endif + } + + SHADER_DRAW + { +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + bool hasParallaxCorrection = params[ENVMAPPARALLAX]->GetIntValue() > 0; +#else + bool hasParallaxCorrection = false; +#endif + + SHADOW_STATE + { + if( g_pHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + pShaderShadow->EnableSRGBWrite( true ); + + pShaderShadow->EnableTexture( SHADER_SAMPLER0, true ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_windowimposter_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( PARALLAXCORRECT, hasParallaxCorrection ); // Parallax cubemaps enabled for 2_0b and onwards + SET_STATIC_VERTEX_SHADER( sdk_windowimposter_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_windowimposter_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, hasParallaxCorrection ); // Parallax cubemaps enabled for 2_0b and onwards + SET_STATIC_PIXEL_SHADER( sdk_windowimposter_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_windowimposter_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, 0 ); // No parallax cubemaps with ps_2_0 :( + SET_STATIC_PIXEL_SHADER( sdk_windowimposter_ps20 ); + } + + unsigned int flags = VERTEX_POSITION; + int nTexCoordCount = 2; + + if (hasParallaxCorrection) + { + flags |= VERTEX_NORMAL; + nTexCoordCount++; + } + + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, 0, 0 ); + pShaderShadow->EnableBlending( true ); + pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA ); + pShaderShadow->EnableDepthWrites( false ); + FogToFogColor(); + } + DYNAMIC_STATE + { + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_windowimposter_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); + SET_DYNAMIC_VERTEX_SHADER( sdk_windowimposter_vs20 ); + + if( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_windowimposter_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_windowimposter_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_windowimposter_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_windowimposter_ps20 ); + } + + SetModulationVertexShaderDynamicState(); + + pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS ); + + float vEyePos_SpecExponent[4]; + pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent ); + vEyePos_SpecExponent[3] = 0.0f; + pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + if (hasParallaxCorrection) + { + pShaderAPI->SetPixelShaderConstant( 0, params[ENVMAPORIGIN]->GetVecValue() ); + + float* vecs[3]; + vecs[0] = const_cast(params[ENVMAPPARALLAXOBB1]->GetVecValue()); + vecs[1] = const_cast(params[ENVMAPPARALLAXOBB2]->GetVecValue()); + vecs[2] = const_cast(params[ENVMAPPARALLAXOBB3]->GetVecValue()); + float matrix[4][4]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 4; j++) + { + matrix[i][j] = vecs[i][j]; + } + } + matrix[3][0] = matrix[3][1] = matrix[3][2] = 0; + matrix[3][3] = 1; + pShaderAPI->SetPixelShaderConstant( 1, &matrix[0][0], 4 ); + } +#endif + +#ifdef MAPBASE + BindTexture( SHADER_SAMPLER0, ENVMAP, ENVMAPFRAME ); +#else + BindTexture( SHADER_SAMPLER0, ENVMAP, -1 ); +#endif + } + Draw(); + } + +END_SHADER diff --git a/mp/src/materialsystem/stdshaders/worldtwotextureblend.cpp b/mp/src/materialsystem/stdshaders/worldtwotextureblend.cpp index 542013e0..b7aaf6d8 100644 --- a/mp/src/materialsystem/stdshaders/worldtwotextureblend.cpp +++ b/mp/src/materialsystem/stdshaders/worldtwotextureblend.cpp @@ -10,9 +10,9 @@ #include "convar.h" -#include "lightmappedgeneric_vs20.inc" -#include "WorldTwoTextureBlend_ps20.inc" -#include "WorldTwoTextureBlend_ps20b.inc" +#include "SDK_lightmappedgeneric_vs20.inc" +#include "SDK_worldtwotextureblend_ps20.inc" +#include "SDK_worldtwotextureblend_ps20b.inc" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -20,8 +20,8 @@ extern ConVar r_flashlight_version2; // FIXME: Need to make a dx9 version so that "CENTROID" works. -BEGIN_VS_SHADER( WorldTwoTextureBlend, - "Help for WorldTwoTextureBlend" ) +BEGIN_VS_SHADER( SDK_WorldTwoTextureBlend, + "Help for SDK_WorldTwoTextureBlend" ) BEGIN_SHADER_PARAMS SHADER_PARAM_OVERRIDE( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "shadertest/WorldTwoTextureBlend", "iris texture", 0 ) @@ -246,7 +246,7 @@ END_SHADER_PARAMS pShaderShadow->EnableSRGBWrite( true ); - DECLARE_STATIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); + DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); SET_STATIC_VERTEX_SHADER_COMBO( ENVMAP_MASK, false ); SET_STATIC_VERTEX_SHADER_COMBO( BUMPMASK, false ); SET_STATIC_VERTEX_SHADER_COMBO( TANGENTSPACE, hasFlashlight ); @@ -259,11 +259,14 @@ END_SHADER_PARAMS #ifdef _X360 SET_STATIC_VERTEX_SHADER_COMBO( FLASHLIGHT, hasFlashlight ); #endif - SET_STATIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); +#ifdef MAPBASE + SET_STATIC_VERTEX_SHADER_COMBO( BASETEXTURETRANSFORM2, false ); +#endif + SET_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_STATIC_PIXEL_SHADER( worldtwotextureblend_ps20b ); + DECLARE_STATIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20b ); SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, hasDetailTexture ); SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, hasBump ); SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); @@ -273,11 +276,11 @@ END_SHADER_PARAMS SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, hasFlashlight ); SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHTDEPTHFILTERMODE, nShadowFilterMode ); - SET_STATIC_PIXEL_SHADER( worldtwotextureblend_ps20b ); + SET_STATIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20b ); } else { - DECLARE_STATIC_PIXEL_SHADER( worldtwotextureblend_ps20 ); + DECLARE_STATIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20 ); SET_STATIC_PIXEL_SHADER_COMBO( DETAILTEXTURE, hasDetailTexture ); SET_STATIC_PIXEL_SHADER_COMBO( BUMPMAP, hasBump ); SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSEBUMPMAP, hasDiffuseBumpmap ); @@ -286,7 +289,7 @@ END_SHADER_PARAMS SET_STATIC_PIXEL_SHADER_COMBO( DETAIL_ALPHA_MASK_BASE_TEXTURE, bHasDetailAlpha ); SET_STATIC_PIXEL_SHADER_COMBO( FLASHLIGHT, hasFlashlight ); SET_STATIC_PIXEL_SHADER_COMBO( SEAMLESS, bSeamlessMapping ); - SET_STATIC_PIXEL_SHADER( worldtwotextureblend_ps20 ); + SET_STATIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20 ); } // HACK HACK HACK - enable alpha writes all the time so that we have them for @@ -387,12 +390,12 @@ END_SHADER_PARAMS } MaterialFogMode_t fogType = pShaderAPI->GetSceneFogMode(); - DECLARE_DYNAMIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG, fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); SET_DYNAMIC_VERTEX_SHADER_COMBO( FASTPATH, bVertexShaderFastPath ); SET_DYNAMIC_VERTEX_SHADER_COMBO( LIGHTING_PREVIEW, pShaderAPI->GetIntRenderingParameter(INT_RENDERPARM_ENABLE_FIXED_LIGHTING)!=0); - SET_DYNAMIC_VERTEX_SHADER( lightmappedgeneric_vs20 ); + SET_DYNAMIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 ); bool bWriteDepthToAlpha; bool bWriteWaterFogToAlpha; @@ -412,24 +415,24 @@ END_SHADER_PARAMS if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - DECLARE_DYNAMIC_PIXEL_SHADER( worldtwotextureblend_ps20b ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20b ); // Don't write fog to alpha if we're using translucency SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, bWriteWaterFogToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITE_DEPTH_TO_DESTALPHA, bWriteDepthToAlpha ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( FLASHLIGHTSHADOWS, bFlashlightShadows ); - SET_DYNAMIC_PIXEL_SHADER( worldtwotextureblend_ps20b ); + SET_DYNAMIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20b ); } else { - DECLARE_DYNAMIC_PIXEL_SHADER( worldtwotextureblend_ps20 ); + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20 ); // Don't write fog to alpha if we're using translucency SET_DYNAMIC_PIXEL_SHADER_COMBO( WRITEWATERFOGTODESTALPHA, (fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z) && (nBlendType != BT_BLENDADD) && (nBlendType != BT_BLEND) && !bIsAlphaTested ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); - SET_DYNAMIC_PIXEL_SHADER( worldtwotextureblend_ps20 ); + SET_DYNAMIC_PIXEL_SHADER( sdk_worldtwotextureblend_ps20 ); } @@ -484,8 +487,10 @@ END_SHADER_PARAMS SHADER_DRAW { + //ConVarRef r_flashlight_version2 = ConVarRef( "r_flashlight_version2" ); + bool bHasFlashlight = UsingFlashlight( params ); - if ( bHasFlashlight && ( IsX360() || r_flashlight_version2.GetInt() ) ) + if ( bHasFlashlight /*&& ( IsX360() || r_flashlight_version2.GetInt() )*/ ) { DrawPass( params, pShaderAPI, pShaderShadow, false, vertexCompression ); SHADOW_STATE diff --git a/mp/src/materialsystem/stdshaders/worldvertextransition.cpp b/mp/src/materialsystem/stdshaders/worldvertextransition.cpp index e7d6a9ab..d8671ddd 100644 --- a/mp/src/materialsystem/stdshaders/worldvertextransition.cpp +++ b/mp/src/materialsystem/stdshaders/worldvertextransition.cpp @@ -9,15 +9,13 @@ #include "BaseVSShader.h" #include "convar.h" -#include "worldvertextransition_dx8_helper.h" +//#include "worldvertextransition_dx8_helper.h" #include "lightmappedgeneric_dx9_helper.h" -static LightmappedGeneric_DX9_Vars_t s_info; +DEFINE_FALLBACK_SHADER( SDK_WorldVertexTransition, SDK_WorldVertexTransition_DX9 ) -DEFINE_FALLBACK_SHADER( WorldVertexTransition, WorldVertexTransition_DX9 ) - -BEGIN_VS_SHADER( WorldVertexTransition_DX9, "Help for WorldVertexTransition" ) +BEGIN_VS_SHADER( SDK_WorldVertexTransition_DX9, "Help for SDK_WorldVertexTransition" ) BEGIN_SHADER_PARAMS SHADER_PARAM( ALBEDO, SHADER_PARAM_TYPE_TEXTURE, "shadertest/BaseTexture", "albedo (Base texture with no baked lighting)" ) @@ -50,6 +48,10 @@ BEGIN_VS_SHADER( WorldVertexTransition_DX9, "Help for WorldVertexTransition" ) SHADER_PARAM( BUMPMASK, SHADER_PARAM_TYPE_TEXTURE, "models/shadertest/shader1_normal", "bump map" ) SHADER_PARAM( BASETEXTURE2, SHADER_PARAM_TYPE_TEXTURE, "shadertest/detail", "detail texture" ) SHADER_PARAM( FRAME2, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $basetexture2" ) +#ifdef MAPBASE + // This needs to be a SHADER_PARAM_TYPE_STRING so it isn't considered "defined" by default. + SHADER_PARAM( BASETEXTURETRANSFORM2, SHADER_PARAM_TYPE_STRING, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$basetexture2 texcoord transform" ) +#endif SHADER_PARAM( BASETEXTURENOENVMAP, SHADER_PARAM_TYPE_BOOL, "0", "" ) SHADER_PARAM( BASETEXTURE2NOENVMAP, SHADER_PARAM_TYPE_BOOL, "0", "" ) SHADER_PARAM( DETAIL_ALPHA_MASK_BASE_TEXTURE, SHADER_PARAM_TYPE_BOOL, "0", @@ -61,17 +63,21 @@ BEGIN_VS_SHADER( WorldVertexTransition_DX9, "Help for WorldVertexTransition" ) SHADER_PARAM( MASKEDBLENDING, SHADER_PARAM_TYPE_INTEGER, "0", "blend using texture with no vertex alpha. For using texture blending on non-displacements" ) SHADER_PARAM( SSBUMP, SHADER_PARAM_TYPE_INTEGER, "0", "whether or not to use alternate bumpmap format with height" ) SHADER_PARAM( SEAMLESS_SCALE, SHADER_PARAM_TYPE_FLOAT, "0", "Scale factor for 'seamless' texture mapping. 0 means to use ordinary mapping" ) - END_SHADER_PARAMS - void SetupVars( WorldVertexTransitionEditor_DX8_Vars_t& info ) - { - info.m_nBaseTextureVar = BASETEXTURE; - info.m_nBaseTextureFrameVar = FRAME; - info.m_nBaseTextureTransformVar = BASETEXTURETRANSFORM; - info.m_nBaseTexture2Var = BASETEXTURE2; - info.m_nBaseTexture2FrameVar = FRAME2; - info.m_nBaseTexture2TransformVar = BASETEXTURETRANSFORM; // FIXME!!!! - } + SHADER_PARAM( PHONG, SHADER_PARAM_TYPE_BOOL, "0", "enables phong lighting" ) + SHADER_PARAM( PHONGBOOST, SHADER_PARAM_TYPE_FLOAT, "1.0", "Phong overbrightening factor (specular mask channel should be authored to account for this)" ) + SHADER_PARAM( PHONGFRESNELRANGES, SHADER_PARAM_TYPE_VEC3, "[0 0.5 1]", "Parameters for remapping fresnel output" ) + SHADER_PARAM( PHONGEXPONENT, SHADER_PARAM_TYPE_FLOAT, "5.0", "Phong exponent for local specular lights" ) + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + SHADER_PARAM( ENVMAPPARALLAX, SHADER_PARAM_TYPE_BOOL, "0", "Enables parallax correction code for env_cubemaps" ) + SHADER_PARAM( ENVMAPPARALLAXOBB1, SHADER_PARAM_TYPE_VEC4, "[1 0 0 0]", "The first line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB2, SHADER_PARAM_TYPE_VEC4, "[0 1 0 0]", "The second line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPPARALLAXOBB3, SHADER_PARAM_TYPE_VEC4, "[0 0 1 0]", "The third line of the parallax correction OBB matrix" ) + SHADER_PARAM( ENVMAPORIGIN, SHADER_PARAM_TYPE_VEC3, "[0 0 0]", "The world space position of the env_cubemap being corrected" ) +#endif + END_SHADER_PARAMS void SetupVars( LightmappedGeneric_DX9_Vars_t& info ) { @@ -105,6 +111,9 @@ BEGIN_VS_SHADER( WorldVertexTransition_DX9, "Help for WorldVertexTransition" ) info.m_nBumpFrame2 = BUMPFRAME2; info.m_nBaseTexture2 = BASETEXTURE2; info.m_nBaseTexture2Frame = FRAME2; +#ifdef MAPBASE + info.m_nBaseTexture2Transform = BASETEXTURETRANSFORM2; +#endif info.m_nBumpTransform2 = BUMPTRANSFORM2; info.m_nBumpMask = BUMPMASK; info.m_nBaseTextureNoEnvmap = BASETEXTURENOENVMAP; @@ -119,6 +128,20 @@ BEGIN_VS_SHADER( WorldVertexTransition_DX9, "Help for WorldVertexTransition" ) info.m_nSelfShadowedBumpFlag = SSBUMP; info.m_nSeamlessMappingScale = SEAMLESS_SCALE; info.m_nAlphaTestReference = -1; + + info.m_nPhong = PHONG; + info.m_nPhongBoost = PHONGBOOST; + info.m_nPhongFresnelRanges = PHONGFRESNELRANGES; + info.m_nPhongExponent = PHONGEXPONENT; + +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemaps + info.m_nEnvmapParallax = ENVMAPPARALLAX; + info.m_nEnvmapParallaxObb1 = ENVMAPPARALLAXOBB1; + info.m_nEnvmapParallaxObb2 = ENVMAPPARALLAXOBB2; + info.m_nEnvmapParallaxObb3 = ENVMAPPARALLAXOBB3; + info.m_nEnvmapOrigin = ENVMAPORIGIN; +#endif } SHADER_FALLBACK @@ -129,29 +152,27 @@ BEGIN_VS_SHADER( WorldVertexTransition_DX9, "Help for WorldVertexTransition" ) return 0; } + // Set up anything that is necessary to make decisions in SHADER_FALLBACK. SHADER_INIT_PARAMS() { - SetupVars( s_info ); - InitParamsLightmappedGeneric_DX9( this, params, pMaterialName, s_info ); + LightmappedGeneric_DX9_Vars_t info; + SetupVars( info ); + InitParamsLightmappedGeneric_DX9( this, params, pMaterialName, info ); } SHADER_INIT { - SetupVars( s_info ); - InitLightmappedGeneric_DX9( this, params, s_info ); + LightmappedGeneric_DX9_Vars_t info; + SetupVars( info ); + InitLightmappedGeneric_DX9( this, params, info ); } SHADER_DRAW { - if ( UsingEditor( params ) ) - { - WorldVertexTransitionEditor_DX8_Vars_t info; - SetupVars( info ); - DrawWorldVertexTransitionEditor_DX8( this, params, pShaderAPI, pShaderShadow, info ); - return; - } - - DrawLightmappedGeneric_DX9( this, params, pShaderAPI, pShaderShadow, s_info, pContextDataPtr ); + LightmappedGeneric_DX9_Vars_t info; + SetupVars( info ); + DrawLightmappedGeneric_DX9( this, params, pShaderAPI, pShaderShadow, info, pContextDataPtr ); } + END_SHADER diff --git a/mp/src/materialsystem/stdshaders/worldvertextransition_dx8_helper.cpp b/mp/src/materialsystem/stdshaders/worldvertextransition_dx8_helper.cpp index 6a49fd71..cac439cc 100644 --- a/mp/src/materialsystem/stdshaders/worldvertextransition_dx8_helper.cpp +++ b/mp/src/materialsystem/stdshaders/worldvertextransition_dx8_helper.cpp @@ -8,7 +8,7 @@ #include "worldvertextransition_dx8_helper.h" #include "BaseVSShader.h" -#include "WorldVertexTransition.inc" +#include "SDK_WorldVertexTransition.inc" // memdbgon must be the last include file in a .cpp file!!! @@ -45,9 +45,9 @@ void DrawWorldVertexTransitionEditor_DX8( CBaseVSShader *pShader, IMaterialVar** int fmt = VERTEX_POSITION | VERTEX_COLOR; pShaderShadow->VertexShaderVertexFormat( fmt, 2, 0, 0 ); - worldvertextransition_Static_Index vshIndex; - pShaderShadow->SetVertexShader( "WorldVertexTransition", vshIndex.GetIndex() ); - pShaderShadow->SetPixelShader( "WorldVertexTransition_Editor" ); + sdk_worldvertextransition_Static_Index vshIndex; + pShaderShadow->SetVertexShader( "SDK_WorldVertexTransition", vshIndex.GetIndex() ); + pShaderShadow->SetPixelShader( "SDK_WorldVertexTransition_Editor" ); pShader->FogToFogColor(); } @@ -73,7 +73,7 @@ void DrawWorldVertexTransitionEditor_DX8( CBaseVSShader *pShader, IMaterialVar** pShader->SetVertexShaderTextureTransform( nTextureTransformConst, info.m_nBaseTextureTransformVar ); pShader->SetVertexShaderTextureTransform( nTextureTransformConst2, info.m_nBaseTexture2TransformVar ); - worldvertextransition_Dynamic_Index vshIndex; + sdk_worldvertextransition_Dynamic_Index vshIndex; vshIndex.SetDOWATERFOG( pShaderAPI->GetSceneFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ); pShaderAPI->SetVertexShaderIndex( vshIndex.GetIndex() ); } diff --git a/mp/src/public/bspfile.h b/mp/src/public/bspfile.h index d4c3a059..9f415474 100644 --- a/mp/src/public/bspfile.h +++ b/mp/src/public/bspfile.h @@ -59,7 +59,11 @@ // 16 bit short limits #define MAX_MAP_MODELS 1024 #define MAX_MAP_BRUSHES 8192 +#ifdef MAPBASE +#define MAX_MAP_ENTITIES 65536 // According to ficool2, this limit is bogus/not enforced by the engine and can be "safely" raised. +#else #define MAX_MAP_ENTITIES 8192 +#endif #define MAX_MAP_TEXINFO 12288 #define MAX_MAP_TEXDATA 2048 #define MAX_MAP_DISPINFO 2048 @@ -90,9 +94,17 @@ #define MAX_MAP_LIGHTING 0x1000000 #define MAX_MAP_VISIBILITY 0x1000000 // increased BSPVERSION 7 #define MAX_MAP_TEXTURES 1024 +#ifdef MAPBASE +#define MAX_MAP_WORLDLIGHTS 65536 // According to ficool2, this limit is bogus/not enforced by the engine and can be "safely" raised. +#else #define MAX_MAP_WORLDLIGHTS 8192 +#endif #define MAX_MAP_CUBEMAPSAMPLES 1024 +#ifdef MAPBASE +#define MAX_MAP_OVERLAYS 8192 // According to ficool2, this limit is bogus/not enforced by the engine and can be "safely" raised. +#else #define MAX_MAP_OVERLAYS 512 +#endif #define MAX_MAP_WATEROVERLAYS 16384 #define MAX_MAP_TEXDATA_STRING_DATA 256000 #define MAX_MAP_TEXDATA_STRING_TABLE 65536 diff --git a/mp/src/public/const.h b/mp/src/public/const.h index 0ed97e9e..0899c7b2 100644 --- a/mp/src/public/const.h +++ b/mp/src/public/const.h @@ -258,7 +258,12 @@ enum SolidFlags_t FSOLID_ROOT_PARENT_ALIGNED = 0x0100, // Collisions are defined in root parent's local coordinate space FSOLID_TRIGGER_TOUCH_DEBRIS = 0x0200, // This trigger will touch debris objects - FSOLID_MAX_BITS = 10 +#ifdef MAPBASE + // From https://developer.valvesoftware.com/wiki/Owner + FSOLID_COLLIDE_WITH_OWNER = 0X0400, +#endif + + FSOLID_MAX_BITS = 11 }; //----------------------------------------------------------------------------- @@ -443,5 +448,17 @@ class CThreadNullMutex; typedef CThreadNullMutex CSourceMutex; #endif +//Tony; added for IPlayerInfo V3. +//Putting all standard possible stances, but GetStance in CBasePlayer will only return standing or ducking by default - +//up to the mod to specify the others, or override what GetStance returns. +enum player_Stance +{ + PINFO_STANCE_STANDING = 0, + PINFO_STANCE_DUCKING, + + PINFO_STANCE_SPRINTING, + PINFO_STANCE_PRONE, +}; + #endif diff --git a/mp/src/public/discord_register.h b/mp/src/public/discord_register.h new file mode 100644 index 00000000..16fb42f3 --- /dev/null +++ b/mp/src/public/discord_register.h @@ -0,0 +1,26 @@ +#pragma once + +#if defined(DISCORD_DYNAMIC_LIB) +#if defined(_WIN32) +#if defined(DISCORD_BUILDING_SDK) +#define DISCORD_EXPORT __declspec(dllexport) +#else +#define DISCORD_EXPORT __declspec(dllimport) +#endif +#else +#define DISCORD_EXPORT __attribute__((visibility("default"))) +#endif +#else +#define DISCORD_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command); +DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId); + +#ifdef __cplusplus +} +#endif diff --git a/mp/src/public/discord_rpc.h b/mp/src/public/discord_rpc.h new file mode 100644 index 00000000..3e1441e0 --- /dev/null +++ b/mp/src/public/discord_rpc.h @@ -0,0 +1,87 @@ +#pragma once +#include + +// clang-format off + +#if defined(DISCORD_DYNAMIC_LIB) +# if defined(_WIN32) +# if defined(DISCORD_BUILDING_SDK) +# define DISCORD_EXPORT __declspec(dllexport) +# else +# define DISCORD_EXPORT __declspec(dllimport) +# endif +# else +# define DISCORD_EXPORT __attribute__((visibility("default"))) +# endif +#else +# define DISCORD_EXPORT +#endif + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DiscordRichPresence { + const char* state; /* max 128 bytes */ + const char* details; /* max 128 bytes */ + int64_t startTimestamp; + int64_t endTimestamp; + const char* largeImageKey; /* max 32 bytes */ + const char* largeImageText; /* max 128 bytes */ + const char* smallImageKey; /* max 32 bytes */ + const char* smallImageText; /* max 128 bytes */ + const char* partyId; /* max 128 bytes */ + int partySize; + int partyMax; + const char* matchSecret; /* max 128 bytes */ + const char* joinSecret; /* max 128 bytes */ + const char* spectateSecret; /* max 128 bytes */ + int8_t instance; +} DiscordRichPresence; + +typedef struct DiscordUser { + const char* userId; + const char* username; + const char* discriminator; + const char* avatar; +} DiscordUser; + +typedef struct DiscordEventHandlers { + void (*ready)(const DiscordUser* request); + void (*disconnected)(int errorCode, const char* message); + void (*errored)(int errorCode, const char* message); + void (*joinGame)(const char* joinSecret); + void (*spectateGame)(const char* spectateSecret); + void (*joinRequest)(const DiscordUser* request); +} DiscordEventHandlers; + +#define DISCORD_REPLY_NO 0 +#define DISCORD_REPLY_YES 1 +#define DISCORD_REPLY_IGNORE 2 + +DISCORD_EXPORT void Discord_Initialize(const char* applicationId, + DiscordEventHandlers* handlers, + int autoRegister, + const char* optionalSteamId); +DISCORD_EXPORT void Discord_Shutdown(void); + +/* checks for incoming messages, dispatches callbacks */ +DISCORD_EXPORT void Discord_RunCallbacks(void); + +/* If you disable the lib starting its own io thread, you'll need to call this from your own */ +#ifdef DISCORD_DISABLE_IO_THREAD +DISCORD_EXPORT void Discord_UpdateConnection(void); +#endif + +DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); +DISCORD_EXPORT void Discord_ClearPresence(void); + +DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); + +DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/mp/src/public/engine/ishadowmgr.h b/mp/src/public/engine/ishadowmgr.h index babac919..5165a8ab 100644 --- a/mp/src/public/engine/ishadowmgr.h +++ b/mp/src/public/engine/ishadowmgr.h @@ -40,6 +40,7 @@ enum ShadowFlags_t { SHADOW_FLAGS_FLASHLIGHT = (1 << 0), SHADOW_FLAGS_SHADOW = (1 << 1), + // Update this if you add flags SHADOW_FLAGS_LAST_FLAG = SHADOW_FLAGS_SHADOW }; diff --git a/mp/src/public/fgdlib/gamedata.h b/mp/src/public/fgdlib/gamedata.h index cf8b5be1..5d55db2f 100644 --- a/mp/src/public/fgdlib/gamedata.h +++ b/mp/src/public/fgdlib/gamedata.h @@ -88,6 +88,11 @@ class GameData bool RemapNameField( const char *pszInValue, char *pszOutValue, TNameFixup NameFixup ); bool LoadFGDMaterialExclusions( TokenReader &tr ); bool LoadFGDAutoVisGroups( TokenReader &tr ); + +#ifdef MAPBASE + // Sets up for additional instance remap fixes from Mapbase + void SetupInstanceRemapParams( int iStartNodes, int iStartBrushSide, bool bRemapVecLines ); +#endif CUtlVector< FGDMatExlcusions_s > m_FGDMaterialExclusions; @@ -109,6 +114,11 @@ class GameData matrix3x4_t m_InstanceMat; // matrix of the origin and rotation of rendering char m_InstancePrefix[ 128 ]; // the prefix used for the instance name remapping GDclass *m_InstanceClass; // the entity class that is being remapped +#ifdef MAPBASE + int m_InstanceStartAINodes; // the number of AI nodes in the level (for AI node remapping) + int m_InstanceStartSide; // the number of brush sides in the level (for brush side remapping) + bool m_bRemapVecLines; // allows ivVecLine to be remapped +#endif }; diff --git a/mp/src/public/fgdlib/gdvar.h b/mp/src/public/fgdlib/gdvar.h index 197ff30d..e6eb37fe 100644 --- a/mp/src/public/fgdlib/gdvar.h +++ b/mp/src/public/fgdlib/gdvar.h @@ -92,6 +92,14 @@ class GDinputvariable inline GDIV_TYPE GetType() { return m_eType; } const char *GetTypeText(void); + +#ifdef MAPBASE + // The FGD library normally enforces that variable types should always stay the same. + // The new AI node remapping code needs to change the "nodeid" keyvalue on AI nodes so + // it's properly recognized as a node ID which needs to be remapped. + // That's what this hack is for. + inline void ForceSetType( GDIV_TYPE newType ) { m_eType = newType; } +#endif inline void GetDefault(int *pnStore) { diff --git a/mp/src/public/materialsystem/imaterialsystem.h b/mp/src/public/materialsystem/imaterialsystem.h index bf896579..36f346e0 100644 --- a/mp/src/public/materialsystem/imaterialsystem.h +++ b/mp/src/public/materialsystem/imaterialsystem.h @@ -423,10 +423,22 @@ struct FlashlightState_t { m_bEnableShadows = false; // Provide reasonable defaults for shadow depth mapping parameters m_bDrawShadowFrustum = false; +#ifdef ASW_PROJECTED_TEXTURES + m_flShadowMapResolution = 2048.0f; + m_flShadowFilterSize = 0.5f; + m_flShadowSlopeScaleDepthBias = 16.0f; + m_flShadowDepthBias = 0.0005f; +#elif defined(MAPBASE) + m_flShadowMapResolution = 2048; + m_flShadowFilterSize = 1.0f; + m_flShadowSlopeScaleDepthBias = 4.0f; + m_flShadowDepthBias = 0.00001f; +#else m_flShadowMapResolution = 1024.0f; m_flShadowFilterSize = 3.0f; m_flShadowSlopeScaleDepthBias = 16.0f; m_flShadowDepthBias = 0.0005f; +#endif m_flShadowJitterSeed = 0.0f; m_flShadowAtten = 0.0f; m_bScissor = false; @@ -435,6 +447,16 @@ struct FlashlightState_t m_nRight = -1; m_nBottom = -1; m_nShadowQuality = 0; +#ifdef ASW_PROJECTED_TEXTURES + m_bOrtho = false; + m_fOrthoLeft = -1.0f; + m_fOrthoRight = 1.0f; + m_fOrthoTop = -1.0f; + m_fOrthoBottom = 1.0f; + + m_fBrightnessScale = 1.0f; + m_pSpotlightTexture = NULL; +#endif } Vector m_vecLightOrigin; @@ -461,6 +483,22 @@ struct FlashlightState_t float m_flShadowAtten; int m_nShadowQuality; +#ifdef ASW_PROJECTED_TEXTURES + bool m_bOrtho; + float m_fOrthoLeft; + float m_fOrthoRight; + float m_fOrthoTop; + float m_fOrthoBottom; + + float m_FarZAtten; + float m_fBrightnessScale; + bool m_bGlobalLight; +#endif + +#ifdef MAPBASE + bool m_bAlwaysDraw; +#endif + // Getters for scissor members bool DoScissor() { return m_bScissor; } int GetLeft() { return m_nLeft; } diff --git a/mp/src/public/mathlib/vector.h b/mp/src/public/mathlib/vector.h index a797da25..64e14802 100644 --- a/mp/src/public/mathlib/vector.h +++ b/mp/src/public/mathlib/vector.h @@ -1557,6 +1557,11 @@ public: inline void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f, vec_t iw=0.0f) { x = ix; y = iy; z = iz; w = iw; } +#ifdef MAPBASE_VSCRIPT + // Needed to get around vec_t recognition and inlining + void ScriptInit( float ix, float iy, float iz, float iw ) { Init( ix, iy, iz, iw ); } +#endif + bool IsValid() const; void Invalidate(); diff --git a/mp/src/public/mathlib/vector4d.h b/mp/src/public/mathlib/vector4d.h index 2b20c882..c5d0699f 100644 --- a/mp/src/public/mathlib/vector4d.h +++ b/mp/src/public/mathlib/vector4d.h @@ -41,9 +41,15 @@ public: Vector4D(void); Vector4D(vec_t X, vec_t Y, vec_t Z, vec_t W); Vector4D(const float *pFloat); +#ifdef MAPBASE + Vector4D(const Vector& vec, vec_t W); +#endif // Initialization void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f, vec_t iw=0.0f); +#ifdef MAPBASE + void Init( const Vector& vec, vec_t W ); +#endif // Got any nasty NAN's? bool IsValid() const; @@ -222,6 +228,14 @@ inline Vector4D::Vector4D(const float *pFloat) Assert( IsValid() ); } +#ifdef MAPBASE +inline Vector4D::Vector4D(const Vector& vec, vec_t W ) +{ + x = vec.x; y = vec.y; z = vec.z; w = W; + Assert( IsValid() ); +} +#endif + //----------------------------------------------------------------------------- // copy constructor @@ -243,6 +257,14 @@ inline void Vector4D::Init( vec_t ix, vec_t iy, vec_t iz, vec_t iw ) Assert( IsValid() ); } +#ifdef MAPBASE +inline void Vector4D::Init( const Vector& vec, vec_t iw ) +{ + x = vec.x; y = vec.y; z = vec.z; w = iw; + Assert( IsValid() ); +} +#endif + inline void Vector4D::Random( vec_t minVal, vec_t maxVal ) { x = minVal + ((vec_t)rand() / VALVE_RAND_MAX) * (maxVal - minVal); diff --git a/mp/src/public/particles/particles.h b/mp/src/public/particles/particles.h index 4066d85e..37af4bf5 100644 --- a/mp/src/public/particles/particles.h +++ b/mp/src/public/particles/particles.h @@ -1391,7 +1391,7 @@ private: -class CM128InitialAttributeIterator : public CStridedConstPtr +class CM128InitialAttributeIterator : public CFltx4StridedConstPtr { public: FORCEINLINE CM128InitialAttributeIterator( int nAttribute, CParticleCollection *pParticles ) @@ -1401,7 +1401,7 @@ public: }; -class CM128AttributeIterator : public CStridedConstPtr +class CM128AttributeIterator : public CFltx4StridedConstPtr { public: FORCEINLINE CM128AttributeIterator( int nAttribute, CParticleCollection *pParticles ) @@ -1419,7 +1419,7 @@ public: } }; -class CM128AttributeWriteIterator : public CStridedPtr +class CM128AttributeWriteIterator : public CFltx4StridedPtr { public: FORCEINLINE CM128AttributeWriteIterator( void ) diff --git a/mp/src/public/renderparm.h b/mp/src/public/renderparm.h index a473c29b..d56e258b 100644 --- a/mp/src/public/renderparm.h +++ b/mp/src/public/renderparm.h @@ -28,6 +28,8 @@ enum RenderParamVector_t VECTOR_RENDERPARM_HMDWARP_ASPECT, INT_RENDERPARM_DISTORTION_TYPE, + VECTOR_RENDERPARM_WIND_DIRECTION, + MAX_VECTOR_RENDER_PARMS = 20 }; @@ -52,6 +54,9 @@ enum RenderParamInt_t INT_RENDERPARM_BACK_BUFFER_INDEX, + INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST, + INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_LAST = INT_FLASHLIGHT_DEPTHTEXTURE_FALLBACK_FIRST + 4, + MAX_INT_RENDER_PARMS = 20 }; @@ -72,4 +77,9 @@ enum RenderParamTexture_t #define ENABLE_FIXED_LIGHTING_OUTPUTMRTS_FOR_DEFERRED_LIGHTING 2 #define ENABLE_FIXED_LIGHTING_OUTPUTNORMAL_AND_DEPTH 3 +enum RenderParamFloat_t +{ + FLOAT_RENDERPARM_MINIMUMLIGHTING = 0, +}; + #endif // RENDERPARM_H diff --git a/mp/src/public/rope_shared.h b/mp/src/public/rope_shared.h index 54c28829..fe79b6e6 100644 --- a/mp/src/public/rope_shared.h +++ b/mp/src/public/rope_shared.h @@ -28,7 +28,11 @@ #define ROPE_COLLIDE (1<<2) // Collide with the world? #define ROPE_SIMULATE (1<<3) // Is the rope valid? #define ROPE_BREAKABLE (1<<4) // Can the endpoints detach? +#ifdef MAPBASE +#define ROPE_USE_WIND (1<<5) // Wind simulation on this rope. +#else #define ROPE_NO_WIND (1<<5) // No wind simulation on this rope. +#endif #define ROPE_INITIAL_HANG (1<<6) // By default, ropes will simulate for a bit internally when they // are created so they sag, but dynamically created ropes for things // like harpoons don't want this. diff --git a/mp/src/public/stdstring.h b/mp/src/public/stdstring.h index c72320f7..50ed1c29 100644 --- a/mp/src/public/stdstring.h +++ b/mp/src/public/stdstring.h @@ -70,6 +70,15 @@ public: std::string *pString = (std::string *)fieldInfo.pField; return pString->empty(); } + +#ifdef MAPBASE + virtual bool Parse( const SaveRestoreFieldInfo_t &fieldInfo, char const* szValue ) + { + std::string *pString = (std::string *)fieldInfo.pField; + pString->assign(szValue); + return true; + } +#endif }; //------------------------------------- @@ -85,4 +94,9 @@ inline ISaveRestoreOps *GetStdStringDataOps() #define DEFINE_STDSTRING(name) \ { FIELD_CUSTOM, #name, { offsetof(classNameTypedef,name), 0 }, 1, FTYPEDESC_SAVE, NULL, GetStdStringDataOps(), NULL } +#ifdef MAPBASE +#define DEFINE_KEYSTDSTRING(name,mapname) \ + { FIELD_CUSTOM, #name, { offsetof(classNameTypedef, name), 0 }, 1, FTYPEDESC_SAVE | FTYPEDESC_KEY, mapname, GetStdStringDataOps(), NULL } +#endif + #endif // STDSTRING_H diff --git a/mp/src/public/tier0/basetypes.h b/mp/src/public/tier0/basetypes.h index 22ce51bc..e8387b56 100644 --- a/mp/src/public/tier0/basetypes.h +++ b/mp/src/public/tier0/basetypes.h @@ -147,6 +147,11 @@ typedef wchar_t ucs2; // under windows wchar_t is ucs2 typedef unsigned short ucs2; #endif +#ifdef MAPBASE +// I'm using ThreeState_t a lot more now and I'm tired of typing this out so much. +#define TO_THREESTATE(num) static_cast(num) +#endif + enum ThreeState_t { TRS_FALSE, diff --git a/mp/src/public/tier1/convar.h b/mp/src/public/tier1/convar.h index 832fbb00..a08e1ce4 100644 --- a/mp/src/public/tier1/convar.h +++ b/mp/src/public/tier1/convar.h @@ -21,6 +21,7 @@ #include "tier1/utlvector.h" #include "tier1/utlstring.h" #include "icvar.h" +#include "color.h" #ifdef _WIN32 #define FORCEINLINE_CVAR FORCEINLINE @@ -350,6 +351,7 @@ public: // Retrieve value FORCEINLINE_CVAR float GetFloat( void ) const; FORCEINLINE_CVAR int GetInt( void ) const; + FORCEINLINE_CVAR Color GetColor( void ) const; FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); } FORCEINLINE_CVAR char const *GetString( void ) const; @@ -435,6 +437,16 @@ FORCEINLINE_CVAR int ConVar::GetInt( void ) const return m_pParent->m_nValue; } +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVar::GetColor( void ) const +{ + unsigned char *pColorElement = ((unsigned char *)&m_pParent->m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} + //----------------------------------------------------------------------------- // Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. @@ -467,6 +479,7 @@ public: // Get/Set value float GetFloat( void ) const; int GetInt( void ) const; + Color GetColor( void ) const; bool GetBool() const { return !!GetInt(); } const char *GetString( void ) const; @@ -521,6 +534,16 @@ FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const return m_pConVarState->m_nValue; } +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVarRef::GetColor( void ) const +{ + unsigned char *pColorElement = ((unsigned char *)&m_pConVarState->m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} + //----------------------------------------------------------------------------- // Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. //----------------------------------------------------------------------------- @@ -632,6 +655,18 @@ private: static ConCommand name##_command( #name, name, description ); \ static void name( const CCommand &args ) +#ifdef CLIENT_DLL + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description ); \ + static void name( const CCommand &args ) +#endif + #define CON_COMMAND_F( name, description, flags ) \ static void name( const CCommand &args ); \ static ConCommand name##_command( #name, name, description, flags ); \ diff --git a/mp/src/public/tier1/mapbase_con_groups.h b/mp/src/public/tier1/mapbase_con_groups.h new file mode 100644 index 00000000..c0e35626 --- /dev/null +++ b/mp/src/public/tier1/mapbase_con_groups.h @@ -0,0 +1,38 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: See tier1/mapbase_con_groups.cpp for more information +// +// $NoKeywords: $ +//============================================================================= + +#ifndef CON_VERBOSE_COLORS_H +#define CON_VERBOSE_COLORS_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//static const Color CON_COLOR_DEV_VERBOSE( 192, 128, 192, 255 ); + +// General +#define CON_GROUP_MAPBASE_MISC "Mapbase Misc." +#define CON_GROUP_PHYSICS "Physics" + +// Server +#define CON_GROUP_IO_SYSTEM "I/O System" +#define CON_GROUP_NPC_AI "NPC AI" +#define CON_GROUP_NPC_SCRIPTS "NPC Scripts" +#define CON_GROUP_CHOREO "Choreo" + +// VScript +#define CON_GROUP_VSCRIPT "VScript" +#define CON_GROUP_VSCRIPT_PRINT "VScript Print" + +// Mapbase console group message. +void CGMsg( int level, const char *pszGroup, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); + +#define CGWarning CGMsg + +#endif diff --git a/mp/src/public/tier1/utlblockmemory.h b/mp/src/public/tier1/utlblockmemory.h index b4a254ff..8d86c59e 100644 --- a/mp/src/public/tier1/utlblockmemory.h +++ b/mp/src/public/tier1/utlblockmemory.h @@ -135,10 +135,10 @@ CUtlBlockMemory::~CUtlBlockMemory() template< class T, class I > void CUtlBlockMemory::Swap( CUtlBlockMemory< T, I > &mem ) { - swap( m_pMemory, mem.m_pMemory ); - swap( m_nBlocks, mem.m_nBlocks ); - swap( m_nIndexMask, mem.m_nIndexMask ); - swap( m_nIndexShift, mem.m_nIndexShift ); + V_swap( m_pMemory, mem.m_pMemory ); + V_swap( m_nBlocks, mem.m_nBlocks ); + V_swap( m_nIndexMask, mem.m_nIndexMask ); + V_swap( m_nIndexShift, mem.m_nIndexShift ); } diff --git a/mp/src/public/tier1/utlbuffer.h b/mp/src/public/tier1/utlbuffer.h index 0de85fda..84e9faa7 100644 --- a/mp/src/public/tier1/utlbuffer.h +++ b/mp/src/public/tier1/utlbuffer.h @@ -142,6 +142,9 @@ public: // Makes sure we've got at least this much memory void EnsureCapacity( int num ); + + // Access for direct read into buffer + void * AccessForDirectRead( int nBytes ); // Attaches the buffer to external memory.... void SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags = 0 ); @@ -596,7 +599,11 @@ inline void CUtlBuffer::GetObject( T *dest ) { if ( CheckGet( sizeof(T) ) ) { +#ifdef MAPBASE + if ( ( sizeof( T ) == 1 ) || !m_Byteswap.IsSwappingBytes() ) +#else if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#endif { *dest = *(T *)PeekGet(); } @@ -628,7 +635,11 @@ inline void CUtlBuffer::GetTypeBin( T &dest ) { if ( CheckGet( sizeof(T) ) ) { +#ifdef MAPBASE + if ( ( sizeof( T ) == 1 ) || !m_Byteswap.IsSwappingBytes() ) +#else if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#endif { dest = *(T *)PeekGet(); } @@ -814,7 +825,11 @@ inline void CUtlBuffer::PutObject( T *src ) { if ( CheckPut( sizeof(T) ) ) { +#ifdef MAPBASE + if ( ( sizeof( T ) == 1 ) || !m_Byteswap.IsSwappingBytes() ) +#else if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#endif { *(T *)PeekPut() = *src; } @@ -843,7 +858,11 @@ inline void CUtlBuffer::PutTypeBin( T src ) { if ( CheckPut( sizeof(T) ) ) { - if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#ifdef MAPBASE + if ((sizeof(T) == 1) || !m_Byteswap.IsSwappingBytes()) +#else + if (!m_Byteswap.IsSwappingBytes() || (sizeof(T) == 1)) +#endif { *(T *)PeekPut() = src; } @@ -1090,5 +1109,13 @@ inline void CUtlBuffer::CopyBuffer( const void *pubData, int cubData ) } } +inline void *CUtlBuffer::AccessForDirectRead( int nBytes ) +{ + Assert( m_Get == 0 && m_Put == 0 && m_nMaxPut == 0 ); + EnsureCapacity( nBytes ); + m_nMaxPut = nBytes; + return Base(); +} + #endif // UTLBUFFER_H diff --git a/mp/src/public/tier1/utlsoacontainer.h b/mp/src/public/tier1/utlsoacontainer.h index b9440c5f..284358db 100644 --- a/mp/src/public/tier1/utlsoacontainer.h +++ b/mp/src/public/tier1/utlsoacontainer.h @@ -109,6 +109,95 @@ public: } }; +class CFltx4StridedPtr +{ +private: + typedef __m128 T; + +protected: + T *m_pData; + size_t m_nStride; + +public: + FORCEINLINE CFltx4StridedPtr( void *pData, size_t nByteStride ) + { + m_pData = reinterpret_cast( pData ); + m_nStride = nByteStride / sizeof( T ); + } + + FORCEINLINE CFltx4StridedPtr( void ) {} + T *operator->(void) const + { + return m_pData; + } + + T & operator*(void) const + { + return *m_pData; + } + + FORCEINLINE operator T *(void) + { + return m_pData; + } + + FORCEINLINE CFltx4StridedPtr& operator++(void) + { + m_pData += m_nStride; + return *this; + } + + FORCEINLINE void operator+=( size_t nNumElements ) + { + m_pData += nNumElements * m_nStride; + } + +}; + +class CFltx4StridedConstPtr +{ +private: + typedef __m128 T; + +protected: + const T *m_pData; + size_t m_nStride; + +public: + FORCEINLINE CFltx4StridedConstPtr( void const *pData, size_t nByteStride ) + { + m_pData = reinterpret_cast( pData ); + m_nStride = nByteStride / sizeof( T ); + } + + FORCEINLINE CFltx4StridedConstPtr( void ) {} + + const T *operator->(void) const + { + return m_pData; + } + + const T & operator*(void) const + { + return *m_pData; + } + + FORCEINLINE operator const T *(void) const + { + return m_pData; + } + + FORCEINLINE CFltx4StridedConstPtr &operator++(void) + { + m_pData += m_nStride; + return *this; + } + FORCEINLINE void operator+=( size_t nNumElements ) + { + m_pData += nNumElements*m_nStride; + } +}; + // allowed field data types. if you change these values, you need to change the tables in the .cpp file enum EAttributeDataType { @@ -311,19 +400,19 @@ public: }; -class CFltX4AttributeIterator : public CStridedConstPtr +class CFltX4AttributeIterator : public CFltx4StridedConstPtr { FORCEINLINE CFltX4AttributeIterator( CSOAContainer const *pContainer, int nAttribute, int nRowNumber = 0 ) - : CStridedConstPtr( pContainer->ConstRowPtr( nAttribute, nRowNumber), + : CFltx4StridedConstPtr( pContainer->ConstRowPtr( nAttribute, nRowNumber), pContainer->ItemByteStride( nAttribute ) ) { } }; -class CFltX4AttributeWriteIterator : public CStridedPtr +class CFltX4AttributeWriteIterator : public CFltx4StridedPtr { FORCEINLINE CFltX4AttributeWriteIterator( CSOAContainer const *pContainer, int nAttribute, int nRowNumber = 0 ) - : CStridedPtr( pContainer->RowPtr( nAttribute, nRowNumber), + : CFltx4StridedPtr( pContainer->RowPtr( nAttribute, nRowNumber), pContainer->ItemByteStride( nAttribute ) ) { } diff --git a/mp/src/public/tier1/utlsymbol.h b/mp/src/public/tier1/utlsymbol.h index e90be466..7b2d0282 100644 --- a/mp/src/public/tier1/utlsymbol.h +++ b/mp/src/public/tier1/utlsymbol.h @@ -16,6 +16,9 @@ #include "tier0/threadtools.h" #include "tier1/utlrbtree.h" #include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier1/utllinkedlist.h" +#include "tier1/stringpool.h" //----------------------------------------------------------------------------- @@ -85,13 +88,17 @@ protected: // of strings to symbols and back. The symbol class itself contains // a static version of this class for creating global strings, but this // class can also be instanced to create local symbol tables. +// +// This class stores the strings in a series of string pools. The first +// two bytes of each string are decorated with a hash to speed up +// comparisons. //----------------------------------------------------------------------------- class CUtlSymbolTable { public: // constructor, destructor - CUtlSymbolTable( int growSize = 0, int initSize = 32, bool caseInsensitive = false ); + CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false ); ~CUtlSymbolTable(); // Finds and/or creates a symbol based on the string @@ -111,6 +118,10 @@ public: return m_Lookup.Count(); } + // We store one of these at the beginning of every string to speed + // up comparisons. + typedef unsigned short hashDecoration_t; + protected: class CStringPoolIndex { @@ -120,10 +131,8 @@ protected: } inline CStringPoolIndex( unsigned short iPool, unsigned short iOffset ) - { - m_iPool = iPool; - m_iOffset = iOffset; - } + : m_iPool(iPool), m_iOffset(iOffset) + {} inline bool operator==( const CStringPoolIndex &other ) const { @@ -158,7 +167,9 @@ protected: }; CTree m_Lookup; + bool m_bInsensitive; + mutable unsigned short m_nUserSearchStringHash; mutable const char* m_pUserSearchString; // stores the string data @@ -167,11 +178,14 @@ protected: private: int FindPoolWithSpace( int len ) const; const char* StringFromIndex( const CStringPoolIndex &index ) const; + const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const; friend class CLess; + friend class CSymbolHash; + }; -class CUtlSymbolTableMT : private CUtlSymbolTable +class CUtlSymbolTableMT : public CUtlSymbolTable { public: CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false ) @@ -189,9 +203,9 @@ public: CUtlSymbol Find( const char* pString ) const { - m_lock.LockForRead(); + m_lock.LockForWrite(); CUtlSymbol result = CUtlSymbolTable::Find( pString ); - m_lock.UnlockRead(); + m_lock.UnlockWrite(); return result; } @@ -204,11 +218,7 @@ public: } private: -#if defined(WIN32) || defined(_WIN32) mutable CThreadSpinRWLock m_lock; -#else - mutable CThreadRWLock m_lock; -#endif }; @@ -225,7 +235,6 @@ private: // The handle is a CUtlSymbol for the dirname and the same for the filename, the accessor // copies them into a static char buffer for return. typedef void* FileNameHandle_t; -#define FILENAMEHANDLE_INVALID 0 // Symbol table for more efficiently storing filenames by breaking paths and filenames apart. // Refactored from BaseFileSystem.h @@ -258,6 +267,9 @@ public: int PathIndex(const FileNameHandle_t &handle) { return (( const FileNameHandleInternal_t * )&handle)->path; } bool String( const FileNameHandle_t& handle, char *buf, int buflen ); void RemoveAll(); + void SpewStrings(); + bool SaveToBuffer( CUtlBuffer &buffer ); + bool RestoreFromBuffer( CUtlBuffer &buffer ); private: //CCountedStringPool m_StringPool; @@ -265,5 +277,50 @@ private: mutable CThreadSpinRWLock m_lock; }; +// This creates a simple class that includes the underlying CUtlSymbol +// as a private member and then instances a private symbol table to +// manage those symbols. Avoids the possibility of the code polluting the +// 'global'/default symbol table, while letting the code look like +// it's just using = and .String() to look at CUtlSymbol type objects +// +// NOTE: You can't pass these objects between .dlls in an interface (also true of CUtlSymbol of course) +// +#define DECLARE_PRIVATE_SYMBOLTYPE( typename ) \ + class typename \ + { \ + public: \ + typename(); \ + typename( const char* pStr ); \ + typename& operator=( typename const& src ); \ + bool operator==( typename const& src ) const; \ + const char* String( ) const; \ + private: \ + CUtlSymbol m_SymbolId; \ + }; + +// Put this in the .cpp file that uses the above typename +#define IMPLEMENT_PRIVATE_SYMBOLTYPE( typename ) \ + static CUtlSymbolTable g_##typename##SymbolTable; \ + typename::typename() \ + { \ + m_SymbolId = UTL_INVAL_SYMBOL; \ + } \ + typename::typename( const char* pStr ) \ + { \ + m_SymbolId = g_##typename##SymbolTable.AddString( pStr ); \ + } \ + typename& typename::operator=( typename const& src ) \ + { \ + m_SymbolId = src.m_SymbolId; \ + return *this; \ + } \ + bool typename::operator==( typename const& src ) const \ + { \ + return ( m_SymbolId == src.m_SymbolId ); \ + } \ + const char* typename::String( ) const \ + { \ + return g_##typename##SymbolTable.String( m_SymbolId ); \ + } #endif // UTLSYMBOL_H diff --git a/mp/src/public/vphysics_interface.h b/mp/src/public/vphysics_interface.h index 1852ab8c..d6e61935 100644 --- a/mp/src/public/vphysics_interface.h +++ b/mp/src/public/vphysics_interface.h @@ -961,6 +961,30 @@ struct surfacedata_t surfacegameprops_t game; // Game data / properties surfacesoundhandles_t soundhandles; + +#ifdef MAPBASE_VSCRIPT + // These functions are for the VScript class description. + + float GetFriction() { return physics.friction; } + float GetThickness() { return physics.thickness; } + + float GetJumpFactor() { return game.jumpFactor; } + char GetMaterialChar() { return game.material; } + +#if defined(CLIENT_DLL) || defined(GAME_DLL) + const char* GetSoundStepLeft(); + const char* GetSoundStepRight(); + const char* GetSoundImpactSoft(); + const char* GetSoundImpactHard(); + const char* GetSoundScrapeSmooth(); + const char* GetSoundScrapeRough(); + const char* GetSoundBulletImpact(); + const char* GetSoundRolling(); + const char* GetSoundBreak(); + const char* GetSoundStrain(); +#endif + +#endif }; #define VPHYSICS_SURFACEPROPS_INTERFACE_VERSION "VPhysicsSurfaceProps001" diff --git a/mp/src/public/vscript/ivscript.h b/mp/src/public/vscript/ivscript.h new file mode 100644 index 00000000..fb3a1b79 --- /dev/null +++ b/mp/src/public/vscript/ivscript.h @@ -0,0 +1,1754 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: VScript +// +// Overview +// -------- +// VScript is an abstract binding layer that allows code to expose itself to +// multiple scripting languages in a uniform format. Code can expose +// functions, classes, and data to the scripting languages, and can also +// call functions that reside in scripts. +// +// Initializing +// ------------ +// +// To create a script virtual machine (VM), grab the global instance of +// IScriptManager, call CreateVM, then call Init on the returned VM. Right +// now you can have multiple VMs, but only VMs for a specific language. +// +// Exposing functions and classes +// ------------------------------ +// +// To expose a C++ function to the scripting system, you just need to fill out a +// description block. Using templates, the system will automatically deduce +// all of the binding requirements (parameters and return values). Functions +// are limited as to what the types of the parameters can be. See ScriptVariant_t. +// +// extern IScriptVM *pScriptVM; +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// +// void RegisterFuncs() +// { +// ScriptRegisterFunction( pScriptVM, Foo ); +// ScriptRegisterFunction( pScriptVM, Bar ); +// ScriptRegisterFunction( pScriptVM, FooBar ); +// ScriptRegisterFunctionNamed( pScriptVM, OverlyTechnicalName, "SimpleName" ); +// } +// +// class CMyClass +// { +// public: +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// }; +// +// BEGIN_SCRIPTDESC_ROOT( CMyClass ) +// DEFINE_SCRIPTFUNC( Foo ) +// DEFINE_SCRIPTFUNC( Bar ) +// DEFINE_SCRIPTFUNC( FooBar ) +// DEFINE_SCRIPTFUNC_NAMED( OverlyTechnicalName, "SimpleMemberName" ) +// END_SCRIPTDESC(); +// +// class CMyDerivedClass : public CMyClass +// { +// public: +// float DerivedFunc() const; +// }; +// +// BEGIN_SCRIPTDESC( CMyDerivedClass, CMyClass ) +// DEFINE_SCRIPTFUNC( DerivedFunc ) +// END_SCRIPTDESC(); +// +// CMyDerivedClass derivedInstance; +// +// void AnotherFunction() +// { +// // Manual class exposure +// pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) ); +// +// // Auto registration by instance +// pScriptVM->RegisterInstance( &derivedInstance, "theInstance" ); +// } +// +// Classes with "DEFINE_SCRIPT_CONSTRUCTOR()" in their description can be instanced within scripts +// +// Scopes +// ------ +// Scripts can either be run at the global scope, or in a user defined scope. In the latter case, +// all "globals" within the script are actually in the scope. This can be used to bind private +// data spaces with C++ objects. +// +// Calling a function on a script +// ------------------------------ +// Generally, use the "Call" functions. This example is the equivalent of DoIt("Har", 6.0, 99). +// +// hFunction = pScriptVM->LookupFunction( "DoIt", hScope ); +// pScriptVM->Call( hFunction, hScope, true, NULL, "Har", 6.0, 99 ); +// +//============================================================================= + +#ifndef IVSCRIPT_H +#define IVSCRIPT_H + +#include "platform.h" +#include "datamap.h" +#include "appframework/IAppSystem.h" +#include "tier1/functors.h" +#include "tier0/memdbgon.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifdef VSCRIPT_DLL_EXPORT +#define VSCRIPT_INTERFACE DLL_EXPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_EXPORT +#define VSCRIPT_CLASS DLL_CLASS_EXPORT +#else +#define VSCRIPT_INTERFACE DLL_IMPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_IMPORT +#define VSCRIPT_CLASS DLL_CLASS_IMPORT +#endif + +class CUtlBuffer; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define VSCRIPT_INTERFACE_VERSION "VScriptManager009" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class IScriptVM; +#ifdef MAPBASE_VSCRIPT +class KeyValues; + +// This has been moved up a bit for IScriptManager +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) +#endif + +enum ScriptLanguage_t +{ + SL_NONE, + SL_GAMEMONKEY, + SL_SQUIRREL, + SL_LUA, + SL_PYTHON, + + SL_DEFAULT = SL_SQUIRREL +}; + +class IScriptManager : public IAppSystem +{ +public: + virtual IScriptVM *CreateVM( ScriptLanguage_t language = SL_DEFAULT ) = 0; + virtual void DestroyVM( IScriptVM * ) = 0; + +#ifdef MAPBASE_VSCRIPT + virtual HSCRIPT CreateScriptKeyValues( IScriptVM *pVM, KeyValues *pKV, bool bAllowDestruct ) = 0; + virtual KeyValues *GetKeyValuesFromScriptKV( IScriptVM *pVM, HSCRIPT hSKV ) = 0; +#endif +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT +template T *HScriptToClass( HSCRIPT hObj ) +{ + return (hObj) ? (T*)g_pScriptVM->GetInstanceValue( hObj, GetScriptDesc( (T*)NULL ) ) : NULL; +} +#else +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ExtendedFieldType +{ + FIELD_TYPEUNKNOWN = FIELD_TYPECOUNT, + FIELD_CSTRING, + FIELD_HSCRIPT, + FIELD_VARIANT, +}; + +typedef int ScriptDataType_t; +struct ScriptVariant_t; + +template struct ScriptDeducer { /*enum { FIELD_TYPE = FIELD_TYPEUNKNOWN };*/ }; +#define DECLARE_DEDUCE_FIELDTYPE( fieldType, type ) template<> struct ScriptDeducer { enum { FIELD_TYPE = fieldType }; }; + +DECLARE_DEDUCE_FIELDTYPE( FIELD_VOID, void ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_FLOAT, float ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, const char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, Vector ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const Vector &); +DECLARE_DEDUCE_FIELDTYPE( FIELD_INTEGER, int ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_BOOLEAN, bool ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CHARACTER, char ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_HSCRIPT, HSCRIPT ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VARIANT, ScriptVariant_t ); +#ifdef MAPBASE_VSCRIPT +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, QAngle ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const QAngle& ); +#endif + +#define ScriptDeduceType( T ) ScriptDeducer::FIELD_TYPE + +template +inline const char * ScriptFieldTypeName() +{ + return T::using_unknown_script_type(); +} + +#define DECLARE_NAMED_FIELDTYPE( fieldType, strName ) template <> inline const char * ScriptFieldTypeName() { return strName; } +DECLARE_NAMED_FIELDTYPE( void, "void" ); +DECLARE_NAMED_FIELDTYPE( float, "float" ); +DECLARE_NAMED_FIELDTYPE( const char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( Vector, "vector" ); +DECLARE_NAMED_FIELDTYPE( const Vector&, "vector" ); +DECLARE_NAMED_FIELDTYPE( int, "integer" ); +DECLARE_NAMED_FIELDTYPE( bool, "boolean" ); +DECLARE_NAMED_FIELDTYPE( char, "character" ); +DECLARE_NAMED_FIELDTYPE( HSCRIPT, "hscript" ); +DECLARE_NAMED_FIELDTYPE( ScriptVariant_t, "variant" ); +#ifdef MAPBASE_VSCRIPT +DECLARE_NAMED_FIELDTYPE( QAngle, "vector" ); +DECLARE_NAMED_FIELDTYPE( const QAngle&, "vector" ); +#endif + +inline const char * ScriptFieldTypeName( int16 eType) +{ + switch( eType ) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "cstring"; + case FIELD_VECTOR: return "vector"; + case FIELD_INTEGER: return "integer"; + case FIELD_BOOLEAN: return "boolean"; + case FIELD_CHARACTER: return "character"; + case FIELD_HSCRIPT: return "hscript"; + case FIELD_VARIANT: return "variant"; + default: return "unknown_script_type"; + } +} + +//--------------------------------------------------------- + +struct ScriptFuncDescriptor_t +{ + ScriptFuncDescriptor_t() + { + m_pszFunction = NULL; + m_ReturnType = FIELD_TYPEUNKNOWN; + m_pszDescription = NULL; + } + + const char *m_pszScriptName; + const char *m_pszFunction; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; + CUtlVector m_Parameters; +}; + +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +// VScript Member Variables +// +// An odd concept. Because IScriptInstanceHelper now supports +// get/set metamethods, classes are capable of pretending they +// have member variables which VScript can get and set. +// +// There's no default way of documenting these variables, so even though +// these are not actually binding anything, this is here to allow VScript +// to describe these fake member variables in its documentation. +//--------------------------------------------------------- +struct ScriptMemberDesc_t +{ + const char *m_pszScriptName; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; +}; +#endif + + +//--------------------------------------------------------- + +// Prefix a script description with this in order to not show the function or class in help +#define SCRIPT_HIDE "@" + +// Prefix a script description of a class to indicate it is a singleton and the single instance should be in the help +#define SCRIPT_SINGLETON "!" + +// Prefix a script description with this to indicate it should be represented using an alternate name +#define SCRIPT_ALIAS( alias, description ) "#" alias ":" description + +//--------------------------------------------------------- + +enum ScriptFuncBindingFlags_t +{ + SF_MEMBER_FUNC = 0x01, +}; + +typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ); + +struct ScriptFunctionBinding_t +{ + ScriptFuncDescriptor_t m_desc; + ScriptBindingFunc_t m_pfnBinding; + void * m_pFunction; + unsigned m_flags; +}; + +//--------------------------------------------------------- +class IScriptInstanceHelper +{ +public: + virtual void *GetProxied( void *p ) { return p; } + virtual bool ToString( void *p, char *pBuf, int bufSize ) { return false; } + virtual void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) { return NULL; } + +#ifdef MAPBASE_VSCRIPT + virtual bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ) { return false; } + virtual bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ) { return false; } + + virtual ScriptVariant_t *Add( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Subtract( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Multiply( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Divide( void *p, ScriptVariant_t &variant ) { return NULL; } +#endif +}; + +//--------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT +struct ScriptHook_t; +#endif + +struct ScriptClassDesc_t +{ + ScriptClassDesc_t() : m_pszScriptName( 0 ), m_pszClassname( 0 ), m_pszDescription( 0 ), m_pBaseDesc( 0 ), m_pfnConstruct( 0 ), m_pfnDestruct( 0 ), pHelper(NULL) + { +#ifdef MAPBASE_VSCRIPT + AllClassesDesc().AddToTail(this); +#endif + } + + const char * m_pszScriptName; + const char * m_pszClassname; + const char * m_pszDescription; + ScriptClassDesc_t * m_pBaseDesc; + CUtlVector m_FunctionBindings; + +#ifdef MAPBASE_VSCRIPT + CUtlVector m_Hooks; + CUtlVector m_Members; +#endif + + void *(*m_pfnConstruct)(); + void (*m_pfnDestruct)( void *); + IScriptInstanceHelper * pHelper; // optional helper + +#ifdef MAPBASE_VSCRIPT + static CUtlVector& AllClassesDesc() + { + static CUtlVector classes; + return classes; + } +#endif +}; + +//--------------------------------------------------------- +// A simple variant type. Intentionally not full featured (no implicit conversion, no memory management) +//--------------------------------------------------------- + +enum SVFlags_t +{ + SV_FREE = 0x01, +}; + +#pragma warning(push) +#pragma warning(disable:4800) +struct ScriptVariant_t +{ + ScriptVariant_t() : m_flags( 0 ), m_type( FIELD_VOID ) { m_pVector = 0; } + ScriptVariant_t( int val ) : m_flags( 0 ), m_type( FIELD_INTEGER ) { m_int = val;} + ScriptVariant_t( float val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = val; } + ScriptVariant_t( double val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = (float)val; } + ScriptVariant_t( char val ) : m_flags( 0 ), m_type( FIELD_CHARACTER ) { m_char = val; } + ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; } + ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; } + + ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = &val; } else { m_pVector = new Vector( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const Vector *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = val; } else { m_pVector = new Vector( *val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { if ( !bCopy ) { m_pszString = val; } else { m_pszString = strdup( val ); m_flags |= SV_FREE; } } + +#ifdef MAPBASE_VSCRIPT + ScriptVariant_t( const QAngle &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pAngle = &val; } else { m_pAngle = new QAngle( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const QAngle *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pAngle = val; } else { m_pAngle = new QAngle( *val ); m_flags |= SV_FREE; } } +#endif + + bool IsNull() const { return (m_type == FIELD_VOID ); } + + operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; } + operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; } + operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; } + operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; } + operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; } + operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; } + operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; } +#ifdef MAPBASE_VSCRIPT + operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); static QAngle vecNull(0, 0, 0); return (m_pAngle) ? *m_pAngle : vecNull; } +#endif + + void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; } + void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; } + void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; } + void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; } + void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; } + void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; } + void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; } + void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; } + void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; } +#ifdef MAPBASE_VSCRIPT + void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_pAngle = &vec; } + void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_pAngle = vec; } +#endif + + void Free() { if ( ( m_flags & SV_FREE ) && ( m_type == FIELD_HSCRIPT || m_type == FIELD_VECTOR || m_type == FIELD_CSTRING ) ) delete m_pszString; } // Generally only needed for return results + + template + T Get() + { + T value; + AssignTo( &value ); + return value; + } + + template + bool AssignTo( T *pDest ) + { + ScriptDataType_t destType = ScriptDeduceType( T ); + if ( destType == FIELD_TYPEUNKNOWN ) + { + DevWarning( "Unable to convert script variant to unknown type\n" ); + } + if ( destType == m_type ) + { + *pDest = *this; + return true; + } + + if ( m_type != FIELD_VECTOR && m_type != FIELD_CSTRING && destType != FIELD_VECTOR && destType != FIELD_CSTRING ) + { + switch ( m_type ) + { + case FIELD_VOID: *pDest = 0; break; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_CHARACTER: *pDest = m_char; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + case FIELD_HSCRIPT: *pDest = m_hScript; return true; + } + } + else + { + DevWarning( "No free conversion of %s script variant to %s right now\n", + ScriptFieldTypeName( m_type ), ScriptFieldTypeName() ); + if ( destType != FIELD_VECTOR ) + { + *pDest = 0; + } + } + return false; + } + + bool AssignTo( float *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to float now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( int *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to int now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( bool *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to bool now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( char **pDest ) + { + DevWarning( "No free conversion of string or vector script variant right now\n" ); + // If want to support this, probably need to malloc string and require free on other side [3/24/2008 tom] + *pDest = ""; + return false; + } + + bool AssignTo( ScriptVariant_t *pDest ) + { + pDest->m_type = m_type; + if ( m_type == FIELD_VECTOR ) + { + pDest->m_pVector = new Vector; + ((Vector *)(pDest->m_pVector))->Init( m_pVector->x, m_pVector->y, m_pVector->z ); + pDest->m_flags |= SV_FREE; + } + else if ( m_type == FIELD_CSTRING ) + { + pDest->m_pszString = strdup( m_pszString ); + pDest->m_flags |= SV_FREE; + } + else + { + pDest->m_int = m_int; + } + return false; + } + + union + { + int m_int; + float m_float; + const char * m_pszString; + const Vector * m_pVector; + char m_char; + bool m_bool; + HSCRIPT m_hScript; +#ifdef MAPBASE_VSCRIPT + // This uses FIELD_VECTOR, so it's considered a Vector in the VM (just like save/restore) + const QAngle * m_pAngle; +#endif + }; + + int16 m_type; + int16 m_flags; + +private: +}; + +#define SCRIPT_VARIANT_NULL ScriptVariant_t() + +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +struct ScriptConstantBinding_t +{ + const char *m_pszScriptName; + const char *m_pszDescription; + ScriptVariant_t m_data; + unsigned m_flags; +}; + +//--------------------------------------------------------- +struct ScriptEnumDesc_t +{ + ScriptEnumDesc_t() : m_pszScriptName( 0 ), m_pszDescription( 0 ), m_flags( 0 ) + { + AllEnumsDesc().AddToTail(this); + } + + virtual void RegisterDesc() = 0; + + const char *m_pszScriptName; + const char *m_pszDescription; + CUtlVector m_ConstantBindings; + unsigned m_flags; + + static CUtlVector& AllEnumsDesc() + { + static CUtlVector enums; + return enums; + } +}; +#endif + +#pragma warning(pop) + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#include "vscript_templates.h" + +// Lower level macro primitives +#define ScriptInitFunctionBinding( pScriptFunction, func ) ScriptInitFunctionBindingNamed( pScriptFunction, func, #func ) +#define ScriptInitFunctionBindingNamed( pScriptFunction, func, scriptName ) do { ScriptInitFuncDescriptorNamed( (&(pScriptFunction)->m_desc), func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( &func ); (pScriptFunction)->m_pFunction = (void *)&func; } while (0) + +#define ScriptInitMemberFunctionBinding( pScriptFunction, class, func ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, #func ) +#define ScriptInitMemberFunctionBindingNamed( pScriptFunction, class, func, scriptName ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) +#define ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) do { ScriptInitMemberFuncDescriptor_( (&(pScriptFunction)->m_desc), class, func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( ((class *)0), &class::func ); (pScriptFunction)->m_pFunction = ScriptConvertFuncPtrToVoid( &class::func ); (pScriptFunction)->m_flags = SF_MEMBER_FUNC; } while (0) + +#define ScriptInitClassDesc( pClassDesc, class, pBaseClassDesc ) ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, #class ) +#define ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) +#define ScriptInitClassDescNoBase( pClassDesc, class ) ScriptInitClassDescNoBaseNamed( pClassDesc, class, #class ) +#define ScriptInitClassDescNoBaseNamed( pClassDesc, class, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, NULL, scriptName ) +#define ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) do { (pClassDesc)->m_pszScriptName = scriptName; (pClassDesc)->m_pszClassname = #class; (pClassDesc)->m_pBaseDesc = pBaseClassDesc; } while ( 0 ) + +#define ScriptAddFunctionToClassDesc( pClassDesc, class, func, description ) ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, #func, description ) +#define ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, scriptName, description ) do { ScriptFunctionBinding_t *pBinding = &((pClassDesc)->m_FunctionBindings[(pClassDesc)->m_FunctionBindings.AddToTail()]); pBinding->m_desc.m_pszDescription = description; ScriptInitMemberFunctionBindingNamed( pBinding, class, func, scriptName ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterFunction( pVM, func, description ) ScriptRegisterFunctionNamed( pVM, func, #func, description ) +#define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) do { static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); pVM->RegisterFunction( &binding ); } while (0) + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterConstant( pVM, constant, description ) ScriptRegisterConstantNamed( pVM, constant, #constant, description ) +#define ScriptRegisterConstantNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = constant; pVM->RegisterConstant( &binding ); } while (0) + +// Could probably use a better name. +// This is used for registering variants (particularly vectors) not tied to existing variables. +// The principal difference is that m_data is initted with bCopy set to true. +#define ScriptRegisterConstantFromTemp( pVM, constant, description ) ScriptRegisterConstantFromTempNamed( pVM, constant, #constant, description ) +#define ScriptRegisterConstantFromTempNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ScriptVariant_t( constant, true ); pVM->RegisterConstant( &binding ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define BEGIN_SCRIPTENUM( enumName, description ) \ + struct ScriptEnum##enumName##Desc_t : public ScriptEnumDesc_t \ + { \ + void RegisterDesc(); \ + }; \ + ScriptEnum##enumName##Desc_t g_##enumName##_EnumDesc; \ + \ + void ScriptEnum##enumName##Desc_t::RegisterDesc() \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + return; \ + \ + bInitialized = true; \ + \ + m_pszScriptName = #enumName; \ + m_pszDescription = description; \ + +#define DEFINE_ENUMCONST( constant, description ) DEFINE_ENUMCONST_NAMED( constant, #constant, description ) +#define DEFINE_ENUMCONST_NAMED( constant, scriptName, description ) do { ScriptConstantBinding_t *pBinding = &(m_ConstantBindings[m_ConstantBindings.AddToTail()]); pBinding->m_pszScriptName = scriptName; pBinding->m_pszDescription = description; pBinding->m_data = constant; pBinding->m_flags = SF_MEMBER_FUNC; } while (0); + +#define END_SCRIPTENUM() \ + } \ + + +#define GetScriptDescForEnum( enumName ) GetScriptDesc( ( className *)NULL ) +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *); + +#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_NAMED( className, baseClass, #className, description ) +#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_NAMED( className, #className, description ) + +#ifdef MSVC + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + ScriptClassDesc_t * GetScriptDesc( className * ) +#else + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + template <> ScriptClassDesc_t * GetScriptDesc( baseClass *); \ + template <> ScriptClassDesc_t * GetScriptDesc( className *) +#endif + +#define BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) \ + ScriptClassDesc_t g_##className##_ScriptDesc; \ + DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + { \ + return &g_##className##_ScriptDesc; \ + } \ + \ + bInitialized = true; \ + \ + typedef className _className; \ + ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ + pDesc->m_pszDescription = description; \ + ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ + ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ + while ( pInstanceHelperBase ) \ + { \ + if ( pInstanceHelperBase->pHelper ) \ + { \ + pDesc->pHelper = pInstanceHelperBase->pHelper; \ + break; \ + } \ + pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \ + } + + +#define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \ + BEGIN_SCRIPTDESC_NAMED( className, ScriptNoBase_t, scriptName, description ) + +#define END_SCRIPTDESC() \ + return pDesc; \ + } + +#define DEFINE_SCRIPTFUNC( func, description ) DEFINE_SCRIPTFUNC_NAMED( func, #func, description ) +#define DEFINE_SCRIPTFUNC_NAMED( func, scriptName, description ) ScriptAddFunctionToClassDescNamed( pDesc, _className, func, scriptName, description ); +#define DEFINE_SCRIPT_CONSTRUCTOR() ScriptAddConstructorToClassDesc( pDesc, _className ); +#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) pDesc->pHelper = (p); + +#ifdef MAPBASE_VSCRIPT +// Use this for hooks which have no parameters +#define DEFINE_SIMPLE_SCRIPTHOOK( hook, hookName, returnType, description ) \ + if (!hook.m_bDefined) \ + { \ + ScriptHook_t *pHook = &hook; \ + pHook->m_desc.m_pszScriptName = hookName; pHook->m_desc.m_pszFunction = #hook; pHook->m_desc.m_ReturnType = returnType; pHook->m_desc.m_pszDescription = description; \ + pDesc->m_Hooks.AddToTail(pHook); \ + } + +#define BEGIN_SCRIPTHOOK( hook, hookName, returnType, description ) \ + if (!hook.m_bDefined) \ + { \ + ScriptHook_t *pHook = &hook; \ + pHook->m_desc.m_pszScriptName = hookName; pHook->m_desc.m_pszFunction = #hook; pHook->m_desc.m_ReturnType = returnType; pHook->m_desc.m_pszDescription = description; + +#define DEFINE_SCRIPTHOOK_PARAM( paramName, type ) pHook->AddParameter( paramName, type ); + +#define END_SCRIPTHOOK() \ + pDesc->m_Hooks.AddToTail(pHook); \ + } + +#define DEFINE_MEMBERVAR( varName, returnType, description ) \ + do { ScriptMemberDesc_t *pBinding = &((pDesc)->m_Members[(pDesc)->m_Members.AddToTail()]); pBinding->m_pszScriptName = varName; pBinding->m_pszDescription = description; pBinding->m_ReturnType = returnType; } while (0); +#endif + +template ScriptClassDesc_t *GetScriptDesc(T *); + +struct ScriptNoBase_t; +template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *) { return NULL; } + +#define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +class CScriptConstructor +{ +public: + static void *Construct() { return new T; } + static void Destruct( void *p ) { delete (T *)p; } +}; + +#define ScriptAddConstructorToClassDesc( pClassDesc, class ) do { (pClassDesc)->m_pfnConstruct = &CScriptConstructor::Construct; (pClassDesc)->m_pfnDestruct = &CScriptConstructor::Destruct; } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ScriptErrorLevel_t +{ + SCRIPT_LEVEL_WARNING = 0, + SCRIPT_LEVEL_ERROR, +}; + +typedef void ( *ScriptOutputFunc_t )( const char *pszText ); +typedef bool ( *ScriptErrorFunc_t )( ScriptErrorLevel_t eLevel, const char *pszText ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef RegisterClass +#undef RegisterClass +#endif + +enum ScriptStatus_t +{ + SCRIPT_ERROR = -1, + SCRIPT_DONE, + SCRIPT_RUNNING, +}; + +class IScriptVM +{ +public: + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual bool ConnectDebugger() = 0; + virtual void DisconnectDebugger() = 0; + + virtual ScriptLanguage_t GetLanguage() = 0; + virtual const char *GetLanguageName() = 0; + + virtual void AddSearchPath( const char *pszSearchPath ) = 0; + + //-------------------------------------------------------- + + virtual bool Frame( float simTime ) = 0; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run( const char *pszScript, bool bWait = true ) = 0; + inline ScriptStatus_t Run( const unsigned char *pszScript, bool bWait = true ) { return Run( (char *)pszScript, bWait ); } + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) = 0; + inline HSCRIPT CompileScript( const unsigned char *pszScript, const char *pszId = NULL ) { return CompileScript( (char *)pszScript, pszId ); } + virtual void ReleaseScript( HSCRIPT ) = 0; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) = 0; + virtual ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) = 0; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) = 0; + virtual void ReleaseScope( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) = 0; + virtual void ReleaseFunction( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) = 0; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) = 0; + +#ifdef MAPBASE_VSCRIPT + //-------------------------------------------------------- + // External constants + //-------------------------------------------------------- + virtual void RegisterConstant( ScriptConstantBinding_t *pScriptConstant ) = 0; + + //-------------------------------------------------------- + // External enums + //-------------------------------------------------------- + virtual void RegisterEnum( ScriptEnumDesc_t *pEnumDesc ) = 0; +#endif + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT + // When a RegisterInstance instance is deleted, VScript normally treats it as a strong reference and only deregisters the instance itself, preserving the registered data + // it points to so the game can continue to use it. + // bAllowDestruct is supposed to allow VScript to treat it as a weak reference created by the script, destructing the registered data automatically like any other type. + // This is useful for classes pretending to be primitive types. + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance, bool bAllowDestruct = false ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance, bool bAllowDestruct = false ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance, bAllowDestruct ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL, bool bAllowDestruct = false) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance, bAllowDestruct ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } +#else + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } +#endif + virtual void RemoveInstance( HSCRIPT ) = 0; + void RemoveInstance( HSCRIPT hInstance, const char *pszInstance, HSCRIPT hScope = NULL ) { ClearValue( hScope, pszInstance ); RemoveInstance( hInstance ); } + void RemoveInstance( const char *pszInstance, HSCRIPT hScope = NULL ) { ScriptVariant_t val; if ( GetValue( hScope, pszInstance, &val ) ) { if ( val.m_type == FIELD_HSCRIPT ) { RemoveInstance( val, pszInstance, hScope ); } ReleaseValue( val ); } } + + virtual void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType = NULL ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) = 0; + bool ValueExists( const char *pszKey ) { return ValueExists( NULL, pszKey ); } + + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) = 0; + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) = 0; + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return SetValue(NULL, pszKey, value ); } + + virtual void CreateTable( ScriptVariant_t &Table ) = 0; + virtual int GetNumTableEntries( HSCRIPT hScope ) = 0; + virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) = 0; + + virtual bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) = 0; + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetValue(NULL, pszKey, pValue ); } + virtual void ReleaseValue( ScriptVariant_t &value ) = 0; + + virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) = 0; + bool ClearValue( const char *pszKey) { return ClearValue( NULL, pszKey ); } + +#ifdef MAPBASE_VSCRIPT + // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; + virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) = 0; +#endif + + //---------------------------------------------------------------------------- + + virtual void WriteState( CUtlBuffer *pBuffer ) = 0; + virtual void ReadState( CUtlBuffer *pBuffer ) = 0; + virtual void RemoveOrphanInstances() = 0; + + virtual void DumpState() = 0; + + virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) = 0; + virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException( const char *pszExceptionText ) = 0; + + //---------------------------------------------------------------------------- + // Call API + // + // Note for string and vector return types, the caller must delete the pointed to memory + //---------------------------------------------------------------------------- + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope = NULL, bool bWait = true, ScriptVariant_t *pReturn = NULL ) + { + return ExecuteFunction( hFunction, NULL, 0, pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + +#ifdef MAPBASE_VSCRIPT + void RegisterAllClasses() + { + CUtlVector& classDescs = ScriptClassDesc_t::AllClassesDesc(); + FOR_EACH_VEC(classDescs, i) + { + RegisterClass(classDescs[i]); + } + } + + void RegisterAllEnums() + { + CUtlVector& enumDescs = ScriptEnumDesc_t::AllEnumsDesc(); + FOR_EACH_VEC(enumDescs, i) + { + enumDescs[i]->RegisterDesc(); + RegisterEnum(enumDescs[i]); + } + } +#endif +}; + + +//----------------------------------------------------------------------------- +// Script scope helper class +//----------------------------------------------------------------------------- + +class CDefScriptScopeBase +{ +public: + static IScriptVM *GetVM() + { + extern IScriptVM *g_pScriptVM; + return g_pScriptVM; + } +}; + +template +class CScriptScopeT : public CDefScriptScopeBase +{ +public: + CScriptScopeT() : + m_hScope( INVALID_HSCRIPT ), + m_flags( 0 ) + { + } + + ~CScriptScopeT() + { + Term(); + } + + bool IsInitialized() + { + return m_hScope != INVALID_HSCRIPT; + } + + bool Init( const char *pszName ) + { + m_hScope = GetVM()->CreateScope( pszName ); + return ( m_hScope != NULL ); + } + + bool Init( HSCRIPT hScope, bool bExternal = true ) + { + if ( bExternal ) + { + m_flags |= EXTERNAL; + } + m_hScope = hScope; + return ( m_hScope != NULL ); + } + + bool InitGlobal() + { + Assert( 0 ); // todo [3/24/2008 tom] + m_hScope = GetVM()->CreateScope( "" ); + return ( m_hScope != NULL ); + } + + void Term() + { + if ( m_hScope != INVALID_HSCRIPT ) + { + IScriptVM *pVM = GetVM(); + if ( pVM ) + { + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + pVM->ReleaseFunction( *m_FuncHandles[i] ); + } + } + m_FuncHandles.Purge(); + if ( m_hScope && pVM && !(m_flags & EXTERNAL) ) + { + pVM->ReleaseScope( m_hScope ); + } + m_hScope = INVALID_HSCRIPT; + } + m_flags = 0; + } + + void InvalidateCachedValues() + { + IScriptVM *pVM = GetVM(); + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + if ( *m_FuncHandles[i] ) + pVM->ReleaseFunction( *m_FuncHandles[i] ); + *m_FuncHandles[i] = INVALID_HSCRIPT; + } + m_FuncHandles.RemoveAll(); + } + + operator HSCRIPT() + { + return ( m_hScope != INVALID_HSCRIPT ) ? m_hScope : NULL; + } + + bool ValueExists( const char *pszKey ) { return GetVM()->ValueExists( m_hScope, pszKey ); } + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return GetVM()->SetValue(m_hScope, pszKey, value ); } + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetVM()->GetValue(m_hScope, pszKey, pValue ); } + void ReleaseValue( ScriptVariant_t &value ) { GetVM()->ReleaseValue( value ); } + bool ClearValue( const char *pszKey) { return GetVM()->ClearValue( m_hScope, pszKey ); } + + ScriptStatus_t Run( HSCRIPT hScript ) + { + InvalidateCachedValues(); + return GetVM()->Run( hScript, m_hScope ); + } + + ScriptStatus_t Run( const char *pszScriptText, const char *pszScriptName = NULL ) + { + InvalidateCachedValues(); + HSCRIPT hScript = GetVM()->CompileScript( pszScriptText, pszScriptName ); + if ( hScript ) + { + ScriptStatus_t result = GetVM()->Run( hScript, m_hScope ); + GetVM()->ReleaseScript( hScript ); + return result; + } + return SCRIPT_ERROR; + } + + ScriptStatus_t Run( const unsigned char *pszScriptText, const char *pszScriptName = NULL ) + { + return Run( (const char *)pszScriptText, pszScriptName); + } + + HSCRIPT LookupFunction( const char *pszFunction ) + { + return GetVM()->LookupFunction( pszFunction, m_hScope ); + } + + void ReleaseFunction( HSCRIPT hScript ) + { + GetVM()->ReleaseFunction( hScript ); + } + + bool FunctionExists( const char *pszFunction ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + GetVM()->ReleaseFunction( hFunction ); + return ( hFunction != NULL ) ; + } + + //----------------------------------------------------- + + enum Flags_t + { + EXTERNAL = 0x01, + }; + + //----------------------------------------------------- + + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn = NULL ) + { + return GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn = NULL ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + +protected: + HSCRIPT m_hScope; + int m_flags; + CUtlVectorConservative m_FuncHandles; +}; + +typedef CScriptScopeT<> CScriptScope; + +#define VScriptAddEnumToScope_( scope, enumVal, scriptName ) (scope).SetValue( scriptName, (int)enumVal ) +#define VScriptAddEnumToScope( scope, enumVal ) VScriptAddEnumToScope_( scope, enumVal, #enumVal ) + +#define VScriptAddEnumToRoot( enumVal ) g_pScriptVM->SetValue( #enumVal, (int)enumVal ) + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Function bindings allow script functions to run C++ functions. +// Hooks allow C++ functions to run script functions. +// +// This was previously done with raw function lookups, but Mapbase adds more and +// it's hard to keep track of them without proper standards or documentation. +// +// At the moment, this simply plugs hook documentation into VScript and maintains +// the same function lookup method on the inside, but it's intended to be open for +// more complex hook mechanisms with proper parameters in the future. +// +// For example: +// +// if (m_hFunc) +// { +// g_pScriptVM->ExecuteFunction( m_Func, pArgs, m_desc.m_Parameters.Count(), pReturn, m_ScriptScope, true ); +// } +//----------------------------------------------------------------------------- +struct ScriptHook_t +{ + ScriptFuncDescriptor_t m_desc; + CUtlVector m_pszParameterNames; + bool m_bDefined; + + void AddParameter( const char *pszName, ScriptDataType_t type ) + { + int iCur = m_desc.m_Parameters.Count(); + m_desc.m_Parameters.SetGrowSize( 1 ); m_desc.m_Parameters.EnsureCapacity( iCur + 1 ); m_desc.m_Parameters.AddToTail( type ); + m_pszParameterNames.SetGrowSize( 1 ); m_pszParameterNames.EnsureCapacity( iCur + 1 ); m_pszParameterNames.AddToTail( pszName ); + } + + // ----------------------------------------------------------------- + + // Cached for when CanRunInScope() is called before Call() + HSCRIPT m_hFunc; + + // Checks if there's a function of this name which would run in this scope + HSCRIPT CanRunInScope( HSCRIPT hScope ) + { + extern IScriptVM *g_pScriptVM; + m_hFunc = g_pScriptVM->LookupFunction( m_desc.m_pszScriptName, hScope ); + return m_hFunc; + } + + // Checks if an existing func can be used + bool CheckFuncValid( HSCRIPT hFunc ) + { + // TODO: Better crtieria for this? + if (hFunc) + { + m_hFunc = hFunc; + return true; + } + return false; + } + + // Call the function + bool Call( HSCRIPT hScope, ScriptVariant_t *pReturn, ScriptVariant_t *pArgs, bool bRelease = true ) + { + extern IScriptVM *g_pScriptVM; + + // Make sure we have a function in this scope + if (!m_hFunc && !CanRunInScope(hScope)) + return false; + else + { + for (int i = 0; i < m_desc.m_Parameters.Count(); i++) + { + g_pScriptVM->SetValue( m_pszParameterNames[i], pArgs[i] ); + } + + g_pScriptVM->ExecuteFunction( m_hFunc, NULL, 0, pReturn, hScope, true ); + + if (bRelease) + g_pScriptVM->ReleaseFunction( m_hFunc ); + + m_hFunc = NULL; + + for (int i = 0; i < m_desc.m_Parameters.Count(); i++) + { + g_pScriptVM->ClearValue( m_pszParameterNames[i] ); + } + + return true; + } + + return false; + } +}; +#endif + +//----------------------------------------------------------------------------- +// Script function proxy support +//----------------------------------------------------------------------------- + +class CScriptFuncHolder +{ +public: + CScriptFuncHolder() : hFunction( INVALID_HSCRIPT ) {} + bool IsValid() { return ( hFunction != INVALID_HSCRIPT ); } + bool IsNull() { return ( !hFunction ); } + HSCRIPT hFunction; +}; + +#define DEFINE_SCRIPT_PROXY_GUTS( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < typename RET_TYPE FUNC_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( RET_TYPE *pRetVal FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptVariant_t returnVal; \ + Assert( N == m_desc.m_Parameters.Count() ); \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, &returnVal, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + returnVal.AssignTo( pRetVal ); \ + returnVal.Free(); \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + Assert( N == m_desc.m_Parameters.Count() ); \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0V( FuncName ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + bool FuncName() \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 0 ) +#define DEFINE_SCRIPT_PROXY_1( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 14 ) + +#define DEFINE_SCRIPT_PROXY_1V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 14 ) + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // IVSCRIPT_H diff --git a/mp/src/public/vscript/vscript_templates.h b/mp/src/public/vscript/vscript_templates.h new file mode 100644 index 00000000..e23a9fe9 --- /dev/null +++ b/mp/src/public/vscript/vscript_templates.h @@ -0,0 +1,414 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_TEMPLATES_H +#define VSCRIPT_TEMPLATES_H + +#include "tier0/basetypes.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#define FUNC_APPEND_PARAMS_0 +#define FUNC_APPEND_PARAMS_1 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 1 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); +#define FUNC_APPEND_PARAMS_2 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 2 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); +#define FUNC_APPEND_PARAMS_3 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 3 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); +#define FUNC_APPEND_PARAMS_4 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 4 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); +#define FUNC_APPEND_PARAMS_5 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 5 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); +#define FUNC_APPEND_PARAMS_6 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 6 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); +#define FUNC_APPEND_PARAMS_7 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 7 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); +#define FUNC_APPEND_PARAMS_8 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 8 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); +#define FUNC_APPEND_PARAMS_9 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 9 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); +#define FUNC_APPEND_PARAMS_10 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 10 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); +#define FUNC_APPEND_PARAMS_11 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 11 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); +#define FUNC_APPEND_PARAMS_12 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 12 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); +#define FUNC_APPEND_PARAMS_13 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 13 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); +#define FUNC_APPEND_PARAMS_14 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 14 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_14 ) ); + +#define DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER ); + +#define DEFINE_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); + +#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } + +#define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } +#define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) +#define ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, scriptName ) ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) +#define ScriptInitMemberFuncDescriptor( pDesc, class, func ) ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, #func ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +inline void *ScriptConvertFuncPtrToVoid( FUNCPTR_TYPE pFunc ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.pFunc = pFunc; + return convert.p; + } +#if defined( _MSC_VER ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } +#elif defined( GNUC ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + AssertMsg( 0, "Note: This path has not been verified yet. See comments below in #else case." ); + + struct GnuMFP + { + union + { + void *funcadr; // If vtable_index_2 is even, then this is the function pointer. + int vtable_index_2; // If vtable_index_2 is odd, then this = vindex*2+1. + }; + int delta; + }; + + GnuMFP *p = (GnuMFP*)&pFunc; + if ( p->vtable_index_2 & 1 ) + { + char **delta = (char**)p->delta; + char *pCur = *delta + (p->vtable_index_2+1)/2; + return (void*)( pCur + 4 ); + } + else + { + return p->funcadr; + } + } +#else +#error "Need to implement code to crack non-offset member function pointer case" + // For gcc, see: http://www.codeproject.com/KB/cpp/FastDelegate.aspx + // + // Current versions of the GNU compiler use a strange and tricky + // optimization. It observes that, for virtual inheritance, you have to look + // up the vtable in order to get the voffset required to calculate the this + // pointer. While you're doing that, you might as well store the function + // pointer in the vtable. By doing this, they combine the m_func_address and + // m_vtable_index fields into one, and they distinguish between them by + // ensuring that function pointers always point to even addresses but vtable + // indices are always odd: + // + // // GNU g++ uses a tricky space optimisation, also adopted by IBM's VisualAge and XLC. + // struct GnuMFP { + // union { + // CODEPTR funcadr; // always even + // int vtable_index_2; // = vindex*2+1, always odd + // }; + // int delta; + // }; + // adjustedthis = this + delta + // if (funcadr & 1) CALL (* ( *delta + (vindex+1)/2) + 4) + // else CALL funcadr + // + // The G++ method is well documented, so it has been adopted by many other + // vendors, including IBM's VisualAge and XLC compilers, recent versions of + // Open64, Pathscale EKO, and Metrowerks' 64-bit compilers. A simpler scheme + // used by earlier versions of GCC is also very common. SGI's now + // discontinued MIPSPro and Pro64 compilers, and Apple's ancient MrCpp + // compiler used this method. (Note that the Pro64 compiler has become the + // open source Open64 compiler). + +#endif + else + AssertMsg( 0, "Member function pointer not supported. Why on earth are you using virtual inheritance!?" ); + return NULL; +} + +template +inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.p = p; + return convert.pFunc; + } + +#if defined( _MSC_VER ) + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } +#elif defined( POSIX ) + AssertMsg( 0, "Note: This path has not been implemented yet." ); +#else +#error "Need to implement code to crack non-offset member function pointer case" +#endif + Assert( 0 ); + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_0 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_1 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_1 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_2 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_2 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_3 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_3 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_4 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_4 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_5 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_5 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_6 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_6 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_7 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_7 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_8 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_8 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_9 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_9 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_10 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_10 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_11 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_11 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_12 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_12 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_13 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_13 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_14 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_14 + +#define SCRIPT_BINDING_ARGS_0 +#define SCRIPT_BINDING_ARGS_1 pArguments[0] +#define SCRIPT_BINDING_ARGS_2 pArguments[0], pArguments[1] +#define SCRIPT_BINDING_ARGS_3 pArguments[0], pArguments[1], pArguments[2] +#define SCRIPT_BINDING_ARGS_4 pArguments[0], pArguments[1], pArguments[2], pArguments[3] +#define SCRIPT_BINDING_ARGS_5 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4] +#define SCRIPT_BINDING_ARGS_6 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5] +#define SCRIPT_BINDING_ARGS_7 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6] +#define SCRIPT_BINDING_ARGS_8 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7] +#define SCRIPT_BINDING_ARGS_9 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8] +#define SCRIPT_BINDING_ARGS_10 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9] +#define SCRIPT_BINDING_ARGS_11 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10] +#define SCRIPT_BINDING_ARGS_12 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11] +#define SCRIPT_BINDING_ARGS_13 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12] +#define SCRIPT_BINDING_ARGS_14 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12], pArguments[13] + + +#define DEFINE_SCRIPT_BINDINGS(N) \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || !pReturn || pContext ) \ + { \ + return false; \ + } \ + *pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || pReturn || pContext ) \ + { \ + return false; \ + } \ + ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || !pReturn || !pContext ) \ + { \ + return false; \ + } \ + *pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || pReturn || !pContext ) \ + { \ + return false; \ + } \ + (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CNonMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } + +FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#endif // VSCRIPT_TEMPLATES_H diff --git a/mp/src/raytrace/raytrace.cpp b/mp/src/raytrace/raytrace.cpp index 7b951dfb..9816560d 100644 --- a/mp/src/raytrace/raytrace.cpp +++ b/mp/src/raytrace/raytrace.cpp @@ -233,7 +233,9 @@ void CacheOptimizedTriangle::ChangeIntoIntersectionFormat(void) } +#ifndef MAPBASE int n_intersection_calculations=0; +#endif int CacheOptimizedTriangle::ClassifyAgainstAxisSplit(int split_plane, float split_value) { @@ -476,7 +478,9 @@ void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 T TriIntersectData_t const *tri = &( OptimizedTriangleList[tnum].m_Data.m_IntersectData ); if ( ( mailboxids[mbox_slot] != tnum ) && ( tri->m_nTriangleID != skip_id ) ) { +#ifndef MAPBASE n_intersection_calculations++; +#endif mailboxids[mbox_slot] = tnum; // compute plane intersection diff --git a/mp/src/tier1/mapbase_con_groups.cpp b/mp/src/tier1/mapbase_con_groups.cpp new file mode 100644 index 00000000..533d5791 --- /dev/null +++ b/mp/src/tier1/mapbase_con_groups.cpp @@ -0,0 +1,179 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: Mapbase classifies certain types of console messages into groups with specific colors. +// +// This is inspired by similar groups seen in CS:GO and Source 2 games. +// +// $NoKeywords: $ +//============================================================================= + +#include +#include +#include +#include "basetypes.h" +#include "tier1/tier1.h" +#include "tier1/utldict.h" +#include "Color.h" +#include "tier1/mapbase_con_groups.h" + +void CV_ColorChanged( IConVar *pConVar, const char *pOldString, float flOldValue ); + +struct ConGroup_t +{ + ConGroup_t( const char *_pszName, ConVar *pCvar ) + { + pszName = _pszName; + cvar = pCvar; + } + + const Color &GetColor() + { + if (_clr.a() == 0) + { + // Read the cvar + int clr[3]; + sscanf( cvar->GetString(), "%i %i %i", &clr[0], &clr[1], &clr[2] ); + _clr.SetColor( clr[0], clr[1], clr[2], 255 ); + } + return _clr; + } + + const char *pszName; + Color _clr; + ConVar *cvar; + + //bool bDisabled; +}; + +//----------------------------------------------------------------------------- +// To define a console group, +//----------------------------------------------------------------------------- + +#define DEFINE_CON_GROUP_CVAR(name, def, desc) static ConVar con_group_##name##_color( "con_group_" #name "_color", def, FCVAR_ARCHIVE | FCVAR_REPLICATED, desc, CV_ColorChanged ) + +DEFINE_CON_GROUP_CVAR( mapbase_misc, "192 128 224", "Messages from misc. Mapbase functions, like map-specific files." ); +DEFINE_CON_GROUP_CVAR( physics, "159 209 159", "Messages from physics-related events." ); + +DEFINE_CON_GROUP_CVAR( inputoutput, "220 170 220", "Messages from I/O events. (these display in developer 2)" ); +DEFINE_CON_GROUP_CVAR( npc_ai, "240 160 200", "Messages from NPC AI, etc. which display at various verbse levels." ); +DEFINE_CON_GROUP_CVAR( npc_scripts, "255 115 215", "Messages from scripted_sequence, etc. (these display in developer 2)" ); +DEFINE_CON_GROUP_CVAR( choreo, "240 224 180", "Messages from choreographed scenes and response expressers. (these display in developer 1, 2, etc.)" ); + +DEFINE_CON_GROUP_CVAR( vscript, "192 224 240", "Internal messages from VScript not produced by the user's scripts." ); +DEFINE_CON_GROUP_CVAR( vscript_print, "80 186 255", "Messages from VScript's 'print' function." ); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#define DEFINE_CON_GROUP(name, codename) { name, &con_group_##codename##_color } + +ConGroup_t g_ConGroups[] = { + + // General + DEFINE_CON_GROUP( CON_GROUP_MAPBASE_MISC, mapbase_misc ), + DEFINE_CON_GROUP( CON_GROUP_PHYSICS, physics ), + + // Server + DEFINE_CON_GROUP( CON_GROUP_IO_SYSTEM, inputoutput ), + DEFINE_CON_GROUP( CON_GROUP_NPC_AI, npc_ai ), + DEFINE_CON_GROUP( CON_GROUP_NPC_SCRIPTS, npc_scripts ), + DEFINE_CON_GROUP( CON_GROUP_CHOREO, choreo ), + + // VScript + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT, vscript ), + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT_PRINT, vscript_print ), + +}; + +void CV_ColorChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + { + // Reset the alpha to indicate it needs to be refreshed + g_ConGroups[i]._clr[3] = 0; + } +} + +ConGroup_t *FindConGroup( const char *pszGroup ) +{ + for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + { + if (V_strcmp(pszGroup, g_ConGroups[i].pszName) == 0) + return &g_ConGroups[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +ConVar con_group_include_name( "con_group_include_name", "0", FCVAR_NONE, "Includes groups when printing." ); + +CON_COMMAND( con_group_list, "Prints a list of all console groups." ) +{ + Msg( "============================================================\n" ); + for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + { + Msg( " # " ); + ConColorMsg( g_ConGroups[i].GetColor(), "%s", g_ConGroups[i].pszName ); + Msg( " - %s ", g_ConGroups[i].cvar->GetHelpText() ); + + //if (g_ConGroups[i].bDisabled) + // Msg("(DISABLED)"); + + Msg("\n"); + } + Msg( "============================================================\n" ); +} + +// TODO: Figure out how this can be done without server/client disparity issues +/* +CON_COMMAND( con_group_toggle, "Toggles a console group." ) +{ + const char *pszGroup = args.Arg( 1 ); + for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + { + if (V_stricmp(pszGroup, g_ConGroups[i].pszName) == 0) + { + Msg( "%s is now %s\n", g_ConGroups[i].pszName, g_ConGroups[i].bDisabled ? "enabled" : "disabled" ); + g_ConGroups[i].bDisabled = !g_ConGroups[i].bDisabled; + return; + } + } + + Msg( "No group named \"%s\"\n", pszGroup ); +} +*/ + +void CGMsg( int level, const char *pszGroup, const tchar* pMsg, ... ) +{ + // Return early if we're not at this level + if (!IsSpewActive("developer", level)) + return; + + char string[ 2048 ]; + va_list argptr; + va_start( argptr, pMsg ); + Q_vsnprintf( string, sizeof(string), pMsg, argptr ); + va_end( argptr ); + + ConGroup_t *pGroup = FindConGroup( pszGroup ); + if (pGroup) + { + /*if (pGroup->bDisabled) + { + // Do nothing + } + else*/ if (con_group_include_name.GetBool()) + { + ConColorMsg( level, pGroup->GetColor(), "[%s] %s", pGroup->pszName, string ); + } + else + { + ConColorMsg( level, pGroup->GetColor(), string ); + } + } + else + DevMsg( level, string ); +} diff --git a/mp/src/tier1/tier1.vpc b/mp/src/tier1/tier1.vpc index 6f163a8b..c9dd4e86 100644 --- a/mp/src/tier1/tier1.vpc +++ b/mp/src/tier1/tier1.vpc @@ -78,6 +78,7 @@ $Project "tier1" $File "snappy.cpp" $File "snappy-sinksource.cpp" $File "snappy-stubs-internal.cpp" + $File "mapbase_con_groups.cpp" [$MAPBASE] } // Select bits from the LZMA SDK to support lzmaDecoder.h @@ -159,6 +160,7 @@ $Project "tier1" $File "$SRCDIR\public\tier1\utlsymbollarge.h" $File "$SRCDIR\public\tier1\utlvector.h" $File "$SRCDIR\public\tier1\utlbinaryblock.h" + $File "$SRCDIR\public\tier1\mapbase_con_groups.h" [$MAPBASE] $File "$SRCDIR\common\xbox\xboxstubs.h" [$WINDOWS] } } diff --git a/mp/src/utils/common/threads.cpp b/mp/src/utils/common/threads.cpp index 74e457a9..28f0894c 100644 --- a/mp/src/utils/common/threads.cpp +++ b/mp/src/utils/common/threads.cpp @@ -19,7 +19,13 @@ #include "threads.h" #include "pacifier.h" +#ifdef MAPBASE +// This was suggested in that Source 2013 pull request that fixed Vrad. +// I trust their judgement on this. +#define MAX_THREADS 32 +#else #define MAX_THREADS 16 +#endif class CRunThreadsData diff --git a/mp/src/utils/common/threads.h b/mp/src/utils/common/threads.h index 0908b67a..7d6beb9f 100644 --- a/mp/src/utils/common/threads.h +++ b/mp/src/utils/common/threads.h @@ -16,9 +16,15 @@ #pragma once +#ifdef MAPBASE +// This was suggested in that Source 2013 pull request that fixed Vrad. +// I trust their judgement on this. +#define MAX_TOOL_THREADS 32 +#else // Arrays that are indexed by thread should always be MAX_TOOL_THREADS+1 // large so THREADINDEX_MAIN can be used from the main thread. #define MAX_TOOL_THREADS 16 +#endif #define THREADINDEX_MAIN (MAX_TOOL_THREADS) diff --git a/mp/src/utils/common/utilmatlib.cpp b/mp/src/utils/common/utilmatlib.cpp index f2dc49fa..7e62e000 100644 --- a/mp/src/utils/common/utilmatlib.cpp +++ b/mp/src/utils/common/utilmatlib.cpp @@ -59,6 +59,7 @@ void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn file LoadMaterialSystemInterface( fileSystemFactory ); MaterialSystem_Config_t config; g_pMaterialSystem->OverrideConfig( config, false ); + g_pMaterialSystem->ModInit(); } void ShutdownMaterialSystem( ) diff --git a/mp/src/utils/vbsp/csg.cpp b/mp/src/utils/vbsp/csg.cpp index 5de1d680..a8c39afb 100644 --- a/mp/src/utils/vbsp/csg.cpp +++ b/mp/src/utils/vbsp/csg.cpp @@ -594,6 +594,13 @@ void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars ) ADD_CONTENTS(CONTENTS_BLOCKLOS) ADD_CONTENTS(CONTENTS_OPAQUE) ADD_CONTENTS(CONTENTS_TESTFOGVOLUME) +#ifdef MAPBASE + ADD_CONTENTS(CONTENTS_UNUSED) + ADD_CONTENTS(CONTENTS_UNUSED6) + ADD_CONTENTS(CONTENTS_TEAM1) + ADD_CONTENTS(CONTENTS_TEAM2) + ADD_CONTENTS(CONTENTS_IGNORE_NODRAW_OPAQUE) +#endif ADD_CONTENTS(CONTENTS_MOVEABLE) ADD_CONTENTS(CONTENTS_AREAPORTAL) ADD_CONTENTS(CONTENTS_PLAYERCLIP) diff --git a/mp/src/utils/vbsp/cubemap.cpp b/mp/src/utils/vbsp/cubemap.cpp index aeff12f1..091253ec 100644 --- a/mp/src/utils/vbsp/cubemap.cpp +++ b/mp/src/utils/vbsp/cubemap.cpp @@ -84,9 +84,15 @@ inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide ) return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap; } - +#ifdef PARALLAX_CORRECTED_CUBEMAPS +char* g_pParallaxObbStrs[MAX_MAP_CUBEMAPSAMPLES]; +void Cubemap_InsertSample( const Vector& origin, int size, char* pParallaxObbStr = "" ) +{ + g_pParallaxObbStrs[g_nCubemapSamples] = pParallaxObbStr; +#else void Cubemap_InsertSample( const Vector& origin, int size ) { +#endif dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples]; pSample->origin[0] = ( int )origin[0]; pSample->origin[1] = ( int )origin[1]; @@ -277,7 +283,15 @@ void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bH Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) ); } +#ifdef MAPBASE + +extern bool g_bSkyboxCubemaps; +extern int g_iDefaultCubemapSize; +#define DEFAULT_CUBEMAP_SIZE g_iDefaultCubemapSize + +#else #define DEFAULT_CUBEMAP_SIZE 32 +#endif void CreateDefaultCubemaps( bool bHDR ) { @@ -340,9 +354,17 @@ void CreateDefaultCubemaps( bool bHDR ) int iSize = pDstCubemap->ComputeMipSize( iMip ); int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset ); +#ifdef MAPBASE + if (!g_bSkyboxCubemaps) + { + memset( pDstBits, 0, iSize ); + continue; + } +#else // !!! FIXME: Set this to black until HDR cubemaps are built properly! memset( pDstBits, 0, iSize ); continue; +#endif if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square { @@ -430,6 +452,14 @@ void CreateDefaultCubemaps( bool bHDR ) pDstCubemap->ConvertImageFormat( originalFormat, false ); } +#ifdef MAPBASE + // Apply a seam fix + pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false ); + + pDstCubemap->MatchCubeMapBorders( 1, IMAGE_FORMAT_RGBA8888, false ); + pDstCubemap->MatchCubeMapBorders( 2, originalFormat, false ); +#endif + // Write the puppy out! char dstVTFFileName[1024]; if( bHDR ) @@ -529,7 +559,11 @@ static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &i //----------------------------------------------------------------------------- // Patches the $envmap for a material and all its dependents, returns true if any patching happened //----------------------------------------------------------------------------- +#ifdef PARALLAX_CORRECTED_CUBEMAPS +static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture, const char *pParallaxObbMatrix = "" ) +#else static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture ) +#endif { // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap' @@ -547,7 +581,11 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar ); if ( pDependentMaterial ) { +#ifdef PARALLAX_CORRECTED_CUBEMAPS + bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture, pParallaxObbMatrix ); +#else bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture ); +#endif } // If we have neither to patch, we're done @@ -558,7 +596,11 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons char pPatchedMaterialName[1024]; GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + MaterialPatchInfo_t pPatchInfo[7]; +#else MaterialPatchInfo_t pPatchInfo[2]; +#endif int nPatchCount = 0; if ( bShouldPatchEnvCubemap ) { @@ -568,6 +610,33 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons ++nPatchCount; } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemap matrix + CUtlVector matRowList; + if (pParallaxObbMatrix[0] != '\0') + { + V_SplitString( pParallaxObbMatrix, ";", matRowList ); + + // Needed for editor + pPatchInfo[nPatchCount].m_pKey = "$envMapParallax"; + pPatchInfo[nPatchCount].m_pValue = "1"; + ++nPatchCount; + + pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB1"; + pPatchInfo[nPatchCount].m_pValue = matRowList[0]; + ++nPatchCount; + pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB2"; + pPatchInfo[nPatchCount].m_pValue = matRowList[1]; + ++nPatchCount; + pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB3"; + pPatchInfo[nPatchCount].m_pValue = matRowList[2]; + ++nPatchCount; + pPatchInfo[nPatchCount].m_pKey = "$envMapOrigin"; + pPatchInfo[nPatchCount].m_pValue = matRowList[3]; + ++nPatchCount; + } +#endif + char pDependentPatchedMaterialName[1024]; if ( bDependentMaterialPatched ) { @@ -579,7 +648,14 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons ++nPatchCount; } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_INSERT ); + + // Clean up parallax stuff + matRowList.PurgeAndDeleteElements(); +#else CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE ); +#endif return true; } @@ -598,7 +674,11 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons // default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at // runtime before they run buildcubemaps. //----------------------------------------------------------------------------- +#ifdef PARALLAX_CORRECTED_CUBEMAPS +static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3], int cubemapIndex ) +#else static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) +#endif { // Don't make cubemap tex infos for nodes if ( originalTexInfo == TEXINFO_NODE ) @@ -630,6 +710,15 @@ static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) char pGeneratedTexDataName[1024]; GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Append origin info if this cubemap has a parallax OBB + char originAppendedString[1024] = ""; + if (g_pParallaxObbStrs[cubemapIndex] && g_pParallaxObbStrs[cubemapIndex][0] != '\0') + { + Q_snprintf(originAppendedString, 1024, "%s;[%d %d %d]", g_pParallaxObbStrs[cubemapIndex], origin[0], origin[1], origin[2]); + } +#endif + // Make sure the texdata doesn't already exist. int nTexDataID = FindTexData( pGeneratedTexDataName ); bool bHasTexData = (nTexDataID != -1); @@ -641,7 +730,11 @@ static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) // Hook the texture into the material and all dependent materials // but if no hooking was necessary, exit out +#ifdef PARALLAX_CORRECTED_CUBEMAPS + if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName, originAppendedString ) ) +#else if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) ) +#endif return originalTexInfo; // Store off the name of the cubemap that we need to create since we successfully patched @@ -731,7 +824,11 @@ void Cubemap_FixupBrushSidesMaterials( void ) } #endif +#ifdef PARALLAX_CORRECTED_CUBEMAPS + pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin, cubemapID ); +#else pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin ); +#endif if ( pSide->pMapDisp ) { pSide->pMapDisp->face.texinfo = pSide->texinfo; @@ -947,7 +1044,11 @@ void Cubemap_AttachDefaultCubemapToSpecularSides( void ) Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo ); } #endif +#ifdef PARALLAX_CORRECTED_CUBEMAPS + pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin, iCubemap ); +#else pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin ); +#endif if ( pSide->pMapDisp ) { pSide->pMapDisp->face.texinfo = pSide->texinfo; diff --git a/mp/src/utils/vbsp/detail.cpp b/mp/src/utils/vbsp/detail.cpp index 840068de..b17917bc 100644 --- a/mp/src/utils/vbsp/detail.cpp +++ b/mp/src/utils/vbsp/detail.cpp @@ -440,6 +440,9 @@ face_t *MakeBrushFace( side_t *originalSide, winding_t *winding ) f->split[0] = f->split[1] = NULL; f->w = CopyWinding( winding ); f->originalface = originalSide; +#ifdef MAPBASE + f->smoothingGroups = originalSide->smoothingGroups; +#endif // // save material info // diff --git a/mp/src/utils/vbsp/manifest.cpp b/mp/src/utils/vbsp/manifest.cpp index c72a9564..4ba2952a 100644 --- a/mp/src/utils/vbsp/manifest.cpp +++ b/mp/src/utils/vbsp/manifest.cpp @@ -5,13 +5,31 @@ #include "manifest.h" #include "windows.h" +#ifdef MAPBASE +entity_t *g_ManifestWorldSpawn = NULL; + +extern char g_MainMapPath[ MAX_PATH ]; +#endif + +//----------------------------------------------------------------------------- +// Purpose: default constructor +//----------------------------------------------------------------------------- +CManifestMapPrefs::CManifestMapPrefs( void ) +{ + m_nInternalId = 0; + m_bIsVisible = true; + //m_bIsPrimary = false; +} + //----------------------------------------------------------------------------- // Purpose: default constructor //----------------------------------------------------------------------------- CManifestMap::CManifestMap( void ) { + m_nInternalId = 0; m_RelativeMapFileName[ 0 ] = 0; m_bTopLevelMap = false; + m_bIsVisible = true; } @@ -35,7 +53,11 @@ CManifest::CManifest( void ) //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap ) { - if ( !stricmp( szKey, "Name" ) ) + if ( !stricmp( szKey, "InternalID" ) ) + { + pManifestMap->m_nInternalId = atoi( szValue ); + } + else if ( !stricmp( szKey, "Name" ) ) { // pManifestMap->m_FriendlyName = szValue; } @@ -260,6 +282,82 @@ ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFi return( eResult ); } +//----------------------------------------------------------------------------- +// Parses the preferences chunk that pertains to specific submaps in the map: +// +// Maps +// { +// VMF +// { +// "InternalID" "1" +// "IsPrimary" "1" +// } +// VMF +// { +// "InternalID" "2" +// } +// VMF +// { +// "InternalID" "3" +// "IsVisible" "0" +// } +// } +// +//----------------------------------------------------------------------------- + +ChunkFileResult_t CManifest::LoadPrefsVmfKeyCallback( const char *szKey, const char *szValue, CManifestMapPrefs *pManifestMapPrefs ) +{ + if ( !stricmp( szKey, "InternalID" ) ) + { + pManifestMapPrefs->m_nInternalId = atoi( szValue ); + } + else if ( !stricmp( szKey, "IsVisible" ) ) + { + pManifestMapPrefs->m_bIsVisible = ( atoi( szValue ) == 1 ); + } + //else if ( !stricmp( szKey, "IsPrimary" ) ) + //{ + // pManifestMapPrefs->m_bIsPrimary = ( atoi( szValue ) == 1 ); + //} + + return ChunkFile_Ok; +} + +//----------------------------------------------------------------------------- +// Parses preferences and applies them to their corresponding submaps +//----------------------------------------------------------------------------- +ChunkFileResult_t CManifest::LoadPrefsVmfCallback( CChunkFile *pFile, CManifest *pManifest ) +{ + CManifestMapPrefs prefs; + ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadPrefsVmfKeyCallback, &prefs ); + + if (eResult == ChunkFile_Ok && prefs.m_nInternalId != 0) + { + for( int i = 0; i < pManifest->m_Maps.Count(); i++ ) + { + if ( pManifest->m_Maps[ i ]->m_nInternalId == prefs.m_nInternalId ) + { + pManifest->m_Maps[ i ]->m_bIsVisible = prefs.m_bIsVisible; + break; + } + } + } + + return(eResult); +} +ChunkFileResult_t CManifest::LoadPrefsMapsCallback( CChunkFile *pFile, CManifest *pManifest ) +{ + CChunkHandlerMap Handlers; + Handlers.AddHandler( "VMF", ( ChunkHandler_t )CManifest::LoadPrefsVmfCallback, pManifest ); + pFile->PushHandlers(&Handlers); + + ChunkFileResult_t eResult = pFile->ReadChunk(); + + pFile->PopHandlers(); + + return( eResult ); +} + //----------------------------------------------------------------------------- // Purpose: this function will create a new entity pair @@ -298,12 +396,20 @@ bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ) memset( InstanceEntity, 0, sizeof( *InstanceEntity ) ); InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f ); + +#ifdef MAPBASE + g_ManifestWorldSpawn = InstanceEntity; +#else pEPair = CreateEPair( "classname", "worldspawn" ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; +#endif for( int i = 0; i < m_Maps.Count(); i++ ) { + if ( g_bNoHiddenManifestMaps && !m_Maps[ i ]->m_bIsVisible ) + continue; + // if ( m_Maps[ i ]->m_bTopLevelMap == false ) { char FileName[ MAX_PATH ]; @@ -361,7 +467,11 @@ bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) UserNameSize = sizeof( UserName ); if ( GetUserName( UserName, &UserNameSize ) == 0 ) { +#ifdef MAPBASE + strcpy( UserName, "default" ); +#else strcpy( UserPrefsFileName, "default" ); +#endif } sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName ); @@ -385,6 +495,9 @@ bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) CChunkHandlerMap Handlers; Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this ); + if (g_bNoHiddenManifestMaps) + Handlers.AddHandler( "Maps", ( ChunkHandler_t )CManifest::LoadPrefsMapsCallback, this ); + // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this); File.PushHandlers(&Handlers); @@ -456,11 +569,14 @@ bool CManifest::LoadVMFManifest( const char *pszFileName ) if ( g_MainMap == NULL ) { g_MainMap = g_LoadingMap; +#ifdef MAPBASE + V_ExtractFilePath( pszFileName, g_MainMapPath, sizeof( g_MainMapPath ) ); +#endif } - LoadSubMaps( g_LoadingMap, pszFileName ); - LoadVMFManifestUserPrefs( pszFileName ); + + LoadSubMaps( g_LoadingMap, pszFileName ); } return ( eResult == ChunkFile_Ok ); diff --git a/mp/src/utils/vbsp/manifest.h b/mp/src/utils/vbsp/manifest.h index e7b801e1..3d772117 100644 --- a/mp/src/utils/vbsp/manifest.h +++ b/mp/src/utils/vbsp/manifest.h @@ -32,8 +32,19 @@ class CManifestMap { public: CManifestMap( void ); + int m_nInternalId; char m_RelativeMapFileName[ MAX_PATH ]; bool m_bTopLevelMap; + bool m_bIsVisible; +}; + +class CManifestMapPrefs +{ +public: + CManifestMapPrefs( void ); + int m_nInternalId; + bool m_bIsVisible; + //int m_bIsPrimary; }; class CManifest @@ -51,6 +62,9 @@ public: static ChunkFileResult_t LoadCordonsKeyCallback( const char *pszKey, const char *pszValue, CManifest *pManifest ); static ChunkFileResult_t LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest ); static ChunkFileResult_t LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pManifest ); + static ChunkFileResult_t LoadPrefsVmfKeyCallback( const char *szKey, const char *szValue, CManifestMapPrefs *pManifestMapPrefs ); + static ChunkFileResult_t LoadPrefsVmfCallback( CChunkFile *pFile, CManifest *pManifest ); + static ChunkFileResult_t LoadPrefsMapsCallback( CChunkFile *pFile, CManifest *pManifest ); bool LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ); epair_t *CreateEPair( char *pKey, char *pValue ); diff --git a/mp/src/utils/vbsp/map.cpp b/mp/src/utils/vbsp/map.cpp index 2221c79f..f59305ba 100644 --- a/mp/src/utils/vbsp/map.cpp +++ b/mp/src/utils/vbsp/map.cpp @@ -15,6 +15,12 @@ #include "materialsub.h" #include "fgdlib/fgdlib.h" #include "manifest.h" +#ifdef PARALLAX_CORRECTED_CUBEMAPS +#include "matrixinvert.h" +#endif +#ifdef MAPBASE_VSCRIPT +#include "vscript_vbsp.h" +#endif #ifdef VSVMFIO #include "VmfImport.h" @@ -46,6 +52,15 @@ struct LoadSide_t extern qboolean onlyents; +#ifdef MAPBASE +extern entity_t *g_ManifestWorldSpawn; + +char g_MainMapPath[ MAX_PATH ]; + +// This is done for the instancing fix +bool g_pParallaxObbsDone[MAX_MAP_CUBEMAPSAMPLES]; +#endif + CUtlVector< CMapFile * > g_Maps; CMapFile *g_MainMap = NULL; @@ -1618,9 +1633,16 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) ) { const char *pSideListStr = ValueForKey( mapent, "sides" ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + char *pParallaxObbStr = ValueForKey( mapent, "parallaxobb" ); +#endif int size; size = IntForKey( mapent, "cubemapsize" ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + Cubemap_InsertSample( mapent->origin, size, pParallaxObbStr ); +#else Cubemap_InsertSample( mapent->origin, size ); +#endif Cubemap_SaveBrushSides( pSideListStr ); } // clear out this entity @@ -1628,6 +1650,88 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) return(ChunkFile_Ok); } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // + // parallax_obb brushes are removed after the transformation matrix is found and saved into + // the entity's data (ent will be removed after data transferred to patched materials) + // + if (!strcmp("parallax_obb", pClassName)) + { + matrix3x4_t obbMatrix, invObbMatrix; + SetIdentityMatrix(obbMatrix); + SetIdentityMatrix(invObbMatrix); + + // Get corner and its 3 edges (scaled, local x, y, and z axes) + mapbrush_t *brush = &mapbrushes[mapent->firstbrush]; + Vector corner, x, y, z; + + // Find first valid winding (with these whiles, if not enough valid windings then identity matrix is passed through to vmts) + int i = 0; + while (i < brush->numsides) + { + winding_t* wind = brush->original_sides[i].winding; + if (!wind) + { + i++; + continue; + } + + corner = wind->p[0]; + y = wind->p[1] - corner; + z = wind->p[3] - corner; + x = CrossProduct(y, z).Normalized(); + + i++; + break; + } + + // Skip second valid winding (opposite face from first, unusable for finding Z's length) + while (i < brush->numsides) + { + winding_t* wind = brush->original_sides[i].winding; + if (!wind) + { + i++; + continue; + } + i++; + break; + } + + // Find third valid winding + while (i < brush->numsides) + { + winding_t* wind = brush->original_sides[i].winding; + if (!wind) + { + i++; + continue; + } + + // Find length of x + // Start with diagonal, then scale x by the projection of diag onto x + Vector diag = wind->p[0] - wind->p[2]; + x *= abs(DotProduct(diag, x)); + + // Build transformation matrix (what is needed to turn a [0,0,0] - [1,1,1] cube into this brush) + MatrixSetColumn(x, 0, obbMatrix); + MatrixSetColumn(y, 1, obbMatrix); + MatrixSetColumn(z, 2, obbMatrix); + MatrixSetColumn(corner, 3, obbMatrix); + + //find inverse (we need the world to local matrix, "transformationmatrix" is kind of a misnomer) + MatrixInversion(obbMatrix, invObbMatrix); + break; + } + + char szMatrix[1024]; + Q_snprintf(szMatrix, 1024, "[%f %f %f %f];[%f %f %f %f];[%f %f %f %f]", invObbMatrix[0][0], invObbMatrix[0][1], invObbMatrix[0][2], invObbMatrix[0][3], invObbMatrix[1][0], invObbMatrix[1][1], invObbMatrix[1][2], invObbMatrix[1][3], invObbMatrix[2][0], invObbMatrix[2][1], invObbMatrix[2][2], invObbMatrix[2][3]); + SetKeyValue(mapent, "transformationmatrix", szMatrix); + + return (ChunkFile_Ok); + } +#endif + if ( !strcmp( "test_sidelist", pClassName ) ) { ConvertSideList(mapent, "sides"); @@ -2024,7 +2128,11 @@ void CMapFile::CheckForInstances( const char *pszFileName ) char InstancePath[ MAX_PATH ]; bool bLoaded = false; +#ifdef MAPBASE + if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) || DeterminePath( g_MainMapPath, pInstanceFile, InstancePath ) ) +#else if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) ) +#endif { if ( LoadMapFile( InstancePath ) ) { @@ -2333,6 +2441,11 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec entity_t *WorldspawnEnt = NULL; GameData::TNameFixup FixupStyle; +#ifdef MAPBASE + // For fixing AI node problems with manifests and instances + int max_ai_node_id = 0; +#endif + char *pTargetName = ValueForKey( pInstanceEntity, "targetname" ); char *pName = ValueForKey( pInstanceEntity, "name" ); if ( pTargetName[ 0 ] ) @@ -2359,6 +2472,20 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec max_entity_id = value; } } + +#ifdef MAPBASE + // If this is a classname starting with "info_node", look for a node ID keyvalue and + // add it to the counter. + if ( strnicmp( ValueForKey( &entities[ i ], "classname" ), "info_node", 9 ) == 0 ) + { + int value = atoi( ValueForKey( &entities[i], "nodeid" ) ); + if ( value > max_ai_node_id ) + { + max_ai_node_id = value; + //Warning( "Max AI nodes is now %i", max_ai_node_id ); + } + } +#endif } FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) ); @@ -2403,6 +2530,11 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle ); if ( EntClass ) { +#ifdef MAPBASE + // Sets up for additional instance remap fixes from Mapbase + GD.SetupInstanceRemapParams( max_ai_node_id, nummapbrushsides - Instance->nummapbrushsides, IntForKey( pInstanceEntity, "remap_vecline" ) > 0 ); +#endif + for( int i = 0; i < EntClass->GetVariableCount(); i++ ) { GDinputvariable *EntVar = EntClass->GetVariableAt( i ); @@ -2442,6 +2574,32 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z ); SetKeyValue( entity, "normal.z", temp );*/ } + +#ifdef MAPBASE + else if ( !strcmp( pEntity, "func_instance" ) ) + { + int iNumReplaces = 0; + for ( epair_t *epSubInstance = entity->epairs; epSubInstance != NULL; epSubInstance = epSubInstance->next ) + { + if ( strnicmp( epSubInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 ) + { + iNumReplaces++; + } + } + + // Merge this instance's keys + for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next ) + { + if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 ) + { + iNumReplaces++; + char szKey[32]; + Q_snprintf(szKey, sizeof(szKey), "replace%i", iNumReplaces); + SetKeyValue( entity, szKey, epInstance->value ); + } + } + } +#endif } #ifdef MERGE_INSTANCE_DEBUG_INFO @@ -2496,7 +2654,16 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec MoveBrushesToWorldGeneral( WorldspawnEnt ); WorldspawnEnt->numbrushes = 0; +#ifdef MAPBASE + char *pIsTopLevel = ValueForKey( pInstanceEntity, "toplevel" ); + if ( strcmp( pIsTopLevel, "1" ) == 0 ) + { + g_ManifestWorldSpawn->epairs = WorldspawnEnt->epairs; + } WorldspawnEnt->epairs = NULL; +#else + WorldspawnEnt->epairs = NULL; +#endif } @@ -2515,10 +2682,27 @@ void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vec for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ ) { Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); + +#ifdef MAPBASE + int iSides = (nummapbrushsides - Instance->nummapbrushsides); + for (int i2 = 0; i2 < g_aMapOverlays[i].aSideList.Count(); i2++) + { + //Warning( "Remapping overlay side %i to %i\n", g_aMapOverlays[i].aSideList[i2], g_aMapOverlays[i].aSideList[i2] + iSides ); + g_aMapOverlays[i].aSideList[i2] += iSides; + } +#endif } for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ ) { Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); + +#ifdef MAPBASE + int iSides = (nummapbrushsides - Instance->nummapbrushsides); + for (int i2 = 0; i2 < g_aMapWaterOverlays[i].aSideList.Count(); i2++) + { + g_aMapWaterOverlays[i].aSideList[i2] += iSides; + } +#endif } } @@ -2573,6 +2757,9 @@ bool LoadMapFile( const char *pszFileName ) if ( g_MainMap == NULL ) { g_MainMap = g_LoadingMap; +#ifdef MAPBASE + V_ExtractFilePath( pszFileName, g_MainMapPath, sizeof( g_MainMapPath ) ); +#endif } if ( g_MainMap == g_LoadingMap || verbose ) @@ -2611,6 +2798,23 @@ bool LoadMapFile( const char *pszFileName ) if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF)) { +#ifdef MAPBASE_VSCRIPT + if ( g_pScriptVM ) + { + HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnMapLoaded" ); + if ( hFunc ) + { + // Use GetLoadingMap() + //g_pScriptVM->SetValue( "map", g_LoadingMap->GetScriptInstance() ); + + g_pScriptVM->Call( hFunc ); + g_pScriptVM->ReleaseFunction( hFunc ); + + //g_pScriptVM->ClearValue( "map" ); + } + } +#endif + // Update the overlay/side list(s). Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays ); OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays ); @@ -2622,6 +2826,51 @@ bool LoadMapFile( const char *pszFileName ) pMainManifest->CordonWorld(); } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Fill out parallax obb matrix array + // "i" is static so this code could account for + // multiple LoadMapFile() calls from instances, etc. + for (int i = 0; i < g_nCubemapSamples; i++) + { + if (g_pParallaxObbStrs[i][0] != '\0' && g_pParallaxObbsDone[i] == false) + { + //Warning( "Testing OBB string %s\n", g_pParallaxObbStrs[i] ); + + entity_t* obbEnt = NULL; + for (int i2 = 0; i2 < g_LoadingMap->num_entities; i2++) + { + if (stricmp( ValueForKey( &g_LoadingMap->entities[i2], "targetname" ), g_pParallaxObbStrs[i] ) != 0) + continue; + + obbEnt = &g_LoadingMap->entities[i2]; + g_pParallaxObbStrs[i] = ValueForKey(obbEnt, "transformationmatrix"); + //Warning( "Using OBB transformation matrix \"%s\"\n", g_pParallaxObbStrs[i] ); + g_pParallaxObbsDone[i] = true; + + break; + } + + if (!obbEnt) + { + Warning( "Cannot find parallax obb \"%s\" (num_entities is %i)\n", g_pParallaxObbStrs[i], g_LoadingMap->num_entities ); + //g_pParallaxObbStrs[i][0] = '\0'; + } + } + } + + // Remove parallax_obb entities (in a nice slow linear search) + for (int i = 0; i < g_LoadingMap->num_entities; i++) + { + entity_t* mapent = &g_LoadingMap->entities[i]; + const char *pClassName = ValueForKey( mapent, "classname" ); + if ( !strcmp( "parallax_obb", pClassName ) ) + { + mapent->numbrushes = 0; + mapent->epairs = NULL; + } + } +#endif + ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs); for (int i=0 ; ientities[0].numbrushes ; i++) { @@ -3120,6 +3369,133 @@ ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, m } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HSCRIPT CMapFile::GetScriptInstance() +{ + if (!m_hScriptInstance) + m_hScriptInstance = g_pScriptVM->RegisterInstance( this ); + + return m_hScriptInstance; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapFile::ScriptGetEntityKeyValues( int idx, HSCRIPT hKeyTable, HSCRIPT hValTable ) +{ + epair_t *curPair = entities[idx].epairs; + while (curPair) + { + g_pScriptVM->ArrayAppend( hKeyTable, curPair->key ); + g_pScriptVM->ArrayAppend( hValTable, curPair->value ); + + curPair = curPair->next; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CMapFile::ScriptAddSimpleEntityKV( HSCRIPT hKV ) +{ + if (num_entities == MAX_MAP_ENTITIES) + { + // Exits. + g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); + return -1; + } + + entity_t *mapent = NULL; + if (::num_entities > 0) + { + // We're not loading maps anymore. Add this to the central BSP + mapent = &::entities[num_entities]; + ::num_entities++; + } + else + { + mapent = &entities[num_entities]; + num_entities++; + } + + memset(mapent, 0, sizeof(*mapent)); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + //mapent->portalareas[0] = -1; + //mapent->portalareas[1] = -1; + + LoadEntity_t LoadEntity; + LoadEntity.pEntity = mapent; + + // No default flags/contents + LoadEntity.nBaseFlags = 0; + LoadEntity.nBaseContents = 0; + + int nIterator = -1; + ScriptVariant_t varKey, varValue; + char szValue[256]; + while ((nIterator = g_pScriptVM->GetKeyValue( hKV, nIterator, &varKey, &varValue )) != -1) + { + switch (varValue.m_type) + { + case FIELD_CSTRING: Q_strncpy( szValue, varValue.m_pszString, sizeof(szValue) ); break; + case FIELD_INTEGER: Q_snprintf( szValue, sizeof(szValue), "%i", varValue.m_int ); break; + case FIELD_FLOAT: Q_snprintf( szValue, sizeof(szValue), "%f", varValue.m_float ); break; + case FIELD_CHARACTER: Q_snprintf( szValue, sizeof( szValue ), "%c", varValue.m_char ); break; + case FIELD_BOOLEAN: Q_snprintf( szValue, sizeof(szValue), "%d", varValue.m_bool ); break; + case FIELD_VECTOR: Q_snprintf( szValue, sizeof(szValue), "%f %f %f", (*varValue.m_pVector).x, (*varValue.m_pVector).y, (*varValue.m_pVector).z ); break; + default: szValue[0] = '\0'; break; + } + + LoadEntityKeyCallback( varKey, szValue, &LoadEntity ); + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + + return num_entities - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CMapFile::ScriptAddInstance( const char *pszVMF, const Vector& vecOrigin, const QAngle& angAngles ) +{ + if (num_entities == MAX_MAP_ENTITIES) + { + // Exits. + g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); + return -1; + } + + entity_t *mapent = &entities[num_entities]; + num_entities++; + memset(mapent, 0, sizeof(*mapent)); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + //mapent->portalareas[0] = -1; + //mapent->portalareas[1] = -1; + + SetKeyValue( mapent, "classname", "func_instance" ); + + SetKeyValue( mapent, "file", pszVMF ); + SetKeyValue( mapent, "fixup_style", "2" ); // No fixup + + char szValue[256]; + Q_snprintf( szValue, sizeof(szValue), "%f %f %f", vecOrigin.x, vecOrigin.y, vecOrigin.z ); + SetKeyValue( mapent, "origin", szValue ); + + Q_snprintf( szValue, sizeof(szValue), "%f %f %f", angAngles.x, angAngles.y, angAngles.z ); + SetKeyValue( mapent, "angles", szValue ); + + return num_entities - 1; +} +#endif + + /* ================ TestExpandBrushes diff --git a/mp/src/utils/vbsp/matrixinvert.h b/mp/src/utils/vbsp/matrixinvert.h new file mode 100644 index 00000000..6e676ad3 --- /dev/null +++ b/mp/src/utils/vbsp/matrixinvert.h @@ -0,0 +1,117 @@ +// By Jason Yu-Tseh Chi +// From http://chi3x10.wordpress.com/2008/05/28/calculate-matrix-inversion-in-c/ +// Modified to work with valve's matrix_3x4_t + +#include "mathlib\mathlib.h" + +// Calculate the cofactor of element (row,col) +int GetMatrixMinor(float **src, float **dest, int row, int col, int order) +{ + // Indicate which col and row is being copied to dest + int colCount = 0, rowCount = 0; + + for (int i = 0; i < order; i++) + { + if (i != row) + { + colCount = 0; + for (int j = 0; j < order; j++) + { + // When j is not the element + if (j != col) + { + dest[rowCount][colCount] = src[i][j]; + colCount++; + } + } + rowCount++; + } + } + + return 1; +} + +// Calculate the determinant recursively. +double CalcMatrixDeterminant(float **mat, int order) +{ + // Order must be >= 0 + // Stop the recursion when matrix is a single element + if (order == 1) + return mat[0][0]; + + // The determinant value + float det = 0; + + // Allocate the cofactor matrix + float **minor; + minor = new float*[order - 1]; + for (int i = 0; inodes[s] ); } } + +#ifdef MAPBASE + if (!noleaktest) + { + Warning( ("--- AREAPORTAL LEAK ---\n") ); + exit(0); + } +#endif } diff --git a/mp/src/utils/vbsp/staticprop.cpp b/mp/src/utils/vbsp/staticprop.cpp index 3ba6b95a..15c354d3 100644 --- a/mp/src/utils/vbsp/staticprop.cpp +++ b/mp/src/utils/vbsp/staticprop.cpp @@ -104,6 +104,7 @@ isstaticprop_ret IsStaticProp( studiohdr_t* pHdr ) if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP)) return RET_FAIL_NOT_MARKED_STATIC_PROP; +#ifndef MAPBASE // If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static KeyValues *modelKeyValues = new KeyValues(pHdr->pszName()); if ( StudioKeyValues( pHdr, modelKeyValues ) ) @@ -119,6 +120,7 @@ isstaticprop_ret IsStaticProp( studiohdr_t* pHdr ) } } modelKeyValues->deleteThis(); +#endif return RET_VALID; } diff --git a/mp/src/utils/vbsp/textures.cpp b/mp/src/utils/vbsp/textures.cpp index 4f49c5d4..3a7cffde 100644 --- a/mp/src/utils/vbsp/textures.cpp +++ b/mp/src/utils/vbsp/textures.cpp @@ -153,6 +153,13 @@ int FindMiptex (const char *name) { textureref[i].flags |= SURF_NOLIGHT; } +#ifdef MAPBASE + // handle Slammin-inspired %compileNoShadows% + else if ( ( propVal = GetMaterialVar( matID, "%compileNoShadows" ) ) && StringIsTrue( propVal ) ) + { + textureref[i].flags |= SURF_NOSHADOWS; + } +#endif else { // HANDLE ALL OF THE STUFF THAT IS RENDERED WITH THE MATERIAL THAT IS ON IT. diff --git a/mp/src/utils/vbsp/vbsp.cpp b/mp/src/utils/vbsp/vbsp.cpp index 7b8c0593..724b8a09 100644 --- a/mp/src/utils/vbsp/vbsp.cpp +++ b/mp/src/utils/vbsp/vbsp.cpp @@ -20,6 +20,11 @@ #include "byteswap.h" #include "worldvertextransitionfixup.h" +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#include "vscript_vbsp.h" +#endif + extern float g_maxLightmapDimension; char source[1024]; @@ -43,7 +48,11 @@ qboolean noshare; qboolean nosubdiv; qboolean notjunc; qboolean noopt; +#ifdef MAPBASE +qboolean noleaktest; +#else qboolean leaktest; +#endif qboolean verboseentities; qboolean dumpcollide = false; qboolean g_bLowPriority = false; @@ -56,6 +65,15 @@ bool g_NodrawTriggers = false; bool g_DisableWaterLighting = false; bool g_bAllowDetailCracks = false; bool g_bNoVirtualMesh = false; +bool g_bNoHiddenManifestMaps = false; +#ifdef MAPBASE +bool g_bNoDefaultCubemaps = true; +bool g_bSkyboxCubemaps = false; +int g_iDefaultCubemapSize = 32; +#endif +#ifdef MAPBASE_VSCRIPT +ScriptLanguage_t g_iScripting = SL_NONE; +#endif float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE; float g_luxelScale = 1.0f; @@ -296,7 +314,11 @@ void ProcessWorldModel (void) Warning( ("**** leaked ****\n") ); leaked = true; LeakFile (tree); +#ifdef MAPBASE + if (!noleaktest) +#else if (leaktest) +#endif { Warning( ("--- MAP LEAKED ---\n") ); exit (0); @@ -670,6 +692,7 @@ void SetOccluderArea( int nOccluder, int nArea, int nEntityNum ) { g_OccluderData[nOccluder].area = nArea; } +#ifndef MAPBASE else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) ) { const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" ); @@ -679,6 +702,7 @@ void SetOccluderArea( int nOccluder, int nArea, int nEntityNum ) } Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName ); } +#endif } @@ -859,7 +883,12 @@ void ProcessModels (void) } // Turn the skybox into a cubemap in case we don't build env_cubemap textures. +#ifdef MAPBASE + if (!g_bNoDefaultCubemaps) + Cubemap_CreateDefaultCubemaps(); +#else Cubemap_CreateDefaultCubemaps(); +#endif EndBSPFile (); } @@ -1000,11 +1029,19 @@ int RunVBSP( int argc, char **argv ) Msg ("microvolume = %f\n", microvolume); i++; } +#ifdef MAPBASE + else if (!Q_stricmp(argv[i], "-noleaktest")) + { + Msg ("noleaktest = true\n"); + noleaktest = true; + } +#else else if (!Q_stricmp(argv[i], "-leaktest")) { Msg ("leaktest = true\n"); leaktest = true; } +#endif else if (!Q_stricmp(argv[i], "-verboseentities")) { Msg ("verboseentities = true\n"); @@ -1150,6 +1187,100 @@ int RunVBSP( int argc, char **argv ) g_pFullFileSystem->AddSearchPath( g_szEmbedDir, "GAME", PATH_ADD_TO_TAIL ); g_pFullFileSystem->AddSearchPath( g_szEmbedDir, "MOD", PATH_ADD_TO_TAIL ); } + else if ( !Q_stricmp( argv[i], "-nohiddenmaps" ) ) + { + g_bNoHiddenManifestMaps = true; + } +#ifdef MAPBASE + // Thanks to Mapbase's shader changes, default all-black cubemaps are no longer needed. + // The command has been switched from "-nodefaultcubemap" to "-defaultcubemap", + // meaning maps are compiled without them by default. + else if ( !Q_stricmp( argv[i], "-defaultcubemap" ) ) + { + g_bNoDefaultCubemaps = false; + } + // Default cubemaps are supposed to show the sky texture, but Valve disabled this + // because they didn't get it working for HDR cubemaps. As a result, all default + // cubemaps appear as all-black textures. However, this parameter has been added to + // re-enable skybox cubemaps for LDR cubemaps. (HDR skybox cubemaps are not supported) + else if ( !Q_stricmp( argv[i], "-skyboxcubemap" ) ) + { + g_bNoDefaultCubemaps = false; + g_bSkyboxCubemaps = true; + } + else if ( !Q_stricmp( argv[i], "-defaultcubemapres" ) ) + { + g_iDefaultCubemapSize = atoi( argv[i + 1] ); + Msg( "Default cubemap size = %i\n", g_iDefaultCubemapSize ); + i++; + } +#endif +#ifdef MAPBASE_VSCRIPT + else if ( !Q_stricmp( argv[i], "-scripting" ) ) + { + const char *pszScriptLanguage = argv[i + 1]; + if( pszScriptLanguage[0] == '-') + { + // It's another command. Just use default + g_iScripting = SL_DEFAULT; + } + else + { + // Use a specific language + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + g_iScripting = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + g_iScripting = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + g_iScripting = SL_PYTHON; + } + else if( !Q_stricmp(pszScriptLanguage, "lua") ) + { + g_iScripting = SL_LUA; + } + else + { + DevWarning("-server_script does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + g_iScripting = SL_NONE; + } + i++; + } + } + else if ( !Q_stricmp( argv[i], "-doc" ) ) + { + // Only print the documentation + + if (g_iScripting) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + VScriptVBSPInit(); + + const char *pszArg1 = argv[i + 1]; + if (pszArg1[0] == '-') + { + // It's another command. Just use * + pszArg1 = "*"; + } + + char szCommand[512]; + _snprintf( szCommand, sizeof( szCommand ), "PrintHelp( \"%s\" );", pszArg1 ); + g_pScriptVM->Run( szCommand ); + } + else + { + Warning("Cannot print documentation without scripting enabled!\n"); + } + + DeleteCmdLine( argc, argv ); + CmdLib_Cleanup(); + CmdLib_Exit( 1 ); + } +#endif else if (argv[i][0] == '-') { Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]); @@ -1236,6 +1367,7 @@ int RunVBSP( int argc, char **argv ) " -nox360 : Disable generation Xbox360 version of vsp (default)\n" " -replacematerials : Substitute materials according to materialsub.txt in content\\maps\n" " -FullMinidumps : Write large minidumps on crash.\n" + " -nohiddenmaps : Exclude manifest maps if they are currently hidden.\n" ); } @@ -1297,6 +1429,15 @@ int RunVBSP( int argc, char **argv ) InitMaterialSystem( materialPath, CmdLib_GetFileSystemFactory() ); Msg( "materialPath: %s\n", materialPath ); +#ifdef MAPBASE_VSCRIPT + if (g_iScripting) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + + VScriptVBSPInit(); + } +#endif + // delete portal and line files sprintf (path, "%s.prt", source); remove (path); @@ -1423,6 +1564,9 @@ int RunVBSP( int argc, char **argv ) ReleasePakFileLumps(); DeleteMaterialReplacementKeys(); ShutdownMaterialSystem(); +#ifdef MAPBASE_VSCRIPT + VScriptVBSPTerm(); +#endif CmdLib_Cleanup(); return 0; } diff --git a/mp/src/utils/vbsp/vbsp.h b/mp/src/utils/vbsp/vbsp.h index 689bbaa8..3e9f44c0 100644 --- a/mp/src/utils/vbsp/vbsp.h +++ b/mp/src/utils/vbsp/vbsp.h @@ -19,6 +19,9 @@ #include "qfiles.h" #include "utilmatlib.h" #include "ChunkFile.h" +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#endif #ifdef WIN32 #pragma warning( disable: 4706 ) @@ -34,6 +37,12 @@ class CUtlBuffer; // this will output glview files for the given brushmodel. Brushmodel 1 is the world, 2 is the first brush entity, etc. #define DEBUG_BRUSHMODEL 0 +#ifdef MAPBASE +// Activates compiler code for parallax corrected cubemaps +// https://developer.valvesoftware.com/wiki/Parallax_Corrected_Cubemaps +#define PARALLAX_CORRECTED_CUBEMAPS 1 +#endif + struct portal_t; struct node_t; @@ -334,6 +343,32 @@ public: int m_StartMapOverlays; int m_StartMapWaterOverlays; + +#ifdef MAPBASE_VSCRIPT + HSCRIPT GetScriptInstance(); + + // VScript functions + ALLOW_SCRIPT_ACCESS(); +private: + + const Vector& GetMins() { return map_mins; } + const Vector& GetMaxs() { return map_maxs; } + + int GetNumMapBrushes() { return nummapbrushes; } + + const Vector& GetEntityOrigin(int idx) { return (idx < num_entities && idx >= 0) ? entities[idx].origin : vec3_origin; } + int GetEntityFirstBrush(int idx) { return (idx < num_entities && idx >= 0) ? entities[idx].firstbrush : 0; } + int GetEntityNumBrushes(int idx) { return (idx < num_entities && idx >= 0) ? entities[idx].numbrushes : 0; } + + void ScriptGetEntityKeyValues(int idx, HSCRIPT hKeyTable, HSCRIPT hValTable); + + int ScriptAddSimpleEntityKV(HSCRIPT hKV/*, const Vector& vecOrigin, int iFirstBrush, int iNumBrushes*/); + int ScriptAddInstance(const char *pszVMF, const Vector& vecOrigin, const QAngle& angAngles); + + int GetNumEntities() { return num_entities; } + + HSCRIPT m_hScriptInstance; +#endif }; extern CMapFile *g_MainMap; @@ -365,6 +400,7 @@ extern bool g_NodrawTriggers; extern bool g_DisableWaterLighting; extern bool g_bAllowDetailCracks; extern bool g_bNoVirtualMesh; +extern bool g_bNoHiddenManifestMaps; extern char outbase[32]; extern char source[1024]; @@ -607,7 +643,12 @@ void SaveVertexNormals( void ); //============================================================================= // cubemap.cpp +#ifdef PARALLAX_CORRECTED_CUBEMAPS +extern char* g_pParallaxObbStrs[MAX_MAP_CUBEMAPSAMPLES]; +void Cubemap_InsertSample( const Vector& origin, int size, char* pParallaxObbStr ); +#else void Cubemap_InsertSample( const Vector& origin, int size ); +#endif void Cubemap_CreateDefaultCubemaps( void ); void Cubemap_SaveBrushSides( const char *pSideListStr ); void Cubemap_FixupBrushSidesMaterials( void ); diff --git a/mp/src/utils/vbsp/vbsp.vpc b/mp/src/utils/vbsp/vbsp.vpc index e55762f2..4d8bd410 100644 --- a/mp/src/utils/vbsp/vbsp.vpc +++ b/mp/src/utils/vbsp/vbsp.vpc @@ -6,6 +6,7 @@ $Macro SRCDIR "..\.." $Macro OUTBINDIR "$SRCDIR\..\game\bin" +$Macro OUTBINNAME "vbsp" $Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc" @@ -15,6 +16,8 @@ $Configuration { $AdditionalIncludeDirectories "$BASE,..\common,..\vmpi" $PreprocessorDefinitions "$BASE;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE" + + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } $Linker @@ -65,6 +68,14 @@ $Project "Vbsp" $File "worldvertextransitionfixup.cpp" $File "writebsp.cpp" $File "$SRCDIR\public\zip_utils.cpp" + + $File "vscript_vbsp.cpp" [$MAPBASE_VSCRIPT] + $File "vscript_vbsp.h" [$MAPBASE_VSCRIPT] + $File "vscript_vbsp.nut" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vmfs.cpp" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vmfs.h" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vis.cpp" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vis.h" [$MAPBASE_VSCRIPT] $Folder "Common Files" { @@ -179,6 +190,7 @@ $Project "Vbsp" $Lib tier2 $Lib vtf $Lib "$LIBCOMMON/lzma" + $Lib vscript [$MAPBASE_VSCRIPT] } $File "notes.txt" diff --git a/mp/src/utils/vbsp/vscript_funcs_vis.cpp b/mp/src/utils/vbsp/vscript_funcs_vis.cpp new file mode 100644 index 00000000..7a5fbfb6 --- /dev/null +++ b/mp/src/utils/vbsp/vscript_funcs_vis.cpp @@ -0,0 +1,29 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" +#include "tier1/fmtstr.h" + +#include "vbsp.h" +#include "map.h" +#include "fgdlib/fgdlib.h" + +#include "vscript_vbsp.h" +#include "vscript_funcs_vis.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// There are currently no vis-related functions, but it would be nice to have them in the future. + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void RegisterVisScriptFunctions() +{ + //ScriptRegisterFunction( g_pScriptVM, VMFKV_CreateBlank, "Creates a CScriptKeyValues instance with VMF formatting." ); +} diff --git a/mp/src/utils/vbsp/vscript_funcs_vis.h b/mp/src/utils/vbsp/vscript_funcs_vis.h new file mode 100644 index 00000000..906b7d7a --- /dev/null +++ b/mp/src/utils/vbsp/vscript_funcs_vis.h @@ -0,0 +1,16 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_VIS +#define VSCRIPT_FUNCS_VIS +#ifdef _WIN32 +#pragma once +#endif + +void RegisterVisScriptFunctions(); + +#endif diff --git a/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp b/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp new file mode 100644 index 00000000..3894ddf3 --- /dev/null +++ b/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp @@ -0,0 +1,156 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" +#include "tier1/fmtstr.h" + +#include "vbsp.h" +#include "map.h" +#include "fgdlib/fgdlib.h" + +#include "vscript_vbsp.h" +#include "vscript_funcs_vmfs.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +static HSCRIPT VMFKV_CreateBlank() +{ + KeyValues *pKV = new KeyValues("VMF"); + + KeyValues *pWorld = pKV->FindKey( "world", true ); + if (pWorld) + { + pWorld->SetString( "classname", "worldspawn" ); + } + + return scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); +} + +static bool VMFKV_SaveToFile( const char *szFile, HSCRIPT hKV ) +{ + Warning( "Getting keyvalues from thing\n" ); + + KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV ); + if (!pKV) + return false; + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + for (KeyValues *pSubKey = pKV->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey()) + { + pSubKey->RecursiveSaveToFile( buf, 0 ); + } + + char pszFullName[MAX_PATH]; + Q_ExtractFilePath( source, pszFullName, sizeof(pszFullName) ); + V_snprintf( pszFullName, sizeof(pszFullName), "%s/vscript_io/%s", pszFullName, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + Warning( "Invalid file location : %s\n", szFile ); + buf.Purge(); + return false; + } + + int nSize = V_strlen(pszFullName) + 1; + char *pszDir = (char*)stackalloc(nSize); + V_memcpy( pszDir, pszFullName, nSize ); + V_StripFilename( pszDir ); + + //g_pFullFileSystem->RelativePathToFullPath( szFile, NULL, pszFullName, sizeof( pszFullName ) ); + Warning( "Full path is %s!\n", pszFullName ); + g_pFullFileSystem->CreateDirHierarchy( pszDir, NULL ); + bool res = g_pFullFileSystem->WriteFile( pszFullName, NULL, buf ); + buf.Purge(); + return res; +} + +static HSCRIPT VMFKV_LoadFromFile( const char *szFile ) +{ + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), NULL, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return NULL; + } + + KeyValues *pKV = new KeyValues( szFile ); + if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, NULL ) ) + { + return NULL; + } + + HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); // bAllowDestruct is supposed to automatically remove the involved KV + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +inline void ScriptVarsToKV( ScriptVariant_t &varKey, ScriptVariant_t &varValue, KeyValues *pKV ) +{ + switch (varValue.m_type) + { + case FIELD_CSTRING: pKV->SetString( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_INTEGER: pKV->SetInt( varKey.m_pszString, varValue.m_int ); break; + case FIELD_FLOAT: pKV->SetFloat( varKey.m_pszString, varValue.m_float ); break; + case FIELD_BOOLEAN: pKV->SetBool( varKey.m_pszString, varValue.m_bool ); break; + case FIELD_VECTOR: pKV->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break; + } +} + +static HSCRIPT VMFKV_AddEntityFromTables( HSCRIPT hVMF, HSCRIPT hKV, HSCRIPT hIO ) +{ + KeyValues *pVMF = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hVMF ); + if (!pVMF) + return false; + + KeyValues *pEnt = pVMF->CreateNewKey(); + if (!pEnt) + return false; + + pEnt->SetName( "entity" ); + + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( hKV, nIterator, &varKey, &varValue )) != -1) + { + ScriptVarsToKV( varKey, varValue, pEnt ); + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + + KeyValues *pConnections = pEnt->FindKey( "connections", true ); + if (hIO && pConnections) + { + nIterator = -1; + while ((nIterator = g_pScriptVM->GetKeyValue( hIO, nIterator, &varKey, &varValue )) != -1) + { + ScriptVarsToKV( varKey, varValue, pEnt ); + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + } + + return scriptmanager->CreateScriptKeyValues( g_pScriptVM, pEnt, false ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void RegisterVMFScriptFunctions() +{ + ScriptRegisterFunction( g_pScriptVM, VMFKV_CreateBlank, "Creates a CScriptKeyValues instance with VMF formatting." ); + ScriptRegisterFunction( g_pScriptVM, VMFKV_SaveToFile, "Saves a CScriptKeyValues instance with VMF formatting." ); + ScriptRegisterFunction( g_pScriptVM, VMFKV_LoadFromFile, "Loads a VMF as a CScriptKeyValues instance with VMF formatting." ); + ScriptRegisterFunction( g_pScriptVM, VMFKV_AddEntityFromTables, "Adds a VMF-formatted entity to a CScriptKeyValues instance." ); +} diff --git a/mp/src/utils/vbsp/vscript_funcs_vmfs.h b/mp/src/utils/vbsp/vscript_funcs_vmfs.h new file mode 100644 index 00000000..0b3237a7 --- /dev/null +++ b/mp/src/utils/vbsp/vscript_funcs_vmfs.h @@ -0,0 +1,16 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_VMFS +#define VSCRIPT_FUNCS_VMFS +#ifdef _WIN32 +#pragma once +#endif + +void RegisterVMFScriptFunctions(); + +#endif diff --git a/mp/src/utils/vbsp/vscript_vbsp.cpp b/mp/src/utils/vbsp/vscript_vbsp.cpp new file mode 100644 index 00000000..91fa41f6 --- /dev/null +++ b/mp/src/utils/vbsp/vscript_vbsp.cpp @@ -0,0 +1,333 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase's implementation of VScript in VBSP, allowing users to modify map compilation behavior with scripts. +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" +#include "tier1/fmtstr.h" + +#include "vbsp.h" +#include "map.h" +#include "fgdlib/fgdlib.h" + +#include "vscript_vbsp.h" +#include "vscript_vbsp.nut" +#include "vscript_funcs_vmfs.h" +#include "vscript_funcs_vis.h" + +IScriptVM *g_pScriptVM; +IScriptManager *scriptmanager = NULL; + +extern ScriptLanguage_t g_iScripting; + +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +// This is to ensure a dependency exists between the vscript library and the game DLLs +extern int vscript_token; +int vscript_token_hack = vscript_token; + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return NULL; + } + + static const char *pszExtensions[] = + { + "", // SL_NONE + ".gm", // SL_GAMEMONKEY + ".nut", // SL_SQUIRREL + ".lua", // SL_LUA + ".py", // SL_PYTHON + }; + + const char *pszVMExtension = pszExtensions[g_pScriptVM->GetLanguage()]; + const char *pszIncomingExtension = V_strrchr( pszScriptName , '.' ); + if ( pszIncomingExtension && V_strcmp( pszIncomingExtension, pszVMExtension ) != 0 ) + { + Warning( "Script file type does not match VM type\n" ); + return NULL; + } + + CFmtStr scriptPath; + if ( pszIncomingExtension ) + { + scriptPath = pszScriptName; + } + else + { + scriptPath.sprintf( "%s%s", pszScriptName, pszVMExtension ); + } + + const char *pBase; + CUtlBuffer bufferScript; + + if ( g_pScriptVM->GetLanguage() == SL_PYTHON ) + { + // python auto-loads raw or precompiled modules - don't load data here + pBase = NULL; + } + else + { + /* + FileFindHandle_t handle = NULL; + const char *file = g_pFullFileSystem->FindFirst( "*", &handle ); + while (file) + { + Msg( "File in this directory: %s\n", file ); + file = g_pFullFileSystem->FindNext(handle); + } + + Msg( "File exists: %d\n", g_pFullFileSystem->FileExists( scriptPath ) ); + */ + + + bool bResult = g_pFullFileSystem->ReadFile( scriptPath, NULL, bufferScript ); + + if ( !bResult && bWarnMissing ) + { + Warning( "Script not found (%s) \n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + + pBase = (const char *) bufferScript.Base(); + + if ( !pBase || !*pBase ) + { + return NULL; + } + } + + + const char *pszFilename = V_strrchr( scriptPath, '\\' ); + pszFilename++; + HSCRIPT hScript = g_pScriptVM->CompileScript( pBase, pszFilename ); + if ( !hScript ) + { + Warning( "FAILED to compile and execute script file named %s\n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + return hScript; +} + +static int g_ScriptVBSPRunScriptDepth; + +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + if ( !pszScriptName || !*pszScriptName ) + { + Warning( "Cannot run script: NULL script name\n" ); + return false; + } + + // Prevent infinite recursion in VM + if ( g_ScriptVBSPRunScriptDepth > 16 ) + { + Warning( "IncludeScript stack overflow\n" ); + return false; + } + + g_ScriptVBSPRunScriptDepth++; + HSCRIPT hScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + bool bSuccess = false; + if ( hScript ) + { + bSuccess = ( g_pScriptVM->Run( hScript, hScope ) != SCRIPT_ERROR ); + if ( !bSuccess ) + { + Warning( "Error running script named %s\n", pszScriptName ); + Assert( "Error running script" ); + } + } + g_ScriptVBSPRunScriptDepth--; + return bSuccess; +} + +BEGIN_SCRIPTDESC_ROOT( CMapFile, "Map file" ) + + DEFINE_SCRIPTFUNC( GetMins, "Get the map's mins." ) + DEFINE_SCRIPTFUNC( GetMaxs, "Get the map's maxs." ) + + DEFINE_SCRIPTFUNC( GetEntityOrigin, "Get the origin of the entity with the specified index." ) + DEFINE_SCRIPTFUNC( GetEntityFirstBrush, "Get the first brush ID of the entity with the specified index." ) + DEFINE_SCRIPTFUNC( GetEntityNumBrushes, "Get the number of brushes in the entity with the specified index." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEntityKeyValues, "GetEntityKeyValues", "Export an entity's keyvalues to two arrays." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddSimpleEntityKV, "AddSimpleEntityKV", "Add a simple entity from a keyvalue table." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddInstance, "AddInstance", "Add an instance to the map." ) + + DEFINE_SCRIPTFUNC( GetNumEntities, "Get the number of entities in the map." ) + +END_SCRIPTDESC(); + +static const char *GetSource() +{ + return source; +} + +static const char *GetMapBase() +{ + return mapbase; +} + +static HSCRIPT GetMainMap() +{ + return g_MainMap ? g_MainMap->GetScriptInstance() : NULL; +} + +static HSCRIPT GetLoadingMap() +{ + return g_LoadingMap ? g_LoadingMap->GetScriptInstance() : NULL; +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +extern qboolean glview; +extern qboolean onlyents; +extern bool onlyprops; +extern qboolean noleaktest; +extern qboolean verboseentities; +extern qboolean g_bLowPriority; +extern bool g_bKeepStaleZip; +extern bool g_bNoDefaultCubemaps; +extern bool g_bSkyboxCubemaps; +extern int g_iDefaultCubemapSize; + +bool VScriptVBSPInit() +{ + VMPROF_START + + if( g_iScripting != SL_NONE && scriptmanager != NULL ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( g_iScripting ); + + if( g_pScriptVM ) + { + Log( "VSCRIPT VBSP: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + + ScriptRegisterFunction( g_pScriptVM, GetSource, "Gets the base directory of the first map loaded." ); + ScriptRegisterFunction( g_pScriptVM, GetMapBase, "Gets the base name of the first map loaded." ); + ScriptRegisterFunction( g_pScriptVM, GetMainMap, "Gets the first map loaded." ); + ScriptRegisterFunction( g_pScriptVM, GetLoadingMap, "Gets the map which is currently loading (e.g. an instance)." ); + + ScriptRegisterFunction( g_pScriptVM, DoUniqueString, SCRIPT_ALIAS( "UniqueString", "Generate a string guaranteed to be unique across the life of the script VM, with an optional root string. Useful for adding data to tables when not sure what keys are already in use in that table." ) ); + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + + ScriptRegisterConstantNamed( g_pScriptVM, GameData::NAME_FIXUP_PREFIX, "NAME_FIXUP_PREFIX", "Prefix name fixup" ); + ScriptRegisterConstantNamed( g_pScriptVM, GameData::NAME_FIXUP_POSTFIX, "NAME_FIXUP_PREFIX", "Postfix name fixup" ); + ScriptRegisterConstantNamed( g_pScriptVM, GameData::NAME_FIXUP_NONE, "NAME_FIXUP_NONE", "No name fixup" ); + + ScriptRegisterConstant( g_pScriptVM, microvolume, "" ); + ScriptRegisterConstant( g_pScriptVM, noprune, "" ); + ScriptRegisterConstant( g_pScriptVM, glview, "" ); + ScriptRegisterConstant( g_pScriptVM, nodetail, "" ); + ScriptRegisterConstant( g_pScriptVM, fulldetail, "" ); + ScriptRegisterConstant( g_pScriptVM, onlyents, "" ); + ScriptRegisterConstant( g_pScriptVM, onlyprops, "" ); + ScriptRegisterConstant( g_pScriptVM, nomerge, "" ); + ScriptRegisterConstant( g_pScriptVM, nomergewater, "" ); + ScriptRegisterConstant( g_pScriptVM, nowater, "" ); + ScriptRegisterConstant( g_pScriptVM, nocsg, "" ); + ScriptRegisterConstant( g_pScriptVM, noweld, "" ); + ScriptRegisterConstant( g_pScriptVM, noshare, "" ); + ScriptRegisterConstant( g_pScriptVM, nosubdiv, "" ); + ScriptRegisterConstant( g_pScriptVM, notjunc, "" ); + ScriptRegisterConstant( g_pScriptVM, noopt, "" ); + ScriptRegisterConstant( g_pScriptVM, noleaktest, "" ); + ScriptRegisterConstant( g_pScriptVM, verboseentities, "" ); + ScriptRegisterConstant( g_pScriptVM, dumpcollide, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bLowPriority, "" ); + ScriptRegisterConstant( g_pScriptVM, g_DumpStaticProps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bSkyVis, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bLightIfMissing, "" ); + ScriptRegisterConstant( g_pScriptVM, g_snapAxialPlanes, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bKeepStaleZip, "" ); + ScriptRegisterConstant( g_pScriptVM, g_NodrawTriggers, "" ); + ScriptRegisterConstant( g_pScriptVM, g_DisableWaterLighting, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bAllowDetailCracks, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bNoVirtualMesh, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bNoHiddenManifestMaps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bNoDefaultCubemaps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bSkyboxCubemaps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_iDefaultCubemapSize, "" ); + + if (g_iScripting == SL_SQUIRREL) + { + g_pScriptVM->Run( g_Script_vscript_vbsp ); + } + + RegisterVMFScriptFunctions(); + + // Run the map's script + char script[96]; + Q_snprintf( script, sizeof(script), "%s_vbsp", source ); + //Msg("VBSP script: \"%s\"\n", script); + VScriptRunScript( script, true ); + + VMPROF_SHOW( g_iScripting, "virtual machine startup" ); + + return true; + } + else + { + DevWarning("VM Did not start!\n"); + } + } + else + { + Log( "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptVBSPTerm() +{ + if( g_pScriptVM != NULL ) + { + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} diff --git a/mp/src/utils/vbsp/vscript_vbsp.h b/mp/src/utils/vbsp/vscript_vbsp.h new file mode 100644 index 00000000..1a6e390a --- /dev/null +++ b/mp/src/utils/vbsp/vscript_vbsp.h @@ -0,0 +1,27 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase's implementation of VScript in VBSP, allowing users to modify map compilation behavior with scripts. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VSCRIPT_VBSP_H +#define VSCRIPT_VBSP_H + +#include "vscript/ivscript.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM *g_pScriptVM; +extern IScriptManager *scriptmanager; + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing = false ); +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = false ) { return VScriptRunScript( pszScriptName, NULL, bWarnMissing ); } + +bool VScriptVBSPInit(); +void VScriptVBSPTerm(); + +#endif // VSCRIPT_SERVER_H diff --git a/mp/src/utils/vbsp/vscript_vbsp.nut b/mp/src/utils/vbsp/vscript_vbsp.nut new file mode 100644 index 00000000..2aa99060 --- /dev/null +++ b/mp/src/utils/vbsp/vscript_vbsp.nut @@ -0,0 +1,52 @@ +static char g_Script_vscript_vbsp[] = R"vscript( +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//=============================================================================// + +function UniqueString( string = "" ) +{ + return ::DoUniqueString( string.tostring() ); +} + +function __ReplaceClosures( script, scope ) +{ + if ( !scope ) + { + scope = getroottable(); + } + + local tempParent = { getroottable = function() { return null; } }; + local temp = { runscript = script }; + temp.set_delegate(tempParent); + + temp.runscript() + foreach( key,val in temp ) + { + if ( typeof(val) == "function" && key != "runscript" ) + { + printl( " Replacing " + key ); + scope[key] <- val; + } + } +} + +function IncludeScript( name, scope = null ) +{ + if ( !scope ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +// VBSP logs don't support ConColorMsg() +print <- Msg + +function printdoc( text ) +{ + return ::print(text + "\n"); +} + +)vscript"; \ No newline at end of file diff --git a/mp/src/utils/vrad/vrad_dll.vpc b/mp/src/utils/vrad/vrad_dll.vpc index 7ebef01d..639cd880 100644 --- a/mp/src/utils/vrad/vrad_dll.vpc +++ b/mp/src/utils/vrad/vrad_dll.vpc @@ -6,6 +6,7 @@ $Macro SRCDIR "..\.." $Macro OUTBINDIR "$SRCDIR\..\game\bin" +$Macro OUTBINNAME "vrad_dll" $Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" diff --git a/mp/src/utils/vrad_launcher/vrad_launcher.cpp b/mp/src/utils/vrad_launcher/vrad_launcher.cpp index a4d31834..0afb27d7 100644 --- a/mp/src/utils/vrad_launcher/vrad_launcher.cpp +++ b/mp/src/utils/vrad_launcher/vrad_launcher.cpp @@ -103,6 +103,18 @@ int main(int argc, char* argv[]) if (mode && (! both_arg)) continue; +#ifdef MAPBASE + // Coming through! + if ( !pModule ) + { + // With this, we just load the DLL with our filename. + // This allows for custom DLLs without having to bother with the launcher. + char filename[64]; + Q_FileBase(argv[0], filename, sizeof(filename)); + Q_snprintf(dllName, sizeof(dllName), "%s%s", filename, "_dll.dll"); + pModule = Sys_LoadModule( dllName ); + } +#endif // If it didn't load the module above, then use the if ( !pModule ) diff --git a/mp/src/utils/vvis/vvis_dll.vpc b/mp/src/utils/vvis/vvis_dll.vpc index 211719ff..529c7aa7 100644 --- a/mp/src/utils/vvis/vvis_dll.vpc +++ b/mp/src/utils/vvis/vvis_dll.vpc @@ -6,6 +6,7 @@ $Macro SRCDIR "..\.." $Macro OUTBINDIR "$SRCDIR\..\game\bin" +$Macro OUTBINNAME "vvis_dll" $Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" diff --git a/mp/src/utils/vvis_launcher/vvis_launcher.cpp b/mp/src/utils/vvis_launcher/vvis_launcher.cpp index edf03d25..ddc2b83f 100644 --- a/mp/src/utils/vvis_launcher/vvis_launcher.cpp +++ b/mp/src/utils/vvis_launcher/vvis_launcher.cpp @@ -45,6 +45,7 @@ char* GetLastErrorString() int main(int argc, char* argv[]) { CommandLine()->CreateCmdLine( argc, argv ); +#ifndef MAPBASE const char *pDLLName = "vvis_dll.dll"; CSysModule *pModule = Sys_LoadModule( pDLLName ); @@ -53,6 +54,31 @@ int main(int argc, char* argv[]) printf( "vvis launcher error: can't load %s\n%s", pDLLName, GetLastErrorString() ); return 1; } +#else + // Coming through! + const char *pDLLName = "vvis_dll.dll"; + + // With this, we just load the DLL with our filename. + // This allows for custom DLLs without having to bother with the launcher. + char filename[128]; + Q_FileBase(argv[0], filename, sizeof(filename)); + Q_snprintf(filename, sizeof(filename), "%s_dll.dll", filename); + pDLLName = filename; + + CSysModule *pModule = Sys_LoadModule( pDLLName ); + if ( !pModule ) + { + // Try loading the default then + pDLLName = "vvis_dll.dll"; + pModule = Sys_LoadModule( pDLLName ); + } + + if ( !pModule ) + { + printf( "vvis launcher error: can't load %s\n%s", pDLLName, GetLastErrorString() ); + return 1; + } +#endif CreateInterfaceFn fn = Sys_GetFactory( pModule ); if( !fn ) diff --git a/mp/src/vpc_scripts/groups.vgc b/mp/src/vpc_scripts/groups.vgc index a50216f5..0350d9c0 100644 --- a/mp/src/vpc_scripts/groups.vgc +++ b/mp/src/vpc_scripts/groups.vgc @@ -22,6 +22,7 @@ $Group "game" "server" "tier1" "vgui_controls" + "vscript" } $Group "shaders" @@ -51,6 +52,7 @@ $Group "everything" "vice" "vrad_dll" "vrad_launcher" + "vscript" "vtf2tga" "vtfdiff" "vvis_dll" diff --git a/mp/src/vpc_scripts/projects.vgc b/mp/src/vpc_scripts/projects.vgc index 1eb2ab5c..964683c5 100644 --- a/mp/src/vpc_scripts/projects.vgc +++ b/mp/src/vpc_scripts/projects.vgc @@ -109,6 +109,11 @@ $Project "vrad_launcher" "utils\vrad_launcher\vrad_launcher.vpc" [$WIN32] } +$Project "vscript" +{ + "vscript\vscript.vpc" +} + $Project "vtf2tga" { "utils\vtf2tga\vtf2tga.vpc" [$WIN32] diff --git a/mp/src/vpc_scripts/source_base.vpc b/mp/src/vpc_scripts/source_base.vpc index 17de60eb..ea658a5c 100644 --- a/mp/src/vpc_scripts/source_base.vpc +++ b/mp/src/vpc_scripts/source_base.vpc @@ -15,6 +15,20 @@ // rel/tf_beta branch: //$Conditional TF_BETA "1" +//----------------------------------------------------------------------------- +// Mapbase conditional, equivalent to (and required for) our MAPBASE preprocessor defined below +$Conditional MAPBASE "1" + +// Mapbase multiplayer conditional +$Conditional MAPBASE_MP "1" + +// Toggles Mapbase's RPC implementation +$Conditional MAPBASE_RPC "1" + +// Toggles VScript implementation (note: interfaces still exist, just the provided implementation is not present) +$Conditional MAPBASE_VSCRIPT "1" +//----------------------------------------------------------------------------- + $Configuration "Debug" { $Compiler @@ -27,6 +41,10 @@ $Configuration "Debug" // Need to revisit the code to make things run with the _RETAIL preprocessor definition // This line was added in the previous check-in, but had previously not been defined in this branch // $PreprocessorDefinitions "$BASE;_RETAIL" [$RETAIL] + + // Mapbase base definitions + $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] + $PreprocessorDefinitions "$BASE;MAPBASE_MP" [$MAPBASE_MP] } } @@ -42,5 +60,9 @@ $Configuration "Release" // Need to revisit the code to make things run with the _RETAIL preprocessor definition // This line was added in the previous check-in, but had previously not been defined in this branch // $PreprocessorDefinitions "$BASE;_RETAIL" [$RETAIL] + + // Mapbase base definitions + $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] + $PreprocessorDefinitions "$BASE;MAPBASE_MP" [$MAPBASE_MP] } } diff --git a/mp/src/vpc_scripts/source_dll_win32_base.vpc b/mp/src/vpc_scripts/source_dll_win32_base.vpc index cec539ad..4b2a2852 100644 --- a/mp/src/vpc_scripts/source_dll_win32_base.vpc +++ b/mp/src/vpc_scripts/source_dll_win32_base.vpc @@ -138,6 +138,9 @@ $Project $Implib "$LIBPUBLIC\tier0" $Lib "$LIBPUBLIC\tier1" $Implib "$LIBPUBLIC\vstdlib" + + // Discord integration + $Lib "$LIBPUBLIC\discord-rpc" [$MAPBASE_RPC] } } diff --git a/mp/src/vscript/sqstdtime.h b/mp/src/vscript/sqstdtime.h new file mode 100644 index 00000000..fbf1bee7 --- /dev/null +++ b/mp/src/vscript/sqstdtime.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------- +// see copyright notice in squirrel.h +// +// Purpose : Squirrel time library cropped out from +// the system library as a safe include. +// +//----------------------------------------------------------------------- + +#include "squirrel.h" +#include "time.h" + +static SQInteger _system_clock(HSQUIRRELVM v) +{ + sq_pushfloat(v, ((SQFloat)clock()) / (SQFloat)CLOCKS_PER_SEC); + return 1; +} + +static SQInteger _system_time(HSQUIRRELVM v) +{ + SQInteger t = (SQInteger)time(NULL); + sq_pushinteger(v, t); + return 1; +} + +static void _set_integer_slot(HSQUIRRELVM v, const SQChar *name, SQInteger val) +{ + sq_pushstring(v, name, -1); + sq_pushinteger(v, val); + sq_rawset(v, -3); +} + +static SQInteger _system_date(HSQUIRRELVM v) +{ + time_t t; + SQInteger it; + SQInteger format = 'l'; + if (sq_gettop(v) > 1) { + sq_getinteger(v, 2, &it); + t = it; + if (sq_gettop(v) > 2) { + sq_getinteger(v, 3, (SQInteger*)&format); + } + } + else { + time(&t); + } + tm *date; + if (format == 'u') + date = gmtime(&t); + else + date = localtime(&t); + if (!date) + return sq_throwerror(v, _SC("crt api failure")); + sq_newtable(v); + _set_integer_slot(v, _SC("sec"), date->tm_sec); + _set_integer_slot(v, _SC("min"), date->tm_min); + _set_integer_slot(v, _SC("hour"), date->tm_hour); + _set_integer_slot(v, _SC("day"), date->tm_mday); + _set_integer_slot(v, _SC("month"), date->tm_mon); + _set_integer_slot(v, _SC("year"), date->tm_year + 1900); + _set_integer_slot(v, _SC("wday"), date->tm_wday); + _set_integer_slot(v, _SC("yday"), date->tm_yday); + return 1; +} + +#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask} +static const SQRegFunction timelib_funcs[] = { + _DECL_FUNC(clock, 0, NULL), + _DECL_FUNC(time, 1, NULL), + _DECL_FUNC(date, -1, _SC(".nn")), + { NULL, (SQFUNCTION)0, 0, NULL } +}; +#undef _DECL_FUNC + +SQInteger sqstd_register_timelib(HSQUIRRELVM v) +{ + SQInteger i = 0; + while (timelib_funcs[i].name != 0) + { + sq_pushstring(v, timelib_funcs[i].name, -1); + sq_newclosure(v, timelib_funcs[i].f, 0); + sq_setparamscheck(v, timelib_funcs[i].nparamscheck, timelib_funcs[i].typemask); + sq_setnativeclosurename(v, -1, timelib_funcs[i].name); + sq_newslot(v, -3, SQFalse); + i++; + } + return 1; +} diff --git a/mp/src/vscript/squirrel/.gitignore b/mp/src/vscript/squirrel/.gitignore new file mode 100644 index 00000000..6e97beb7 --- /dev/null +++ b/mp/src/vscript/squirrel/.gitignore @@ -0,0 +1,6 @@ +# Folders created at compilation +bin/ +lib/ + +# Folders created at documentation generation +doc/build/ diff --git a/mp/src/vscript/squirrel/.travis.yml b/mp/src/vscript/squirrel/.travis.yml new file mode 100644 index 00000000..1e31c1d6 --- /dev/null +++ b/mp/src/vscript/squirrel/.travis.yml @@ -0,0 +1,17 @@ +language: cpp +compiler: + - gcc + - clang + +# Travis VMs are 64-bit but we compile both for 32 and 64 bit. To enable the +# 32-bit builds to work, we need gcc-multilib. +addons: + apt: + packages: + - gcc-multilib + - g++-multilib + +# Enable container-based builds. +sudo: false + +script: mkdir build && cd build && cmake .. && make -j2 diff --git a/mp/src/vscript/squirrel/CMakeLists.txt b/mp/src/vscript/squirrel/CMakeLists.txt new file mode 100644 index 00000000..dc35b6f4 --- /dev/null +++ b/mp/src/vscript/squirrel/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.4) +project(squirrel VERSION 3.1 LANGUAGES C CXX) + +option(DISABLE_STATIC "Avoid building/installing static libraries.") +option(LONG_OUTPUT_NAMES "Use longer names for binaries and libraries: squirrel3 (not sq).") + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif () + +include(GNUInstallDirs) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") +set(CMAKE_CXX_STANDARD 11) + +if(CMAKE_COMPILER_IS_GNUCXX) + add_compile_options( + "$<$:-fno-rtti;-fno-exceptions>" + -fno-strict-aliasing + -Wall + -Wextra + -pedantic + -Wcast-qual + "$<$:-O3>" + "$<$:-O3;-g>" + "$<$:-Os>" + "$<$:-pg;-pie;-gstabs;-g3;-Og>" + ) +elseif(MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +add_subdirectory(squirrel) +add_subdirectory(sqstdlib) +add_subdirectory(sq) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(tgts) + if(NOT DISABLE_DYNAMIC) + list(APPEND tgts squirrel sqstdlib sq) + endif() + if(NOT DISABLE_STATIC) + list(APPEND tgts squirrel_static sqstdlib_static sq_static) + endif() + foreach(t ${tgts}) + target_compile_definitions(${t} PUBLIC -D_SQ64) + endforeach() +endif() + +if(NOT DISABLE_DYNAMIC) + set_target_properties(squirrel sqstdlib PROPERTIES SOVERSION 0 VERSION 0.0.0) +endif() + +if(NOT SQ_DISABLE_INSTALLER AND NOT SQ_DISABLE_HEADER_INSTALLER) + install(FILES + include/sqconfig.h + include/squirrel.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT Development + ) + install(FILES + include/sqstdaux.h + include/sqstdblob.h + include/sqstdio.h + include/sqstdmath.h + include/sqstdstring.h + include/sqstdsystem.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT Development + ) +endif() + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config-version.cmake" + VERSION "${squirrel_VERSION}" + COMPATIBILITY AnyNewerVersion + ) + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/squirrel-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" + ) + +export(EXPORT squirrel + NAMESPACE squirrel:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-targets.cmake" + ) + +if(NOT SQ_DISABLE_INSTALLER AND NOT SQ_DISABLE_CMAKE_INSTALLER) + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config-version.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" + COMPONENT Development + ) + + install(EXPORT squirrel + NAMESPACE squirrel:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" + FILE "squirrel-targets.cmake" + COMPONENT Development + ) +endif() diff --git a/mp/src/vscript/squirrel/COMPILE b/mp/src/vscript/squirrel/COMPILE new file mode 100644 index 00000000..375a9209 --- /dev/null +++ b/mp/src/vscript/squirrel/COMPILE @@ -0,0 +1,86 @@ +Squirrel 3.1 stable +-------------------------------------------------------- +What is in this distribution? + +squirrel + static library implementing the compiler and interpreter of the language + +sqstdlib + the standard utility libraries + +sq + stand alone interpreter + +doc + The manual + +etc + a minimalistic embedding sample + +samples + samples programs + + +HOW TO COMPILE +--------------------------------------------------------- +CMAKE USERS +......................................................... +If you want to build the shared libraries under Windows using Visual +Studio, you will have to use CMake version 3.4 or newer. If not, an +earlier version will suffice. For a traditional out-of-source build +under Linux, type something like + + $ mkdir build # Create temporary build directory + $ cd build + $ cmake .. # CMake will determine all the necessary information, + # including the platform (32- vs. 64-bit) + $ make + $ make install + $ cd ..; rm -r build + +The default installation directory will be /usr/local on Unix platforms, +and C:/Program Files/squirrel on Windows. The binaries will go into bin/ +and the libraries into lib/. You can change this behavior by calling CMake like +this: + + $ cmake .. -DCMAKE_INSTALL_PREFIX=/some/path/on/your/system + +With the CMAKE_INSTALL_BINDIR and CMAKE_INSTALL_LIBDIR options, the directories +the binaries & libraries will go in (relative to CMAKE_INSTALL_PREFIX) +can be specified. For instance, + + $ cmake .. -DCMAKE_INSTALL_LIBDIR=lib64 + +will install the libraries into a 'lib64' subdirectory in the top +source directory. The public header files will be installed into the directory +the value of CMAKE_INSTALL_INCLUDEDIR points to. If you want only the +binaries and no headers, just set -DSQ_DISABLE_HEADER_INSTALLER=ON, and no +header files will be installed. + +Under Windows, it is probably easiest to use the CMake GUI interface, +although invoking CMake from the command line as explained above +should work as well. + +GCC USERS +......................................................... +There is a very simple makefile that compiles all libraries and exes +from the root of the project run 'make' + +for 32 bits systems + + $ make + +for 64 bits systems + + $ make sq64 + +VISUAL C++ USERS +......................................................... +Open squirrel.dsw from the root project directory and build(dho!) + +DOCUMENTATION GENERATION +......................................................... +To be able to compile the documentation, make sure that you have Python +installed and the packages sphinx and sphinx_rtd_theme. Browse into doc/ +and use either the Makefile for GCC-based platforms or make.bat for +Windows platforms. diff --git a/mp/src/vscript/squirrel/COPYRIGHT b/mp/src/vscript/squirrel/COPYRIGHT new file mode 100644 index 00000000..17d13ac1 --- /dev/null +++ b/mp/src/vscript/squirrel/COPYRIGHT @@ -0,0 +1,21 @@ +Copyright (c) 2003-2017 Alberto Demichelis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------- +END OF COPYRIGHT diff --git a/mp/src/vscript/squirrel/HISTORY b/mp/src/vscript/squirrel/HISTORY new file mode 100644 index 00000000..f8c29e6b --- /dev/null +++ b/mp/src/vscript/squirrel/HISTORY @@ -0,0 +1,533 @@ +***version 3.2 stable*** +-added sq_tailcall +-added rawcall keyword +-added post call initializer syntax +-added table.keys() and table.values() +-added table.filter() +-additional parameters in array.map() and array.apply() +-additional optional initializer in array.reduce() +-closure.call() is now a "native tailcall" and the invoked function can now be suspended +-fixed sq_newmember and sq_rawnewmember properly pop parameters +-fixed capturing free variable on for loop counter before a break statement +-fixed \u in lexer +-various bugfixes + +***version 3.1.1 stable*** +-sq_gettypetag doesn't set last error(it's treated as SQBool function but keeps a SQRESULT for backward compatibility) +-fixed _set method in userdata delegates +-fixed some warnings + +***version 3.1 stable*** +-added slice range for tolower and toupper +-added startswith() and endswith() in string lib +-added SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS to exclude default mem fuction from compilation +-added sq_getreleasehook +-added thread.wakeupthrow() +-added sq_pushthread +-added \u and \U escape sequence for UTF8,UTF16 or UCS4 characters +-added CMake scripts(thx Fabian Wolff) +-the escape character \x is based on sizeof(SQChar) +-fixed several warnings(thx Markus Oberhumer) +-fixed optimizer bug in compound arith oprators(+=,-= etc...) +-fixed sq_getrefvmcount() (thx Gerrit) +-fixed sq_getrefcount() when no references were added with sq_addref() (thx Gerrit) +-fixed bug in string.tointeger() (thx Domingo) +-fixed weakref comparison in 32bit builds using doubles(thx Domingo) +-fixed compiler bug(thx Peter) +-fixed some error in the documentation(thx Alexander) +-fixed some error reporting in compiler(thx Alexander) +-fixed incorrect optional semicolon after "if block"(thx Alexander) +-fixed crash bug in compiler related to compound arith operators(+=,-= etc...) (thx Jeff1) + +***2015-01-10 *** +***version 3.1 RC 1*** +-added new header sqconfig.h for all optional type declarations(unicode, 64bits etc..) +-added sq_setsharedforeignptr sq_getsharedforeignptr +-added sq_setsharedreleasehook sq_getsharedreleasehook +-added escape() in sqstd string library +-added __LINE__ and __FILE__ (thx mingodad) +-widechar support on gcc builds +-now boolean can be used in constants +-reduced dependencies on C runtime library +-newthread and sq_newthread() no longer reinitialize the root table on friend VMs(thx Lucas Cardellini) +-exceptions in the _inherited metamethod are propagated(thx Lucas Cardellini) +-'in' operator performance improvement(thx unagipai and mingodad) +-fixes crash in compiler when trying to write 'base' +-fixed bug in switch statement when using locals as case values (thx mingodad) +-fixed bug in print()(thx Lucas Cardellini) + +***2013-08-30 *** +***version 3.1 beta 1*** +-added new scoping rule(root attached to closures) +-added closure.setroot() closure.getroot() +-added sq_setclosureroot() and sq_getclosureroot() +-added sq_setvmreleasehook() and sq_getvmreleasehook() +-added documentaion for sq_getbase() +-now string.tointeger() accepts an optional parameter 'base' +-now format accepts zeroes in the format string (thx mingodad) +-fixed bug in sqstd_createfile() (thx mingodad) +-minor buxfixes + +***2012-11-10 *** +***version 3.0.4 stable*** +-sq_deleteslot slot now pops the key in case of failure +-fixed bug when _get metamethod throws null +-fixed a bug in rstrip +-added some error handling +-minor bugfixes + +***2012-06-19 *** +***version 3.1.0 alpha 1*** +-changed in and instanceof operator precendence +-root object in closures +-added closure.setroot closure.getroot +-added sq_setclosureroot and sq_getclosureroot + +***version 3.0.3 stable*** +-improved error messages for _cmp(when a non integer value is returned) (thx Yexo) +-added class.newmember() built in method (thx Nam) +-added class.rawnewmember() built in method (thx Nam) +-added sq_rawnewmember() (thx Nam) +-added sq_getversion() +-added sq_typeof() +-added sq_getclosurename() +-added file.close() in stdlib +-documented closure.getinfos() built-in method +-fixed string iteration doesn't return negative numbers for characters > 127 +-fixed bug in tofloat() when converting a string with scientific notation without a decimal point (thx wr2) +-fixed potential infinite loop in array.sort() when the _cmp function is inconsistent (thx Yexo) +-fixed obscure bug in the compiler(thx yishin) +-fixed some minor bug + +***2011-11-28 *** +***version 3.0.2 stable*** +-added sq_gethash API +-now array.sort() is implemented with heapsort +-now floats in scientific notation also accept numbers with no '.' (eg. 1e+6 or 1e6) +-fixed some warning +-fixed some documentation +-fixed bug in GC + +***2011-09-08 *** +***version 3.0.1 stable*** +-added # as alternative symbol for "line comment"(mostly useful for shell scripts) +-added sq_throwobject() to throw an arbitrary object from the C API +-added alignement flag for userdata types, SQ_ALIGNMENT (thx Shigemasa) +-added rawset() and rawget() to class and instance default delegate +-changed bytecode format now ensures matching integer size and float size +-now inherited classes also inherit userdatasize +-added SQUIRREL_VERSION_NUMBER in squirrel.h and _versionnumber_ global symbol +-fixed sq_getmemberhandle +-fixed sq_getrefcount +-refactored some sqstdio code +-refactored some clone code +-refactored some stuff in the string lib +-added -s and -fno-exceptions in GCC makefile(better performance when using GCC) + +***2011-03-13 *** +***version 3.0 stable*** +-added sq_getcallee() +-sq_getfreevariable() also works for native closures +-minior optimizations +-removed several warning when compiling with GCC 4.x +-fixed some errors in the documentation +-fixed bug when using SQUSEDOUBLE and 32bits intengers +-fixed bug when invoking generators with closure.call() (thx huntercool) + +***2010-12-19 *** +***version 3.0 release candidate 1(RC 1)*** +-improved metamethods error handling +-added parameter 'isstatic' to _newmember metamethod(thx G.Meyer) +-added sq_getrefcount() to return number of refences from C++(thx G.Meyer) + +***2010-11-07 *** +***version 3.0 beta 3*** +-license changed to "MIT license" +-added sq_resurrectunreachable() and resurrectunreachable() +-added callee() built in function, returns the current running closure +-added thread.getstackinfos() +-added sq_objtouserpointer() +-added sq_newtableex() +-various refactoring and optimizations +-fixed several 64bits issues regarding integer to string conversions +-fixed some bugs when SQUSEDOUBLE is used in 32bits systems + +***2010-08-18 *** +***version 3.0 beta 2.1*** +-fixed bug in class constructor +-fixed bug in compound arith + +***2010-08-12 *** +***version 3.0 beta 2*** +-class methods can be added or replaced after the class as been instantiated +-JSON compliant table syntax, this is currently an experimental feature (thx atai) +-sq_getsize() now returns userdatasize for classes and instances +-now setroottable() and setconsttable() return the previous value of the respective table +-fixed bug in compound arith operators when used on a free variable (thx ellon) +-fixed some x64 minor bugs +-fixed minor bug in the compiler +-refactored some VM internals +-documented sq_getmemberhandle, sq_getbyhandle, sq_setbyhandle to set and get value from classes + +***2009-11-15 *** +***version 3.0 beta 1*** +-various refactoring and optimizations +-fixed bug in free variables (thx mokehehe) +-fixed bug in functions with default parameters (thx ara & Yexo) +-fixed bug in exception handling +-improved error propagation in _set and _get metamethods ( and 'throw null' for clean failure) +-added sq_getmemberhandle, sq_getbyhandle, sq_setbyhandle to set and get value from classes + +***2009-06-30 *** +***version 3.0 alpha 2*** +-added real free variables(thx Paul Ruizendaal) +-added refactored function call implementation and compiler(thx Paul Ruizendaal) +-added sq_getfunctioninfo +-added compile time flag SQUSEDOUBLE to use double precision floats +-added global slot _floatsize_ int the base lib to recognize single precision and double precision builds +-sq_wakeupvm can now resume the vm with an exception +-added sqstd_format +-now blobs can be cloned +-generators can now be instantiated by calling sq_call() or closure.call() +-fixed debughook bug +-fixed cooroutine error propagation + +***2008-07-23 *** +***version 3.0 alpha 1*** +-first branch from 2.x source tree +-added 'base' keyword +-removed 'delegate' keyword +-now compiled scripts are vararg functions +-added setdelegate() and getdelegate() table builtin methods +-added <=> 3 ways compare operator +-added lambda expression @(a,b) a + b +-added local function statement +-added array built-in map(),reduce(),apply(),filter() and find() +-generators hold only a weak reference of the enviroment object +-removed 'vargv' and 'vargc' keywords +-now var args are passed as an array called vargv(as a paramter) +-removed 'parent' keyword +-added class getbase() built in method +-instanceof doesn't throw an exception if the left expression is not a class +-lexical scoping for free variables(free variables are no longer in the second parameter list) +-sq_setprintfunc accept error func +-sq_geterrorfunc() +-added sq_arrayremove() and sq_arrayinsert() +-error() built in function(works like print but prints using the errorfunc) +-added native debug hook + +***2008-02-17 *** +***version 2.2 stable*** +-added _newslot metamethod in classes +-added enums added constants +-added sq_pushconsttable, sq_setconsttable +-added default param +-added octal literals(thx Dinosaur) +-fixed debug hook, 'calls' and 'returns' are properly notified in the same number. +-fixed a coroutine bug + +***2007-07-29 *** +***version 2.1.2 stable*** +-new behaviour for generators iteration using foreach +now when a generator is iterated by foreach the value returned by a 'return val' statement +will terminate the iteration but will not be returned as foreach iteration +-added sq_setclassudsize() +-added sq_clear() +-added table.clear(), array.clear() +-fixed sq_cmp() (thx jyuill) +-fixed minor bugs + +***2006-08-21 *** +***version 2.1.1 stable*** +-vm refactoring +-optimized internal function memory layout +-new global symbol _version_ (is the version string) +-code size optimization for float literals(on 32bits float builts) +-now the raw ref API(sq_addref etc...) is fully reentrant. +-fixed a bug in sq_getdelegate() now pushes null if the object doesn't have a delegate(thx MatzeB) +-improved C reference performances in NO_GARBAGE_COLLECTOR builds +-sq_getlocal() now enumerates also outer values. +-fixed regexp library for GCC users. + +***2006-03-19 *** +***version 2.1 stable*** +-added static class fields, new keyword static +-added 64bits architecture support +-added global slot _intsize_ int the base lib to recognize 32bits and 64bits builds +-added functions with fixed environment, closure.bindenv() built-in function +-all types except userdata and null implement the tostring() method +-string concatenation now invokes metamethod _tostring +-new metamethods for class objects _newmember and _inherited +-sq_call() sq_resume() sq_wakeupvm() have a new signature +-new C referencing implementation(scales more with the amount of references) +-refactored hash table +-new api functions sq_newslot(),sq_tobool(),sq_getbase(), sq_instanceof(), sq_bindenv() +-the api func sq_createslot was deprecated but still supported in form of C macro on top of sq_newslot +-sq_setreleasehook() now also works for classes +-stream.readstr() and stream.writestr() have been deprecated(this affects file and blob) +-fixed squirrel.h undeclared api calls +-fixed few minor bugs +-SQChar is now defined as wchar_t +-removed warning when building with -Wall -pedantic for GCC users +-added new std io function writeclosuretofile() +-added new std string functions strip(),rstrip(),lstrip() and split() +-regular expressions operators (+,*) now have more POSIX greedyness behaviour +-class constructors are now invoked as normal functions + +***2005-10-02 *** +***version 2.0.5 stable*** +-fixed some 64bits incompatibilities (thx sarge) +-fixed minor bug in the stdlib format() function (thx Rick) +-fixed a bug in dofile() that was preventing to compile empty files +-added new API sq_poptop() & sq_getfreevariable() +-some performance improvements + +***2005-08-14 *** +***version 2.0.4 stable*** +-weak references and related API calls +-added sq_objtobool() +-class instances memory policies improved(1 mem allocation for the whole instance) +-typetags are now declared as SQUserPointer instead of unsigned int +-first pass for 64bits compatibility +-fixed minor bug in the stdio stream +-fixed a bug in format() +-fixed bug in string.tointeger() and string.tofloat() + +***2005-06-24 *** +***version 2.0.3 stable*** +-dofile() and loadfile() in the iolib now can decode ASCII, UTF8 files UCS2 big-endian and little-endian +-sq_setparamscheck() : now typemesk can check for null +-added string escape sequence \xhhhh +-fixed some C++ standard incompatibilities + +***2005-05-15 *** +***version 2.0.2 stable*** +-performances improvements (expecially for GCC users) +-removed all dependencies from C++ exception handling +-various bugfixes + +***2005-04-12 *** +***version 2.0.1 stable*** +-various bugfixes +-sq_setparamscheck() now allows spaces in the typemask + +***2005-04-03 *** +***version 2.0 stable*** +-added API sq_gettypetag() +-added built-in function to the bool type(tointeger, tostring etc...) + +***2005-02-27 *** +***version 2.0 release candidate 1(RC 1)*** +-added API sq_reseterror() +-modified sq_release() +-now class instances can be cloned +-various bufixes + +***2005-01-26 *** +***version 2.0 beta 1*** +-added bool type +-class properties can be redefined in a derived class +-added ops *= /= and %= +-new syntax for class attributes declaration instead of ( and ) +-increased the max number of literals per function from 65535 to 16777215 +-now free variables have proper lexical scoping +-added API sq_createinstance(), sq_pushbool(), sq_getbool() +-added built-in function type() +-added built-in function obj.rawin(key) in table,class and instance +-sq_rawget() and sq_rawset() now work also on classes and instances +-the VM no longer uses C++ exception handling (more suitable for embedded devices) +-various bufixes + +***2004-12-21 *** +***version 2.0 alpha 2*** +-globals scoping changed, now if :: is omitted the VM automatically falls back on the root table +-various bufixes +-added class level attributes + +***2004-12-12 *** +***version 2.0 alpha 1*** +-codebase branch from version 1.x +-added classes +-added functions with variable number of parameters(vargc & vargv and the ...) +-0 and 0.0 are now considered 'false' by all conditional statements(if,while,for,?,do-while) +-added new api functions sq_newclass() sq_setinstanceup() sq_getinstanceup() sq_getattributes() sq_setattributes() +-modified api sq_settypetag() + +***2004-11-01 *** +***version 1.0 stable*** +-fixed some minor bug +-improved operator 'delete' performances +-added scientific notation for float numbers( eg. 2.e16 or 2.e-2) + +***2004-08-30 *** +***version 1.0 release candidate 2(RC 2)*** +-fixed bug in the vm(thx Pierre Renaux) +-fixed bug in the optimizer(thx Pierre Renaux) +-fixed some bug in the documentation(thx JD) +-added new api functions for raw object handling +-removed nested multiline comments +-reduced memory footprint in C references + +***2004-08-23 *** +***version 1.0 release candidate 1(RC 1)*** +-fixed division by zero +-the 'in' operator and obj.rawget() do not query the default delegate anymore +-added function sq_getprintfunc() +-added new standard library 'auxlib'(implements default error handlers) + +***2004-07-12 *** +***version 1.0 beta 4*** +-fixed a bug in the integer.tochar() built-in method +-fixed unary minus operator +-fixed bug in dofile() +-fixed inconsistency between != and == operators(on float/integer comparison) +-added javascript style unsigned right shift operator '>>>' +-added array(size) constructor built-in function +-array.resize(size,[fill]) built-in function accepts an optional 'fill' value +-improved debug API, added sq_getclosureinfo() and sq_setnativeclosurename() + +***2004-05-23 *** +***version 1.0 beta 3*** +-minor vm bug fixes +-string allocation is now faster +-tables and array memory usage is now less conservative(they shrink) +-added regular expression routines in the standard library +-The 'c' expression now accepts only 1 character(thx irbrian) +-multiline strings <[ ]> have been substituted with C# style verbatim strings (eg. @"string") +-added new keyword 'parent' for accessing the delegate of tables and unserdata +-The metamethod '_clone' has been renamed '_cloned' +-the _delslot metamethod's behaviour and prototype have been changed +-new default function in the integer and float object 'tochar()' +-the built-in function chcode2string has been removed +-the default method [table].getdelegate() has been removed +-new api sq_rawdeleteslot() +-new table built-in method rawdelete(key) +-the dynamic mudule loading has been removed from the standard distribution +-some optimizations in the VM + +***2004-04-21 *** +***version 1.0 beta 2*** +-minor compiler/parser bug fixes +-sq_newclosure has a different prototype, the "paramscheck" of paramter has been moved to the new function sq_setparamscheck() +-sq_setparamscheck allows to add automatic parameters type checking in native closures +-sq_compile() lost the lineinfo parameter +-new api sq_enabledebuginfo() globally sets compiler's debug info generation +-added consistency check on bytecode serialization +-fixed += operator, now works on strings like + +-added global slot in the base lib _charsize_ to recognize unicode builds from ascii builds runtime +-added registry table +-new api call sq_pushregistrytable() +-added type tag to the userdata type sq_settypetag() +-sq_getuserdata now queries the userdata typetag +-the built in function collect_garbage() as been renamed collectgarbage() for consistency reasons +-new standard libraries(sqlibs are now obsolete) + +***2004-02-20 *** +***version 1.0 beta 1*** +-fixed a bug in the compiler (thanks Martin Kofler) +-fixed bug in the switch case statement +-fixed the _unm metamethod +-fixed minor bugs in the API +-fixed automatic stack resizing +-first beta version + first pass code clean up in the VM and base lib + first pass code coverege test has been done on VM and built-in lib +-new VM creation API sq_open() sq_close() (sq_newvm and sq_releasevm are now obsolete) +-new api allows to specifiy a "print" function to output text(sq_printfunc) +-added some small optimizations +-new cooperative multi-threading capabilities in the base library(coroutines), VMs are now a built in type("thread") +-new built in functions have been added for manipulating the new "thread" type +-friend virtual machines share the same root table, error handler and debug hook by default +-new compile time options + +***2004-01-19 *** +***version 0.9 alpha*** +-fixed a garbage collection bug +-fixed some API bugs(thanks to Joshua Jensen) +-fixed tail calls (in the version 0.8 the tail call optimization was erroneously disabled) +-new function parameters semantic, now passing a wrong number of parameters generates an exception +-native closures have now a built in parameter number checking +-sq_rawget and sq_rawset now work also on arrays +-sq_getsize now woks also on userdata +-the userdata release hook prototype is changed(now passes the size of the userdata) +-the lexer reader function now returns an integer instead of a char that allows better error checking on the input(thx Joshua Jensen) +-faster compiler +-try/catch blocks do not cause any runtime memory allocation anymore + +***2003-12-06 *** +***version 0.8 alpha*** +-fixed a bug that was preventing to have callable userdata throught the metamethod _call +-fixed a garbage collection bug +-fixed == operator now can compare correctly different types +-new built in method getstackinfos(level) +-improved line informations precision for the debug hook +-new api call sq_compilebuffer() +-new built-in api function compilestring() +-new syntactic sugar for function declarations inside tables +-the debug API has been finalized + +***2003-11-17 *** +***version 0.7 alpha*** +-fixed critical bug SQInteger the tail call system +-fixed bug in the continue statement code generation +-fixed func call param issue(thanks to Rewoonenco Andrew) +-added _delslot metamethod(thanks to Rewoonenco Andrew) +-new multiline string expression ( delimited by <[ and ]> ) +-normal strings ("") do not allow embedded new line anymore +-reduced vm memory footprint(C refs are shared between friend VMs) +-new api method sq_deleteslot() +-new debug hook event 'r' is triggered when a function returns + +***2003-11-04 *** +***version 0.6 alpha*** +-fixed switch statement(was executing the default case after a break) +-sq_call() doesn't pop the closure (just the params) +-the vm execution can be suspended from the C API anytime (micro-threads) +-new api calls sq_suspendvm() sq_wakeupvm() sq_getvmstate() and sq_reservestack() + +***2003-10-13 *** +***version 0.5 alpha*** +-fixed some minor bug +-tested with non ASCII identifiers in unicode mode(I've tried chinese chars) +-added built-in function string.find() +-the built-in function array.sort() optionally accepts a cmp(a,b) function +-the debug hook function now has a new prototype debug_hook(event_type,sourcefile,line,functionname) +-fixed some debug info imprecision + +***2003-10-01 *** +***version 0.4 alpha*** +-faster VM +-sq_call will pop arguments and closure also in case of failure +-fixed a bug in sq_remove +-now the VM detects delegation cycles(and throws an exception) +-new operators ++ and -- +-new operator ',' comma operator +-fixed some expression precedence issue +-fixed bug in sq_arraypop + +***2003-09-15 *** +***version 0.3 alpha*** +-fixed a bug in array::insert() +-optional Unicode core(define SQUNICODE or _UNICODE on Win32) +-sq_compiler uses a new reader function SQLEXREADFUNC +-the debug hook passes 'l' instead of 'line' for line callbacks + and 'c' instead of 'call' for call callbacks +-new array.extend() bulit-in function +-new API sq_clone() + +***2003-09-10 *** +***version 0.2 pre-alpha*** +-new completely reentrant VM (sq_open and sq_close are now obsolete) +-sq_newvm() has a new prototype +-allocators are now global and linked in the VM +-_newslot meta method added +-rawset creates a slot if doesn't exists +-the compiler error callback pass the vm handle(thanks Pierre Renaux) +-sq_setforeignptr() sq_getforeingptr() are now public +-sq_resume() now is possible to resume generators from C +-sq_getlasterror() retrieve the last thrown error +-improved docs + +***2003-09-06 *** +***version 0.1 pre-alpha*** +first release diff --git a/mp/src/vscript/squirrel/Makefile b/mp/src/vscript/squirrel/Makefile new file mode 100644 index 00000000..2ed97e26 --- /dev/null +++ b/mp/src/vscript/squirrel/Makefile @@ -0,0 +1,22 @@ + +SQUIRREL=. +MAKE=make + +sq32: folders + cd squirrel; $(MAKE) + cd sqstdlib; $(MAKE) + cd sq; $(MAKE) + +sqprof: folders + cd squirrel; $(MAKE) sqprof + cd sqstdlib; $(MAKE) sqprof + cd sq; $(MAKE) sqprof + +sq64: folders + cd squirrel; $(MAKE) sq64 + cd sqstdlib; $(MAKE) sq64 + cd sq; $(MAKE) sq64 + +folders: + mkdir -p lib + mkdir -p bin diff --git a/mp/src/vscript/squirrel/README b/mp/src/vscript/squirrel/README new file mode 100644 index 00000000..298aec75 --- /dev/null +++ b/mp/src/vscript/squirrel/README @@ -0,0 +1,33 @@ +The programming language SQUIRREL 3.1 stable + +-------------------------------------------------- +This project has successfully been compiled and run on + * Windows (x86 and amd64) + * Linux (x86, amd64 and ARM) + * Illumos (x86 and amd64) + * FreeBSD (x86 and ARM) + +The following compilers have been confirmed to be working: + MS Visual C++ 6.0 (all on x86 and amd64) + 7.0 | + 7.1 v + 8.0 + 9.0 + 10.0 + 12.0 --- + MinGW gcc 3.2 (mingw special 20020817-1) + Cygnus gcc 3.2 + Linux gcc 3.2.3 + 4.0.0 (x86 and amd64) + 5.3.1 (amd64) + Illumos gcc 4.0.0 (x86 and amd64) + ARM Linux gcc 4.6.3 (Raspberry Pi Model B) + + +Feedback and suggestions are appreciated +project page - http://www.squirrel-lang.org +community forums - http://forum.squirrel-lang.org +wiki - http://wiki.squirrel-lang.org +author - alberto@demichelis.net + +END OF README diff --git a/mp/src/vscript/squirrel/appveyor.yml b/mp/src/vscript/squirrel/appveyor.yml new file mode 100644 index 00000000..4da9b37b --- /dev/null +++ b/mp/src/vscript/squirrel/appveyor.yml @@ -0,0 +1,28 @@ +version: 0.0.{build} + +platform: + - x86 + - x64 + +configuration: + - Debug + - Release + +clone_folder: c:\sq + +before_build: + - mkdir build + - cd build + - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %platform% + - echo %platform% + - if %platform%==X64 (cmake .. -G "Visual Studio 14 2015 Win64") + - if %platform%==x86 (cmake .. -G "Visual Studio 14 2015") + +build_script: + - cmake --build . --config %configuration% -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + +artifacts: + - path: build\*\%configuration%\*.exe + - path: build\*\%configuration%\*.dll + +test: off diff --git a/mp/src/vscript/squirrel/doc/Makefile b/mp/src/vscript/squirrel/doc/Makefile new file mode 100644 index 00000000..b01e98db --- /dev/null +++ b/mp/src/vscript/squirrel/doc/Makefile @@ -0,0 +1,216 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/testy_sphinxy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/testy_sphinxy.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/testy_sphinxy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/testy_sphinxy" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/mp/src/vscript/squirrel/doc/make.bat b/mp/src/vscript/squirrel/doc/make.bat new file mode 100644 index 00000000..a32fa10a --- /dev/null +++ b/mp/src/vscript/squirrel/doc/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\testy_sphinxy.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\testy_sphinxy.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/mp/src/vscript/squirrel/doc/source/_static/nut.ico b/mp/src/vscript/squirrel/doc/source/_static/nut.ico new file mode 100644 index 0000000000000000000000000000000000000000..c28977b0209b7060618d0c6bfb7b25058fc6479e GIT binary patch literal 318 zcmZQzU<5(|0RbS%!l1#(z#zuJz@P!d0zj+)#2|4P5awoJ0AY|E3`9&j#b7t-7Q@CX zpBaQL6Btx{N?}q2=`;+Gz`y_&V^R`i12LGC6rr>-h)z&aQWij^l|XzJ7Z;aMpaK@x z&`=Pa;1U`d08$4O2GMCiPXL91AOTE5iU0qL828OO3_yJh3;@hmN5B98 literal 0 HcmV?d00001 diff --git a/mp/src/vscript/squirrel/doc/source/_static/simple_nut.png b/mp/src/vscript/squirrel/doc/source/_static/simple_nut.png new file mode 100644 index 0000000000000000000000000000000000000000..4c0e8a20f4a17c3cee8d723f9749f9977e265d69 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPNvoyDk_?r1rPZ$^&#XMacLn>~)oxPX$kby|c=KwJW zLDnm08eE(m&k9`G(xLexf{87GX_|vmPJ=+shZS$xa{jEYxxTbRsCHMmrFG|utjp=V zv*vL#Fud4#D@I&QP`NfVv;b&VKt9_IS|(>t!om7u$b5^ZtDK`mIdL3=CBcGPd*9YgMs;*bVnj2ksWX z!`HpS{8yg(v5z^$pQ_$H2I^EeU)Qku*y*)18K*1r0r_CCV1isi`THB$%>j4i*?}UZ zYgpG!++`1vKXrzo`aC~K&{Jh&-K?U&->#RwZDeL(XxL=T01^g)Q`gx!U%ACSFNshB OaXnrAT-G@yGywq8bBb#K literal 0 HcmV?d00001 diff --git a/mp/src/vscript/squirrel/doc/source/conf.py b/mp/src/vscript/squirrel/doc/source/conf.py new file mode 100644 index 00000000..996eafef --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/conf.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# +# Squirrel documentation build configuration file, created by +# sphinx-quickstart on Sun Jan 31 00:26:52 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import time + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Squirrel documentation' +copyright = '2003-%s, Alberto Demichelis' % time.strftime('%Y') +author = u'Alberto Demichelis' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'3.1' +# The full version, including alpha/beta/rc tags. +release = u'3.1 stable' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'simple_nut.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'nut.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'squirrel_doc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +_stdauthor = r'Alberto Demichelis' +latex_documents = [ + ('reference/index', 'reference.tex', + 'Squirrel Reference Manual', _stdauthor, 'manual'), + ('stdlib/index', 'stdlib.tex', + 'Squirrel Standard Library', _stdauthor, 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'Squirrel', u'Squirrel Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Squirrel', u'Squirrel Documentation', + author, 'Squirrel', 'The Programming Language.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/mp/src/vscript/squirrel/doc/source/index.rst b/mp/src/vscript/squirrel/doc/source/index.rst new file mode 100644 index 00000000..0cc1bb4d --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/index.rst @@ -0,0 +1,24 @@ +.. Squirrel documentation master file, created by + sphinx-quickstart on Sun Jan 31 00:26:52 2016. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Squirrel's documentation +========================================= + +Contents: + +.. toctree:: + :maxdepth: 1 + + reference/index.rst + stdlib/index.rst + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + + diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst b/mp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst new file mode 100644 index 00000000..0e1f586a --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst @@ -0,0 +1,32 @@ +.. _api_ref_bytecode_serialization: + +====================== +Bytecode serialization +====================== + +.. _sq_readclosure: + +.. c:function:: SQRESULT sq_readclosure(HSQUIRRELVM v, SQREADFUNC readf, SQUserPointer up) + + :param HSQUIRRELVM v: the target VM + :param SQREADFUNC readf: pointer to a read function that will be invoked by the vm during the serialization. + :param SQUserPointer up: pointer that will be passed to each call to the read function + :returns: a SQRESULT + +serialize (read) a closure and pushes it on top of the stack, the source is user defined through a read callback. + + + + + +.. _sq_writeclosure: + +.. c:function:: SQRESULT sq_writeclosure(HSQUIRRELVM v, SQWRITEFUNC writef, SQUserPointer up) + + :param HSQUIRRELVM v: the target VM + :param SQWRITEFUNC writef: pointer to a write function that will be invoked by the vm during the serialization. + :param SQUserPointer up: pointer that will be passed to each call to the write function + :returns: a SQRESULT + :remarks: closures with free variables cannot be serialized + +serializes(writes) the closure on top of the stack, the destination is user defined through a write callback. diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/calls.rst b/mp/src/vscript/squirrel/doc/source/reference/api/calls.rst new file mode 100644 index 00000000..7ba43fb6 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/calls.rst @@ -0,0 +1,130 @@ +.. _api_ref_calls: + +===== +Calls +===== + +.. _sq_call: + +.. c:function:: SQRESULT sq_call(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param SQInteger params: number of parameters of the function + :param SQBool retval: if true the function will push the return value in the stack + :param SQBool raiseerror: if true, if a runtime error occurs during the execution of the call, the vm will invoke the error handler. + :returns: a SQRESULT + +calls a closure or a native closure. The function pops all the parameters and leave the closure in the stack; if retval is true the return value of the closure is pushed. If the execution of the function is suspended through sq_suspendvm(), the closure and the arguments will not be automatically popped from the stack. + +When using to create an instance, push a dummy parameter to be filled with the newly-created instance for the constructor's 'this' parameter. + + + +.. _sq_getcallee: + +.. c:function:: SQRESULT sq_getcallee(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: a SQRESULT + +push in the stack the currently running closure. + + + + + +.. _sq_getlasterror: + +.. c:function:: SQRESULT sq_getlasterror(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: a SQRESULT + :remarks: the pushed error descriptor can be any valid squirrel type. + +pushes the last error in the stack. + + + + + +.. _sq_getlocal: + +.. c:function:: const SQChar * sq_getlocal(HSQUIRRELVM v, SQUnsignedInteger level, SQUnsignedInteger nseq) + + :param HSQUIRRELVM v: the target VM + :param SQUnsignedInteger level: the function index in the calls stack, 0 is the current function + :param SQUnsignedInteger nseq: the index of the local variable in the stack frame (0 is 'this') + :returns: the name of the local variable if a variable exists at the given level/seq otherwise NULL. + +Returns the name of a local variable given stackframe and sequence in the stack and pushes is current value. Free variables are treated as local variables, by sq_getlocal(), and will be returned as they would be at the base of the stack, just before the real local variables. + + + + + +.. _sq_reseterror: + +.. c:function:: void sq_reseterror(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + +reset the last error in the virtual machine to null + + + + + +.. _sq_resume: + +.. c:function:: SQRESULT sq_resume(HSQUIRRELVM v, SQBool retval, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param SQBool retval: if true the function will push the return value in the stack + :param SQBool raiseerror: if true, if a runtime error occurs during the execution of the call, the vm will invoke the error handler. + :returns: a SQRESULT + :remarks: if retval != 0 the return value of the generator is pushed. + +resumes the generator at the top position of the stack. + + +.. _sq_tailcall: + +.. c:function:: SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) + + :param HSQUIRRELVM v: the target VM + :param SQInteger params: number of parameters of the function + + Calls a closure and removes the caller function from the call stack. + This function must be invoke from a native closure and + he return value of sq_tailcall must be returned by the caller function(see example). + +*.eg* + +:: + + SQInteger tailcall_something_example(HSQUIRRELVM v) + { + //push closure and parameters here + ... + return sq_tailcall(v,2); + } + +.. _sq_throwerror: + +.. c:function:: SQRESULT sq_throwerror(HSQUIRRELVM v, const SQChar * err) + + :param HSQUIRRELVM v: the target VM + :param const SQChar * err: the description of the error that has to be thrown + :returns: the value that has to be returned by a native closure in order to throw an exception in the virtual machine. + +sets the last error in the virtual machine and returns the value that has to be returned by a native closure in order to trigger an exception in the virtual machine. + + +.. _sq_throwobject: + +.. c:function:: SQRESULT sq_throwobject(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: the value that has to be returned by a native closure in order to throw an exception in the virtual machine. + +pops a value from the stack sets it as the last error in the virtual machine. Returns the value that has to be returned by a native closure in order to trigger an exception in the virtual machine (aka SQ_ERROR). diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/compiler.rst b/mp/src/vscript/squirrel/doc/source/reference/api/compiler.rst new file mode 100644 index 00000000..7fcb933d --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/compiler.rst @@ -0,0 +1,79 @@ +.. _api_ref_compiler: + +======== +Compiler +======== + +.. _sq_compile: + +.. c:function:: SQRESULT sq_compile(HSQUIRRELVM v, HSQLEXREADFUNC read, SQUserPointer p, const SQChar * sourcename, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param HSQLEXREADFUNC read: a pointer to a read function that will feed the compiler with the program. + :param SQUserPointer p: a user defined pointer that will be passed by the compiler to the read function at each invocation. + :param const SQChar * sourcename: the symbolic name of the program (used only for more meaningful runtime errors) + :param SQBool raiseerror: if this value is true the compiler error handler will be called in case of an error + :returns: a SQRESULT. If the sq_compile fails nothing is pushed in the stack. + :remarks: in case of an error the function will call the function set by sq_setcompilererrorhandler(). + +compiles a squirrel program; if it succeeds, push the compiled script as function in the stack. + + + + + +.. _sq_compilebuffer: + +.. c:function:: SQRESULT sq_compilebuffer(HSQUIRRELVM v, const SQChar* s, SQInteger size, const SQChar * sourcename, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param const SQChar* s: a pointer to the buffer that has to be compiled. + :param SQInteger size: size in characters of the buffer passed in the parameter 's'. + :param const SQChar * sourcename: the symbolic name of the program (used only for more meaningful runtime errors) + :param SQBool raiseerror: if this value true the compiler error handler will be called in case of an error + :returns: a SQRESULT. If the sq_compilebuffer fails nothing is pushed in the stack. + :remarks: in case of an error the function will call the function set by sq_setcompilererrorhandler(). + +compiles a squirrel program from a memory buffer; if it succeeds, push the compiled script as function in the stack. + + + + + +.. _sq_enabledebuginfo: + +.. c:function:: void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable) + + :param HSQUIRRELVM v: the target VM + :param SQBool enable: if true enables the debug info generation, if == 0 disables it. + :remarks: The function affects all threads as well. + +enable/disable the debug line information generation at compile time. + + + + + +.. _sq_notifyallexceptions: + +.. c:function:: void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) + + :param HSQUIRRELVM v: the target VM + :param SQBool enable: if true enables the error callback notification of handled exceptions. + :remarks: By default the VM will invoke the error callback only if an exception is not handled (no try/catch traps are present in the call stack). If notifyallexceptions is enabled, the VM will call the error callback for any exception even if between try/catch blocks. This feature is useful for implementing debuggers. + +enable/disable the error callback notification of handled exceptions. + + + + + +.. _sq_setcompilererrorhandler: + +.. c:function:: void sq_setcompilererrorhandler(HSQUIRRELVM v, SQCOMPILERERROR f) + + :param HSQUIRRELVM v: the target VM + :param SQCOMPILERERROR f: A pointer to the error handler function + :remarks: if the parameter f is NULL no function will be called when a compiler error occurs. The compiler error handler is shared between friend VMs. + +sets the compiler error handler function diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst b/mp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst new file mode 100644 index 00000000..ac929e14 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst @@ -0,0 +1,72 @@ +.. _api_ref_debug_interface: + +=============== +Debug interface +=============== + +.. _sq_getfunctioninfo: + +.. c:function:: SQRESULT sq_getfunctioninfo(HSQUIRRELVM v, SQInteger level, SQFunctionInfo * fi) + + :param HSQUIRRELVM v: the target VM + :param SQInteger level: calls stack level + :param SQFunctionInfo * fi: pointer to the SQFunctionInfo structure that will store the closure informations + :returns: a SQRESULT. + :remarks: the member 'funcid' of the returned SQFunctionInfo structure is a unique identifier of the function; this can be useful to identify a specific piece of squirrel code in an application like for instance a profiler. this method will fail if the closure in the stack is a native C closure. + + + +*.eg* + +:: + + + typedef struct tagSQFunctionInfo { + SQUserPointer funcid; //unique idetifier for a function (all it's closures will share the same funcid) + const SQChar *name; //function name + const SQChar *source; //function source file name + }SQFunctionInfo; + + + + + + + +.. _sq_setdebughook: + +.. c:function:: void sq_setdebughook(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :remarks: In order to receive a 'per line' callback, is necessary to compile the scripts with the line informations. Without line informations activated, only the 'call/return' callbacks will be invoked. + +pops a closure from the stack an sets it as debug hook. When a debug hook is set it overrides any previously set native or non native hooks. if the hook is null the debug hook will be disabled. + + + + + +.. _sq_setnativedebughook: + +.. c:function:: void sq_setnativedebughook(HSQUIRRELVM v, SQDEBUGHOOK hook) + + :param HSQUIRRELVM v: the target VM + :param SQDEBUGHOOK hook: the native hook function + :remarks: In order to receive a 'per line' callback, is necessary to compile the scripts with the line informations. Without line informations activated, only the 'call/return' callbacks will be invoked. + +sets the native debug hook. When a native hook is set it overrides any previously set native or non native hooks. if the hook is NULL the debug hook will be disabled. + + + + + +.. _sq_stackinfos: + +.. c:function:: SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos * si) + + :param HSQUIRRELVM v: the target VM + :param SQInteger level: calls stack level + :param SQStackInfos * si: pointer to the SQStackInfos structure that will store the stack informations + :returns: a SQRESULT. + +retrieve the calls stack informations of a ceratain level in the calls stack. diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst b/mp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst new file mode 100644 index 00000000..cfe03e68 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst @@ -0,0 +1,27 @@ +.. _api_ref_garbage_collector: + +================= +Garbage Collector +================= + +.. _sq_collectgarbage: + +.. c:function:: SQInteger sq_collectgarbage(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :remarks: this api only works with garbage collector builds (NO_GARBAGE_COLLECTOR is not defined) + +runs the garbage collector and returns the number of reference cycles found (and deleted) + + + + + +.. _sq_resurrectunreachable: + +.. c:function:: SQRESULT sq_resurrectunreachable(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :remarks: this api only works with garbage collector builds (NO_GARBAGE_COLLECTOR is not defined) + +runs the garbage collector and pushes an array in the stack containing all unreachable object found. If no unreachable object is found, null is pushed instead. This function is meant to help debug reference cycles. diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst b/mp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst new file mode 100644 index 00000000..5345c05c --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst @@ -0,0 +1,695 @@ +.. _api_ref_object_creation_and_handling: + +============================ +Object creation and handling +============================ + +.. _sq_bindenv: + +.. c:function:: SQRESULT sq_bindenv(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: a SQRESULT + :remarks: the cloned closure holds the environment object as weak reference + +pops an object from the stack (must be a table, instance, or class); clones the closure at position idx in the stack and sets the popped object as environment of the cloned closure. Then pushes the new cloned closure on top of the stack. + + + + + +.. _sq_createinstance: + +.. c:function:: SQRESULT sq_createinstance(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class + :returns: a SQRESULT + :remarks: the function doesn't invoke the instance contructor. To create an instance and automatically invoke its contructor, sq_call must be used instead. + +creates an instance of the class at 'idx' position in the stack. The new class instance is pushed on top of the stack. + + + + + +.. _sq_getbool: + +.. c:function:: SQRESULT sq_getbool(HSQUIRRELVM v, SQInteger idx, SQBool * b) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQBool * b: A pointer to the bool that will store the value + :returns: a SQRESULT + +gets the value of the bool at the idx position in the stack. + + + + + +.. _sq_getbyhandle: + +.. c:function:: SQRESULT sq_getbyhandle(HSQUIRRELVM v, SQInteger idx, HSQMEMBERHANDLE* handle) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class or instance + :param HSQMEMBERHANDLE* handle: a pointer to the member handle + :returns: a SQRESULT + +pushes the value of a class or instance member using a member handle (see sq_getmemberhandle) + + + + + +.. _sq_getclosureinfo: + +.. c:function:: SQRESULT sq_getclosureinfo(HSQUIRRELVM v, SQInteger idx, SQInteger * nparams, SQInteger * nfreevars) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :param SQInteger * nparams: a pointer to an integer that will store the number of parameters + :param SQInteger * nfreevars: a pointer to an integer that will store the number of free variables + :returns: an SQRESULT + +retrieves number of parameters and number of freevariables from a squirrel closure. + + + + + +.. _sq_getclosurename: + +.. c:function:: SQRESULT sq_getclosurename(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: an SQRESULT + +pushes the name of the closure at position idx in the stack. Note that the name can be a string or null if the closure is anonymous or a native closure with no name assigned to it. + + + + + +.. _sq_getclosureroot: + +.. c:function:: SQRESULT sq_getclosureroot(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: an SQRESULT + +pushes the root table of the closure at position idx in the stack + + + + + +.. _sq_getfloat: + +.. c:function:: SQRESULT sq_getfloat(HSQUIRRELVM v, SQInteger idx, SQFloat * f) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQFloat * f: A pointer to the float that will store the value + :returns: a SQRESULT + +gets the value of the float at the idx position in the stack. + + + + + +.. _sq_gethash: + +.. c:function:: SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: the hash key of the value at the position idx in the stack + :remarks: the hash value function is the same used by the VM. + +returns the hash key of a value at the idx position in the stack. + + + + + +.. _sq_getinstanceup: + +.. c:function:: SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer * up, SQUSerPointer typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * up: a pointer to the userpointer that will store the result + :param SQUSerPointer typetag: the typetag that has to be checked, if this value is set to 0 the typetag is ignored. + :returns: a SQRESULT + +gets the userpointer of the class instance at position idx in the stack. if the parameter 'typetag' is different than 0, the function checks that the class or a base class of the instance is tagged with the specified tag; if not the function fails. If 'typetag' is 0 the function will ignore the tag check. + + + + + +.. _sq_getinteger: + +.. c:function:: SQRESULT sq_getinteger(HSQUIRRELVM v, SQInteger idx, SQInteger * i) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQInteger * i: A pointer to the integer that will store the value + :returns: a SQRESULT + +gets the value of the integer at the idx position in the stack. + + + + + +.. _sq_getmemberhandle: + +.. c:function:: SQRESULT sq_getmemberhandle(HSQUIRRELVM v, SQInteger idx, HSQMEMBERHANDLE* handle) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class + :param HSQMEMBERHANDLE* handle: a pointer to the variable that will store the handle + :returns: a SQRESULT + :remarks: This method works only with classes. A handle retrieved through a class can be later used to set or get values from one of the class instances. Handles retrieved from base classes are still valid in derived classes and respect inheritance rules. + +pops a value from the stack and uses it as index to fetch the handle of a class member. The handle can be later used to set or get the member value using sq_getbyhandle(), sq_setbyhandle(). + + + + + +.. _sq_getreleasehook: + +.. c:function:: SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :remarks: if the object that position idx is not an userdata, class instance or class the function returns NULL. + +gets the release hook of the userdata, class instance or class at position idx in the stack. + + + + + +.. _sq_getscratchpad: + +.. c:function:: SQChar * sq_getscratchpad(HSQUIRRELVM v, SQInteger minsize) + + :param HSQUIRRELVM v: the target VM + :param SQInteger minsize: the requested size for the scratchpad buffer + :remarks: the buffer is valid until the next call to sq_getscratchpad + +returns a pointer to a memory buffer that is at least as big as minsize. + + + + + +.. _sq_getsize: + +.. c:function:: SQObjectType sq_getsize(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: the size of the value at the position idx in the stack + :remarks: this function only works with strings, arrays, tables, classes, instances, and userdata if the value is not a valid type, the function will return -1. + +returns the size of a value at the idx position in the stack. If the value is a class or a class instance the size returned is the size of the userdata buffer (see sq_setclassudsize). + + + + + +.. _sq_getstring: + +.. c:function:: SQRESULT sq_getstring(HSQUIRRELVM v, SQInteger idx, const SQChar ** c) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param const SQChar ** c: a pointer to the pointer that will point to the string + :returns: a SQRESULT + +gets a pointer to the string at the idx position in the stack. + + + + + +.. _sq_getstringandsize: + +.. c:function:: SQRESULT sq_getstringandsize(HSQUIRRELVM v, SQInteger idx, const SQChar ** c, SQInteger* size) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param const SQChar ** c: a pointer to the pointer that will point to the string + :param SQInteger * size: a pointer to a SQInteger which will receive the size of the string + :returns: a SQRESULT + +gets a pointer to the string at the idx position in the stack; additionally retrieves its size. + + + + +.. _sq_getthread: + +.. c:function:: SQRESULT sq_getthread(HSQUIRRELVM v, SQInteger idx, HSQUIRRELVM* v) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param HSQUIRRELVM* v: A pointer to the variable that will store the thread pointer + :returns: a SQRESULT + +gets a pointer to the thread the idx position in the stack. + + + + + +.. _sq_gettype: + +.. c:function:: SQObjectType sq_gettype(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: the type of the value at the position idx in the stack + +returns the type of the value at the position idx in the stack + + + + + +.. _sq_gettypetag: + +.. c:function:: SQRESULT sq_gettypetag(HSQUIRRELVM v, SQInteger idx, SQUserPointer * typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * typetag: a pointer to the variable that will store the tag + :returns: a SQRESULT + :remarks: the function works also with instances. if the taget object is an instance, the typetag of it's base class is fetched. + +gets the typetag of the object (userdata or class) at position idx in the stack. + + + + + +.. _sq_getuserdata: + +.. c:function:: SQRESULT sq_getuserdata(HSQUIRRELVM v, SQInteger idx, SQUserPointer * p, SQUserPointer * typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * p: A pointer to the userpointer that will point to the userdata's payload + :param SQUserPointer * typetag: A pointer to a SQUserPointer that will store the userdata tag(see sq_settypetag). The parameter can be NULL. + :returns: a SQRESULT + +gets a pointer to the value of the userdata at the idx position in the stack. + + + + + +.. _sq_getuserpointer: + +.. c:function:: SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer * p) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * p: A pointer to the userpointer that will store the value + :returns: a SQRESULT + +gets the value of the userpointer at the idx position in the stack. + + + + + +.. _sq_newarray: + +.. c:function:: void sq_newarray(HSQUIRRELVM v, SQInteger size) + + :param HSQUIRRELVM v: the target VM + :param SQInteger size: the size of the array that as to be created + +creates a new array and pushes it in the stack + + + + + +.. _sq_newclass: + +.. c:function:: SQRESULT sq_newclass(HSQUIRRELVM v, SQBool hasbase) + + :param HSQUIRRELVM v: the target VM + :param SQBool hasbase: if the parameter is true the function expects a base class on top of the stack. + :returns: a SQRESULT + +creates a new class object. If the parameter 'hasbase' is different than 0, the function pops a class from the stack and inherits the new created class from it. The new class is pushed in the stack. + + + + + +.. _sq_newclosure: + +.. c:function:: void sq_newclosure(HSQUIRRELVM v, HSQFUNCTION func, SQInteger nfreevars) + + :param HSQUIRRELVM v: the target VM + :param HSQFUNCTION func: a pointer to a native-function + :param SQInteger nfreevars: number of free variables(can be 0) + +create a new native closure, pops n values set those as free variables of the new closure, and push the new closure in the stack. + + + + + +.. _sq_newtable: + +.. c:function:: void sq_newtable(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + +creates a new table and pushes it in the stack + + + + + +.. _sq_newtableex: + +.. c:function:: void sq_newtableex(HSQUIRRELVM v, SQInteger initialcapacity) + + :param HSQUIRRELVM v: the target VM + :param SQInteger initialcapacity: number of key/value pairs to preallocate + +creates a new table and pushes it in the stack. This function allows you to specify the initial capacity of the table to prevent unnecessary rehashing when the number of slots required is known at creation-time. + + + + + +.. _sq_newuserdata: + +.. c:function:: SQUserPointer sq_newuserdata(HSQUIRRELVM v, SQUnsignedInteger size) + + :param HSQUIRRELVM v: the target VM + :param SQUnsignedInteger size: the size of the userdata that as to be created in bytes + +creates a new userdata and pushes it in the stack + + + + + +.. _sq_pushbool: + +.. c:function:: void sq_pushbool(HSQUIRRELVM v, SQBool b) + + :param HSQUIRRELVM v: the target VM + :param SQBool b: the bool that has to be pushed(SQTrue or SQFalse) + +pushes a bool into the stack + + + + + +.. _sq_pushfloat: + +.. c:function:: void sq_pushfloat(HSQUIRRELVM v, SQFloat f) + + :param HSQUIRRELVM v: the target VM + :param SQFloat f: the float that has to be pushed + +pushes a float into the stack + + + + + +.. _sq_pushinteger: + +.. c:function:: void sq_pushinteger(HSQUIRRELVM v, SQInteger n) + + :param HSQUIRRELVM v: the target VM + :param SQInteger n: the integer that has to be pushed + +pushes an integer into the stack + + + + + +.. _sq_pushnull: + +.. c:function:: void sq_pushnull(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + +pushes a null value into the stack + + + + + +.. _sq_pushstring: + +.. c:function:: void sq_pushstring(HSQUIRRELVM v, const SQChar * s, SQInteger len) + + :param HSQUIRRELVM v: the target VM + :param const SQChar * s: pointer to the string that has to be pushed + :param SQInteger len: length of the string pointed by s + :remarks: if the parameter len is less than 0 the VM will calculate the length using strlen(s) + +pushes a string in the stack + + + + + +.. _sq_pushuserpointer: + +.. c:function:: void sq_pushuserpointer(HSQUIRRELVM v, SQUserPointer p) + + :param HSQUIRRELVM v: the target VM + :param SQUserPointer p: the pointer that as to be pushed + +pushes a userpointer into the stack + + + + + +.. _sq_setbyhandle: + +.. c:function:: SQRESULT sq_setbyhandle(HSQUIRRELVM v, SQInteger idx, HSQMEMBERHANDLE* handle) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class + :param HSQMEMBERHANDLE* handle: a pointer the member handle + :returns: a SQRESULT + +pops a value from the stack and sets it to a class or instance member using a member handle (see sq_getmemberhandle) + + + + + +.. _sq_setclassudsize: + +.. c:function:: SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class + :param SQInteger udsize: size in bytes reserved for user data + :returns: a SQRESULT + +Sets the user data size of a class. If a class 'user data size' is greater than 0. When an instance of the class is created additional space will be reserved at the end of the memory chunk where the instance is stored. The userpointer of the instance will also be automatically set to this memory area. This allows you to minimize allocations in applications that have to carry data along with the class instance. + + + + + +.. _sq_setclosureroot: + +.. c:function:: SQRESULT sq_setclosureroot(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: an SQRESULT + +pops a table from the stack and sets it as root of the closure at position idx in the stack + + + + + +.. _sq_setinstanceup: + +.. c:function:: SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer up) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer up: an arbitrary user pointer + :returns: a SQRESULT + +sets the userpointer of the class instance at position idx in the stack. + + + + + +.. _sq_setnativeclosurename: + +.. c:function:: SQRESULT sq_setnativeclosurename(HSQUIRRELVM v, SQInteger idx, const SQChar * name) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target native closure + :param const SQChar * name: the name that has to be set + :returns: an SQRESULT + +sets the name of the native closure at the position idx in the stack. The name of a native closure is purely for debug purposes. The name is retrieved through the function sq_stackinfos() while the closure is in the call stack. + + + + + +.. _sq_setparamscheck: + +.. c:function:: SQRESULT sq_setparamscheck(HSQUIRRELVM v, SQInteger nparamscheck, const SQChar * typemask) + + :param HSQUIRRELVM v: the target VM + :param SQInteger nparamscheck: defines the parameters number check policy (0 disables the param checking). If nparamscheck is greater than 0, the VM ensures that the number of parameters is exactly the number specified in nparamscheck (eg. if nparamscheck == 3 the function can only be called with 3 parameters). If nparamscheck is less than 0 the VM ensures that the closure is called with at least the absolute value of the number specified in nparamcheck (eg. nparamscheck == -3 will check that the function is called with at least 3 parameters). The hidden parameter 'this' is included in this number; free variables aren't. If SQ_MATCHTYPEMASKSTRING is passed instead of the number of parameters, the function will automatically infer the number of parameters to check from the typemask (eg. if the typemask is ".sn", it is like passing 3). + :param const SQChar * typemask: defines a mask to validate the parametes types passed to the function. If the parameter is NULL, no typechecking is applied (default). + :remarks: The typemask consists in a zero terminated string that represent the expected parameter type. The types are expressed as follows: 'o' null, 'i' integer, 'f' float, 'n' integer or float, 's' string, 't' table, 'a' array, 'u' userdata, 'c' closure and nativeclosure, 'g' generator, 'p' userpointer, 'v' thread, 'x' instance(class instance), 'y' class, 'b' bool. and '.' any type. The symbol '|' can be used as 'or' to accept multiple types on the same parameter. There isn't any limit on the number of 'or' that can be used. Spaces are ignored so can be inserted between types to increase readability. For instance to check a function that expect a table as 'this' a string as first parameter and a number or a userpointer as second parameter, the string would be "tsn|p" (table,string,number or userpointer). If the parameters mask is contains fewer parameters than 'nparamscheck', the remaining parameters will not be typechecked. + +Sets the parameter validation scheme for the native closure at the top position in the stack. Allows you to validate the number of parameters accepted by the function and optionally their types. If the function call does not comply with the parameter schema set by sq_setparamscheck, an exception is thrown. + +*.eg* + +:: + + //example + SQInteger testy(HSQUIRRELVM v) + { + SQUserPointer p; + const SQChar *s; + SQInteger i; + //no type checking, if the call complies with the mask + //surely the functions will succeed. + sq_getuserdata(v,1,&p,NULL); + sq_getstring(v,2,&s); + sq_getinteger(v,3,&i); + //... do something + return 0; + } + + //the reg code + + //....stuff + sq_newclosure(v,testy,0); + //expects exactly 3 parameters(userdata,string,number) + sq_setparamscheck(v,3,_SC("usn")); + //....stuff + + + + + + +.. _sq_setreleasehook: + +.. c:function:: void sq_setreleasehook(HSQUIRRELVM v, SQInteger idx, SQRELEASEHOOK hook) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQRELEASEHOOK hook: a function pointer to the hook(see sample below) + :remarks: the function hook is called by the VM before the userdata memory is deleted. + +sets the release hook of the userdata, class instance, or class at position idx in the stack. + +*.eg* + +:: + + + /* tyedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size); */ + + SQInteger my_release_hook(SQUserPointer p,SQInteger size) + { + /* do something here */ + return 1; + } + + + + + + +.. _sq_settypetag: + +.. c:function:: SQRESULT sq_settypetag(HSQUIRRELVM v, SQInteger idx, SQUserPointer typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer typetag: an arbitrary SQUserPointer + :returns: a SQRESULT + +sets the typetag of the object (userdata or class) at position idx in the stack. + + + + + +.. _sq_tobool: + +.. c:function:: void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool * b) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQBool * b: A pointer to the bool that will store the value + :remarks: if the object is not a bool the function converts the value to bool according to squirrel's rules. For instance the number 1 will result in true, and the number 0 in false. + +gets the value at position idx in the stack as bool. + + + + + +.. _sq_tostring: + +.. c:function:: void sq_tostring(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + +converts the object at position idx in the stack to string and pushes the resulting string in the stack. + + + + + +.. _sq_typeof: + +.. c:function:: SQObjectType sq_typeof(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: a SQRESULT + +pushes the type name of the value at the position idx in the stack. It also invokes the _typeof metamethod for tables and class instances that implement it; in that case the pushed object could be something other than a string (is up to the _typeof implementation). + + + diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst b/mp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst new file mode 100644 index 00000000..d5fe5336 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst @@ -0,0 +1,451 @@ +.. _api_ref_object_manipulation: + +==================== +Object manipulation +==================== + +.. _sq_arrayappend: + +.. c:function:: SQRESULT sq_arrayappend(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :returns: a SQRESULT + :remarks: Only works on arrays. + +pops a value from the stack and pushes it in the back of the array at the position idx in the stack. + + + + + +.. _sq_arrayinsert: + +.. c:function:: SQRESULT sq_arrayinsert(HSQUIRRELVM v, SQInteger idx, SQInteger destpos) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :param SQInteger destpos: the position in the array where the item has to be inserted + :returns: a SQRESULT + :remarks: Only works on arrays. + +pops a value from the stack and inserts it in an array at the specified position + + + + + +.. _sq_arraypop: + +.. c:function:: SQRESULT sq_arraypop(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :returns: a SQRESULT + :remarks: Only works on arrays. + +pops a value from the back of the array at the position idx in the stack. + + + + + +.. _sq_arrayremove: + +.. c:function:: SQRESULT sq_arrayremove(HSQUIRRELVM v, SQInteger idx, SQInteger itemidx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :param SQInteger itemidx: the index of the item in the array that has to be removed + :returns: a SQRESULT + :remarks: Only works on arrays. + +removes an item from an array + + + + + +.. _sq_arrayresize: + +.. c:function:: SQRESULT sq_arrayresize(HSQUIRRELVM v, SQInteger idx, SQInteger newsize) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :param SQInteger newsize: requested size of the array + :returns: a SQRESULT + :remarks: Only works on arrays. If newsize if greater than the current size the new array slots will be filled with nulls. + +resizes the array at the position idx in the stack. + + + + + +.. _sq_arrayreverse: + +.. c:function:: SQRESULT sq_arrayreverse(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :returns: a SQRESULT + :remarks: Only works on arrays. + +reverses an array in place. + + + + + +.. _sq_clear: + +.. c:function:: SQRESULT sq_clear(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: Only works on tables and arrays. + +clears all the elements of the table/array at position idx in the stack. + + + + + +.. _sq_clone: + +.. c:function:: SQRESULT sq_clone(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + +pushes a clone of the table, array, or class instance at the position idx. + + + + + +.. _sq_createslot: + +.. c:function:: SQRESULT sq_createslot(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :returns: a SQRESULT + :remarks: invoke the _newslot metamethod in the table delegate. it only works on tables. [this function is deperecated since version 2.0.5 use sq_newslot() instead] + +pops a key and a value from the stack and performs a set operation on the table or class that is at position idx in the stack; if the slot does not exist, it will be created. + + + + + +.. _sq_deleteslot: + +.. c:function:: SQRESULT sq_deleteslot(HSQUIRRELVM v, SQInteger idx, SQBool pushval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool pushval: if this param is true the function will push the value of the deleted slot. + :returns: a SQRESULT + :remarks: invoke the _delslot metamethod in the table delegate. it only works on tables. + +pops a key from the stack and delete the slot indexed by it from the table at position idx in the stack; if the slot does not exist, nothing happens. + + + + + +.. _sq_get: + +.. c:function:: SQRESULT sq_get(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: this call will invokes the delegation system like a normal dereference it only works on tables, arrays, classes, instances and userdata; if the function fails, nothing will be pushed in the stack. + +pops a key from the stack and performs a get operation on the object at the position idx in the stack; and pushes the result in the stack. + + + + + +.. _sq_getattributes: + +.. c:function:: SQRESULT sq_getattributes(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class in the stack + :returns: a SQRESULT + +Gets the attribute of a class member. The function pops a key from the stack and pushes the attribute of the class member indexed by they key from a class at position idx in the stack. If key is null the function gets the class level attribute. + + + + + +.. _sq_getbase: + +.. c:function:: SQRESULT sq_getbase(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class in the stack + :returns: a SQRESULT + +pushes the base class of the 'class' at stored position idx in the stack. + + + + + +.. _sq_getclass: + +.. c:function:: SQRESULT sq_getclass(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class instance in the stack + :returns: a SQRESULT + +pushes the class of the 'class instance' at stored position idx in the stack. + + + + + +.. _sq_getdelegate: + +.. c:function:: SQRESULT sq_getdelegate(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + +pushes the current delegate of the object at the position idx in the stack. + + + + + +.. _sq_getfreevariable: + +.. c:function:: const SQChar * sq_getfreevariable(HSQUIRRELVM v, SQInteger idx, SQInteger nval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack(closure) + :param SQInteger nval: 0 based index of the free variable(relative to the closure). + :returns: the name of the free variable for pure squirrel closures. NULL in case of error or if the index of the variable is out of range. In case the target closure is a native closure, the return name is always "@NATIVE". + :remarks: The function works for both squirrel closure and native closure. + +gets the value of the free variable of the closure at the position idx in the stack. + + + + + +.. _sq_getweakrefval: + +.. c:function:: SQRESULT sq_getweakrefval(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target weak reference + :returns: a SQRESULT + :remarks: if the function fails, nothing is pushed in the stack. + +pushes the object pointed by the weak reference at position idx in the stack. + + + + + +.. _sq_instanceof: + +.. c:function:: SQBool sq_instanceof(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: SQTrue if the instance at position -2 in the stack is an instance of the class object at position -1 in the stack. + :remarks: The function doesn't pop any object from the stack. + +Determines if an object is an instance of a certain class. Expects an instance and a class in the stack. + + + + + +.. _sq_newmember: + +.. c:function:: SQRESULT sq_newmember(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool bstatic: if SQTrue creates a static member. + :returns: a SQRESULT + :remarks: Invokes the _newmember metamethod in the class. it only works on classes. + +pops a key, a value and an object (which will be set as attribute of the member) from the stack and performs a new slot operation on the class that is at position idx in the stack; if the slot does not exist, it will be created. + + + + + +.. _sq_newslot: + +.. c:function:: SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool bstatic: if SQTrue creates a static member. This parameter is only used if the target object is a class. + :returns: a SQRESULT + :remarks: Invokes the _newslot metamethod in the table delegate. it only works on tables and classes. + +pops a key and a value from the stack and performs a set operation on the table or class that is at position idx in the stack, if the slot does not exist it will be created. + + + + + +.. _sq_next: + +.. c:function:: SQRESULT sq_next(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + +Pushes in the stack the next key and value of an array, table, or class slot. To start the iteration this function expects a null value on top of the stack; at every call the function will substitute the null value with an iterator and push key and value of the container slot. Every iteration the application has to pop the previous key and value but leave the iterator(that is used as reference point for the next iteration). The function will fail when all slots have been iterated(see Tables and arrays manipulation). + + + + + +.. _sq_rawdeleteslot: + +.. c:function:: SQRESULT sq_rawdeleteslot(HSQUIRRELVM v, SQInteger idx, SQBool pushval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool pushval: if this param is true the function will push the value of the deleted slot. + :returns: a SQRESULT + +Deletes a slot from a table without employing the _delslot metamethod. Pops a key from the stack and delete the slot indexed by it from the table at position idx in the stack; if the slot does not exist nothing happens. + + + + + +.. _sq_rawget: + +.. c:function:: SQRESULT sq_rawget(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: Only works on tables and arrays. + +pops a key from the stack and performs a get operation on the object at position idx in the stack, without employing delegation or metamethods. + + + + + +.. _sq_rawnewmember: + +.. c:function:: SQRESULT sq_rawnewmember(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool bstatic: if SQTrue creates a static member. + :returns: a SQRESULT + :remarks: it only works on classes. + +pops a key, a value and an object(that will be set as attribute of the member) from the stack and performs a new slot operation on the class that is at position idx in the stack; if the slot does not exist it will be created. + + + + + +.. _sq_rawset: + +.. c:function:: SQRESULT sq_rawset(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: it only works on tables and arrays. if the function fails nothing will be pushed in the stack. + +pops a key and a value from the stack and performs a set operation on the object at position idx in the stack, without employing delegation or metamethods. + + + + + +.. _sq_set: + +.. c:function:: SQRESULT sq_set(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: this call will invoke the delegation system like a normal assignment, it only works on tables, arrays and userdata. + +pops a key and a value from the stack and performs a set operation on the object at position idx in the stack. + + + + + +.. _sq_setattributes: + +.. c:function:: SQRESULT sq_setattributes(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class in the stack. + :returns: a SQRESULT + +Sets the attribute of a class member. The function pops a key and a value from the stack and sets the attribute (indexed by the key) on the class at position idx in the stack. If key is null the function sets the class level attribute. If the function succeed, the old attribute value is pushed in the stack. + + + + + +.. _sq_setdelegate: + +.. c:function:: SQRESULT sq_setdelegate(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: to remove the delegate from an object, set a null value. + +pops a table from the stack and sets it as the delegate of the object at the position idx in the stack. + + + + + +.. _sq_setfreevariable: + +.. c:function:: SQRESULT sq_setfreevariable(HSQUIRRELVM v, SQInteger idx, SQInteger nval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :param SQInteger nval: 0 based index of the free variable(relative to the closure). + :returns: a SQRESULT + +pops a value from the stack and sets it as a free variable of the closure at the position idx in the stack. + + + + + +.. _sq_weakref: + +.. c:function:: void sq_weakref(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index to the target object in the stack + :returns: a SQRESULT + :remarks: if the object at idx position is one of (integer, float, bool, null), the object itself is pushed instead of a weak ref. + +pushes a weak reference to the object at position idx in the stack. diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst b/mp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst new file mode 100644 index 00000000..f655c345 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst @@ -0,0 +1,163 @@ +.. _api_ref_raw_object_handling: + +=================== +Raw object handling +=================== + +.. _sq_addref: + +.. c:function:: void sq_addref(HSQUIRRELVM v, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT* po: pointer to an object handler + +adds a reference to an object handler. + + + + + +.. _sq_getobjtypetag: + +.. c:function:: SQRESULT sq_getobjtypetag(HSQOBJECT* o, SQUserPointer* typetag) + + :param HSQOBJECT* o: pointer to an object handler + :param SQUserPointer* typetag: a pointer to the variable that will store the tag + :returns: a SQRESULT + :remarks: the function works also with instances. if the target object is an instance, the typetag of it's base class is fetched. + +gets the typetag of a raw object reference(userdata or class). + + + + + +.. _sq_getrefcount: + +.. c:function:: SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT* po: object handler + +returns the number of references of a given object. + + + + + +.. _sq_getstackobj: + +.. c:function:: SQRESULT sq_getstackobj(HSQUIRRELVM v, SQInteger idx, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :param HSQOBJECT* po: pointer to an object handler + :returns: a SQRESULT + +gets an object from the stack and stores it in a object handler. + + + + + +.. _sq_objtobool: + +.. c:function:: SQBool sq_objtobool(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object is not a bool will always return false. + +return the bool value of a raw object reference. + + + + + +.. _sq_objtofloat: + +.. c:function:: SQFloat sq_objtofloat(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object is an integer will convert it to float. If the object is not a number will always return 0. + +return the float value of a raw object reference. + + + + + +.. _sq_objtointeger: + +.. c:function:: SQInteger sq_objtointeger(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object is a float will convert it to integer. If the object is not a number will always return 0. + +return the integer value of a raw object reference. + + + + + +.. _sq_objtostring: + +.. c:function:: const SQChar* sq_objtostring(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object doesn't reference a string it returns NULL. + +return the string value of a raw object reference. + + + + + +.. _sq_objtouserpointer: + +.. c:function:: SQUserPointer sq_objtouserpointer(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object doesn't reference a userpointer it returns NULL. + +return the userpointer value of a raw object reference. + + + + + +.. _sq_pushobject: + +.. c:function:: void sq_pushobject(HSQUIRRELVM v, HSQOBJECT obj) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT obj: object handler + +push an object referenced by an object handler into the stack. + + + + + +.. _sq_release: + +.. c:function:: SQBool sq_release(HSQUIRRELVM v, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT* po: pointer to an object handler + :returns: SQTrue if the object handler released has lost all is references(the ones added with sq_addref). SQFalse otherwise. + :remarks: the function will reset the object handler to null when it loses all references. + +remove a reference from an object handler. + + + + + +.. _sq_resetobject: + +.. c:function:: void sq_resetobject(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: Every object handler has to be initialized with this function. + +resets(initialize) an object handler. diff --git a/mp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst b/mp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst new file mode 100644 index 00000000..bcd0e6c2 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst @@ -0,0 +1,107 @@ +.. _api_ref_stack_operations: + +================ +Stack Operations +================ + +.. _sq_cmp: + +.. c:function:: SQInteger sq_cmp(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: > 0 if obj1>obj2 + :returns: == 0 if obj1==obj2 + :returns: < 0 if obj1 0. :: + + sq_pushroottable(v); + sq_pushstring(v,"foo",-1); + sq_get(v,-2); //get the function from the root table + sq_pushroottable(v); //'this' (function environment object) + sq_pushinteger(v,1); + sq_pushfloat(v,2.0); + sq_pushstring(v,"three",-1); + sq_call(v,4,SQFalse,SQFalse); + sq_pop(v,2); //pops the roottable and the function + +this is equivalent to the following Squirrel code:: + + foo(1,2.0,"three"); + +If a runtime error occurs (or a exception is thrown) during the squirrel code execution +the sq_call will fail. diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst new file mode 100644 index 00000000..88c15c86 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst @@ -0,0 +1,58 @@ +.. embedding_compiling_a_script: + +================== +Compiling a script +================== + +You can compile a Squirrel script with the function *sq_compile*.:: + + typedef SQInteger (*SQLEXREADFUNC)(SQUserPointer userdata); + + SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p, + const SQChar *sourcename,SQBool raiseerror); + +In order to compile a script is necessary for the host application to implement a reader +function (SQLEXREADFUNC); this function is used to feed the compiler with the script +data. +The function is called every time the compiler needs a character; It has to return a +character code if succeed or 0 if the source is finished. + +If sq_compile succeeds, the compiled script will be pushed as Squirrel function in the +stack. + +.. :note:: + In order to execute the script, the function generated by *sq_compile()* has + to be called through *sq_call()* + +Here an example of a 'read' function that read from a file: :: + + SQInteger file_lexfeedASCII(SQUserPointer file) + { + int ret; + char c; + if( ( ret=fread(&c,sizeof(c),1,(FILE *)file )>0) ) + return c; + return 0; + } + + int compile_file(HSQUIRRELVM v,const char *filename) + { + FILE *f=fopen(filename,"rb"); + if(f) + { + sq_compile(v,file_lexfeedASCII,f,filename,1); + fclose(f); + return 1; + } + return 0; + } + +When the compiler fails for a syntax error it will try to call the 'compiler error handler'; +this function must be declared as follow: :: + + typedef void (*SQCOMPILERERROR)(HSQUIRRELVM /*v*/,const SQChar * /*desc*/,const SQChar * /*source*/, + SQInteger /*line*/,SQInteger /*column*/); + +and can be set with the following API call:: + + void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f); diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst new file mode 100644 index 00000000..43772f38 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst @@ -0,0 +1,106 @@ +.. _embedding_creating_a_c_function: + +=================== +Create a C function +=================== + +A native C function must have the following prototype: :: + + typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM); + +The parameters is an handle to the calling VM and the return value is an integer +respecting the following rules: + +* 1 if the function returns a value +* 0 if the function does not return a value +* SQ_ERROR runtime error is thrown + +In order to obtain a new callable squirrel function from a C function pointer, is necessary +to call sq_newclosure() passing the C function to it; the new Squirrel function will be +pushed in the stack. + +When the function is called, the stackbase is the first parameter of the function and the +top is the last. In order to return a value the function has to push it in the stack and +return 1. + +Function parameters are in the stack from position 1 ('this') to *n*. +*sq_gettop()* can be used to determinate the number of parameters. + +If the function has free variables, those will be in the stack after the explicit parameters +an can be handled as normal parameters. Note also that the value returned by *sq_gettop()* will be +affected by free variables. *sq_gettop()* will return the number of parameters plus +number of free variables. + +Here an example, the following function print the value of each argument and return the +number of arguments. :: + + SQInteger print_args(HSQUIRRELVM v) + { + SQInteger nargs = sq_gettop(v); //number of arguments + for(SQInteger n=1;n<=nargs;n++) + { + printf("arg %d is ",n); + switch(sq_gettype(v,n)) + { + case OT_NULL: + printf("null"); + break; + case OT_INTEGER: + printf("integer"); + break; + case OT_FLOAT: + printf("float"); + break; + case OT_STRING: + printf("string"); + break; + case OT_TABLE: + printf("table"); + break; + case OT_ARRAY: + printf("array"); + break; + case OT_USERDATA: + printf("userdata"); + break; + case OT_CLOSURE: + printf("closure(function)"); + break; + case OT_NATIVECLOSURE: + printf("native closure(C function)"); + break; + case OT_GENERATOR: + printf("generator"); + break; + case OT_USERPOINTER: + printf("userpointer"); + break; + case OT_CLASS: + printf("class"); + break; + case OT_INSTANCE: + printf("instance"); + break; + case OT_WEAKREF: + printf("weak reference"); + break; + default: + return sq_throwerror(v,"invalid param"); //throws an exception + } + } + printf("\n"); + sq_pushinteger(v,nargs); //push the number of arguments as return value + return 1; //1 because 1 value is returned + } + +Here an example of how to register a function:: + + SQInteger register_global_func(HSQUIRRELVM v,SQFUNCTION f,const char *fname) + { + sq_pushroottable(v); + sq_pushstring(v,fname,-1); + sq_newclosure(v,f,0); //create a new function + sq_newslot(v,-3,SQFalse); + sq_pop(v,1); //pops the root table + return 0; + } diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst new file mode 100644 index 00000000..3d38bf27 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst @@ -0,0 +1,58 @@ +.. _embedding_debug_interface: + +=============== +Debug Interface +=============== + +The squirrel VM exposes a very simple debug interface that allows to easily built a full +featured debugger. +Through the functions sq_setdebughook and sq_setnativedebughook is possible in fact to set a callback function that +will be called every time the VM executes an new line of a script or if a function get +called/returns. The callback will pass as argument the current line the current source and the +current function name (if any).:: + + SQUIRREL_API void sq_setdebughook(HSQUIRRELVM v); + +or :: + + SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook); + +The following code shows how a debug hook could look like(obviously is possible to +implement this function in C as well). :: + + function debughook(event_type,sourcefile,line,funcname) + { + local fname=funcname?funcname:"unknown"; + local srcfile=sourcefile?sourcefile:"unknown" + switch (event_type) { + case 'l': //called every line(that contains some code) + ::print("LINE line [" + line + "] func [" + fname + "]"); + ::print("file [" + srcfile + "]\n"); + break; + case 'c': //called when a function has been called + ::print("LINE line [" + line + "] func [" + fname + "]"); + ::print("file [" + srcfile + "]\n"); + break; + case 'r': //called when a function returns + ::print("LINE line [" + line + "] func [" + fname + "]"); + ::print("file [" + srcfile + "]\n"); + break; + } + } + +The parameter *event_type* can be 'l' ,'c' or 'r' ; a hook with a 'l' event is called for each line that +gets executed, 'c' every time a function gets called and 'r' every time a function returns. + +A full-featured debugger always allows displaying local variables and calls stack. +The call stack information are retrieved through sq_getstackinfos():: + + SQInteger sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si); + +While the local variables info through sq_getlocal():: + + SQInteger sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger nseq); + +In order to receive line callbacks the scripts have to be compiled with debug infos enabled +this is done through sq_enabledebuginfo(); :: + + void sq_enabledebuginfo(HSQUIRRELVM v, SQInteger debuginfo); diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst new file mode 100644 index 00000000..f86e7416 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst @@ -0,0 +1,16 @@ +.. _embedding_error_convetions: + + +======================== +Error Conventions +======================== + +.. index:: + single: Error Conventions + +Most of the functions in the API return a SQRESULT value; SQRESULT indicates if a +function completed successfully or not. +The macros SQ_SUCCEEDED() and SQ_FAILED() are used to test the result of a function.:: + + if(SQ_FAILED(sq_getstring(v,-1,&s))) + printf("getstring failed"); diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst new file mode 100644 index 00000000..ad0fb082 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst @@ -0,0 +1,28 @@ +.. _embedding_memory_management: + +======================== +Memory Management +======================== + +.. index:: single: Memory Management + +Squirrel uses reference counting (RC) as primary system for memory management; +however, the virtual machine (VM) has an auxiliary +mark and sweep garbage collector that can be invoked on demand. + +There are 2 possible compile time options: + + * The default configuration consists in RC plus a mark and sweep garbage collector. + The host program can call the function sq_collectgarbage() and perform a garbage collection cycle + during the program execution. The garbage collector isn't invoked by the VM and has to + be explicitly called by the host program. + + * The second a situation consists in RC only(define NO_GARBAGE_COLLECTOR); in this case is impossible for + the VM to detect reference cycles, so is the programmer that has to solve them explicitly in order to + avoid memory leaks. + +The only advantage introduced by the second option is that saves 2 additional +pointers that have to be stored for each object in the default configuration with +garbage collector(8 bytes for 32 bits systems). +The types involved are: tables, arrays, functions, threads, userdata and generators; all other +types are untouched. These options do not affect execution speed. diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst new file mode 100644 index 00000000..ba84042c --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst @@ -0,0 +1,21 @@ +.. embedding_references_from_c: + +======================================================== +Mantaining references to Squirrel values from the C API +======================================================== + +Squirrel allows to reference values through the C API; the function sq_getstackobj() gets +a handle to a squirrel object(any type). The object handle can be used to control the lifetime +of an object by adding or removing references to it( see sq_addref() and sq_release()). +The object can be also re-pushed in the VM stack using sq_pushobject().:: + + HSQOBJECT obj; + + sq_resetobject(&obj); //initialize the handle + sq_getstackobj(v,-2,&obj); //retrieve an object handle from the pos -2 + sq_addref(v,&obj); //adds a reference to the object + + ... //do stuff + + sq_pushobject(v,obj); //push the object in the stack + sq_release(v,&obj); //relese the object diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst new file mode 100644 index 00000000..b956e4be --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst @@ -0,0 +1,17 @@ +.. _embedding_runtime_error_handling: + +====================== +Runtime error handling +====================== + +When an exception is not handled by Squirrel code with a try/catch statement, a runtime +error is raised and the execution of the current program is interrupted. It is possible to +set a call back function to intercept the runtime error from the host program; this is +useful to show meaningful errors to the script writer and for implementing visual +debuggers. +The following API call pops a Squirrel function from the stack and sets it as error handler.:: + + SQUIRREL_API void sq_seterrorhandler(HSQUIRRELVM v); + +The error handler is called with 2 parameters, an environment object (this) and a object. +The object can be any squirrel type. diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst new file mode 100644 index 00000000..8378a102 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst @@ -0,0 +1,70 @@ +.. _embedding_tables_and_arrays_manipulation: + +============================== +Tables and arrays manipulation +============================== + +A new table is created calling sq_newtable, this function pushes a new table in the stack.:: + + void sq_newtable(HSQUIRRELVM v); + +To create a new slot:: + + SQRESULT sq_newslot(HSQUIRRELVM v,SQInteger idx,SQBool bstatic); + +To set or get the table delegate:: + + SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx); + SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx); + + +A new array is created calling sq_newarray, the function pushes a new array in the +stack; if the parameters size is bigger than 0 the elements are initialized to null.:: + + void sq_newarray (HSQUIRRELVM v,SQInteger size); + +To append a value to the back of the array:: + + SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx); + +To remove a value from the back of the array:: + + SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQInteger pushval); + +To resize the array:: + + SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize); + +To retrieve the size of a table or an array you must use sq_getsize():: + + SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx); + +To set a value in an array or table:: + + SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx); + +To get a value from an array or table:: + + SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx); + +To get or set a value from a table without employing delegation:: + + SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx); + SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx); + +To iterate a table or an array:: + + SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx); + +Here an example of how to perform an iteration: :: + + //push your table/array here + sq_pushnull(v) //null iterator + while(SQ_SUCCEEDED(sq_next(v,-2))) + { + //here -1 is the value and -2 is the key + + sq_pop(v,2); //pops key and val before the nex iteration + } + + sq_pop(v,1); //pops the null iterator diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst new file mode 100644 index 00000000..4aa5f782 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst @@ -0,0 +1,14 @@ +.. _embedding_the_registry_table: + +================== +The registry table +================== + +The registry table is an hidden table shared between vm and all his thread(friend vms). +This table is accessible only through the C API and is meant to be an utility structure +for native C library implementation. +For instance the sqstdlib(squirrel standard library)uses it to store configuration and shared objects +delegates. +The registry is accessible through the API call *sq_pushregistrytable()*.:: + + void sq_pushregistrytable(HSQUIRRELVM v); diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst new file mode 100644 index 00000000..9c5f9aef --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst @@ -0,0 +1,104 @@ +.. _embedding_the_stack: + + +========== +The Stack +========== + +Squirrel exchanges values with the virtual machine through a stack. This mechanism has +been inherited from the language Lua. +For instance to call a Squirrel function from C it is necessary to push the function and the +arguments in the stack and then invoke the function; also when Squirrel calls a C +function the parameters will be in the stack as well. + +------------- +Stack indexes +------------- + +Many API functions can arbitrarily refer to any element in the stack through an index. +The stack indexes follow those conventions: + +* 1 is the stack base +* Negative indexes are considered an offset from top of the stack. For instance -1 isthe top of the stack. +* 0 is an invalid index + +Here an example (let's pretend that this table is the VM stack) + ++------------+--------------------+--------------------+ +| **STACK** | **positive index** | **negative index** | ++============+====================+====================+ +| "test" | 4 | -1(top) | ++------------+--------------------+--------------------+ +| 1 | 3 | -2 | ++------------+--------------------+--------------------+ +| 0.5 | 2 | -3 | ++------------+--------------------+--------------------+ +| "foo" | 1(base) | -4 | ++------------+--------------------+--------------------+ + +In this case, the function *sq_gettop* would return 4; + +------------------ +Stack manipulation +------------------ + +The API offers several functions to push and retrieve data from the Squirrel stack. + +To push a value that is already present in the stack in the top position:: + + void sq_push(HSQUIRRELVM v,SQInteger idx); + +To pop an arbitrary number of elements:: + + void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop); + +To remove an element from the stack:: + + void sq_remove(HSQUIRRELVM v,SQInteger idx); + +To retrieve the top index (and size) of the current +virtual stack you must call *sq_gettop* :: + + SQInteger sq_gettop(HSQUIRRELVM v); + +To force the stack to a certain size you can call *sq_settop* :: + + void sq_settop(HSQUIRRELVM v,SQInteger newtop); + +If the newtop is bigger than the previous one, the new positions in the stack will be +filled with null values. + +The following function pushes a C value into the stack:: + + void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len); + void sq_pushfloat(HSQUIRRELVM v,SQFloat f); + void sq_pushinteger(HSQUIRRELVM v,SQInteger n); + void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p); + void sq_pushbool(HSQUIRRELVM v,SQBool b); + +this function pushes a null into the stack:: + + void sq_pushnull(HSQUIRRELVM v); + +returns the type of the value in a arbitrary position in the stack:: + + SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx); + +the result can be one of the following values: :: + + OT_NULL,OT_INTEGER,OT_FLOAT,OT_STRING,OT_TABLE,OT_ARRAY,OT_USERDATA, + OT_CLOSURE,OT_NATIVECLOSURE,OT_GENERATOR,OT_USERPOINTER,OT_BOOL,OT_INSTANCE,OT_CLASS,OT_WEAKREF + +The following functions convert a squirrel value in the stack to a C value:: + + SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c); + SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger size); + SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i); + SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f); + SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p); + SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag); + SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *p); + +The function sq_cmp compares 2 values from the stack and returns their relation (like strcmp() in ANSI C).:: + + SQInteger sq_cmp(HSQUIRRELVM v); diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst new file mode 100644 index 00000000..81e14f03 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst @@ -0,0 +1,33 @@ +.. _embedding_userdata_and_userpointers: + +========================= +Userdata and UserPointers +========================= + +Squirrel allows the host application put arbitrary data chunks into a Squirrel value, this is +possible through the data type userdata.:: + + SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size); + +When the function *sq_newuserdata* is called, Squirrel allocates a new userdata with the +specified size, returns a pointer to his payload buffer and push the object in the stack; at +this point the application can do whatever it want with this memory chunk, the VM will +automatically take cake of the memory deallocation like for every other built-in type. +A userdata can be passed to a function or stored in a table slot. By default Squirrel +cannot manipulate directly userdata; however is possible to assign a delegate to it and +define a behavior like it would be a table. +Because the application would want to do something with the data stored in a userdata +object when it get deleted, is possible to assign a callback that will be called by the VM +just before deleting a certain userdata. +This is done through the API call *sq_setreleasehook*.:: + + typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size); + + void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook); + +Another kind of userdata is the userpointer; this type is not a memory chunk like the +normal userdata, but just a 'void*' pointer. It cannot have a delegate and is passed by +value, so pushing a userpointer doesn't cause any memory allocation.:: + + void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p); + diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst new file mode 100644 index 00000000..03fba06c --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst @@ -0,0 +1,21 @@ +.. _embedding_vm_initialization: + +============================== +Virtual Machine Initialization +============================== + +The first thing that a host application has to do, is create a virtual machine. +The host application can create any number of virtual machines through the function +*sq_open()*. +Every single VM that was created using *sq_open()* has to be released with the function *sq_close()* when it is no +longer needed.:: + + int main(int argc, char* argv[]) + { + HSQUIRRELVM v; + v = sq_open(1024); //creates a VM with initial stack size 1024 + + //do some stuff with squirrel here + + sq_close(v); + } diff --git a/mp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst b/mp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst new file mode 100644 index 00000000..dc8d7fb1 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst @@ -0,0 +1,29 @@ +.. _embedding_squirrel: + +*************************** + Embedding Squirrel +*************************** + +*This section describes how to embed Squirrel in a host application, +C language knowledge is required to understand this part of the manual.* + +Because of his nature of extension language, Squirrel's compiler and virtual machine +are implemented as C library. The library exposes a set of functions to compile scripts, +call functions, manipulate data and extend the virtual machine. +All declarations needed for embedding the language in an application are in the header file 'squirrel.h'. + +.. toctree:: + embedding/memory_management.rst + embedding/build_configuration.rst + embedding/error_conventions.rst + embedding/vm_initialization.rst + embedding/the_stack.rst + embedding/runtime_error_handling.rst + embedding/compiling_a_script.rst + embedding/calling_a_function.rst + embedding/creating_a_c_function.rst + embedding/tables_and_arrays_manipulation.rst + embedding/userdata_and_userpointers.rst + embedding/the_registry_table.rst + embedding/references_from_c.rst + embedding/debug_interface.rst diff --git a/mp/src/vscript/squirrel/doc/source/reference/index.rst b/mp/src/vscript/squirrel/doc/source/reference/index.rst new file mode 100644 index 00000000..e2d38184 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/index.rst @@ -0,0 +1,34 @@ +.. _reference: + +################################# + Squirrel 3.1 Reference Manual +################################# + +Copyright (c) 2003-2016 Alberto Demichelis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +.. toctree:: + :maxdepth: 3 + :numbered: + + introduction.rst + language.rst + embedding_squirrel.rst + api_reference.rst diff --git a/mp/src/vscript/squirrel/doc/source/reference/introduction.rst b/mp/src/vscript/squirrel/doc/source/reference/introduction.rst new file mode 100644 index 00000000..bc83447f --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/introduction.rst @@ -0,0 +1,16 @@ +.. _introduction: + +************ +Introduction +************ + +.. index:: + single: introduction + +Squirrel is a high-level, imperative-OO programming language, designed to be a powerful +scripting tool that fits within the size, memory bandwidth, and real-time requirements of +applications like games. +Squirrel offers a wide range of features like dynamic typing, delegation, higher +order functions, generators, tail recursion, exception handling, automatic memory +management while fitting both compiler and virtual machine into about 6k lines of C++ +code. diff --git a/mp/src/vscript/squirrel/doc/source/reference/language.rst b/mp/src/vscript/squirrel/doc/source/reference/language.rst new file mode 100644 index 00000000..35621196 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language.rst @@ -0,0 +1,23 @@ +.. _thelanguage: + +*************************** + The language +*************************** + +.. toctree:: + language/lexical_structure.rst + language/datatypes.rst + language/execution_context.rst + language/statements.rst + language/expressions.rst + language/tables.rst + language/arrays.rst + language/functions.rst + language/classes.rst + language/generators.rst + language/constants_and_enumerations.rst + language/threads.rst + language/weak_references.rst + language/delegation.rst + language/metamethods.rst + language/builtin_functions.rst diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/arrays.rst b/mp/src/vscript/squirrel/doc/source/reference/language/arrays.rst new file mode 100644 index 00000000..ddf5114f --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/arrays.rst @@ -0,0 +1,19 @@ +.. _arrays: + + +================= +Arrays +================= + +.. index:: + single: Arrays + +An array is a sequence of values indexed by a integer number from 0 to the size of the +array minus 1. Arrays elements can be obtained through their index.:: + + local a=["I'm a string", 123] + print(typeof a[0]) //prints "string" + print(typeof a[1]) //prints "integer" + +Resizing, insertion, deletion of arrays and arrays elements is done through a set of +standard functions (see :ref:`built-in functions `). diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst b/mp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst new file mode 100644 index 00000000..2f64105b --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst @@ -0,0 +1,693 @@ +.. _builtin_functions: + + +================== +Built-in Functions +================== + +.. index:: + single: Built-in Functions + pair: Global Symbols; Built-in Functions + + +^^^^^^^^^^^^^^ +Global Symbols +^^^^^^^^^^^^^^ + +.. js:function:: array(size,[fill]) + +creates and returns array of a specified size. If the optional parameter fill is specified its value will be used to fill the new array's slots. If the fill parameter is omitted, null is used instead. + +.. js:function:: seterrorhandler(func) + + +sets the runtime error handler + +.. js:function:: callee() + + +returns the currently running closure + +.. js:function:: setdebughook(hook_func) + + +sets the debug hook + +.. js:function:: enabledebuginfo(enable) + +enable/disable the debug line information generation at compile time. enable != null enables. enable == null disables. + +.. js:function:: getroottable() + +returns the root table of the VM. + +.. js:function:: setroottable(table) + +sets the root table of the VM. And returns the previous root table. + +.. js:function:: getconsttable() + +returns the const table of the VM. + +.. js:function:: setconsttable(table) + +sets the const table of the VM; returns the previous const table. + +.. js:function:: assert(exp, [message]) + +throws an exception if exp is null or false. Throws "assertion failed" string by default, or message if specified. + +.. js:function:: print(x) + +prints x to the standard output + +.. js:function:: error(x) + +prints x in the standard error output + +.. js:function:: compilestring(string,[buffername]) + +compiles a string containing a squirrel script into a function and returns it:: + + local compiledscript=compilestring("::print(\"ciao\")"); + //run the script + compiledscript(); + +.. js:function:: collectgarbage() + + Runs the garbage collector and returns the number of reference cycles found (and deleted). This function only works on garbage collector builds. + +.. js:function:: resurrectunreachable() + +Runs the garbage collector and returns an array containing all unreachable object found. If no unreachable object is found, null is returned instead. This function is meant to help debugging reference cycles. This function only works on garbage collector builds. + +.. js:function:: type(obj) + +return the 'raw' type of an object without invoking the metamethod '_typeof'. + +.. js:function:: getstackinfos(level) + +returns the stack informations of a given call stack level. returns a table formatted as follow: :: + + { + func="DoStuff", //function name + + src="test.nut", //source file + + line=10, //line number + + locals = { //a table containing the local variables + + a=10, + + testy="I'm a string" + } + } + +level = 0 is getstackinfos() itself! level = 1 is the current function, level = 2 is the caller of the current function, and so on. If the stack level doesn't exist the function returns null. + +.. js:function:: newthread(threadfunc) + +creates a new cooperative thread object(coroutine) and returns it + +.. js:data:: _versionnumber_ + +integer values describing the version of VM and compiler. e.g. for Squirrel 3.0.1 this value will be 301 + +.. js:data:: _version_ + +string values describing the version of VM and compiler. + +.. js:data:: _charsize_ + +size in bytes of the internal VM representation for characters(1 for ASCII builds 2 for UNICODE builds). + +.. js:data:: _intsize_ + +size in bytes of the internal VM representation for integers(4 for 32bits builds 8 for 64bits builds). + +.. js:data:: _floatsize_ + +size in bytes of the internal VM representation for floats(4 for single precision builds 8 for double precision builds). + +----------------- +Default delegates +----------------- + +Except null and userdata every squirrel object has a default delegate containing a set of functions to manipulate and retrieve information from the object itself. + +^^^^^^^^ +Integer +^^^^^^^^ + +.. js:function:: integer.tofloat() + +convert the number to float and returns it + + +.. js:function:: integer.tostring() + +converts the number to string and returns it + + +.. js:function:: integer.tointeger() + +dummy function; returns the value of the integer. + + +.. js:function:: integer.tochar() + +returns a string containing a single character represented by the integer. + + +.. js:function:: integer.weakref() + +dummy function; returns the integer itself. + +^^^^^ +Float +^^^^^ + +.. js:function:: float.tofloat() + +returns the value of the float(dummy function) + + +.. js:function:: float.tointeger() + +converts the number to integer and returns it + + +.. js:function:: float.tostring() + +converts the number to string and returns it + + +.. js:function:: float.tochar() + +returns a string containing a single character represented by the integer part of the float. + + +.. js:function:: float.weakref() + +dummy function; returns the float itself. + +^^^^ +Bool +^^^^ + +.. js:function:: bool.tofloat() + +returns 1.0 for true 0.0 for false + + +.. js:function:: bool.tointeger() + +returns 1 for true 0 for false + + +.. js:function:: bool.tostring() + +returns "true" for true and "false" for false + + +.. js:function:: bool.weakref() + +dummy function; returns the bool itself. + +^^^^^^ +String +^^^^^^ + +.. js:function:: string.len() + +returns the string length + + +.. js:function:: string.tointeger([base]) + +Converts the string to integer and returns it. An optional parameter base can be specified--if a base is not specified, it defaults to base 10. + + +.. js:function:: string.tofloat() + +converts the string to float and returns it + + +.. js:function:: string.tostring() + +returns the string (really, a dummy function) + + +.. js:function:: string.slice(start,[end]) + +returns a section of the string as new string. Copies from start to the end (not included). If start is negative the index is calculated as length + start, if end is negative the index is calculated as length + end. If end is omitted end is equal to the string length. + + +.. js:function:: string.find(substr,[startidx]) + +Searches a sub string (substr) starting from the index startidx and returns the index of its first occurrence. If startidx is omitted the search operation starts from the beginning of the string. The function returns null if substr is not found. + + +.. js:function:: string.tolower() + +returns a lowercase copy of the string. + + +.. js:function:: string.toupper() + +returns a uppercase copy of the string. + + +.. js:function:: string.weakref() + +returns a weak reference to the object. + +^^^^^ +Table +^^^^^ + +.. js:function:: table.len() + +returns the number of slots contained in a table + + +.. js:function:: table.rawget(key) + +tries to get a value from the slot 'key' without employing delegation + + +.. js:function:: table.rawset(key,val) + +Sets the slot 'key' with the value 'val' without employing delegation. If the slot does not exists, it will be created. Returns table itself. + + +.. js:function:: table.rawdelete() + +Deletes the slot key without employing delegation and returns its value. If the slot does not exists, returns null. + + +.. js:function:: table.rawin(key) + +returns true if the slot 'key' exists. the function has the same effect as the operator 'in' but does not employ delegation. + + +.. js:function:: table.weakref() + +returns a weak reference to the object. + + +.. js:function:: table.tostring() + +Tries to invoke the _tostring metamethod. If that fails, it returns "(table : pointer)". + + +.. js:function:: table.clear() + +removes all the slots from the table. Returns table itself. + + +.. js:function:: table.setdelegate(table) + +Sets the delegate of the table. To remove a delegate, 'null' must be passed to the function. The function returns the table itself (e.g. a.setdelegate(b) -- in this case 'a' is the return value). + + +.. js:function:: table.getdelegate() + +returns the table's delegate or null if no delegate was set. + + +.. js:function:: table.filter(func(key,val)) + +Creates a new table with all values that pass the test implemented by the provided function. In detail, it creates a new table, invokes the specified function for each key-value pair in the original table; if the function returns 'true', then the value is added to the newly created table at the same key. + +.. js:function:: table.keys() + +returns an array containing all the keys of the table slots. + +.. js:function:: table.values() + +returns an array containing all the values of the table slots. + +^^^^^^ +Array +^^^^^^ + +.. js:function:: array.len() + +returns the length of the array + + +.. js:function:: array.append(val) + +appends the value 'val' at the end of the array. Returns array itself. + + +.. js:function:: array.push(val) + +appends the value 'val' at the end of the array. Returns array itself. + + +.. js:function:: array.extend(array) + +Extends the array by appending all the items in the given array. Returns array itself. + + +.. js:function:: array.pop() + +removes a value from the back of the array and returns it. + + +.. js:function:: array.top() + +returns the value of the array with the higher index + + +.. js:function:: array.insert(idx,val) + +inserts the value 'val' at the position 'idx' in the array. Returns array itself. + + +.. js:function:: array.remove(idx) + +removes the value at the position 'idx' in the array and returns its value. + + +.. js:function:: array.resize(size,[fill]) + +Resizes the array. If the optional parameter 'fill' is specified, its value will be used to fill the new array's slots when the size specified is bigger than the previous size. If the fill parameter is omitted, null is used instead. Returns array itself. + + +.. js:function:: array.sort([compare_func]) + +Sorts the array in-place. A custom compare function can be optionally passed. The function prototype as to be the following.:: + + function custom_compare(a,b) + { + if(a>b) return 1 + else if(a :: + + arr.sort(@(a,b) a <=> b); + +Returns array itself. + +.. js:function:: array.reverse() + +reverse the elements of the array in place. Returns array itself. + + +.. js:function:: array.slice(start,[end]) + +Returns a section of the array as new array. Copies from start to the end (not included). If start is negative the index is calculated as length + start, if end is negative the index is calculated as length + end. If end is omitted end is equal to the array length. + + +.. js:function:: array.weakref() + +returns a weak reference to the object. + + +.. js:function:: array.tostring() + +returns the string "(array : pointer)". + + +.. js:function:: array.clear() + +removes all the items from the array + + +.. js:function:: array.map(func(item_value, [item_index], [array_ref])) + +Creates a new array of the same size. For each element in the original array invokes the function 'func' and assigns the return value of the function to the corresponding element of the newly created array. +Provided func can accept up to 3 arguments: array item value (required), array item index (optional), reference to array itself (optional). + + +.. js:function:: array.apply(func([item_value, [item_index], [array_ref])) + +for each element in the array invokes the function 'func' and replace the original value of the element with the return value of the function. + + +.. js:function:: array.reduce(func(prevval,curval), [initializer]) + +Reduces an array to a single value. For each element in the array invokes the function 'func' passing +the initial value (or value from the previous callback call) and the value of the current element. +The return value of the function is then used as 'prevval' for the next element. +If the optional initializer is present, it is placed before the items of the array in the calculation, +and serves as a default when the sequence is empty. +If initializer is not given then for sequence contains only one item, reduce() returns the first item, +and for empty sequence returns null. + +Given an sequence with 2 or more elements (including initializer) calls the function with the first two elements as the parameters, +gets that result, then calls the function with that result and the third element, gets that result, +calls the function with that result and the fourth parameter and so on until all element have been processed. +Finally, returns the return value of the last invocation of func. + + +.. js:function:: array.filter(func(index,val)) + +Creates a new array with all elements that pass the test implemented by the provided function. In detail, it creates a new array, for each element in the original array invokes the specified function passing the index of the element and it's value; if the function returns 'true', then the value of the corresponding element is added on the newly created array. + + +.. js:function:: array.find(value) + +Performs a linear search for the value in the array. Returns the index of the value if it was found null otherwise. + +^^^^^^^^ +Function +^^^^^^^^ + +.. js:function:: function.call(_this,args...) + +calls the function with the specified environment object('this') and parameters + + +.. js:function:: function.pcall(_this,args...) + +calls the function with the specified environment object('this') and parameters, this function will not invoke the error callback in case of failure(pcall stays for 'protected call') + + +.. js:function:: function.acall(array_args) + +calls the function with the specified environment object('this') and parameters. The function accepts an array containing the parameters that will be passed to the called function.Where array_args has to contain the required 'this' object at the [0] position. + + +.. js:function:: function.pacall(array_args) + +calls the function with the specified environment object('this') and parameters. The function accepts an array containing the parameters that will be passed to the called function.Where array_args has to contain the required 'this' object at the [0] position. This function will not invoke the error callback in case of failure(pacall stays for 'protected array call') + + +.. js:function:: function.weakref() + +returns a weak reference to the object. + + +.. js:function:: function.tostring() + +returns the string "(closure : pointer)". + + +.. js:function:: function.setroot(table) + +sets the root table of a closure + + +.. js:function:: function.getroot() + +returns the root table of the closure + + +.. js:function:: function.bindenv(env) + +clones the function(aka closure) and bind the environment object to it(table,class or instance). the this parameter of the newly create function will always be set to env. Note that the created function holds a weak reference to its environment object so cannot be used to control its lifetime. + + +.. js:function:: function.getinfos() + +returns a table containing informations about the function, like parameters, name and source name; :: + + //the data is returned as a table is in form + //pure squirrel function + { + native = false + name = "zefuncname" + src = "/somthing/something.nut" + parameters = ["a","b","c"] + defparams = [1,"def"] + varargs = 2 + } + //native C function + { + native = true + name = "zefuncname" + paramscheck = 2 + typecheck = [83886082,83886384] //this is the typemask (see C defines OT_INTEGER,OT_FLOAT etc...) + } + + + +^^^^^ +Class +^^^^^ + +.. js:function:: class.instance() + +returns a new instance of the class. this function does not invoke the instance constructor. The constructor must be explicitly called (eg. class_inst.constructor(class_inst) ). + + +.. js:function:: class.getattributes(membername) + +returns the attributes of the specified member. if the parameter member is null the function returns the class level attributes. + + +.. js:function:: class.setattributes(membername,attr) + +sets the attribute of the specified member and returns the previous attribute value. if the parameter member is null the function sets the class level attributes. + + +.. js:function:: class.rawin(key) + +returns true if the slot 'key' exists. the function has the same effect as the operator 'in' but does not employ delegation. + + +.. js:function:: class.weakref() + +returns a weak reference to the object. + + +.. js:function:: class.tostring() + +returns the string "(class : pointer)". + + +.. js:function:: class.rawget(key) + +tries to get a value from the slot 'key' without employing delegation + + +.. js:function:: class.rawset(key,val) + +sets the slot 'key' with the value 'val' without employing delegation. If the slot does not exists, it will be created. + + +.. js:function:: class.newmember(key,val,[attrs],[bstatic]) + +sets/adds the slot 'key' with the value 'val' and attributes 'attrs' and if present invokes the _newmember metamethod. If bstatic is true the slot will be added as static. If the slot does not exists , it will be created. + + +.. js:function:: class.rawnewmember(key,val,[attrs],[bstatic]) + +sets/adds the slot 'key' with the value 'val' and attributes 'attrs'. If bstatic is true the slot will be added as static. If the slot does not exist, it will be created. It doesn't invoke any metamethod. + +^^^^^^^^^^^^^^ +Class Instance +^^^^^^^^^^^^^^ + +.. js:function:: instance.getclass() + +returns the class that created the instance. + + +.. js:function:: instance.rawin(key) + + :param key: ze key + +returns true if the slot 'key' exists. the function has the same effect as the operator 'in' but does not employ delegation. + + +.. js:function:: instance.weakref() + +returns a weak reference to the object. + + +.. js:function:: instance.tostring() + +tries to invoke the _tostring metamethod, if failed. returns "(instance : pointer)". + + +.. js:function:: instance.rawget(key) + +tries to get a value from the slot 'key' without employing delegation + + +.. js:function:: instance.rawset(key,val) + +sets the slot 'key' with the value 'val' without employing delegation. If the slot does not exists, it will be created. + +^^^^^^^^^^^^^^ +Generator +^^^^^^^^^^^^^^ + + +.. js:function:: generator.getstatus() + +returns the status of the generator as string : "running", "dead" or "suspended". + + +.. js:function:: generator.weakref() + +returns a weak reference to the object. + + +.. js:function:: generator.tostring() + +returns the string "(generator : pointer)". + +^^^^^^^^^^^^^^ +Thread +^^^^^^^^^^^^^^ + +.. js:function:: thread.call(...) + +starts the thread with the specified parameters + + +.. js:function:: thread.wakeup([wakeupval]) + +wakes up a suspended thread, accepts a optional parameter that will be used as return value for the function that suspended the thread(usually suspend()) + + +.. js:function:: thread.wakeupthrow(objtothrow,[propagateerror = true]) + +wakes up a suspended thread, throwing an exception in the awaken thread, throwing the object 'objtothrow'. + + +.. js:function:: thread.getstatus() + +returns the status of the thread ("idle","running","suspended") + + +.. js:function:: thread.weakref() + +returns a weak reference to the object. + + +.. js:function:: thread.tostring() + +returns the string "(thread : pointer)". + + +.. js:function:: thread.getstackinfos(stacklevel) + +returns the stack frame informations at the given stack level (0 is the current function 1 is the caller and so on). + +^^^^^^^^^^^^^^ +Weak Reference +^^^^^^^^^^^^^^ + +.. js:function:: weakreference.ref() + +returns the object that the weak reference is pointing at; null if the object that was point at was destroyed. + + +.. js:function:: weakreference.weakref() + +returns a weak reference to the object. + + +.. js:function:: weakreference.tostring() + +returns the string "(weakref : pointer)". diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/classes.rst b/mp/src/vscript/squirrel/doc/source/reference/language/classes.rst new file mode 100644 index 00000000..81ee4c41 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/classes.rst @@ -0,0 +1,429 @@ +.. _classes: + + +================= +Classes +================= + +.. index:: + single: Classes + +Squirrel implements a class mechanism similar to languages like Java/C++/etc... +however because of its dynamic nature it differs in several aspects. +Classes are first class objects like integer or strings and can be stored in +table slots local variables, arrays and passed as function parameters. + +----------------- +Class Declaration +----------------- + +.. index:: + pair: declaration; Class + single: Class Declaration + +A class object is created through the keyword 'class' . The class object follows +the same declaration syntax of a table(see :ref:`Tables `) with the only difference +of using ';' as optional separator rather than ','. + +For instance: :: + + class Foo { + //constructor + constructor(a) + { + testy = ["stuff",1,2,3,a]; + } + //member function + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //property + testy = null; + + } + +the previous code example is a syntactic sugar for: :: + + Foo <- class { + //constructor + constructor(a) + { + testy = ["stuff",1,2,3,a]; + } + //member function + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //property + testy = null; + + } + +in order to emulate namespaces, it is also possible to declare something like this:: + + //just 2 regular nested tables + FakeNamespace <- { + Utils = {} + } + + class FakeNamespace.Utils.SuperClass { + constructor() + { + ::print("FakeNamespace.Utils.SuperClass") + } + function DoSomething() + { + ::print("DoSomething()") + } + } + + function FakeNamespace::Utils::SuperClass::DoSomethingElse() + { + ::print("FakeNamespace::Utils::SuperClass::DoSomethingElse()") + } + + local testy = FakeNamespace.Utils.SuperClass(); + testy.DoSomething(); + testy.DoSomethingElse(); + +After its declaration, methods or properties can be added or modified by following +the same rules that apply to a table(operator ``<-``).:: + + //adds a new property + Foo.stuff <- 10; + + //modifies the default value of an existing property + Foo.testy <- "I'm a string"; + + //adds a new method + function Foo::DoSomething(a,b) + { + return a+b; + } + +After a class is instantiated is no longer possible to add new properties however is possible to add or replace methods. + +^^^^^^^^^^^^^^^^ +Static variables +^^^^^^^^^^^^^^^^ + +.. index:: + pair: static variables; Class + single: Static variables + +Squirrel's classes support static member variables. A static variable shares its value +between all instances of the class. Statics are declared by prefixing the variable declaration +with the keyword ``static``; the declaration must be in the class body. + +.. note:: Statics are read-only. + +:: + + class Foo { + constructor() + { + //..stuff + } + name = "normal variable"; + //static variable + static classname = "The class name is foo"; + }; + +^^^^^^^^^^^^^^^^ +Class Attributes +^^^^^^^^^^^^^^^^ + +.. index:: + pair: attributes; Class + single: Class Attributes + +Classes allow to associate attributes to it's members. Attributes are a form of metadata +that can be used to store application specific informations, like documentations +strings, properties for IDEs, code generators etc... +Class attributes are declared in the class body by preceding the member declaration and +are delimited by the symbol ````. +Here an example: :: + + class Foo { + //attributes of PrintTesty + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //attributes of testy + testy = null; + } + +Attributes are, matter of fact, a table. Squirrel uses ```` syntax +instead of curly brackets ``{}`` for the attribute declaration to increase readability. + +This means that all rules that apply to tables apply to attributes. + +Attributes can be retrieved through the built-in function ``classobj.getattributes(membername)`` (see built-in functions). +and can be modified/added through the built-in function ``classobj.setattributes(membername,val)``. + +the following code iterates through the attributes of all Foo members.:: + + foreach(member,val in Foo) + { + ::print(member+"\n"); + local attr; + if((attr = Foo.getattributes(member)) != null) { + foreach(i,v in attr) + { + ::print("\t"+i+" = "+(typeof v)+"\n"); + } + } + else { + ::print("\t\n") + } + } + +----------------- +Class Instances +----------------- + +.. index:: + pair: instances; Class + single: Class Instances + +The class objects inherits several of the table's feature with the difference that multiple instances of the +same class can be created. +A class instance is an object that share the same structure of the table that created it but +holds is own values. +Class *instantiation* uses function notation. +A class instance is created by calling a class object. Can be useful to imagine a class like a function +that returns a class instance.:: + + //creates a new instance of Foo + local inst = Foo(); + +When a class instance is created its member are initialized *with the same value* specified in the +class declaration. The values are copied verbatim, *no cloning is performed* even if the value is a container or a class instances. + +.. note:: FOR C# and Java programmers: + + Squirrel doesn't clone member's default values nor executes the member declaration for each instance(as C# or java). + + So consider this example: :: + + class Foo { + myarray = [1,2,3] + mytable = {} + } + + local a = Foo(); + local b = Foo(); + + In the snippet above both instances will refer to the same array and same table.To achieve what a C# or Java programmer would + expect, the following approach should be taken. :: + + class Foo { + myarray = null + mytable = null + constructor() + { + myarray = [1,2,3] + mytable = {} + } + } + + local a = Foo(); + local b = Foo(); + +When a class defines a method called 'constructor', the class instantiation operation will +automatically invoke it for the newly created instance. +The constructor method can have parameters, this will impact on the number of parameters +that the *instantiation operation* will require. +Constructors, as normal functions, can have variable number of parameters (using the parameter ``...``).:: + + class Rect { + constructor(w,h) + { + width = w; + height = h; + } + x = 0; + y = 0; + width = null; + height = null; + } + + //Rect's constructor has 2 parameters so the class has to be 'called' + //with 2 parameters + local rc = Rect(100,100); + +After an instance is created, its properties can be set or fetched following the +same rules that apply to tables. Methods cannot be set. + +Instance members cannot be removed. + +The class object that created a certain instance can be retrieved through the built-in function +``instance.getclass()`` (see :ref:`built-in functions `) + +The operator ``instanceof`` tests if a class instance is an instance of a certain class.:: + + local rc = Rect(100,100); + if(rc instanceof ::Rect) { + ::print("It's a rect"); + } + else { + ::print("It isn't a rect"); + } + +.. note:: Since Squirrel 3.x instanceof doesn't throw an exception if the left expression is not a class, it simply fails + +-------------- +Inheritance +-------------- + +.. index:: + pair: inheritance; Class + single: Inheritance + +Squirrel's classes support single inheritance by adding the keyword ``extends``, followed +by an expression, in the class declaration. +The syntax for a derived class is the following: :: + + class SuperFoo extends Foo { + function DoSomething() { + ::print("I'm doing something"); + } + } + +When a derived class is declared, Squirrel first copies all base's members in the +new class then proceeds with evaluating the rest of the declaration. + +A derived class inherit all members and properties of it's base, if the derived class +overrides a base function the base implementation is shadowed. +It's possible to access a overridden method of the base class by fetching the method from +through the 'base' keyword. + +Here an example: :: + + class Foo { + function DoSomething() { + ::print("I'm the base"); + } + }; + + class SuperFoo extends Foo { + //overridden method + function DoSomething() { + //calls the base method + base.DoSomething(); + ::print("I'm doing something"); + } + } + +Same rule apply to the constructor. The constructor is a regular function (apart from being automatically invoked on construction).:: + + class BaseClass { + constructor() + { + ::print("Base constructor\n"); + } + } + + class ChildClass extends BaseClass { + constructor() + { + base.constructor(); + ::print("Child constructor\n"); + } + } + + local test = ChildClass(); + +The base class of a derived class can be retrieved through the built-in method ``getbase()``.:: + + local thebaseclass = SuperFoo.getbase(); + +Note that because methods do not have special protection policies when calling methods of the same +objects, a method of a base class that calls a method of the same class can end up calling a overridden method of the derived class. + +A method of a base class can be explicitly invoked by a method of a derived class though the keyword ``base`` (as in base.MyMethod() ).:: + + class Foo { + function DoSomething() { + ::print("I'm the base"); + } + function DoIt() + { + DoSomething(); + } + }; + + class SuperFoo extends Foo { + //overridden method + function DoSomething() { + ::print("I'm the derived"); + + } + function DoIt() { + base.DoIt(); + } + } + + //creates a new instance of SuperFoo + local inst = SuperFoo(); + + //prints "I'm the derived" + inst.DoIt(); + +---------------------- +Metamethods +---------------------- + +.. index:: + pair: metamethods; Class + single: Class metamethods + +Class instances allow the customization of certain aspects of the +their semantics through metamethods(see see :ref:`Metamethods `). +For C++ programmers: "metamethods behave roughly like overloaded operators". +The metamethods supported by classes are ``_add, _sub, _mul, _div, _unm, _modulo, +_set, _get, _typeof, _nexti, _cmp, _call, _delslot, _tostring`` + +Class objects instead support only 2 metamethods : ``_newmember`` and ``_inherited`` + +the following example show how to create a class that implements the metamethod ``_add``.:: + + class Vector3 { + constructor(...) + { + if(vargv.len() >= 3) { + x = vargv[0]; + y = vargv[1]; + z = vargv[2]; + } + } + function _add(other) + { + return ::Vector3(x+other.x,y+other.y,z+other.z); + } + + x = 0; + y = 0; + z = 0; + } + + local v0 = Vector3(1,2,3) + local v1 = Vector3(11,12,13) + local v2 = v0 + v1; + ::print(v2.x+","+v2.y+","+v2.z+"\n"); + +Since version 2.1, classes support 2 metamethods ``_inherited`` and ``_newmember``. +``_inherited`` is invoked when a class inherits from the one that implements ``_inherited``. +``_newmember`` is invoked for each member that is added to the class(at declaration time). diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst b/mp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst new file mode 100644 index 00000000..77fd9bc2 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst @@ -0,0 +1,97 @@ +.. _constants_and_enumerations: + + +======================== +Constants & Enumerations +======================== + +.. index:: + single: Constants & Enumerations + + + +Squirrel allows to bind constant values to an identifier that will be evaluated compile-time. +This is achieved though constants and Enumerations. + +--------------- +Constants +--------------- + +.. index:: + single: Constants + +Constants bind a specific value to an identifier. Constants are similar to +global values, except that they are evaluated compile time and their value cannot be changed. + +constants values can only be integers, floats or string literals. No expression are allowed. +are declared with the following syntax.:: + + const foobar = 100; + const floatbar = 1.2; + const stringbar = "I'm a constant string"; + +constants are always globally scoped, from the moment they are declared, any following code +can reference them. +Constants will shadow any global slot with the same name( the global slot will remain visible by using the ``::`` syntax).:: + + local x = foobar * 2; + +--------------- +Enumerations +--------------- + +.. index:: + single: Enumerations + +As Constants, Enumerations bind a specific value to a name. Enumerations are also evaluated at compile time +and their value cannot be changed. + +An enum declaration introduces a new enumeration into the program. +Enumeration values can only be integers, floats or string literals. No expression are allowed.:: + + enum Stuff { + first, //this will be 0 + second, //this will be 1 + third //this will be 2 + } + +or:: + + enum Stuff { + first = 10 + second = "string" + third = 1.2 + } + +An enum value is accessed in a manner that's similar to accessing a static class member. +The name of the member must be qualified with the name of the enumeration, for example ``Stuff.second`` +Enumerations will shadow any global slot with the same name( the global slot will remain visible by using the ``::`` syntax).:: + + local x = Stuff.first * 2; + +-------------------- +Implementation notes +-------------------- + +Enumerations and Constants are a compile-time feature. Only integers, string and floats can be declared as const/enum; +No expressions are allowed(because they would have to be evaluated compile time). +When a const or an enum is declared, it is added compile time to the ``consttable``. This table is stored in the VM shared state +and is shared by the VM and all its threads. +The ``consttable`` is a regular squirrel table; In the same way as the ``roottable`` +it can be modified runtime. +You can access the ``consttable`` through the built-in function ``getconsttable()`` +and also change it through the built-in function ``setconsttable()`` + +here some example: :: + + //creates a constant + getconsttable()["something"] <- 10" + //creates an enumeration + getconsttable()["somethingelse"] <- { a = "10", c = "20", d = "200"}; + //deletes the constant + delete getconsttable()["something"] + //deletes the enumeration + delete getconsttable()["somethingelse"] + +This system allows to procedurally declare constants and enumerations, it is also possible to assign any squirrel type +to a constant/enumeration(function,classes etc...). However this will make serialization of a code chunk impossible. diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst b/mp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst new file mode 100644 index 00000000..843ed8b3 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst @@ -0,0 +1,162 @@ +.. _datatypes_and_values: + +===================== +Values and Data types +===================== + +While Squirrel is a dynamically typed language and variables do not +have a type, different operations may interpret the variable as +containing a type. Squirrel's basic types are integer, float, string, +null, table, array, function, generator, class, instance, bool, thread +and userdata. + +.. _userdata-index: + +-------- +Integer +-------- + +An Integer represents a 32 bit (or better) signed number.:: + + local a = 123 //decimal + local b = 0x0012 //hexadecimal + local c = 075 //octal + local d = 'w' //char code + +-------- +Float +-------- + +A float represents a 32 bit (or better) floating point number.:: + + local a=1.0 + local b=0.234 + +-------- +String +-------- + +Strings are an immutable sequence of characters. In order to modify a +string is it necessary create a new one. + +Squirrel's strings are similar to strings in C or C++. They are +delimited by quotation marks(``"``) and can contain escape +sequences (``\t``, ``\a``, ``\b``, ``\n``, ``\r``, ``\v``, ``\f``, +``\\``, ``\"``, ``\'``, ``\0``, ``\x``, ``\u`` and +``\U``). + +Verbatim string literals do not interpret escape sequences. They begin +with ``@"`` and end with the matching quote. Verbatim string literals +also can extend over a line break. If they do, they include any white +space characters between the quotes: :: + + local a = "I'm a wonderful string\n" + // has a newline at the end of the string + local x = @"I'm a verbatim string\n" + // the \n is literal, similar to "\\n" in a regular string. + +However, a doubled quotation mark within a verbatim string is replaced +by a single quotation mark: :: + + local multiline = @" + this is a multiline string + it will ""embed"" all the new line + characters + " + +-------- +Null +-------- + +The null value is a primitive value that represents the null, empty, or non-existent +reference. The type Null has exactly one value, called null.:: + + local a = null + +-------- +Bool +-------- + +Bool is a double-valued (Boolean) data type. Its literals are ``true`` +and ``false``. A bool value expresses the validity of a condition +(tells whether the condition is true or false).:: + + local a = true; + +-------- +Table +-------- + +Tables are associative containers implemented as a set of key/value pairs +called slots.:: + + local t={} + local test= + { + a=10 + b=function(a) { return a+1; } + } + +-------- +Array +-------- + +Arrays are simple sequence of objects. Their size is dynamic and their index always starts from 0.:: + + local a = ["I'm","an","array"] + local b = [null] + b[0] = a[2]; + +-------- +Function +-------- + +Functions are similar to those in other C-like languages with a few key differences (see below). + +-------- +Class +-------- + +Classes are associative containers implemented as sets of key/value +pairs. Classes are created through a 'class expression' or a 'class +statement'. class members can be inherited from another class object +at creation time. After creation, members can be added until an +instance of the class is created. + +-------------- +Class Instance +-------------- + +Class instances are created by calling a *class object*. Instances, as +tables, are implemented as sets of key/value pairs. Instance members +cannot be dynamically added or removed; however the value of the +members can be changed. + +--------- +Generator +--------- + +Generators are functions that can be suspended with the statement +'yield' and resumed later (see :ref:`Generators `). + +--------- +Userdata +--------- + +Userdata objects are blobs of memory or pointers defined by the host +application but stored within Squirrel variables (See :ref:`Userdata +and UserPointers `). + +--------- +Thread +--------- + +Threads are objects representing a cooperative thread of execution, +also known as coroutines. + +-------------- +Weak Reference +-------------- + +Weak References are objects that point to another (non-scalar) object but do not own a strong reference to it. +(See :ref:`Weak References `). diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/delegation.rst b/mp/src/vscript/squirrel/doc/source/reference/language/delegation.rst new file mode 100644 index 00000000..63607d5c --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/delegation.rst @@ -0,0 +1,35 @@ +.. _delegation: + + +======================== +Delegation +======================== + +.. index:: + single: Delegation + +Squirrel supports implicit delegation. Every table or userdata can have a parent table +(delegate). A parent table is a normal table that allows the definition of special behaviors +for his child. +When a table (or userdata) is indexed with a key that doesn't correspond to one of its +slots, the interpreter automatically delegates the get (or set) operation to its parent.:: + + Entity <- { + } + + function Entity::DoStuff() + { + ::print(_name); + } + + local newentity = { + _name="I'm the new entity" + } + newentity.setdelegate(Entity) + + newentity.DoStuff(); //prints "I'm the new entity" + +The delegate of a table can be retreived through built-in method ``table.getdelegate()``.:: + + local thedelegate = newentity.getdelegate(); + diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst b/mp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst new file mode 100644 index 00000000..47ef3209 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst @@ -0,0 +1,95 @@ +.. _executioncontext: + +======================= +Execution Context +======================= + +.. index:: + single: execution context + +The execution context is the union of the function stack frame and the function +environment object(this) and the function root(root table). +The stack frame is the portion of stack where the local variables declared in its body are +stored. +The environment object is an implicit parameter that is automatically passed by the +function caller (see see :ref:`functions `). +The root table is a table associated to the function during its creation. +The root table value of a function is the root table of the VM at the function creation. +The root table of function can also be changed after creation with closure.setroot(). +During the execution, the body of a function can only transparently refer to his execution +context. +This mean that a single identifier can refer to a local variable, to an environment object slot +or to the slot of the closure root table; +The environment object can be explicitly accessed by the keyword ``this``. +The closure root table can be explicitly accessed through the operator ``::`` (see :ref:`Variables `). + +.. _variables: + +----------------- +Variables +----------------- + +There are two types of variables in Squirrel, local variables and tables/arrays slots. +Because global variables(variables stored in the root of a closure) are stored in a table, they are table slots. + +A single identifier refers to a local variable or a slot in the environment object.:: + + derefexp := id; + +:: + + _table["foo"] + _array[10] + +with tables we can also use the '.' syntax:: + + derefexp := exp '.' id + +:: + + _table.foo + +Squirrel first checks if an identifier is a local variable (function arguments are local +variables) if not looks up the environment object (this) and finally looks up +to the closure root. + +For instance::: + + function testy(arg) + { + local a=10; + print(a); + return arg; + } + +in this case 'foo' will be equivalent to 'this.foo' or this["foo"]. + +Global variables are stored in a table called the root table. Usually in the global scope the +environment object is the root table, but to explicitly access the closure root of the function from +another scope, the slot name must be prefixed with ``'::'`` (``::foo``). + +For instance::: + + function testy(arg) + { + local a=10; + return arg+::foo; + } + +accesses the variable 'foo' in the closure root table. + +Since Squirrel 3.1 each function has a weak reference to a specific root table, this can differ from the current VM root table.:: + + function test() { + foo = 10; + } + +is equivalent to write:: + + function test() { + if("foo" in this) { + this.foo = 10; + }else { + ::foo = 10; + } + } diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/expressions.rst b/mp/src/vscript/squirrel/doc/source/reference/language/expressions.rst new file mode 100644 index 00000000..9e75c7e7 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/expressions.rst @@ -0,0 +1,374 @@ +.. _expressions: + + +================= +Expressions +================= + +.. index:: + single: Expressions + +---------------- +Assignment +---------------- + +.. index:: + single: assignment(=) + single: new slot(<-) + +:: + + exp := derefexp '=' exp + exp:= derefexp '<-' exp + +squirrel implements 2 kind of assignment: the normal assignment(=):: + + a = 10; + +and the "new slot" assignment.:: + + a <- 10; + +The new slot expression allows to add a new slot into a table(see :ref:`Tables `). If the slot +already exists in the table it behaves like a normal assignment. + +---------------- +Operators +---------------- + +.. index:: + single: Operators + +^^^^^^^^^^^^^ +?: Operator +^^^^^^^^^^^^^ + +.. index:: + pair: ?: Operator; Operators + +:: + + exp := exp_cond '?' exp1 ':' exp2 + +conditionally evaluate an expression depending on the result of an expression. + +^^^^^^^^^^^^^ +Arithmetic +^^^^^^^^^^^^^ + +.. index:: + pair: Arithmetic Operators; Operators + +:: + + exp:= 'exp' op 'exp' + +Squirrel supports the standard arithmetic operators ``+, -, *, / and %``. +Other than that is also supports compact operators (``+=,-=,*=,/=,%=``) and +increment and decrement operators(++ and --);:: + + a += 2; + //is the same as writing + a = a + 2; + x++ + //is the same as writing + x = x + 1 + +All operators work normally with integers and floats; if one operand is an integer and one +is a float the result of the expression will be float. +The + operator has a special behavior with strings; if one of the operands is a string the +operator + will try to convert the other operand to string as well and concatenate both +together. For instances and tables, ``_tostring`` is invoked. + +^^^^^^^^^^^^^ +Relational +^^^^^^^^^^^^^ + +.. index:: + pair: Relational Operators; Operators + +:: + + exp:= 'exp' op 'exp' + +Relational operators in Squirrel are : ``==, <, <=, <, <=, !=`` + +These operators return true if the expression is false and a value different than true if the +expression is true. Internally the VM uses the integer 1 as true but this could change in +the future. + +^^^^^^^^^^^^^^ +3 ways compare +^^^^^^^^^^^^^^ + +.. index:: + pair: 3 ways compare operator; Operators + +:: + + exp:= 'exp' <=> 'exp' + +the 3 ways compare operator <=> compares 2 values A and B and returns an integer less than 0 +if A < B, 0 if A == B and an integer greater than 0 if A > B. + +^^^^^^^^^^^^^^ +Logical +^^^^^^^^^^^^^^ + +.. index:: + pair: Logical operators; Operators + +:: + + exp := exp op exp + exp := '!' exp + +Logical operators in Squirrel are : ``&&, ||, !`` + +The operator ``&&`` (logical and) returns null if its first argument is null, otherwise returns +its second argument. +The operator ``||`` (logical or) returns its first argument if is different than null, otherwise +returns the second argument. + +The '!' operator will return null if the given value to negate was different than null, or a +value different than null if the given value was null. + +^^^^^^^^^^^^^^^ +in operator +^^^^^^^^^^^^^^^ + +.. index:: + pair: in operator; Operators + +:: + + exp:= keyexp 'in' tableexp + +Tests the existence of a slot in a table. +Returns true if *keyexp* is a valid key in *tableexp* :: + + local t= + { + foo="I'm foo", + [123]="I'm not foo" + } + + if("foo" in t) dostuff("yep"); + if(123 in t) dostuff(); + +^^^^^^^^^^^^^^^^^^^ +instanceof operator +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: instanceof operator; Operators + +:: + + exp:= instanceexp 'instanceof' classexp + +Tests if a class instance is an instance of a certain class. +Returns true if *instanceexp* is an instance of *classexp*. + +^^^^^^^^^^^^^^^^^^^ +typeof operator +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: typeof operator; Operators + +:: + + exp:= 'typeof' exp + +returns the type name of a value as string.:: + + local a={},b="squirrel" + print(typeof a); //will print "table" + print(typeof b); //will print "string" + +^^^^^^^^^^^^^^^^^^^ +Comma operator +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: Comma operator; Operators + +:: + + exp:= exp ',' exp + +The comma operator evaluates two expression left to right, the result of the operator is +the result of the expression on the right; the result of the left expression is discarded.:: + + local j=0,k=0; + for(local i=0; i<10; i++ , j++) + { + k = i + j; + } + local a,k; + a = (k=1,k+2); //a becomes 3 + +^^^^^^^^^^^^^^^^^^^ +Bitwise Operators +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: Bitwise Operators; Operators + +:: + + exp:= 'exp' op 'exp' + exp := '~' exp + +Squirrel supports the standard C-like bitwise operators ``&, |, ^, ~, <<, >>`` plus the unsigned +right shift operator ``>>>``. The unsigned right shift works exactly like the normal right shift operator(``>>``) +except for treating the left operand as an unsigned integer, so is not affected by the sign. Those operators +only work on integer values; passing of any other operand type to these operators will +cause an exception. + +^^^^^^^^^^^^^^^^^^^^^ +Operators precedence +^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: Operators precedence; Operators + ++---------------------------------------+-----------+ +| ``-, ~, !, typeof , ++, --`` | highest | ++---------------------------------------+-----------+ +| ``/, *, %`` | ... | ++---------------------------------------+-----------+ +| ``+, -`` | | ++---------------------------------------+-----------+ +| ``<<, >>, >>>`` | | ++---------------------------------------+-----------+ +| ``<, <=, >, >=, instanceof`` | | ++---------------------------------------+-----------+ +| ``==, !=, <=>`` | | ++---------------------------------------+-----------+ +| ``&`` | | ++---------------------------------------+-----------+ +| ``^`` | | ++---------------------------------------+-----------+ +| ``&&, in`` | | ++---------------------------------------+-----------+ +| ``+=, =, -=, /=, *=, %=`` | ... | ++---------------------------------------+-----------+ +| ``, (comma operator)`` | lowest | ++---------------------------------------+-----------+ + +.. _table_contructor: + +----------------- +Table Constructor +----------------- + +.. index:: + single: Table Contructor + +:: + + tslots := ( 'id' '=' exp | '[' exp ']' '=' exp ) [','] + exp := '{' [tslots] '}' + +Creates a new table.:: + + local a = {} //create an empty table + +A table constructor can also contain slots declaration; With the syntax: :: + + local a = { + slot1 = "I'm the slot value" + } + +An alternative syntax can be:: + + '[' exp1 ']' = exp2 [','] + +A new slot with exp1 as key and exp2 as value is created:: + + local a= + { + [1]="I'm the value" + } + +Both syntaxes can be mixed:: + + local table= + { + a=10, + b="string", + [10]={}, + function bau(a,b) + { + return a+b; + } + } + +The comma between slots is optional. + +^^^^^^^^^^^^^^^^^^^^^^ +Table with JSON syntax +^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: Table with JSON syntax + +Since Squirrel 3.0 is possible to declare a table using JSON syntax(see http://www.wikipedia.org/wiki/JSON). + +the following JSON snippet: :: + + local x = { + "id": 1, + "name": "Foo", + "price": 123, + "tags": ["Bar","Eek"] + } + +is equivalent to the following squirrel code: :: + + local x = { + id = 1, + name = "Foo", + price = 123, + tags = ["Bar","Eek"] + } + +----------------- +clone +----------------- + +.. index:: + single: clone + +:: + + exp:= 'clone' exp + +Clone performs shallow copy of a table, array or class instance (copies all slots in the new object without +recursion). If the source table has a delegate, the same delegate will be assigned as +delegate (not copied) to the new table (see :ref:`Delegation `). + +After the new object is ready the "_cloned" meta method is called (see :ref:`Metamethods `). + +When a class instance is cloned the constructor is not invoked(initializations must rely on ```_cloned``` instead + +----------------- +Array contructor +----------------- + +.. index:: + single: Array constructor + +:: + + exp := '[' [explist] ']' + +Creates a new array.:: + + a <- [] //creates an empty array + +Arrays can be initialized with values during the construction:: + + a <- [1,"string!",[],{}] //creates an array with 4 elements diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/functions.rst b/mp/src/vscript/squirrel/doc/source/reference/language/functions.rst new file mode 100644 index 00000000..f3f98c0b --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/functions.rst @@ -0,0 +1,272 @@ +.. _functions: + + +================= +Functions +================= + +.. index:: + single: Functions + +Functions are first class values like integer or strings and can be stored in table slots, +local variables, arrays and passed as function parameters. +Functions can be implemented in Squirrel or in a native language with calling conventions +compatible with ANSI C. + +-------------------- +Function declaration +-------------------- + +.. index:: + single: Function Declaration + +Functions are declared through the function expression:: + + local a = function(a, b, c) { return a + b - c; } + +or with the syntactic sugar:: + + function ciao(a,b,c) + { + return a+b-c; + } + +that is equivalent to:: + + this.ciao <- function(a,b,c) + { + return a+b-c; + } + +a local function can be declared with this syntactic sugar:: + + local function tuna(a,b,c) + { + return a+b-c; + } + +that is equivalent to:: + + local tuna = function(a,b,c) + { + return a+b-c; + } + +is also possible to declare something like:: + + T <- {} + function T::ciao(a,b,c) + { + return a+b-c; + } + + //that is equivalent to write + + T.ciao <- function(a,b,c) + { + return a+b-c; + } + + //or + + T <- { + function ciao(a,b,c) + { + return a+b-c; + } + } + +^^^^^^^^^^^^^^^^^^ +Default Paramaters +^^^^^^^^^^^^^^^^^^ + +.. index:: + single: Function Default Paramaters + +Squirrel's functions can have default parameters. + +A function with default parameters is declared as follows: :: + + function test(a,b,c = 10, d = 20) + { + .... + } + +when the function *test* is invoked and the parameter c or d are not specified, +the VM autometically assigns the default value to the unspecified parameter. A default parameter can be +any valid squirrel expression. The expression is evaluated at runtime. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Function with variable number of paramaters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: Function with variable number of paramaters + +Squirrel's functions can have variable number of parameters(varargs functions). + +A vararg function is declared by adding three dots (`...`) at the end of its parameter list. + +When the function is called all the extra parameters will be accessible through the *array* +called ``vargv``, that is passed as implicit parameter. + +``vargv`` is a regular squirrel array and can be used accordingly.:: + + function test(a,b,...) + { + for(local i = 0; i< vargv.len(); i++) + { + ::print("varparam "+i+" = "+vargv[i]+"\n"); + } + foreach(i,val in vargv) + { + ::print("varparam "+i+" = "+val+"\n"); + } + } + + test("goes in a","goes in b",0,1,2,3,4,5,6,7,8); + +--------------- +Function calls +--------------- + +.. index:: + single: Function calls + +:: + + exp:= derefexp '(' explist ')' + +The expression is evaluated in this order: derefexp after the explist (arguments) and at +the end the call. + +A function call in Squirrel passes the current environment object *this* as a hidden parameter. +But when the function was immediately indexed from an object, *this* shall be the object +which was indexed, instead. + +If we call a function with the syntax:: + + mytable.foo(x,y) + +the environment object passed to 'foo' as *this* will be 'mytable' (since 'foo' was immediately indexed from 'mytable') + +Whereas with the syntax:: + + foo(x,y) // implicitly equivalent to this.foo(x,y) + +the environment object will be the current *this* (that is, propagated from the caller's *this*). + +It may help to remember the rules in the following way: + + foo(x,y) ---> this.foo(x,y) + table.foo(x,y) ---> call foo with (table,x,y) + +It may also help to consider why it works this way: it's designed to assist with object-oriented style. +When calling 'foo(x,y)' it's assumed you're calling another member of the object (or of the file) and +so should operate on the same object. +When calling 'mytable.foo(x,y)' it's written plainly that you're calling a member of a different object. + +--------------------------------------------- +Binding an environment to a function +--------------------------------------------- + +.. index:: + single: Binding an environment to a function + +while by default a squirrel function call passes as environment object 'this', the object +where the function was indexed from. However, is also possible to statically bind an evironment to a +closure using the built-in method ``closure.bindenv(env_obj)``. +The method bindenv() returns a new instance of a closure with the environment bound to it. +When an environment object is bound to a function, every time the function is invoked, its +'this' parameter will always be the previously bound environent. +This mechanism is useful to implement callbacks systems similar to C# delegates. + +.. note:: The closure keeps a weak reference to the bound environmet object, because of this if + the object is deleted, the next call to the closure will result in a ``null`` + environment object. + +--------------------------------------------- +Lambda Expressions +--------------------------------------------- + +.. index:: + single: Lambda Expressions + +:: + + exp := '@' '(' paramlist ')' exp + +Lambda expressions are a syntactic sugar to quickly define a function that consists of a single expression. +This feature comes handy when functional programming patterns are applied, like map/reduce or passing a compare method to +array.sort(). + +here a lambda expression:: + + local myexp = @(a,b) a + b + +that is equivalent to:: + + local myexp = function(a,b) { return a + b; } + +a more useful usage could be:: + + local arr = [2,3,5,8,3,5,1,2,6]; + arr.sort(@(a,b) a <=> b); + arr.sort(@(a,b) -(a <=> b)); + +that could have been written as:: + + local arr = [2,3,5,8,3,5,1,2,6]; + arr.sort(function(a,b) { return a <=> b; } ); + arr.sort(function(a,b) { return -(a <=> b); } ); + +other than being limited to a single expression lambdas support all features of regular functions. +in fact are implemented as a compile time feature. + +--------------------------------------------- +Free Variables +--------------------------------------------- + +.. index:: + single: Free Variables + +A free variable is a variable external from the function scope as is not a local variable +or parameter of the function. +Free variables reference a local variable from a outer scope. +In the following example the variables 'testy', 'x' and 'y' are bound to the function 'foo'.:: + + local x=10,y=20 + local testy="I'm testy" + + function foo(a,b) + { + ::print(testy); + return a+b+x+y; + } + +A program can read or write a free variable. + +--------------------------------------------- +Tail Recursion +--------------------------------------------- + +.. index:: + single: Tail Recursion + +Tail recursion is a method for partially transforming a recursion in a program into an +iteration: it applies when the recursive calls in a function are the last executed +statements in that function (just before the return). +If this happenes the squirrel interpreter collapses the caller stack frame before the +recursive call; because of that very deep recursions are possible without risk of a stack +overflow.:: + + function loopy(n) + { + if(n>0){ + ::print("n="+n+"\n"); + return loopy(n-1); + } + } + + loopy(1000); + diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/generators.rst b/mp/src/vscript/squirrel/doc/source/reference/language/generators.rst new file mode 100644 index 00000000..6c03de9a --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/generators.rst @@ -0,0 +1,51 @@ +.. _generators: + + +================= +Generators +================= + +.. index:: + single: Generators + +A function that contains a ``yield`` statement is called *'generator function'* . +When a generator function is called, it does not execute the function body, instead it +returns a new suspended generator. +The returned generator can be resumed through the resume statement while it is alive. +The yield keyword, suspends the execution of a generator and optionally returns the +result of an expression to the function that resumed the generator. +The generator dies when it returns, this can happen through an explicit return +statement or by exiting the function body; If an unhandled exception (or runtime error) +occurs while a generator is running, the generator will automatically die. A dead +generator cannot be resumed anymore.:: + + function geny(n) + { + for(local i=1;i<=n;i+=1) + yield i; + return null; + } + + local gtor=geny(10); + local x; + while(x=resume gtor) print(x+"\n"); + +the output of this program will be:: + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + +generators can also be iterated using the foreach statement. When a generator is evaluated +by ``foreach``, the generator will be resumed for each iteration until it returns. The value +returned by the ``return`` statement will be ignored. + +.. note:: A suspended generator will hold a strong reference to all the values stored in it's local variables except the ``this`` + object that is only a weak reference. A running generator hold a strong reference also to the ``this`` object. diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst b/mp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst new file mode 100644 index 00000000..72e23f5c --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst @@ -0,0 +1,154 @@ +.. _lexical_structure: + + +================= +Lexical Structure +================= + +.. index:: single: lexical structure + +----------- +Identifiers +----------- + +.. index:: single: identifiers + +Identifiers start with an alphabetic character or the symbol '_' followed by any number +of alphabetic characters, '_' or digits ([0-9]). Squirrel is a case sensitive language +meaning that the lowercase and uppercase representation of the same alphabetic +character are considered different characters. For instance, "foo", "Foo" and "fOo" are +treated as 3 distinct identifiers. + +----------- +Keywords +----------- + +.. index:: single: keywords + +The following words are reserved and cannot be used as identifiers: + ++------------+------------+-----------+------------+------------+-------------+ +| base | break | case | catch | class | clone | ++------------+------------+-----------+------------+------------+-------------+ +| continue | const | default | delete | else | enum | ++------------+------------+-----------+------------+------------+-------------+ +| extends | for | foreach | function | if | in | ++------------+------------+-----------+------------+------------+-------------+ +| local | null | resume | return | switch | this | ++------------+------------+-----------+------------+------------+-------------+ +| throw | try | typeof | while | yield | constructor | ++------------+------------+-----------+------------+------------+-------------+ +| instanceof | true | false | static | __LINE__ | __FILE__ | ++------------+------------+-----------+------------+------------+-------------+ + +Keywords are covered in detail later in this document. + +----------- +Operators +----------- + +.. index:: single: operators + +Squirrel recognizes the following operators: + ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``!`` | ``!=`` | ``||`` | ``==`` | ``&&`` | ``>=`` | ``<=`` | ``>`` | ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``<=>`` | ``+`` | ``+=`` | ``-`` | ``-=`` | ``/`` | ``/=`` | ``*`` | ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``*=`` | ``%`` | ``%=`` | ``++`` | ``--`` | ``<-`` | ``=`` | ``&`` | ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``^`` | ``|`` | ``~`` | ``>>`` | ``<<`` | ``>>>`` | | | ++----------+----------+----------+----------+----------+----------+----------+----------+ + +------------ +Other tokens +------------ + +.. index:: + single: delimiters + single: other tokens + +Other significant tokens are: + ++----------+----------+----------+----------+----------+----------+ +| ``{`` | ``}`` | ``[`` | ``]`` | ``.`` | ``:`` | ++----------+----------+----------+----------+----------+----------+ +| ``::`` | ``'`` | ``;`` | ``"`` | ``@"`` | | ++----------+----------+----------+----------+----------+----------+ + +----------- +Literals +----------- + +.. index:: + single: literals + single: string literals + single: numeric literals + +Squirrel accepts integer numbers, floating point numbers and string literals. + ++-------------------------------+------------------------------------------+ +| ``34`` | Integer number(base 10) | ++-------------------------------+------------------------------------------+ +| ``0xFF00A120`` | Integer number(base 16) | ++-------------------------------+------------------------------------------+ +| ``0753`` | Integer number(base 8) | ++-------------------------------+------------------------------------------+ +| ``'a'`` | Integer number | ++-------------------------------+------------------------------------------+ +| ``1.52`` | Floating point number | ++-------------------------------+------------------------------------------+ +| ``1.e2`` | Floating point number | ++-------------------------------+------------------------------------------+ +| ``1.e-2`` | Floating point number | ++-------------------------------+------------------------------------------+ +| ``"I'm a string"`` | String | ++-------------------------------+------------------------------------------+ +| ``@"I'm a verbatim string"`` | String | ++-------------------------------+------------------------------------------+ +| ``@" I'm a`` | | +| ``multiline verbatim string`` | | +| ``"`` | String | ++-------------------------------+------------------------------------------+ + +Pesudo BNF + +.. productionlist:: + IntegerLiteral : [1-9][0-9]* | '0x' [0-9A-Fa-f]+ | ''' [.]+ ''' | 0[0-7]+ + FloatLiteral : [0-9]+ '.' [0-9]+ + FloatLiteral : [0-9]+ '.' 'e'|'E' '+'|'-' [0-9]+ + StringLiteral: '"'[.]* '"' + VerbatimStringLiteral: '@''"'[.]* '"' + +----------- +Comments +----------- + +.. index:: single: comments + +A comment is text that the compiler ignores but that is useful for programmers. +Comments are normally used to embed annotations in the code. The compiler +treats them as white space. + +A comment can be ``/*`` (slash, asterisk) characters, followed by any +sequence of characters (including new lines), +followed by the ``*/`` characters. This syntax is the same as ANSI C.:: + + /* + this is + a multiline comment. + this lines will be ignored by the compiler + */ + +A comment can also be ``//`` (two slashes) characters, followed by any sequence of +characters. A new line not immediately preceded by a backslash terminates this form of +comment. It is commonly called a *"single-line comment."*:: + + //this is a single line comment. this line will be ignored by the compiler + +The character ``#`` is an alternative syntax for single line comment.:: + + # this is also a single line comment. + +This to facilitate the use of squirrel in UNIX-style shell scripts. diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst b/mp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst new file mode 100644 index 00000000..260b0308 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst @@ -0,0 +1,270 @@ +.. _metamethods: + +----------- +Metamethods +----------- + +Metamethods are a mechanism that allows the customization of certain aspects of the +language semantics. Those methods are normal functions placed in a table +parent(delegate) or class declaration; It is possible to change many aspects of a table/class instance behavior by just defining +a metamethod. Class objects (not instances) support only 2 metamethods ``_newmember, _inherited`` . + +For example when we use relational operators other than '==' on 2 tables, the VM will +check if the table has a method in his parent called '_cmp'; if so it will call it to determine +the relation between the tables.:: + + local comparable={ + _cmp = function (other) + { + if(nameother.name)return 1; + return 0; + } + } + + local a={ name="Alberto" }.setdelegate(comparable); + local b={ name="Wouter" }.setdelegate(comparable); + + if(a>b) + print("a>b") + else + print("b<=a"); + +for classes the previous code become: :: + + class Comparable { + constructor(n) + { + name = n; + } + function _cmp(other) + { + if(nameother.name) return 1; + return 0; + } + name = null; + } + + local a = Comparable("Alberto"); + local b = Comparable("Wouter"); + + if(a>b) + print("a>b") + else + print("b<=a"); + +^^^^^ +_set +^^^^^ + +:: + + _set(idx,val) + +invoked when the index idx is not present in the object or in its delegate chain. +``_set`` must 'throw null' to notify that a key wasn't found but the there were not runtime errors (clean failure). +This allows the program to differentiate between a runtime error and a 'index not found'. + +^^^^^ +_get +^^^^^ + +:: + + _get(idx) + +invoked when the index idx is not present in the object or in its delegate chain. +_get must 'throw null' to notify that a key wasn't found but the there were not runtime errors (clean failure). +This allows the program to differentiate between a runtime error and a 'index not found'. + +^^^^^^^^^ +_newslot +^^^^^^^^^ + +:: + + _newslot(key,value) + +invoked when a script tries to add a new slot in a table. + +if the slot already exists in the target table the method will not be invoked also if the +"new slot" operator is used. + +^^^^^^^^^ +_delslot +^^^^^^^^^ + +:: + + _delslot(key) + +invoked when a script deletes a slot from a table. +if the method is invoked squirrel will not try to delete the slot himself + +^^^^^^^^ +_add +^^^^^^^^ + +:: + + _add(other) + +the + operator + +returns this + other + +^^^^^^^^^^^^^^^^^^^^^^^^ +_sub +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _sub(other) + +the - operator (like _add) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_mul +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _mul(other) + +the ``*`` operator (like _add) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_div +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _div(other) + +the ``/`` operator (like _add) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_modulo +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _modulo(other) + +the ``%`` operator (like _add) + +^^^^^^^^^ +_unm +^^^^^^^^^ + +:: + + _unm() + +the unary minus operator + +^^^^^^^^^^^^^^^^^^^^^^^^ +_typeof +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _typeof() + +invoked by the typeof operator on tables, userdata, and class instances. + +Returns the type of ``this`` as string + +^^^^^^^^^^^^^^^^^^^^^^^^ +_cmp +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _cmp(other) + +invoked to emulate the ``< > <= >=`` and ``<=>`` operators + +returns an integer as follow: + ++-----------+----------------------------+ +| returns | relationship | ++===========+============================+ +| > 0 | if ``this`` > ``other`` | ++-----------+----------------------------+ +| 0 | if ``this`` == ``other`` | ++-----------+----------------------------+ +| < 0 | if ``this`` < ``other`` | ++-----------+----------------------------+ + +^^^^^^^^^^^^^^^^^^^^^^^^ +_call +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _call(other) + +invoked when a table, userdata, or class instance is called + +^^^^^^^^^^^^^^^^^^^^^^^^ +_cloned +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _cloned(original) + +invoked when a table or class instance is cloned(in the cloned table) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_nexti +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _nexti(previdx) + +invoked when a userdata or class instance is iterated by a foreach loop. + +If previdx==null it means that it is the first iteration. +The function has to return the index of the 'next' value. + +^^^^^^^^^^^^^^^^^^^^^^^^ +_tostring +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _tostring() + +Invoked when during string concatenation or when the ``print`` function prints a table, instance, or userdata. +The method is also invoked by the sq_tostring() API. + +Must return a string representation of the object. + +^^^^^^^^^^^^^^^^^^^^^^^^ +_inherited +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _inherited(attributes) + +invoked when a class object inherits from the class implementing ``_inherited``. +The ``this`` contains the new class. + +Return value is ignored. + +^^^^^^^^^^^^^^^^^^^^^^^^ +_newmember +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _newmember(index,value,attributes,isstatic) + +invoked for each member declared in a class body (at declaration time). + +If the function is implemented, members will not be added to the class. diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/statements.rst b/mp/src/vscript/squirrel/doc/source/reference/language/statements.rst new file mode 100644 index 00000000..febde526 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/statements.rst @@ -0,0 +1,386 @@ +.. _statements: + + +================= +Statements +================= + +.. index:: + single: statements + +A squirrel program is a simple sequence of statements.:: + + stats := stat [';'|'\n'] stats + +Statements in squirrel are comparable to the C-Family languages (C/C++, Java, C# +etc...): assignment, function calls, program flow control structures etc.. plus some +custom statement like yield, table and array constructors (All those will be covered in detail +later in this document). +Statements can be separated with a new line or ';' (or with the keywords case or default if +inside a switch/case statement), both symbols are not required if the statement is +followed by '}'. + +------ +Block +------ + +.. index:: + pair: block; statement + +:: + + stat := '{' stats '}' + +A sequence of statements delimited by curly brackets ({ }) is called block; +a block is a statement itself. + +----------------------- +Control Flow Statements +----------------------- + +.. index:: + single: control flow statements + +squirrel implements the most common control flow statements: ``if, while, do-while, switch-case, for, foreach`` + +^^^^^^^^^^^^^^ +true and false +^^^^^^^^^^^^^^ + +.. index:: + single: true and false + single: true + single: false + +Squirrel has a boolean type (bool) however like C++ it considers null, 0(integer) and 0.0(float) +as *false*, any other value is considered *true*. + +^^^^^^^^^^^^^^^^^ +if/else statement +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: if/else; statement + pair: if; statement + pair: else; statement + +:: + + stat:= 'if' '(' exp ')' stat ['else' stat] + +Conditionally execute a statement depending on the result of an expression.:: + + if(a>b) + a=b; + else + b=a; + //// + if(a==10) + { + b=a+b; + return a; + } + +^^^^^^^^^^^^^^^^^ +while statement +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: while; statement + +:: + + stat:= 'while' '(' exp ')' stat + +Executes a statement while the condition is true.:: + + function testy(n) + { + local a=0; + while(a100) + +^^^^^^^^^^^^^^^^^ +switch statement +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: switch; statement + +:: + + stat := 'switch' ''( exp ')' '{' + 'case' case_exp ':' + stats + ['default' ':' + stats] + '}' + +Switch is a control statement allows multiple selections of code by passing control to one of the +case statements within its body. +The control is transferred to the case label whose case_exp matches with exp if none of +the case match will jump to the default label (if present). +A switch statement can contain any number if case instances, if 2 case have the same +expression result the first one will be taken in account first. The default label is only +allowed once and must be the last one. +A break statement will jump outside the switch block. + +----- +Loops +----- + +.. index:: + single: Loops + +^^^^^^^^ +for +^^^^^^^^ + +.. index:: + pair: for; statement + +:: + + stat:= 'for' '(' [initexp] ';' [condexp] ';' [incexp] ')' statement + +Executes a statement as long as a condition is different than false.:: + + for(local a=0;a<10;a+=1) + print(a+"\n"); + //or + glob <- null + for(glob=0;glob<10;glob+=1){ + print(glob+"\n"); + } + //or + for(;;){ + print(loops forever+"\n"); + } + +^^^^^^^^ +foreach +^^^^^^^^ + +.. index:: + pair: foreach; statement + +:: + + 'foreach' '(' [index_id','] value_id 'in' exp ')' stat + +Executes a statement for every element contained in an array, table, class, string or generator. +If exp is a generator it will be resumed every iteration as long as it is alive; the value will +be the result of 'resume' and the index the sequence number of the iteration starting +from 0.:: + + local a=[10,23,33,41,589,56] + foreach(idx,val in a) + print("index="+idx+" value="+val+"\n"); + //or + foreach(val in a) + print("value="+val+"\n"); + +------- +break +------- + +.. index:: + pair: break; statement + +:: + + stat := 'break' + +The break statement terminates the execution of a loop (for, foreach, while or do/while) +or jumps out of switch statement; + +--------- +continue +--------- + +.. index:: + pair: continue; statement + +:: + + stat := 'continue' + +The continue operator jumps to the next iteration of the loop skipping the execution of +the following statements. + +--------- +return +--------- + +.. index:: + pair: return; statement + +:: + + stat:= return [exp] + +The return statement terminates the execution of the current function/generator and +optionally returns the result of an expression. If the expression is omitted the function +will return null. If the return statement is used inside a generator, the generator will not +be resumable anymore. + +--------- +yield +--------- + +.. index:: + pair: yield; statement + +:: + + stat := yield [exp] + +(see :ref:`Generators `). + + +--------------------------- +Local variables declaration +--------------------------- + +.. index:: + pair: Local variables declaration; statement + +:: + + initz := id [= exp][',' initz] + stat := 'local' initz + +Local variables can be declared at any point in the program; they exist between their +declaration to the end of the block where they have been declared. +*EXCEPTION:* a local declaration statement is allowed as first expression in a for loop.:: + + for(local a=0;a<10;a+=1) + print(a); + +-------------------- +Function declaration +-------------------- + +.. index:: + pair: Function declaration; statement + +:: + + funcname := id ['::' id] + stat:= 'function' id ['::' id]+ '(' args ')' stat + +creates a new function. + +----------------- +Class declaration +----------------- + +.. index:: + pair: Class declaration; statement + +:: + + memberdecl := id '=' exp [';'] | '[' exp ']' '=' exp [';'] | functionstat | 'constructor' functionexp + stat:= 'class' derefexp ['extends' derefexp] '{' + [memberdecl] + '}' + +creates a new class. + +----------- +try/catch +----------- + +.. index:: + pair: try/catch; statement + +:: + + stat:= 'try' stat 'catch' '(' id ')' stat + +The try statement encloses a block of code in which an exceptional condition can occur, +such as a runtime error or a throw statement. The catch clause provides the exception-handling +code. When a catch clause catches an exception, its id is bound to that +exception. + +----------- +throw +----------- + +.. index:: + pair: throw; statement + +:: + + stat:= 'throw' exp + +Throws an exception. Any value can be thrown. + +-------------- +const +-------------- + +.. index:: + pair: const; statement + +:: + + stat:= 'const' id '=' 'Integer | Float | StringLiteral + +Declares a constant (see :ref:`Constants & Enumerations `). + +-------------- +enum +-------------- + +.. index:: + pair: enum; statement + +:: + + enumerations := ( 'id' '=' Integer | Float | StringLiteral ) [','] + stat:= 'enum' id '{' enumerations '}' + +Declares an enumeration (see :ref:`Constants & Enumerations `). + +-------------------- +Expression statement +-------------------- + +.. index:: + pair: Expression statement; statement + +:: + + stat := exp + +In Squirrel every expression is also allowed as statement, if so, the result of the +expression is thrown away. + diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/tables.rst b/mp/src/vscript/squirrel/doc/source/reference/language/tables.rst new file mode 100644 index 00000000..eb80a688 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/tables.rst @@ -0,0 +1,71 @@ +.. _tables: + + +================= +Tables +================= + +.. index:: + single: Tables + +Tables are associative containers implemented as pairs of key/value (called slot); values +can be any possible type and keys any type except 'null'. +Tables are squirrel's skeleton, delegation and many other features are all implemented +through this type; even the environment, where "global" variables are stored, is a table +(known as root table). + +------------------ +Construction +------------------ + +Tables are created through the table constructor (see :ref:`Table constructor `) + +------------------ +Slot creation +------------------ + +.. index:: + single: Slot Creation(table) + +Adding a new slot in a existing table is done through the "new slot" operator ``<-``; this +operator behaves like a normal assignment except that if the slot does not exists it will +be created.:: + + local a = {} + +The following line will cause an exception because the slot named 'newslot' does not exist +in the table 'a':: + + a.newslot = 1234 + +this will succeed: :: + + a.newslot <- 1234; + +or:: + + a[1] <- "I'm the value of the new slot"; + +----------------- +Slot deletion +----------------- + +.. index:: + single: Slot Deletion(table) + + +:: + + exp:= delete derefexp + +Deletion of a slot is done through the keyword delete; the result of this expression will be +the value of the deleted slot.:: + + a <- { + test1=1234 + deleteme="now" + } + + delete a.test1 + print(delete a.deleteme); //this will print the string "now" + diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/threads.rst b/mp/src/vscript/squirrel/doc/source/reference/language/threads.rst new file mode 100644 index 00000000..efe114ab --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/threads.rst @@ -0,0 +1,106 @@ +.. _threads: + + +======================== +Threads +======================== + +.. index:: + single: Threads + +Squirrel supports cooperative threads(also known as coroutines). +A cooperative thread is a subroutine that can suspended in mid-execution and provide a value to the +caller without returning program flow, then its execution can be resumed later from the same +point where it was suspended. +At first look a Squirrel thread can be confused with a generator, in fact their behaviour is quite similar. +However while a generator runs in the caller stack and can suspend only the local routine stack a thread +has its own execution stack, global table and error handler; This allows a thread to suspend nested calls and +have it's own error policies. + +------------------ +Using threads +------------------ + +.. index:: + single: Using Threads + +Threads are created through the built-in function 'newthread(func)'; this function +gets as parameter a squirrel function and bind it to the new thread objects (will be the thread body). +The returned thread object is initially in 'idle' state. the thread can be started with the function +'threadobj.call()'; the parameters passed to 'call' are passed to the thread function. + +A thread can be be suspended calling the function suspend(), when this happens the function +that wokeup(or started) the thread returns (If a parameter is passed to suspend() it will +be the return value of the wakeup function , if no parameter is passed the return value will be null). +A suspended thread can be resumed calling the function 'threadobj.wakeup', when this happens +the function that suspended the thread will return(if a parameter is passed to wakeup it will +be the return value of the suspend function, if no parameter is passed the return value will be null). + +A thread terminates when its main function returns or when an unhandled exception occurs during its execution.:: + + function coroutine_test(a,b) + { + ::print(a+" "+b+"\n"); + local ret = ::suspend("suspend 1"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 2"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 3"); + ::print("the coroutine says "+ret+"\n"); + return "I'm done" + } + + local coro = ::newthread(coroutine_test); + + local susparam = coro.call("test","coroutine"); //starts the coroutine + + local i = 1; + do + { + ::print("suspend passed ("+susparam+")\n") + susparam = coro.wakeup("ciao "+i); + ++i; + }while(coro.getstatus()=="suspended") + + ::print("return passed ("+susparam+")\n") + +the result of this program will be:: + + test coroutine + suspend passed (suspend 1) + the coroutine says ciao 1 + suspend passed (suspend 2) + the coroutine says ciao 2 + suspend passed (suspend 3) + the coroutine says ciao 3 + return passed (I'm done). + + +the following is an interesting example of how threads and tail recursion +can be combined.:: + + function state1() + { + ::suspend("state1"); + return state2(); //tail call + } + + function state2() + { + ::suspend("state2"); + return state3(); //tail call + } + + function state3() + { + ::suspend("state3"); + return state1(); //tail call + } + + local statethread = ::newthread(state1) + + ::print(statethread.call()+"\n"); + + for(local i = 0; i < 10000; i++) + ::print(statethread.wakeup()+"\n"); + diff --git a/mp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst b/mp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst new file mode 100644 index 00000000..5f87bc82 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst @@ -0,0 +1,61 @@ +.. _weak_references: + + +======================== +Weak References +======================== + +.. index:: + single: Weak References + + +The weak references allows the programmers to create references to objects without +influencing the lifetime of the object itself. +In squirrel Weak references are first-class objects created through the built-in method obj.weakref(). +All types except null implement the weakref() method; however in bools, integers, and floats the method +simply returns the object itself(this because this types are always passed by value). +When a weak references is assigned to a container (table slot,array,class or +instance) is treated differently than other objects; When a container slot that hold a weak +reference is fetched, it always returns the value pointed by the weak reference instead of the weak +reference object. This allow the programmer to ignore the fact that the value handled is weak. +When the object pointed by weak reference is destroyed, the weak reference is automatically set to null.:: + + local t = {} + local a = ["first","second","third"] + //creates a weakref to the array and assigns it to a table slot + t.thearray <- a.weakref(); + +The table slot 'thearray' contains a weak reference to an array. +The following line prints "first", because tables(and all other containers) always return +the object pointed by a weak ref:: + + print(t.thearray[0]); + +the only strong reference to the array is owned by the local variable 'a', so +because the following line assigns a integer to 'a' the array is destroyed.:: + + a = 123; + +When an object pointed by a weak ref is destroyed the weak ref is automatically set to null, +so the following line will print "null".:: + + ::print(typeof(t.thearray)) + +----------------------------------- +Handling weak references explicitly +----------------------------------- + +If a weak reference is assigned to a local variable, then is treated as any other value.:: + + local t = {} + local weakobj = t.weakref(); + +the following line prints "weakref".:: + + ::print(typeof(weakobj)) + +the object pointed by the weakref can be obtained through the built-in method weakref.ref(). + +The following line prints "table".:: + + ::print(typeof(weakobj.ref())) diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/index.rst b/mp/src/vscript/squirrel/doc/source/stdlib/index.rst new file mode 100644 index 00000000..7137bdb5 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/index.rst @@ -0,0 +1,39 @@ +.. _stdlib: + +################################# + Squirrel Standard Library 3.1 +################################# + +Copyright (c) 2003-2016 Alberto Demichelis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +.. toctree:: + :maxdepth: 1 + :numbered: + + introduction.rst + stdiolib.rst + stdbloblib.rst + stdmathlib.rst + stdsystemlib.rst + stdstringlib.rst + stdauxlib.rst + diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/introduction.rst b/mp/src/vscript/squirrel/doc/source/stdlib/introduction.rst new file mode 100644 index 00000000..171e2425 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/introduction.rst @@ -0,0 +1,22 @@ +.. _stdlib_introduction: + +============ +Introduction +============ + +The squirrel standard libraries consist in a set of modules implemented in C++. +While are not essential for the language, they provide a set of useful services that are +commonly used by a wide range of applications(file I/O, regular expressions, etc...), +plus they offer a foundation for developing additional libraries. + +All libraries are implemented through the squirrel API and the ANSI C runtime library. +The modules are organized in the following way: + +* I/O : input and output +* blob : binary buffers manipilation +* math : basic mathematical routines +* system : system access function +* string : string formatting and manipulation +* aux : auxiliary functions + +The libraries can be registered independently,except for the IO library that depends from the bloblib. diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst new file mode 100644 index 00000000..b800c100 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst @@ -0,0 +1,31 @@ +.. _stdlib_stdauxlib: + +=============== +The Aux library +=============== + +The aux library implements default handlers for compiler and runtime errors and a stack dumping. + ++++++++++++ +C API ++++++++++++ + +.. _sqstd_seterrorhandlers: + +.. c:function:: void sqstd_seterrorhandlers(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + + initialize compiler and runtime error handlers, the handlers + use the print function set through(:ref:`sq_setprintfunc `) to output + the error. + +.. _sqstd_printcallstack: + +.. c:function:: void sqstd_printcallstack(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + + prints the call stack and stack contents. the function + uses the print function set through(:ref:`sq_setprintfunc `) to output + the stack dump. diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst new file mode 100644 index 00000000..83d34264 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst @@ -0,0 +1,213 @@ +.. _stdlib_stdbloblib: + +================== +The Blob library +================== +The blob library implements binary data manipulations routines. The library is +based on `blob objects` that represent a buffer of arbitrary +binary data. + +--------------- +Squirrel API +--------------- + ++++++++++++++++ +Global symbols ++++++++++++++++ + +.. js:function:: castf2i(f) + + casts a float to a int + +.. js:function:: casti2f(n) + + casts a int to a float + +.. js:function:: swap2(n) + + swap the byte order of a number (like it would be a 16bits integer) + +.. js:function:: swap4(n) + + swap the byte order of an integer + +.. js:function:: swapfloat(n) + + swaps the byteorder of a float + +++++++++++++++++++ +The blob class +++++++++++++++++++ + +The blob object is a buffer of arbitrary binary data. The object behaves like +a file stream, it has a read/write pointer and it automatically grows if data +is written out of his boundary. +A blob can also be accessed byte by byte through the `[]` operator. + +.. js:class:: blob(size) + + :param int size: initial size of the blob + + returns a new instance of a blob class of the specified size in bytes + +.. js:function:: blob.eos() + + returns a non null value if the read/write pointer is at the end of the stream. + +.. js:function:: blob.flush() + + flushes the stream.return a value != null if succeded, otherwise returns null + +.. js:function:: blob.len() + + returns the length of the stream + +.. js:function:: blob.readblob(size) + + :param int size: number of bytes to read + + read n bytes from the stream and returns them as blob + +.. js:function:: blob.readn(type) + + :param int type: type of the number to read + + reads a number from the stream according to the type parameter. + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+----------------------+ +| parameter | return description | return type | ++==============+================================================================================+======================+ +| 'l' | processor dependent, 32bits on 32bits processors, 64bits on 64bits processors | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'i' | 32bits number | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 's' | 16bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'w' | 16bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'c' | 8bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'b' | 8bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'f' | 32bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'd' | 64bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ + +.. js:function:: blob.resize(size) + + :param int size: the new size of the blob in bytes + + resizes the blob to the specified `size` + +.. js:function:: blob.seek(offset [,origin]) + + :param int offset: indicates the number of bytes from `origin`. + :param int origin: origin of the seek + + +--------------+-------------------------------------------+ + | 'b' | beginning of the stream | + +--------------+-------------------------------------------+ + | 'c' | current location | + +--------------+-------------------------------------------+ + | 'e' | end of the stream | + +--------------+-------------------------------------------+ + + Moves the read/write pointer to a specified location. + +.. note:: If origin is omitted the parameter is defaulted as 'b'(beginning of the stream). + +.. js:function:: blob.swap2() + + swaps the byte order of the blob content as it would be an array of `16bits integers` + +.. js:function:: blob.swap4() + + swaps the byte order of the blob content as it would be an array of `32bits integers` + +.. js:function:: blob.tell() + + returns the read/write pointer absolute position + +.. js:function:: blob.writeblob(src) + + :param blob src: the source blob containing the data to be written + + writes a blob in the stream + +.. js:function:: blob.writen(n, type) + + :param number n: the value to be written + :param int type: type of the number to write + + writes a number in the stream formatted according to the `type` parameter + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+ +| parameter | return description | ++==============+================================================================================+ +| 'i' | 32bits number | ++--------------+--------------------------------------------------------------------------------+ +| 's' | 16bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'w' | 16bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'c' | 8bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'b' | 8bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'f' | 32bits float | ++--------------+--------------------------------------------------------------------------------+ +| 'd' | 64bits float | ++--------------+--------------------------------------------------------------------------------+ + + +------ +C API +------ + +.. _sqstd_register_bloblib: + +.. c:function:: SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initializes and registers the blob library in the given VM. + +.. _sqstd_getblob: + +.. c:function:: SQRESULT sqstd_getblob(HSQUIRRELVM v, SQInteger idx, SQUserPointer* ptr) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: and index in the stack + :param SQUserPointer* ptr: A pointer to the userpointer that will point to the blob's payload + :returns: an SQRESULT + + retrieve the pointer of a blob's payload from an arbitrary + position in the stack. + +.. _sqstd_getblobsize: + +.. c:function:: SQInteger sqstd_getblobsize(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: and index in the stack + :returns: the size of the blob at `idx` position + + retrieves the size of a blob's payload from an arbitrary + position in the stack. + +.. _sqstd_createblob: + +.. c:function:: SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) + + :param HSQUIRRELVM v: the target VM + :param SQInteger size: the size of the blob payload that has to be created + :returns: a pointer to the newly created blob payload + + creates a blob with the given payload size and pushes it in the stack. diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst new file mode 100644 index 00000000..c7de168e --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst @@ -0,0 +1,264 @@ +.. _stdlib_stdiolib: + +======================== +The Input/Output library +======================== + +the i/o library implements basic input/output routines. + +-------------- +Squirrel API +-------------- + +++++++++++++++ +Global Symbols +++++++++++++++ + + +.. js:function:: dofile(path, [raiseerror]) + + compiles a squirrel script or loads a precompiled one and executes it. + returns the value returned by the script or null if no value is returned. + if the optional parameter 'raiseerror' is true, the compiler error handler is invoked + in case of a syntax error. If raiseerror is omitted or set to false, the compiler + error handler is not invoked. + When squirrel is compiled in Unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed UTF8 encoding is used as default. + +.. js:function:: loadfile(path, [raiseerror]) + + compiles a squirrel script or loads a precompiled one an returns it as as function. + if the optional parameter 'raiseerror' is true, the compiler error handler is invoked + in case of a syntax error. If raiseerror is omitted or set to false, the compiler + error handler is not invoked. + When squirrel is compiled in Unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed UTF8 encoding is used as default. + +.. js:function:: writeclosuretofile(destpath, closure) + + serializes a closure to a bytecode file (destpath). The serialized file can be loaded + using loadfile() and dofile(). + + +.. js:data:: stderr + + File object bound on the os *standard error* stream + +.. js:data:: stdin + + File object bound on the os *standard input* stream + +.. js:data:: stdout + + File object bound on the os *standard output* stream + + +++++++++++++++ +The file class +++++++++++++++ + + The file object implements a stream on a operating system file. + +.. js:class:: file(path, patten) + + It's constructor imitates the behaviour of the C runtime function fopen for eg. :: + + local myfile = file("test.xxx","wb+"); + + creates a file with read/write access in the current directory. + +.. js:function:: file.close() + + closes the file. + +.. js:function:: file.eos() + + returns a non null value if the read/write pointer is at the end of the stream. + +.. js:function:: file.flush() + + flushes the stream.return a value != null if succeeded, otherwise returns null + +.. js:function:: file.len() + + returns the length of the stream + +.. js:function:: file.readblob(size) + + :param int size: number of bytes to read + + read n bytes from the stream and returns them as blob + +.. js:function:: file.readn(type) + + :param int type: type of the number to read + + reads a number from the stream according to the type parameter. + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+----------------------+ +| parameter | return description | return type | ++==============+================================================================================+======================+ +| 'l' | processor dependent, 32bits on 32bits processors, 64bits on 64bits processors | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'i' | 32bits number | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 's' | 16bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'w' | 16bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'c' | 8bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'b' | 8bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'f' | 32bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'd' | 64bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ + +.. js:function:: file.resize(size) + + :param int size: the new size of the blob in bytes + + resizes the blob to the specified `size` + +.. js:function:: file.seek(offset [,origin]) + + :param int offset: indicates the number of bytes from `origin`. + :param int origin: origin of the seek + + +--------------+-------------------------------------------+ + | 'b' | beginning of the stream | + +--------------+-------------------------------------------+ + | 'c' | current location | + +--------------+-------------------------------------------+ + | 'e' | end of the stream | + +--------------+-------------------------------------------+ + + Moves the read/write pointer to a specified location. + +.. note:: If origin is omitted the parameter is defaulted as 'b'(beginning of the stream). + +.. js:function:: file.tell() + + returns the read/write pointer absolute position + +.. js:function:: file.writeblob(src) + + :param blob src: the source blob containing the data to be written + + writes a blob in the stream + +.. js:function:: file.writen(n, type) + + :param number n: the value to be written + :param int type: type of the number to write + + writes a number in the stream formatted according to the `type` pamraeter + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+ +| parameter | return description | ++==============+================================================================================+ +| 'i' | 32bits number | ++--------------+--------------------------------------------------------------------------------+ +| 's' | 16bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'w' | 16bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'c' | 8bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'b' | 8bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'f' | 32bits float | ++--------------+--------------------------------------------------------------------------------+ +| 'd' | 64bits float | ++--------------+--------------------------------------------------------------------------------+ + + +-------------- +C API +-------------- + +.. _sqstd_register_iolib: + +.. c:function:: SQRESULT sqstd_register_iolib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initialize and register the io library in the given VM. + +++++++++++++++ +File Object +++++++++++++++ + +.. c:function:: SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file, SQBool owns) + + :param HSQUIRRELVM v: the target VM + :param SQFILE file: the stream that will be rapresented by the file object + :param SQBool owns: if different true the stream will be automatically closed when the newly create file object is destroyed. + :returns: an SQRESULT + + creates a file object bound to the SQFILE passed as parameter + and pushes it in the stack + +.. c:function:: SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE* file) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: and index in the stack + :param SQFILE* file: A pointer to a SQFILE handle that will store the result + :returns: an SQRESULT + + retrieve the pointer of a stream handle from an arbitrary + position in the stack. + +++++++++++++++++++++++++++++++++ +Script loading and serialization +++++++++++++++++++++++++++++++++ + +.. c:function:: SQRESULT sqstd_loadfile(HSQUIRRELVM v, const SQChar* filename, SQBool printerror) + + :param HSQUIRRELVM v: the target VM + :param SQChar* filename: path of the script that has to be loaded + :param SQBool printerror: if true the compiler error handler will be called if a error occurs + :returns: an SQRESULT + + Compiles a squirrel script or loads a precompiled one an pushes it as closure in the stack. + When squirrel is compiled in Unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed UTF8 encoding is used as default. + +.. c:function:: SQRESULT sqstd_dofile(HSQUIRRELVM v, const SQChar* filename, SQBool retval, SQBool printerror) + + :param HSQUIRRELVM v: the target VM + :param SQChar* filename: path of the script that has to be loaded + :param SQBool retval: if true the function will push the return value of the executed script in the stack. + :param SQBool printerror: if true the compiler error handler will be called if a error occurs + :returns: an SQRESULT + :remarks: the function expects a table on top of the stack that will be used as 'this' for the execution of the script. The 'this' parameter is left untouched in the stack. + + Compiles a squirrel script or loads a precompiled one and executes it. + Optionally pushes the return value of the executed script in the stack. + When squirrel is compiled in unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed, UTF8 encoding is used as default. :: + + sq_pushroottable(v); //push the root table(were the globals of the script will are stored) + sqstd_dofile(v, _SC("test.nut"), SQFalse, SQTrue);// also prints syntax errors if any + +.. c:function:: SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v, const SQChar* filename) + + :param HSQUIRRELVM v: the target VM + :param SQChar* filename: destination path of serialized closure + :returns: an SQRESULT + + serializes the closure at the top position in the stack as bytecode in + the file specified by the parameter filename. If a file with the + same name already exists, it will be overwritten. + diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst new file mode 100644 index 00000000..5f0b8b49 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst @@ -0,0 +1,111 @@ +.. _stdlib_stdmathlib: + +================ +The Math library +================ + +the math lib provides basic mathematic routines. The library mimics the +C runtime library implementation. + +------------ +Squirrel API +------------ + ++++++++++++++++ +Global Symbols ++++++++++++++++ + +.. js:function:: abs(x) + + returns the absolute value of `x` as an integer + +.. js:function:: acos(x) + + returns the arccosine of `x` + +.. js:function:: asin(x) + + returns the arcsine of `x` + +.. js:function:: atan(x) + + returns the arctangent of `x` + +.. js:function:: atan2(x,y) + + returns the arctangent of `x/y` + +.. js:function:: ceil(x) + + returns a float value representing the smallest integer that is greater than or equal to `x` + +.. js:function:: cos(x) + + returns the cosine of `x` + +.. js:function:: exp(x) + + returns the exponential value of the float parameter `x` + +.. js:function:: fabs(x) + + returns the absolute value of `x` as a float + +.. js:function:: floor(x) + + returns a float value representing the largest integer that is less than or equal to `x` + +.. js:function:: log(x) + + returns the natural logarithm of `x` + +.. js:function:: log10(x) + + returns the logarithm base-10 of `x` + +.. js:function:: pow(x,y) + + returns `x` raised to the power of `y` + +.. js:function:: rand() + + returns a pseudorandom integer in the range 0 to `RAND_MAX` + +.. js:function:: sin(x) + + rreturns the sine of `x` + +.. js:function:: sqrt(x) + + returns the square root of `x` + +.. js:function:: srand(seed) + + sets the starting point for generating a series of pseudorandom integers + +.. js:function:: tan(x) + + returns the tangent of `x` + +.. js:data:: RAND_MAX + + the maximum value that can be returned by the `rand()` function + +.. js:data:: PI + + The numeric constant pi (3.141592) is the ratio of the circumference of a circle to its diameter + +------------ +C API +------------ + +.. _sqstd_register_mathlib: + +.. c:function:: SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initializes and register the math library in the given VM. + diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst new file mode 100644 index 00000000..19c18d7a --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst @@ -0,0 +1,319 @@ +.. _stdlib_stdstringlib: + +================== +The String library +================== + +the string lib implements string formatting and regular expression matching routines. + +-------------- +Squirrel API +-------------- + +++++++++++++++ +Global Symbols +++++++++++++++ + +.. js:function:: endswith(str, cmp) + + returns `true` if the end of the string `str` matches a the string `cmp` otherwise returns `false` + +.. js:function:: escape(str) + + Returns a string with backslashes before characters that need to be escaped(`\",\a,\b,\t,\n,\v,\f,\r,\\,\",\',\0,\xnn`). + +.. js:function:: format(formatstr, ...) + + Returns a string formatted according `formatstr` and the optional parameters following it. + The format string follows the same rules as the `printf` family of + standard C functions( the "*" is not supported). :: + + e.g. + sq> print(format("%s %d 0x%02X\n","this is a test :",123,10)); + this is a test : 123 0x0A + +.. js:function:: printf(formatstr, ...) + + Just like calling `print(format(formatstr` as in the example above, but is more convenient AND more efficient. :: + + e.g. + sq> printf("%s %d 0x%02X\n","this is a test :",123,10); + this is a test : 123 0x0A + +.. js:function:: lstrip(str) + + Strips white-space-only characters that might appear at the beginning of the given string + and returns the new stripped string. + +.. js:function:: rstrip(str) + + Strips white-space-only characters that might appear at the end of the given string + and returns the new stripped string. + +.. js:function:: split(str, separators) + + returns an array of strings split at each point where a separator character occurs in `str`. + The separator is not returned as part of any array element. + The parameter `separators` is a string that specifies the characters as to be used for the splitting. + + :: + + eg. + local a = split("1.2-3;4/5",".-/;"); + // the result will be [1,2,3,4,5] + + +.. js:function:: startswith(str, cmp) + + returns `true` if the beginning of the string `str` matches the string `cmp`; otherwise returns `false` + +.. js:function:: strip(str) + + Strips white-space-only characters that might appear at the beginning or end of the given string and returns the new stripped string. + +++++++++++++++++++ +The regexp class +++++++++++++++++++ + +.. js:class:: regexp(pattern) + + The regexp object represents a precompiled regular expression pattern. The object is created + through `regexp(pattern)`. + + ++---------------------+--------------------------------------+ +| `\\` | Quote the next metacharacter | ++---------------------+--------------------------------------+ +| `^` | Match the beginning of the string | ++---------------------+--------------------------------------+ +| `.` | Match any character | ++---------------------+--------------------------------------+ +| `$` | Match the end of the string | ++---------------------+--------------------------------------+ +| `|` | Alternation | ++---------------------+--------------------------------------+ +| `(subexp)` | Grouping (creates a capture) | ++---------------------+--------------------------------------+ +| `(?:subexp)` | No Capture Grouping (no capture) | ++---------------------+--------------------------------------+ +| `[]` | Character class | ++---------------------+--------------------------------------+ + +**GREEDY CLOSURES** + ++---------------------+---------------------------------------------+ +| `*` | Match 0 or more times | ++---------------------+---------------------------------------------+ +| `+` | Match 1 or more times | ++---------------------+---------------------------------------------+ +| `?` | Match 1 or 0 times | ++---------------------+---------------------------------------------+ +| `{n}` | Match exactly n times | ++---------------------+---------------------------------------------+ +| `{n,}` | Match at least n times | ++---------------------+---------------------------------------------+ +| `{n,m}` | Match at least n but not more than m times | ++---------------------+---------------------------------------------+ + +**ESCAPE CHARACTERS** + ++---------------------+--------------------------------------+ +| `\\t` | tab (HT, TAB) | ++---------------------+--------------------------------------+ +| `\\n` | newline (LF, NL) | ++---------------------+--------------------------------------+ +| `\\r` | return (CR) | ++---------------------+--------------------------------------+ +| `\\f` | form feed (FF) | ++---------------------+--------------------------------------+ + +**PREDEFINED CLASSES** + ++---------------------+--------------------------------------+ +| `\\l` | lowercase next char | ++---------------------+--------------------------------------+ +| `\\u` | uppercase next char | ++---------------------+--------------------------------------+ +| `\\a` | letters | ++---------------------+--------------------------------------+ +| `\\A` | non letters | ++---------------------+--------------------------------------+ +| `\\w` | alphanumeric `[_0-9a-zA-Z]` | ++---------------------+--------------------------------------+ +| `\\W` | non alphanumeric `[^_0-9a-zA-Z]` | ++---------------------+--------------------------------------+ +| `\\s` | space | ++---------------------+--------------------------------------+ +| `\\S` | non space | ++---------------------+--------------------------------------+ +| `\\d` | digits | ++---------------------+--------------------------------------+ +| `\\D` | non digits | ++---------------------+--------------------------------------+ +| `\\x` | hexadecimal digits | ++---------------------+--------------------------------------+ +| `\\X` | non hexadecimal digits | ++---------------------+--------------------------------------+ +| `\\c` | control characters | ++---------------------+--------------------------------------+ +| `\\C` | non control characters | ++---------------------+--------------------------------------+ +| `\\p` | punctuation | ++---------------------+--------------------------------------+ +| `\\P` | non punctuation | ++---------------------+--------------------------------------+ +| `\\b` | word boundary | ++---------------------+--------------------------------------+ +| `\\B` | non word boundary | ++---------------------+--------------------------------------+ + + +.. js:function:: regexp.capture(str [, start]) + + returns an array of tables containing two indexes ("begin" and "end") of + the first match of the regular expression in the string `str`. + An array entry is created for each captured sub expressions. If no match occurs returns null. + The search starts from the index `start` + of the string; if `start` is omitted the search starts from the beginning of the string. + + The first element of the returned array(index 0) always contains the complete match. + + :: + + local ex = regexp(@"(\d+) ([a-zA-Z]+)(\p)"); + local string = "stuff 123 Test;"; + local res = ex.capture(string); + foreach(i,val in res) + { + print(format("match number[%02d] %s\n", + i,string.slice(val.begin,val.end))); //prints "Test" + } + + ... + will print + match number[00] 123 Test; + match number[01] 123 + match number[02] Test + match number[03] ; + +.. js:function:: regexp.match(str) + + returns a true if the regular expression matches the string + `str`, otherwise returns false. + +.. js:function:: regexp.search(str [, start]) + + returns a table containing two indexes ("begin" and "end") of the first match of the regular expression in + the string `str`, otherwise if no match occurs returns null. The search starts from the index `start` + of the string; if `start` is omitted the search starts from the beginning of the string. + + :: + + local ex = regexp("[a-zA-Z]+"); + local string = "123 Test;"; + local res = ex.search(string); + print(string.slice(res.begin,res.end)); //prints "Test" + +------------- +C API +------------- + +.. _sqstd_register_stringlib: + +.. c:function:: SQRESULT sqstd_register_stringlib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initialize and register the string library in the given VM. + ++++++++++++++ +Formatting ++++++++++++++ + +.. c:function:: SQRESULT sqstd_format(HSQUIRRELVM v, SQInteger nformatstringidx, SQInteger* outlen, SQChar** output) + + :param HSQUIRRELVM v: the target VM + :param SQInteger nformatstringidx: index in the stack of the format string + :param SQInteger* outlen: a pointer to an integer that will be filled with the length of the newly created string + :param SQChar** output: a pointer to a string pointer that will receive the newly created string + :returns: an SQRESULT + :remarks: the newly created string is allocated in the scratchpad memory. + + + creates a new string formatted according to the object at position `nformatstringidx` and the optional parameters following it. + The format string follows the same rules as the `printf` family of + standard C functions( the "*" is not supported). + +++++++++++++++++++ +Regular Expessions +++++++++++++++++++ + +.. c:function:: SQRex* sqstd_rex_compile(const SQChar *pattern, const SQChar ** error) + + :param SQChar* pattern: a pointer to a zero terminated string containing the pattern that has to be compiled. + :param SQChar** error: a pointer to a string pointer that will be set with an error string in case of failure. + :returns: a pointer to the compiled pattern + + compiles an expression and returns a pointer to the compiled version. + in case of failure returns NULL.The returned object has to be deleted + through the function sqstd_rex_free(). + +.. c:function:: void sqstd_rex_free(SQRex * exp) + + :param SQRex* exp: the expression structure that has to be deleted. + + deletes a expression structure created with sqstd_rex_compile() + +.. c:function:: SQBool sqstd_rex_match(SQRex * exp,const SQChar * text) + + :param SQRex* exp: a compiled expression + :param SQChar* text: the string that has to be tested + :returns: SQTrue if successful otherwise SQFalse + + returns SQTrue if the string specified in the parameter text is an + exact match of the expression, otherwise returns SQFalse. + +.. c:function:: SQBool sqstd_rex_search(SQRex * exp, const SQChar * text, const SQChar ** out_begin, const SQChar ** out_end) + + :param SQRex* exp: a compiled expression + :param SQChar* text: the string that has to be tested + :param SQChar** out_begin: a pointer to a string pointer that will be set with the beginning of the match + :param SQChar** out_end: a pointer to a string pointer that will be set with the end of the match + :returns: SQTrue if successful otherwise SQFalse + + searches the first match of the expression in the string specified in the parameter text. + if the match is found returns SQTrue and the sets out_begin to the beginning of the + match and out_end at the end of the match; otherwise returns SQFalse. + +.. c:function:: SQBool sqstd_rex_searchrange(SQRex * exp, const SQChar * text_begin, const SQChar * text_end, const SQChar ** out_begin, const SQChar ** out_end) + + :param SQRex* exp: a compiled expression + :param SQChar* text_begin: a pointer to the beginnning of the string that has to be tested + :param SQChar* text_end: a pointer to the end of the string that has to be tested + :param SQChar** out_begin: a pointer to a string pointer that will be set with the beginning of the match + :param SQChar** out_end: a pointer to a string pointer that will be set with the end of the match + :returns: SQTrue if successful otherwise SQFalse + + searches the first match of the expression in the string delimited + by the parameter text_begin and text_end. + if the match is found returns SQTrue and sets out_begin to the beginning of the + match and out_end at the end of the match; otherwise returns SQFalse. + +.. c:function:: SQInteger sqstd_rex_getsubexpcount(SQRex * exp) + + :param SQRex* exp: a compiled expression + :returns: the number of sub expressions matched by the expression + + returns the number of sub expressions matched by the expression + +.. c:function:: SQBool sqstd_rex_getsubexp(SQRex * exp, SQInteger n, SQRexMatch *subexp) + + :param SQRex* exp: a compiled expression + :param SQInteger n: the index of the submatch(0 is the complete match) + :param SQRexMatch* a: pointer to structure that will store the result + :returns: the function returns SQTrue if n is a valid index; otherwise SQFalse. + + retrieve the begin and and pointer to the length of the sub expression indexed + by n. The result is passed through the struct SQRexMatch. diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst new file mode 100644 index 00000000..5c565fd6 --- /dev/null +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst @@ -0,0 +1,82 @@ +.. _stdlib_stdsystemlib: + +================== +The System library +================== + +The system library exposes operating system facilities like environment variables, +date time manipulation etc.. + +-------------- +Squirrel API +-------------- + +++++++++++++++ +Global Symbols +++++++++++++++ + +.. js:function:: clock() + + returns a float representing the number of seconds elapsed since the start of the process + +.. js:function:: date([time [, format]]) + + returns a table containing a date/time split into the slots: + ++-------------+----------------------------------------+ +| sec | Seconds after minute (0 - 59). | ++-------------+----------------------------------------+ +| min | Minutes after hour (0 - 59). | ++-------------+----------------------------------------+ +| hour | Hours since midnight (0 - 23). | ++-------------+----------------------------------------+ +| day | Day of month (1 - 31). | ++-------------+----------------------------------------+ +| month | Month (0 - 11; January = 0). | ++-------------+----------------------------------------+ +| year | Year (current year). | ++-------------+----------------------------------------+ +| wday | Day of week (0 - 6; Sunday = 0). | ++-------------+----------------------------------------+ +| yday | Day of year (0 - 365; January 1 = 0). | ++-------------+----------------------------------------+ + +if `time` is omitted the current time is used. + +if `format` can be 'l' local time or 'u' UTC time, if omitted is defaulted as 'l'(local time). + +.. js:function:: getenv(varaname) + + Returns a string containing the value of the environment variable `varname` + +.. js:function:: remove(path) + + deletes the file specified by `path` + +.. js:function:: rename(oldname, newname) + + renames the file or directory specified by `oldname` to the name given by `newname` + +.. js:function:: system(cmd) + + xecutes the string `cmd` through the os command interpreter. + +.. js:function:: time() + + returns the number of seconds elapsed since midnight 00:00:00, January 1, 1970. + + the result of this function can be formatted through the function `date()` + +-------------- +C API +-------------- + +.. _sqstd_register_systemlib: + +.. c:function:: SQRESULT sqstd_register_systemlib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initialize and register the system library in the given VM. diff --git a/mp/src/vscript/squirrel/etc/minimal.c b/mp/src/vscript/squirrel/etc/minimal.c new file mode 100644 index 00000000..0695768b --- /dev/null +++ b/mp/src/vscript/squirrel/etc/minimal.c @@ -0,0 +1,78 @@ +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment (lib ,"squirrel.lib") +#pragma comment (lib ,"sqstdlib.lib") +#endif + +#ifdef SQUNICODE + +#define scvprintf vfwprintf +#else + +#define scvprintf vfprintf +#endif + +void printfunc(HSQUIRRELVM v,const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stdout, s, vl); + va_end(vl); +} + +void errorfunc(HSQUIRRELVM v,const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stderr, s, vl); + va_end(vl); +} + +void call_foo(HSQUIRRELVM v, int n,float f,const SQChar *s) +{ + SQInteger top = sq_gettop(v); //saves the stack size before the call + sq_pushroottable(v); //pushes the global table + sq_pushstring(v,_SC("foo"),-1); + if(SQ_SUCCEEDED(sq_get(v,-2))) { //gets the field 'foo' from the global table + sq_pushroottable(v); //push the 'this' (in this case is the global table) + sq_pushinteger(v,n); + sq_pushfloat(v,f); + sq_pushstring(v,s,-1); + sq_call(v,4,SQFalse,SQTrue); //calls the function + } + sq_settop(v,top); //restores the original stack size +} + +int main(int argc, char* argv[]) +{ + HSQUIRRELVM v; + v = sq_open(1024); // creates a VM with initial stack size 1024 + + //REGISTRATION OF STDLIB + //sq_pushroottable(v); //push the root table where the std function will be registered + //sqstd_register_iolib(v); //registers a library + // ... call here other stdlibs string,math etc... + //sq_pop(v,1); //pops the root table + //END REGISTRATION OF STDLIB + + sqstd_seterrorhandlers(v); //registers the default error handlers + + sq_setprintfunc(v, printfunc,errorfunc); //sets the print function + + sq_pushroottable(v); //push the root table(were the globals of the script will be stored) + if(SQ_SUCCEEDED(sqstd_dofile(v, _SC("test.nut"), SQFalse, SQTrue))) // also prints syntax errors if any + { + call_foo(v,1,2.5,_SC("teststring")); + } + + sq_pop(v,1); //pops the root table + sq_close(v); + + return 0; +} diff --git a/mp/src/vscript/squirrel/etc/test.nut b/mp/src/vscript/squirrel/etc/test.nut new file mode 100644 index 00000000..125df32c --- /dev/null +++ b/mp/src/vscript/squirrel/etc/test.nut @@ -0,0 +1,4 @@ +function foo(i, f, s) +{ + print("Called foo(), i="+i+", f="+f+", s='"+s+"'\n"); +} diff --git a/mp/src/vscript/squirrel/include/sqconfig.h b/mp/src/vscript/squirrel/include/sqconfig.h new file mode 100644 index 00000000..58bc9793 --- /dev/null +++ b/mp/src/vscript/squirrel/include/sqconfig.h @@ -0,0 +1,146 @@ + +#ifdef _SQ64 + +#ifdef _MSC_VER +typedef __int64 SQInteger; +typedef unsigned __int64 SQUnsignedInteger; +typedef unsigned __int64 SQHash; /*should be the same size of a pointer*/ +#else +typedef long long SQInteger; +typedef unsigned long long SQUnsignedInteger; +typedef unsigned long long SQHash; /*should be the same size of a pointer*/ +#endif +typedef int SQInt32; +typedef unsigned int SQUnsignedInteger32; +#else +typedef int SQInteger; +typedef int SQInt32; /*must be 32 bits(also on 64bits processors)*/ +typedef unsigned int SQUnsignedInteger32; /*must be 32 bits(also on 64bits processors)*/ +typedef unsigned int SQUnsignedInteger; +typedef unsigned int SQHash; /*should be the same size of a pointer*/ +#endif + + +#ifdef SQUSEDOUBLE +typedef double SQFloat; +#else +typedef float SQFloat; +#endif + +#if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) +#ifdef _MSC_VER +typedef __int64 SQRawObjectVal; //must be 64bits +#else +typedef long long SQRawObjectVal; //must be 64bits +#endif +#define SQ_OBJECT_RAWINIT() { _unVal.raw = 0; } +#else +typedef SQUnsignedInteger SQRawObjectVal; //is 32 bits on 32 bits builds and 64 bits otherwise +#define SQ_OBJECT_RAWINIT() +#endif + +#ifndef SQ_ALIGNMENT // SQ_ALIGNMENT shall be less than or equal to SQ_MALLOC alignments, and its value shall be power of 2. +#if defined(SQUSEDOUBLE) || defined(_SQ64) +#define SQ_ALIGNMENT 8 +#else +#define SQ_ALIGNMENT 4 +#endif +#endif + +typedef void* SQUserPointer; +typedef SQUnsignedInteger SQBool; +typedef SQInteger SQRESULT; + +#ifdef SQUNICODE +#include +#include + + +typedef wchar_t SQChar; + + +#define scstrcmp wcscmp +#ifdef _WIN32 +#define scsprintf _snwprintf +#else +#define scsprintf swprintf +#endif +#define scstrlen wcslen +#define scstrtod wcstod +#ifdef _SQ64 +#define scstrtol wcstoll +#else +#define scstrtol wcstol +#endif +#define scstrtoul wcstoul +#define scvsprintf vswprintf +#define scstrstr wcsstr +#define scprintf wprintf + +#ifdef _WIN32 +#define WCHAR_SIZE 2 +#define WCHAR_SHIFT_MUL 1 +#define MAX_CHAR 0xFFFF +#else +#define WCHAR_SIZE 4 +#define WCHAR_SHIFT_MUL 2 +#define MAX_CHAR 0xFFFFFFFF +#endif + +#define _SC(a) L##a + + +#define scisspace iswspace +#define scisdigit iswdigit +#define scisprint iswprint +#define scisxdigit iswxdigit +#define scisalpha iswalpha +#define sciscntrl iswcntrl +#define scisalnum iswalnum + + +#define sq_rsl(l) ((l)<=0) + +#ifdef __GNUC__ +# define SQ_UNUSED_ARG(x) x __attribute__((__unused__)) +#else +# define SQ_UNUSED_ARG(x) x +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*_SQUIRREL_H_*/ diff --git a/mp/src/vscript/squirrel/samples/ackermann.nut b/mp/src/vscript/squirrel/samples/ackermann.nut new file mode 100644 index 00000000..8b18ec21 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/ackermann.nut @@ -0,0 +1,23 @@ +/* +* +* Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) +* +*/ + +function Ack(M, N) { + if (M == 0) return( N + 1 ); + if (N == 0) return( Ack(M - 1, 1) ); + return( Ack(M - 1, Ack(M, (N - 1))) ); +} + +local n; + +if(vargv.len()!=0) { + n = vargv[0].tointeger(); + if(n < 1) n = 1; +} else { + n = 1; +} +print("n="+n+"\n"); +print("Ack(3,"+ n+ "):"+ Ack(3, n)); + diff --git a/mp/src/vscript/squirrel/samples/array.nut b/mp/src/vscript/squirrel/samples/array.nut new file mode 100644 index 00000000..0102d62f --- /dev/null +++ b/mp/src/vscript/squirrel/samples/array.nut @@ -0,0 +1,29 @@ +/* +* +* Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) +* +*/ +local n, i, k; + +if(vargv.len()!=0) { + n = vargv[0].tointeger(); + if(n < 1) n = 1; +} else { + n = 1; +} + +local x = []; x.resize(n); +local y = []; y.resize(n); + +for (i = 0; i < n; i+=1) { + x[i] = i + 1; + y[i] = 0; +} + +for (k = 0 ; k < n; k+=1) { + for (i = n-1; i >= 0; i-=1) { + y[i] = y[i]+ x[i]; + } +} +print(y[0].tostring()+" "+y[n-1]); + diff --git a/mp/src/vscript/squirrel/samples/class.nut b/mp/src/vscript/squirrel/samples/class.nut new file mode 100644 index 00000000..2d924602 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/class.nut @@ -0,0 +1,49 @@ +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +class BaseVector { + constructor(...) + { + if(vargv.len() >= 3) { + x = vargv[0]; + y = vargv[1]; + z = vargv[2]; + } + } + + + x = 0; + y = 0; + z = 0; +} + +class Vector3 extends BaseVector { + function _add(other) + { + if(other instanceof this.getclass()) + return ::Vector3(x+other.x,y+other.y,z+other.z); + else + throw "wrong parameter"; + } + function Print() + { + ::print(x+","+y+","+z+"\n"); + } +} + +local v0 = Vector3(1,2,3) +local v1 = Vector3(11,12,13) +local v2 = v0 + v1; +v2.Print(); + +FakeNamespace <- { + Utils = {} +} + +class FakeNamespace.Utils.SuperClass { + constructor() + { + ::print("FakeNamespace.Utils.SuperClass") + } +} + +local testy = FakeNamespace.Utils.SuperClass(); diff --git a/mp/src/vscript/squirrel/samples/classattributes.nut b/mp/src/vscript/squirrel/samples/classattributes.nut new file mode 100644 index 00000000..4d2d078d --- /dev/null +++ b/mp/src/vscript/squirrel/samples/classattributes.nut @@ -0,0 +1,35 @@ +class Foo { + //constructor + constructor(a) + { + testy = ["stuff",1,2,3]; + } + //attributes of PrintTesty + + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //attributes of testy + + testy = null; + +} + +foreach(member,val in Foo) +{ + ::print(member+"\n"); + local attr; + if((attr = Foo.getattributes(member)) != null) { + foreach(i,v in attr) + { + ::print("\t"+i+" = "+(typeof v)+"\n"); + } + } + else { + ::print("\t\n") + } +} diff --git a/mp/src/vscript/squirrel/samples/coroutines.nut b/mp/src/vscript/squirrel/samples/coroutines.nut new file mode 100644 index 00000000..0cc19920 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/coroutines.nut @@ -0,0 +1,25 @@ +function coroutine_test(a,b) +{ + ::print(a+" "+b+"\n"); + local ret = ::suspend("suspend 1"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 2"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 3"); + ::print("the coroutine says "+ret+"\n"); + return "I'm done" +} + +local coro = ::newthread(coroutine_test); + +local susparam = coro.call("test","coroutine"); //starts the coroutine + +local i = 1; +do +{ + ::print("suspend passed ["+susparam+"]\n") + susparam = coro.wakeup("ciao "+i); + ++i; +}while(coro.getstatus()=="suspended") + +::print("return passed ["+susparam+"]\n") diff --git a/mp/src/vscript/squirrel/samples/delegation.nut b/mp/src/vscript/squirrel/samples/delegation.nut new file mode 100644 index 00000000..9f208dc4 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/delegation.nut @@ -0,0 +1,54 @@ + +PEntity <- { + name="noname" + pos={x=0,y=0,z=0} + type="entity" + //methamethod + _typeof=function() + { + return type; + } +} + +function PEntity::PrintPos() +{ + ::print("x="+pos.x+" y="+pos.y+" z="+pos.z+"\n"); +} + +function PEntity::new(name,pos) +{ + local newentity=clone ::PEntity; + if(name) + newentity.name=name; + if(pos) + newentity.pos=pos; + return newentity; +} + +PPlayer <- { + model="warrior.mdl" + weapon="fist" + health=100 + armor=0 + //overrides the parent type + type="player" +} + +function PPlayer::new(name,pos) +{ + local p = clone ::PPlayer; + local newplayer = ::PEntity.new(name,pos); + newplayer.setdelegate(p); + return newplayer; +} + +local player=PPlayer.new("godzilla",{x=10,y=20,z=30}); + +::print("PLAYER NAME"+player.name+"\n"); +::print("ENTITY TYPE"+typeof player+"\n"); + +player.PrintPos(); + +player.pos.x=123; + +player.PrintPos(); diff --git a/mp/src/vscript/squirrel/samples/fibonacci.nut b/mp/src/vscript/squirrel/samples/fibonacci.nut new file mode 100644 index 00000000..c722d1a1 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/fibonacci.nut @@ -0,0 +1,15 @@ +/* +* +* Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) +* +*/ + +function fib(n) +{ + if (n < 2) return 1 + return fib(n-2) + fib(n-1) +} + +local n = vargv.len()!=0?vargv[0].tointeger():1 + +print(fib(n)+"\n") diff --git a/mp/src/vscript/squirrel/samples/flow.nut b/mp/src/vscript/squirrel/samples/flow.nut new file mode 100644 index 00000000..635bc89f --- /dev/null +++ b/mp/src/vscript/squirrel/samples/flow.nut @@ -0,0 +1,33 @@ +function min(x,y) + return xy?x:y; + +if(min(100,200)>max(50,20)) + print("I'm useless statement just to show up the if/else\n"); +else + print("squirrel!!\n"); + +print("\n") + +function typy(obj) +{ + switch(typeof obj) + { + case "integer": + case "float": + return "is a number"; + case "table": + case "array": + return "is a container"; + default: + return "is other stuff" + } +} + +local a=1,b={},c=function(a,b){return a+b;} + +print("a "+typy(a)+"\n"); +print("b "+typy(b)+"\n"); +print("c "+typy(c)+"\n"); diff --git a/mp/src/vscript/squirrel/samples/generators.nut b/mp/src/vscript/squirrel/samples/generators.nut new file mode 100644 index 00000000..eb5eef6b --- /dev/null +++ b/mp/src/vscript/squirrel/samples/generators.nut @@ -0,0 +1,42 @@ +/* +*Random number function from The Great Computer Language shootout +*converted to a generator func +*/ + +function gen_random(max) { + local last=42 + local IM = 139968; + local IA = 3877; + local IC = 29573; + for(;;){ //loops forever + yield (max * (last = (last * IA + IC) % IM) / IM); + } +} + +local randtor=gen_random(100); + +print("RAND NUMBERS \n") + +for(local i=0;i<10;i+=1) + print(">"+resume randtor+"\n"); + +print("FIBONACCI \n") +function fiboz(n) +{ + local prev=0; + local curr=1; + yield 1; + + for(local i=0;i"+val+"\n"); +} diff --git a/mp/src/vscript/squirrel/samples/hello.nut b/mp/src/vscript/squirrel/samples/hello.nut new file mode 100644 index 00000000..f301245e --- /dev/null +++ b/mp/src/vscript/squirrel/samples/hello.nut @@ -0,0 +1 @@ +print("Hello World!") diff --git a/mp/src/vscript/squirrel/samples/list.nut b/mp/src/vscript/squirrel/samples/list.nut new file mode 100644 index 00000000..e7cc2218 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/list.nut @@ -0,0 +1,40 @@ +/*translation of the list test from The Great Computer Language Shootout +*/ + +function compare_arr(a1,a2) +{ + foreach(i,val in a1) + if(val!=a2[i])return null; + return 1; +} + +function test() +{ + local size=10000 + local l1=[]; l1.resize(size); + for(local i=0;i0) + l3.append(l2.pop()); + while(l3.len()>0) + l2.append(l3.pop()); + l1.reverse(); + + if(compare_arr(l1,l2)) + return l1.len(); + return null; +} + +local n = vargv.len()!=0?vargv[0].tointeger():1 +for(local i=0;i\n"); +else + print("\n"); diff --git a/mp/src/vscript/squirrel/samples/methcall.nut b/mp/src/vscript/squirrel/samples/methcall.nut new file mode 100644 index 00000000..73bb2e84 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/methcall.nut @@ -0,0 +1,68 @@ +/*translation of the methcall test from The Great Computer Language Shootout +*/ + +class Toggle { + bool=null +} + +function Toggle::constructor(startstate) { + bool = startstate +} + +function Toggle::value() { + return bool; +} + +function Toggle::activate() { + bool = !bool; + return this; +} + +class NthToggle extends Toggle { + count_max=null + count=0 +} + +function NthToggle::constructor(start_state,max_counter) +{ + base.constructor(start_state); + count_max = max_counter +} + +function NthToggle::activate () +{ + ++count; + if (count >= count_max ) { + base.activate(); + count = 0; + } + return this; +} + + +function main() { + local n = vargv.len()!=0?vargv[0].tointeger():1 + + + + local val = 1; + local toggle = Toggle(val); + local i = n; + while(i--) { + val = toggle.activate().value(); + + } + print(toggle.value() ? "true\n" : "false\n"); + + val = 1; + local ntoggle = NthToggle(val, 3); + i = n; + while(i--) { + val = ntoggle.activate().value(); + } + print(ntoggle.value() ? "true\n" : "false\n"); + +} +local start=clock(); +main(); +print("TIME="+(clock()-start)+"\n"); diff --git a/mp/src/vscript/squirrel/samples/regex.nut b/mp/src/vscript/squirrel/samples/regex.nut new file mode 100644 index 00000000..fcd8e59e --- /dev/null +++ b/mp/src/vscript/squirrel/samples/regex.nut @@ -0,0 +1,10 @@ +local ex = regexp("[a-zA-Z]+"); +local string = "123 Test; strlen(str);"; +local res = ex.search(string); +print(string.slice(res.begin,res.end)); //prints "Test" +print("\n"); +ex = regexp(@"\m()"); +string = "123 Test; doSomething(str, getTemp(), (a+(b/c)));"; +res = ex.search(string); +print(string.slice(res.begin,res.end)); //prints "(...)" +print("\n"); diff --git a/mp/src/vscript/squirrel/samples/tailstate.nut b/mp/src/vscript/squirrel/samples/tailstate.nut new file mode 100644 index 00000000..7ea06c69 --- /dev/null +++ b/mp/src/vscript/squirrel/samples/tailstate.nut @@ -0,0 +1,24 @@ +function state1() +{ + ::suspend("state1"); + return state2(); +} + +function state2() +{ + ::suspend("state2"); + return state3(); +} + +function state3() +{ + ::suspend("state3"); + return state1(); +} + +local statethread = ::newthread(state1) + +::print(statethread.call()+"\n"); + +for(local i = 0; i < 10000; i++) + ::print(statethread.wakeup()+"\n"); diff --git a/mp/src/vscript/squirrel/sq/CMakeLists.txt b/mp/src/vscript/squirrel/sq/CMakeLists.txt new file mode 100644 index 00000000..bdea07d0 --- /dev/null +++ b/mp/src/vscript/squirrel/sq/CMakeLists.txt @@ -0,0 +1,38 @@ +set(CMAKE_C_STANDARD 99) +if(NOT DISABLE_DYNAMIC) + add_executable(sq sq.c) + add_executable(squirrel::interpreter ALIAS sq) + set_target_properties(sq PROPERTIES LINKER_LANGUAGE C EXPORT_NAME interpreter) + target_link_libraries(sq squirrel sqstdlib) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sq EXPORT squirrel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) + endif() + target_include_directories(sq PUBLIC + "$" + "$" + ) +endif() + +if(NOT DISABLE_STATIC) + add_executable(sq_static sq.c) + add_executable(squirrel::interpreter_static ALIAS sq) + set_target_properties(sq_static PROPERTIES LINKER_LANGUAGE C EXPORT_NAME interpreter_static) + target_link_libraries(sq_static squirrel_static sqstdlib_static) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sq_static EXPORT squirrel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) + endif() + target_include_directories(sq_static PUBLIC + "$" + "$" + ) +endif() + +if(LONG_OUTPUT_NAMES) + if(NOT DISABLE_DYNAMIC) + set_target_properties(sq PROPERTIES OUTPUT_NAME squirrel3) + endif() + + if(NOT DISABLE_STATIC) + set_target_properties(sq_static PROPERTIES OUTPUT_NAME squirrel3_static) + endif() +endif() diff --git a/mp/src/vscript/squirrel/sq/Makefile b/mp/src/vscript/squirrel/sq/Makefile new file mode 100644 index 00000000..948fd1ea --- /dev/null +++ b/mp/src/vscript/squirrel/sq/Makefile @@ -0,0 +1,21 @@ +SQUIRREL= .. + + +OUT= $(SQUIRREL)/bin/sq +INCZ= -I$(SQUIRREL)/include -I. -I$(SQUIRREL)/sqlibs +LIBZ= -L$(SQUIRREL)/lib +LIB= -lsquirrel -lsqstdlib + +OBJS= sq.o + +SRCS= sq.c + + +sq32: + g++ -O2 -fno-exceptions -fno-rtti -o $(OUT) $(SRCS) $(INCZ) $(LIBZ) $(LIB) + +sqprof: + g++ -O2 -pg -fno-exceptions -fno-rtti -pie -gstabs -g3 -o $(OUT) $(SRCS) $(INCZ) $(LIBZ) $(LIB) + +sq64: + g++ -O2 -m64 -fno-exceptions -fno-rtti -D_SQ64 -o $(OUT) $(SRCS) $(INCZ) $(LIBZ) $(LIB) diff --git a/mp/src/vscript/squirrel/sq/sq.c b/mp/src/vscript/squirrel/sq/sq.c new file mode 100644 index 00000000..ee5eabbb --- /dev/null +++ b/mp/src/vscript/squirrel/sq/sq.c @@ -0,0 +1,349 @@ +/* see copyright notice in squirrel.h */ + +#include +#include +#include +#include + +#if defined(_MSC_VER) && defined(_DEBUG) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef SQUNICODE +#define scfprintf fwprintf +#define scvprintf vfwprintf +#else +#define scfprintf fprintf +#define scvprintf vfprintf +#endif + + +void PrintVersionInfos(); + +#if defined(_MSC_VER) && defined(_DEBUG) +int MemAllocHook( int allocType, void *userData, size_t size, int blockType, + long requestNumber, const unsigned char *filename, int lineNumber) +{ + //if(requestNumber==769)_asm int 3; + return 1; +} +#endif + + +SQInteger quit(HSQUIRRELVM v) +{ + int *done; + sq_getuserpointer(v,-1,(SQUserPointer*)&done); + *done=1; + return 0; +} + +void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stdout, s, vl); + va_end(vl); +} + +void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stderr, s, vl); + va_end(vl); +} + +void PrintVersionInfos() +{ + scfprintf(stdout,_SC("%s %s (%d bits)\n"),SQUIRREL_VERSION,SQUIRREL_COPYRIGHT,((int)(sizeof(SQInteger)*8))); +} + +void PrintUsage() +{ + scfprintf(stderr,_SC("usage: sq .\n") + _SC("Available options are:\n") + _SC(" -c compiles the file to bytecode(default output 'out.cnut')\n") + _SC(" -o specifies output file for the -c option\n") + _SC(" -c compiles only\n") + _SC(" -d generates debug infos\n") + _SC(" -v displays version infos\n") + _SC(" -h prints help\n")); +} + +#define _INTERACTIVE 0 +#define _DONE 2 +#define _ERROR 3 +//<> this func is a mess +int getargs(HSQUIRRELVM v,int argc, char* argv[],SQInteger *retval) +{ + int i; + int compiles_only = 0; +#ifdef SQUNICODE + static SQChar temp[500]; +#endif + char * output = NULL; + *retval = 0; + if(argc>1) + { + int arg=1,exitloop=0; + + while(arg < argc && !exitloop) + { + + if(argv[arg][0]=='-') + { + switch(argv[arg][1]) + { + case 'd': //DEBUG(debug infos) + sq_enabledebuginfo(v,1); + break; + case 'c': + compiles_only = 1; + break; + case 'o': + if(arg < argc) { + arg++; + output = argv[arg]; + } + break; + case 'v': + PrintVersionInfos(); + return _DONE; + + case 'h': + PrintVersionInfos(); + PrintUsage(); + return _DONE; + default: + PrintVersionInfos(); + scprintf(_SC("unknown prameter '-%c'\n"),argv[arg][1]); + PrintUsage(); + *retval = -1; + return _ERROR; + } + }else break; + arg++; + } + + // src file + + if(arg")); + for(;;) { + int c; + if(done)return; + c = getchar(); + if (c == _SC('\n')) { + if (i>0 && buffer[i-1] == _SC('\\')) + { + buffer[i-1] = _SC('\n'); + } + else if(blocks==0)break; + buffer[i++] = _SC('\n'); + } + else if (c==_SC('}')) {blocks--; buffer[i++] = (SQChar)c;} + else if(c==_SC('{') && !string){ + blocks++; + buffer[i++] = (SQChar)c; + } + else if(c==_SC('"') || c==_SC('\'')){ + string=!string; + buffer[i++] = (SQChar)c; + } + else if (i >= MAXINPUT-1) { + scfprintf(stderr, _SC("sq : input line too long\n")); + break; + } + else{ + buffer[i++] = (SQChar)c; + } + } + buffer[i] = _SC('\0'); + + if(buffer[0]==_SC('=')){ + scsprintf(sq_getscratchpad(v,MAXINPUT),(size_t)MAXINPUT,_SC("return (%s)"),&buffer[1]); + memcpy(buffer,sq_getscratchpad(v,-1),(scstrlen(sq_getscratchpad(v,-1))+1)*sizeof(SQChar)); + retval=1; + } + i=scstrlen(buffer); + if(i>0){ + SQInteger oldtop=sq_gettop(v); + if(SQ_SUCCEEDED(sq_compilebuffer(v,buffer,i,_SC("interactive console"),SQTrue))){ + sq_pushroottable(v); + if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue)) && retval){ + scprintf(_SC("\n")); + sq_pushroottable(v); + sq_pushstring(v,_SC("print"),-1); + sq_get(v,-2); + sq_pushroottable(v); + sq_push(v,-4); + sq_call(v,2,SQFalse,SQTrue); + retval=0; + scprintf(_SC("\n")); + } + } + + sq_settop(v,oldtop); + } + } +} + +int main(int argc, char* argv[]) +{ + HSQUIRRELVM v; + SQInteger retval = 0; +#if defined(_MSC_VER) && defined(_DEBUG) + _CrtSetAllocHook(MemAllocHook); +#endif + + v=sq_open(1024); + sq_setprintfunc(v,printfunc,errorfunc); + + sq_pushroottable(v); + + sqstd_register_bloblib(v); + sqstd_register_iolib(v); + sqstd_register_systemlib(v); + sqstd_register_mathlib(v); + sqstd_register_stringlib(v); + + //aux library + //sets error handlers + sqstd_seterrorhandlers(v); + + //gets arguments + switch(getargs(v,argc,argv,&retval)) + { + case _INTERACTIVE: + Interactive(v); + break; + case _DONE: + case _ERROR: + default: + break; + } + + sq_close(v); + +#if defined(_MSC_VER) && defined(_DEBUG) + _getch(); + _CrtMemDumpAllObjectsSince( NULL ); +#endif + return retval; +} + diff --git a/mp/src/vscript/squirrel/sq/sq.dsp b/mp/src/vscript/squirrel/sq/sq.dsp new file mode 100644 index 00000000..a632d12b --- /dev/null +++ b/mp/src/vscript/squirrel/sq/sq.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="sq" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=sq - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sq.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sq.mak" CFG="sq - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sq - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "sq - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sq - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sqstdlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x410 /d "NDEBUG" +# ADD RSC /l 0x410 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 squirrel.lib sqstdlib.lib /nologo /subsystem:console /machine:I386 /out:"../bin/sq.exe" /libpath:"../lib" + +!ELSEIF "$(CFG)" == "sq - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\sqstdlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x410 /d "_DEBUG" +# ADD RSC /l 0x410 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 squirrel.lib sqstdlib.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/sq.exe" /pdbtype:sept /libpath:"../lib" + +!ENDIF + +# Begin Target + +# Name "sq - Win32 Release" +# Name "sq - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sq.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/mp/src/vscript/squirrel/sqstdlib/CMakeLists.txt b/mp/src/vscript/squirrel/sqstdlib/CMakeLists.txt new file mode 100644 index 00000000..34fba82f --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/CMakeLists.txt @@ -0,0 +1,52 @@ +set(SQSTDLIB_SRC sqstdaux.cpp + sqstdblob.cpp + sqstdio.cpp + sqstdmath.cpp + sqstdrex.cpp + sqstdstream.cpp + sqstdstring.cpp + sqstdsystem.cpp) + +if(NOT DISABLE_DYNAMIC) + add_library(sqstdlib SHARED ${SQSTDLIB_SRC}) + add_library(squirrel::sqstdlib ALIAS sqstdlib) + set_property(TARGET sqstdlib PROPERTY EXPORT_NAME sqstdlib) + target_link_libraries(sqstdlib squirrel) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sqstdlib EXPORT squirrel + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Libraries + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries NAMELINK_SKIP + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries + ) + install(TARGETS sqstdlib EXPORT squirrel + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development NAMELINK_ONLY + ) + endif() + target_include_directories(sqstdlib PUBLIC + "$" + "$" + ) +endif() + +if(NOT DISABLE_STATIC) + add_library(sqstdlib_static STATIC ${SQSTDLIB_SRC}) + add_library(squirrel::sqstdlib_static ALIAS sqstdlib_static) + set_property(TARGET sqstdlib_static PROPERTY EXPORT_NAME sqstdlib_static) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sqstdlib_static EXPORT squirrel ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development) + endif() + target_include_directories(sqstdlib_static PUBLIC + "$" + "$" + ) +endif() + +if(LONG_OUTPUT_NAMES) + if(NOT DISABLE_DYNAMIC) + set_target_properties(sqstdlib PROPERTIES OUTPUT_NAME sqstdlib3) + endif() + + if(NOT DISABLE_STATIC) + set_target_properties(sqstdlib_static PROPERTIES OUTPUT_NAME sqstdlib3_static) + endif() +endif() diff --git a/mp/src/vscript/squirrel/sqstdlib/Makefile b/mp/src/vscript/squirrel/sqstdlib/Makefile new file mode 100644 index 00000000..6ed1c12b --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/Makefile @@ -0,0 +1,44 @@ +SQUIRREL= .. + + +CC?= gcc +OUT?= $(SQUIRREL)/lib/libsqstdlib.a +INCZ?= -I$(SQUIRREL)/include -I. -Iinclude +DEFS= $(CC_EXTRA_FLAGS) +LIB= + +OBJS= \ + sqstdblob.o \ + sqstdio.o \ + sqstdstream.o \ + sqstdmath.o \ + sqstdsystem.o \ + sqstdstring.o \ + sqstdaux.o \ + sqstdrex.o + +SRCS= \ + sqstdblob.cpp \ + sqstdio.cpp \ + sqstdstream.cpp \ + sqstdmath.cpp \ + sqstdsystem.cpp \ + sqstdstring.cpp \ + sqstdaux.cpp \ + sqstdrex.cpp + + +sq32: + $(CC) -O2 -fno-exceptions -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sqprof: + $(CC) -O2 -pg -fno-exceptions -fno-rtti -pie -gstabs -g3 -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sq64: + $(CC) -O2 -m64 -fno-exceptions -D_SQ64 -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp new file mode 100644 index 00000000..75c16533 --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp @@ -0,0 +1,151 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include + +void sqstd_printcallstack(HSQUIRRELVM v) +{ + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { + SQStackInfos si; + SQInteger i; + SQFloat f; + const SQChar *s; + SQInteger level=1; //1 is to skip this function that is level 0 + const SQChar *name=0; + SQInteger seq=0; + pf(v,_SC("\nCALLSTACK\n")); + while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) + { + const SQChar *fn=_SC("unknown"); + const SQChar *src=_SC("unknown"); + if(si.funcname)fn=si.funcname; + if(si.source)src=si.source; + pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line); + level++; + } + level=0; + pf(v,_SC("\nLOCALS\n")); + + for(level=0;level<10;level++){ + seq=0; + while((name = sq_getlocal(v,level,seq))) + { + seq++; + switch(sq_gettype(v,-1)) + { + case OT_NULL: + pf(v,_SC("[%s] NULL\n"),name); + break; + case OT_INTEGER: + sq_getinteger(v,-1,&i); + pf(v,_SC("[%s] %d\n"),name,i); + break; + case OT_FLOAT: + sq_getfloat(v,-1,&f); + pf(v,_SC("[%s] %.14g\n"),name,f); + break; + case OT_USERPOINTER: + pf(v,_SC("[%s] USERPOINTER\n"),name); + break; + case OT_STRING: + sq_getstring(v,-1,&s); + pf(v,_SC("[%s] \"%s\"\n"),name,s); + break; + case OT_TABLE: + pf(v,_SC("[%s] TABLE\n"),name); + break; + case OT_ARRAY: + pf(v,_SC("[%s] ARRAY\n"),name); + break; + case OT_CLOSURE: + pf(v,_SC("[%s] CLOSURE\n"),name); + break; + case OT_NATIVECLOSURE: + pf(v,_SC("[%s] NATIVECLOSURE\n"),name); + break; + case OT_GENERATOR: + pf(v,_SC("[%s] GENERATOR\n"),name); + break; + case OT_USERDATA: + pf(v,_SC("[%s] USERDATA\n"),name); + break; + case OT_THREAD: + pf(v,_SC("[%s] THREAD\n"),name); + break; + case OT_CLASS: + pf(v,_SC("[%s] CLASS\n"),name); + break; + case OT_INSTANCE: + pf(v,_SC("[%s] INSTANCE\n"),name); + break; + case OT_WEAKREF: + pf(v,_SC("[%s] WEAKREF\n"),name); + break; + case OT_BOOL:{ + SQBool bval; + sq_getbool(v,-1,&bval); + pf(v,_SC("[%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); + } + break; + default: assert(0); break; + } + sq_pop(v,1); + } + } + } +} + +static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v) +{ + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { + const SQChar *sErr = 0; + if(sq_gettop(v)>=1) { + if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { + pf(v,_SC("\nAN ERROR HAS OCCURRED [%s]\n"),sErr); + } + else{ + pf(v,_SC("\nAN ERROR HAS OCCURRED [unknown]\n")); + } + sqstd_printcallstack(v); + } + } + return 0; +} + +void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column) +{ + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { + pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr); + } +} + +void sqstd_seterrorhandlers(HSQUIRRELVM v) +{ + sq_setcompilererrorhandler(v,_sqstd_compiler_error); + sq_newclosure(v,_sqstd_aux_printerror,0); + sq_seterrorhandler(v); +} + +SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const SQChar *err,...) +{ + SQInteger n=256; + va_list args; +begin: + va_start(args,err); + SQChar *b=sq_getscratchpad(v,n); + SQInteger r=scvsprintf(b,n,err,args); + va_end(args); + if (r>=n) { + n=r+1;//required+null + goto begin; + } else if (r<0) { + return sq_throwerror(v,_SC("@failed to generate formatted error message")); + } else { + return sq_throwerror(v,b); + } +} diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp new file mode 100644 index 00000000..261b938b --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp @@ -0,0 +1,283 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include +#include "sqstdstream.h" +#include "sqstdblobimpl.h" + +#define SQSTD_BLOB_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000002)) + +//Blob + + +#define SETUP_BLOB(v) \ + SQBlob *self = NULL; \ + { if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) \ + return sq_throwerror(v,_SC("invalid type tag")); } \ + if(!self || !self->IsValid()) \ + return sq_throwerror(v,_SC("the blob is invalid")); + + +static SQInteger _blob_resize(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger size; + sq_getinteger(v,2,&size); + if(!self->Resize(size)) + return sq_throwerror(v,_SC("resize failed")); + return 0; +} + +static void __swap_dword(unsigned int *n) +{ + *n=(unsigned int)(((*n&0xFF000000)>>24) | + ((*n&0x00FF0000)>>8) | + ((*n&0x0000FF00)<<8) | + ((*n&0x000000FF)<<24)); +} + +static void __swap_word(unsigned short *n) +{ + *n=(unsigned short)((*n>>8)&0x00FF)| ((*n<<8)&0xFF00); +} + +static SQInteger _blob_swap4(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger num=(self->Len()-(self->Len()%4))>>2; + unsigned int *t=(unsigned int *)self->GetBuf(); + for(SQInteger i = 0; i < num; i++) { + __swap_dword(&t[i]); + } + return 0; +} + +static SQInteger _blob_swap2(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger num=(self->Len()-(self->Len()%2))>>1; + unsigned short *t = (unsigned short *)self->GetBuf(); + for(SQInteger i = 0; i < num; i++) { + __swap_word(&t[i]); + } + return 0; +} + +static SQInteger _blob__set(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger idx,val; + sq_getinteger(v,2,&idx); + sq_getinteger(v,3,&val); + if(idx < 0 || idx >= self->Len()) + return sq_throwerror(v,_SC("index out of range")); + ((unsigned char *)self->GetBuf())[idx] = (unsigned char) val; + sq_push(v,3); + return 1; +} + +static SQInteger _blob__get(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger idx; + + if ((sq_gettype(v, 2) & SQOBJECT_NUMERIC) == 0) + { + sq_pushnull(v); + return sq_throwobject(v); + } + sq_getinteger(v,2,&idx); + if(idx < 0 || idx >= self->Len()) + return sq_throwerror(v,_SC("index out of range")); + sq_pushinteger(v,((unsigned char *)self->GetBuf())[idx]); + return 1; +} + +static SQInteger _blob__nexti(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + if(sq_gettype(v,2) == OT_NULL) { + sq_pushinteger(v, 0); + return 1; + } + SQInteger idx; + if(SQ_SUCCEEDED(sq_getinteger(v, 2, &idx))) { + if(idx+1 < self->Len()) { + sq_pushinteger(v, idx+1); + return 1; + } + sq_pushnull(v); + return 1; + } + return sq_throwerror(v,_SC("internal error (_nexti) wrong argument type")); +} + +static SQInteger _blob__typeof(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("blob"),-1); + return 1; +} + +static SQInteger _blob_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) +{ + SQBlob *self = (SQBlob*)p; + self->~SQBlob(); + sq_free(self,sizeof(SQBlob)); + return 1; +} + +static SQInteger _blob_constructor(HSQUIRRELVM v) +{ + SQInteger nparam = sq_gettop(v); + SQInteger size = 0; + if(nparam == 2) { + sq_getinteger(v, 2, &size); + } + if(size < 0) return sq_throwerror(v, _SC("cannot create blob with negative size")); + //SQBlob *b = new SQBlob(size); + + SQBlob *b = new (sq_malloc(sizeof(SQBlob)))SQBlob(size); + if(SQ_FAILED(sq_setinstanceup(v,1,b))) { + b->~SQBlob(); + sq_free(b,sizeof(SQBlob)); + return sq_throwerror(v, _SC("cannot create blob")); + } + sq_setreleasehook(v,1,_blob_releasehook); + return 0; +} + +static SQInteger _blob__cloned(HSQUIRRELVM v) +{ + SQBlob *other = NULL; + { + if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&other,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) + return SQ_ERROR; + } + //SQBlob *thisone = new SQBlob(other->Len()); + SQBlob *thisone = new (sq_malloc(sizeof(SQBlob)))SQBlob(other->Len()); + memcpy(thisone->GetBuf(),other->GetBuf(),thisone->Len()); + if(SQ_FAILED(sq_setinstanceup(v,1,thisone))) { + thisone->~SQBlob(); + sq_free(thisone,sizeof(SQBlob)); + return sq_throwerror(v, _SC("cannot clone blob")); + } + sq_setreleasehook(v,1,_blob_releasehook); + return 0; +} + +#define _DECL_BLOB_FUNC(name,nparams,typecheck) {_SC(#name),_blob_##name,nparams,typecheck} +static const SQRegFunction _blob_methods[] = { + _DECL_BLOB_FUNC(constructor,-1,_SC("xn")), + _DECL_BLOB_FUNC(resize,2,_SC("xn")), + _DECL_BLOB_FUNC(swap2,1,_SC("x")), + _DECL_BLOB_FUNC(swap4,1,_SC("x")), + _DECL_BLOB_FUNC(_set,3,_SC("xnn")), + _DECL_BLOB_FUNC(_get,2,_SC("x.")), + _DECL_BLOB_FUNC(_typeof,1,_SC("x")), + _DECL_BLOB_FUNC(_nexti,2,_SC("x")), + _DECL_BLOB_FUNC(_cloned,2,_SC("xx")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + + + +//GLOBAL FUNCTIONS + +static SQInteger _g_blob_casti2f(HSQUIRRELVM v) +{ + SQInteger i; + sq_getinteger(v,2,&i); + sq_pushfloat(v,*((const SQFloat *)&i)); + return 1; +} + +static SQInteger _g_blob_castf2i(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat(v,2,&f); + sq_pushinteger(v,*((const SQInteger *)&f)); + return 1; +} + +static SQInteger _g_blob_swap2(HSQUIRRELVM v) +{ + SQInteger i; + sq_getinteger(v,2,&i); + short s=(short)i; + sq_pushinteger(v,(s<<8)|((s>>8)&0x00FF)); + return 1; +} + +static SQInteger _g_blob_swap4(HSQUIRRELVM v) +{ + SQInteger i; + sq_getinteger(v,2,&i); + unsigned int t4 = (unsigned int)i; + __swap_dword(&t4); + sq_pushinteger(v,(SQInteger)t4); + return 1; +} + +static SQInteger _g_blob_swapfloat(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat(v,2,&f); + __swap_dword((unsigned int *)&f); + sq_pushfloat(v,f); + return 1; +} + +#define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck} +static const SQRegFunction bloblib_funcs[]={ + _DECL_GLOBALBLOB_FUNC(casti2f,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(castf2i,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + +SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr) +{ + SQBlob *blob; + if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) + return -1; + *ptr = blob->GetBuf(); + return SQ_OK; +} + +SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx) +{ + SQBlob *blob; + if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) + return -1; + return blob->Len(); +} + +SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) +{ + SQInteger top = sq_gettop(v); + sq_pushregistrytable(v); + sq_pushstring(v,_SC("std_blob"),-1); + if(SQ_SUCCEEDED(sq_get(v,-2))) { + sq_remove(v,-2); //removes the registry + sq_push(v,1); // push the this + sq_pushinteger(v,size); //size + SQBlob *blob = NULL; + if(SQ_SUCCEEDED(sq_call(v,2,SQTrue,SQFalse)) + && SQ_SUCCEEDED(sq_getinstanceup(v,-1,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) { + sq_remove(v,-2); + return blob->GetBuf(); + } + } + sq_settop(v,top); + return NULL; +} + +SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) +{ + return declare_stream(v,_SC("blob"),(SQUserPointer)SQSTD_BLOB_TYPE_TAG,_SC("std_blob"),_blob_methods,bloblib_funcs); +} + diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h b/mp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h new file mode 100644 index 00000000..bfdaddc2 --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h @@ -0,0 +1,108 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTD_BLOBIMPL_H_ +#define _SQSTD_BLOBIMPL_H_ + +struct SQBlob : public SQStream +{ + SQBlob(SQInteger size) { + _size = size; + _allocated = size; + _buf = (unsigned char *)sq_malloc(size); + memset(_buf, 0, _size); + _ptr = 0; + _owns = true; + } + virtual ~SQBlob() { + sq_free(_buf, _allocated); + } + SQInteger Write(void *buffer, SQInteger size) { + if(!CanAdvance(size)) { + GrowBufOf(_ptr + size - _size); + } + memcpy(&_buf[_ptr], buffer, size); + _ptr += size; + return size; + } + SQInteger Read(void *buffer,SQInteger size) { + SQInteger n = size; + if(!CanAdvance(size)) { + if((_size - _ptr) > 0) + n = _size - _ptr; + else return 0; + } + memcpy(buffer, &_buf[_ptr], n); + _ptr += n; + return n; + } + bool Resize(SQInteger n) { + if(!_owns) return false; + if(n != _allocated) { + unsigned char *newbuf = (unsigned char *)sq_malloc(n); + memset(newbuf,0,n); + if(_size > n) + memcpy(newbuf,_buf,n); + else + memcpy(newbuf,_buf,_size); + sq_free(_buf,_allocated); + _buf=newbuf; + _allocated = n; + if(_size > _allocated) + _size = _allocated; + if(_ptr > _allocated) + _ptr = _allocated; + } + return true; + } + bool GrowBufOf(SQInteger n) + { + bool ret = true; + if(_size + n > _allocated) { + if(_size + n > _size * 2) + ret = Resize(_size + n); + else + ret = Resize(_size * 2); + } + _size = _size + n; + return ret; + } + bool CanAdvance(SQInteger n) { + if(_ptr+n>_size)return false; + return true; + } + SQInteger Seek(SQInteger offset, SQInteger origin) { + switch(origin) { + case SQ_SEEK_SET: + if(offset > _size || offset < 0) return -1; + _ptr = offset; + break; + case SQ_SEEK_CUR: + if(_ptr + offset > _size || _ptr + offset < 0) return -1; + _ptr += offset; + break; + case SQ_SEEK_END: + if(_size + offset > _size || _size + offset < 0) return -1; + _ptr = _size + offset; + break; + default: return -1; + } + return 0; + } + bool IsValid() { + return _size == 0 || _buf?true:false; + } + bool EOS() { + return _ptr == _size; + } + SQInteger Flush() { return 0; } + SQInteger Tell() { return _ptr; } + SQInteger Len() { return _size; } + SQUserPointer GetBuf(){ return _buf; } +private: + SQInteger _size; + SQInteger _allocated; + SQInteger _ptr; + unsigned char *_buf; + bool _owns; +}; + +#endif //_SQSTD_BLOBIMPL_H_ diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdio.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdio.cpp new file mode 100644 index 00000000..52cd5158 --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdio.cpp @@ -0,0 +1,489 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include "sqstdstream.h" + +#define SQSTD_FILE_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000001)) +//basic API +SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode) +{ +#ifndef SQUNICODE + return (SQFILE)fopen(filename,mode); +#else + return (SQFILE)_wfopen(filename,mode); +#endif +} + +SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file) +{ + SQInteger ret = (SQInteger)fread(buffer,size,count,(FILE *)file); + return ret; +} + +SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file) +{ + return (SQInteger)fwrite(buffer,size,count,(FILE *)file); +} + +SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin) +{ + SQInteger realorigin; + switch(origin) { + case SQ_SEEK_CUR: realorigin = SEEK_CUR; break; + case SQ_SEEK_END: realorigin = SEEK_END; break; + case SQ_SEEK_SET: realorigin = SEEK_SET; break; + default: return -1; //failed + } + return fseek((FILE *)file,(long)offset,(int)realorigin); +} + +SQInteger sqstd_ftell(SQFILE file) +{ + return ftell((FILE *)file); +} + +SQInteger sqstd_fflush(SQFILE file) +{ + return fflush((FILE *)file); +} + +SQInteger sqstd_fclose(SQFILE file) +{ + return fclose((FILE *)file); +} + +SQInteger sqstd_feof(SQFILE file) +{ + return feof((FILE *)file); +} + +//File +struct SQFile : public SQStream { + SQFile() { _handle = NULL; _owns = false;} + SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;} + virtual ~SQFile() { Close(); } + bool Open(const SQChar *filename ,const SQChar *mode) { + Close(); + if( (_handle = sqstd_fopen(filename,mode)) ) { + _owns = true; + return true; + } + return false; + } + void Close() { + if(_handle && _owns) { + sqstd_fclose(_handle); + _handle = NULL; + _owns = false; + } + } + SQInteger Read(void *buffer,SQInteger size) { + return sqstd_fread(buffer,1,size,_handle); + } + SQInteger Write(void *buffer,SQInteger size) { + return sqstd_fwrite(buffer,1,size,_handle); + } + SQInteger Flush() { + return sqstd_fflush(_handle); + } + SQInteger Tell() { + return sqstd_ftell(_handle); + } + SQInteger Len() { + SQInteger prevpos=Tell(); + Seek(0,SQ_SEEK_END); + SQInteger size=Tell(); + Seek(prevpos,SQ_SEEK_SET); + return size; + } + SQInteger Seek(SQInteger offset, SQInteger origin) { + return sqstd_fseek(_handle,offset,origin); + } + bool IsValid() { return _handle?true:false; } + bool EOS() { return Tell()==Len()?true:false;} + SQFILE GetHandle() {return _handle;} +private: + SQFILE _handle; + bool _owns; +}; + +static SQInteger _file__typeof(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("file"),-1); + return 1; +} + +static SQInteger _file_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) +{ + SQFile *self = (SQFile*)p; + self->~SQFile(); + sq_free(self,sizeof(SQFile)); + return 1; +} + +static SQInteger _file_constructor(HSQUIRRELVM v) +{ + const SQChar *filename,*mode; + bool owns = true; + SQFile *f; + SQFILE newf; + if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) { + sq_getstring(v, 2, &filename); + sq_getstring(v, 3, &mode); + newf = sqstd_fopen(filename, mode); + if(!newf) return sq_throwerror(v, _SC("cannot open file")); + } else if(sq_gettype(v,2) == OT_USERPOINTER) { + owns = !(sq_gettype(v,3) == OT_NULL); + sq_getuserpointer(v,2,&newf); + } else { + return sq_throwerror(v,_SC("wrong parameter")); + } + + f = new (sq_malloc(sizeof(SQFile)))SQFile(newf,owns); + if(SQ_FAILED(sq_setinstanceup(v,1,f))) { + f->~SQFile(); + sq_free(f,sizeof(SQFile)); + return sq_throwerror(v, _SC("cannot create blob with negative size")); + } + sq_setreleasehook(v,1,_file_releasehook); + return 0; +} + +static SQInteger _file_close(HSQUIRRELVM v) +{ + SQFile *self = NULL; + if(SQ_SUCCEEDED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_FILE_TYPE_TAG)) + && self != NULL) + { + self->Close(); + } + return 0; +} + +//bindings +#define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck} +static const SQRegFunction _file_methods[] = { + _DECL_FILE_FUNC(constructor,3,_SC("x")), + _DECL_FILE_FUNC(_typeof,1,_SC("x")), + _DECL_FILE_FUNC(close,1,_SC("x")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + + + +SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own) +{ + SQInteger top = sq_gettop(v); + sq_pushregistrytable(v); + sq_pushstring(v,_SC("std_file"),-1); + if(SQ_SUCCEEDED(sq_get(v,-2))) { + sq_remove(v,-2); //removes the registry + sq_pushroottable(v); // push the this + sq_pushuserpointer(v,file); //file + if(own){ + sq_pushinteger(v,1); //true + } + else{ + sq_pushnull(v); //false + } + if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) { + sq_remove(v,-2); + return SQ_OK; + } + } + sq_settop(v,top); + return SQ_ERROR; +} + +SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file) +{ + SQFile *fileobj = NULL; + if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) { + *file = fileobj->GetHandle(); + return SQ_OK; + } + return sq_throwerror(v,_SC("not a file")); +} + + + +#define IO_BUFFER_SIZE 2048 +struct IOBuffer { + unsigned char buffer[IO_BUFFER_SIZE]; + SQInteger size; + SQInteger ptr; + SQFILE file; +}; + +SQInteger _read_byte(IOBuffer *iobuffer) +{ + if(iobuffer->ptr < iobuffer->size) { + + SQInteger ret = iobuffer->buffer[iobuffer->ptr]; + iobuffer->ptr++; + return ret; + } + else { + if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) + { + SQInteger ret = iobuffer->buffer[0]; + iobuffer->ptr = 1; + return ret; + } + } + + return 0; +} + +SQInteger _read_two_bytes(IOBuffer *iobuffer) +{ + if(iobuffer->ptr < iobuffer->size) { + if(iobuffer->size < 2) return 0; + SQInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]); + iobuffer->ptr += 2; + return ret; + } + else { + if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) + { + if(iobuffer->size < 2) return 0; + SQInteger ret = *((const wchar_t*)&iobuffer->buffer[0]); + iobuffer->ptr = 2; + return ret; + } + } + + return 0; +} + +static SQInteger _io_file_lexfeed_PLAIN(SQUserPointer iobuf) +{ + IOBuffer *iobuffer = (IOBuffer *)iobuf; + return _read_byte(iobuffer); + +} + +#ifdef SQUNICODE +static SQInteger _io_file_lexfeed_UTF8(SQUserPointer iobuf) +{ + IOBuffer *iobuffer = (IOBuffer *)iobuf; +#define READ(iobuf) \ + if((inchar = (unsigned char)_read_byte(iobuf)) == 0) \ + return 0; + + static const SQInteger utf8_lengths[16] = + { + 1,1,1,1,1,1,1,1, /* 0000 to 0111 : 1 byte (plain ASCII) */ + 0,0,0,0, /* 1000 to 1011 : not valid */ + 2,2, /* 1100, 1101 : 2 bytes */ + 3, /* 1110 : 3 bytes */ + 4 /* 1111 :4 bytes */ + }; + static const unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07}; + unsigned char inchar; + SQInteger c = 0; + READ(iobuffer); + c = inchar; + // + if(c >= 0x80) { + SQInteger tmp; + SQInteger codelen = utf8_lengths[c>>4]; + if(codelen == 0) + return 0; + //"invalid UTF-8 stream"; + tmp = c&byte_masks[codelen]; + for(SQInteger n = 0; n < codelen-1; n++) { + tmp<<=6; + READ(iobuffer); + tmp |= inchar & 0x3F; + } + c = tmp; + } + return c; +} +#endif + +static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer iobuf) +{ + SQInteger ret; + IOBuffer *iobuffer = (IOBuffer *)iobuf; + if( (ret = _read_two_bytes(iobuffer)) > 0 ) + return ret; + return 0; +} + +static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer iobuf) +{ + SQInteger c; + IOBuffer *iobuffer = (IOBuffer *)iobuf; + if( (c = _read_two_bytes(iobuffer)) > 0 ) { + c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00); + return c; + } + return 0; +} + +SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size) +{ + SQInteger ret; + if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret; + return -1; +} + +SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size) +{ + return sqstd_fwrite(p,1,size,(SQFILE)file); +} + +SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror) +{ + SQFILE file = sqstd_fopen(filename,_SC("rb")); + + SQInteger ret; + unsigned short us; + unsigned char uc; + SQLEXREADFUNC func = _io_file_lexfeed_PLAIN; + if(file){ + ret = sqstd_fread(&us,1,2,file); + if(ret != 2) { + //probably an empty file + us = 0; + } + if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE + sqstd_fseek(file,0,SQ_SEEK_SET); + if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) { + sqstd_fclose(file); + return SQ_OK; + } + } + else { //SCRIPT + + switch(us) + { + //gotta swap the next 2 lines on BIG endian machines + case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian; + case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian; + case 0xBBEF: + if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) { + sqstd_fclose(file); + return sq_throwerror(v,_SC("io error")); + } + if(uc != 0xBF) { + sqstd_fclose(file); + return sq_throwerror(v,_SC("Unrecognized encoding")); + } +#ifdef SQUNICODE + func = _io_file_lexfeed_UTF8; +#else + func = _io_file_lexfeed_PLAIN; +#endif + break;//UTF-8 ; + default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii + } + IOBuffer buffer; + buffer.ptr = 0; + buffer.size = 0; + buffer.file = file; + if(SQ_SUCCEEDED(sq_compile(v,func,&buffer,filename,printerror))){ + sqstd_fclose(file); + return SQ_OK; + } + } + sqstd_fclose(file); + return SQ_ERROR; + } + return sq_throwerror(v,_SC("cannot open the file")); +} + +SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror) +{ + //at least one entry must exist in order for us to push it as the environment + if(sq_gettop(v) == 0) + return sq_throwerror(v,_SC("environment table expected")); + + if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) { + sq_push(v,-2); + if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) { + sq_remove(v,retval?-2:-1); //removes the closure + return 1; + } + sq_pop(v,1); //removes the closure + } + return SQ_ERROR; +} + +SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename) +{ + SQFILE file = sqstd_fopen(filename,_SC("wb+")); + if(!file) return sq_throwerror(v,_SC("cannot open the file")); + if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) { + sqstd_fclose(file); + return SQ_OK; + } + sqstd_fclose(file); + return SQ_ERROR; //forward the error +} + +SQInteger _g_io_loadfile(HSQUIRRELVM v) +{ + const SQChar *filename; + SQBool printerror = SQFalse; + sq_getstring(v,2,&filename); + if(sq_gettop(v) >= 3) { + sq_getbool(v,3,&printerror); + } + if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) + return 1; + return SQ_ERROR; //propagates the error +} + +SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v) +{ + const SQChar *filename; + sq_getstring(v,2,&filename); + if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename))) + return 1; + return SQ_ERROR; //propagates the error +} + +SQInteger _g_io_dofile(HSQUIRRELVM v) +{ + const SQChar *filename; + SQBool printerror = SQFalse; + sq_getstring(v,2,&filename); + if(sq_gettop(v) >= 3) { + sq_getbool(v,3,&printerror); + } + sq_push(v,1); //repush the this + if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror))) + return 1; + return SQ_ERROR; //propagates the error +} + +#define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck} +static const SQRegFunction iolib_funcs[]={ + _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")), + _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")), + _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + +SQRESULT sqstd_register_iolib(HSQUIRRELVM v) +{ + SQInteger top = sq_gettop(v); + //create delegate + declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs); + sq_pushstring(v,_SC("stdout"),-1); + sqstd_createfile(v,stdout,SQFalse); + sq_newslot(v,-3,SQFalse); + sq_pushstring(v,_SC("stdin"),-1); + sqstd_createfile(v,stdin,SQFalse); + sq_newslot(v,-3,SQFalse); + sq_pushstring(v,_SC("stderr"),-1); + sqstd_createfile(v,stderr,SQFalse); + sq_newslot(v,-3,SQFalse); + sq_settop(v,top); + return SQ_OK; +} diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp b/mp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp new file mode 100644 index 00000000..ef5b7f48 --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp @@ -0,0 +1,131 @@ +# Microsoft Developer Studio Project File - Name="sqstdlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=sqstdlib - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sqstdlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sqstdlib.mak" CFG="sqstdlib - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sqstdlib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "sqstdlib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sqstdlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x410 /d "NDEBUG" +# ADD RSC /l 0x410 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\sqstdlib.lib" + +!ELSEIF "$(CFG)" == "sqstdlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x410 /d "_DEBUG" +# ADD RSC /l 0x410 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\sqstdlib.lib" + +!ENDIF + +# Begin Target + +# Name "sqstdlib - Win32 Release" +# Name "sqstdlib - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sqstdblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdio.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdmath.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdrex.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdstream.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdstring.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdaux.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdsystem.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\sqstdblobimpl.h +# End Source File +# Begin Source File + +SOURCE=.\sqstdstream.h +# End Source File +# End Group +# End Target +# End Project diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp new file mode 100644 index 00000000..c41ffd5e --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp @@ -0,0 +1,107 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include + +#define SINGLE_ARG_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ + SQFloat f; \ + sq_getfloat(v,2,&f); \ + sq_pushfloat(v,(SQFloat)_funcname(f)); \ + return 1; \ +} + +#define TWO_ARGS_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ + SQFloat p1,p2; \ + sq_getfloat(v,2,&p1); \ + sq_getfloat(v,3,&p2); \ + sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \ + return 1; \ +} + +static SQInteger math_srand(HSQUIRRELVM v) +{ + SQInteger i; + if(SQ_FAILED(sq_getinteger(v,2,&i))) + return sq_throwerror(v,_SC("invalid param")); + srand((unsigned int)i); + return 0; +} + +static SQInteger math_rand(HSQUIRRELVM v) +{ + sq_pushinteger(v,rand()); + return 1; +} + +static SQInteger math_abs(HSQUIRRELVM v) +{ + SQInteger n; + sq_getinteger(v,2,&n); + sq_pushinteger(v,(SQInteger)abs((int)n)); + return 1; +} + +SINGLE_ARG_FUNC(sqrt) +SINGLE_ARG_FUNC(fabs) +SINGLE_ARG_FUNC(sin) +SINGLE_ARG_FUNC(cos) +SINGLE_ARG_FUNC(asin) +SINGLE_ARG_FUNC(acos) +SINGLE_ARG_FUNC(log) +SINGLE_ARG_FUNC(log10) +SINGLE_ARG_FUNC(tan) +SINGLE_ARG_FUNC(atan) +TWO_ARGS_FUNC(atan2) +TWO_ARGS_FUNC(pow) +SINGLE_ARG_FUNC(floor) +SINGLE_ARG_FUNC(ceil) +SINGLE_ARG_FUNC(exp) + +#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),math_##name,nparams,tycheck} +static const SQRegFunction mathlib_funcs[] = { + _DECL_FUNC(sqrt,2,_SC(".n")), + _DECL_FUNC(sin,2,_SC(".n")), + _DECL_FUNC(cos,2,_SC(".n")), + _DECL_FUNC(asin,2,_SC(".n")), + _DECL_FUNC(acos,2,_SC(".n")), + _DECL_FUNC(log,2,_SC(".n")), + _DECL_FUNC(log10,2,_SC(".n")), + _DECL_FUNC(tan,2,_SC(".n")), + _DECL_FUNC(atan,2,_SC(".n")), + _DECL_FUNC(atan2,3,_SC(".nn")), + _DECL_FUNC(pow,3,_SC(".nn")), + _DECL_FUNC(floor,2,_SC(".n")), + _DECL_FUNC(ceil,2,_SC(".n")), + _DECL_FUNC(exp,2,_SC(".n")), + _DECL_FUNC(srand,2,_SC(".n")), + _DECL_FUNC(rand,1,NULL), + _DECL_FUNC(fabs,2,_SC(".n")), + _DECL_FUNC(abs,2,_SC(".n")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_FUNC + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) +{ + SQInteger i=0; + while(mathlib_funcs[i].name!=0) { + sq_pushstring(v,mathlib_funcs[i].name,-1); + sq_newclosure(v,mathlib_funcs[i].f,0); + sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask); + sq_setnativeclosurename(v,-1,mathlib_funcs[i].name); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_pushstring(v,_SC("RAND_MAX"),-1); + sq_pushinteger(v,RAND_MAX); + sq_newslot(v,-3,SQFalse); + sq_pushstring(v,_SC("PI"),-1); + sq_pushfloat(v,(SQFloat)M_PI); + sq_newslot(v,-3,SQFalse); + return SQ_OK; +} diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp new file mode 100644 index 00000000..d0583a6b --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp @@ -0,0 +1,666 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include + +#ifdef _DEBUG +#include + +static const SQChar *g_nnames[] = +{ + _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"), + _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"), + _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"), + _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB"),_SC("OP_MB") +}; + +#endif + +#define OP_GREEDY (MAX_CHAR+1) // * + ? {n} +#define OP_OR (MAX_CHAR+2) +#define OP_EXPR (MAX_CHAR+3) //parentesis () +#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:) +#define OP_DOT (MAX_CHAR+5) +#define OP_CLASS (MAX_CHAR+6) +#define OP_CCLASS (MAX_CHAR+7) +#define OP_NCLASS (MAX_CHAR+8) //negates class the [^ +#define OP_RANGE (MAX_CHAR+9) +#define OP_CHAR (MAX_CHAR+10) +#define OP_EOL (MAX_CHAR+11) +#define OP_BOL (MAX_CHAR+12) +#define OP_WB (MAX_CHAR+13) +#define OP_MB (MAX_CHAR+14) //match balanced + +#define SQREX_SYMBOL_ANY_CHAR ('.') +#define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') +#define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') +#define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') +#define SQREX_SYMBOL_BRANCH ('|') +#define SQREX_SYMBOL_END_OF_STRING ('$') +#define SQREX_SYMBOL_BEGINNING_OF_STRING ('^') +#define SQREX_SYMBOL_ESCAPE_CHAR ('\\') + + +typedef int SQRexNodeType; + +typedef struct tagSQRexNode{ + SQRexNodeType type; + SQInteger left; + SQInteger right; + SQInteger next; +}SQRexNode; + +struct SQRex{ + const SQChar *_eol; + const SQChar *_bol; + const SQChar *_p; + SQInteger _first; + SQInteger _op; + SQRexNode *_nodes; + SQInteger _nallocated; + SQInteger _nsize; + SQInteger _nsubexpr; + SQRexMatch *_matches; + SQInteger _currsubexp; + void *_jmpbuf; + const SQChar **_error; +}; + +static SQInteger sqstd_rex_list(SQRex *exp); + +static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type) +{ + SQRexNode n; + n.type = type; + n.next = n.right = n.left = -1; + if(type == OP_EXPR) + n.right = exp->_nsubexpr++; + if(exp->_nallocated < (exp->_nsize + 1)) { + SQInteger oldsize = exp->_nallocated; + exp->_nallocated *= 2; + exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode)); + } + exp->_nodes[exp->_nsize++] = n; + SQInteger newid = exp->_nsize - 1; + return (SQInteger)newid; +} + +static void sqstd_rex_error(SQRex *exp,const SQChar *error) +{ + if(exp->_error) *exp->_error = error; + longjmp(*((jmp_buf*)exp->_jmpbuf),-1); +} + +static void sqstd_rex_expect(SQRex *exp, SQInteger n){ + if((*exp->_p) != n) + sqstd_rex_error(exp, _SC("expected paren")); + exp->_p++; +} + +static SQChar sqstd_rex_escapechar(SQRex *exp) +{ + if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){ + exp->_p++; + switch(*exp->_p) { + case 'v': exp->_p++; return '\v'; + case 'n': exp->_p++; return '\n'; + case 't': exp->_p++; return '\t'; + case 'r': exp->_p++; return '\r'; + case 'f': exp->_p++; return '\f'; + default: return (*exp->_p++); + } + } else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,_SC("letter expected")); + return (*exp->_p++); +} + +static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid) +{ + SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS); + exp->_nodes[n].left = classid; + return n; +} + +static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass) +{ + SQChar t; + if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) { + exp->_p++; + switch(*exp->_p) { + case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n'); + case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t'); + case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r'); + case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f'); + case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v'); + case 'a': case 'A': case 'w': case 'W': case 's': case 'S': + case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': + case 'p': case 'P': case 'l': case 'u': + { + t = *exp->_p; exp->_p++; + return sqstd_rex_charclass(exp,t); + } + case 'm': + { + SQChar cb, ce; //cb = character begin match ce = character end match + cb = *++exp->_p; //skip 'm' + ce = *++exp->_p; + exp->_p++; //points to the next char to be parsed + if ((!cb) || (!ce)) sqstd_rex_error(exp,_SC("balanced chars expected")); + if ( cb == ce ) sqstd_rex_error(exp,_SC("open/close char can't be the same")); + SQInteger node = sqstd_rex_newnode(exp,OP_MB); + exp->_nodes[node].left = cb; + exp->_nodes[node].right = ce; + return node; + } + case 0: + sqstd_rex_error(exp,_SC("letter expected for argument of escape sequence")); + break; + case 'b': + case 'B': + if(!isclass) { + SQInteger node = sqstd_rex_newnode(exp,OP_WB); + exp->_nodes[node].left = *exp->_p; + exp->_p++; + return node; + } //else default + default: + t = *exp->_p; exp->_p++; + return sqstd_rex_newnode(exp,t); + } + } + else if(!scisprint(*exp->_p)) { + + sqstd_rex_error(exp,_SC("letter expected")); + } + t = *exp->_p; exp->_p++; + return sqstd_rex_newnode(exp,t); +} +static SQInteger sqstd_rex_class(SQRex *exp) +{ + SQInteger ret = -1; + SQInteger first = -1,chain; + if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){ + ret = sqstd_rex_newnode(exp,OP_NCLASS); + exp->_p++; + }else ret = sqstd_rex_newnode(exp,OP_CLASS); + + if(*exp->_p == ']') sqstd_rex_error(exp,_SC("empty class")); + chain = ret; + while(*exp->_p != ']' && exp->_p != exp->_eol) { + if(*exp->_p == '-' && first != -1){ + SQInteger r; + if(*exp->_p++ == ']') sqstd_rex_error(exp,_SC("unfinished range")); + r = sqstd_rex_newnode(exp,OP_RANGE); + if(exp->_nodes[first].type>*exp->_p) sqstd_rex_error(exp,_SC("invalid range")); + if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,_SC("cannot use character classes in ranges")); + exp->_nodes[r].left = exp->_nodes[first].type; + SQInteger t = sqstd_rex_escapechar(exp); + exp->_nodes[r].right = t; + exp->_nodes[chain].next = r; + chain = r; + first = -1; + } + else{ + if(first!=-1){ + SQInteger c = first; + exp->_nodes[chain].next = c; + chain = c; + first = sqstd_rex_charnode(exp,SQTrue); + } + else{ + first = sqstd_rex_charnode(exp,SQTrue); + } + } + } + if(first!=-1){ + SQInteger c = first; + exp->_nodes[chain].next = c; + } + /* hack? */ + exp->_nodes[ret].left = exp->_nodes[ret].next; + exp->_nodes[ret].next = -1; + return ret; +} + +static SQInteger sqstd_rex_parsenumber(SQRex *exp) +{ + SQInteger ret = *exp->_p-'0'; + SQInteger positions = 10; + exp->_p++; + while(isdigit(*exp->_p)) { + ret = ret*10+(*exp->_p++-'0'); + if(positions==1000000000) sqstd_rex_error(exp,_SC("overflow in numeric constant")); + positions *= 10; + }; + return ret; +} + +static SQInteger sqstd_rex_element(SQRex *exp) +{ + SQInteger ret = -1; + switch(*exp->_p) + { + case '(': { + SQInteger expr; + exp->_p++; + + + if(*exp->_p =='?') { + exp->_p++; + sqstd_rex_expect(exp,':'); + expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR); + } + else + expr = sqstd_rex_newnode(exp,OP_EXPR); + SQInteger newn = sqstd_rex_list(exp); + exp->_nodes[expr].left = newn; + ret = expr; + sqstd_rex_expect(exp,')'); + } + break; + case '[': + exp->_p++; + ret = sqstd_rex_class(exp); + sqstd_rex_expect(exp,']'); + break; + case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break; + case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break; + default: + ret = sqstd_rex_charnode(exp,SQFalse); + break; + } + + + SQBool isgreedy = SQFalse; + unsigned short p0 = 0, p1 = 0; + switch(*exp->_p){ + case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; + case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; + case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break; + case '{': + exp->_p++; + if(!isdigit(*exp->_p)) sqstd_rex_error(exp,_SC("number expected")); + p0 = (unsigned short)sqstd_rex_parsenumber(exp); + /*******************************/ + switch(*exp->_p) { + case '}': + p1 = p0; exp->_p++; + break; + case ',': + exp->_p++; + p1 = 0xFFFF; + if(isdigit(*exp->_p)){ + p1 = (unsigned short)sqstd_rex_parsenumber(exp); + } + sqstd_rex_expect(exp,'}'); + break; + default: + sqstd_rex_error(exp,_SC(", or } expected")); + } + /*******************************/ + isgreedy = SQTrue; + break; + + } + if(isgreedy) { + SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY); + exp->_nodes[nnode].left = ret; + exp->_nodes[nnode].right = ((p0)<<16)|p1; + ret = nnode; + } + + if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { + SQInteger nnode = sqstd_rex_element(exp); + exp->_nodes[ret].next = nnode; + } + + return ret; +} + +static SQInteger sqstd_rex_list(SQRex *exp) +{ + SQInteger ret=-1,e; + if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) { + exp->_p++; + ret = sqstd_rex_newnode(exp,OP_BOL); + } + e = sqstd_rex_element(exp); + if(ret != -1) { + exp->_nodes[ret].next = e; + } + else ret = e; + + if(*exp->_p == SQREX_SYMBOL_BRANCH) { + SQInteger temp,tright; + exp->_p++; + temp = sqstd_rex_newnode(exp,OP_OR); + exp->_nodes[temp].left = ret; + tright = sqstd_rex_list(exp); + exp->_nodes[temp].right = tright; + ret = temp; + } + return ret; +} + +static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c) +{ + switch(cclass) { + case 'a': return isalpha(c)?SQTrue:SQFalse; + case 'A': return !isalpha(c)?SQTrue:SQFalse; + case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse; + case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse; + case 's': return isspace(c)?SQTrue:SQFalse; + case 'S': return !isspace(c)?SQTrue:SQFalse; + case 'd': return isdigit(c)?SQTrue:SQFalse; + case 'D': return !isdigit(c)?SQTrue:SQFalse; + case 'x': return isxdigit(c)?SQTrue:SQFalse; + case 'X': return !isxdigit(c)?SQTrue:SQFalse; + case 'c': return iscntrl(c)?SQTrue:SQFalse; + case 'C': return !iscntrl(c)?SQTrue:SQFalse; + case 'p': return ispunct(c)?SQTrue:SQFalse; + case 'P': return !ispunct(c)?SQTrue:SQFalse; + case 'l': return islower(c)?SQTrue:SQFalse; + case 'u': return isupper(c)?SQTrue:SQFalse; + } + return SQFalse; /*cannot happen*/ +} + +static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQChar c) +{ + do { + switch(node->type) { + case OP_RANGE: + if(c >= node->left && c <= node->right) return SQTrue; + break; + case OP_CCLASS: + if(sqstd_rex_matchcclass(node->left,c)) return SQTrue; + break; + default: + if(c == node->type)return SQTrue; + } + } while((node->next != -1) && (node = &exp->_nodes[node->next])); + return SQFalse; +} + +static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next) +{ + + SQRexNodeType type = node->type; + switch(type) { + case OP_GREEDY: { + //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; + SQRexNode *greedystop = NULL; + SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0; + const SQChar *s=str, *good = str; + + if(node->next != -1) { + greedystop = &exp->_nodes[node->next]; + } + else { + greedystop = next; + } + + while((nmaches == 0xFFFF || nmaches < p1)) { + + const SQChar *stop; + if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop))) + break; + nmaches++; + good=s; + if(greedystop) { + //checks that 0 matches satisfy the expression(if so skips) + //if not would always stop(for instance if is a '?') + if(greedystop->type != OP_GREEDY || + (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0)) + { + SQRexNode *gnext = NULL; + if(greedystop->next != -1) { + gnext = &exp->_nodes[greedystop->next]; + }else if(next && next->next != -1){ + gnext = &exp->_nodes[next->next]; + } + stop = sqstd_rex_matchnode(exp,greedystop,s,gnext); + if(stop) { + //if satisfied stop it + if(p0 == p1 && p0 == nmaches) break; + else if(nmaches >= p0 && p1 == 0xFFFF) break; + else if(nmaches >= p0 && nmaches <= p1) break; + } + } + } + + if(s >= exp->_eol) + break; + } + if(p0 == p1 && p0 == nmaches) return good; + else if(nmaches >= p0 && p1 == 0xFFFF) return good; + else if(nmaches >= p0 && nmaches <= p1) return good; + return NULL; + } + case OP_OR: { + const SQChar *asd = str; + SQRexNode *temp=&exp->_nodes[node->left]; + while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + asd = str; + temp = &exp->_nodes[node->right]; + while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + return NULL; + break; + } + case OP_EXPR: + case OP_NOCAPEXPR:{ + SQRexNode *n = &exp->_nodes[node->left]; + const SQChar *cur = str; + SQInteger capture = -1; + if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { + capture = exp->_currsubexp; + exp->_matches[capture].begin = cur; + exp->_currsubexp++; + } + SQInteger tempcap = exp->_currsubexp; + do { + SQRexNode *subnext = NULL; + if(n->next != -1) { + subnext = &exp->_nodes[n->next]; + }else { + subnext = next; + } + if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) { + if(capture != -1){ + exp->_matches[capture].begin = 0; + exp->_matches[capture].len = 0; + } + return NULL; + } + } while((n->next != -1) && (n = &exp->_nodes[n->next])); + + exp->_currsubexp = tempcap; + if(capture != -1) + exp->_matches[capture].len = cur - exp->_matches[capture].begin; + return cur; + } + case OP_WB: + if((str == exp->_bol && !isspace(*str)) + || (str == exp->_eol && !isspace(*(str-1))) + || (!isspace(*str) && isspace(*(str+1))) + || (isspace(*str) && !isspace(*(str+1))) ) { + return (node->left == 'b')?str:NULL; + } + return (node->left == 'b')?NULL:str; + case OP_BOL: + if(str == exp->_bol) return str; + return NULL; + case OP_EOL: + if(str == exp->_eol) return str; + return NULL; + case OP_DOT:{ + if (str == exp->_eol) return NULL; + str++; + } + return str; + case OP_NCLASS: + case OP_CLASS: + if (str == exp->_eol) return NULL; + if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) { + str++; + return str; + } + return NULL; + case OP_CCLASS: + if (str == exp->_eol) return NULL; + if(sqstd_rex_matchcclass(node->left,*str)) { + str++; + return str; + } + return NULL; + case OP_MB: + { + SQInteger cb = node->left; //char that opens a balanced expression + if(*str != cb) return NULL; // string doesnt start with open char + SQInteger ce = node->right; //char that closes a balanced expression + SQInteger cont = 1; + const SQChar *streol = exp->_eol; + while (++str < streol) { + if (*str == ce) { + if (--cont == 0) { + return ++str; + } + } + else if (*str == cb) cont++; + } + } + return NULL; // string ends out of balance + default: /* char */ + if (str == exp->_eol) return NULL; + if(*str != node->type) return NULL; + str++; + return str; + } + return NULL; +} + +/* public api */ +SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error) +{ + SQRex * volatile exp = (SQRex *)sq_malloc(sizeof(SQRex)); // "volatile" is needed for setjmp() + exp->_eol = exp->_bol = NULL; + exp->_p = pattern; + exp->_nallocated = (SQInteger)scstrlen(pattern) * sizeof(SQChar); + exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode)); + exp->_nsize = 0; + exp->_matches = 0; + exp->_nsubexpr = 0; + exp->_first = sqstd_rex_newnode(exp,OP_EXPR); + exp->_error = error; + exp->_jmpbuf = sq_malloc(sizeof(jmp_buf)); + if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { + SQInteger res = sqstd_rex_list(exp); + exp->_nodes[exp->_first].left = res; + if(*exp->_p!='\0') + sqstd_rex_error(exp,_SC("unexpected character")); +#ifdef _DEBUG + { + SQInteger nsize,i; + SQRexNode *t; + nsize = exp->_nsize; + t = &exp->_nodes[0]; + scprintf(_SC("\n")); + for(i = 0;i < nsize; i++) { + if(exp->_nodes[i].type>MAX_CHAR) + scprintf(_SC("[%02d] %10s "), (SQInt32)i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); + else + scprintf(_SC("[%02d] %10c "), (SQInt32)i,exp->_nodes[i].type); + scprintf(_SC("left %02d right %02d next %02d\n"), (SQInt32)exp->_nodes[i].left, (SQInt32)exp->_nodes[i].right, (SQInt32)exp->_nodes[i].next); + } + scprintf(_SC("\n")); + } +#endif + exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch)); + memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch)); + } + else{ + sqstd_rex_free(exp); + return NULL; + } + return exp; +} + +void sqstd_rex_free(SQRex *exp) +{ + if(exp) { + if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode)); + if(exp->_jmpbuf) sq_free(exp->_jmpbuf,sizeof(jmp_buf)); + if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch)); + sq_free(exp,sizeof(SQRex)); + } +} + +SQBool sqstd_rex_match(SQRex* exp,const SQChar* text) +{ + const SQChar* res = NULL; + exp->_bol = text; + exp->_eol = text + scstrlen(text); + exp->_currsubexp = 0; + res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL); + if(res == NULL || res != exp->_eol) + return SQFalse; + return SQTrue; +} + +SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end) +{ + const SQChar *cur = NULL; + SQInteger node = exp->_first; + if(text_begin >= text_end) return SQFalse; + exp->_bol = text_begin; + exp->_eol = text_end; + do { + cur = text_begin; + while(node != -1) { + exp->_currsubexp = 0; + cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL); + if(!cur) + break; + node = exp->_nodes[node].next; + } + text_begin++; + } while(cur == NULL && text_begin != text_end); + + if(cur == NULL) + return SQFalse; + + --text_begin; + + if(out_begin) *out_begin = text_begin; + if(out_end) *out_end = cur; + return SQTrue; +} + +SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end) +{ + return sqstd_rex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end); +} + +SQInteger sqstd_rex_getsubexpcount(SQRex* exp) +{ + return exp->_nsubexpr; +} + +SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp) +{ + if( n<0 || n >= exp->_nsubexpr) return SQFalse; + *subexp = exp->_matches[n]; + return SQTrue; +} + diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp new file mode 100644 index 00000000..b5c47cfb --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp @@ -0,0 +1,336 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include +#include +#include +#include "sqstdstream.h" +#include "sqstdblobimpl.h" + +#define SETUP_STREAM(v) \ + SQStream *self = NULL; \ + if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)))) \ + return sq_throwerror(v,_SC("invalid type tag")); \ + if(!self || !self->IsValid()) \ + return sq_throwerror(v,_SC("the stream is invalid")); + +SQInteger _stream_readblob(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQUserPointer data,blobp; + SQInteger size,res; + sq_getinteger(v,2,&size); + if(size > self->Len()) { + size = self->Len(); + } + data = sq_getscratchpad(v,size); + res = self->Read(data,size); + if(res <= 0) + return sq_throwerror(v,_SC("no data left to read")); + blobp = sqstd_createblob(v,res); + memcpy(blobp,data,res); + return 1; +} + +#define SAFE_READN(ptr,len) { \ + if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \ + } +SQInteger _stream_readn(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQInteger format; + sq_getinteger(v, 2, &format); + switch(format) { + case 'l': { + SQInteger i; + SAFE_READN(&i, sizeof(i)); + sq_pushinteger(v, i); + } + break; + case 'i': { + SQInt32 i; + SAFE_READN(&i, sizeof(i)); + sq_pushinteger(v, i); + } + break; + case 's': { + short s; + SAFE_READN(&s, sizeof(short)); + sq_pushinteger(v, s); + } + break; + case 'w': { + unsigned short w; + SAFE_READN(&w, sizeof(unsigned short)); + sq_pushinteger(v, w); + } + break; + case 'c': { + char c; + SAFE_READN(&c, sizeof(char)); + sq_pushinteger(v, c); + } + break; + case 'b': { + unsigned char c; + SAFE_READN(&c, sizeof(unsigned char)); + sq_pushinteger(v, c); + } + break; + case 'f': { + float f; + SAFE_READN(&f, sizeof(float)); + sq_pushfloat(v, f); + } + break; + case 'd': { + double d; + SAFE_READN(&d, sizeof(double)); + sq_pushfloat(v, (SQFloat)d); + } + break; + default: + return sq_throwerror(v, _SC("invalid format")); + } + return 1; +} + +SQInteger _stream_writeblob(HSQUIRRELVM v) +{ + SQUserPointer data; + SQInteger size; + SETUP_STREAM(v); + if(SQ_FAILED(sqstd_getblob(v,2,&data))) + return sq_throwerror(v,_SC("invalid parameter")); + size = sqstd_getblobsize(v,2); + if(self->Write(data,size) != size) + return sq_throwerror(v,_SC("io error")); + sq_pushinteger(v,size); + return 1; +} + +SQInteger _stream_writen(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQInteger format, ti; + SQFloat tf; + sq_getinteger(v, 3, &format); + switch(format) { + case 'l': { + SQInteger i; + sq_getinteger(v, 2, &ti); + i = ti; + self->Write(&i, sizeof(SQInteger)); + } + break; + case 'i': { + SQInt32 i; + sq_getinteger(v, 2, &ti); + i = (SQInt32)ti; + self->Write(&i, sizeof(SQInt32)); + } + break; + case 's': { + short s; + sq_getinteger(v, 2, &ti); + s = (short)ti; + self->Write(&s, sizeof(short)); + } + break; + case 'w': { + unsigned short w; + sq_getinteger(v, 2, &ti); + w = (unsigned short)ti; + self->Write(&w, sizeof(unsigned short)); + } + break; + case 'c': { + char c; + sq_getinteger(v, 2, &ti); + c = (char)ti; + self->Write(&c, sizeof(char)); + } + break; + case 'b': { + unsigned char b; + sq_getinteger(v, 2, &ti); + b = (unsigned char)ti; + self->Write(&b, sizeof(unsigned char)); + } + break; + case 'f': { + float f; + sq_getfloat(v, 2, &tf); + f = (float)tf; + self->Write(&f, sizeof(float)); + } + break; + case 'd': { + double d; + sq_getfloat(v, 2, &tf); + d = tf; + self->Write(&d, sizeof(double)); + } + break; + default: + return sq_throwerror(v, _SC("invalid format")); + } + return 0; +} + +SQInteger _stream_seek(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQInteger offset, origin = SQ_SEEK_SET; + sq_getinteger(v, 2, &offset); + if(sq_gettop(v) > 2) { + SQInteger t; + sq_getinteger(v, 3, &t); + switch(t) { + case 'b': origin = SQ_SEEK_SET; break; + case 'c': origin = SQ_SEEK_CUR; break; + case 'e': origin = SQ_SEEK_END; break; + default: return sq_throwerror(v,_SC("invalid origin")); + } + } + sq_pushinteger(v, self->Seek(offset, origin)); + return 1; +} + +SQInteger _stream_tell(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + sq_pushinteger(v, self->Tell()); + return 1; +} + +SQInteger _stream_len(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + sq_pushinteger(v, self->Len()); + return 1; +} + +SQInteger _stream_flush(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + if(!self->Flush()) + sq_pushinteger(v, 1); + else + sq_pushnull(v); + return 1; +} + +SQInteger _stream_eos(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + if(self->EOS()) + sq_pushinteger(v, 1); + else + sq_pushnull(v); + return 1; +} + + SQInteger _stream__cloned(HSQUIRRELVM v) + { + return sq_throwerror(v,_SC("this object cannot be cloned")); + } + +static const SQRegFunction _stream_methods[] = { + _DECL_STREAM_FUNC(readblob,2,_SC("xn")), + _DECL_STREAM_FUNC(readn,2,_SC("xn")), + _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")), + _DECL_STREAM_FUNC(writen,3,_SC("xnn")), + _DECL_STREAM_FUNC(seek,-2,_SC("xnn")), + _DECL_STREAM_FUNC(tell,1,_SC("x")), + _DECL_STREAM_FUNC(len,1,_SC("x")), + _DECL_STREAM_FUNC(eos,1,_SC("x")), + _DECL_STREAM_FUNC(flush,1,_SC("x")), + _DECL_STREAM_FUNC(_cloned,0,NULL), + {NULL,(SQFUNCTION)0,0,NULL} +}; + +void init_streamclass(HSQUIRRELVM v) +{ + sq_pushregistrytable(v); + sq_pushstring(v,_SC("std_stream"),-1); + if(SQ_FAILED(sq_get(v,-2))) { + sq_pushstring(v,_SC("std_stream"),-1); + sq_newclass(v,SQFalse); + sq_settypetag(v,-1,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)); + SQInteger i = 0; + while(_stream_methods[i].name != 0) { + const SQRegFunction &f = _stream_methods[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_newslot(v,-3,SQFalse); + sq_pushroottable(v); + sq_pushstring(v,_SC("stream"),-1); + sq_pushstring(v,_SC("std_stream"),-1); + sq_get(v,-4); + sq_newslot(v,-3,SQFalse); + sq_pop(v,1); + } + else { + sq_pop(v,1); //result + } + sq_pop(v,1); +} + +SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals) +{ + if(sq_gettype(v,-1) != OT_TABLE) + return sq_throwerror(v,_SC("table expected")); + SQInteger top = sq_gettop(v); + //create delegate + init_streamclass(v); + sq_pushregistrytable(v); + sq_pushstring(v,reg_name,-1); + sq_pushstring(v,_SC("std_stream"),-1); + if(SQ_SUCCEEDED(sq_get(v,-3))) { + sq_newclass(v,SQTrue); + sq_settypetag(v,-1,typetag); + SQInteger i = 0; + while(methods[i].name != 0) { + const SQRegFunction &f = methods[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_setnativeclosurename(v,-1,f.name); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_newslot(v,-3,SQFalse); + sq_pop(v,1); + + i = 0; + while(globals[i].name!=0) + { + const SQRegFunction &f = globals[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_setnativeclosurename(v,-1,f.name); + sq_newslot(v,-3,SQFalse); + i++; + } + //register the class in the target table + sq_pushstring(v,name,-1); + sq_pushregistrytable(v); + sq_pushstring(v,reg_name,-1); + sq_get(v,-2); + sq_remove(v,-2); + sq_newslot(v,-3,SQFalse); + + sq_settop(v,top); + return SQ_OK; + } + sq_settop(v,top); + return SQ_ERROR; +} diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdstream.h b/mp/src/vscript/squirrel/sqstdlib/sqstdstream.h new file mode 100644 index 00000000..867c135f --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdstream.h @@ -0,0 +1,18 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTD_STREAM_H_ +#define _SQSTD_STREAM_H_ + +SQInteger _stream_readblob(HSQUIRRELVM v); +SQInteger _stream_readline(HSQUIRRELVM v); +SQInteger _stream_readn(HSQUIRRELVM v); +SQInteger _stream_writeblob(HSQUIRRELVM v); +SQInteger _stream_writen(HSQUIRRELVM v); +SQInteger _stream_seek(HSQUIRRELVM v); +SQInteger _stream_tell(HSQUIRRELVM v); +SQInteger _stream_len(HSQUIRRELVM v); +SQInteger _stream_eos(HSQUIRRELVM v); +SQInteger _stream_flush(HSQUIRRELVM v); + +#define _DECL_STREAM_FUNC(name,nparams,typecheck) {_SC(#name),_stream_##name,nparams,typecheck} +SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals); +#endif /*_SQSTD_STREAM_H_*/ diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp new file mode 100644 index 00000000..919bd9e9 --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp @@ -0,0 +1,538 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FORMAT_LEN 20 +#define MAX_WFORMAT_LEN 3 +#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar)) + +static SQBool isfmtchr(SQChar ch) +{ + switch(ch) { + case '-': case '+': case ' ': case '#': case '0': return SQTrue; + } + return SQFalse; +} + +static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width) +{ + SQChar *dummy; + SQChar swidth[MAX_WFORMAT_LEN]; + SQInteger wc = 0; + SQInteger start = n; + fmt[0] = '%'; + while (isfmtchr(src[n])) n++; + while (scisdigit(src[n])) { + swidth[wc] = src[n]; + n++; + wc++; + if(wc>=MAX_WFORMAT_LEN) + return sq_throwerror(v,_SC("width format too long")); + } + swidth[wc] = '\0'; + if(wc > 0) { + width = scstrtol(swidth,&dummy,10); + } + else + width = 0; + if (src[n] == '.') { + n++; + + wc = 0; + while (scisdigit(src[n])) { + swidth[wc] = src[n]; + n++; + wc++; + if(wc>=MAX_WFORMAT_LEN) + return sq_throwerror(v,_SC("precision format too long")); + } + swidth[wc] = '\0'; + if(wc > 0) { + width += scstrtol(swidth,&dummy,10); + + } + } + if (n-start > MAX_FORMAT_LEN ) + return sq_throwerror(v,_SC("format too long")); + memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar)); + fmt[(n-start)+2] = '\0'; + return n; +} + +SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output) +{ + const SQChar *format; + SQChar *dest; + SQChar fmt[MAX_FORMAT_LEN]; + const SQRESULT res = sq_getstring(v,nformatstringidx,&format); + if (SQ_FAILED(res)) { + return res; // propagate the error + } + SQInteger format_size = sq_getsize(v,nformatstringidx); + SQInteger allocated = (format_size+2)*sizeof(SQChar); + dest = sq_getscratchpad(v,allocated); + SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0; + //while(format[n] != '\0') + while(n < format_size) + { + if(format[n] != '%') { + assert(i < allocated); + dest[i++] = format[n]; + n++; + } + else if(format[n+1] == '%') { //handles %% + dest[i++] = '%'; + n += 2; + } + else { + n++; + if( nparam > sq_gettop(v) ) + return sq_throwerror(v,_SC("not enough parameters for the given format string")); + n = validate_format(v,fmt,format,n,w); + if(n < 0) return -1; + SQInteger addlen = 0; + SQInteger valtype = 0; + const SQChar *ts = NULL; + SQInteger ti = 0; + SQFloat tf = 0; + switch(format[n]) { + case 's': + if(SQ_FAILED(sq_getstring(v,nparam,&ts))) + return sq_throwerror(v,_SC("string expected for the specified format")); + addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar)); + valtype = 's'; + break; + case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': +#ifdef _SQ64 + { + size_t flen = scstrlen(fmt); + SQInteger fpos = flen - 1; + SQChar f = fmt[fpos]; + const SQChar *prec = (const SQChar *)_PRINT_INT_PREC; + while(*prec != _SC('\0')) { + fmt[fpos++] = *prec++; + } + fmt[fpos++] = f; + fmt[fpos++] = _SC('\0'); + } +#endif + case 'c': + if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) + return sq_throwerror(v,_SC("integer expected for the specified format")); + addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar)); + valtype = 'i'; + break; + case 'f': case 'g': case 'G': case 'e': case 'E': + if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) + return sq_throwerror(v,_SC("float expected for the specified format")); + addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar)); + valtype = 'f'; + break; + default: + return sq_throwerror(v,_SC("invalid format")); + } + n++; + allocated += addlen + sizeof(SQChar); + dest = sq_getscratchpad(v,allocated); + switch(valtype) { + case 's': i += scsprintf(&dest[i],allocated,fmt,ts); break; + case 'i': i += scsprintf(&dest[i],allocated,fmt,ti); break; + case 'f': i += scsprintf(&dest[i],allocated,fmt,tf); break; + }; + nparam ++; + } + } + *outlen = i; + dest[i] = '\0'; + *output = dest; + return SQ_OK; +} + +void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...) +{ + SQInteger n=256; + va_list args; +begin: + va_start(args,s); + SQChar *b=sq_getscratchpad(v,n); + SQInteger r=scvsprintf(b,n,s,args); + va_end(args); + if (r>=n) { + n=r+1;//required+null + goto begin; + } else if (r<0) { + sq_pushnull(v); + } else { + sq_pushstring(v,b,r); + } +} + +static SQInteger _string_printf(HSQUIRRELVM v) +{ + SQChar *dest = NULL; + SQInteger length = 0; + if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) + return -1; + + SQPRINTFUNCTION printfunc = sq_getprintfunc(v); + if(printfunc) printfunc(v,_SC("%s"),dest); + + return 0; +} + +static SQInteger _string_format(HSQUIRRELVM v) +{ + SQChar *dest = NULL; + SQInteger length = 0; + if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) + return -1; + sq_pushstring(v,dest,length); + return 1; +} + +static void __strip_l(const SQChar *str,const SQChar **start) +{ + const SQChar *t = str; + while(((*t) != '\0') && scisspace(*t)){ t++; } + *start = t; +} + +static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end) +{ + if(len == 0) { + *end = str; + return; + } + const SQChar *t = &str[len-1]; + while(t >= str && scisspace(*t)) { t--; } + *end = t + 1; +} + +static SQInteger _string_strip(HSQUIRRELVM v) +{ + const SQChar *str,*start,*end; + sq_getstring(v,2,&str); + SQInteger len = sq_getsize(v,2); + __strip_l(str,&start); + __strip_r(str,len,&end); + sq_pushstring(v,start,end - start); + return 1; +} + +static SQInteger _string_lstrip(HSQUIRRELVM v) +{ + const SQChar *str,*start; + sq_getstring(v,2,&str); + __strip_l(str,&start); + sq_pushstring(v,start,-1); + return 1; +} + +static SQInteger _string_rstrip(HSQUIRRELVM v) +{ + const SQChar *str,*end; + sq_getstring(v,2,&str); + SQInteger len = sq_getsize(v,2); + __strip_r(str,len,&end); + sq_pushstring(v,str,end - str); + return 1; +} + +static SQInteger _string_split(HSQUIRRELVM v) +{ + const SQChar *str,*seps; + SQChar *stemp; + sq_getstring(v,2,&str); + sq_getstring(v,3,&seps); + SQInteger sepsize = sq_getsize(v,3); + if(sepsize == 0) return sq_throwerror(v,_SC("empty separators string")); + SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar); + stemp = sq_getscratchpad(v,memsize); + memcpy(stemp,str,memsize); + SQChar *start = stemp; + SQChar *end = stemp; + sq_newarray(v,0); + while(*end != '\0') + { + SQChar cur = *end; + for(SQInteger i = 0; i < sepsize; i++) + { + if(cur == seps[i]) + { + *end = 0; + sq_pushstring(v,start,-1); + sq_arrayappend(v,-2); + start = end + 1; + break; + } + } + end++; + } + if(end != start) + { + sq_pushstring(v,start,-1); + sq_arrayappend(v,-2); + } + return 1; +} + +static SQInteger _string_escape(HSQUIRRELVM v) +{ + const SQChar *str; + SQChar *dest,*resstr; + SQInteger size; + sq_getstring(v,2,&str); + size = sq_getsize(v,2); + if(size == 0) { + sq_push(v,2); + return 1; + } +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 + const SQChar *escpat = _SC("\\x%04x"); + const SQInteger maxescsize = 6; +#else //WCHAR_SIZE == 4 + const SQChar *escpat = _SC("\\x%08x"); + const SQInteger maxescsize = 10; +#endif +#else + const SQChar *escpat = _SC("\\x%02x"); + const SQInteger maxescsize = 4; +#endif + SQInteger destcharsize = (size * maxescsize); //assumes every char could be escaped + resstr = dest = (SQChar *)sq_getscratchpad(v,destcharsize * sizeof(SQChar)); + SQChar c; + SQChar escch; + SQInteger escaped = 0; + for(int n = 0; n < size; n++){ + c = *str++; + escch = 0; + if(scisprint(c) || c == 0) { + switch(c) { + case '\a': escch = 'a'; break; + case '\b': escch = 'b'; break; + case '\t': escch = 't'; break; + case '\n': escch = 'n'; break; + case '\v': escch = 'v'; break; + case '\f': escch = 'f'; break; + case '\r': escch = 'r'; break; + case '\\': escch = '\\'; break; + case '\"': escch = '\"'; break; + case '\'': escch = '\''; break; + case 0: escch = '0'; break; + } + if(escch) { + *dest++ = '\\'; + *dest++ = escch; + escaped++; + } + else { + *dest++ = c; + } + } + else { + + dest += scsprintf(dest, destcharsize, escpat, c); + escaped++; + } + } + + if(escaped) { + sq_pushstring(v,resstr,dest - resstr); + } + else { + sq_push(v,2); //nothing escaped + } + return 1; +} + +static SQInteger _string_startswith(HSQUIRRELVM v) +{ + const SQChar *str,*cmp; + sq_getstring(v,2,&str); + sq_getstring(v,3,&cmp); + SQInteger len = sq_getsize(v,2); + SQInteger cmplen = sq_getsize(v,3); + SQBool ret = SQFalse; + if(cmplen <= len) { + ret = memcmp(str,cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse; + } + sq_pushbool(v,ret); + return 1; +} + +static SQInteger _string_endswith(HSQUIRRELVM v) +{ + const SQChar *str,*cmp; + sq_getstring(v,2,&str); + sq_getstring(v,3,&cmp); + SQInteger len = sq_getsize(v,2); + SQInteger cmplen = sq_getsize(v,3); + SQBool ret = SQFalse; + if(cmplen <= len) { + ret = memcmp(&str[len - cmplen],cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse; + } + sq_pushbool(v,ret); + return 1; +} + +#define SETUP_REX(v) \ + SQRex *self = NULL; \ + sq_getinstanceup(v,1,(SQUserPointer *)&self,0); + +static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) +{ + SQRex *self = ((SQRex *)p); + sqstd_rex_free(self); + return 1; +} + +static SQInteger _regexp_match(HSQUIRRELVM v) +{ + SETUP_REX(v); + const SQChar *str; + sq_getstring(v,2,&str); + if(sqstd_rex_match(self,str) == SQTrue) + { + sq_pushbool(v,SQTrue); + return 1; + } + sq_pushbool(v,SQFalse); + return 1; +} + +static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end) +{ + sq_newtable(v); + sq_pushstring(v,_SC("begin"),-1); + sq_pushinteger(v,begin - str); + sq_rawset(v,-3); + sq_pushstring(v,_SC("end"),-1); + sq_pushinteger(v,end - str); + sq_rawset(v,-3); +} + +static SQInteger _regexp_search(HSQUIRRELVM v) +{ + SETUP_REX(v); + const SQChar *str,*begin,*end; + SQInteger start = 0; + sq_getstring(v,2,&str); + if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); + if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { + _addrexmatch(v,str,begin,end); + return 1; + } + return 0; +} + +static SQInteger _regexp_capture(HSQUIRRELVM v) +{ + SETUP_REX(v); + const SQChar *str,*begin,*end; + SQInteger start = 0; + sq_getstring(v,2,&str); + if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); + if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { + SQInteger n = sqstd_rex_getsubexpcount(self); + SQRexMatch match; + sq_newarray(v,0); + for(SQInteger i = 0;i < n; i++) { + sqstd_rex_getsubexp(self,i,&match); + if(match.len > 0) + _addrexmatch(v,str,match.begin,match.begin+match.len); + else + _addrexmatch(v,str,str,str); //empty match + sq_arrayappend(v,-2); + } + return 1; + } + return 0; +} + +static SQInteger _regexp_subexpcount(HSQUIRRELVM v) +{ + SETUP_REX(v); + sq_pushinteger(v,sqstd_rex_getsubexpcount(self)); + return 1; +} + +static SQInteger _regexp_constructor(HSQUIRRELVM v) +{ + const SQChar *error,*pattern; + sq_getstring(v,2,&pattern); + SQRex *rex = sqstd_rex_compile(pattern,&error); + if(!rex) return sq_throwerror(v,error); + sq_setinstanceup(v,1,rex); + sq_setreleasehook(v,1,_rexobj_releasehook); + return 0; +} + +static SQInteger _regexp__typeof(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("regexp"),-1); + return 1; +} + +#define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask} +static const SQRegFunction rexobj_funcs[]={ + _DECL_REX_FUNC(constructor,2,_SC(".s")), + _DECL_REX_FUNC(search,-2,_SC("xsn")), + _DECL_REX_FUNC(match,2,_SC("xs")), + _DECL_REX_FUNC(capture,-2,_SC("xsn")), + _DECL_REX_FUNC(subexpcount,1,_SC("x")), + _DECL_REX_FUNC(_typeof,1,_SC("x")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_REX_FUNC + +#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask} +static const SQRegFunction stringlib_funcs[]={ + _DECL_FUNC(format,-2,_SC(".s")), + _DECL_FUNC(printf,-2,_SC(".s")), + _DECL_FUNC(strip,2,_SC(".s")), + _DECL_FUNC(lstrip,2,_SC(".s")), + _DECL_FUNC(rstrip,2,_SC(".s")), + _DECL_FUNC(split,3,_SC(".ss")), + _DECL_FUNC(escape,2,_SC(".s")), + _DECL_FUNC(startswith,3,_SC(".ss")), + _DECL_FUNC(endswith,3,_SC(".ss")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_FUNC + + +SQInteger sqstd_register_stringlib(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("regexp"),-1); + sq_newclass(v,SQFalse); + SQInteger i = 0; + while(rexobj_funcs[i].name != 0) { + const SQRegFunction &f = rexobj_funcs[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_setnativeclosurename(v,-1,f.name); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_newslot(v,-3,SQFalse); + + i = 0; + while(stringlib_funcs[i].name!=0) + { + sq_pushstring(v,stringlib_funcs[i].name,-1); + sq_newclosure(v,stringlib_funcs[i].f,0); + sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask); + sq_setnativeclosurename(v,-1,stringlib_funcs[i].name); + sq_newslot(v,-3,SQFalse); + i++; + } + return 1; +} diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp new file mode 100644 index 00000000..d326be1a --- /dev/null +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp @@ -0,0 +1,154 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include + +#ifdef SQUNICODE +#include +#define scgetenv _wgetenv +#define scsystem _wsystem +#define scasctime _wasctime +#define scremove _wremove +#define screname _wrename +#else +#define scgetenv getenv +#define scsystem system +#define scasctime asctime +#define scremove remove +#define screname rename +#endif +#ifdef IOS + #include + extern char **environ; +#endif + +static SQInteger _system_getenv(HSQUIRRELVM v) +{ + const SQChar *s; + if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ + sq_pushstring(v,scgetenv(s),-1); + return 1; + } + return 0; +} + +static SQInteger _system_system(HSQUIRRELVM v) +{ + const SQChar *s; + if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ + #ifdef IOS + pid_t pid; + posix_spawn(&pid, s, NULL, NULL, NULL, environ); + sq_pushinteger(v, 0); + #else + sq_pushinteger(v,scsystem(s)); + #endif + return 1; + } + return sq_throwerror(v,_SC("wrong param")); +} + +static SQInteger _system_clock(HSQUIRRELVM v) +{ + sq_pushfloat(v,((SQFloat)clock())/(SQFloat)CLOCKS_PER_SEC); + return 1; +} + +static SQInteger _system_time(HSQUIRRELVM v) +{ + SQInteger t = (SQInteger)time(NULL); + sq_pushinteger(v,t); + return 1; +} + +static SQInteger _system_remove(HSQUIRRELVM v) +{ + const SQChar *s; + sq_getstring(v,2,&s); + if(scremove(s)==-1) + return sq_throwerror(v,_SC("remove() failed")); + return 0; +} + +static SQInteger _system_rename(HSQUIRRELVM v) +{ + const SQChar *oldn,*newn; + sq_getstring(v,2,&oldn); + sq_getstring(v,3,&newn); + if(screname(oldn,newn)==-1) + return sq_throwerror(v,_SC("rename() failed")); + return 0; +} + +static void _set_integer_slot(HSQUIRRELVM v,const SQChar *name,SQInteger val) +{ + sq_pushstring(v,name,-1); + sq_pushinteger(v,val); + sq_rawset(v,-3); +} + +static SQInteger _system_date(HSQUIRRELVM v) +{ + time_t t; + SQInteger it; + SQInteger format = 'l'; + if(sq_gettop(v) > 1) { + sq_getinteger(v,2,&it); + t = it; + if(sq_gettop(v) > 2) { + sq_getinteger(v,3,(SQInteger*)&format); + } + } + else { + time(&t); + } + tm *date; + if(format == 'u') + date = gmtime(&t); + else + date = localtime(&t); + if(!date) + return sq_throwerror(v,_SC("crt api failure")); + sq_newtable(v); + _set_integer_slot(v, _SC("sec"), date->tm_sec); + _set_integer_slot(v, _SC("min"), date->tm_min); + _set_integer_slot(v, _SC("hour"), date->tm_hour); + _set_integer_slot(v, _SC("day"), date->tm_mday); + _set_integer_slot(v, _SC("month"), date->tm_mon); + _set_integer_slot(v, _SC("year"), date->tm_year+1900); + _set_integer_slot(v, _SC("wday"), date->tm_wday); + _set_integer_slot(v, _SC("yday"), date->tm_yday); + return 1; +} + + + +#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask} +static const SQRegFunction systemlib_funcs[]={ + _DECL_FUNC(getenv,2,_SC(".s")), + _DECL_FUNC(system,2,_SC(".s")), + _DECL_FUNC(clock,0,NULL), + _DECL_FUNC(time,1,NULL), + _DECL_FUNC(date,-1,_SC(".nn")), + _DECL_FUNC(remove,2,_SC(".s")), + _DECL_FUNC(rename,3,_SC(".ss")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_FUNC + +SQInteger sqstd_register_systemlib(HSQUIRRELVM v) +{ + SQInteger i=0; + while(systemlib_funcs[i].name!=0) + { + sq_pushstring(v,systemlib_funcs[i].name,-1); + sq_newclosure(v,systemlib_funcs[i].f,0); + sq_setparamscheck(v,systemlib_funcs[i].nparamscheck,systemlib_funcs[i].typemask); + sq_setnativeclosurename(v,-1,systemlib_funcs[i].name); + sq_newslot(v,-3,SQFalse); + i++; + } + return 1; +} diff --git a/mp/src/vscript/squirrel/squirrel-config.cmake.in b/mp/src/vscript/squirrel/squirrel-config.cmake.in new file mode 100644 index 00000000..6ed4a8aa --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel-config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/squirrel-config-version.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/squirrel-targets.cmake") diff --git a/mp/src/vscript/squirrel/squirrel.dsw b/mp/src/vscript/squirrel/squirrel.dsw new file mode 100644 index 00000000..72150910 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel.dsw @@ -0,0 +1,77 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "sq"=.\sq\sq.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + . + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name sqlibs + End Project Dependency + Begin Project Dependency + Project_Dep_Name squirrel + End Project Dependency + Begin Project Dependency + Project_Dep_Name sqstdlib + End Project Dependency +}}} + +############################################################################### + +Project: "sqstdlib"=.\sqstdlib\sqstdlib.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/squirrel", HAAAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "squirrel"=.\squirrel\squirrel.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/squirrel", HAAAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/squirrel", HAAAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/mp/src/vscript/squirrel/squirrel/CMakeLists.txt b/mp/src/vscript/squirrel/squirrel/CMakeLists.txt new file mode 100644 index 00000000..f96b9584 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/CMakeLists.txt @@ -0,0 +1,55 @@ +set(SQUIRREL_SRC sqapi.cpp + sqbaselib.cpp + sqclass.cpp + sqcompiler.cpp + sqdebug.cpp + sqfuncstate.cpp + sqlexer.cpp + sqmem.cpp + sqobject.cpp + sqstate.cpp + sqtable.cpp + sqvm.cpp) + +if(NOT DISABLE_DYNAMIC) + add_library(squirrel SHARED ${SQUIRREL_SRC}) + add_library(squirrel::squirrel ALIAS squirrel) + set_property(TARGET squirrel PROPERTY EXPORT_NAME squirrel) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS squirrel EXPORT squirrel + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Libraries + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries NAMELINK_SKIP + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries + ) + install(TARGETS squirrel EXPORT squirrel + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development NAMELINK_ONLY + ) + endif() + target_include_directories(squirrel PUBLIC + "$" + "$" + ) +endif() + +if(NOT DISABLE_STATIC) + add_library(squirrel_static STATIC ${SQUIRREL_SRC}) + add_library(squirrel::squirrel_static ALIAS squirrel_static) + set_property(TARGET squirrel_static PROPERTY EXPORT_NAME squirrel_static) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS squirrel_static EXPORT squirrel ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development) + endif() + target_include_directories(squirrel_static PUBLIC + "$" + "$" + ) +endif() + +if(LONG_OUTPUT_NAMES) + if(NOT DISABLE_DYNAMIC) + set_target_properties(squirrel PROPERTIES OUTPUT_NAME squirrel3) + endif() + + if(NOT DISABLE_STATIC) + set_target_properties(squirrel_static PROPERTIES OUTPUT_NAME squirrel3_static) + endif() +endif() diff --git a/mp/src/vscript/squirrel/squirrel/Makefile b/mp/src/vscript/squirrel/squirrel/Makefile new file mode 100644 index 00000000..2422a834 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/Makefile @@ -0,0 +1,53 @@ +SQUIRREL= .. + + +CC?= gcc +OUT?= $(SQUIRREL)/lib/libsquirrel.a +INCZ?= -I$(SQUIRREL)/include -I. -Iinclude +DEFS= $(CC_EXTRA_FLAGS) +LIB= + +OBJS= \ + sqapi.o \ + sqbaselib.o \ + sqfuncstate.o \ + sqdebug.o \ + sqlexer.o \ + sqobject.o \ + sqcompiler.o \ + sqstate.o \ + sqtable.o \ + sqmem.o \ + sqvm.o \ + sqclass.o + +SRCS= \ + sqapi.cpp \ + sqbaselib.cpp \ + sqfuncstate.cpp \ + sqdebug.cpp \ + sqlexer.cpp \ + sqobject.cpp \ + sqcompiler.cpp \ + sqstate.cpp \ + sqtable.cpp \ + sqmem.cpp \ + sqvm.cpp \ + sqclass.cpp + + + +sq32: + $(CC) -O2 -fno-exceptions -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sqprof: + $(CC) -O2 -pg -fno-exceptions -fno-rtti -pie -gstabs -g3 -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sq64: + $(CC) -O2 -m64 -D_SQ64 -fno-exceptions -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o diff --git a/mp/src/vscript/squirrel/squirrel/sqapi.cpp b/mp/src/vscript/squirrel/squirrel/sqapi.cpp new file mode 100644 index 00000000..a3221503 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqapi.cpp @@ -0,0 +1,1631 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqstring.h" +#include "sqtable.h" +#include "sqarray.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "squserdata.h" +#include "sqcompiler.h" +#include "sqfuncstate.h" +#include "sqclass.h" + +static bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o) +{ + *o = &stack_get(v,idx); + if(sq_type(**o) != type){ + SQObjectPtr oval = v->PrintObjVal(**o); + v->Raise_Error(_SC("wrong argument type, expected '%s' got '%.50s'"),IdType2Name(type),_stringval(oval)); + return false; + } + return true; +} + +#define _GETSAFE_OBJ(v,idx,type,o) { if(!sq_aux_gettypedarg(v,idx,type,&o)) return SQ_ERROR; } + +#define sq_aux_paramscheck(v,count) \ +{ \ + if(sq_gettop(v) < count){ v->Raise_Error(_SC("not enough params in the stack")); return SQ_ERROR; }\ +} + + +SQInteger sq_aux_invalidtype(HSQUIRRELVM v,SQObjectType type) +{ + SQUnsignedInteger buf_size = 100 *sizeof(SQChar); + scsprintf(_ss(v)->GetScratchPad(buf_size), buf_size, _SC("unexpected type %s"), IdType2Name(type)); + return sq_throwerror(v, _ss(v)->GetScratchPad(-1)); +} + +HSQUIRRELVM sq_open(SQInteger initialstacksize) +{ + SQSharedState *ss; + SQVM *v; + sq_new(ss, SQSharedState); + ss->Init(); + v = (SQVM *)SQ_MALLOC(sizeof(SQVM)); + new (v) SQVM(ss); + ss->_root_vm = v; + if(v->Init(NULL, initialstacksize)) { + return v; + } else { + sq_delete(v, SQVM); + return NULL; + } + return v; +} + +HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize) +{ + SQSharedState *ss; + SQVM *v; + ss=_ss(friendvm); + + v= (SQVM *)SQ_MALLOC(sizeof(SQVM)); + new (v) SQVM(ss); + + if(v->Init(friendvm, initialstacksize)) { + friendvm->Push(v); + return v; + } else { + sq_delete(v, SQVM); + return NULL; + } +} + +SQInteger sq_getvmstate(HSQUIRRELVM v) +{ + if(v->_suspended) + return SQ_VMSTATE_SUSPENDED; + else { + if(v->_callsstacksize != 0) return SQ_VMSTATE_RUNNING; + else return SQ_VMSTATE_IDLE; + } +} + +void sq_seterrorhandler(HSQUIRRELVM v) +{ + SQObject o = stack_get(v, -1); + if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { + v->_errorhandler = o; + v->Pop(); + } +} + +void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook) +{ + v->_debughook_native = hook; + v->_debughook_closure.Null(); + v->_debughook = hook?true:false; +} + +void sq_setdebughook(HSQUIRRELVM v) +{ + SQObject o = stack_get(v,-1); + if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { + v->_debughook_closure = o; + v->_debughook_native = NULL; + v->_debughook = !sq_isnull(o); + v->Pop(); + } +} + +void sq_close(HSQUIRRELVM v) +{ + SQSharedState *ss = _ss(v); + _thread(ss->_root_vm)->Finalize(); + sq_delete(ss, SQSharedState); +} + +SQInteger sq_getversion() +{ + return SQUIRREL_VERSION_NUMBER; +} + +SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror) +{ + SQObjectPtr o; +#ifndef NO_COMPILER + if(Compile(v, read, p, sourcename, o, raiseerror?true:false, _ss(v)->_debuginfo)) { + v->Push(SQClosure::Create(_ss(v), _funcproto(o), _table(v->_roottable)->GetWeakRef(OT_TABLE))); + return SQ_OK; + } + return SQ_ERROR; +#else + return sq_throwerror(v,_SC("this is a no compiler build")); +#endif +} + +void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable) +{ + _ss(v)->_debuginfo = enable?true:false; +} + +void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) +{ + _ss(v)->_notifyallexceptions = enable?true:false; +} + +void sq_addref(HSQUIRRELVM v,HSQOBJECT *po) +{ + if(!ISREFCOUNTED(sq_type(*po))) return; +#ifdef NO_GARBAGE_COLLECTOR + __AddRef(po->_type,po->_unVal); +#else + _ss(v)->_refs_table.AddRef(*po); +#endif +} + +SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po) +{ + if(!ISREFCOUNTED(sq_type(*po))) return 0; +#ifdef NO_GARBAGE_COLLECTOR + return po->_unVal.pRefCounted->_uiRef; +#else + return _ss(v)->_refs_table.GetRefCount(*po); +#endif +} + +SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po) +{ + if(!ISREFCOUNTED(sq_type(*po))) return SQTrue; +#ifdef NO_GARBAGE_COLLECTOR + bool ret = (po->_unVal.pRefCounted->_uiRef <= 1) ? SQTrue : SQFalse; + __Release(po->_type,po->_unVal); + return ret; //the ret val doesn't work(and cannot be fixed) +#else + return _ss(v)->_refs_table.Release(*po); +#endif +} + +SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM SQ_UNUSED_ARG(v), const HSQOBJECT *po) +{ + if (!ISREFCOUNTED(sq_type(*po))) return 0; + return po->_unVal.pRefCounted->_uiRef; +} + +const SQChar *sq_objtostring(const HSQOBJECT *o) +{ + if(sq_type(*o) == OT_STRING) { + return _stringval(*o); + } + return NULL; +} + +SQInteger sq_objtointeger(const HSQOBJECT *o) +{ + if(sq_isnumeric(*o)) { + return tointeger(*o); + } + return 0; +} + +SQFloat sq_objtofloat(const HSQOBJECT *o) +{ + if(sq_isnumeric(*o)) { + return tofloat(*o); + } + return 0; +} + +SQBool sq_objtobool(const HSQOBJECT *o) +{ + if(sq_isbool(*o)) { + return _integer(*o); + } + return SQFalse; +} + +SQUserPointer sq_objtouserpointer(const HSQOBJECT *o) +{ + if(sq_isuserpointer(*o)) { + return _userpointer(*o); + } + return 0; +} + +void sq_pushnull(HSQUIRRELVM v) +{ + v->PushNull(); +} + +void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len) +{ + if(s) + v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len))); + else v->PushNull(); +} + +void sq_pushinteger(HSQUIRRELVM v,SQInteger n) +{ + v->Push(n); +} + +void sq_pushbool(HSQUIRRELVM v,SQBool b) +{ + v->Push(b?true:false); +} + +void sq_pushfloat(HSQUIRRELVM v,SQFloat n) +{ + v->Push(n); +} + +void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p) +{ + v->Push(p); +} + +void sq_pushthread(HSQUIRRELVM v, HSQUIRRELVM thread) +{ + v->Push(thread); +} + +SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size) +{ + SQUserData *ud = SQUserData::Create(_ss(v), size + SQ_ALIGNMENT); + v->Push(ud); + return (SQUserPointer)sq_aligning(ud + 1); +} + +void sq_newtable(HSQUIRRELVM v) +{ + v->Push(SQTable::Create(_ss(v), 0)); +} + +void sq_newtableex(HSQUIRRELVM v,SQInteger initialcapacity) +{ + v->Push(SQTable::Create(_ss(v), initialcapacity)); +} + +void sq_newarray(HSQUIRRELVM v,SQInteger size) +{ + v->Push(SQArray::Create(_ss(v), size)); +} + +SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase) +{ + SQClass *baseclass = NULL; + if(hasbase) { + SQObjectPtr &base = stack_get(v,-1); + if(sq_type(base) != OT_CLASS) + return sq_throwerror(v,_SC("invalid base type")); + baseclass = _class(base); + } + SQClass *newclass = SQClass::Create(_ss(v), baseclass); + if(baseclass) v->Pop(); + v->Push(newclass); + return SQ_OK; +} + +SQBool sq_instanceof(HSQUIRRELVM v) +{ + SQObjectPtr &inst = stack_get(v,-1); + SQObjectPtr &cl = stack_get(v,-2); + if(sq_type(inst) != OT_INSTANCE || sq_type(cl) != OT_CLASS) + return sq_throwerror(v,_SC("invalid param type")); + return _instance(inst)->InstanceOf(_class(cl))?SQTrue:SQFalse; +} + +SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx) +{ + sq_aux_paramscheck(v,2); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + _array(*arr)->Append(v->GetUp(-1)); + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + if(_array(*arr)->Size() > 0) { + if(pushval != 0){ v->Push(_array(*arr)->Top()); } + _array(*arr)->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("empty array")); +} + +SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize) +{ + sq_aux_paramscheck(v,1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + if(newsize >= 0) { + _array(*arr)->Resize(newsize); + return SQ_OK; + } + return sq_throwerror(v,_SC("negative size")); +} + + +SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *o; + _GETSAFE_OBJ(v, idx, OT_ARRAY,o); + SQArray *arr = _array(*o); + if(arr->Size() > 0) { + SQObjectPtr t; + SQInteger size = arr->Size(); + SQInteger n = size >> 1; size -= 1; + for(SQInteger i = 0; i < n; i++) { + t = arr->_values[i]; + arr->_values[i] = arr->_values[size-i]; + arr->_values[size-i] = t; + } + return SQ_OK; + } + return SQ_OK; +} + +SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + return _array(*arr)->Remove(itemidx) ? SQ_OK : sq_throwerror(v,_SC("index out of range")); +} + +SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + SQRESULT ret = _array(*arr)->Insert(destpos, v->GetUp(-1)) ? SQ_OK : sq_throwerror(v,_SC("index out of range")); + v->Pop(); + return ret; +} + +void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars) +{ + SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func,nfreevars); + nc->_nparamscheck = 0; + for(SQUnsignedInteger i = 0; i < nfreevars; i++) { + nc->_outervalues[i] = v->Top(); + v->Pop(); + } + v->Push(SQObjectPtr(nc)); +} + +SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars) +{ + SQObject o = stack_get(v, idx); + if(sq_type(o) == OT_CLOSURE) { + SQClosure *c = _closure(o); + SQFunctionProto *proto = c->_function; + *nparams = proto->_nparameters; + *nfreevars = proto->_noutervalues; + return SQ_OK; + } + else if(sq_type(o) == OT_NATIVECLOSURE) + { + SQNativeClosure *c = _nativeclosure(o); + *nparams = c->_nparamscheck; + *nfreevars = (SQInteger)c->_noutervalues; + return SQ_OK; + } + return sq_throwerror(v,_SC("the object is not a closure")); +} + +SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name) +{ + SQObject o = stack_get(v, idx); + if(sq_isnativeclosure(o)) { + SQNativeClosure *nc = _nativeclosure(o); + nc->_name = SQString::Create(_ss(v),name); + return SQ_OK; + } + return sq_throwerror(v,_SC("the object is not a nativeclosure")); +} + +SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask) +{ + SQObject o = stack_get(v, -1); + if(!sq_isnativeclosure(o)) + return sq_throwerror(v, _SC("native closure expected")); + SQNativeClosure *nc = _nativeclosure(o); + nc->_nparamscheck = nparamscheck; + if(typemask) { + SQIntVec res; + if(!CompileTypemask(res, typemask)) + return sq_throwerror(v, _SC("invalid typemask")); + nc->_typecheck.copy(res); + } + else { + nc->_typecheck.resize(0); + } + if(nparamscheck == SQ_MATCHTYPEMASKSTRING) { + nc->_nparamscheck = nc->_typecheck.size(); + } + return SQ_OK; +} + +SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + if(!sq_isnativeclosure(o) && + !sq_isclosure(o)) + return sq_throwerror(v,_SC("the target is not a closure")); + SQObjectPtr &env = stack_get(v,-1); + if(!sq_istable(env) && + !sq_isarray(env) && + !sq_isclass(env) && + !sq_isinstance(env)) + return sq_throwerror(v,_SC("invalid environment")); + SQWeakRef *w = _refcounted(env)->GetWeakRef(sq_type(env)); + SQObjectPtr ret; + if(sq_isclosure(o)) { + SQClosure *c = _closure(o)->Clone(); + __ObjRelease(c->_env); + c->_env = w; + __ObjAddRef(c->_env); + if(_closure(o)->_base) { + c->_base = _closure(o)->_base; + __ObjAddRef(c->_base); + } + ret = c; + } + else { //then must be a native closure + SQNativeClosure *c = _nativeclosure(o)->Clone(); + __ObjRelease(c->_env); + c->_env = w; + __ObjAddRef(c->_env); + ret = c; + } + v->Pop(); + v->Push(ret); + return SQ_OK; +} + +SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + if(!sq_isnativeclosure(o) && + !sq_isclosure(o)) + return sq_throwerror(v,_SC("the target is not a closure")); + if(sq_isnativeclosure(o)) + { + v->Push(_nativeclosure(o)->_name); + } + else { //closure + v->Push(_closure(o)->_function->_name); + } + return SQ_OK; +} + +SQRESULT sq_setclosureroot(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &c = stack_get(v,idx); + SQObject o = stack_get(v, -1); + if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected")); + if(sq_istable(o)) { + _closure(c)->SetRoot(_table(o)->GetWeakRef(OT_TABLE)); + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("invalid type")); +} + +SQRESULT sq_getclosureroot(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &c = stack_get(v,idx); + if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected")); + v->Push(_closure(c)->_root->_obj); + return SQ_OK; +} + +SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx) +{ + SQObject &o=stack_get(v,idx); + switch(sq_type(o)) { + case OT_TABLE: _table(o)->Clear(); break; + case OT_ARRAY: _array(o)->Resize(0); break; + default: + return sq_throwerror(v, _SC("clear only works on table and array")); + break; + + } + return SQ_OK; +} + +void sq_pushroottable(HSQUIRRELVM v) +{ + v->Push(v->_roottable); +} + +void sq_pushregistrytable(HSQUIRRELVM v) +{ + v->Push(_ss(v)->_registry); +} + +void sq_pushconsttable(HSQUIRRELVM v) +{ + v->Push(_ss(v)->_consts); +} + +SQRESULT sq_setroottable(HSQUIRRELVM v) +{ + SQObject o = stack_get(v, -1); + if(sq_istable(o) || sq_isnull(o)) { + v->_roottable = o; + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("invalid type")); +} + +SQRESULT sq_setconsttable(HSQUIRRELVM v) +{ + SQObject o = stack_get(v, -1); + if(sq_istable(o)) { + _ss(v)->_consts = o; + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("invalid type, expected table")); +} + +void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p) +{ + v->_foreignptr = p; +} + +SQUserPointer sq_getforeignptr(HSQUIRRELVM v) +{ + return v->_foreignptr; +} + +void sq_setsharedforeignptr(HSQUIRRELVM v,SQUserPointer p) +{ + _ss(v)->_foreignptr = p; +} + +SQUserPointer sq_getsharedforeignptr(HSQUIRRELVM v) +{ + return _ss(v)->_foreignptr; +} + +void sq_setvmreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) +{ + v->_releasehook = hook; +} + +SQRELEASEHOOK sq_getvmreleasehook(HSQUIRRELVM v) +{ + return v->_releasehook; +} + +void sq_setsharedreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) +{ + _ss(v)->_releasehook = hook; +} + +SQRELEASEHOOK sq_getsharedreleasehook(HSQUIRRELVM v) +{ + return _ss(v)->_releasehook; +} + +void sq_push(HSQUIRRELVM v,SQInteger idx) +{ + v->Push(stack_get(v, idx)); +} + +SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx) +{ + return sq_type(stack_get(v, idx)); +} + +SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + SQObjectPtr res; + if(!v->TypeOf(o,res)) { + return SQ_ERROR; + } + v->Push(res); + return SQ_OK; +} + +SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + SQObjectPtr res; + if(!v->ToString(o,res)) { + return SQ_ERROR; + } + v->Push(res); + return SQ_OK; +} + +void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b) +{ + SQObjectPtr &o = stack_get(v, idx); + *b = SQVM::IsFalse(o)?SQFalse:SQTrue; +} + +SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i) +{ + SQObjectPtr &o = stack_get(v, idx); + if(sq_isnumeric(o)) { + *i = tointeger(o); + return SQ_OK; + } + if(sq_isbool(o)) { + *i = SQVM::IsFalse(o)?SQFalse:SQTrue; + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f) +{ + SQObjectPtr &o = stack_get(v, idx); + if(sq_isnumeric(o)) { + *f = tofloat(o); + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b) +{ + SQObjectPtr &o = stack_get(v, idx); + if(sq_isbool(o)) { + *b = _integer(o); + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger *size) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_STRING,o); + *c = _stringval(*o); + *size = _string(*o)->_len; + return SQ_OK; +} + +SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_STRING,o); + *c = _stringval(*o); + return SQ_OK; +} + +SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_THREAD,o); + *thread = _thread(*o); + return SQ_OK; +} + +SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + v->PushNull(); + if(!v->Clone(o, stack_get(v, -1))){ + v->Pop(); + return SQ_ERROR; + } + return SQ_OK; +} + +SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + SQObjectType type = sq_type(o); + switch(type) { + case OT_STRING: return _string(o)->_len; + case OT_TABLE: return _table(o)->CountUsed(); + case OT_ARRAY: return _array(o)->Size(); + case OT_USERDATA: return _userdata(o)->_size; + case OT_INSTANCE: return _instance(o)->_class->_udsize; + case OT_CLASS: return _class(o)->_udsize; + default: + return sq_aux_invalidtype(v, type); + } +} + +SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + return HashObj(o); +} + +SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_USERDATA,o); + (*p) = _userdataval(*o); + if(typetag) *typetag = _userdata(*o)->_typetag; + return SQ_OK; +} + +SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag) +{ + SQObjectPtr &o = stack_get(v,idx); + switch(sq_type(o)) { + case OT_USERDATA: _userdata(o)->_typetag = typetag; break; + case OT_CLASS: _class(o)->_typetag = typetag; break; + default: return sq_throwerror(v,_SC("invalid object type")); + } + return SQ_OK; +} + +SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag) +{ + switch(sq_type(*o)) { + case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break; + case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break; + case OT_CLASS: *typetag = _class(*o)->_typetag; break; + default: return SQ_ERROR; + } + return SQ_OK; +} + +SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag) +{ + SQObjectPtr &o = stack_get(v,idx); + if (SQ_FAILED(sq_getobjtypetag(&o, typetag))) + return SQ_ERROR;// this is not an error it should be a bool but would break backward compatibility + return SQ_OK; +} + +SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_USERPOINTER,o); + (*p) = _userpointer(*o); + return SQ_OK; +} + +SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); + _instance(o)->_userpointer = p; + return SQ_OK; +} + +SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class")); + if(_class(o)->_locked) return sq_throwerror(v,_SC("the class is locked")); + _class(o)->_udsize = udsize; + return SQ_OK; +} + + +SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); + (*p) = _instance(o)->_userpointer; + if(typetag != 0) { + SQClass *cl = _instance(o)->_class; + do{ + if(cl->_typetag == typetag) + return SQ_OK; + cl = cl->_base; + }while(cl != NULL); + return sq_throwerror(v,_SC("invalid type tag")); + } + return SQ_OK; +} + +SQInteger sq_gettop(HSQUIRRELVM v) +{ + return (v->_top) - v->_stackbase; +} + +void sq_settop(HSQUIRRELVM v, SQInteger newtop) +{ + SQInteger top = sq_gettop(v); + if(top > newtop) + sq_pop(v, top - newtop); + else + while(top++ < newtop) sq_pushnull(v); +} + +void sq_pop(HSQUIRRELVM v, SQInteger nelemstopop) +{ + assert(v->_top >= nelemstopop); + v->Pop(nelemstopop); +} + +void sq_poptop(HSQUIRRELVM v) +{ + assert(v->_top >= 1); + v->Pop(); +} + + +void sq_remove(HSQUIRRELVM v, SQInteger idx) +{ + v->Remove(idx); +} + +SQInteger sq_cmp(HSQUIRRELVM v) +{ + SQInteger res; + v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res); + return res; +} + +SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) +{ + sq_aux_paramscheck(v, 3); + SQObjectPtr &self = stack_get(v, idx); + if(sq_type(self) == OT_TABLE || sq_type(self) == OT_CLASS) { + SQObjectPtr &key = v->GetUp(-2); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); + v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false); + v->Pop(2); + } + return SQ_OK; +} + +SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) +{ + sq_aux_paramscheck(v, 2); + SQObjectPtr *self; + _GETSAFE_OBJ(v, idx, OT_TABLE,self); + SQObjectPtr &key = v->GetUp(-1); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); + SQObjectPtr res; + if(!v->DeleteSlot(*self, key, res)){ + v->Pop(); + return SQ_ERROR; + } + if(pushval) v->GetUp(-1) = res; + else v->Pop(); + return SQ_OK; +} + +SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self = stack_get(v, idx); + if(v->Set(self, v->GetUp(-2), v->GetUp(-1),DONT_FALL_BACK)) { + v->Pop(2); + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self = stack_get(v, idx); + SQObjectPtr &key = v->GetUp(-2); + if(sq_type(key) == OT_NULL) { + v->Pop(2); + return sq_throwerror(v, _SC("null key")); + } + switch(sq_type(self)) { + case OT_TABLE: + _table(self)->NewSlot(key, v->GetUp(-1)); + v->Pop(2); + return SQ_OK; + break; + case OT_CLASS: + _class(self)->NewSlot(_ss(v), key, v->GetUp(-1),false); + v->Pop(2); + return SQ_OK; + break; + case OT_INSTANCE: + if(_instance(self)->Set(key, v->GetUp(-1))) { + v->Pop(2); + return SQ_OK; + } + break; + case OT_ARRAY: + if(v->Set(self, key, v->GetUp(-1),false)) { + v->Pop(2); + return SQ_OK; + } + break; + default: + v->Pop(2); + return sq_throwerror(v, _SC("rawset works only on array/table/class and instance")); + } + v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR; +} + +SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) +{ + SQObjectPtr &self = stack_get(v, idx); + if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); + SQObjectPtr &key = v->GetUp(-3); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); + if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,false)) { + v->Pop(3); + return SQ_ERROR; + } + v->Pop(3); + return SQ_OK; +} + +SQRESULT sq_rawnewmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) +{ + SQObjectPtr &self = stack_get(v, idx); + if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); + SQObjectPtr &key = v->GetUp(-3); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); + if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,true)) { + v->Pop(3); + return SQ_ERROR; + } + v->Pop(3); + return SQ_OK; +} + +SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self = stack_get(v, idx); + SQObjectPtr &mt = v->GetUp(-1); + SQObjectType type = sq_type(self); + switch(type) { + case OT_TABLE: + if(sq_type(mt) == OT_TABLE) { + if(!_table(self)->SetDelegate(_table(mt))) { + return sq_throwerror(v, _SC("delegate cycle")); + } + v->Pop(); + } + else if(sq_type(mt)==OT_NULL) { + _table(self)->SetDelegate(NULL); v->Pop(); } + else return sq_aux_invalidtype(v,type); + break; + case OT_USERDATA: + if(sq_type(mt)==OT_TABLE) { + _userdata(self)->SetDelegate(_table(mt)); v->Pop(); } + else if(sq_type(mt)==OT_NULL) { + _userdata(self)->SetDelegate(NULL); v->Pop(); } + else return sq_aux_invalidtype(v, type); + break; + default: + return sq_aux_invalidtype(v, type); + break; + } + return SQ_OK; +} + +SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) +{ + sq_aux_paramscheck(v, 2); + SQObjectPtr *self; + _GETSAFE_OBJ(v, idx, OT_TABLE,self); + SQObjectPtr &key = v->GetUp(-1); + SQObjectPtr t; + if(_table(*self)->Get(key,t)) { + _table(*self)->Remove(key); + } + if(pushval != 0) + v->GetUp(-1) = t; + else + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self=stack_get(v,idx); + switch(sq_type(self)){ + case OT_TABLE: + case OT_USERDATA: + if(!_delegable(self)->_delegate){ + v->PushNull(); + break; + } + v->Push(SQObjectPtr(_delegable(self)->_delegate)); + break; + default: return sq_throwerror(v,_SC("wrong type")); break; + } + return SQ_OK; + +} + +SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self=stack_get(v,idx); + SQObjectPtr &obj = v->GetUp(-1); + if(v->Get(self,obj,obj,false,DONT_FALL_BACK)) + return SQ_OK; + v->Pop(); + return SQ_ERROR; +} + +SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self=stack_get(v,idx); + SQObjectPtr &obj = v->GetUp(-1); + switch(sq_type(self)) { + case OT_TABLE: + if(_table(self)->Get(obj,obj)) + return SQ_OK; + break; + case OT_CLASS: + if(_class(self)->Get(obj,obj)) + return SQ_OK; + break; + case OT_INSTANCE: + if(_instance(self)->Get(obj,obj)) + return SQ_OK; + break; + case OT_ARRAY:{ + if(sq_isnumeric(obj)){ + if(_array(self)->Get(tointeger(obj),obj)) { + return SQ_OK; + } + } + else { + v->Pop(); + return sq_throwerror(v,_SC("invalid index type for an array")); + } + } + break; + default: + v->Pop(); + return sq_throwerror(v,_SC("rawget works only on array/table/instance and class")); + } + v->Pop(); + return sq_throwerror(v,_SC("the index doesn't exist")); +} + +SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po) +{ + *po=stack_get(v,idx); + return SQ_OK; +} + +const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx) +{ + SQUnsignedInteger cstksize=v->_callsstacksize; + SQUnsignedInteger lvl=(cstksize-level)-1; + SQInteger stackbase=v->_stackbase; + if(lvl_callsstack[(cstksize-i)-1]; + stackbase-=ci._prevstkbase; + } + SQVM::CallInfo &ci=v->_callsstack[lvl]; + if(sq_type(ci._closure)!=OT_CLOSURE) + return NULL; + SQClosure *c=_closure(ci._closure); + SQFunctionProto *func=c->_function; + if(func->_noutervalues > (SQInteger)idx) { + v->Push(*_outer(c->_outervalues[idx])->_valptr); + return _stringval(func->_outervalues[idx]._name); + } + idx -= func->_noutervalues; + return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1); + } + return NULL; +} + +void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj) +{ + v->Push(SQObjectPtr(obj)); +} + +void sq_resetobject(HSQOBJECT *po) +{ + po->_unVal.pUserPointer=NULL;po->_type=OT_NULL; +} + +SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err) +{ + v->_lasterror=SQString::Create(_ss(v),err); + return SQ_ERROR; +} + +SQRESULT sq_throwobject(HSQUIRRELVM v) +{ + v->_lasterror = v->GetUp(-1); + v->Pop(); + return SQ_ERROR; +} + + +void sq_reseterror(HSQUIRRELVM v) +{ + v->_lasterror.Null(); +} + +void sq_getlasterror(HSQUIRRELVM v) +{ + v->Push(v->_lasterror); +} + +SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize) +{ + if (((SQUnsignedInteger)v->_top + nsize) > v->_stack.size()) { + if(v->_nmetamethodscall) { + return sq_throwerror(v,_SC("cannot resize stack while in a metamethod")); + } + v->_stack.resize(v->_stack.size() + ((v->_top + nsize) - v->_stack.size())); + } + return SQ_OK; +} + +SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) +{ + if (sq_type(v->GetUp(-1)) == OT_GENERATOR) + { + v->PushNull(); //retval + if (!v->Execute(v->GetUp(-2), 0, v->_top, v->GetUp(-1), raiseerror, SQVM::ET_RESUME_GENERATOR)) + {v->Raise_Error(v->_lasterror); return SQ_ERROR;} + if(!retval) + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v,_SC("only generators can be resumed")); +} + +SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) +{ + SQObjectPtr res; + if(!v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false)){ + v->Pop(params); //pop args + return SQ_ERROR; + } + if(!v->_suspended) + v->Pop(params); //pop args + if(retval) + v->Push(res); // push result + return SQ_OK; +} + +SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) +{ + SQObjectPtr &res = v->GetUp(-(nparams + 1)); + if (sq_type(res) != OT_CLOSURE) { + return sq_throwerror(v, _SC("only closure can be tail called")); + } + SQClosure *clo = _closure(res); + if (clo->_function->_bgenerator) + { + return sq_throwerror(v, _SC("generators cannot be tail called")); + } + + SQInteger stackbase = (v->_top - nparams) - v->_stackbase; + if (!v->TailCall(clo, stackbase, nparams)) { + return SQ_ERROR; + } + return SQ_TAILCALL_FLAG; +} + +SQRESULT sq_suspendvm(HSQUIRRELVM v) +{ + return v->Suspend(); +} + +SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseerror,SQBool throwerror) +{ + SQObjectPtr ret; + if(!v->_suspended) + return sq_throwerror(v,_SC("cannot resume a vm that is not running any code")); + SQInteger target = v->_suspended_target; + if(wakeupret) { + if(target != -1) { + v->GetAt(v->_stackbase+v->_suspended_target)=v->GetUp(-1); //retval + } + v->Pop(); + } else if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target).Null(); } + SQObjectPtr dummy; + if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM)) { + return SQ_ERROR; + } + if(retval) + v->Push(ret); + return SQ_OK; +} + +void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook) +{ + SQObjectPtr &ud=stack_get(v,idx); + switch(sq_type(ud) ) { + case OT_USERDATA: _userdata(ud)->_hook = hook; break; + case OT_INSTANCE: _instance(ud)->_hook = hook; break; + case OT_CLASS: _class(ud)->_hook = hook; break; + default: return; + } +} + +SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &ud=stack_get(v,idx); + switch(sq_type(ud) ) { + case OT_USERDATA: return _userdata(ud)->_hook; break; + case OT_INSTANCE: return _instance(ud)->_hook; break; + case OT_CLASS: return _class(ud)->_hook; break; + default: return NULL; + } +} + +void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f) +{ + _ss(v)->_compilererrorhandler = f; +} + +SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, -1, OT_CLOSURE,o); + unsigned short tag = SQ_BYTECODE_STREAM_TAG; + if(_closure(*o)->_function->_noutervalues) + return sq_throwerror(v,_SC("a closure with free variables bound cannot be serialized")); + if(w(up,&tag,2) != 2) + return sq_throwerror(v,_SC("io error")); + if(!_closure(*o)->Save(v,up,w)) + return SQ_ERROR; + return SQ_OK; +} + +SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up) +{ + SQObjectPtr closure; + + unsigned short tag; + if(r(up,&tag,2) != 2) + return sq_throwerror(v,_SC("io error")); + if(tag != SQ_BYTECODE_STREAM_TAG) + return sq_throwerror(v,_SC("invalid stream")); + if(!SQClosure::Load(v,up,r,closure)) + return SQ_ERROR; + v->Push(closure); + return SQ_OK; +} + +SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize) +{ + return _ss(v)->GetScratchPad(minsize); +} + +SQRESULT sq_resurrectunreachable(HSQUIRRELVM v) +{ +#ifndef NO_GARBAGE_COLLECTOR + _ss(v)->ResurrectUnreachable(v); + return SQ_OK; +#else + return sq_throwerror(v,_SC("sq_resurrectunreachable requires a garbage collector build")); +#endif +} + +SQInteger sq_collectgarbage(HSQUIRRELVM v) +{ +#ifndef NO_GARBAGE_COLLECTOR + return _ss(v)->CollectGarbage(v); +#else + return -1; +#endif +} + +SQRESULT sq_getcallee(HSQUIRRELVM v) +{ + if(v->_callsstacksize > 1) + { + v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); + return SQ_OK; + } + return sq_throwerror(v,_SC("no closure in the calls stack")); +} + +const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) +{ + SQObjectPtr &self=stack_get(v,idx); + const SQChar *name = NULL; + switch(sq_type(self)) + { + case OT_CLOSURE:{ + SQClosure *clo = _closure(self); + SQFunctionProto *fp = clo->_function; + if(((SQUnsignedInteger)fp->_noutervalues) > nval) { + v->Push(*(_outer(clo->_outervalues[nval])->_valptr)); + SQOuterVar &ov = fp->_outervalues[nval]; + name = _stringval(ov._name); + } + } + break; + case OT_NATIVECLOSURE:{ + SQNativeClosure *clo = _nativeclosure(self); + if(clo->_noutervalues > nval) { + v->Push(clo->_outervalues[nval]); + name = _SC("@NATIVE"); + } + } + break; + default: break; //shutup compiler + } + return name; +} + +SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) +{ + SQObjectPtr &self=stack_get(v,idx); + switch(sq_type(self)) + { + case OT_CLOSURE:{ + SQFunctionProto *fp = _closure(self)->_function; + if(((SQUnsignedInteger)fp->_noutervalues) > nval){ + *(_outer(_closure(self)->_outervalues[nval])->_valptr) = stack_get(v,-1); + } + else return sq_throwerror(v,_SC("invalid free var index")); + } + break; + case OT_NATIVECLOSURE: + if(_nativeclosure(self)->_noutervalues > nval){ + _nativeclosure(self)->_outervalues[nval] = stack_get(v,-1); + } + else return sq_throwerror(v,_SC("invalid free var index")); + break; + default: + return sq_aux_invalidtype(v, sq_type(self)); + } + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + SQObjectPtr &key = stack_get(v,-2); + SQObjectPtr &val = stack_get(v,-1); + SQObjectPtr attrs; + if(sq_type(key) == OT_NULL) { + attrs = _class(*o)->_attributes; + _class(*o)->_attributes = val; + v->Pop(2); + v->Push(attrs); + return SQ_OK; + }else if(_class(*o)->GetAttributes(key,attrs)) { + _class(*o)->SetAttributes(key,val); + v->Pop(2); + v->Push(attrs); + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong index")); +} + +SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + SQObjectPtr &key = stack_get(v,-1); + SQObjectPtr attrs; + if(sq_type(key) == OT_NULL) { + attrs = _class(*o)->_attributes; + v->Pop(); + v->Push(attrs); + return SQ_OK; + } + else if(_class(*o)->GetAttributes(key,attrs)) { + v->Pop(); + v->Push(attrs); + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong index")); +} + +SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + SQObjectPtr &key = stack_get(v,-1); + SQTable *m = _class(*o)->_members; + SQObjectPtr val; + if(m->Get(key,val)) { + handle->_static = _isfield(val) ? SQFalse : SQTrue; + handle->_index = _member_idx(val); + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong index")); +} + +SQRESULT _getmemberbyhandle(HSQUIRRELVM v,SQObjectPtr &self,const HSQMEMBERHANDLE *handle,SQObjectPtr *&val) +{ + switch(sq_type(self)) { + case OT_INSTANCE: { + SQInstance *i = _instance(self); + if(handle->_static) { + SQClass *c = i->_class; + val = &c->_methods[handle->_index].val; + } + else { + val = &i->_values[handle->_index]; + + } + } + break; + case OT_CLASS: { + SQClass *c = _class(self); + if(handle->_static) { + val = &c->_methods[handle->_index].val; + } + else { + val = &c->_defaultvalues[handle->_index].val; + } + } + break; + default: + return sq_throwerror(v,_SC("wrong type(expected class or instance)")); + } + return SQ_OK; +} + +SQRESULT sq_getbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) +{ + SQObjectPtr &self = stack_get(v,idx); + SQObjectPtr *val = NULL; + if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { + return SQ_ERROR; + } + v->Push(_realval(*val)); + return SQ_OK; +} + +SQRESULT sq_setbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) +{ + SQObjectPtr &self = stack_get(v,idx); + SQObjectPtr &newval = stack_get(v,-1); + SQObjectPtr *val = NULL; + if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { + return SQ_ERROR; + } + *val = newval; + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + if(_class(*o)->_base) + v->Push(SQObjectPtr(_class(*o)->_base)); + else + v->PushNull(); + return SQ_OK; +} + +SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_INSTANCE,o); + v->Push(SQObjectPtr(_instance(*o)->_class)); + return SQ_OK; +} + +SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + v->Push(_class(*o)->CreateInstance()); + return SQ_OK; +} + +void sq_weakref(HSQUIRRELVM v,SQInteger idx) +{ + SQObject &o=stack_get(v,idx); + if(ISREFCOUNTED(sq_type(o))) { + v->Push(_refcounted(o)->GetWeakRef(sq_type(o))); + return; + } + v->Push(o); +} + +SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_WEAKREF) { + return sq_throwerror(v,_SC("the object must be a weakref")); + } + v->Push(_weakref(o)->_obj); + return SQ_OK; +} + +SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t) +{ + SQSharedState *ss = _ss(v); + switch(t) { + case OT_TABLE: v->Push(ss->_table_default_delegate); break; + case OT_ARRAY: v->Push(ss->_array_default_delegate); break; + case OT_STRING: v->Push(ss->_string_default_delegate); break; + case OT_INTEGER: case OT_FLOAT: v->Push(ss->_number_default_delegate); break; + case OT_GENERATOR: v->Push(ss->_generator_default_delegate); break; + case OT_CLOSURE: case OT_NATIVECLOSURE: v->Push(ss->_closure_default_delegate); break; + case OT_THREAD: v->Push(ss->_thread_default_delegate); break; + case OT_CLASS: v->Push(ss->_class_default_delegate); break; + case OT_INSTANCE: v->Push(ss->_instance_default_delegate); break; + case OT_WEAKREF: v->Push(ss->_weakref_default_delegate); break; + default: return sq_throwerror(v,_SC("the type doesn't have a default delegate")); + } + return SQ_OK; +} + +SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val; + if(sq_type(o) == OT_GENERATOR) { + return sq_throwerror(v,_SC("cannot iterate a generator")); + } + int faketojump; + if(!v->FOREACH_OP(o,realkey,val,refpos,0,666,faketojump)) + return SQ_ERROR; + if(faketojump != 666) { + v->Push(realkey); + v->Push(val); + return SQ_OK; + } + return SQ_ERROR; +} + +struct BufState{ + const SQChar *buf; + SQInteger ptr; + SQInteger size; +}; + +SQInteger buf_lexfeed(SQUserPointer file) +{ + BufState *buf=(BufState*)file; + if(buf->size<(buf->ptr+1)) + return 0; + return buf->buf[buf->ptr++]; +} + +SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) { + BufState buf; + buf.buf = s; + buf.size = size; + buf.ptr = 0; + return sq_compile(v, buf_lexfeed, &buf, sourcename, raiseerror); +} + +void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx) +{ + dest->Push(stack_get(src,idx)); +} + +void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc,SQPRINTFUNCTION errfunc) +{ + _ss(v)->_printfunc = printfunc; + _ss(v)->_errorfunc = errfunc; +} + +SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v) +{ + return _ss(v)->_printfunc; +} + +SQPRINTFUNCTION sq_geterrorfunc(HSQUIRRELVM v) +{ + return _ss(v)->_errorfunc; +} + +void *sq_malloc(SQUnsignedInteger size) +{ + return SQ_MALLOC(size); +} + +void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize) +{ + return SQ_REALLOC(p,oldsize,newsize); +} + +void sq_free(void *p,SQUnsignedInteger size) +{ + SQ_FREE(p,size); +} diff --git a/mp/src/vscript/squirrel/squirrel/sqarray.h b/mp/src/vscript/squirrel/squirrel/sqarray.h new file mode 100644 index 00000000..7c6c2049 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqarray.h @@ -0,0 +1,94 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQARRAY_H_ +#define _SQARRAY_H_ + +struct SQArray : public CHAINABLE_OBJ +{ +private: + SQArray(SQSharedState *ss,SQInteger nsize){_values.resize(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} + ~SQArray() + { + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); + } +public: + static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){ + SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray)); + new (newarray) SQArray(ss,nInitialSize); + return newarray; + } +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + SQObjectType GetType() {return OT_ARRAY;} +#endif + void Finalize(){ + _values.resize(0); + } + bool Get(const SQInteger nidx,SQObjectPtr &val) + { + if(nidx>=0 && nidx<(SQInteger)_values.size()){ + SQObjectPtr &o = _values[nidx]; + val = _realval(o); + return true; + } + else return false; + } + bool Set(const SQInteger nidx,const SQObjectPtr &val) + { + if(nidx>=0 && nidx<(SQInteger)_values.size()){ + _values[nidx]=val; + return true; + } + else return false; + } + SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval) + { + SQUnsignedInteger idx=TranslateIndex(refpos); + while(idx<_values.size()){ + //first found + outkey=(SQInteger)idx; + SQObjectPtr &o = _values[idx]; + outval = _realval(o); + //return idx for the next iteration + return ++idx; + } + //nothing to iterate anymore + return -1; + } + SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),0); anew->_values.copy(_values); return anew; } + SQInteger Size() const {return _values.size();} + void Resize(SQInteger size) + { + SQObjectPtr _null; + Resize(size,_null); + } + void Resize(SQInteger size,SQObjectPtr &fill) { _values.resize(size,fill); ShrinkIfNeeded(); } + void Reserve(SQInteger size) { _values.reserve(size); } + void Append(const SQObject &o){_values.push_back(o);} + void Extend(const SQArray *a); + SQObjectPtr &Top(){return _values.top();} + void Pop(){_values.pop_back(); ShrinkIfNeeded(); } + bool Insert(SQInteger idx,const SQObject &val){ + if(idx < 0 || idx > (SQInteger)_values.size()) + return false; + _values.insert(idx,val); + return true; + } + void ShrinkIfNeeded() { + if(_values.size() <= _values.capacity()>>2) //shrink the array + _values.shrinktofit(); + } + bool Remove(SQInteger idx){ + if(idx < 0 || idx >= (SQInteger)_values.size()) + return false; + _values.remove(idx); + ShrinkIfNeeded(); + return true; + } + void Release() + { + sq_delete(this,SQArray); + } + + SQObjectPtrVec _values; +}; +#endif //_SQARRAY_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp b/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp new file mode 100644 index 00000000..fb6545f9 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp @@ -0,0 +1,1370 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqstring.h" +#include "sqtable.h" +#include "sqarray.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqclass.h" +#include +#include +#include + +static bool str2num(const SQChar *s,SQObjectPtr &res,SQInteger base) +{ + SQChar *end; + const SQChar *e = s; + bool iseintbase = base > 13; //to fix error converting hexadecimals with e like 56f0791e + bool isfloat = false; + SQChar c; + while((c = *e) != _SC('\0')) + { + if (c == _SC('.') || (!iseintbase && (c == _SC('E') || c == _SC('e')))) { //e and E is for scientific notation + isfloat = true; + break; + } + e++; + } + if(isfloat){ + SQFloat r = SQFloat(scstrtod(s,&end)); + if(s == end) return false; + res = r; + } + else{ + SQInteger r = SQInteger(scstrtol(s,&end,(int)base)); + if(s == end) return false; + res = r; + } + return true; +} + +static SQInteger base_dummy(HSQUIRRELVM SQ_UNUSED_ARG(v)) +{ + return 0; +} + +#ifndef NO_GARBAGE_COLLECTOR +static SQInteger base_collectgarbage(HSQUIRRELVM v) +{ + sq_pushinteger(v, sq_collectgarbage(v)); + return 1; +} +static SQInteger base_resurectureachable(HSQUIRRELVM v) +{ + sq_resurrectunreachable(v); + return 1; +} +#endif + +static SQInteger base_getroottable(HSQUIRRELVM v) +{ + v->Push(v->_roottable); + return 1; +} + +static SQInteger base_getconsttable(HSQUIRRELVM v) +{ + v->Push(_ss(v)->_consts); + return 1; +} + + +static SQInteger base_setroottable(HSQUIRRELVM v) +{ + SQObjectPtr o = v->_roottable; + if(SQ_FAILED(sq_setroottable(v))) return SQ_ERROR; + v->Push(o); + return 1; +} + +static SQInteger base_setconsttable(HSQUIRRELVM v) +{ + SQObjectPtr o = _ss(v)->_consts; + if(SQ_FAILED(sq_setconsttable(v))) return SQ_ERROR; + v->Push(o); + return 1; +} + +static SQInteger base_seterrorhandler(HSQUIRRELVM v) +{ + sq_seterrorhandler(v); + return 0; +} + +static SQInteger base_setdebughook(HSQUIRRELVM v) +{ + sq_setdebughook(v); + return 0; +} + +static SQInteger base_enabledebuginfo(HSQUIRRELVM v) +{ + SQObjectPtr &o=stack_get(v,2); + + sq_enabledebuginfo(v,SQVM::IsFalse(o)?SQFalse:SQTrue); + return 0; +} + +static SQInteger __getcallstackinfos(HSQUIRRELVM v,SQInteger level) +{ + SQStackInfos si; + SQInteger seq = 0; + const SQChar *name = NULL; + + if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si))) + { + const SQChar *fn = _SC("unknown"); + const SQChar *src = _SC("unknown"); + if(si.funcname)fn = si.funcname; + if(si.source)src = si.source; + sq_newtable(v); + sq_pushstring(v, _SC("func"), -1); + sq_pushstring(v, fn, -1); + sq_newslot(v, -3, SQFalse); + sq_pushstring(v, _SC("src"), -1); + sq_pushstring(v, src, -1); + sq_newslot(v, -3, SQFalse); + sq_pushstring(v, _SC("line"), -1); + sq_pushinteger(v, si.line); + sq_newslot(v, -3, SQFalse); + sq_pushstring(v, _SC("locals"), -1); + sq_newtable(v); + seq=0; + while ((name = sq_getlocal(v, level, seq))) { + sq_pushstring(v, name, -1); + sq_push(v, -2); + sq_newslot(v, -4, SQFalse); + sq_pop(v, 1); + seq++; + } + sq_newslot(v, -3, SQFalse); + return 1; + } + + return 0; +} +static SQInteger base_getstackinfos(HSQUIRRELVM v) +{ + SQInteger level; + sq_getinteger(v, -1, &level); + return __getcallstackinfos(v,level); +} + +static SQInteger base_assert(HSQUIRRELVM v) +{ + if(SQVM::IsFalse(stack_get(v,2))){ + SQInteger top = sq_gettop(v); + if (top>2 && SQ_SUCCEEDED(sq_tostring(v,3))) { + const SQChar *str = 0; + if (SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + return sq_throwerror(v, str); + } + } + return sq_throwerror(v, _SC("assertion failed")); + } + return 0; +} + +static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o) +{ + SQInteger top = sq_gettop(v); + sidx=0; + eidx=0; + o=stack_get(v,1); + if(top>1){ + SQObjectPtr &start=stack_get(v,2); + if(sq_type(start)!=OT_NULL && sq_isnumeric(start)){ + sidx=tointeger(start); + } + } + if(top>2){ + SQObjectPtr &end=stack_get(v,3); + if(sq_isnumeric(end)){ + eidx=tointeger(end); + } + } + else { + eidx = sq_getsize(v,1); + } + return 1; +} + +static SQInteger base_print(HSQUIRRELVM v) +{ + const SQChar *str; + if(SQ_SUCCEEDED(sq_tostring(v,2))) + { + if(SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,_SC("%s"),str); + return 0; + } + } + return SQ_ERROR; +} + +static SQInteger base_error(HSQUIRRELVM v) +{ + const SQChar *str; + if(SQ_SUCCEEDED(sq_tostring(v,2))) + { + if(SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + if(_ss(v)->_errorfunc) _ss(v)->_errorfunc(v,_SC("%s"),str); + return 0; + } + } + return SQ_ERROR; +} + +static SQInteger base_compilestring(HSQUIRRELVM v) +{ + SQInteger nargs=sq_gettop(v); + const SQChar *src=NULL,*name=_SC("unnamedbuffer"); + SQInteger size; + sq_getstring(v,2,&src); + size=sq_getsize(v,2); + if(nargs>2){ + sq_getstring(v,3,&name); + } + if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse))) + return 1; + else + return SQ_ERROR; +} + +static SQInteger base_newthread(HSQUIRRELVM v) +{ + SQObjectPtr &func = stack_get(v,2); + SQInteger stksize = (_closure(func)->_function->_stacksize << 1) +2; + HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize); + sq_move(newv,v,-2); + return 1; +} + +static SQInteger base_suspend(HSQUIRRELVM v) +{ + return sq_suspendvm(v); +} + +static SQInteger base_array(HSQUIRRELVM v) +{ + SQArray *a; + SQObject &size = stack_get(v,2); + if(sq_gettop(v) > 2) { + a = SQArray::Create(_ss(v),0); + a->Resize(tointeger(size),stack_get(v,3)); + } + else { + a = SQArray::Create(_ss(v),tointeger(size)); + } + v->Push(a); + return 1; +} + +static SQInteger base_type(HSQUIRRELVM v) +{ + SQObjectPtr &o = stack_get(v,2); + v->Push(SQString::Create(_ss(v),GetTypeName(o),-1)); + return 1; +} + +static SQInteger base_callee(HSQUIRRELVM v) +{ + if(v->_callsstacksize > 1) + { + v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); + return 1; + } + return sq_throwerror(v,_SC("no closure in the calls stack")); +} + +static const SQRegFunction base_funcs[]={ + //generic + {_SC("seterrorhandler"),base_seterrorhandler,2, NULL}, + {_SC("setdebughook"),base_setdebughook,2, NULL}, + {_SC("enabledebuginfo"),base_enabledebuginfo,2, NULL}, + {_SC("getstackinfos"),base_getstackinfos,2, _SC(".n")}, + {_SC("getroottable"),base_getroottable,1, NULL}, + {_SC("setroottable"),base_setroottable,2, NULL}, + {_SC("getconsttable"),base_getconsttable,1, NULL}, + {_SC("setconsttable"),base_setconsttable,2, NULL}, + {_SC("assert"),base_assert,-2, NULL}, + {_SC("print"),base_print,2, NULL}, + {_SC("error"),base_error,2, NULL}, + {_SC("compilestring"),base_compilestring,-2, _SC(".ss")}, + {_SC("newthread"),base_newthread,2, _SC(".c")}, + {_SC("suspend"),base_suspend,-1, NULL}, + {_SC("array"),base_array,-2, _SC(".n")}, + {_SC("type"),base_type,2, NULL}, + {_SC("callee"),base_callee,0,NULL}, + {_SC("dummy"),base_dummy,0,NULL}, +#ifndef NO_GARBAGE_COLLECTOR + {_SC("collectgarbage"),base_collectgarbage,0, NULL}, + {_SC("resurrectunreachable"),base_resurectureachable,0, NULL}, +#endif + {NULL,(SQFUNCTION)0,0,NULL} +}; + +void sq_base_register(HSQUIRRELVM v) +{ + SQInteger i=0; + sq_pushroottable(v); + while(base_funcs[i].name!=0) { + sq_pushstring(v,base_funcs[i].name,-1); + sq_newclosure(v,base_funcs[i].f,0); + sq_setnativeclosurename(v,-1,base_funcs[i].name); + sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask); + sq_newslot(v,-3, SQFalse); + i++; + } + + sq_pushstring(v,_SC("_versionnumber_"),-1); + sq_pushinteger(v,SQUIRREL_VERSION_NUMBER); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_version_"),-1); + sq_pushstring(v,SQUIRREL_VERSION,-1); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_charsize_"),-1); + sq_pushinteger(v,sizeof(SQChar)); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_intsize_"),-1); + sq_pushinteger(v,sizeof(SQInteger)); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_floatsize_"),-1); + sq_pushinteger(v,sizeof(SQFloat)); + sq_newslot(v,-3, SQFalse); + sq_pop(v,1); +} + +static SQInteger default_delegate_len(HSQUIRRELVM v) +{ + v->Push(SQInteger(sq_getsize(v,1))); + return 1; +} + +static SQInteger default_delegate_tofloat(HSQUIRRELVM v) +{ + SQObjectPtr &o=stack_get(v,1); + switch(sq_type(o)){ + case OT_STRING:{ + SQObjectPtr res; + if(str2num(_stringval(o),res,10)){ + v->Push(SQObjectPtr(tofloat(res))); + break; + }} + return sq_throwerror(v, _SC("cannot convert the string")); + break; + case OT_INTEGER:case OT_FLOAT: + v->Push(SQObjectPtr(tofloat(o))); + break; + case OT_BOOL: + v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0))); + break; + default: + v->PushNull(); + break; + } + return 1; +} + +static SQInteger default_delegate_tointeger(HSQUIRRELVM v) +{ + SQObjectPtr &o=stack_get(v,1); + SQInteger base = 10; + if(sq_gettop(v) > 1) { + sq_getinteger(v,2,&base); + } + switch(sq_type(o)){ + case OT_STRING:{ + SQObjectPtr res; + if(str2num(_stringval(o),res,base)){ + v->Push(SQObjectPtr(tointeger(res))); + break; + }} + return sq_throwerror(v, _SC("cannot convert the string")); + break; + case OT_INTEGER:case OT_FLOAT: + v->Push(SQObjectPtr(tointeger(o))); + break; + case OT_BOOL: + v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0)); + break; + default: + v->PushNull(); + break; + } + return 1; +} + +static SQInteger default_delegate_tostring(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_tostring(v,1))) + return SQ_ERROR; + return 1; +} + +static SQInteger obj_delegate_weakref(HSQUIRRELVM v) +{ + sq_weakref(v,1); + return 1; +} + +static SQInteger obj_clear(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_clear(v,-1)) ? 1 : SQ_ERROR; +} + + +static SQInteger number_delegate_tochar(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + SQChar c = (SQChar)tointeger(o); + v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1)); + return 1; +} + + + +///////////////////////////////////////////////////////////////// +//TABLE DEFAULT DELEGATE + +static SQInteger table_rawdelete(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue))) + return SQ_ERROR; + return 1; +} + + +static SQInteger container_rawexists(HSQUIRRELVM v) +{ + if(SQ_SUCCEEDED(sq_rawget(v,-2))) { + sq_pushbool(v,SQTrue); + return 1; + } + sq_pushbool(v,SQFalse); + return 1; +} + +static SQInteger container_rawset(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_rawset(v,-3)) ? 1 : SQ_ERROR; +} + + +static SQInteger container_rawget(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR; +} + +static SQInteger table_setdelegate(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_setdelegate(v,-2))) + return SQ_ERROR; + sq_push(v,-1); // -1 because sq_setdelegate pops 1 + return 1; +} + +static SQInteger table_getdelegate(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_getdelegate(v,-1))?1:SQ_ERROR; +} + +static SQInteger table_filter(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQTable *tbl = _table(o); + SQObjectPtr ret = SQTable::Create(_ss(v),0); + + SQObjectPtr itr, key, val; + SQInteger nitr; + while((nitr = tbl->Next(false, itr, key, val)) != -1) { + itr = (SQInteger)nitr; + + v->Push(o); + v->Push(key); + v->Push(val); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + if(!SQVM::IsFalse(v->GetUp(-1))) { + _table(ret)->NewSlot(key, val); + } + v->Pop(); + } + + v->Push(ret); + return 1; +} + +#define TABLE_TO_ARRAY_FUNC(_funcname_,_valname_) static SQInteger _funcname_(HSQUIRRELVM v) \ +{ \ + SQObject &o = stack_get(v, 1); \ + SQTable *t = _table(o); \ + SQObjectPtr itr, key, val; \ + SQObjectPtr _null; \ + SQInteger nitr, n = 0; \ + SQInteger nitems = t->CountUsed(); \ + SQArray *a = SQArray::Create(_ss(v), nitems); \ + a->Resize(nitems, _null); \ + if (nitems) { \ + while ((nitr = t->Next(false, itr, key, val)) != -1) { \ + itr = (SQInteger)nitr; \ + a->Set(n, _valname_); \ + n++; \ + } \ + } \ + v->Push(a); \ + return 1; \ +} + +TABLE_TO_ARRAY_FUNC(table_keys, key) +TABLE_TO_ARRAY_FUNC(table_values, val) + + +const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("t")}, + {_SC("rawget"),container_rawget,2, _SC("t")}, + {_SC("rawset"),container_rawset,3, _SC("t")}, + {_SC("rawdelete"),table_rawdelete,2, _SC("t")}, + {_SC("rawin"),container_rawexists,2, _SC("t")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("clear"),obj_clear,1, _SC(".")}, + {_SC("setdelegate"),table_setdelegate,2, _SC(".t|o")}, + {_SC("getdelegate"),table_getdelegate,1, _SC(".")}, + {_SC("filter"),table_filter,2, _SC("tc")}, + {_SC("keys"),table_keys,1, _SC("t") }, + {_SC("values"),table_values,1, _SC("t") }, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//ARRAY DEFAULT DELEGATE/////////////////////////////////////// + +static SQInteger array_append(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_arrayappend(v,-2)) ? 1 : SQ_ERROR; +} + +static SQInteger array_extend(HSQUIRRELVM v) +{ + _array(stack_get(v,1))->Extend(_array(stack_get(v,2))); + sq_pop(v,1); + return 1; +} + +static SQInteger array_reverse(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_arrayreverse(v,-1)) ? 1 : SQ_ERROR; +} + +static SQInteger array_pop(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR; +} + +static SQInteger array_top(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + if(_array(o)->Size()>0){ + v->Push(_array(o)->Top()); + return 1; + } + else return sq_throwerror(v,_SC("top() on a empty array")); +} + +static SQInteger array_insert(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + SQObject &idx=stack_get(v,2); + SQObject &val=stack_get(v,3); + if(!_array(o)->Insert(tointeger(idx),val)) + return sq_throwerror(v,_SC("index out of range")); + sq_pop(v,2); + return 1; +} + +static SQInteger array_remove(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v, 1); + SQObject &idx = stack_get(v, 2); + if(!sq_isnumeric(idx)) return sq_throwerror(v, _SC("wrong type")); + SQObjectPtr val; + if(_array(o)->Get(tointeger(idx), val)) { + _array(o)->Remove(tointeger(idx)); + v->Push(val); + return 1; + } + return sq_throwerror(v, _SC("idx out of range")); +} + +static SQInteger array_resize(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v, 1); + SQObject &nsize = stack_get(v, 2); + SQObjectPtr fill; + if(sq_isnumeric(nsize)) { + SQInteger sz = tointeger(nsize); + if (sz<0) + return sq_throwerror(v, _SC("resizing to negative length")); + + if(sq_gettop(v) > 2) + fill = stack_get(v, 3); + _array(o)->Resize(sz,fill); + sq_settop(v, 1); + return 1; + } + return sq_throwerror(v, _SC("size must be a number")); +} + +static SQInteger __map_array(SQArray *dest,SQArray *src,HSQUIRRELVM v) { + SQObjectPtr temp; + SQInteger size = src->Size(); + SQObject &closure = stack_get(v, 2); + v->Push(closure); + + SQInteger nArgs = 0; + if(sq_type(closure) == OT_CLOSURE) { + nArgs = _closure(closure)->_function->_nparameters; + } + else if (sq_type(closure) == OT_NATIVECLOSURE) { + SQInteger nParamsCheck = _nativeclosure(closure)->_nparamscheck; + if (nParamsCheck > 0) + nArgs = nParamsCheck; + else // push all params when there is no check or only minimal count set + nArgs = 4; + } + + for(SQInteger n = 0; n < size; n++) { + src->Get(n,temp); + v->Push(src); + v->Push(temp); + if (nArgs >= 3) + v->Push(SQObjectPtr(n)); + if (nArgs >= 4) + v->Push(src); + if(SQ_FAILED(sq_call(v,nArgs,SQTrue,SQFalse))) { + return SQ_ERROR; + } + dest->Set(n,v->GetUp(-1)); + v->Pop(); + } + v->Pop(); + return 0; +} + +static SQInteger array_map(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQInteger size = _array(o)->Size(); + SQObjectPtr ret = SQArray::Create(_ss(v),size); + if(SQ_FAILED(__map_array(_array(ret),_array(o),v))) + return SQ_ERROR; + v->Push(ret); + return 1; +} + +static SQInteger array_apply(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + if(SQ_FAILED(__map_array(_array(o),_array(o),v))) + return SQ_ERROR; + sq_pop(v,1); + return 1; +} + +static SQInteger array_reduce(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQArray *a = _array(o); + SQInteger size = a->Size(); + SQObjectPtr res; + SQInteger iterStart; + if (sq_gettop(v)>2) { + res = stack_get(v,3); + iterStart = 0; + } else if (size==0) { + return 0; + } else { + a->Get(0,res); + iterStart = 1; + } + if (size > iterStart) { + SQObjectPtr other; + v->Push(stack_get(v,2)); + for (SQInteger n = iterStart; n < size; n++) { + a->Get(n,other); + v->Push(o); + v->Push(res); + v->Push(other); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + res = v->GetUp(-1); + v->Pop(); + } + v->Pop(); + } + v->Push(res); + return 1; +} + +static SQInteger array_filter(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQArray *a = _array(o); + SQObjectPtr ret = SQArray::Create(_ss(v),0); + SQInteger size = a->Size(); + SQObjectPtr val; + for(SQInteger n = 0; n < size; n++) { + a->Get(n,val); + v->Push(o); + v->Push(n); + v->Push(val); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + if(!SQVM::IsFalse(v->GetUp(-1))) { + _array(ret)->Append(val); + } + v->Pop(); + } + v->Push(ret); + return 1; +} + +static SQInteger array_find(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQObjectPtr &val = stack_get(v,2); + SQArray *a = _array(o); + SQInteger size = a->Size(); + SQObjectPtr temp; + for(SQInteger n = 0; n < size; n++) { + bool res = false; + a->Get(n,temp); + if(SQVM::IsEqual(temp,val,res) && res) { + v->Push(n); + return 1; + } + } + return 0; +} + + +static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) +{ + if(func < 0) { + if(!v->ObjCmp(a,b,ret)) return false; + } + else { + SQInteger top = sq_gettop(v); + sq_push(v, func); + sq_pushroottable(v); + v->Push(a); + v->Push(b); + if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) { + if(!sq_isstring( v->_lasterror)) + v->Raise_Error(_SC("compare func failed")); + return false; + } + if(SQ_FAILED(sq_getinteger(v, -1, &ret))) { + v->Raise_Error(_SC("numeric value expected as return value of the compare function")); + return false; + } + sq_settop(v, top); + return true; + } + return true; +} + +static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, SQInteger func) +{ + SQInteger maxChild; + SQInteger done = 0; + SQInteger ret; + SQInteger root2; + while (((root2 = root * 2) <= bottom) && (!done)) + { + if (root2 == bottom) { + maxChild = root2; + } + else { + if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret)) + return false; + if (ret > 0) { + maxChild = root2; + } + else { + maxChild = root2 + 1; + } + } + + if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret)) + return false; + if (ret < 0) { + if (root == maxChild) { + v->Raise_Error(_SC("inconsistent compare function")); + return false; // We'd be swapping ourselve. The compare function is incorrect + } + + _Swap(arr->_values[root],arr->_values[maxChild]); + root = maxChild; + } + else { + done = 1; + } + } + return true; +} + +static bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger SQ_UNUSED_ARG(l), SQInteger SQ_UNUSED_ARG(r),SQInteger func) +{ + SQArray *a = _array(arr); + SQInteger i; + SQInteger array_size = a->Size(); + for (i = (array_size / 2); i >= 0; i--) { + if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false; + } + + for (i = array_size-1; i >= 1; i--) + { + _Swap(a->_values[0],a->_values[i]); + if(!_hsort_sift_down(v,a, 0, i-1,func)) return false; + } + return true; +} + +static SQInteger array_sort(HSQUIRRELVM v) +{ + SQInteger func = -1; + SQObjectPtr &o = stack_get(v,1); + if(_array(o)->Size() > 1) { + if(sq_gettop(v) == 2) func = 2; + if(!_hsort(v, o, 0, _array(o)->Size()-1, func)) + return SQ_ERROR; + + } + sq_settop(v,1); + return 1; +} + +static SQInteger array_slice(HSQUIRRELVM v) +{ + SQInteger sidx,eidx; + SQObjectPtr o; + if(get_slice_params(v,sidx,eidx,o)==-1)return -1; + SQInteger alen = _array(o)->Size(); + if(sidx < 0)sidx = alen + sidx; + if(eidx < 0)eidx = alen + eidx; + if(eidx < sidx)return sq_throwerror(v,_SC("wrong indexes")); + if(eidx > alen || sidx < 0)return sq_throwerror(v, _SC("slice out of range")); + SQArray *arr=SQArray::Create(_ss(v),eidx-sidx); + SQObjectPtr t; + SQInteger count=0; + for(SQInteger i=sidx;iGet(i,t); + arr->Set(count++,t); + } + v->Push(arr); + return 1; + +} + +const SQRegFunction SQSharedState::_array_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("a")}, + {_SC("append"),array_append,2, _SC("a")}, + {_SC("extend"),array_extend,2, _SC("aa")}, + {_SC("push"),array_append,2, _SC("a")}, + {_SC("pop"),array_pop,1, _SC("a")}, + {_SC("top"),array_top,1, _SC("a")}, + {_SC("insert"),array_insert,3, _SC("an")}, + {_SC("remove"),array_remove,2, _SC("an")}, + {_SC("resize"),array_resize,-2, _SC("an")}, + {_SC("reverse"),array_reverse,1, _SC("a")}, + {_SC("sort"),array_sort,-1, _SC("ac")}, + {_SC("slice"),array_slice,-1, _SC("ann")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("clear"),obj_clear,1, _SC(".")}, + {_SC("map"),array_map,2, _SC("ac")}, + {_SC("apply"),array_apply,2, _SC("ac")}, + {_SC("reduce"),array_reduce,-2, _SC("ac.")}, + {_SC("filter"),array_filter,2, _SC("ac")}, + {_SC("find"),array_find,2, _SC("a.")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//STRING DEFAULT DELEGATE////////////////////////// +static SQInteger string_slice(HSQUIRRELVM v) +{ + SQInteger sidx,eidx; + SQObjectPtr o; + if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1; + SQInteger slen = _string(o)->_len; + if(sidx < 0)sidx = slen + sidx; + if(eidx < 0)eidx = slen + eidx; + if(eidx < sidx) return sq_throwerror(v,_SC("wrong indexes")); + if(eidx > slen || sidx < 0) return sq_throwerror(v, _SC("slice out of range")); + v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx)); + return 1; +} + +static SQInteger string_find(HSQUIRRELVM v) +{ + SQInteger top,start_idx=0; + const SQChar *str,*substr,*ret; + if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){ + if(top>2)sq_getinteger(v,3,&start_idx); + if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){ + ret=scstrstr(&str[start_idx],substr); + if(ret){ + sq_pushinteger(v,(SQInteger)(ret-str)); + return 1; + } + } + return 0; + } + return sq_throwerror(v,_SC("invalid param")); +} + +#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \ +{\ + SQInteger sidx,eidx; \ + SQObjectPtr str; \ + if(SQ_FAILED(get_slice_params(v,sidx,eidx,str)))return -1; \ + SQInteger slen = _string(str)->_len; \ + if(sidx < 0)sidx = slen + sidx; \ + if(eidx < 0)eidx = slen + eidx; \ + if(eidx < sidx) return sq_throwerror(v,_SC("wrong indexes")); \ + if(eidx > slen || sidx < 0) return sq_throwerror(v,_SC("slice out of range")); \ + SQInteger len=_string(str)->_len; \ + const SQChar *sthis=_stringval(str); \ + SQChar *snew=(_ss(v)->GetScratchPad(sq_rsl(len))); \ + memcpy(snew,sthis,sq_rsl(len));\ + for(SQInteger i=sidx;iPush(SQString::Create(_ss(v),snew,len)); \ + return 1; \ +} + + +STRING_TOFUNCZ(tolower) +STRING_TOFUNCZ(toupper) + +const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("s")}, + {_SC("tointeger"),default_delegate_tointeger,-1, _SC("sn")}, + {_SC("tofloat"),default_delegate_tofloat,1, _SC("s")}, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("slice"),string_slice,-1, _SC("s n n")}, + {_SC("find"),string_find,-2, _SC("s s n")}, + {_SC("tolower"),string_tolower,-1, _SC("s n n")}, + {_SC("toupper"),string_toupper,-1, _SC("s n n")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//INTEGER DEFAULT DELEGATE////////////////////////// +const SQRegFunction SQSharedState::_number_default_delegate_funcz[]={ + {_SC("tointeger"),default_delegate_tointeger,1, _SC("n|b")}, + {_SC("tofloat"),default_delegate_tofloat,1, _SC("n|b")}, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("tochar"),number_delegate_tochar,1, _SC("n|b")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//CLOSURE DEFAULT DELEGATE////////////////////////// +static SQInteger closure_pcall(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR; +} + +static SQInteger closure_call(HSQUIRRELVM v) +{ + SQObjectPtr &c = stack_get(v, -1); + if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) + { + return sq_tailcall(v, sq_gettop(v) - 1); + } + return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; +} + +static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror) +{ + SQArray *aparams=_array(stack_get(v,2)); + SQInteger nparams=aparams->Size(); + v->Push(stack_get(v,1)); + for(SQInteger i=0;iPush(aparams->_values[i]); + return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,raiseerror))?1:SQ_ERROR; +} + +static SQInteger closure_acall(HSQUIRRELVM v) +{ + return _closure_acall(v,SQTrue); +} + +static SQInteger closure_pacall(HSQUIRRELVM v) +{ + return _closure_acall(v,SQFalse); +} + +static SQInteger closure_bindenv(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_bindenv(v,1))) + return SQ_ERROR; + return 1; +} + +static SQInteger closure_getroot(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_getclosureroot(v,-1))) + return SQ_ERROR; + return 1; +} + +static SQInteger closure_setroot(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_setclosureroot(v,-2))) + return SQ_ERROR; + return 1; +} + +static SQInteger closure_getinfos(HSQUIRRELVM v) { + SQObject o = stack_get(v,1); + SQTable *res = SQTable::Create(_ss(v),4); + if(sq_type(o) == OT_CLOSURE) { + SQFunctionProto *f = _closure(o)->_function; + SQInteger nparams = f->_nparameters + (f->_varparams?1:0); + SQObjectPtr params = SQArray::Create(_ss(v),nparams); + SQObjectPtr defparams = SQArray::Create(_ss(v),f->_ndefaultparams); + for(SQInteger n = 0; n_nparameters; n++) { + _array(params)->Set((SQInteger)n,f->_parameters[n]); + } + for(SQInteger j = 0; j_ndefaultparams; j++) { + _array(defparams)->Set((SQInteger)j,_closure(o)->_defaultparams[j]); + } + if(f->_varparams) { + _array(params)->Set(nparams-1,SQString::Create(_ss(v),_SC("..."),-1)); + } + res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),false); + res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),f->_name); + res->NewSlot(SQString::Create(_ss(v),_SC("src"),-1),f->_sourcename); + res->NewSlot(SQString::Create(_ss(v),_SC("parameters"),-1),params); + res->NewSlot(SQString::Create(_ss(v),_SC("varargs"),-1),f->_varparams); + res->NewSlot(SQString::Create(_ss(v),_SC("defparams"),-1),defparams); + } + else { //OT_NATIVECLOSURE + SQNativeClosure *nc = _nativeclosure(o); + res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),true); + res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),nc->_name); + res->NewSlot(SQString::Create(_ss(v),_SC("paramscheck"),-1),nc->_nparamscheck); + SQObjectPtr typecheck; + if(nc->_typecheck.size() > 0) { + typecheck = + SQArray::Create(_ss(v), nc->_typecheck.size()); + for(SQUnsignedInteger n = 0; n_typecheck.size(); n++) { + _array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]); + } + } + res->NewSlot(SQString::Create(_ss(v),_SC("typecheck"),-1),typecheck); + } + v->Push(res); + return 1; +} + + + +const SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={ + {_SC("call"),closure_call,-1, _SC("c")}, + {_SC("pcall"),closure_pcall,-1, _SC("c")}, + {_SC("acall"),closure_acall,2, _SC("ca")}, + {_SC("pacall"),closure_pacall,2, _SC("ca")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("bindenv"),closure_bindenv,2, _SC("c x|y|t")}, + {_SC("getinfos"),closure_getinfos,1, _SC("c")}, + {_SC("getroot"),closure_getroot,1, _SC("c")}, + {_SC("setroot"),closure_setroot,2, _SC("ct")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//GENERATOR DEFAULT DELEGATE +static SQInteger generator_getstatus(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + switch(_generator(o)->_state){ + case SQGenerator::eSuspended:v->Push(SQString::Create(_ss(v),_SC("suspended")));break; + case SQGenerator::eRunning:v->Push(SQString::Create(_ss(v),_SC("running")));break; + case SQGenerator::eDead:v->Push(SQString::Create(_ss(v),_SC("dead")));break; + } + return 1; +} + +const SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={ + {_SC("getstatus"),generator_getstatus,1, _SC("g")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//THREAD DEFAULT DELEGATE +static SQInteger thread_call(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQInteger nparams = sq_gettop(v); + _thread(o)->Push(_thread(o)->_roottable); + for(SQInteger i = 2; i<(nparams+1); i++) + sq_move(_thread(o),v,i); + if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQTrue))) { + sq_move(v,_thread(o),-1); + sq_pop(_thread(o),1); + return 1; + } + v->_lasterror = _thread(o)->_lasterror; + return SQ_ERROR; + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +static SQInteger thread_wakeup(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQVM *thread = _thread(o); + SQInteger state = sq_getvmstate(thread); + if(state != SQ_VMSTATE_SUSPENDED) { + switch(state) { + case SQ_VMSTATE_IDLE: + return sq_throwerror(v,_SC("cannot wakeup a idle thread")); + break; + case SQ_VMSTATE_RUNNING: + return sq_throwerror(v,_SC("cannot wakeup a running thread")); + break; + } + } + + SQInteger wakeupret = sq_gettop(v)>1?SQTrue:SQFalse; + if(wakeupret) { + sq_move(thread,v,2); + } + if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,SQTrue,SQTrue,SQFalse))) { + sq_move(v,thread,-1); + sq_pop(thread,1); //pop retval + if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { + sq_settop(thread,1); //pop roottable + } + return 1; + } + sq_settop(thread,1); + v->_lasterror = thread->_lasterror; + return SQ_ERROR; + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +static SQInteger thread_wakeupthrow(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQVM *thread = _thread(o); + SQInteger state = sq_getvmstate(thread); + if(state != SQ_VMSTATE_SUSPENDED) { + switch(state) { + case SQ_VMSTATE_IDLE: + return sq_throwerror(v,_SC("cannot wakeup a idle thread")); + break; + case SQ_VMSTATE_RUNNING: + return sq_throwerror(v,_SC("cannot wakeup a running thread")); + break; + } + } + + sq_move(thread,v,2); + sq_throwobject(thread); + SQBool rethrow_error = SQTrue; + if(sq_gettop(v) > 2) { + sq_getbool(v,3,&rethrow_error); + } + if(SQ_SUCCEEDED(sq_wakeupvm(thread,SQFalse,SQTrue,SQTrue,SQTrue))) { + sq_move(v,thread,-1); + sq_pop(thread,1); //pop retval + if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { + sq_settop(thread,1); //pop roottable + } + return 1; + } + sq_settop(thread,1); + if(rethrow_error) { + v->_lasterror = thread->_lasterror; + return SQ_ERROR; + } + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +static SQInteger thread_getstatus(HSQUIRRELVM v) +{ + SQObjectPtr &o = stack_get(v,1); + switch(sq_getvmstate(_thread(o))) { + case SQ_VMSTATE_IDLE: + sq_pushstring(v,_SC("idle"),-1); + break; + case SQ_VMSTATE_RUNNING: + sq_pushstring(v,_SC("running"),-1); + break; + case SQ_VMSTATE_SUSPENDED: + sq_pushstring(v,_SC("suspended"),-1); + break; + default: + return sq_throwerror(v,_SC("internal VM error")); + } + return 1; +} + +static SQInteger thread_getstackinfos(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQVM *thread = _thread(o); + SQInteger threadtop = sq_gettop(thread); + SQInteger level; + sq_getinteger(v,-1,&level); + SQRESULT res = __getcallstackinfos(thread,level); + if(SQ_FAILED(res)) + { + sq_settop(thread,threadtop); + if(sq_type(thread->_lasterror) == OT_STRING) { + sq_throwerror(v,_stringval(thread->_lasterror)); + } + else { + sq_throwerror(v,_SC("unknown error")); + } + } + if(res > 0) { + //some result + sq_move(v,thread,-1); + sq_settop(thread,threadtop); + return 1; + } + //no result + sq_settop(thread,threadtop); + return 0; + + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +const SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = { + {_SC("call"), thread_call, -1, _SC("v")}, + {_SC("wakeup"), thread_wakeup, -1, _SC("v")}, + {_SC("wakeupthrow"), thread_wakeupthrow, -2, _SC("v.b")}, + {_SC("getstatus"), thread_getstatus, 1, _SC("v")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("getstackinfos"),thread_getstackinfos,2, _SC("vn")}, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +static SQInteger class_getattributes(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_getattributes(v,-2))?1:SQ_ERROR; +} + +static SQInteger class_setattributes(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_setattributes(v,-3))?1:SQ_ERROR; +} + +static SQInteger class_instance(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_createinstance(v,-1))?1:SQ_ERROR; +} + +static SQInteger class_getbase(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_getbase(v,-1))?1:SQ_ERROR; +} + +static SQInteger class_newmember(HSQUIRRELVM v) +{ + SQInteger top = sq_gettop(v); + SQBool bstatic = SQFalse; + if(top == 5) + { + sq_tobool(v,-1,&bstatic); + sq_pop(v,1); + } + + if(top < 4) { + sq_pushnull(v); + } + return SQ_SUCCEEDED(sq_newmember(v,-4,bstatic))?1:SQ_ERROR; +} + +static SQInteger class_rawnewmember(HSQUIRRELVM v) +{ + SQInteger top = sq_gettop(v); + SQBool bstatic = SQFalse; + if(top == 5) + { + sq_tobool(v,-1,&bstatic); + sq_pop(v,1); + } + + if(top < 4) { + sq_pushnull(v); + } + return SQ_SUCCEEDED(sq_rawnewmember(v,-4,bstatic))?1:SQ_ERROR; +} + +const SQRegFunction SQSharedState::_class_default_delegate_funcz[] = { + {_SC("getattributes"), class_getattributes, 2, _SC("y.")}, + {_SC("setattributes"), class_setattributes, 3, _SC("y..")}, + {_SC("rawget"),container_rawget,2, _SC("y")}, + {_SC("rawset"),container_rawset,3, _SC("y")}, + {_SC("rawin"),container_rawexists,2, _SC("y")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("instance"),class_instance,1, _SC("y")}, + {_SC("getbase"),class_getbase,1, _SC("y")}, + {_SC("newmember"),class_newmember,-3, _SC("y")}, + {_SC("rawnewmember"),class_rawnewmember,-3, _SC("y")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + + +static SQInteger instance_getclass(HSQUIRRELVM v) +{ + if(SQ_SUCCEEDED(sq_getclass(v,1))) + return 1; + return SQ_ERROR; +} + +const SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = { + {_SC("getclass"), instance_getclass, 1, _SC("x")}, + {_SC("rawget"),container_rawget,2, _SC("x")}, + {_SC("rawset"),container_rawset,3, _SC("x")}, + {_SC("rawin"),container_rawexists,2, _SC("x")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +static SQInteger weakref_ref(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_getweakrefval(v,1))) + return SQ_ERROR; + return 1; +} + +const SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = { + {_SC("ref"),weakref_ref,1, _SC("r")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; diff --git a/mp/src/vscript/squirrel/squirrel/sqclass.cpp b/mp/src/vscript/squirrel/squirrel/sqclass.cpp new file mode 100644 index 00000000..fc619616 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqclass.cpp @@ -0,0 +1,210 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqtable.h" +#include "sqclass.h" +#include "sqfuncproto.h" +#include "sqclosure.h" + + + +SQClass::SQClass(SQSharedState *ss,SQClass *base) +{ + _base = base; + _typetag = 0; + _hook = NULL; + _udsize = 0; + _locked = false; + _constructoridx = -1; + if(_base) { + _constructoridx = _base->_constructoridx; + _udsize = _base->_udsize; + _defaultvalues.copy(base->_defaultvalues); + _methods.copy(base->_methods); + _COPY_VECTOR(_metamethods,base->_metamethods,MT_LAST); + __ObjAddRef(_base); + } + _members = base?base->_members->Clone() : SQTable::Create(ss,0); + __ObjAddRef(_members); + + INIT_CHAIN(); + ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); +} + +void SQClass::Finalize() { + _attributes.Null(); + _NULL_SQOBJECT_VECTOR(_defaultvalues,_defaultvalues.size()); + _methods.resize(0); + _NULL_SQOBJECT_VECTOR(_metamethods,MT_LAST); + __ObjRelease(_members); + if(_base) { + __ObjRelease(_base); + } +} + +SQClass::~SQClass() +{ + REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); + Finalize(); +} + +bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) +{ + SQObjectPtr temp; + bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic; + if(_locked && !belongs_to_static_table) + return false; //the class already has an instance so cannot be modified + if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value + { + _defaultvalues[_member_idx(temp)].val = val; + return true; + } + if(belongs_to_static_table) { + SQInteger mmidx; + if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) && + (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) { + _metamethods[mmidx] = val; + } + else { + SQObjectPtr theval = val; + if(_base && sq_type(val) == OT_CLOSURE) { + theval = _closure(val)->Clone(); + _closure(theval)->_base = _base; + __ObjAddRef(_base); //ref for the closure + } + if(sq_type(temp) == OT_NULL) { + bool isconstructor; + SQVM::IsEqual(ss->_constructoridx, key, isconstructor); + if(isconstructor) { + _constructoridx = (SQInteger)_methods.size(); + } + SQClassMember m; + m.val = theval; + _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size()))); + _methods.push_back(m); + } + else { + _methods[_member_idx(temp)].val = theval; + } + } + return true; + } + SQClassMember m; + m.val = val; + _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size()))); + _defaultvalues.push_back(m); + return true; +} + +SQInstance *SQClass::CreateInstance() +{ + if(!_locked) Lock(); + return SQInstance::Create(_opt_ss(this),this); +} + +SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) +{ + SQObjectPtr oval; + SQInteger idx = _members->Next(false,refpos,outkey,oval); + if(idx != -1) { + if(_ismethod(oval)) { + outval = _methods[_member_idx(oval)].val; + } + else { + SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val; + outval = _realval(o); + } + } + return idx; +} + +bool SQClass::SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val) +{ + SQObjectPtr idx; + if(_members->Get(key,idx)) { + if(_isfield(idx)) + _defaultvalues[_member_idx(idx)].attrs = val; + else + _methods[_member_idx(idx)].attrs = val; + return true; + } + return false; +} + +bool SQClass::GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval) +{ + SQObjectPtr idx; + if(_members->Get(key,idx)) { + outval = (_isfield(idx)?_defaultvalues[_member_idx(idx)].attrs:_methods[_member_idx(idx)].attrs); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////// +void SQInstance::Init(SQSharedState *ss) +{ + _userpointer = NULL; + _hook = NULL; + __ObjAddRef(_class); + _delegate = _class->_members; + INIT_CHAIN(); + ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); +} + +SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize) +{ + _memsize = memsize; + _class = c; + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger n = 0; n < nvalues; n++) { + new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val); + } + Init(ss); +} + +SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize) +{ + _memsize = memsize; + _class = i->_class; + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger n = 0; n < nvalues; n++) { + new (&_values[n]) SQObjectPtr(i->_values[n]); + } + Init(ss); +} + +void SQInstance::Finalize() +{ + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + __ObjRelease(_class); + _NULL_SQOBJECT_VECTOR(_values,nvalues); +} + +SQInstance::~SQInstance() +{ + REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); + if(_class){ Finalize(); } //if _class is null it was already finalized by the GC +} + +bool SQInstance::GetMetaMethod(SQVM* SQ_UNUSED_ARG(v),SQMetaMethod mm,SQObjectPtr &res) +{ + if(sq_type(_class->_metamethods[mm]) != OT_NULL) { + res = _class->_metamethods[mm]; + return true; + } + return false; +} + +bool SQInstance::InstanceOf(SQClass *trg) +{ + SQClass *parent = _class; + while(parent != NULL) { + if(parent == trg) + return true; + parent = parent->_base; + } + return false; +} diff --git a/mp/src/vscript/squirrel/squirrel/sqclass.h b/mp/src/vscript/squirrel/squirrel/sqclass.h new file mode 100644 index 00000000..7d402172 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqclass.h @@ -0,0 +1,162 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQCLASS_H_ +#define _SQCLASS_H_ + +struct SQInstance; + +struct SQClassMember { + SQObjectPtr val; + SQObjectPtr attrs; + void Null() { + val.Null(); + attrs.Null(); + } +}; + +typedef sqvector SQClassMemberVec; + +#define MEMBER_TYPE_METHOD 0x01000000 +#define MEMBER_TYPE_FIELD 0x02000000 + +#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD) +#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD) +#define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i)) +#define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i)) +#define _member_type(o) (_integer(o)&0xFF000000) +#define _member_idx(o) (_integer(o)&0x00FFFFFF) + +struct SQClass : public CHAINABLE_OBJ +{ + SQClass(SQSharedState *ss,SQClass *base); +public: + static SQClass* Create(SQSharedState *ss,SQClass *base) { + SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass)); + new (newclass) SQClass(ss, base); + return newclass; + } + ~SQClass(); + bool NewSlot(SQSharedState *ss, const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic); + bool Get(const SQObjectPtr &key,SQObjectPtr &val) { + if(_members->Get(key,val)) { + if(_isfield(val)) { + SQObjectPtr &o = _defaultvalues[_member_idx(val)].val; + val = _realval(o); + } + else { + val = _methods[_member_idx(val)].val; + } + return true; + } + return false; + } + bool GetConstructor(SQObjectPtr &ctor) + { + if(_constructoridx != -1) { + ctor = _methods[_constructoridx].val; + return true; + } + return false; + } + bool SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val); + bool GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval); + void Lock() { _locked = true; if(_base) _base->Lock(); } + void Release() { + if (_hook) { _hook(_typetag,0);} + sq_delete(this, SQClass); + } + void Finalize(); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable ** ); + SQObjectType GetType() {return OT_CLASS;} +#endif + SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); + SQInstance *CreateInstance(); + SQTable *_members; + SQClass *_base; + SQClassMemberVec _defaultvalues; + SQClassMemberVec _methods; + SQObjectPtr _metamethods[MT_LAST]; + SQObjectPtr _attributes; + SQUserPointer _typetag; + SQRELEASEHOOK _hook; + bool _locked; + SQInteger _constructoridx; + SQInteger _udsize; +}; + +#define calcinstancesize(_theclass_) \ + (_theclass_->_udsize + sq_aligning(sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))) + +struct SQInstance : public SQDelegable +{ + void Init(SQSharedState *ss); + SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize); + SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize); +public: + static SQInstance* Create(SQSharedState *ss,SQClass *theclass) { + + SQInteger size = calcinstancesize(theclass); + SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); + new (newinst) SQInstance(ss, theclass,size); + if(theclass->_udsize) { + newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize); + } + return newinst; + } + SQInstance *Clone(SQSharedState *ss) + { + SQInteger size = calcinstancesize(_class); + SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); + new (newinst) SQInstance(ss, this,size); + if(_class->_udsize) { + newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize); + } + return newinst; + } + ~SQInstance(); + bool Get(const SQObjectPtr &key,SQObjectPtr &val) { + if(_class->_members->Get(key,val)) { + if(_isfield(val)) { + SQObjectPtr &o = _values[_member_idx(val)]; + val = _realval(o); + } + else { + val = _class->_methods[_member_idx(val)].val; + } + return true; + } + return false; + } + bool Set(const SQObjectPtr &key,const SQObjectPtr &val) { + SQObjectPtr idx; + if(_class->_members->Get(key,idx) && _isfield(idx)) { + _values[_member_idx(idx)] = val; + return true; + } + return false; + } + void Release() { + _uiRef++; + if (_hook) { _hook(_userpointer,0);} + _uiRef--; + if(_uiRef > 0) return; + SQInteger size = _memsize; + this->~SQInstance(); + SQ_FREE(this, size); + } + void Finalize(); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable ** ); + SQObjectType GetType() {return OT_INSTANCE;} +#endif + bool InstanceOf(SQClass *trg); + bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); + + SQClass *_class; + SQUserPointer _userpointer; + SQRELEASEHOOK _hook; + SQInteger _memsize; + SQObjectPtr _values[1]; +}; + +#endif //_SQCLASS_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqclosure.h b/mp/src/vscript/squirrel/squirrel/sqclosure.h new file mode 100644 index 00000000..66495b94 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqclosure.h @@ -0,0 +1,201 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQCLOSURE_H_ +#define _SQCLOSURE_H_ + + +#define _CALC_CLOSURE_SIZE(func) (sizeof(SQClosure) + (func->_noutervalues*sizeof(SQObjectPtr)) + (func->_ndefaultparams*sizeof(SQObjectPtr))) + +struct SQFunctionProto; +struct SQClass; +struct SQClosure : public CHAINABLE_OBJ +{ +private: + SQClosure(SQSharedState *ss,SQFunctionProto *func){_function = func; __ObjAddRef(_function); _base = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; _root=NULL;} +public: + static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func,SQWeakRef *root){ + SQInteger size = _CALC_CLOSURE_SIZE(func); + SQClosure *nc=(SQClosure*)SQ_MALLOC(size); + new (nc) SQClosure(ss,func); + nc->_outervalues = (SQObjectPtr *)(nc + 1); + nc->_defaultparams = &nc->_outervalues[func->_noutervalues]; + nc->_root = root; + __ObjAddRef(nc->_root); + _CONSTRUCT_VECTOR(SQObjectPtr,func->_noutervalues,nc->_outervalues); + _CONSTRUCT_VECTOR(SQObjectPtr,func->_ndefaultparams,nc->_defaultparams); + return nc; + } + void Release(){ + SQFunctionProto *f = _function; + SQInteger size = _CALC_CLOSURE_SIZE(f); + _DESTRUCT_VECTOR(SQObjectPtr,f->_noutervalues,_outervalues); + _DESTRUCT_VECTOR(SQObjectPtr,f->_ndefaultparams,_defaultparams); + __ObjRelease(_function); + this->~SQClosure(); + sq_vm_free(this,size); + } + void SetRoot(SQWeakRef *r) + { + __ObjRelease(_root); + _root = r; + __ObjAddRef(_root); + } + SQClosure *Clone() + { + SQFunctionProto *f = _function; + SQClosure * ret = SQClosure::Create(_opt_ss(this),f,_root); + ret->_env = _env; + if(ret->_env) __ObjAddRef(ret->_env); + _COPY_VECTOR(ret->_outervalues,_outervalues,f->_noutervalues); + _COPY_VECTOR(ret->_defaultparams,_defaultparams,f->_ndefaultparams); + return ret; + } + ~SQClosure(); + + bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); + static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){ + SQFunctionProto *f = _function; + _NULL_SQOBJECT_VECTOR(_outervalues,f->_noutervalues); + _NULL_SQOBJECT_VECTOR(_defaultparams,f->_ndefaultparams); + } + SQObjectType GetType() {return OT_CLOSURE;} +#endif + SQWeakRef *_env; + SQWeakRef *_root; + SQClass *_base; + SQFunctionProto *_function; + SQObjectPtr *_outervalues; + SQObjectPtr *_defaultparams; +}; + +////////////////////////////////////////////// +struct SQOuter : public CHAINABLE_OBJ +{ + +private: + SQOuter(SQSharedState *ss, SQObjectPtr *outer){_valptr = outer; _next = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } + +public: + static SQOuter *Create(SQSharedState *ss, SQObjectPtr *outer) + { + SQOuter *nc = (SQOuter*)SQ_MALLOC(sizeof(SQOuter)); + new (nc) SQOuter(ss, outer); + return nc; + } + ~SQOuter() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } + + void Release() + { + this->~SQOuter(); + sq_vm_free(this,sizeof(SQOuter)); + } + +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize() { _value.Null(); } + SQObjectType GetType() {return OT_OUTER;} +#endif + + SQObjectPtr *_valptr; /* pointer to value on stack, or _value below */ + SQInteger _idx; /* idx in stack array, for relocation */ + SQObjectPtr _value; /* value of outer after stack frame is closed */ + SQOuter *_next; /* pointer to next outer when frame is open */ +}; + +////////////////////////////////////////////// +struct SQGenerator : public CHAINABLE_OBJ +{ + enum SQGeneratorState{eRunning,eSuspended,eDead}; +private: + SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} +public: + static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){ + SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator)); + new (nc) SQGenerator(ss,closure); + return nc; + } + ~SQGenerator() + { + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); + } + void Kill(){ + _state=eDead; + _stack.resize(0); + _closure.Null();} + void Release(){ + sq_delete(this,SQGenerator); + } + + bool Yield(SQVM *v,SQInteger target); + bool Resume(SQVM *v,SQObjectPtr &dest); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){_stack.resize(0);_closure.Null();} + SQObjectType GetType() {return OT_GENERATOR;} +#endif + SQObjectPtr _closure; + SQObjectPtrVec _stack; + SQVM::CallInfo _ci; + ExceptionsTraps _etraps; + SQGeneratorState _state; +}; + +#define _CALC_NATVIVECLOSURE_SIZE(noutervalues) (sizeof(SQNativeClosure) + (noutervalues*sizeof(SQObjectPtr))) + +struct SQNativeClosure : public CHAINABLE_OBJ +{ +private: + SQNativeClosure(SQSharedState *ss,SQFUNCTION func){_function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL;} +public: + static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func,SQInteger nouters) + { + SQInteger size = _CALC_NATVIVECLOSURE_SIZE(nouters); + SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(size); + new (nc) SQNativeClosure(ss,func); + nc->_outervalues = (SQObjectPtr *)(nc + 1); + nc->_noutervalues = nouters; + _CONSTRUCT_VECTOR(SQObjectPtr,nc->_noutervalues,nc->_outervalues); + return nc; + } + SQNativeClosure *Clone() + { + SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function,_noutervalues); + ret->_env = _env; + if(ret->_env) __ObjAddRef(ret->_env); + ret->_name = _name; + _COPY_VECTOR(ret->_outervalues,_outervalues,_noutervalues); + ret->_typecheck.copy(_typecheck); + ret->_nparamscheck = _nparamscheck; + return ret; + } + ~SQNativeClosure() + { + __ObjRelease(_env); + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); + } + void Release(){ + SQInteger size = _CALC_NATVIVECLOSURE_SIZE(_noutervalues); + _DESTRUCT_VECTOR(SQObjectPtr,_noutervalues,_outervalues); + this->~SQNativeClosure(); + sq_free(this,size); + } + +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize() { _NULL_SQOBJECT_VECTOR(_outervalues,_noutervalues); } + SQObjectType GetType() {return OT_NATIVECLOSURE;} +#endif + SQInteger _nparamscheck; + SQIntVec _typecheck; + SQObjectPtr *_outervalues; + SQUnsignedInteger _noutervalues; + SQWeakRef *_env; + SQFUNCTION _function; + SQObjectPtr _name; +}; + + + +#endif //_SQCLOSURE_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqcompiler.cpp b/mp/src/vscript/squirrel/squirrel/sqcompiler.cpp new file mode 100644 index 00000000..095edd71 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqcompiler.cpp @@ -0,0 +1,1611 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#ifndef NO_COMPILER +#include +#include +#include "sqopcodes.h" +#include "sqstring.h" +#include "sqfuncproto.h" +#include "sqcompiler.h" +#include "sqfuncstate.h" +#include "sqlexer.h" +#include "sqvm.h" +#include "sqtable.h" + +#define EXPR 1 +#define OBJECT 2 +#define BASE 3 +#define LOCAL 4 +#define OUTER 5 + +struct SQExpState { + SQInteger etype; /* expr. type; one of EXPR, OBJECT, BASE, OUTER or LOCAL */ + SQInteger epos; /* expr. location on stack; -1 for OBJECT and BASE */ + bool donot_get; /* signal not to deref the next value */ +}; + +#define MAX_COMPILER_ERROR_LEN 256 + +struct SQScope { + SQInteger outers; + SQInteger stacksize; +}; + +#define BEGIN_SCOPE() SQScope __oldscope__ = _scope; \ + _scope.outers = _fs->_outers; \ + _scope.stacksize = _fs->GetStackSize(); + +#define RESOLVE_OUTERS() if(_fs->GetStackSize() != _scope.stacksize) { \ + if(_fs->CountOuters(_scope.stacksize)) { \ + _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ + } \ + } + +#define END_SCOPE_NO_CLOSE() { if(_fs->GetStackSize() != _scope.stacksize) { \ + _fs->SetStackSize(_scope.stacksize); \ + } \ + _scope = __oldscope__; \ + } + +#define END_SCOPE() { SQInteger oldouters = _fs->_outers;\ + if(_fs->GetStackSize() != _scope.stacksize) { \ + _fs->SetStackSize(_scope.stacksize); \ + if(oldouters != _fs->_outers) { \ + _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ + } \ + } \ + _scope = __oldscope__; \ + } + +#define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \ + SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \ + _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0); + +#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \ + __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \ + if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \ + if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \ + _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();} + +class SQCompiler +{ +public: + SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo) + { + _vm=v; + _lex.Init(_ss(v), rg, up,ThrowError,this); + _sourcename = SQString::Create(_ss(v), sourcename); + _lineinfo = lineinfo;_raiseerror = raiseerror; + _scope.outers = 0; + _scope.stacksize = 0; + _compilererror[0] = _SC('\0'); + } + static void ThrowError(void *ud, const SQChar *s) { + SQCompiler *c = (SQCompiler *)ud; + c->Error(s); + } + void Error(const SQChar *s, ...) + { + va_list vl; + va_start(vl, s); + scvsprintf(_compilererror, MAX_COMPILER_ERROR_LEN, s, vl); + va_end(vl); + longjmp(_errorjmp,1); + } + void Lex(){ _token = _lex.Lex();} + SQObject Expect(SQInteger tok) + { + + if(_token != tok) { + if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) { + //do nothing + } + else { + const SQChar *etypename; + if(tok > 255) { + switch(tok) + { + case TK_IDENTIFIER: + etypename = _SC("IDENTIFIER"); + break; + case TK_STRING_LITERAL: + etypename = _SC("STRING_LITERAL"); + break; + case TK_INTEGER: + etypename = _SC("INTEGER"); + break; + case TK_FLOAT: + etypename = _SC("FLOAT"); + break; + default: + etypename = _lex.Tok2Str(tok); + } + Error(_SC("expected '%s'"), etypename); + } + Error(_SC("expected '%c'"), tok); + } + } + SQObjectPtr ret; + switch(tok) + { + case TK_IDENTIFIER: + ret = _fs->CreateString(_lex._svalue); + break; + case TK_STRING_LITERAL: + ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); + break; + case TK_INTEGER: + ret = SQObjectPtr(_lex._nvalue); + break; + case TK_FLOAT: + ret = SQObjectPtr(_lex._fvalue); + break; + } + Lex(); + return ret; + } + bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); } + void OptionalSemicolon() + { + if(_token == _SC(';')) { Lex(); return; } + if(!IsEndOfStatement()) { + Error(_SC("end of statement expected (; or lf)")); + } + } + void MoveIfCurrentTargetIsLocal() { + SQInteger trg = _fs->TopTarget(); + if(_fs->IsLocal(trg)) { + trg = _fs->PopTarget(); //pops the target and moves it + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); + } + } + bool Compile(SQObjectPtr &o) + { + _debugline = 1; + _debugop = 0; + + SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this); + funcstate._name = SQString::Create(_ss(_vm), _SC("main")); + _fs = &funcstate; + _fs->AddParameter(_fs->CreateString(_SC("this"))); + _fs->AddParameter(_fs->CreateString(_SC("vargv"))); + _fs->_varparams = true; + _fs->_sourcename = _sourcename; + SQInteger stacksize = _fs->GetStackSize(); + if(setjmp(_errorjmp) == 0) { + Lex(); + while(_token > 0){ + Statement(); + if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + } + _fs->SetStackSize(stacksize); + _fs->AddLineInfos(_lex._currentline, _lineinfo, true); + _fs->AddInstruction(_OP_RETURN, 0xFF); + _fs->SetStackSize(0); + o =_fs->BuildProto(); +#ifdef _DEBUG_DUMP + _fs->Dump(_funcproto(o)); +#endif + } + else { + if(_raiseerror && _ss(_vm)->_compilererrorhandler) { + _ss(_vm)->_compilererrorhandler(_vm, _compilererror, sq_type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), + _lex._currentline, _lex._currentcolumn); + } + _vm->_lasterror = SQString::Create(_ss(_vm), _compilererror, -1); + return false; + } + return true; + } + void Statements() + { + while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) { + Statement(); + if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + } + } + void Statement(bool closeframe = true) + { + _fs->AddLineInfos(_lex._currentline, _lineinfo); + switch(_token){ + case _SC(';'): Lex(); break; + case TK_IF: IfStatement(); break; + case TK_WHILE: WhileStatement(); break; + case TK_DO: DoWhileStatement(); break; + case TK_FOR: ForStatement(); break; + case TK_FOREACH: ForEachStatement(); break; + case TK_SWITCH: SwitchStatement(); break; + case TK_LOCAL: LocalDeclStatement(); break; + case TK_RETURN: + case TK_YIELD: { + SQOpcode op; + if(_token == TK_RETURN) { + op = _OP_RETURN; + } + else { + op = _OP_YIELD; + _fs->_bgenerator = true; + } + Lex(); + if(!IsEndOfStatement()) { + SQInteger retexp = _fs->GetCurrentPos()+1; + CommaExpr(); + if(op == _OP_RETURN && _fs->_traps > 0) + _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); + _fs->_returnexp = retexp; + _fs->AddInstruction(op, 1, _fs->PopTarget(),_fs->GetStackSize()); + } + else{ + if(op == _OP_RETURN && _fs->_traps > 0) + _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0); + _fs->_returnexp = -1; + _fs->AddInstruction(op, 0xFF,0,_fs->GetStackSize()); + } + break;} + case TK_BREAK: + if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block")); + if(_fs->_breaktargets.top() > 0){ + _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); + } + RESOLVE_OUTERS(); + _fs->AddInstruction(_OP_JMP, 0, -1234); + _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); + Lex(); + break; + case TK_CONTINUE: + if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block")); + if(_fs->_continuetargets.top() > 0) { + _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); + } + RESOLVE_OUTERS(); + _fs->AddInstruction(_OP_JMP, 0, -1234); + _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); + Lex(); + break; + case TK_FUNCTION: + FunctionStatement(); + break; + case TK_CLASS: + ClassStatement(); + break; + case TK_ENUM: + EnumStatement(); + break; + case _SC('{'):{ + BEGIN_SCOPE(); + Lex(); + Statements(); + Expect(_SC('}')); + if(closeframe) { + END_SCOPE(); + } + else { + END_SCOPE_NO_CLOSE(); + } + } + break; + case TK_TRY: + TryCatchStatement(); + break; + case TK_THROW: + Lex(); + CommaExpr(); + _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); + break; + case TK_CONST: + { + Lex(); + SQObject id = Expect(TK_IDENTIFIER); + Expect('='); + SQObject val = ExpectScalar(); + OptionalSemicolon(); + SQTable *enums = _table(_ss(_vm)->_consts); + SQObjectPtr strongid = id; + enums->NewSlot(strongid,SQObjectPtr(val)); + strongid.Null(); + } + break; + default: + CommaExpr(); + _fs->DiscardTarget(); + //_fs->PopTarget(); + break; + } + _fs->SnoozeOpt(); + } + void EmitDerefOp(SQOpcode op) + { + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(op,_fs->PushTarget(),src,key,val); + } + void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0) + { + SQInteger p2 = _fs->PopTarget(); //src in OP_GET + SQInteger p1 = _fs->PopTarget(); //key in OP_GET + _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3); + } + void EmitCompoundArith(SQInteger tok, SQInteger etype, SQInteger pos) + { + /* Generate code depending on the expression type */ + switch(etype) { + case LOCAL:{ + SQInteger p2 = _fs->PopTarget(); //src in OP_GET + SQInteger p1 = _fs->PopTarget(); //key in OP_GET + _fs->PushTarget(p1); + //EmitCompArithLocal(tok, p1, p1, p2); + _fs->AddInstruction(ChooseArithOpByToken(tok),p1, p2, p1, 0); + _fs->SnoozeOpt(); + } + break; + case OBJECT: + case BASE: + { + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger src = _fs->PopTarget(); + /* _OP_COMPARITH mixes dest obj and source val in the arg1 */ + _fs->AddInstruction(_OP_COMPARITH, _fs->PushTarget(), (src<<16)|val, key, ChooseCompArithCharByToken(tok)); + } + break; + case OUTER: + { + SQInteger val = _fs->TopTarget(); + SQInteger tmp = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, tmp, pos); + _fs->AddInstruction(ChooseArithOpByToken(tok), tmp, val, tmp, 0); + _fs->PopTarget(); + _fs->PopTarget(); + _fs->AddInstruction(_OP_SETOUTER, _fs->PushTarget(), pos, tmp); + } + break; + } + } + void CommaExpr() + { + for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr()); + } + void Expression() + { + SQExpState es = _es; + _es.etype = EXPR; + _es.epos = -1; + _es.donot_get = false; + LogicalOrExp(); + switch(_token) { + case _SC('='): + case TK_NEWSLOT: + case TK_MINUSEQ: + case TK_PLUSEQ: + case TK_MULEQ: + case TK_DIVEQ: + case TK_MODEQ:{ + SQInteger op = _token; + SQInteger ds = _es.etype; + SQInteger pos = _es.epos; + if(ds == EXPR) Error(_SC("can't assign expression")); + else if(ds == BASE) Error(_SC("'base' cannot be modified")); + Lex(); Expression(); + + switch(op){ + case TK_NEWSLOT: + if(ds == OBJECT || ds == BASE) + EmitDerefOp(_OP_NEWSLOT); + else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local + Error(_SC("can't 'create' a local slot")); + break; + case _SC('='): //ASSIGN + switch(ds) { + case LOCAL: + { + SQInteger src = _fs->PopTarget(); + SQInteger dst = _fs->TopTarget(); + _fs->AddInstruction(_OP_MOVE, dst, src); + } + break; + case OBJECT: + case BASE: + EmitDerefOp(_OP_SET); + break; + case OUTER: + { + SQInteger src = _fs->PopTarget(); + SQInteger dst = _fs->PushTarget(); + _fs->AddInstruction(_OP_SETOUTER, dst, pos, src); + } + } + break; + case TK_MINUSEQ: + case TK_PLUSEQ: + case TK_MULEQ: + case TK_DIVEQ: + case TK_MODEQ: + EmitCompoundArith(op, ds, pos); + break; + } + } + break; + case _SC('?'): { + Lex(); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + SQInteger jzpos = _fs->GetCurrentPos(); + SQInteger trg = _fs->PushTarget(); + Expression(); + SQInteger first_exp = _fs->PopTarget(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + SQInteger endfirstexp = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_JMP, 0, 0); + Expect(_SC(':')); + SQInteger jmppos = _fs->GetCurrentPos(); + Expression(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + _fs->SetInstructionParam(jzpos, 1, endfirstexp - jzpos + 1); + _fs->SnoozeOpt(); + } + break; + } + _es = es; + } + template void INVOKE_EXP(T f) + { + SQExpState es = _es; + _es.etype = EXPR; + _es.epos = -1; + _es.donot_get = false; + (this->*f)(); + _es = es; + } + template void BIN_EXP(SQOpcode op, T f,SQInteger op3 = 0) + { + Lex(); + INVOKE_EXP(f); + SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget(); + _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); + _es.etype = EXPR; + } + void LogicalOrExp() + { + LogicalAndExp(); + for(;;) if(_token == TK_OR) { + SQInteger first_exp = _fs->PopTarget(); + SQInteger trg = _fs->PushTarget(); + _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0); + SQInteger jpos = _fs->GetCurrentPos(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + Lex(); INVOKE_EXP(&SQCompiler::LogicalOrExp); + _fs->SnoozeOpt(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SnoozeOpt(); + _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _es.etype = EXPR; + break; + }else return; + } + void LogicalAndExp() + { + BitwiseOrExp(); + for(;;) switch(_token) { + case TK_AND: { + SQInteger first_exp = _fs->PopTarget(); + SQInteger trg = _fs->PushTarget(); + _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0); + SQInteger jpos = _fs->GetCurrentPos(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + Lex(); INVOKE_EXP(&SQCompiler::LogicalAndExp); + _fs->SnoozeOpt(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SnoozeOpt(); + _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _es.etype = EXPR; + break; + } + + default: + return; + } + } + void BitwiseOrExp() + { + BitwiseXorExp(); + for(;;) if(_token == _SC('|')) + {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR); + }else return; + } + void BitwiseXorExp() + { + BitwiseAndExp(); + for(;;) if(_token == _SC('^')) + {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR); + }else return; + } + void BitwiseAndExp() + { + EqExp(); + for(;;) if(_token == _SC('&')) + {BIN_EXP(_OP_BITW, &SQCompiler::EqExp,BW_AND); + }else return; + } + void EqExp() + { + CompExp(); + for(;;) switch(_token) { + case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::CompExp); break; + case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::CompExp); break; + case TK_3WAYSCMP: BIN_EXP(_OP_CMP, &SQCompiler::CompExp,CMP_3W); break; + default: return; + } + } + void CompExp() + { + ShiftExp(); + for(;;) switch(_token) { + case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break; + case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break; + case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break; + case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break; + case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::ShiftExp); break; + case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::ShiftExp); break; + default: return; + } + } + void ShiftExp() + { + PlusExp(); + for(;;) switch(_token) { + case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break; + case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break; + case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break; + default: return; + } + } + SQOpcode ChooseArithOpByToken(SQInteger tok) + { + switch(tok) { + case TK_PLUSEQ: case '+': return _OP_ADD; + case TK_MINUSEQ: case '-': return _OP_SUB; + case TK_MULEQ: case '*': return _OP_MUL; + case TK_DIVEQ: case '/': return _OP_DIV; + case TK_MODEQ: case '%': return _OP_MOD; + default: assert(0); + } + return _OP_ADD; + } + SQInteger ChooseCompArithCharByToken(SQInteger tok) + { + SQInteger oper; + switch(tok){ + case TK_MINUSEQ: oper = '-'; break; + case TK_PLUSEQ: oper = '+'; break; + case TK_MULEQ: oper = '*'; break; + case TK_DIVEQ: oper = '/'; break; + case TK_MODEQ: oper = '%'; break; + default: oper = 0; //shut up compiler + assert(0); break; + }; + return oper; + } + void PlusExp() + { + MultExp(); + for(;;) switch(_token) { + case _SC('+'): case _SC('-'): + BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::MultExp); break; + default: return; + } + } + + void MultExp() + { + PrefixedExpr(); + for(;;) switch(_token) { + case _SC('*'): case _SC('/'): case _SC('%'): + BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::PrefixedExpr); break; + default: return; + } + } + //if 'pos' != -1 the previous variable is a local variable + void PrefixedExpr() + { + SQInteger pos = Factor(); + for(;;) { + switch(_token) { + case _SC('.'): + pos = -1; + Lex(); + + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + if(_es.etype==BASE) { + Emit2ArgsOP(_OP_GET); + pos = _fs->TopTarget(); + _es.etype = EXPR; + _es.epos = pos; + } + else { + if(NeedGet()) { + Emit2ArgsOP(_OP_GET); + } + _es.etype = OBJECT; + } + break; + case _SC('['): + if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot break deref/or comma needed after [exp]=exp slot declaration")); + Lex(); Expression(); Expect(_SC(']')); + pos = -1; + if(_es.etype==BASE) { + Emit2ArgsOP(_OP_GET); + pos = _fs->TopTarget(); + _es.etype = EXPR; + _es.epos = pos; + } + else { + if(NeedGet()) { + Emit2ArgsOP(_OP_GET); + } + _es.etype = OBJECT; + } + break; + case TK_MINUSMINUS: + case TK_PLUSPLUS: + { + if(IsEndOfStatement()) return; + SQInteger diff = (_token==TK_MINUSMINUS) ? -1 : 1; + Lex(); + switch(_es.etype) + { + case EXPR: Error(_SC("can't '++' or '--' an expression")); break; + case OBJECT: + case BASE: + if(_es.donot_get == true) { Error(_SC("can't '++' or '--' an expression")); break; } //mmh dor this make sense? + Emit2ArgsOP(_OP_PINC, diff); + break; + case LOCAL: { + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, diff); + } + break; + case OUTER: { + SQInteger tmp1 = _fs->PushTarget(); + SQInteger tmp2 = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, tmp2, _es.epos); + _fs->AddInstruction(_OP_PINCL, tmp1, tmp2, 0, diff); + _fs->AddInstruction(_OP_SETOUTER, tmp2, _es.epos, tmp2); + _fs->PopTarget(); + } + } + } + return; + break; + case _SC('('): + switch(_es.etype) { + case OBJECT: { + SQInteger key = _fs->PopTarget(); /* location of the key */ + SQInteger table = _fs->PopTarget(); /* location of the object */ + SQInteger closure = _fs->PushTarget(); /* location for the closure */ + SQInteger ttarget = _fs->PushTarget(); /* location for 'this' pointer */ + _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); + } + break; + case BASE: + //Emit2ArgsOP(_OP_GET); + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + break; + case OUTER: + _fs->AddInstruction(_OP_GETOUTER, _fs->PushTarget(), _es.epos); + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + break; + default: + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + } + _es.etype = EXPR; + Lex(); + FunctionCallArgs(); + break; + default: return; + } + } + } + SQInteger Factor() + { + //_es.etype = EXPR; + switch(_token) + { + case TK_STRING_LITERAL: + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); + Lex(); + break; + case TK_BASE: + Lex(); + _fs->AddInstruction(_OP_GETBASE, _fs->PushTarget()); + _es.etype = BASE; + _es.epos = _fs->TopTarget(); + return (_es.epos); + break; + case TK_IDENTIFIER: + case TK_CONSTRUCTOR: + case TK_THIS:{ + SQObject id; + SQObject constant; + + switch(_token) { + case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break; + case TK_THIS: id = _fs->CreateString(_SC("this"),4); break; + case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor"),11); break; + } + + SQInteger pos = -1; + Lex(); + if((pos = _fs->GetLocalVariable(id)) != -1) { + /* Handle a local variable (includes 'this') */ + _fs->PushTarget(pos); + _es.etype = LOCAL; + _es.epos = pos; + } + + else if((pos = _fs->GetOuterVariable(id)) != -1) { + /* Handle a free var */ + if(NeedGet()) { + _es.epos = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, _es.epos, pos); + /* _es.etype = EXPR; already default value */ + } + else { + _es.etype = OUTER; + _es.epos = pos; + } + } + + else if(_fs->IsConstant(id, constant)) { + /* Handle named constant */ + SQObjectPtr constval; + SQObject constid; + if(sq_type(constant) == OT_TABLE) { + Expect('.'); + constid = Expect(TK_IDENTIFIER); + if(!_table(constant)->Get(constid, constval)) { + constval.Null(); + Error(_SC("invalid constant [%s.%s]"), _stringval(id), _stringval(constid)); + } + } + else { + constval = constant; + } + _es.epos = _fs->PushTarget(); + + /* generate direct or literal function depending on size */ + SQObjectType ctype = sq_type(constval); + switch(ctype) { + case OT_INTEGER: EmitLoadConstInt(_integer(constval),_es.epos); break; + case OT_FLOAT: EmitLoadConstFloat(_float(constval),_es.epos); break; + case OT_BOOL: _fs->AddInstruction(_OP_LOADBOOL, _es.epos, _integer(constval)); break; + default: _fs->AddInstruction(_OP_LOAD,_es.epos,_fs->GetConstant(constval)); break; + } + _es.etype = EXPR; + } + else { + /* Handle a non-local variable, aka a field. Push the 'this' pointer on + * the virtual stack (always found in offset 0, so no instruction needs to + * be generated), and push the key next. Generate an _OP_LOAD instruction + * for the latter. If we are not using the variable as a dref expr, generate + * the _OP_GET instruction. + */ + _fs->PushTarget(0); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(NeedGet()) { + Emit2ArgsOP(_OP_GET); + } + _es.etype = OBJECT; + } + return _es.epos; + } + break; + case TK_DOUBLE_COLON: // "::" + _fs->AddInstruction(_OP_LOADROOT, _fs->PushTarget()); + _es.etype = OBJECT; + _token = _SC('.'); /* hack: drop into PrefixExpr, case '.'*/ + _es.epos = -1; + return _es.epos; + break; + case TK_NULL: + _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); + Lex(); + break; + case TK_INTEGER: EmitLoadConstInt(_lex._nvalue,-1); Lex(); break; + case TK_FLOAT: EmitLoadConstFloat(_lex._fvalue,-1); Lex(); break; + case TK_TRUE: case TK_FALSE: + _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0); + Lex(); + break; + case _SC('['): { + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,0,NOT_ARRAY); + SQInteger apos = _fs->GetCurrentPos(),key = 0; + Lex(); + while(_token != _SC(']')) { + Expression(); + if(_token == _SC(',')) Lex(); + SQInteger val = _fs->PopTarget(); + SQInteger array = _fs->TopTarget(); + _fs->AddInstruction(_OP_APPENDARRAY, array, val, AAT_STACK); + key++; + } + _fs->SetInstructionParam(apos, 1, key); + Lex(); + } + break; + case _SC('{'): + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); + Lex();ParseTableOrClass(_SC(','),_SC('}')); + break; + case TK_FUNCTION: FunctionExp(_token);break; + case _SC('@'): FunctionExp(_token,true);break; + case TK_CLASS: Lex(); ClassExp();break; + case _SC('-'): + Lex(); + switch(_token) { + case TK_INTEGER: EmitLoadConstInt(-_lex._nvalue,-1); Lex(); break; + case TK_FLOAT: EmitLoadConstFloat(-_lex._fvalue,-1); Lex(); break; + default: UnaryOP(_OP_NEG); + } + break; + case _SC('!'): Lex(); UnaryOP(_OP_NOT); break; + case _SC('~'): + Lex(); + if(_token == TK_INTEGER) { EmitLoadConstInt(~_lex._nvalue,-1); Lex(); break; } + UnaryOP(_OP_BWNOT); + break; + case TK_TYPEOF : Lex() ;UnaryOP(_OP_TYPEOF); break; + case TK_RESUME : Lex(); UnaryOP(_OP_RESUME); break; + case TK_CLONE : Lex(); UnaryOP(_OP_CLONE); break; + case TK_RAWCALL: Lex(); Expect('('); FunctionCallArgs(true); break; + case TK_MINUSMINUS : + case TK_PLUSPLUS :PrefixIncDec(_token); break; + case TK_DELETE : DeleteExpr(); break; + case _SC('('): Lex(); CommaExpr(); Expect(_SC(')')); + break; + case TK___LINE__: EmitLoadConstInt(_lex._currentline,-1); Lex(); break; + case TK___FILE__: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_sourcename)); Lex(); break; + default: Error(_SC("expression expected")); + } + _es.etype = EXPR; + return -1; + } + void EmitLoadConstInt(SQInteger value,SQInteger target) + { + if(target < 0) { + target = _fs->PushTarget(); + } + if(value <= INT_MAX && value > INT_MIN) { //does it fit in 32 bits? + _fs->AddInstruction(_OP_LOADINT, target,value); + } + else { + _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); + } + } + void EmitLoadConstFloat(SQFloat value,SQInteger target) + { + if(target < 0) { + target = _fs->PushTarget(); + } + if(sizeof(SQFloat) == sizeof(SQInt32)) { + _fs->AddInstruction(_OP_LOADFLOAT, target,*((SQInt32 *)&value)); + } + else { + _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); + } + } + void UnaryOP(SQOpcode op) + { + PrefixedExpr(); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(op, _fs->PushTarget(), src); + } + bool NeedGet() + { + switch(_token) { + case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_MODEQ: case TK_MULEQ: + case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: + return false; + case TK_PLUSPLUS: case TK_MINUSMINUS: + if (!IsEndOfStatement()) { + return false; + } + break; + } + return (!_es.donot_get || ( _es.donot_get && (_token == _SC('.') || _token == _SC('[')))); + } + void FunctionCallArgs(bool rawcall = false) + { + SQInteger nargs = 1;//this + while(_token != _SC(')')) { + Expression(); + MoveIfCurrentTargetIsLocal(); + nargs++; + if(_token == _SC(',')){ + Lex(); + if(_token == ')') Error(_SC("expression expected, found ')'")); + } + } + Lex(); + if (rawcall) { + if (nargs < 3) Error(_SC("rawcall requires at least 2 parameters (callee and this)")); + nargs -= 2; //removes callee and this from count + } + for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget(); + SQInteger stackbase = _fs->PopTarget(); + SQInteger closure = _fs->PopTarget(); + _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); + if (_token == '{') + { + SQInteger retval = _fs->TopTarget(); + SQInteger nkeys = 0; + Lex(); + while (_token != '}') { + switch (_token) { + case _SC('['): + Lex(); CommaExpr(); Expect(_SC(']')); + Expect(_SC('=')); Expression(); + break; + default: + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + Expect(_SC('=')); Expression(); + break; + } + if (_token == ',') Lex(); + nkeys++; + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + _fs->AddInstruction(_OP_SET, 0xFF, retval, key, val); + } + Lex(); + } + } + void ParseTableOrClass(SQInteger separator,SQInteger terminator) + { + SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0; + while(_token != terminator) { + bool hasattrs = false; + bool isstatic = false; + //check if is an attribute + if(separator == ';') { + if(_token == TK_ATTR_OPEN) { + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex(); + ParseTableOrClass(',',TK_ATTR_CLOSE); + hasattrs = true; + } + if(_token == TK_STATIC) { + isstatic = true; + Lex(); + } + } + switch(_token) { + case TK_FUNCTION: + case TK_CONSTRUCTOR:{ + SQInteger tk = _token; + Lex(); + SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor")); + Expect(_SC('(')); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + CreateFunction(id); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + } + break; + case _SC('['): + Lex(); CommaExpr(); Expect(_SC(']')); + Expect(_SC('=')); Expression(); + break; + case TK_STRING_LITERAL: //JSON + if(separator == ',') { //only works for tables + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_STRING_LITERAL))); + Expect(_SC(':')); Expression(); + break; + } + default : + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + Expect(_SC('=')); Expression(); + } + if(_token == separator) Lex();//optional comma/semicolon + nkeys++; + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger attrs = hasattrs ? _fs->PopTarget():-1; + ((void)attrs); + assert((hasattrs && (attrs == key-1)) || !hasattrs); + unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0); + SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOT, 0xFF, table, key, val); + } + else { + _fs->AddInstruction(_OP_NEWSLOTA, flags, table, key, val); //this for classes only as it invokes _newmember + } + } + if(separator == _SC(',')) //hack recognizes a table from the separator + _fs->SetInstructionParam(tpos, 1, nkeys); + Lex(); + } + void LocalDeclStatement() + { + SQObject varname; + Lex(); + if( _token == TK_FUNCTION) { + Lex(); + varname = Expect(TK_IDENTIFIER); + Expect(_SC('(')); + CreateFunction(varname,false); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + _fs->PopTarget(); + _fs->PushLocalVariable(varname); + return; + } + + do { + varname = Expect(TK_IDENTIFIER); + if(_token == _SC('=')) { + Lex(); Expression(); + SQInteger src = _fs->PopTarget(); + SQInteger dest = _fs->PushTarget(); + if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src); + } + else{ + _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); + } + _fs->PopTarget(); + _fs->PushLocalVariable(varname); + if(_token == _SC(',')) Lex(); else break; + } while(1); + } + void IfBlock() + { + if (_token == _SC('{')) + { + BEGIN_SCOPE(); + Lex(); + Statements(); + Expect(_SC('}')); + if (true) { + END_SCOPE(); + } + else { + END_SCOPE_NO_CLOSE(); + } + } + else { + //BEGIN_SCOPE(); + Statement(); + if (_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + //END_SCOPE(); + } + } + void IfStatement() + { + SQInteger jmppos; + bool haselse = false; + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + SQInteger jnepos = _fs->GetCurrentPos(); + + + + IfBlock(); + // + /*static int n = 0; + if (_token != _SC('}') && _token != TK_ELSE) { + printf("IF %d-----------------------!!!!!!!!!\n", n); + if (n == 5) + { + printf("asd"); + } + n++; + //OptionalSemicolon(); + }*/ + + + SQInteger endifblock = _fs->GetCurrentPos(); + if(_token == TK_ELSE){ + haselse = true; + //BEGIN_SCOPE(); + _fs->AddInstruction(_OP_JMP); + jmppos = _fs->GetCurrentPos(); + Lex(); + //Statement(); if(_lex._prevtoken != _SC('}')) OptionalSemicolon(); + IfBlock(); + //END_SCOPE(); + _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + } + _fs->SetInstructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); + } + void WhileStatement() + { + SQInteger jzpos, jmppos; + jmppos = _fs->GetCurrentPos(); + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + + BEGIN_BREAKBLE_BLOCK(); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + jzpos = _fs->GetCurrentPos(); + BEGIN_SCOPE(); + + Statement(); + + END_SCOPE(); + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); + _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + + END_BREAKBLE_BLOCK(jmppos); + } + void DoWhileStatement() + { + Lex(); + SQInteger jmptrg = _fs->GetCurrentPos(); + BEGIN_BREAKBLE_BLOCK() + BEGIN_SCOPE(); + Statement(); + END_SCOPE(); + Expect(TK_WHILE); + SQInteger continuetrg = _fs->GetCurrentPos(); + Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), 1); + _fs->AddInstruction(_OP_JMP, 0, jmptrg - _fs->GetCurrentPos() - 1); + END_BREAKBLE_BLOCK(continuetrg); + } + void ForStatement() + { + Lex(); + BEGIN_SCOPE(); + Expect(_SC('(')); + if(_token == TK_LOCAL) LocalDeclStatement(); + else if(_token != _SC(';')){ + CommaExpr(); + _fs->PopTarget(); + } + Expect(_SC(';')); + _fs->SnoozeOpt(); + SQInteger jmppos = _fs->GetCurrentPos(); + SQInteger jzpos = -1; + if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } + Expect(_SC(';')); + _fs->SnoozeOpt(); + SQInteger expstart = _fs->GetCurrentPos() + 1; + if(_token != _SC(')')) { + CommaExpr(); + _fs->PopTarget(); + } + Expect(_SC(')')); + _fs->SnoozeOpt(); + SQInteger expend = _fs->GetCurrentPos(); + SQInteger expsize = (expend - expstart) + 1; + SQInstructionVec exp; + if(expsize > 0) { + for(SQInteger i = 0; i < expsize; i++) + exp.push_back(_fs->GetInstruction(expstart + i)); + _fs->PopInstructions(expsize); + } + BEGIN_BREAKBLE_BLOCK() + Statement(); + SQInteger continuetrg = _fs->GetCurrentPos(); + if(expsize > 0) { + for(SQInteger i = 0; i < expsize; i++) + _fs->AddInstruction(exp[i]); + } + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); + if(jzpos> 0) _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + + END_BREAKBLE_BLOCK(continuetrg); + + END_SCOPE(); + } + void ForEachStatement() + { + SQObject idxname, valname; + Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER); + if(_token == _SC(',')) { + idxname = valname; + Lex(); valname = Expect(TK_IDENTIFIER); + } + else{ + idxname = _fs->CreateString(_SC("@INDEX@")); + } + Expect(TK_IN); + + //save the stack size + BEGIN_SCOPE(); + //put the table in the stack(evaluate the table expression) + Expression(); Expect(_SC(')')); + SQInteger container = _fs->TopTarget(); + //push the index local var + SQInteger indexpos = _fs->PushLocalVariable(idxname); + _fs->AddInstruction(_OP_LOADNULLS, indexpos,1); + //push the value local var + SQInteger valuepos = _fs->PushLocalVariable(valname); + _fs->AddInstruction(_OP_LOADNULLS, valuepos,1); + //push reference index + SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible + _fs->AddInstruction(_OP_LOADNULLS, itrpos,1); + SQInteger jmppos = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos); + SQInteger foreachpos = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos); + //generate the statement code + BEGIN_BREAKBLE_BLOCK() + Statement(); + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); + _fs->SetInstructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); + _fs->SetInstructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos); + END_BREAKBLE_BLOCK(foreachpos - 1); + //restore the local variable stack(remove index,val and ref idx) + _fs->PopTarget(); + END_SCOPE(); + } + void SwitchStatement() + { + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + Expect(_SC('{')); + SQInteger expr = _fs->TopTarget(); + bool bfirst = true; + SQInteger tonextcondjmp = -1; + SQInteger skipcondjmp = -1; + SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size(); + _fs->_breaktargets.push_back(0); + while(_token == TK_CASE) { + if(!bfirst) { + _fs->AddInstruction(_OP_JMP, 0, 0); + skipcondjmp = _fs->GetCurrentPos(); + _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + } + //condition + Lex(); Expression(); Expect(_SC(':')); + SQInteger trg = _fs->PopTarget(); + SQInteger eqtarget = trg; + bool local = _fs->IsLocal(trg); + if(local) { + eqtarget = _fs->PushTarget(); //we need to allocate a extra reg + } + _fs->AddInstruction(_OP_EQ, eqtarget, trg, expr); + _fs->AddInstruction(_OP_JZ, eqtarget, 0); + if(local) { + _fs->PopTarget(); + } + + //end condition + if(skipcondjmp != -1) { + _fs->SetInstructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); + } + tonextcondjmp = _fs->GetCurrentPos(); + BEGIN_SCOPE(); + Statements(); + END_SCOPE(); + bfirst = false; + } + if(tonextcondjmp != -1) + _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + if(_token == TK_DEFAULT) { + Lex(); Expect(_SC(':')); + BEGIN_SCOPE(); + Statements(); + END_SCOPE(); + } + Expect(_SC('}')); + _fs->PopTarget(); + __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; + if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__); + _fs->_breaktargets.pop_back(); + } + void FunctionStatement() + { + SQObject id; + Lex(); id = Expect(TK_IDENTIFIER); + _fs->PushTarget(0); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); + + while(_token == TK_DOUBLE_COLON) { + Lex(); + id = Expect(TK_IDENTIFIER); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); + } + Expect(_SC('(')); + CreateFunction(id); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + EmitDerefOp(_OP_NEWSLOT); + _fs->PopTarget(); + } + void ClassStatement() + { + SQExpState es; + Lex(); + es = _es; + _es.donot_get = true; + PrefixedExpr(); + if(_es.etype == EXPR) { + Error(_SC("invalid class name")); + } + else if(_es.etype == OBJECT || _es.etype == BASE) { + ClassExp(); + EmitDerefOp(_OP_NEWSLOT); + _fs->PopTarget(); + } + else { + Error(_SC("cannot create a class in a local with the syntax(class )")); + } + _es = es; + } + SQObject ExpectScalar() + { + SQObject val; + val._type = OT_NULL; val._unVal.nInteger = 0; //shut up GCC 4.x + switch(_token) { + case TK_INTEGER: + val._type = OT_INTEGER; + val._unVal.nInteger = _lex._nvalue; + break; + case TK_FLOAT: + val._type = OT_FLOAT; + val._unVal.fFloat = _lex._fvalue; + break; + case TK_STRING_LITERAL: + val = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); + break; + case TK_TRUE: + case TK_FALSE: + val._type = OT_BOOL; + val._unVal.nInteger = _token == TK_TRUE ? 1 : 0; + break; + case '-': + Lex(); + switch(_token) + { + case TK_INTEGER: + val._type = OT_INTEGER; + val._unVal.nInteger = -_lex._nvalue; + break; + case TK_FLOAT: + val._type = OT_FLOAT; + val._unVal.fFloat = -_lex._fvalue; + break; + default: + Error(_SC("scalar expected : integer, float")); + } + break; + default: + Error(_SC("scalar expected : integer, float, or string")); + } + Lex(); + return val; + } + void EnumStatement() + { + Lex(); + SQObject id = Expect(TK_IDENTIFIER); + Expect(_SC('{')); + + SQObject table = _fs->CreateTable(); + SQInteger nval = 0; + while(_token != _SC('}')) { + SQObject key = Expect(TK_IDENTIFIER); + SQObject val; + if(_token == _SC('=')) { + Lex(); + val = ExpectScalar(); + } + else { + val._type = OT_INTEGER; + val._unVal.nInteger = nval++; + } + _table(table)->NewSlot(SQObjectPtr(key),SQObjectPtr(val)); + if(_token == ',') Lex(); + } + SQTable *enums = _table(_ss(_vm)->_consts); + SQObjectPtr strongid = id; + enums->NewSlot(SQObjectPtr(strongid),SQObjectPtr(table)); + strongid.Null(); + Lex(); + } + void TryCatchStatement() + { + SQObject exid; + Lex(); + _fs->AddInstruction(_OP_PUSHTRAP,0,0); + _fs->_traps++; + if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++; + if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++; + SQInteger trappos = _fs->GetCurrentPos(); + { + BEGIN_SCOPE(); + Statement(); + END_SCOPE(); + } + _fs->_traps--; + _fs->AddInstruction(_OP_POPTRAP, 1, 0); + if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--; + if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--; + _fs->AddInstruction(_OP_JMP, 0, 0); + SQInteger jmppos = _fs->GetCurrentPos(); + _fs->SetInstructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); + Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')')); + { + BEGIN_SCOPE(); + SQInteger ex_target = _fs->PushLocalVariable(exid); + _fs->SetInstructionParam(trappos, 0, ex_target); + Statement(); + _fs->SetInstructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); + END_SCOPE(); + } + } + void FunctionExp(SQInteger ftype,bool lambda = false) + { + Lex(); Expect(_SC('(')); + SQObjectPtr dummy; + CreateFunction(dummy,lambda); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1); + } + void ClassExp() + { + SQInteger base = -1; + SQInteger attrs = -1; + if(_token == TK_EXTENDS) { + Lex(); Expression(); + base = _fs->TopTarget(); + } + if(_token == TK_ATTR_OPEN) { + Lex(); + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); + ParseTableOrClass(_SC(','),TK_ATTR_CLOSE); + attrs = _fs->TopTarget(); + } + Expect(_SC('{')); + if(attrs != -1) _fs->PopTarget(); + if(base != -1) _fs->PopTarget(); + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), base, attrs,NOT_CLASS); + ParseTableOrClass(_SC(';'),_SC('}')); + } + void DeleteExpr() + { + SQExpState es; + Lex(); + es = _es; + _es.donot_get = true; + PrefixedExpr(); + if(_es.etype==EXPR) Error(_SC("can't delete an expression")); + if(_es.etype==OBJECT || _es.etype==BASE) { + Emit2ArgsOP(_OP_DELETE); + } + else { + Error(_SC("cannot delete an (outer) local")); + } + _es = es; + } + void PrefixIncDec(SQInteger token) + { + SQExpState es; + SQInteger diff = (token==TK_MINUSMINUS) ? -1 : 1; + Lex(); + es = _es; + _es.donot_get = true; + PrefixedExpr(); + if(_es.etype==EXPR) { + Error(_SC("can't '++' or '--' an expression")); + } + else if(_es.etype==OBJECT || _es.etype==BASE) { + Emit2ArgsOP(_OP_INC, diff); + } + else if(_es.etype==LOCAL) { + SQInteger src = _fs->TopTarget(); + _fs->AddInstruction(_OP_INCL, src, src, 0, diff); + + } + else if(_es.etype==OUTER) { + SQInteger tmp = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, tmp, _es.epos); + _fs->AddInstruction(_OP_INCL, tmp, tmp, 0, diff); + _fs->AddInstruction(_OP_SETOUTER, tmp, _es.epos, tmp); + } + _es = es; + } + void CreateFunction(SQObject &name,bool lambda = false) + { + SQFuncState *funcstate = _fs->PushChildState(_ss(_vm)); + funcstate->_name = name; + SQObject paramname; + funcstate->AddParameter(_fs->CreateString(_SC("this"))); + funcstate->_sourcename = _sourcename; + SQInteger defparams = 0; + while(_token!=_SC(')')) { + if(_token == TK_VARPARAMS) { + if(defparams > 0) Error(_SC("function with default parameters cannot have variable number of parameters")); + funcstate->AddParameter(_fs->CreateString(_SC("vargv"))); + funcstate->_varparams = true; + Lex(); + if(_token != _SC(')')) Error(_SC("expected ')'")); + break; + } + else { + paramname = Expect(TK_IDENTIFIER); + funcstate->AddParameter(paramname); + if(_token == _SC('=')) { + Lex(); + Expression(); + funcstate->AddDefaultParam(_fs->TopTarget()); + defparams++; + } + else { + if(defparams > 0) Error(_SC("expected '='")); + } + if(_token == _SC(',')) Lex(); + else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); + } + } + Expect(_SC(')')); + for(SQInteger n = 0; n < defparams; n++) { + _fs->PopTarget(); + } + + SQFuncState *currchunk = _fs; + _fs = funcstate; + if(lambda) { + Expression(); + _fs->AddInstruction(_OP_RETURN, 1, _fs->PopTarget());} + else { + Statement(false); + } + funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true); + funcstate->AddInstruction(_OP_RETURN, -1); + funcstate->SetStackSize(0); + + SQFunctionProto *func = funcstate->BuildProto(); +#ifdef _DEBUG_DUMP + funcstate->Dump(func); +#endif + _fs = currchunk; + _fs->_functions.push_back(func); + _fs->PopChildState(); + } + void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve) + { + while(ntoresolve > 0) { + SQInteger pos = funcstate->_unresolvedbreaks.back(); + funcstate->_unresolvedbreaks.pop_back(); + //set the jmp instruction + funcstate->SetInstructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); + ntoresolve--; + } + } + void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos) + { + while(ntoresolve > 0) { + SQInteger pos = funcstate->_unresolvedcontinues.back(); + funcstate->_unresolvedcontinues.pop_back(); + //set the jmp instruction + funcstate->SetInstructionParams(pos, 0, targetpos - pos, 0); + ntoresolve--; + } + } +private: + SQInteger _token; + SQFuncState *_fs; + SQObjectPtr _sourcename; + SQLexer _lex; + bool _lineinfo; + bool _raiseerror; + SQInteger _debugline; + SQInteger _debugop; + SQExpState _es; + SQScope _scope; + SQChar _compilererror[MAX_COMPILER_ERROR_LEN]; + jmp_buf _errorjmp; + SQVM *_vm; +}; + +bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo) +{ + SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo); + return p.Compile(out); +} + +#endif diff --git a/mp/src/vscript/squirrel/squirrel/sqcompiler.h b/mp/src/vscript/squirrel/squirrel/sqcompiler.h new file mode 100644 index 00000000..e7da6423 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqcompiler.h @@ -0,0 +1,79 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQCOMPILER_H_ +#define _SQCOMPILER_H_ + +struct SQVM; + +#define TK_IDENTIFIER 258 +#define TK_STRING_LITERAL 259 +#define TK_INTEGER 260 +#define TK_FLOAT 261 +#define TK_BASE 262 +#define TK_DELETE 263 +#define TK_EQ 264 +#define TK_NE 265 +#define TK_LE 266 +#define TK_GE 267 +#define TK_SWITCH 268 +#define TK_ARROW 269 +#define TK_AND 270 +#define TK_OR 271 +#define TK_IF 272 +#define TK_ELSE 273 +#define TK_WHILE 274 +#define TK_BREAK 275 +#define TK_FOR 276 +#define TK_DO 277 +#define TK_NULL 278 +#define TK_FOREACH 279 +#define TK_IN 280 +#define TK_NEWSLOT 281 +#define TK_MODULO 282 +#define TK_LOCAL 283 +#define TK_CLONE 284 +#define TK_FUNCTION 285 +#define TK_RETURN 286 +#define TK_TYPEOF 287 +#define TK_UMINUS 288 +#define TK_PLUSEQ 289 +#define TK_MINUSEQ 290 +#define TK_CONTINUE 291 +#define TK_YIELD 292 +#define TK_TRY 293 +#define TK_CATCH 294 +#define TK_THROW 295 +#define TK_SHIFTL 296 +#define TK_SHIFTR 297 +#define TK_RESUME 298 +#define TK_DOUBLE_COLON 299 +#define TK_CASE 300 +#define TK_DEFAULT 301 +#define TK_THIS 302 +#define TK_PLUSPLUS 303 +#define TK_MINUSMINUS 304 +#define TK_3WAYSCMP 305 +#define TK_USHIFTR 306 +#define TK_CLASS 307 +#define TK_EXTENDS 308 +#define TK_CONSTRUCTOR 310 +#define TK_INSTANCEOF 311 +#define TK_VARPARAMS 312 +#define TK___LINE__ 313 +#define TK___FILE__ 314 +#define TK_TRUE 315 +#define TK_FALSE 316 +#define TK_MULEQ 317 +#define TK_DIVEQ 318 +#define TK_MODEQ 319 +#define TK_ATTR_OPEN 320 +#define TK_ATTR_CLOSE 321 +#define TK_STATIC 322 +#define TK_ENUM 323 +#define TK_CONST 324 +#define TK_RAWCALL 325 + + + +typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s); +bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo); +#endif //_SQCOMPILER_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqdebug.cpp b/mp/src/vscript/squirrel/squirrel/sqdebug.cpp new file mode 100644 index 00000000..d55b1b2e --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqdebug.cpp @@ -0,0 +1,118 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include "sqvm.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqstring.h" + +SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi) +{ + SQInteger cssize = v->_callsstacksize; + if (cssize > level) { + SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; + if(sq_isclosure(ci._closure)) { + SQClosure *c = _closure(ci._closure); + SQFunctionProto *proto = c->_function; + fi->funcid = proto; + fi->name = sq_type(proto->_name) == OT_STRING?_stringval(proto->_name):_SC("unknown"); + fi->source = sq_type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):_SC("unknown"); + fi->line = proto->_lineinfos[0]._line; + return SQ_OK; + } + } + return sq_throwerror(v,_SC("the object is not a closure")); +} + +SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si) +{ + SQInteger cssize = v->_callsstacksize; + if (cssize > level) { + memset(si, 0, sizeof(SQStackInfos)); + SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; + switch (sq_type(ci._closure)) { + case OT_CLOSURE:{ + SQFunctionProto *func = _closure(ci._closure)->_function; + if (sq_type(func->_name) == OT_STRING) + si->funcname = _stringval(func->_name); + if (sq_type(func->_sourcename) == OT_STRING) + si->source = _stringval(func->_sourcename); + si->line = func->GetLine(ci._ip); + } + break; + case OT_NATIVECLOSURE: + si->source = _SC("NATIVE"); + si->funcname = _SC("unknown"); + if(sq_type(_nativeclosure(ci._closure)->_name) == OT_STRING) + si->funcname = _stringval(_nativeclosure(ci._closure)->_name); + si->line = -1; + break; + default: break; //shutup compiler + } + return SQ_OK; + } + return SQ_ERROR; +} + +void SQVM::Raise_Error(const SQChar *s, ...) +{ + va_list vl; + va_start(vl, s); + SQInteger buffersize = (SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2); + scvsprintf(_sp(sq_rsl(buffersize)),buffersize, s, vl); + va_end(vl); + _lasterror = SQString::Create(_ss(this),_spval,-1); +} + +void SQVM::Raise_Error(const SQObjectPtr &desc) +{ + _lasterror = desc; +} + +SQString *SQVM::PrintObjVal(const SQObjectPtr &o) +{ + switch(sq_type(o)) { + case OT_STRING: return _string(o); + case OT_INTEGER: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR), _PRINT_INT_FMT, _integer(o)); + return SQString::Create(_ss(this), _spval); + break; + case OT_FLOAT: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)), sq_rsl(NUMBER_MAX_CHAR), _SC("%.14g"), _float(o)); + return SQString::Create(_ss(this), _spval); + break; + default: + return SQString::Create(_ss(this), GetTypeName(o)); + } +} + +void SQVM::Raise_IdxError(const SQObjectPtr &o) +{ + SQObjectPtr oval = PrintObjVal(o); + Raise_Error(_SC("the index '%.50s' does not exist"), _stringval(oval)); +} + +void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2) +{ + SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2); + Raise_Error(_SC("comparison between '%.50s' and '%.50s'"), _stringval(oval1), _stringval(oval2)); +} + + +void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type) +{ + SQObjectPtr exptypes = SQString::Create(_ss(this), _SC(""), -1); + SQInteger found = 0; + for(SQInteger i=0; i<16; i++) + { + SQInteger mask = ((SQInteger)1) << i; + if(typemask & (mask)) { + if(found>0) StringCat(exptypes,SQString::Create(_ss(this), _SC("|"), -1), exptypes); + found ++; + StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes); + } + } + Raise_Error(_SC("parameter %d has an invalid type '%s' ; expected: '%s'"), nparam, IdType2Name((SQObjectType)type), _stringval(exptypes)); +} diff --git a/mp/src/vscript/squirrel/squirrel/sqfuncproto.h b/mp/src/vscript/squirrel/squirrel/sqfuncproto.h new file mode 100644 index 00000000..546dbabb --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqfuncproto.h @@ -0,0 +1,154 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQFUNCTION_H_ +#define _SQFUNCTION_H_ + +#include "sqopcodes.h" + +enum SQOuterType { + otLOCAL = 0, + otOUTER = 1 +}; + +struct SQOuterVar +{ + + SQOuterVar(){} + SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t) + { + _name = name; + _src=src; + _type=t; + } + SQOuterVar(const SQOuterVar &ov) + { + _type=ov._type; + _src=ov._src; + _name=ov._name; + } + SQOuterType _type; + SQObjectPtr _name; + SQObjectPtr _src; +}; + +struct SQLocalVarInfo +{ + SQLocalVarInfo():_start_op(0),_end_op(0),_pos(0){} + SQLocalVarInfo(const SQLocalVarInfo &lvi) + { + _name=lvi._name; + _start_op=lvi._start_op; + _end_op=lvi._end_op; + _pos=lvi._pos; + } + SQObjectPtr _name; + SQUnsignedInteger _start_op; + SQUnsignedInteger _end_op; + SQUnsignedInteger _pos; +}; + +struct SQLineInfo { SQInteger _line;SQInteger _op; }; + +typedef sqvector SQOuterVarVec; +typedef sqvector SQLocalVarInfoVec; +typedef sqvector SQLineInfoVec; + +#define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,localinf,defparams) (sizeof(SQFunctionProto) \ + +((ni-1)*sizeof(SQInstruction))+(nl*sizeof(SQObjectPtr)) \ + +(nparams*sizeof(SQObjectPtr))+(nfuncs*sizeof(SQObjectPtr)) \ + +(nouters*sizeof(SQOuterVar))+(nlineinf*sizeof(SQLineInfo)) \ + +(localinf*sizeof(SQLocalVarInfo))+(defparams*sizeof(SQInteger))) + + +struct SQFunctionProto : public CHAINABLE_OBJ +{ +private: + SQFunctionProto(SQSharedState *ss); + ~SQFunctionProto(); + +public: + static SQFunctionProto *Create(SQSharedState *ss,SQInteger ninstructions, + SQInteger nliterals,SQInteger nparameters, + SQInteger nfunctions,SQInteger noutervalues, + SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams) + { + SQFunctionProto *f; + //I compact the whole class and members in a single memory allocation + f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams)); + new (f) SQFunctionProto(ss); + f->_ninstructions = ninstructions; + f->_literals = (SQObjectPtr*)&f->_instructions[ninstructions]; + f->_nliterals = nliterals; + f->_parameters = (SQObjectPtr*)&f->_literals[nliterals]; + f->_nparameters = nparameters; + f->_functions = (SQObjectPtr*)&f->_parameters[nparameters]; + f->_nfunctions = nfunctions; + f->_outervalues = (SQOuterVar*)&f->_functions[nfunctions]; + f->_noutervalues = noutervalues; + f->_lineinfos = (SQLineInfo *)&f->_outervalues[noutervalues]; + f->_nlineinfos = nlineinfos; + f->_localvarinfos = (SQLocalVarInfo *)&f->_lineinfos[nlineinfos]; + f->_nlocalvarinfos = nlocalvarinfos; + f->_defaultparams = (SQInteger *)&f->_localvarinfos[nlocalvarinfos]; + f->_ndefaultparams = ndefaultparams; + + _CONSTRUCT_VECTOR(SQObjectPtr,f->_nliterals,f->_literals); + _CONSTRUCT_VECTOR(SQObjectPtr,f->_nparameters,f->_parameters); + _CONSTRUCT_VECTOR(SQObjectPtr,f->_nfunctions,f->_functions); + _CONSTRUCT_VECTOR(SQOuterVar,f->_noutervalues,f->_outervalues); + //_CONSTRUCT_VECTOR(SQLineInfo,f->_nlineinfos,f->_lineinfos); //not required are 2 integers + _CONSTRUCT_VECTOR(SQLocalVarInfo,f->_nlocalvarinfos,f->_localvarinfos); + return f; + } + void Release(){ + _DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals); + _DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters); + _DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions); + _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues); + //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers + _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos); + SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos,_ndefaultparams); + this->~SQFunctionProto(); + sq_vm_free(this,size); + } + + const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop); + SQInteger GetLine(SQInstruction *curr); + bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); + static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){ _NULL_SQOBJECT_VECTOR(_literals,_nliterals); } + SQObjectType GetType() {return OT_FUNCPROTO;} +#endif + SQObjectPtr _sourcename; + SQObjectPtr _name; + SQInteger _stacksize; + bool _bgenerator; + SQInteger _varparams; + + SQInteger _nlocalvarinfos; + SQLocalVarInfo *_localvarinfos; + + SQInteger _nlineinfos; + SQLineInfo *_lineinfos; + + SQInteger _nliterals; + SQObjectPtr *_literals; + + SQInteger _nparameters; + SQObjectPtr *_parameters; + + SQInteger _nfunctions; + SQObjectPtr *_functions; + + SQInteger _noutervalues; + SQOuterVar *_outervalues; + + SQInteger _ndefaultparams; + SQInteger *_defaultparams; + + SQInteger _ninstructions; + SQInstruction _instructions[1]; +}; + +#endif //_SQFUNCTION_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqfuncstate.cpp b/mp/src/vscript/squirrel/squirrel/sqfuncstate.cpp new file mode 100644 index 00000000..779b40df --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqfuncstate.cpp @@ -0,0 +1,649 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#ifndef NO_COMPILER +#include "sqcompiler.h" +#include "sqstring.h" +#include "sqfuncproto.h" +#include "sqtable.h" +#include "sqopcodes.h" +#include "sqfuncstate.h" + +#ifdef _DEBUG_DUMP +SQInstructionDesc g_InstrDesc[]={ + {_SC("_OP_LINE")}, + {_SC("_OP_LOAD")}, + {_SC("_OP_LOADINT")}, + {_SC("_OP_LOADFLOAT")}, + {_SC("_OP_DLOAD")}, + {_SC("_OP_TAILCALL")}, + {_SC("_OP_CALL")}, + {_SC("_OP_PREPCALL")}, + {_SC("_OP_PREPCALLK")}, + {_SC("_OP_GETK")}, + {_SC("_OP_MOVE")}, + {_SC("_OP_NEWSLOT")}, + {_SC("_OP_DELETE")}, + {_SC("_OP_SET")}, + {_SC("_OP_GET")}, + {_SC("_OP_EQ")}, + {_SC("_OP_NE")}, + {_SC("_OP_ADD")}, + {_SC("_OP_SUB")}, + {_SC("_OP_MUL")}, + {_SC("_OP_DIV")}, + {_SC("_OP_MOD")}, + {_SC("_OP_BITW")}, + {_SC("_OP_RETURN")}, + {_SC("_OP_LOADNULLS")}, + {_SC("_OP_LOADROOT")}, + {_SC("_OP_LOADBOOL")}, + {_SC("_OP_DMOVE")}, + {_SC("_OP_JMP")}, + {_SC("_OP_JCMP")}, + {_SC("_OP_JZ")}, + {_SC("_OP_SETOUTER")}, + {_SC("_OP_GETOUTER")}, + {_SC("_OP_NEWOBJ")}, + {_SC("_OP_APPENDARRAY")}, + {_SC("_OP_COMPARITH")}, + {_SC("_OP_INC")}, + {_SC("_OP_INCL")}, + {_SC("_OP_PINC")}, + {_SC("_OP_PINCL")}, + {_SC("_OP_CMP")}, + {_SC("_OP_EXISTS")}, + {_SC("_OP_INSTANCEOF")}, + {_SC("_OP_AND")}, + {_SC("_OP_OR")}, + {_SC("_OP_NEG")}, + {_SC("_OP_NOT")}, + {_SC("_OP_BWNOT")}, + {_SC("_OP_CLOSURE")}, + {_SC("_OP_YIELD")}, + {_SC("_OP_RESUME")}, + {_SC("_OP_FOREACH")}, + {_SC("_OP_POSTFOREACH")}, + {_SC("_OP_CLONE")}, + {_SC("_OP_TYPEOF")}, + {_SC("_OP_PUSHTRAP")}, + {_SC("_OP_POPTRAP")}, + {_SC("_OP_THROW")}, + {_SC("_OP_NEWSLOTA")}, + {_SC("_OP_GETBASE")}, + {_SC("_OP_CLOSE")}, +}; +#endif +void DumpLiteral(SQObjectPtr &o) +{ + switch(sq_type(o)){ + case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break; + case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break; + case OT_INTEGER: scprintf(_SC("{") _PRINT_INT_FMT _SC("}"),_integer(o));break; + case OT_BOOL: scprintf(_SC("%s"),_integer(o)?_SC("true"):_SC("false"));break; + default: scprintf(_SC("(%s %p)"),GetTypeName(o),(void*)_rawval(o));break; break; //shut up compiler + } +} + +SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed) +{ + _nliterals = 0; + _literals = SQTable::Create(ss,0); + _strings = SQTable::Create(ss,0); + _sharedstate = ss; + _lastline = 0; + _optimization = true; + _parent = parent; + _stacksize = 0; + _traps = 0; + _returnexp = 0; + _varparams = false; + _errfunc = efunc; + _errtarget = ed; + _bgenerator = false; + _outers = 0; + _ss = ss; + +} + +void SQFuncState::Error(const SQChar *err) +{ + _errfunc(_errtarget,err); +} + +#ifdef _DEBUG_DUMP +void SQFuncState::Dump(SQFunctionProto *func) +{ + SQUnsignedInteger n=0,i; + SQInteger si; + scprintf(_SC("SQInstruction sizeof %d\n"),(SQInt32)sizeof(SQInstruction)); + scprintf(_SC("SQObject sizeof %d\n"), (SQInt32)sizeof(SQObject)); + scprintf(_SC("--------------------------------------------------------------------\n")); + scprintf(_SC("*****FUNCTION [%s]\n"),sq_type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown")); + scprintf(_SC("-----LITERALS\n")); + SQObjectPtr refidx,key,val; + SQInteger idx; + SQObjectPtrVec templiterals; + templiterals.resize(_nliterals); + while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { + refidx=idx; + templiterals[_integer(val)]=key; + } + for(i=0;i>\n")); + n=0; + for(i=0;i<_parameters.size();i++){ + scprintf(_SC("[%d] "), (SQInt32)n); + DumpLiteral(_parameters[i]); + scprintf(_SC("\n")); + n++; + } + scprintf(_SC("-----LOCALS\n")); + for(si=0;si_nlocalvarinfos;si++){ + SQLocalVarInfo lvi=func->_localvarinfos[si]; + scprintf(_SC("[%d] %s \t%d %d\n"), (SQInt32)lvi._pos,_stringval(lvi._name), (SQInt32)lvi._start_op, (SQInt32)lvi._end_op); + n++; + } + scprintf(_SC("-----LINE INFO\n")); + for(i=0;i<_lineinfos.size();i++){ + SQLineInfo li=_lineinfos[i]; + scprintf(_SC("op [%d] line [%d] \n"), (SQInt32)li._op, (SQInt32)li._line); + n++; + } + scprintf(_SC("-----dump\n")); + n=0; + for(i=0;i<_instructions.size();i++){ + SQInstruction &inst=_instructions[i]; + if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){ + + SQInteger lidx = inst._arg1; + scprintf(_SC("[%03d] %15s %d "), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0); + if(lidx >= 0xFFFFFFFF) + scprintf(_SC("null")); + else { + SQInteger refidx; + SQObjectPtr val,key,refo; + while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) { + refo = refidx; + } + DumpLiteral(key); + } + if(inst.op != _OP_DLOAD) { + scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3); + } + else { + scprintf(_SC(" %d "),inst._arg2); + lidx = inst._arg3; + if(lidx >= 0xFFFFFFFF) + scprintf(_SC("null")); + else { + SQInteger refidx; + SQObjectPtr val,key,refo; + while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) { + refo = refidx; + } + DumpLiteral(key); + scprintf(_SC("\n")); + } + } + } + else if(inst.op==_OP_LOADFLOAT) { + scprintf(_SC("[%03d] %15s %d %f %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3); + } + /* else if(inst.op==_OP_ARITH){ + scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); + }*/ + else { + scprintf(_SC("[%03d] %15s %d %d %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); + } + n++; + } + scprintf(_SC("-----\n")); + scprintf(_SC("stack size[%d]\n"), (SQInt32)func->_stacksize); + scprintf(_SC("--------------------------------------------------------------------\n\n")); +} +#endif + +SQInteger SQFuncState::GetNumericConstant(const SQInteger cons) +{ + return GetConstant(SQObjectPtr(cons)); +} + +SQInteger SQFuncState::GetNumericConstant(const SQFloat cons) +{ + return GetConstant(SQObjectPtr(cons)); +} + +SQInteger SQFuncState::GetConstant(const SQObject &cons) +{ + SQObjectPtr val; + if(!_table(_literals)->Get(cons,val)) + { + val = _nliterals; + _table(_literals)->NewSlot(cons,val); + _nliterals++; + if(_nliterals > MAX_LITERALS) { + val.Null(); + Error(_SC("internal compiler error: too many literals")); + } + } + return _integer(val); +} + +void SQFuncState::SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) +{ + _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0); + _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1); + _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2); + _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3); +} + +void SQFuncState::SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val) +{ + switch(arg){ + case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break; + case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break; + case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break; + case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break; + }; +} + +SQInteger SQFuncState::AllocStackPos() +{ + SQInteger npos=_vlocals.size(); + _vlocals.push_back(SQLocalVarInfo()); + if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) { + if(_stacksize>MAX_FUNC_STACKSIZE) Error(_SC("internal compiler error: too many locals")); + _stacksize=_vlocals.size(); + } + return npos; +} + +SQInteger SQFuncState::PushTarget(SQInteger n) +{ + if(n!=-1){ + _targetstack.push_back(n); + return n; + } + n=AllocStackPos(); + _targetstack.push_back(n); + return n; +} + +SQInteger SQFuncState::GetUpTarget(SQInteger n){ + return _targetstack[((_targetstack.size()-1)-n)]; +} + +SQInteger SQFuncState::TopTarget(){ + return _targetstack.back(); +} +SQInteger SQFuncState::PopTarget() +{ + SQUnsignedInteger npos=_targetstack.back(); + assert(npos < _vlocals.size()); + SQLocalVarInfo &t = _vlocals[npos]; + if(sq_type(t._name)==OT_NULL){ + _vlocals.pop_back(); + } + _targetstack.pop_back(); + return npos; +} + +SQInteger SQFuncState::GetStackSize() +{ + return _vlocals.size(); +} + +SQInteger SQFuncState::CountOuters(SQInteger stacksize) +{ + SQInteger outers = 0; + SQInteger k = _vlocals.size() - 1; + while(k >= stacksize) { + SQLocalVarInfo &lvi = _vlocals[k]; + k--; + if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer + outers++; + } + } + return outers; +} + +void SQFuncState::SetStackSize(SQInteger n) +{ + SQInteger size=_vlocals.size(); + while(size>n){ + size--; + SQLocalVarInfo lvi = _vlocals.back(); + if(sq_type(lvi._name)!=OT_NULL){ + if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer + _outers--; + } + lvi._end_op = GetCurrentPos(); + _localvarinfos.push_back(lvi); + } + _vlocals.pop_back(); + } +} + +bool SQFuncState::IsConstant(const SQObject &name,SQObject &e) +{ + SQObjectPtr val; + if(_table(_sharedstate->_consts)->Get(name,val)) { + e = val; + return true; + } + return false; +} + +bool SQFuncState::IsLocal(SQUnsignedInteger stkpos) +{ + if(stkpos>=_vlocals.size())return false; + else if(sq_type(_vlocals[stkpos]._name)!=OT_NULL)return true; + return false; +} + +SQInteger SQFuncState::PushLocalVariable(const SQObject &name) +{ + SQInteger pos=_vlocals.size(); + SQLocalVarInfo lvi; + lvi._name=name; + lvi._start_op=GetCurrentPos()+1; + lvi._pos=_vlocals.size(); + _vlocals.push_back(lvi); + if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size(); + return pos; +} + + + +SQInteger SQFuncState::GetLocalVariable(const SQObject &name) +{ + SQInteger locals=_vlocals.size(); + while(locals>=1){ + SQLocalVarInfo &lvi = _vlocals[locals-1]; + if(sq_type(lvi._name)==OT_STRING && _string(lvi._name)==_string(name)){ + return locals-1; + } + locals--; + } + return -1; +} + +void SQFuncState::MarkLocalAsOuter(SQInteger pos) +{ + SQLocalVarInfo &lvi = _vlocals[pos]; + lvi._end_op = UINT_MINUS_ONE; + _outers++; +} + +SQInteger SQFuncState::GetOuterVariable(const SQObject &name) +{ + SQInteger outers = _outervalues.size(); + for(SQInteger i = 0; iGetLocalVariable(name); + if(pos == -1) { + pos = _parent->GetOuterVariable(name); + if(pos != -1) { + _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local + return _outervalues.size() - 1; + } + } + else { + _parent->MarkLocalAsOuter(pos); + _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local + return _outervalues.size() - 1; + + + } + } + return -1; +} + +void SQFuncState::AddParameter(const SQObject &name) +{ + PushLocalVariable(name); + _parameters.push_back(name); +} + +void SQFuncState::AddLineInfos(SQInteger line,bool lineop,bool force) +{ + if(_lastline!=line || force){ + SQLineInfo li; + li._line=line;li._op=(GetCurrentPos()+1); + if(lineop)AddInstruction(_OP_LINE,0,line); + if(_lastline!=line) { + _lineinfos.push_back(li); + } + _lastline=line; + } +} + +void SQFuncState::DiscardTarget() +{ + SQInteger discardedtarget = PopTarget(); + SQInteger size = _instructions.size(); + if(size > 0 && _optimization){ + SQInstruction &pi = _instructions[size-1];//previous instruction + switch(pi.op) { + case _OP_SET:case _OP_NEWSLOT:case _OP_SETOUTER:case _OP_CALL: + if(pi._arg0 == discardedtarget) { + pi._arg0 = 0xFF; + } + } + } +} + +void SQFuncState::AddInstruction(SQInstruction &i) +{ + SQInteger size = _instructions.size(); + if(size > 0 && _optimization){ //simple optimizer + SQInstruction &pi = _instructions[size-1];//previous instruction + switch(i.op) { + case _OP_JZ: + if( pi.op == _OP_CMP && pi._arg1 < 0xFF) { + pi.op = _OP_JCMP; + pi._arg0 = (unsigned char)pi._arg1; + pi._arg1 = i._arg1; + return; + } + break; + case _OP_SET: + case _OP_NEWSLOT: + if(i._arg0 == i._arg3) { + i._arg0 = 0xFF; + } + break; + case _OP_SETOUTER: + if(i._arg0 == i._arg2) { + i._arg0 = 0xFF; + } + break; + case _OP_RETURN: + if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) { + pi.op = _OP_TAILCALL; + } else if(pi.op == _OP_CLOSE){ + pi = i; + return; + } + break; + case _OP_GET: + if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){ + pi._arg2 = (unsigned char)i._arg1; + pi.op = _OP_GETK; + pi._arg0 = i._arg0; + + return; + } + break; + case _OP_PREPCALL: + if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ + pi.op = _OP_PREPCALLK; + pi._arg0 = i._arg0; + pi._arg2 = i._arg2; + pi._arg3 = i._arg3; + return; + } + break; + case _OP_APPENDARRAY: { + SQInteger aat = -1; + switch(pi.op) { + case _OP_LOAD: aat = AAT_LITERAL; break; + case _OP_LOADINT: aat = AAT_INT; break; + case _OP_LOADBOOL: aat = AAT_BOOL; break; + case _OP_LOADFLOAT: aat = AAT_FLOAT; break; + default: break; + } + if(aat != -1 && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ + pi.op = _OP_APPENDARRAY; + pi._arg0 = i._arg0; + pi._arg2 = (unsigned char)aat; + pi._arg3 = MAX_FUNC_STACKSIZE; + return; + } + } + break; + case _OP_MOVE: + switch(pi.op) { + case _OP_GET: case _OP_ADD: case _OP_SUB: case _OP_MUL: case _OP_DIV: case _OP_MOD: case _OP_BITW: + case _OP_LOADINT: case _OP_LOADFLOAT: case _OP_LOADBOOL: case _OP_LOAD: + + if(pi._arg0 == i._arg1) + { + pi._arg0 = i._arg0; + _optimization = false; + //_result_elimination = false; + return; + } + } + + if(pi.op == _OP_MOVE) + { + pi.op = _OP_DMOVE; + pi._arg2 = i._arg0; + pi._arg3 = (unsigned char)i._arg1; + return; + } + break; + case _OP_LOAD: + if(pi.op == _OP_LOAD && i._arg1 < 256) { + pi.op = _OP_DLOAD; + pi._arg2 = i._arg0; + pi._arg3 = (unsigned char)i._arg1; + return; + } + break; + case _OP_EQ:case _OP_NE: + if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) )) + { + pi.op = i.op; + pi._arg0 = i._arg0; + pi._arg2 = i._arg2; + pi._arg3 = MAX_FUNC_STACKSIZE; + return; + } + break; + case _OP_LOADNULLS: + if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) { + + pi._arg1 = pi._arg1 + 1; + pi.op = _OP_LOADNULLS; + return; + } + break; + case _OP_LINE: + if(pi.op == _OP_LINE) { + _instructions.pop_back(); + _lineinfos.pop_back(); + } + break; + } + } + _optimization = true; + _instructions.push_back(i); +} + +SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len) +{ + SQObjectPtr ns(SQString::Create(_sharedstate,s,len)); + _table(_strings)->NewSlot(ns,(SQInteger)1); + return ns; +} + +SQObject SQFuncState::CreateTable() +{ + SQObjectPtr nt(SQTable::Create(_sharedstate,0)); + _table(_strings)->NewSlot(nt,(SQInteger)1); + return nt; +} + +SQFunctionProto *SQFuncState::BuildProto() +{ + + SQFunctionProto *f=SQFunctionProto::Create(_ss,_instructions.size(), + _nliterals,_parameters.size(),_functions.size(),_outervalues.size(), + _lineinfos.size(),_localvarinfos.size(),_defaultparams.size()); + + SQObjectPtr refidx,key,val; + SQInteger idx; + + f->_stacksize = _stacksize; + f->_sourcename = _sourcename; + f->_bgenerator = _bgenerator; + f->_name = _name; + + while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { + f->_literals[_integer(val)]=key; + refidx=idx; + } + + for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf]; + for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) f->_parameters[np] = _parameters[np]; + for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no]; + for(SQUnsignedInteger nl = 0; nl < _localvarinfos.size(); nl++) f->_localvarinfos[nl] = _localvarinfos[nl]; + for(SQUnsignedInteger ni = 0; ni < _lineinfos.size(); ni++) f->_lineinfos[ni] = _lineinfos[ni]; + for(SQUnsignedInteger nd = 0; nd < _defaultparams.size(); nd++) f->_defaultparams[nd] = _defaultparams[nd]; + + memcpy(f->_instructions,&_instructions[0],_instructions.size()*sizeof(SQInstruction)); + + f->_varparams = _varparams; + + return f; +} + +SQFuncState *SQFuncState::PushChildState(SQSharedState *ss) +{ + SQFuncState *child = (SQFuncState *)sq_malloc(sizeof(SQFuncState)); + new (child) SQFuncState(ss,this,_errfunc,_errtarget); + _childstates.push_back(child); + return child; +} + +void SQFuncState::PopChildState() +{ + SQFuncState *child = _childstates.back(); + sq_delete(child,SQFuncState); + _childstates.pop_back(); +} + +SQFuncState::~SQFuncState() +{ + while(_childstates.size() > 0) + { + PopChildState(); + } +} + +#endif diff --git a/mp/src/vscript/squirrel/squirrel/sqfuncstate.h b/mp/src/vscript/squirrel/squirrel/sqfuncstate.h new file mode 100644 index 00000000..130aa54f --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqfuncstate.h @@ -0,0 +1,91 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQFUNCSTATE_H_ +#define _SQFUNCSTATE_H_ +/////////////////////////////////// +#include "squtils.h" + +struct SQFuncState +{ + SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed); + ~SQFuncState(); +#ifdef _DEBUG_DUMP + void Dump(SQFunctionProto *func); +#endif + void Error(const SQChar *err); + SQFuncState *PushChildState(SQSharedState *ss); + void PopChildState(); + void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);} + void AddInstruction(SQInstruction &i); + void SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0); + void SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val); + SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];} + void PopInstructions(SQInteger size){for(SQInteger i=0;i _childstates; + SQInteger GetConstant(const SQObject &cons); +private: + CompilerErrorFunc _errfunc; + void *_errtarget; + SQSharedState *_ss; +}; + + +#endif //_SQFUNCSTATE_H_ + diff --git a/mp/src/vscript/squirrel/squirrel/sqlexer.cpp b/mp/src/vscript/squirrel/squirrel/sqlexer.cpp new file mode 100644 index 00000000..6a8e7c4f --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqlexer.cpp @@ -0,0 +1,563 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include +#include "sqtable.h" +#include "sqstring.h" +#include "sqcompiler.h" +#include "sqlexer.h" + +#define CUR_CHAR (_currdata) +#define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;} +#define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB) +#define NEXT() {Next();_currentcolumn++;} +#define INIT_TEMP_STRING() { _longstr.resize(0);} +#define APPEND_CHAR(c) { _longstr.push_back(c);} +#define TERMINATE_BUFFER() {_longstr.push_back(_SC('\0'));} +#define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, _SC(#key)) ,SQInteger(id)) + +SQLexer::SQLexer(){} +SQLexer::~SQLexer() +{ + _keywords->Release(); +} + +void SQLexer::Init(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed) +{ + _errfunc = efunc; + _errtarget = ed; + _sharedstate = ss; + _keywords = SQTable::Create(ss, 37); + ADD_KEYWORD(while, TK_WHILE); + ADD_KEYWORD(do, TK_DO); + ADD_KEYWORD(if, TK_IF); + ADD_KEYWORD(else, TK_ELSE); + ADD_KEYWORD(break, TK_BREAK); + ADD_KEYWORD(continue, TK_CONTINUE); + ADD_KEYWORD(return, TK_RETURN); + ADD_KEYWORD(null, TK_NULL); + ADD_KEYWORD(function, TK_FUNCTION); + ADD_KEYWORD(local, TK_LOCAL); + ADD_KEYWORD(for, TK_FOR); + ADD_KEYWORD(foreach, TK_FOREACH); + ADD_KEYWORD(in, TK_IN); + ADD_KEYWORD(typeof, TK_TYPEOF); + ADD_KEYWORD(base, TK_BASE); + ADD_KEYWORD(delete, TK_DELETE); + ADD_KEYWORD(try, TK_TRY); + ADD_KEYWORD(catch, TK_CATCH); + ADD_KEYWORD(throw, TK_THROW); + ADD_KEYWORD(clone, TK_CLONE); + ADD_KEYWORD(yield, TK_YIELD); + ADD_KEYWORD(resume, TK_RESUME); + ADD_KEYWORD(switch, TK_SWITCH); + ADD_KEYWORD(case, TK_CASE); + ADD_KEYWORD(default, TK_DEFAULT); + ADD_KEYWORD(this, TK_THIS); + ADD_KEYWORD(class,TK_CLASS); + ADD_KEYWORD(extends,TK_EXTENDS); + ADD_KEYWORD(constructor,TK_CONSTRUCTOR); + ADD_KEYWORD(instanceof,TK_INSTANCEOF); + ADD_KEYWORD(true,TK_TRUE); + ADD_KEYWORD(false,TK_FALSE); + ADD_KEYWORD(static,TK_STATIC); + ADD_KEYWORD(enum,TK_ENUM); + ADD_KEYWORD(const,TK_CONST); + ADD_KEYWORD(__LINE__,TK___LINE__); + ADD_KEYWORD(__FILE__,TK___FILE__); + ADD_KEYWORD(rawcall, TK_RAWCALL); + + + _readf = rg; + _up = up; + _lasttokenline = _currentline = 1; + _currentcolumn = 0; + _prevtoken = -1; + _reached_eof = SQFalse; + Next(); +} + +void SQLexer::Error(const SQChar *err) +{ + _errfunc(_errtarget,err); +} + +void SQLexer::Next() +{ + SQInteger t = _readf(_up); + if(t > MAX_CHAR) Error(_SC("Invalid character")); + if(t != 0) { + _currdata = (LexChar)t; + return; + } + _currdata = SQUIRREL_EOB; + _reached_eof = SQTrue; +} + +const SQChar *SQLexer::Tok2Str(SQInteger tok) +{ + SQObjectPtr itr, key, val; + SQInteger nitr; + while((nitr = _keywords->Next(false,itr, key, val)) != -1) { + itr = (SQInteger)nitr; + if(((SQInteger)_integer(val)) == tok) + return _stringval(key); + } + return NULL; +} + +void SQLexer::LexBlockComment() +{ + bool done = false; + while(!done) { + switch(CUR_CHAR) { + case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue; + case _SC('\n'): _currentline++; NEXT(); continue; + case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment")); + default: NEXT(); + } + } +} +void SQLexer::LexLineComment() +{ + do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB())); +} + +SQInteger SQLexer::Lex() +{ + _lasttokenline = _currentline; + while(CUR_CHAR != SQUIRREL_EOB) { + switch(CUR_CHAR){ + case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue; + case _SC('\n'): + _currentline++; + _prevtoken=_curtoken; + _curtoken=_SC('\n'); + NEXT(); + _currentcolumn=1; + continue; + case _SC('#'): LexLineComment(); continue; + case _SC('/'): + NEXT(); + switch(CUR_CHAR){ + case _SC('*'): + NEXT(); + LexBlockComment(); + continue; + case _SC('/'): + LexLineComment(); + continue; + case _SC('='): + NEXT(); + RETURN_TOKEN(TK_DIVEQ); + continue; + case _SC('>'): + NEXT(); + RETURN_TOKEN(TK_ATTR_CLOSE); + continue; + default: + RETURN_TOKEN('/'); + } + case _SC('='): + NEXT(); + if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') } + else { NEXT(); RETURN_TOKEN(TK_EQ); } + case _SC('<'): + NEXT(); + switch(CUR_CHAR) { + case _SC('='): + NEXT(); + if(CUR_CHAR == _SC('>')) { + NEXT(); + RETURN_TOKEN(TK_3WAYSCMP); + } + RETURN_TOKEN(TK_LE) + break; + case _SC('-'): NEXT(); RETURN_TOKEN(TK_NEWSLOT); break; + case _SC('<'): NEXT(); RETURN_TOKEN(TK_SHIFTL); break; + case _SC('/'): NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); break; + } + RETURN_TOKEN('<'); + case _SC('>'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);} + else if(CUR_CHAR == _SC('>')){ + NEXT(); + if(CUR_CHAR == _SC('>')){ + NEXT(); + RETURN_TOKEN(TK_USHIFTR); + } + RETURN_TOKEN(TK_SHIFTR); + } + else { RETURN_TOKEN('>') } + case _SC('!'): + NEXT(); + if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')} + else { NEXT(); RETURN_TOKEN(TK_NE); } + case _SC('@'): { + SQInteger stype; + NEXT(); + if(CUR_CHAR != _SC('"')) { + RETURN_TOKEN('@'); + } + if((stype=ReadString('"',true))!=-1) { + RETURN_TOKEN(stype); + } + Error(_SC("error parsing the string")); + } + case _SC('"'): + case _SC('\''): { + SQInteger stype; + if((stype=ReadString(CUR_CHAR,false))!=-1){ + RETURN_TOKEN(stype); + } + Error(_SC("error parsing the string")); + } + case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'): + case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'): + {SQInteger ret = CUR_CHAR; + NEXT(); RETURN_TOKEN(ret); } + case _SC('.'): + NEXT(); + if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') } + NEXT(); + if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); } + NEXT(); + RETURN_TOKEN(TK_VARPARAMS); + case _SC('&'): + NEXT(); + if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') } + else { NEXT(); RETURN_TOKEN(TK_AND); } + case _SC('|'): + NEXT(); + if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') } + else { NEXT(); RETURN_TOKEN(TK_OR); } + case _SC(':'): + NEXT(); + if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') } + else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); } + case _SC('*'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);} + else RETURN_TOKEN('*'); + case _SC('%'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);} + else RETURN_TOKEN('%'); + case _SC('-'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);} + else if (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);} + else RETURN_TOKEN('-'); + case _SC('+'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);} + else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);} + else RETURN_TOKEN('+'); + case SQUIRREL_EOB: + return 0; + default:{ + if (scisdigit(CUR_CHAR)) { + SQInteger ret = ReadNumber(); + RETURN_TOKEN(ret); + } + else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) { + SQInteger t = ReadID(); + RETURN_TOKEN(t); + } + else { + SQInteger c = CUR_CHAR; + if (sciscntrl((int)c)) Error(_SC("unexpected character(control)")); + NEXT(); + RETURN_TOKEN(c); + } + RETURN_TOKEN(0); + } + } + } + return 0; +} + +SQInteger SQLexer::GetIDType(const SQChar *s,SQInteger len) +{ + SQObjectPtr t; + if(_keywords->GetStr(s,len, t)) { + return SQInteger(_integer(t)); + } + return TK_IDENTIFIER; +} + +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 +SQInteger SQLexer::AddUTF16(SQUnsignedInteger ch) +{ + if (ch >= 0x10000) + { + SQUnsignedInteger code = (ch - 0x10000); + APPEND_CHAR((SQChar)(0xD800 | (code >> 10))); + APPEND_CHAR((SQChar)(0xDC00 | (code & 0x3FF))); + return 2; + } + else { + APPEND_CHAR((SQChar)ch); + return 1; + } +} +#endif +#else +SQInteger SQLexer::AddUTF8(SQUnsignedInteger ch) +{ + if (ch < 0x80) { + APPEND_CHAR((char)ch); + return 1; + } + if (ch < 0x800) { + APPEND_CHAR((SQChar)((ch >> 6) | 0xC0)); + APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); + return 2; + } + if (ch < 0x10000) { + APPEND_CHAR((SQChar)((ch >> 12) | 0xE0)); + APPEND_CHAR((SQChar)(((ch >> 6) & 0x3F) | 0x80)); + APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); + return 3; + } + if (ch < 0x110000) { + APPEND_CHAR((SQChar)((ch >> 18) | 0xF0)); + APPEND_CHAR((SQChar)(((ch >> 12) & 0x3F) | 0x80)); + APPEND_CHAR((SQChar)(((ch >> 6) & 0x3F) | 0x80)); + APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); + return 4; + } + return 0; +} +#endif + +SQInteger SQLexer::ProcessStringHexEscape(SQChar *dest, SQInteger maxdigits) +{ + NEXT(); + if (!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected")); + SQInteger n = 0; + while (isxdigit(CUR_CHAR) && n < maxdigits) { + dest[n] = CUR_CHAR; + n++; + NEXT(); + } + dest[n] = 0; + return n; +} + +SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim) +{ + INIT_TEMP_STRING(); + NEXT(); + if(IS_EOB()) return -1; + for(;;) { + while(CUR_CHAR != ndelim) { + SQInteger x = CUR_CHAR; + switch (x) { + case SQUIRREL_EOB: + Error(_SC("unfinished string")); + return -1; + case _SC('\n'): + if(!verbatim) Error(_SC("newline in a constant")); + APPEND_CHAR(CUR_CHAR); NEXT(); + _currentline++; + break; + case _SC('\\'): + if(verbatim) { + APPEND_CHAR('\\'); NEXT(); + } + else { + NEXT(); + switch(CUR_CHAR) { + case _SC('x'): { + const SQInteger maxdigits = sizeof(SQChar) * 2; + SQChar temp[maxdigits + 1]; + ProcessStringHexEscape(temp, maxdigits); + SQChar *stemp; + APPEND_CHAR((SQChar)scstrtoul(temp, &stemp, 16)); + } + break; + case _SC('U'): + case _SC('u'): { + const SQInteger maxdigits = CUR_CHAR == 'u' ? 4 : 8; + SQChar temp[8 + 1]; + ProcessStringHexEscape(temp, maxdigits); + SQChar *stemp; +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 + AddUTF16(scstrtoul(temp, &stemp, 16)); +#else + APPEND_CHAR((SQChar)scstrtoul(temp, &stemp, 16)); +#endif +#else + AddUTF8(scstrtoul(temp, &stemp, 16)); +#endif + } + break; + case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break; + case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break; + case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break; + case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break; + case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break; + case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break; + case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break; + case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break; + case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break; + case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break; + case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break; + default: + Error(_SC("unrecognised escaper char")); + break; + } + } + break; + default: + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + } + NEXT(); + if(verbatim && CUR_CHAR == '"') { //double quotation + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + else { + break; + } + } + TERMINATE_BUFFER(); + SQInteger len = _longstr.size()-1; + if(ndelim == _SC('\'')) { + if(len == 0) Error(_SC("empty constant")); + if(len > 1) Error(_SC("constant too long")); + _nvalue = _longstr[0]; + return TK_INTEGER; + } + _svalue = &_longstr[0]; + return TK_STRING_LITERAL; +} + +void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res) +{ + *res = 0; + while(*s != 0) + { + if(scisdigit(*s)) *res = (*res)*16+((*s++)-'0'); + else if(scisxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10); + else { assert(0); } + } +} + +void LexInteger(const SQChar *s,SQUnsignedInteger *res) +{ + *res = 0; + while(*s != 0) + { + *res = (*res)*10+((*s++)-'0'); + } +} + +SQInteger scisodigit(SQInteger c) { return c >= _SC('0') && c <= _SC('7'); } + +void LexOctal(const SQChar *s,SQUnsignedInteger *res) +{ + *res = 0; + while(*s != 0) + { + if(scisodigit(*s)) *res = (*res)*8+((*s++)-'0'); + else { assert(0); } + } +} + +SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; } + + +#define MAX_HEX_DIGITS (sizeof(SQInteger)*2) +SQInteger SQLexer::ReadNumber() +{ +#define TINT 1 +#define TFLOAT 2 +#define THEX 3 +#define TSCIENTIFIC 4 +#define TOCTAL 5 + SQInteger type = TINT, firstchar = CUR_CHAR; + SQChar *sTemp; + INIT_TEMP_STRING(); + NEXT(); + if(firstchar == _SC('0') && (toupper(CUR_CHAR) == _SC('X') || scisodigit(CUR_CHAR)) ) { + if(scisodigit(CUR_CHAR)) { + type = TOCTAL; + while(scisodigit(CUR_CHAR)) { + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + if(scisdigit(CUR_CHAR)) Error(_SC("invalid octal number")); + } + else { + NEXT(); + type = THEX; + while(isxdigit(CUR_CHAR)) { + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + if(_longstr.size() > MAX_HEX_DIGITS) Error(_SC("too many digits for an Hex number")); + } + } + else { + APPEND_CHAR((int)firstchar); + while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) { + if(CUR_CHAR == _SC('.') || isexponent(CUR_CHAR)) type = TFLOAT; + if(isexponent(CUR_CHAR)) { + if(type != TFLOAT) Error(_SC("invalid numeric format")); + type = TSCIENTIFIC; + APPEND_CHAR(CUR_CHAR); + NEXT(); + if(CUR_CHAR == '+' || CUR_CHAR == '-'){ + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected")); + } + + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + } + TERMINATE_BUFFER(); + switch(type) { + case TSCIENTIFIC: + case TFLOAT: + _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp); + return TK_FLOAT; + case TINT: + LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue); + return TK_INTEGER; + case THEX: + LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); + return TK_INTEGER; + case TOCTAL: + LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); + return TK_INTEGER; + } + return 0; +} + +SQInteger SQLexer::ReadID() +{ + SQInteger res; + INIT_TEMP_STRING(); + do { + APPEND_CHAR(CUR_CHAR); + NEXT(); + } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_')); + TERMINATE_BUFFER(); + res = GetIDType(&_longstr[0],_longstr.size() - 1); + if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) { + _svalue = &_longstr[0]; + } + return res; +} diff --git a/mp/src/vscript/squirrel/squirrel/sqlexer.h b/mp/src/vscript/squirrel/squirrel/sqlexer.h new file mode 100644 index 00000000..d731c20e --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqlexer.h @@ -0,0 +1,55 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQLEXER_H_ +#define _SQLEXER_H_ + +#ifdef SQUNICODE +typedef SQChar LexChar; +#else +typedef unsigned char LexChar; +#endif + +struct SQLexer +{ + SQLexer(); + ~SQLexer(); + void Init(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up,CompilerErrorFunc efunc,void *ed); + void Error(const SQChar *err); + SQInteger Lex(); + const SQChar *Tok2Str(SQInteger tok); +private: + SQInteger GetIDType(const SQChar *s,SQInteger len); + SQInteger ReadString(SQInteger ndelim,bool verbatim); + SQInteger ReadNumber(); + void LexBlockComment(); + void LexLineComment(); + SQInteger ReadID(); + void Next(); +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 + SQInteger AddUTF16(SQUnsignedInteger ch); +#endif +#else + SQInteger AddUTF8(SQUnsignedInteger ch); +#endif + SQInteger ProcessStringHexEscape(SQChar *dest, SQInteger maxdigits); + SQInteger _curtoken; + SQTable *_keywords; + SQBool _reached_eof; +public: + SQInteger _prevtoken; + SQInteger _currentline; + SQInteger _lasttokenline; + SQInteger _currentcolumn; + const SQChar *_svalue; + SQInteger _nvalue; + SQFloat _fvalue; + SQLEXREADFUNC _readf; + SQUserPointer _up; + LexChar _currdata; + SQSharedState *_sharedstate; + sqvector _longstr; + CompilerErrorFunc _errfunc; + void *_errtarget; +}; + +#endif diff --git a/mp/src/vscript/squirrel/squirrel/sqmem.cpp b/mp/src/vscript/squirrel/squirrel/sqmem.cpp new file mode 100644 index 00000000..378e254b --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqmem.cpp @@ -0,0 +1,11 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#ifndef SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS +void *sq_vm_malloc(SQUnsignedInteger size){ return malloc(size); } + +void *sq_vm_realloc(void *p, SQUnsignedInteger SQ_UNUSED_ARG(oldsize), SQUnsignedInteger size){ return realloc(p, size); } + +void sq_vm_free(void *p, SQUnsignedInteger SQ_UNUSED_ARG(size)){ free(p); } +#endif diff --git a/mp/src/vscript/squirrel/squirrel/sqobject.cpp b/mp/src/vscript/squirrel/squirrel/sqobject.cpp new file mode 100644 index 00000000..7d7f297f --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqobject.cpp @@ -0,0 +1,677 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqstring.h" +#include "sqarray.h" +#include "sqtable.h" +#include "squserdata.h" +#include "sqfuncproto.h" +#include "sqclass.h" +#include "sqclosure.h" + + +const SQChar *IdType2Name(SQObjectType type) +{ + switch(_RAW_TYPE(type)) + { + case _RT_NULL:return _SC("null"); + case _RT_INTEGER:return _SC("integer"); + case _RT_FLOAT:return _SC("float"); + case _RT_BOOL:return _SC("bool"); + case _RT_STRING:return _SC("string"); + case _RT_TABLE:return _SC("table"); + case _RT_ARRAY:return _SC("array"); + case _RT_GENERATOR:return _SC("generator"); + case _RT_CLOSURE: + case _RT_NATIVECLOSURE: + return _SC("function"); + case _RT_USERDATA: + case _RT_USERPOINTER: + return _SC("userdata"); + case _RT_THREAD: return _SC("thread"); + case _RT_FUNCPROTO: return _SC("function"); + case _RT_CLASS: return _SC("class"); + case _RT_INSTANCE: return _SC("instance"); + case _RT_WEAKREF: return _SC("weakref"); + case _RT_OUTER: return _SC("outer"); + default: + return NULL; + } +} + +const SQChar *GetTypeName(const SQObjectPtr &obj1) +{ + return IdType2Name(sq_type(obj1)); +} + +SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len) +{ + SQString *str=ADD_STRING(ss,s,len); + return str; +} + +void SQString::Release() +{ + REMOVE_STRING(_sharedstate,this); +} + +SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) +{ + SQInteger idx = (SQInteger)TranslateIndex(refpos); + while(idx < _len){ + outkey = (SQInteger)idx; + outval = (SQInteger)((SQUnsignedInteger)_val[idx]); + //return idx for the next iteration + return ++idx; + } + //nothing to iterate anymore + return -1; +} + +SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx) +{ + switch(sq_type(idx)){ + case OT_NULL: + return 0; + case OT_INTEGER: + return (SQUnsignedInteger)_integer(idx); + default: assert(0); break; + } + return 0; +} + +SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type) +{ + if(!_weakref) { + sq_new(_weakref,SQWeakRef); +#if defined(SQUSEDOUBLE) && !defined(_SQ64) + _weakref->_obj._unVal.raw = 0; //clean the whole union on 32 bits with double +#endif + _weakref->_obj._type = type; + _weakref->_obj._unVal.pRefCounted = this; + } + return _weakref; +} + +SQRefCounted::~SQRefCounted() +{ + if(_weakref) { + _weakref->_obj._type = OT_NULL; + _weakref->_obj._unVal.pRefCounted = NULL; + } +} + +void SQWeakRef::Release() { + if(ISREFCOUNTED(_obj._type)) { + _obj._unVal.pRefCounted->_weakref = NULL; + } + sq_delete(this,SQWeakRef); +} + +bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) { + if(_delegate) { + return _delegate->Get((*_ss(v)->_metamethods)[mm],res); + } + return false; +} + +bool SQDelegable::SetDelegate(SQTable *mt) +{ + SQTable *temp = mt; + if(temp == this) return false; + while (temp) { + if (temp->_delegate == this) return false; //cycle detected + temp = temp->_delegate; + } + if (mt) __ObjAddRef(mt); + __ObjRelease(_delegate); + _delegate = mt; + return true; +} + +bool SQGenerator::Yield(SQVM *v,SQInteger target) +{ + if(_state==eSuspended) { v->Raise_Error(_SC("internal vm error, yielding dead generator")); return false;} + if(_state==eDead) { v->Raise_Error(_SC("internal vm error, yielding a dead generator")); return false; } + SQInteger size = v->_top-v->_stackbase; + + _stack.resize(size); + SQObject _this = v->_stack[v->_stackbase]; + _stack._vals[0] = ISREFCOUNTED(sq_type(_this)) ? SQObjectPtr(_refcounted(_this)->GetWeakRef(sq_type(_this))) : _this; + for(SQInteger n =1; n_stack[v->_stackbase+n]; + } + for(SQInteger j =0; j < size; j++) + { + v->_stack[v->_stackbase+j].Null(); + } + + _ci = *v->ci; + _ci._generator=NULL; + for(SQInteger i=0;i<_ci._etraps;i++) { + _etraps.push_back(v->_etraps.top()); + v->_etraps.pop_back(); + // store relative stack base and size in case of resume to other _top + SQExceptionTrap &et = _etraps.back(); + et._stackbase -= v->_stackbase; + et._stacksize -= v->_stackbase; + } + _state=eSuspended; + return true; +} + +bool SQGenerator::Resume(SQVM *v,SQObjectPtr &dest) +{ + if(_state==eDead){ v->Raise_Error(_SC("resuming dead generator")); return false; } + if(_state==eRunning){ v->Raise_Error(_SC("resuming active generator")); return false; } + SQInteger size = _stack.size(); + SQInteger target = &dest - &(v->_stack._vals[v->_stackbase]); + assert(target>=0 && target<=255); + SQInteger newbase = v->_top; + if(!v->EnterFrame(v->_top, v->_top + size, false)) + return false; + v->ci->_generator = this; + v->ci->_target = (SQInt32)target; + v->ci->_closure = _ci._closure; + v->ci->_ip = _ci._ip; + v->ci->_literals = _ci._literals; + v->ci->_ncalls = _ci._ncalls; + v->ci->_etraps = _ci._etraps; + v->ci->_root = _ci._root; + + + for(SQInteger i=0;i<_ci._etraps;i++) { + v->_etraps.push_back(_etraps.top()); + _etraps.pop_back(); + SQExceptionTrap &et = v->_etraps.back(); + // restore absolute stack base and size + et._stackbase += newbase; + et._stacksize += newbase; + } + SQObject _this = _stack._vals[0]; + v->_stack[v->_stackbase] = sq_type(_this) == OT_WEAKREF ? _weakref(_this)->_obj : _this; + + for(SQInteger n = 1; n_stack[v->_stackbase+n] = _stack._vals[n]; + _stack._vals[n].Null(); + } + + _state=eRunning; + if (v->_debughook) + v->CallDebugHook(_SC('c')); + + return true; +} + +void SQArray::Extend(const SQArray *a){ + SQInteger xlen; + if((xlen=a->Size())) + for(SQInteger i=0;i_values[i]); +} + +const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop) +{ + SQUnsignedInteger nvars=_nlocalvarinfos; + const SQChar *res=NULL; + if(nvars>=nseq){ + for(SQUnsignedInteger i=0;i=nop) + { + if(nseq==0){ + vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]); + res=_stringval(_localvarinfos[i]._name); + break; + } + nseq--; + } + } + } + return res; +} + + +SQInteger SQFunctionProto::GetLine(SQInstruction *curr) +{ + SQInteger op = (SQInteger)(curr-_instructions); + SQInteger line=_lineinfos[0]._line; + SQInteger low = 0; + SQInteger high = _nlineinfos - 1; + SQInteger mid = 0; + while(low <= high) + { + mid = low + ((high - low) >> 1); + SQInteger curop = _lineinfos[mid]._op; + if(curop > op) + { + high = mid - 1; + } + else if(curop < op) { + if(mid < (_nlineinfos - 1) + && _lineinfos[mid + 1]._op >= op) { + break; + } + low = mid + 1; + } + else { //equal + break; + } + } + + while(mid > 0 && _lineinfos[mid]._op >= op) mid--; + + line = _lineinfos[mid]._line; + + return line; +} + +SQClosure::~SQClosure() +{ + __ObjRelease(_root); + __ObjRelease(_env); + __ObjRelease(_base); + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); +} + +#define _CHECK_IO(exp) { if(!exp)return false; } +bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size) +{ + if(write(up,dest,size) != size) { + v->Raise_Error(_SC("io error (write function failure)")); + return false; + } + return true; +} + +bool SafeRead(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size) +{ + if(size && read(up,dest,size) != size) { + v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated")); + return false; + } + return true; +} + +bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUnsignedInteger32 tag) +{ + return SafeWrite(v,write,up,&tag,sizeof(tag)); +} + +bool CheckTag(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUnsignedInteger32 tag) +{ + SQUnsignedInteger32 t; + _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t))); + if(t != tag){ + v->Raise_Error(_SC("invalid or corrupted closure stream")); + return false; + } + return true; +} + +bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o) +{ + SQUnsignedInteger32 _type = (SQUnsignedInteger32)sq_type(o); + _CHECK_IO(SafeWrite(v,write,up,&_type,sizeof(_type))); + switch(sq_type(o)){ + case OT_STRING: + _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger))); + _CHECK_IO(SafeWrite(v,write,up,_stringval(o),sq_rsl(_string(o)->_len))); + break; + case OT_BOOL: + case OT_INTEGER: + _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break; + case OT_FLOAT: + _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break; + case OT_NULL: + break; + default: + v->Raise_Error(_SC("cannot serialize a %s"),GetTypeName(o)); + return false; + } + return true; +} + +bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o) +{ + SQUnsignedInteger32 _type; + _CHECK_IO(SafeRead(v,read,up,&_type,sizeof(_type))); + SQObjectType t = (SQObjectType)_type; + switch(t){ + case OT_STRING:{ + SQInteger len; + _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger))); + _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(sq_rsl(len)),sq_rsl(len))); + o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len); + } + break; + case OT_INTEGER:{ + SQInteger i; + _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break; + } + case OT_BOOL:{ + SQInteger i; + _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o._type = OT_BOOL; o._unVal.nInteger = i; break; + } + case OT_FLOAT:{ + SQFloat f; + _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break; + } + case OT_NULL: + o.Null(); + break; + default: + v->Raise_Error(_SC("cannot serialize a %s"),IdType2Name(t)); + return false; + } + return true; +} + +bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) +{ + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD)); + _CHECK_IO(WriteTag(v,write,up,sizeof(SQChar))); + _CHECK_IO(WriteTag(v,write,up,sizeof(SQInteger))); + _CHECK_IO(WriteTag(v,write,up,sizeof(SQFloat))); + _CHECK_IO(_function->Save(v,up,write)); + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL)); + return true; +} + +bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) +{ + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD)); + _CHECK_IO(CheckTag(v,read,up,sizeof(SQChar))); + _CHECK_IO(CheckTag(v,read,up,sizeof(SQInteger))); + _CHECK_IO(CheckTag(v,read,up,sizeof(SQFloat))); + SQObjectPtr func; + _CHECK_IO(SQFunctionProto::Load(v,up,read,func)); + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL)); + ret = SQClosure::Create(_ss(v),_funcproto(func),_table(v->_roottable)->GetWeakRef(OT_TABLE)); + //FIXME: load an root for this closure + return true; +} + +SQFunctionProto::SQFunctionProto(SQSharedState *ss) +{ + _stacksize=0; + _bgenerator=false; + INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); +} + +SQFunctionProto::~SQFunctionProto() +{ + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); +} + +bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) +{ + SQInteger i,nliterals = _nliterals,nparameters = _nparameters; + SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos; + SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions; + SQInteger ndefaultparams = _ndefaultparams; + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(WriteObject(v,up,write,_sourcename)); + _CHECK_IO(WriteObject(v,up,write,_name)); + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals))); + _CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters))); + _CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues))); + _CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos))); + _CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos))); + _CHECK_IO(SafeWrite(v,write,up,&ndefaultparams,sizeof(ndefaultparams))); + _CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions))); + _CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions))); + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); + for(i=0;iSave(v,up,write)); + } + _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize))); + _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator))); + _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams))); + return true; +} + +bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) +{ + SQInteger i, nliterals,nparameters; + SQInteger noutervalues ,nlocalvarinfos ; + SQInteger nlineinfos,ninstructions ,nfunctions,ndefaultparams ; + SQObjectPtr sourcename, name; + SQObjectPtr o; + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(ReadObject(v, up, read, sourcename)); + _CHECK_IO(ReadObject(v, up, read, name)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals))); + _CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters))); + _CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues))); + _CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos))); + _CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos))); + _CHECK_IO(SafeRead(v,read,up, &ndefaultparams, sizeof(ndefaultparams))); + _CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions))); + _CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions))); + + + SQFunctionProto *f = SQFunctionProto::Create(_opt_ss(v),ninstructions,nliterals,nparameters, + nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams); + SQObjectPtr proto = f; //gets a ref in case of failure + f->_sourcename = sourcename; + f->_name = name; + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0;i < nliterals; i++){ + _CHECK_IO(ReadObject(v, up, read, o)); + f->_literals[i] = o; + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0; i < nparameters; i++){ + _CHECK_IO(ReadObject(v, up, read, o)); + f->_parameters[i] = o; + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0; i < noutervalues; i++){ + SQUnsignedInteger type; + SQObjectPtr name; + _CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger))); + _CHECK_IO(ReadObject(v, up, read, o)); + _CHECK_IO(ReadObject(v, up, read, name)); + f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type); + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0; i < nlocalvarinfos; i++){ + SQLocalVarInfo lvi; + _CHECK_IO(ReadObject(v, up, read, lvi._name)); + _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger))); + _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger))); + _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger))); + f->_localvarinfos[i] = lvi; + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, f->_lineinfos, sizeof(SQLineInfo)*nlineinfos)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, f->_defaultparams, sizeof(SQInteger)*ndefaultparams)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + for(i = 0; i < nfunctions; i++){ + _CHECK_IO(_funcproto(o)->Load(v, up, read, o)); + f->_functions[i] = o; + } + _CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize))); + _CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator))); + _CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams))); + + ret = f; + return true; +} + +#ifndef NO_GARBAGE_COLLECTOR + +#define START_MARK() if(!(_uiRef&MARK_FLAG)){ \ + _uiRef|=MARK_FLAG; + +#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \ + AddToChain(chain, this); } + +void SQVM::Mark(SQCollectable **chain) +{ + START_MARK() + SQSharedState::MarkObject(_lasterror,chain); + SQSharedState::MarkObject(_errorhandler,chain); + SQSharedState::MarkObject(_debughook_closure,chain); + SQSharedState::MarkObject(_roottable, chain); + SQSharedState::MarkObject(temp_reg, chain); + for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); + for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain); + END_MARK() +} + +void SQArray::Mark(SQCollectable **chain) +{ + START_MARK() + SQInteger len = _values.size(); + for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain); + END_MARK() +} +void SQTable::Mark(SQCollectable **chain) +{ + START_MARK() + if(_delegate) _delegate->Mark(chain); + SQInteger len = _numofnodes; + for(SQInteger i = 0; i < len; i++){ + SQSharedState::MarkObject(_nodes[i].key, chain); + SQSharedState::MarkObject(_nodes[i].val, chain); + } + END_MARK() +} + +void SQClass::Mark(SQCollectable **chain) +{ + START_MARK() + _members->Mark(chain); + if(_base) _base->Mark(chain); + SQSharedState::MarkObject(_attributes, chain); + for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { + SQSharedState::MarkObject(_defaultvalues[i].val, chain); + SQSharedState::MarkObject(_defaultvalues[i].attrs, chain); + } + for(SQUnsignedInteger j =0; j< _methods.size(); j++) { + SQSharedState::MarkObject(_methods[j].val, chain); + SQSharedState::MarkObject(_methods[j].attrs, chain); + } + for(SQUnsignedInteger k =0; k< MT_LAST; k++) { + SQSharedState::MarkObject(_metamethods[k], chain); + } + END_MARK() +} + +void SQInstance::Mark(SQCollectable **chain) +{ + START_MARK() + _class->Mark(chain); + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger i =0; i< nvalues; i++) { + SQSharedState::MarkObject(_values[i], chain); + } + END_MARK() +} + +void SQGenerator::Mark(SQCollectable **chain) +{ + START_MARK() + for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); + SQSharedState::MarkObject(_closure, chain); + END_MARK() +} + +void SQFunctionProto::Mark(SQCollectable **chain) +{ + START_MARK() + for(SQInteger i = 0; i < _nliterals; i++) SQSharedState::MarkObject(_literals[i], chain); + for(SQInteger k = 0; k < _nfunctions; k++) SQSharedState::MarkObject(_functions[k], chain); + END_MARK() +} + +void SQClosure::Mark(SQCollectable **chain) +{ + START_MARK() + if(_base) _base->Mark(chain); + SQFunctionProto *fp = _function; + fp->Mark(chain); + for(SQInteger i = 0; i < fp->_noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); + for(SQInteger k = 0; k < fp->_ndefaultparams; k++) SQSharedState::MarkObject(_defaultparams[k], chain); + END_MARK() +} + +void SQNativeClosure::Mark(SQCollectable **chain) +{ + START_MARK() + for(SQUnsignedInteger i = 0; i < _noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); + END_MARK() +} + +void SQOuter::Mark(SQCollectable **chain) +{ + START_MARK() + /* If the valptr points to a closed value, that value is alive */ + if(_valptr == &_value) { + SQSharedState::MarkObject(_value, chain); + } + END_MARK() +} + +void SQUserData::Mark(SQCollectable **chain){ + START_MARK() + if(_delegate) _delegate->Mark(chain); + END_MARK() +} + +void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; } + +#endif + diff --git a/mp/src/vscript/squirrel/squirrel/sqobject.h b/mp/src/vscript/squirrel/squirrel/sqobject.h new file mode 100644 index 00000000..a2022227 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqobject.h @@ -0,0 +1,353 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQOBJECT_H_ +#define _SQOBJECT_H_ + +#include "squtils.h" + +#ifdef _SQ64 +#define UINT_MINUS_ONE (0xFFFFFFFFFFFFFFFF) +#else +#define UINT_MINUS_ONE (0xFFFFFFFF) +#endif + +#define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) +#define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T')) +#define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L')) + +struct SQSharedState; + +enum SQMetaMethod{ + MT_ADD=0, + MT_SUB=1, + MT_MUL=2, + MT_DIV=3, + MT_UNM=4, + MT_MODULO=5, + MT_SET=6, + MT_GET=7, + MT_TYPEOF=8, + MT_NEXTI=9, + MT_CMP=10, + MT_CALL=11, + MT_CLONED=12, + MT_NEWSLOT=13, + MT_DELSLOT=14, + MT_TOSTRING=15, + MT_NEWMEMBER=16, + MT_INHERITED=17, + MT_LAST = 18 +}; + +#define MM_ADD _SC("_add") +#define MM_SUB _SC("_sub") +#define MM_MUL _SC("_mul") +#define MM_DIV _SC("_div") +#define MM_UNM _SC("_unm") +#define MM_MODULO _SC("_modulo") +#define MM_SET _SC("_set") +#define MM_GET _SC("_get") +#define MM_TYPEOF _SC("_typeof") +#define MM_NEXTI _SC("_nexti") +#define MM_CMP _SC("_cmp") +#define MM_CALL _SC("_call") +#define MM_CLONED _SC("_cloned") +#define MM_NEWSLOT _SC("_newslot") +#define MM_DELSLOT _SC("_delslot") +#define MM_TOSTRING _SC("_tostring") +#define MM_NEWMEMBER _SC("_newmember") +#define MM_INHERITED _SC("_inherited") + + +#define _CONSTRUCT_VECTOR(type,size,ptr) { \ + for(SQInteger n = 0; n < ((SQInteger)size); n++) { \ + new (&ptr[n]) type(); \ + } \ +} + +#define _DESTRUCT_VECTOR(type,size,ptr) { \ + for(SQInteger nl = 0; nl < ((SQInteger)size); nl++) { \ + ptr[nl].~type(); \ + } \ +} + +#define _COPY_VECTOR(dest,src,size) { \ + for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ + dest[_n_] = src[_n_]; \ + } \ +} + +#define _NULL_SQOBJECT_VECTOR(vec,size) { \ + for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ + vec[_n_].Null(); \ + } \ +} + +#define MINPOWER2 4 + +struct SQRefCounted +{ + SQUnsignedInteger _uiRef; + struct SQWeakRef *_weakref; + SQRefCounted() { _uiRef = 0; _weakref = NULL; } + virtual ~SQRefCounted(); + SQWeakRef *GetWeakRef(SQObjectType type); + virtual void Release()=0; + +}; + +struct SQWeakRef : SQRefCounted +{ + void Release(); + SQObject _obj; +}; + +#define _realval(o) (sq_type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj) + +struct SQObjectPtr; + +#define __AddRef(type,unval) if(ISREFCOUNTED(type)) \ + { \ + unval.pRefCounted->_uiRef++; \ + } + +#define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)==0)) \ + { \ + unval.pRefCounted->Release(); \ + } + +#define __ObjRelease(obj) { \ + if((obj)) { \ + (obj)->_uiRef--; \ + if((obj)->_uiRef == 0) \ + (obj)->Release(); \ + (obj) = NULL; \ + } \ +} + +#define __ObjAddRef(obj) { \ + (obj)->_uiRef++; \ +} + +#define is_delegable(t) (sq_type(t)&SQOBJECT_DELEGABLE) +#define raw_type(obj) _RAW_TYPE((obj)._type) + +#define _integer(obj) ((obj)._unVal.nInteger) +#define _float(obj) ((obj)._unVal.fFloat) +#define _string(obj) ((obj)._unVal.pString) +#define _table(obj) ((obj)._unVal.pTable) +#define _array(obj) ((obj)._unVal.pArray) +#define _closure(obj) ((obj)._unVal.pClosure) +#define _generator(obj) ((obj)._unVal.pGenerator) +#define _nativeclosure(obj) ((obj)._unVal.pNativeClosure) +#define _userdata(obj) ((obj)._unVal.pUserData) +#define _userpointer(obj) ((obj)._unVal.pUserPointer) +#define _thread(obj) ((obj)._unVal.pThread) +#define _funcproto(obj) ((obj)._unVal.pFunctionProto) +#define _class(obj) ((obj)._unVal.pClass) +#define _instance(obj) ((obj)._unVal.pInstance) +#define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable) +#define _weakref(obj) ((obj)._unVal.pWeakRef) +#define _outer(obj) ((obj)._unVal.pOuter) +#define _refcounted(obj) ((obj)._unVal.pRefCounted) +#define _rawval(obj) ((obj)._unVal.raw) + +#define _stringval(obj) (obj)._unVal.pString->_val +#define _userdataval(obj) ((SQUserPointer)sq_aligning((obj)._unVal.pUserData + 1)) + +#define tofloat(num) ((sq_type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) +#define tointeger(num) ((sq_type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num)) +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +#if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) +#define SQ_REFOBJECT_INIT() SQ_OBJECT_RAWINIT() +#else +#define SQ_REFOBJECT_INIT() +#endif + +#define _REF_TYPE_DECL(type,_class,sym) \ + SQObjectPtr(_class * x) \ + { \ + SQ_OBJECT_RAWINIT() \ + _type=type; \ + _unVal.sym = x; \ + assert(_unVal.pTable); \ + _unVal.pRefCounted->_uiRef++; \ + } \ + inline SQObjectPtr& operator=(_class *x) \ + { \ + SQObjectType tOldType; \ + SQObjectValue unOldVal; \ + tOldType=_type; \ + unOldVal=_unVal; \ + _type = type; \ + SQ_REFOBJECT_INIT() \ + _unVal.sym = x; \ + _unVal.pRefCounted->_uiRef++; \ + __Release(tOldType,unOldVal); \ + return *this; \ + } + +#define _SCALAR_TYPE_DECL(type,_class,sym) \ + SQObjectPtr(_class x) \ + { \ + SQ_OBJECT_RAWINIT() \ + _type=type; \ + _unVal.sym = x; \ + } \ + inline SQObjectPtr& operator=(_class x) \ + { \ + __Release(_type,_unVal); \ + _type = type; \ + SQ_OBJECT_RAWINIT() \ + _unVal.sym = x; \ + return *this; \ + } +struct SQObjectPtr : public SQObject +{ + SQObjectPtr() + { + SQ_OBJECT_RAWINIT() + _type=OT_NULL; + _unVal.pUserPointer=NULL; + } + SQObjectPtr(const SQObjectPtr &o) + { + _type = o._type; + _unVal = o._unVal; + __AddRef(_type,_unVal); + } + SQObjectPtr(const SQObject &o) + { + _type = o._type; + _unVal = o._unVal; + __AddRef(_type,_unVal); + } + _REF_TYPE_DECL(OT_TABLE,SQTable,pTable) + _REF_TYPE_DECL(OT_CLASS,SQClass,pClass) + _REF_TYPE_DECL(OT_INSTANCE,SQInstance,pInstance) + _REF_TYPE_DECL(OT_ARRAY,SQArray,pArray) + _REF_TYPE_DECL(OT_CLOSURE,SQClosure,pClosure) + _REF_TYPE_DECL(OT_NATIVECLOSURE,SQNativeClosure,pNativeClosure) + _REF_TYPE_DECL(OT_OUTER,SQOuter,pOuter) + _REF_TYPE_DECL(OT_GENERATOR,SQGenerator,pGenerator) + _REF_TYPE_DECL(OT_STRING,SQString,pString) + _REF_TYPE_DECL(OT_USERDATA,SQUserData,pUserData) + _REF_TYPE_DECL(OT_WEAKREF,SQWeakRef,pWeakRef) + _REF_TYPE_DECL(OT_THREAD,SQVM,pThread) + _REF_TYPE_DECL(OT_FUNCPROTO,SQFunctionProto,pFunctionProto) + + _SCALAR_TYPE_DECL(OT_INTEGER,SQInteger,nInteger) + _SCALAR_TYPE_DECL(OT_FLOAT,SQFloat,fFloat) + _SCALAR_TYPE_DECL(OT_USERPOINTER,SQUserPointer,pUserPointer) + + SQObjectPtr(bool bBool) + { + SQ_OBJECT_RAWINIT() + _type = OT_BOOL; + _unVal.nInteger = bBool?1:0; + } + inline SQObjectPtr& operator=(bool b) + { + __Release(_type,_unVal); + SQ_OBJECT_RAWINIT() + _type = OT_BOOL; + _unVal.nInteger = b?1:0; + return *this; + } + + ~SQObjectPtr() + { + __Release(_type,_unVal); + } + + inline SQObjectPtr& operator=(const SQObjectPtr& obj) + { + SQObjectType tOldType; + SQObjectValue unOldVal; + tOldType=_type; + unOldVal=_unVal; + _unVal = obj._unVal; + _type = obj._type; + __AddRef(_type,_unVal); + __Release(tOldType,unOldVal); + return *this; + } + inline SQObjectPtr& operator=(const SQObject& obj) + { + SQObjectType tOldType; + SQObjectValue unOldVal; + tOldType=_type; + unOldVal=_unVal; + _unVal = obj._unVal; + _type = obj._type; + __AddRef(_type,_unVal); + __Release(tOldType,unOldVal); + return *this; + } + inline void Null() + { + SQObjectType tOldType = _type; + SQObjectValue unOldVal = _unVal; + _type = OT_NULL; + _unVal.raw = (SQRawObjectVal)NULL; + __Release(tOldType ,unOldVal); + } + private: + SQObjectPtr(const SQChar *){} //safety +}; + + +inline void _Swap(SQObject &a,SQObject &b) +{ + SQObjectType tOldType = a._type; + SQObjectValue unOldVal = a._unVal; + a._type = b._type; + a._unVal = b._unVal; + b._type = tOldType; + b._unVal = unOldVal; +} + +///////////////////////////////////////////////////////////////////////////////////// +#ifndef NO_GARBAGE_COLLECTOR +#define MARK_FLAG 0x80000000 +struct SQCollectable : public SQRefCounted { + SQCollectable *_next; + SQCollectable *_prev; + SQSharedState *_sharedstate; + virtual SQObjectType GetType()=0; + virtual void Release()=0; + virtual void Mark(SQCollectable **chain)=0; + void UnMark(); + virtual void Finalize()=0; + static void AddToChain(SQCollectable **chain,SQCollectable *c); + static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); +}; + + +#define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj) +#define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);} +#define CHAINABLE_OBJ SQCollectable +#define INIT_CHAIN() {_next=NULL;_prev=NULL;_sharedstate=ss;} +#else + +#define ADD_TO_CHAIN(chain,obj) ((void)0) +#define REMOVE_FROM_CHAIN(chain,obj) ((void)0) +#define CHAINABLE_OBJ SQRefCounted +#define INIT_CHAIN() ((void)0) +#endif + +struct SQDelegable : public CHAINABLE_OBJ { + bool SetDelegate(SQTable *m); + virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); + SQTable *_delegate; +}; + +SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx); +typedef sqvector SQObjectPtrVec; +typedef sqvector SQIntVec; +const SQChar *GetTypeName(const SQObjectPtr &obj1); +const SQChar *IdType2Name(SQObjectType type); + + + +#endif //_SQOBJECT_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqopcodes.h b/mp/src/vscript/squirrel/squirrel/sqopcodes.h new file mode 100644 index 00000000..15d80e4b --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqopcodes.h @@ -0,0 +1,132 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQOPCODES_H_ +#define _SQOPCODES_H_ + +#define MAX_FUNC_STACKSIZE 0xFF +#define MAX_LITERALS ((SQInteger)0x7FFFFFFF) + +enum BitWiseOP { + BW_AND = 0, + BW_OR = 2, + BW_XOR = 3, + BW_SHIFTL = 4, + BW_SHIFTR = 5, + BW_USHIFTR = 6 +}; + +enum CmpOP { + CMP_G = 0, + CMP_GE = 2, + CMP_L = 3, + CMP_LE = 4, + CMP_3W = 5 +}; + +enum NewObjectType { + NOT_TABLE = 0, + NOT_ARRAY = 1, + NOT_CLASS = 2 +}; + +enum AppendArrayType { + AAT_STACK = 0, + AAT_LITERAL = 1, + AAT_INT = 2, + AAT_FLOAT = 3, + AAT_BOOL = 4 +}; + +enum SQOpcode +{ + _OP_LINE= 0x00, + _OP_LOAD= 0x01, + _OP_LOADINT= 0x02, + _OP_LOADFLOAT= 0x03, + _OP_DLOAD= 0x04, + _OP_TAILCALL= 0x05, + _OP_CALL= 0x06, + _OP_PREPCALL= 0x07, + _OP_PREPCALLK= 0x08, + _OP_GETK= 0x09, + _OP_MOVE= 0x0A, + _OP_NEWSLOT= 0x0B, + _OP_DELETE= 0x0C, + _OP_SET= 0x0D, + _OP_GET= 0x0E, + _OP_EQ= 0x0F, + _OP_NE= 0x10, + _OP_ADD= 0x11, + _OP_SUB= 0x12, + _OP_MUL= 0x13, + _OP_DIV= 0x14, + _OP_MOD= 0x15, + _OP_BITW= 0x16, + _OP_RETURN= 0x17, + _OP_LOADNULLS= 0x18, + _OP_LOADROOT= 0x19, + _OP_LOADBOOL= 0x1A, + _OP_DMOVE= 0x1B, + _OP_JMP= 0x1C, + //_OP_JNZ= 0x1D, + _OP_JCMP= 0x1D, + _OP_JZ= 0x1E, + _OP_SETOUTER= 0x1F, + _OP_GETOUTER= 0x20, + _OP_NEWOBJ= 0x21, + _OP_APPENDARRAY= 0x22, + _OP_COMPARITH= 0x23, + _OP_INC= 0x24, + _OP_INCL= 0x25, + _OP_PINC= 0x26, + _OP_PINCL= 0x27, + _OP_CMP= 0x28, + _OP_EXISTS= 0x29, + _OP_INSTANCEOF= 0x2A, + _OP_AND= 0x2B, + _OP_OR= 0x2C, + _OP_NEG= 0x2D, + _OP_NOT= 0x2E, + _OP_BWNOT= 0x2F, + _OP_CLOSURE= 0x30, + _OP_YIELD= 0x31, + _OP_RESUME= 0x32, + _OP_FOREACH= 0x33, + _OP_POSTFOREACH= 0x34, + _OP_CLONE= 0x35, + _OP_TYPEOF= 0x36, + _OP_PUSHTRAP= 0x37, + _OP_POPTRAP= 0x38, + _OP_THROW= 0x39, + _OP_NEWSLOTA= 0x3A, + _OP_GETBASE= 0x3B, + _OP_CLOSE= 0x3C +}; + +struct SQInstructionDesc { + const SQChar *name; +}; + +struct SQInstruction +{ + SQInstruction(){}; + SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0) + { op = (unsigned char)_op; + _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1; + _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; + } + + + SQInt32 _arg1; + unsigned char op; + unsigned char _arg0; + unsigned char _arg2; + unsigned char _arg3; +}; + +#include "squtils.h" +typedef sqvector SQInstructionVec; + +#define NEW_SLOT_ATTRIBUTES_FLAG 0x01 +#define NEW_SLOT_STATIC_FLAG 0x02 + +#endif // _SQOPCODES_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqpcheader.h b/mp/src/vscript/squirrel/squirrel/sqpcheader.h new file mode 100644 index 00000000..8df5ef4c --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqpcheader.h @@ -0,0 +1,20 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQPCHEADER_H_ +#define _SQPCHEADER_H_ + +#if defined(_MSC_VER) && defined(_DEBUG) +#include +#endif + +#include +#include +#include +#include +#include +#include +//squirrel stuff +#include +#include "sqobject.h" +#include "sqstate.h" + +#endif //_SQPCHEADER_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqstate.cpp b/mp/src/vscript/squirrel/squirrel/sqstate.cpp new file mode 100644 index 00000000..c89bdc4a --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqstate.cpp @@ -0,0 +1,647 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqopcodes.h" +#include "sqvm.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqstring.h" +#include "sqtable.h" +#include "sqarray.h" +#include "squserdata.h" +#include "sqclass.h" + +SQSharedState::SQSharedState() +{ + _compilererrorhandler = NULL; + _printfunc = NULL; + _errorfunc = NULL; + _debuginfo = false; + _notifyallexceptions = false; + _foreignptr = NULL; + _releasehook = NULL; +} + +#define newsysstring(s) { \ + _systemstrings->push_back(SQString::Create(this,s)); \ + } + +#define newmetamethod(s) { \ + _metamethods->push_back(SQString::Create(this,s)); \ + _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \ + } + +bool CompileTypemask(SQIntVec &res,const SQChar *typemask) +{ + SQInteger i = 0; + SQInteger mask = 0; + while(typemask[i] != 0) { + switch(typemask[i]) { + case 'o': mask |= _RT_NULL; break; + case 'i': mask |= _RT_INTEGER; break; + case 'f': mask |= _RT_FLOAT; break; + case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break; + case 's': mask |= _RT_STRING; break; + case 't': mask |= _RT_TABLE; break; + case 'a': mask |= _RT_ARRAY; break; + case 'u': mask |= _RT_USERDATA; break; + case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break; + case 'b': mask |= _RT_BOOL; break; + case 'g': mask |= _RT_GENERATOR; break; + case 'p': mask |= _RT_USERPOINTER; break; + case 'v': mask |= _RT_THREAD; break; + case 'x': mask |= _RT_INSTANCE; break; + case 'y': mask |= _RT_CLASS; break; + case 'r': mask |= _RT_WEAKREF; break; + case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; + case ' ': i++; continue; //ignores spaces + default: + return false; + } + i++; + if(typemask[i] == '|') { + i++; + if(typemask[i] == 0) + return false; + continue; + } + res.push_back(mask); + mask = 0; + + } + return true; +} + +SQTable *CreateDefaultDelegate(SQSharedState *ss,const SQRegFunction *funcz) +{ + SQInteger i=0; + SQTable *t=SQTable::Create(ss,0); + while(funcz[i].name!=0){ + SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f,0); + nc->_nparamscheck = funcz[i].nparamscheck; + nc->_name = SQString::Create(ss,funcz[i].name); + if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask)) + return NULL; + t->NewSlot(SQString::Create(ss,funcz[i].name),nc); + i++; + } + return t; +} + +void SQSharedState::Init() +{ + _scratchpad=NULL; + _scratchpadsize=0; +#ifndef NO_GARBAGE_COLLECTOR + _gc_chain=NULL; +#endif + _stringtable = (SQStringTable*)SQ_MALLOC(sizeof(SQStringTable)); + new (_stringtable) SQStringTable(this); + sq_new(_metamethods,SQObjectPtrVec); + sq_new(_systemstrings,SQObjectPtrVec); + sq_new(_types,SQObjectPtrVec); + _metamethodsmap = SQTable::Create(this,MT_LAST-1); + //adding type strings to avoid memory trashing + //types names + newsysstring(_SC("null")); + newsysstring(_SC("table")); + newsysstring(_SC("array")); + newsysstring(_SC("closure")); + newsysstring(_SC("string")); + newsysstring(_SC("userdata")); + newsysstring(_SC("integer")); + newsysstring(_SC("float")); + newsysstring(_SC("userpointer")); + newsysstring(_SC("function")); + newsysstring(_SC("generator")); + newsysstring(_SC("thread")); + newsysstring(_SC("class")); + newsysstring(_SC("instance")); + newsysstring(_SC("bool")); + //meta methods + newmetamethod(MM_ADD); + newmetamethod(MM_SUB); + newmetamethod(MM_MUL); + newmetamethod(MM_DIV); + newmetamethod(MM_UNM); + newmetamethod(MM_MODULO); + newmetamethod(MM_SET); + newmetamethod(MM_GET); + newmetamethod(MM_TYPEOF); + newmetamethod(MM_NEXTI); + newmetamethod(MM_CMP); + newmetamethod(MM_CALL); + newmetamethod(MM_CLONED); + newmetamethod(MM_NEWSLOT); + newmetamethod(MM_DELSLOT); + newmetamethod(MM_TOSTRING); + newmetamethod(MM_NEWMEMBER); + newmetamethod(MM_INHERITED); + + _constructoridx = SQString::Create(this,_SC("constructor")); + _registry = SQTable::Create(this,0); + _consts = SQTable::Create(this,0); + _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz); + _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz); + _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz); + _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz); + _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz); + _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz); + _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz); + _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz); + _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz); + _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz); +} + +SQSharedState::~SQSharedState() +{ + if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } + _constructoridx.Null(); + _table(_registry)->Finalize(); + _table(_consts)->Finalize(); + _table(_metamethodsmap)->Finalize(); + _registry.Null(); + _consts.Null(); + _metamethodsmap.Null(); + while(!_systemstrings->empty()) { + _systemstrings->back().Null(); + _systemstrings->pop_back(); + } + _thread(_root_vm)->Finalize(); + _root_vm.Null(); + _table_default_delegate.Null(); + _array_default_delegate.Null(); + _string_default_delegate.Null(); + _number_default_delegate.Null(); + _closure_default_delegate.Null(); + _generator_default_delegate.Null(); + _thread_default_delegate.Null(); + _class_default_delegate.Null(); + _instance_default_delegate.Null(); + _weakref_default_delegate.Null(); + _refs_table.Finalize(); +#ifndef NO_GARBAGE_COLLECTOR + SQCollectable *t = _gc_chain; + SQCollectable *nx = NULL; + if(t) { + t->_uiRef++; + while(t) { + t->Finalize(); + nx = t->_next; + if(nx) nx->_uiRef++; + if(--t->_uiRef == 0) + t->Release(); + t = nx; + } + } + assert(_gc_chain==NULL); //just to proove a theory + while(_gc_chain){ + _gc_chain->_uiRef++; + _gc_chain->Release(); + } +#endif + + sq_delete(_types,SQObjectPtrVec); + sq_delete(_systemstrings,SQObjectPtrVec); + sq_delete(_metamethods,SQObjectPtrVec); + sq_delete(_stringtable,SQStringTable); + if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize); +} + + +SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) +{ + if(sq_type(name) != OT_STRING) + return -1; + SQObjectPtr ret; + if(_table(_metamethodsmap)->Get(name,ret)) { + return _integer(ret); + } + return -1; +} + +#ifndef NO_GARBAGE_COLLECTOR + +void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) +{ + switch(sq_type(o)){ + case OT_TABLE:_table(o)->Mark(chain);break; + case OT_ARRAY:_array(o)->Mark(chain);break; + case OT_USERDATA:_userdata(o)->Mark(chain);break; + case OT_CLOSURE:_closure(o)->Mark(chain);break; + case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; + case OT_GENERATOR:_generator(o)->Mark(chain);break; + case OT_THREAD:_thread(o)->Mark(chain);break; + case OT_CLASS:_class(o)->Mark(chain);break; + case OT_INSTANCE:_instance(o)->Mark(chain);break; + case OT_OUTER:_outer(o)->Mark(chain);break; + case OT_FUNCPROTO:_funcproto(o)->Mark(chain);break; + default: break; //shutup compiler + } +} + +void SQSharedState::RunMark(SQVM* SQ_UNUSED_ARG(vm),SQCollectable **tchain) +{ + SQVM *vms = _thread(_root_vm); + + vms->Mark(tchain); + + _refs_table.Mark(tchain); + MarkObject(_registry,tchain); + MarkObject(_consts,tchain); + MarkObject(_metamethodsmap,tchain); + MarkObject(_table_default_delegate,tchain); + MarkObject(_array_default_delegate,tchain); + MarkObject(_string_default_delegate,tchain); + MarkObject(_number_default_delegate,tchain); + MarkObject(_generator_default_delegate,tchain); + MarkObject(_thread_default_delegate,tchain); + MarkObject(_closure_default_delegate,tchain); + MarkObject(_class_default_delegate,tchain); + MarkObject(_instance_default_delegate,tchain); + MarkObject(_weakref_default_delegate,tchain); + +} + +SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) +{ + SQInteger n=0; + SQCollectable *tchain=NULL; + + RunMark(vm,&tchain); + + SQCollectable *resurrected = _gc_chain; + SQCollectable *t = resurrected; + + _gc_chain = tchain; + + SQArray *ret = NULL; + if(resurrected) { + ret = SQArray::Create(this,0); + SQCollectable *rlast = NULL; + while(t) { + rlast = t; + SQObjectType type = t->GetType(); + if(type != OT_FUNCPROTO && type != OT_OUTER) { + SQObject sqo; + sqo._type = type; + sqo._unVal.pRefCounted = t; + ret->Append(sqo); + } + t = t->_next; + n++; + } + + assert(rlast->_next == NULL); + rlast->_next = _gc_chain; + if(_gc_chain) + { + _gc_chain->_prev = rlast; + } + _gc_chain = resurrected; + } + + t = _gc_chain; + while(t) { + t->UnMark(); + t = t->_next; + } + + if(ret) { + SQObjectPtr temp = ret; + vm->Push(temp); + } + else { + vm->PushNull(); + } + return n; +} + +SQInteger SQSharedState::CollectGarbage(SQVM *vm) +{ + SQInteger n = 0; + SQCollectable *tchain = NULL; + + RunMark(vm,&tchain); + + SQCollectable *t = _gc_chain; + SQCollectable *nx = NULL; + if(t) { + t->_uiRef++; + while(t) { + t->Finalize(); + nx = t->_next; + if(nx) nx->_uiRef++; + if(--t->_uiRef == 0) + t->Release(); + t = nx; + n++; + } + } + + t = tchain; + while(t) { + t->UnMark(); + t = t->_next; + } + _gc_chain = tchain; + + return n; +} +#endif + +#ifndef NO_GARBAGE_COLLECTOR +void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c) +{ + c->_prev = NULL; + c->_next = *chain; + if(*chain) (*chain)->_prev = c; + *chain = c; +} + +void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c) +{ + if(c->_prev) c->_prev->_next = c->_next; + else *chain = c->_next; + if(c->_next) + c->_next->_prev = c->_prev; + c->_next = NULL; + c->_prev = NULL; +} +#endif + +SQChar* SQSharedState::GetScratchPad(SQInteger size) +{ + SQInteger newsize; + if(size>0) { + if(_scratchpadsize < size) { + newsize = size + (size>>1); + _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); + _scratchpadsize = newsize; + + }else if(_scratchpadsize >= (size<<5)) { + newsize = _scratchpadsize >> 1; + _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); + _scratchpadsize = newsize; + } + } + return _scratchpad; +} + +RefTable::RefTable() +{ + AllocNodes(4); +} + +void RefTable::Finalize() +{ + RefNode *nodes = _nodes; + for(SQUnsignedInteger n = 0; n < _numofslots; n++) { + nodes->obj.Null(); + nodes++; + } +} + +RefTable::~RefTable() +{ + SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode))); +} + +#ifndef NO_GARBAGE_COLLECTOR +void RefTable::Mark(SQCollectable **chain) +{ + RefNode *nodes = (RefNode *)_nodes; + for(SQUnsignedInteger n = 0; n < _numofslots; n++) { + if(sq_type(nodes->obj) != OT_NULL) { + SQSharedState::MarkObject(nodes->obj,chain); + } + nodes++; + } +} +#endif + +void RefTable::AddRef(SQObject &obj) +{ + SQHash mainpos; + RefNode *prev; + RefNode *ref = Get(obj,mainpos,&prev,true); + ref->refs++; +} + +SQUnsignedInteger RefTable::GetRefCount(SQObject &obj) +{ + SQHash mainpos; + RefNode *prev; + RefNode *ref = Get(obj,mainpos,&prev,true); + return ref->refs; +} + + +SQBool RefTable::Release(SQObject &obj) +{ + SQHash mainpos; + RefNode *prev; + RefNode *ref = Get(obj,mainpos,&prev,false); + if(ref) { + if(--ref->refs == 0) { + SQObjectPtr o = ref->obj; + if(prev) { + prev->next = ref->next; + } + else { + _buckets[mainpos] = ref->next; + } + ref->next = _freelist; + _freelist = ref; + _slotused--; + ref->obj.Null(); + //<>test for shrink? + return SQTrue; + } + } + else { + assert(0); + } + return SQFalse; +} + +void RefTable::Resize(SQUnsignedInteger size) +{ + RefNode **oldbucks = _buckets; + RefNode *t = _nodes; + SQUnsignedInteger oldnumofslots = _numofslots; + AllocNodes(size); + //rehash + SQUnsignedInteger nfound = 0; + for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { + if(sq_type(t->obj) != OT_NULL) { + //add back; + assert(t->refs != 0); + RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); + nn->refs = t->refs; + t->obj.Null(); + nfound++; + } + t++; + } + assert(nfound == oldnumofslots); + SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode))); +} + +RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj) +{ + RefNode *t = _buckets[mainpos]; + RefNode *newnode = _freelist; + newnode->obj = obj; + _buckets[mainpos] = newnode; + _freelist = _freelist->next; + newnode->next = t; + assert(newnode->refs == 0); + _slotused++; + return newnode; +} + +RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add) +{ + RefNode *ref; + mainpos = ::HashObj(obj)&(_numofslots-1); + *prev = NULL; + for (ref = _buckets[mainpos]; ref; ) { + if(_rawval(ref->obj) == _rawval(obj) && sq_type(ref->obj) == sq_type(obj)) + break; + *prev = ref; + ref = ref->next; + } + if(ref == NULL && add) { + if(_numofslots == _slotused) { + assert(_freelist == 0); + Resize(_numofslots*2); + mainpos = ::HashObj(obj)&(_numofslots-1); + } + ref = Add(mainpos,obj); + } + return ref; +} + +void RefTable::AllocNodes(SQUnsignedInteger size) +{ + RefNode **bucks; + RefNode *nodes; + bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode))); + nodes = (RefNode *)&bucks[size]; + RefNode *temp = nodes; + SQUnsignedInteger n; + for(n = 0; n < size - 1; n++) { + bucks[n] = NULL; + temp->refs = 0; + new (&temp->obj) SQObjectPtr; + temp->next = temp+1; + temp++; + } + bucks[n] = NULL; + temp->refs = 0; + new (&temp->obj) SQObjectPtr; + temp->next = NULL; + _freelist = nodes; + _nodes = nodes; + _buckets = bucks; + _slotused = 0; + _numofslots = size; +} +////////////////////////////////////////////////////////////////////////// +//SQStringTable +/* +* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) +* http://www.lua.org/copyright.html#4 +* http://www.lua.org/source/4.0.1/src_lstring.c.html +*/ + +SQStringTable::SQStringTable(SQSharedState *ss) +{ + _sharedstate = ss; + AllocNodes(4); + _slotused = 0; +} + +SQStringTable::~SQStringTable() +{ + SQ_FREE(_strings,sizeof(SQString*)*_numofslots); + _strings = NULL; +} + +void SQStringTable::AllocNodes(SQInteger size) +{ + _numofslots = size; + _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots); + memset(_strings,0,sizeof(SQString*)*_numofslots); +} + +SQString *SQStringTable::Add(const SQChar *news,SQInteger len) +{ + if(len<0) + len = (SQInteger)scstrlen(news); + SQHash newhash = ::_hashstr(news,len); + SQHash h = newhash&(_numofslots-1); + SQString *s; + for (s = _strings[h]; s; s = s->_next){ + if(s->_len == len && (!memcmp(news,s->_val,sq_rsl(len)))) + return s; //found + } + + SQString *t = (SQString *)SQ_MALLOC(sq_rsl(len)+sizeof(SQString)); + new (t) SQString; + t->_sharedstate = _sharedstate; + memcpy(t->_val,news,sq_rsl(len)); + t->_val[len] = _SC('\0'); + t->_len = len; + t->_hash = newhash; + t->_next = _strings[h]; + _strings[h] = t; + _slotused++; + if (_slotused > _numofslots) /* too crowded? */ + Resize(_numofslots*2); + return t; +} + +void SQStringTable::Resize(SQInteger size) +{ + SQInteger oldsize=_numofslots; + SQString **oldtable=_strings; + AllocNodes(size); + for (SQInteger i=0; i_next; + SQHash h = p->_hash&(_numofslots-1); + p->_next = _strings[h]; + _strings[h] = p; + p = next; + } + } + SQ_FREE(oldtable,oldsize*sizeof(SQString*)); +} + +void SQStringTable::Remove(SQString *bs) +{ + SQString *s; + SQString *prev=NULL; + SQHash h = bs->_hash&(_numofslots - 1); + + for (s = _strings[h]; s; ){ + if(s == bs){ + if(prev) + prev->_next = s->_next; + else + _strings[h] = s->_next; + _slotused--; + SQInteger slen = s->_len; + s->~SQString(); + SQ_FREE(s,sizeof(SQString) + sq_rsl(slen)); + return; + } + prev = s; + s = s->_next; + } + assert(0);//if this fail something is wrong +} diff --git a/mp/src/vscript/squirrel/squirrel/sqstate.h b/mp/src/vscript/squirrel/squirrel/sqstate.h new file mode 100644 index 00000000..2cdc8da4 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqstate.h @@ -0,0 +1,136 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTATE_H_ +#define _SQSTATE_H_ + +#include "squtils.h" +#include "sqobject.h" +struct SQString; +struct SQTable; +//max number of character for a printed number +#define NUMBER_MAX_CHAR 50 + +struct SQStringTable +{ + SQStringTable(SQSharedState*ss); + ~SQStringTable(); + SQString *Add(const SQChar *,SQInteger len); + void Remove(SQString *); +private: + void Resize(SQInteger size); + void AllocNodes(SQInteger size); + SQString **_strings; + SQUnsignedInteger _numofslots; + SQUnsignedInteger _slotused; + SQSharedState *_sharedstate; +}; + +struct RefTable { + struct RefNode { + SQObjectPtr obj; + SQUnsignedInteger refs; + struct RefNode *next; + }; + RefTable(); + ~RefTable(); + void AddRef(SQObject &obj); + SQBool Release(SQObject &obj); + SQUnsignedInteger GetRefCount(SQObject &obj); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); +#endif + void Finalize(); +private: + RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add); + RefNode *Add(SQHash mainpos,SQObject &obj); + void Resize(SQUnsignedInteger size); + void AllocNodes(SQUnsignedInteger size); + SQUnsignedInteger _numofslots; + SQUnsignedInteger _slotused; + RefNode *_nodes; + RefNode *_freelist; + RefNode **_buckets; +}; + +#define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len) +#define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr) + +struct SQObjectPtr; + +struct SQSharedState +{ + SQSharedState(); + ~SQSharedState(); + void Init(); +public: + SQChar* GetScratchPad(SQInteger size); + SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); +#ifndef NO_GARBAGE_COLLECTOR + SQInteger CollectGarbage(SQVM *vm); + void RunMark(SQVM *vm,SQCollectable **tchain); + SQInteger ResurrectUnreachable(SQVM *vm); + static void MarkObject(SQObjectPtr &o,SQCollectable **chain); +#endif + SQObjectPtrVec *_metamethods; + SQObjectPtr _metamethodsmap; + SQObjectPtrVec *_systemstrings; + SQObjectPtrVec *_types; + SQStringTable *_stringtable; + RefTable _refs_table; + SQObjectPtr _registry; + SQObjectPtr _consts; + SQObjectPtr _constructoridx; +#ifndef NO_GARBAGE_COLLECTOR + SQCollectable *_gc_chain; +#endif + SQObjectPtr _root_vm; + SQObjectPtr _table_default_delegate; + static const SQRegFunction _table_default_delegate_funcz[]; + SQObjectPtr _array_default_delegate; + static const SQRegFunction _array_default_delegate_funcz[]; + SQObjectPtr _string_default_delegate; + static const SQRegFunction _string_default_delegate_funcz[]; + SQObjectPtr _number_default_delegate; + static const SQRegFunction _number_default_delegate_funcz[]; + SQObjectPtr _generator_default_delegate; + static const SQRegFunction _generator_default_delegate_funcz[]; + SQObjectPtr _closure_default_delegate; + static const SQRegFunction _closure_default_delegate_funcz[]; + SQObjectPtr _thread_default_delegate; + static const SQRegFunction _thread_default_delegate_funcz[]; + SQObjectPtr _class_default_delegate; + static const SQRegFunction _class_default_delegate_funcz[]; + SQObjectPtr _instance_default_delegate; + static const SQRegFunction _instance_default_delegate_funcz[]; + SQObjectPtr _weakref_default_delegate; + static const SQRegFunction _weakref_default_delegate_funcz[]; + + SQCOMPILERERROR _compilererrorhandler; + SQPRINTFUNCTION _printfunc; + SQPRINTFUNCTION _errorfunc; + bool _debuginfo; + bool _notifyallexceptions; + SQUserPointer _foreignptr; + SQRELEASEHOOK _releasehook; +private: + SQChar *_scratchpad; + SQInteger _scratchpadsize; +}; + +#define _sp(s) (_sharedstate->GetScratchPad(s)) +#define _spval (_sharedstate->GetScratchPad(-1)) + +#define _table_ddel _table(_sharedstate->_table_default_delegate) +#define _array_ddel _table(_sharedstate->_array_default_delegate) +#define _string_ddel _table(_sharedstate->_string_default_delegate) +#define _number_ddel _table(_sharedstate->_number_default_delegate) +#define _generator_ddel _table(_sharedstate->_generator_default_delegate) +#define _closure_ddel _table(_sharedstate->_closure_default_delegate) +#define _thread_ddel _table(_sharedstate->_thread_default_delegate) +#define _class_ddel _table(_sharedstate->_class_default_delegate) +#define _instance_ddel _table(_sharedstate->_instance_default_delegate) +#define _weakref_ddel _table(_sharedstate->_weakref_default_delegate) + +bool CompileTypemask(SQIntVec &res,const SQChar *typemask); + + +#endif //_SQSTATE_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqstring.h b/mp/src/vscript/squirrel/squirrel/sqstring.h new file mode 100644 index 00000000..82f1cdf4 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqstring.h @@ -0,0 +1,31 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTRING_H_ +#define _SQSTRING_H_ + +inline SQHash _hashstr (const SQChar *s, size_t l) +{ + SQHash h = (SQHash)l; /* seed */ + size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */ + for (; l>=step; l-=step) + h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++)); + return h; +} + +struct SQString : public SQRefCounted +{ + SQString(){} + ~SQString(){} +public: + static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 ); + SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); + void Release(); + SQSharedState *_sharedstate; + SQString *_next; //chain for the string table + SQInteger _len; + SQHash _hash; + SQChar _val[1]; +}; + + + +#endif //_SQSTRING_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqtable.cpp b/mp/src/vscript/squirrel/squirrel/sqtable.cpp new file mode 100644 index 00000000..3a89c459 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqtable.cpp @@ -0,0 +1,221 @@ +/* +see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqtable.h" +#include "sqfuncproto.h" +#include "sqclosure.h" + +SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize) +{ + SQInteger pow2size=MINPOWER2; + while(nInitialSize>pow2size)pow2size=pow2size<<1; + AllocNodes(pow2size); + _usednodes = 0; + _delegate = NULL; + INIT_CHAIN(); + ADD_TO_CHAIN(&_sharedstate->_gc_chain,this); +} + +void SQTable::Remove(const SQObjectPtr &key) +{ + + _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); + if (n) { + n->val.Null(); + n->key.Null(); + _usednodes--; + Rehash(false); + } +} + +void SQTable::AllocNodes(SQInteger nSize) +{ + _HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize); + for(SQInteger i=0;i= oldsize-oldsize/4) /* using more than 3/4? */ + AllocNodes(oldsize*2); + else if (nelems <= oldsize/4 && /* less than 1/4? */ + oldsize > MINPOWER2) + AllocNodes(oldsize/2); + else if(force) + AllocNodes(oldsize); + else + return; + _usednodes = 0; + for (SQInteger i=0; ikey) != OT_NULL) + NewSlot(old->key,old->val); + } + for(SQInteger k=0;k_nodes; + _HashNode *src = _nodes; + _HashNode *dst = nt->_nodes; + SQInteger n = 0; + for(n = 0; n < _numofnodes; n++) { + dst->key = src->key; + dst->val = src->val; + if(src->next) { + assert(src->next > basesrc); + dst->next = basedst + (src->next - basesrc); + assert(dst != dst->next); + } + dst++; + src++; + } + assert(_firstfree > basesrc); + assert(_firstfree != NULL); + nt->_firstfree = basedst + (_firstfree - basesrc); + nt->_usednodes = _usednodes; +#else + SQInteger ridx=0; + SQObjectPtr key,val; + while((ridx=Next(true,ridx,key,val))!=-1){ + nt->NewSlot(key,val); + } +#endif + nt->SetDelegate(_delegate); + return nt; +} + +bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val) +{ + if(sq_type(key) == OT_NULL) + return false; + _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); + if (n) { + val = _realval(n->val); + return true; + } + return false; +} +bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val) +{ + assert(sq_type(key) != OT_NULL); + SQHash h = HashObj(key) & (_numofnodes - 1); + _HashNode *n = _Get(key, h); + if (n) { + n->val = val; + return false; + } + _HashNode *mp = &_nodes[h]; + n = mp; + + + //key not found I'll insert it + //main pos is not free + + if(sq_type(mp->key) != OT_NULL) { + n = _firstfree; /* get a free place */ + SQHash mph = HashObj(mp->key) & (_numofnodes - 1); + _HashNode *othern; /* main position of colliding node */ + + if (mp > n && (othern = &_nodes[mph]) != mp){ + /* yes; move colliding node into free position */ + while (othern->next != mp){ + assert(othern->next != NULL); + othern = othern->next; /* find previous */ + } + othern->next = n; /* redo the chain with `n' in place of `mp' */ + n->key = mp->key; + n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */ + n->next = mp->next; + mp->key.Null(); + mp->val.Null(); + mp->next = NULL; /* now `mp' is free */ + } + else{ + /* new node will go into free position */ + n->next = mp->next; /* chain new position */ + mp->next = n; + mp = n; + } + } + mp->key = key; + + for (;;) { /* correct `firstfree' */ + if (sq_type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) { + mp->val = val; + _usednodes++; + return true; /* OK; table still has a free place */ + } + else if (_firstfree == _nodes) break; /* cannot decrement from here */ + else (_firstfree)--; + } + Rehash(true); + return NewSlot(key, val); +} + +SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) +{ + SQInteger idx = (SQInteger)TranslateIndex(refpos); + while (idx < _numofnodes) { + if(sq_type(_nodes[idx].key) != OT_NULL) { + //first found + _HashNode &n = _nodes[idx]; + outkey = n.key; + outval = getweakrefs?(SQObject)n.val:_realval(n.val); + //return idx for the next iteration + return ++idx; + } + ++idx; + } + //nothing to iterate anymore + return -1; +} + + +bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val) +{ + _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); + if (n) { + n->val = val; + return true; + } + return false; +} + +void SQTable::_ClearNodes() +{ + for(SQInteger i = 0;i < _numofnodes; i++) { _HashNode &n = _nodes[i]; n.key.Null(); n.val.Null(); } +} + +void SQTable::Finalize() +{ + _ClearNodes(); + SetDelegate(NULL); +} + +void SQTable::Clear() +{ + _ClearNodes(); + _usednodes = 0; + Rehash(true); +} diff --git a/mp/src/vscript/squirrel/squirrel/sqtable.h b/mp/src/vscript/squirrel/squirrel/sqtable.h new file mode 100644 index 00000000..59db3317 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqtable.h @@ -0,0 +1,110 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQTABLE_H_ +#define _SQTABLE_H_ +/* +* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) +* http://www.lua.org/copyright.html#4 +* http://www.lua.org/source/4.0.1/src_ltable.c.html +*/ + +#include "sqstring.h" + + +#define hashptr(p) ((SQHash)(((SQInteger)p) >> 3)) + +inline SQHash HashObj(const SQObjectPtr &key) +{ + switch(sq_type(key)) { + case OT_STRING: return _string(key)->_hash; + case OT_FLOAT: return (SQHash)((SQInteger)_float(key)); + case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key)); + default: return hashptr(key._unVal.pRefCounted); + } +} + +struct SQTable : public SQDelegable +{ +private: + struct _HashNode + { + _HashNode() { next = NULL; } + SQObjectPtr val; + SQObjectPtr key; + _HashNode *next; + }; + _HashNode *_firstfree; + _HashNode *_nodes; + SQInteger _numofnodes; + SQInteger _usednodes; + +/////////////////////////// + void AllocNodes(SQInteger nSize); + void Rehash(bool force); + SQTable(SQSharedState *ss, SQInteger nInitialSize); + void _ClearNodes(); +public: + static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize) + { + SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable)); + new (newtable) SQTable(ss, nInitialSize); + newtable->_delegate = NULL; + return newtable; + } + void Finalize(); + SQTable *Clone(); + ~SQTable() + { + SetDelegate(NULL); + REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); + for (SQInteger i = 0; i < _numofnodes; i++) _nodes[i].~_HashNode(); + SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode)); + } +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + SQObjectType GetType() {return OT_TABLE;} +#endif + inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash) + { + _HashNode *n = &_nodes[hash]; + do{ + if(_rawval(n->key) == _rawval(key) && sq_type(n->key) == sq_type(key)){ + return n; + } + }while((n = n->next)); + return NULL; + } + //for compiler use + inline bool GetStr(const SQChar* key,SQInteger keylen,SQObjectPtr &val) + { + SQHash hash = _hashstr(key,keylen); + _HashNode *n = &_nodes[hash & (_numofnodes - 1)]; + _HashNode *res = NULL; + do{ + if(sq_type(n->key) == OT_STRING && (scstrcmp(_stringval(n->key),key) == 0)){ + res = n; + break; + } + }while((n = n->next)); + if (res) { + val = _realval(res->val); + return true; + } + return false; + } + bool Get(const SQObjectPtr &key,SQObjectPtr &val); + void Remove(const SQObjectPtr &key); + bool Set(const SQObjectPtr &key, const SQObjectPtr &val); + //returns true if a new slot has been created false if it was already present + bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val); + SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); + + SQInteger CountUsed(){ return _usednodes;} + void Clear(); + void Release() + { + sq_delete(this, SQTable); + } + +}; + +#endif //_SQTABLE_H_ diff --git a/mp/src/vscript/squirrel/squirrel/squirrel.dsp b/mp/src/vscript/squirrel/squirrel/squirrel.dsp new file mode 100644 index 00000000..66a84f7d --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/squirrel.dsp @@ -0,0 +1,302 @@ +# Microsoft Developer Studio Project File - Name="squirrel" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=squirrel - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "squirrel.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "squirrel.mak" CFG="squirrel - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "squirrel - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "squirrel - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "squirrel - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "GARBAGE_COLLECTOR" /YX /FD /c +# ADD BASE RSC /l 0x410 /d "NDEBUG" +# ADD RSC /l 0x410 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\squirrel.lib" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x410 /d "_DEBUG" +# ADD RSC /l 0x410 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\squirrel.lib" + +!ENDIF + +# Begin Target + +# Name "squirrel - Win32 Release" +# Name "squirrel - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sqapi.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqbaselib.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqcompiler.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqdebug.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqfuncstate.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqlexer.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqmem.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqobject.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqstate.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqtable.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqclass.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqvm.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\sqarray.h +# End Source File +# Begin Source File + +SOURCE=.\sqclosure.h +# End Source File +# Begin Source File + +SOURCE=.\sqcompiler.h +# End Source File +# Begin Source File + +SOURCE=.\sqfuncproto.h +# End Source File +# Begin Source File + +SOURCE=.\sqfuncstate.h +# End Source File +# Begin Source File + +SOURCE=.\sqlexer.h +# End Source File +# Begin Source File + +SOURCE=.\sqobject.h +# End Source File +# Begin Source File + +SOURCE=.\sqopcodes.h +# End Source File +# Begin Source File + +SOURCE=.\sqpcheader.h +# End Source File +# Begin Source File + +SOURCE=.\sqstate.h +# End Source File +# Begin Source File + +SOURCE=.\sqstring.h +# End Source File +# Begin Source File + +SOURCE=.\sqtable.h +# End Source File +# Begin Source File + +SOURCE=.\squserdata.h +# End Source File +# Begin Source File + +SOURCE=.\squtils.h +# End Source File +# Begin Source File + +SOURCE=.\sqclass.h +# End Source File +# Begin Source File + +SOURCE=.\sqvm.h +# End Source File +# End Group +# End Target +# End Project diff --git a/mp/src/vscript/squirrel/squirrel/squserdata.h b/mp/src/vscript/squirrel/squirrel/squserdata.h new file mode 100644 index 00000000..ec217313 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/squserdata.h @@ -0,0 +1,40 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQUSERDATA_H_ +#define _SQUSERDATA_H_ + +struct SQUserData : SQDelegable +{ + SQUserData(SQSharedState *ss){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); } + ~SQUserData() + { + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this); + SetDelegate(NULL); + } + static SQUserData* Create(SQSharedState *ss, SQInteger size) + { + SQUserData* ud = (SQUserData*)SQ_MALLOC(sq_aligning(sizeof(SQUserData))+size); + new (ud) SQUserData(ss); + ud->_size = size; + ud->_typetag = 0; + return ud; + } +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){SetDelegate(NULL);} + SQObjectType GetType(){ return OT_USERDATA;} +#endif + void Release() { + if (_hook) _hook((SQUserPointer)sq_aligning(this + 1),_size); + SQInteger tsize = _size; + this->~SQUserData(); + SQ_FREE(this, sq_aligning(sizeof(SQUserData)) + tsize); + } + + + SQInteger _size; + SQRELEASEHOOK _hook; + SQUserPointer _typetag; + //SQChar _val[1]; +}; + +#endif //_SQUSERDATA_H_ diff --git a/mp/src/vscript/squirrel/squirrel/squtils.h b/mp/src/vscript/squirrel/squirrel/squtils.h new file mode 100644 index 00000000..f3e819a5 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/squtils.h @@ -0,0 +1,116 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQUTILS_H_ +#define _SQUTILS_H_ + +void *sq_vm_malloc(SQUnsignedInteger size); +void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size); +void sq_vm_free(void *p,SQUnsignedInteger size); + +#define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;} +#define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));} +#define SQ_MALLOC(__size) sq_vm_malloc((__size)); +#define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size)); +#define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size)); + +#define sq_aligning(v) (((size_t)(v) + (SQ_ALIGNMENT-1)) & (~(SQ_ALIGNMENT-1))) + +//sqvector mini vector class, supports objects by value +template class sqvector +{ +public: + sqvector() + { + _vals = NULL; + _size = 0; + _allocated = 0; + } + sqvector(const sqvector& v) + { + copy(v); + } + void copy(const sqvector& v) + { + if(_size) { + resize(0); //destroys all previous stuff + } + //resize(v._size); + if(v._size > _allocated) { + _realloc(v._size); + } + for(SQUnsignedInteger i = 0; i < v._size; i++) { + new ((void *)&_vals[i]) T(v._vals[i]); + } + _size = v._size; + } + ~sqvector() + { + if(_allocated) { + for(SQUnsignedInteger i = 0; i < _size; i++) + _vals[i].~T(); + SQ_FREE(_vals, (_allocated * sizeof(T))); + } + } + void reserve(SQUnsignedInteger newsize) { _realloc(newsize); } + void resize(SQUnsignedInteger newsize, const T& fill = T()) + { + if(newsize > _allocated) + _realloc(newsize); + if(newsize > _size) { + while(_size < newsize) { + new ((void *)&_vals[_size]) T(fill); + _size++; + } + } + else{ + for(SQUnsignedInteger i = newsize; i < _size; i++) { + _vals[i].~T(); + } + _size = newsize; + } + } + void shrinktofit() { if(_size > 4) { _realloc(_size); } } + T& top() const { return _vals[_size - 1]; } + inline SQUnsignedInteger size() const { return _size; } + bool empty() const { return (_size <= 0); } + inline T &push_back(const T& val = T()) + { + if(_allocated <= _size) + _realloc(_size * 2); + return *(new ((void *)&_vals[_size++]) T(val)); + } + inline void pop_back() + { + _size--; _vals[_size].~T(); + } + void insert(SQUnsignedInteger idx, const T& val) + { + resize(_size + 1); + for(SQUnsignedInteger i = _size - 1; i > idx; i--) { + _vals[i] = _vals[i - 1]; + } + _vals[idx] = val; + } + void remove(SQUnsignedInteger idx) + { + _vals[idx].~T(); + if(idx < (_size - 1)) { + memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - idx - 1)); + } + _size--; + } + SQUnsignedInteger capacity() { return _allocated; } + inline T &back() const { return _vals[_size - 1]; } + inline T& operator[](SQUnsignedInteger pos) const{ return _vals[pos]; } + T* _vals; +private: + void _realloc(SQUnsignedInteger newsize) + { + newsize = (newsize > 0)?newsize:4; + _vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T)); + _allocated = newsize; + } + SQUnsignedInteger _size; + SQUnsignedInteger _allocated; +}; + +#endif //_SQUTILS_H_ diff --git a/mp/src/vscript/squirrel/squirrel/sqvm.cpp b/mp/src/vscript/squirrel/squirrel/sqvm.cpp new file mode 100644 index 00000000..dcf823d7 --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqvm.cpp @@ -0,0 +1,1791 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include +#include "sqopcodes.h" +#include "sqvm.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqstring.h" +#include "sqtable.h" +#include "squserdata.h" +#include "sqarray.h" +#include "sqclass.h" + +#define TOP() (_stack._vals[_top-1]) +#define TARGET _stack._vals[_stackbase+arg0] +#define STK(a) _stack._vals[_stackbase+(a)] + +bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) +{ + SQInteger res; + if((sq_type(o1)| sq_type(o2)) == OT_INTEGER) + { + SQInteger i1 = _integer(o1), i2 = _integer(o2); + switch(op) { + case BW_AND: res = i1 & i2; break; + case BW_OR: res = i1 | i2; break; + case BW_XOR: res = i1 ^ i2; break; + case BW_SHIFTL: res = i1 << i2; break; + case BW_SHIFTR: res = i1 >> i2; break; + case BW_USHIFTR:res = (SQInteger)(*((SQUnsignedInteger*)&i1) >> i2); break; + default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; } + } + } + else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;} + trg = res; + return true; +} + +#define _ARITH_(op,trg,o1,o2) \ +{ \ + SQInteger tmask = sq_type(o1)|sq_type(o2); \ + switch(tmask) { \ + case OT_INTEGER: trg = _integer(o1) op _integer(o2);break; \ + case (OT_FLOAT|OT_INTEGER): \ + case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\ + default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ + } \ +} + +#define _ARITH_NOZERO(op,trg,o1,o2,err) \ +{ \ + SQInteger tmask = sq_type(o1)|sq_type(o2); \ + switch(tmask) { \ + case OT_INTEGER: { SQInteger i2 = _integer(o2); if(i2 == 0) { Raise_Error(err); SQ_THROW(); } trg = _integer(o1) op i2; } break;\ + case (OT_FLOAT|OT_INTEGER): \ + case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\ + default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ + } \ +} + +bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) +{ + SQInteger tmask = sq_type(o1)| sq_type(o2); + switch(tmask) { + case OT_INTEGER:{ + SQInteger res, i1 = _integer(o1), i2 = _integer(o2); + switch(op) { + case '+': res = i1 + i2; break; + case '-': res = i1 - i2; break; + case '/': if (i2 == 0) { Raise_Error(_SC("division by zero")); return false; } + else if (i2 == -1 && i1 == INT_MIN) { Raise_Error(_SC("integer overflow")); return false; } + res = i1 / i2; + break; + case '*': res = i1 * i2; break; + case '%': if (i2 == 0) { Raise_Error(_SC("modulo by zero")); return false; } + else if (i2 == -1 && i1 == INT_MIN) { res = 0; break; } + res = i1 % i2; + break; + default: res = 0xDEADBEEF; + } + trg = res; } + break; + case (OT_FLOAT|OT_INTEGER): + case (OT_FLOAT):{ + SQFloat res, f1 = tofloat(o1), f2 = tofloat(o2); + switch(op) { + case '+': res = f1 + f2; break; + case '-': res = f1 - f2; break; + case '/': res = f1 / f2; break; + case '*': res = f1 * f2; break; + case '%': res = SQFloat(fmod((double)f1,(double)f2)); break; + default: res = 0x0f; + } + trg = res; } + break; + default: + if(op == '+' && (tmask & _RT_STRING)){ + if(!StringCat(o1, o2, trg)) return false; + } + else if(!ArithMetaMethod(op,o1,o2,trg)) { + return false; + } + } + return true; +} + +SQVM::SQVM(SQSharedState *ss) +{ + _sharedstate=ss; + _suspended = SQFalse; + _suspended_target = -1; + _suspended_root = SQFalse; + _suspended_traps = -1; + _foreignptr = NULL; + _nnativecalls = 0; + _nmetamethodscall = 0; + _lasterror.Null(); + _errorhandler.Null(); + _debughook = false; + _debughook_native = NULL; + _debughook_closure.Null(); + _openouters = NULL; + ci = NULL; + _releasehook = NULL; + INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); +} + +void SQVM::Finalize() +{ + if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } + if(_openouters) CloseOuters(&_stack._vals[0]); + _roottable.Null(); + _lasterror.Null(); + _errorhandler.Null(); + _debughook = false; + _debughook_native = NULL; + _debughook_closure.Null(); + temp_reg.Null(); + _callstackdata.resize(0); + SQInteger size=_stack.size(); + for(SQInteger i=0;i_gc_chain,this); +} + +bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest) +{ + SQMetaMethod mm; + switch(op){ + case _SC('+'): mm=MT_ADD; break; + case _SC('-'): mm=MT_SUB; break; + case _SC('/'): mm=MT_DIV; break; + case _SC('*'): mm=MT_MUL; break; + case _SC('%'): mm=MT_MODULO; break; + default: mm = MT_ADD; assert(0); break; //shutup compiler + } + if(is_delegable(o1) && _delegable(o1)->_delegate) { + + SQObjectPtr closure; + if(_delegable(o1)->GetMetaMethod(this, mm, closure)) { + Push(o1);Push(o2); + return CallMetaMethod(closure,mm,2,dest); + } + } + Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); + return false; +} + +bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) +{ + + switch(sq_type(o)) { + case OT_INTEGER: + trg = -_integer(o); + return true; + case OT_FLOAT: + trg = -_float(o); + return true; + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o)->_delegate) { + SQObjectPtr closure; + if(_delegable(o)->GetMetaMethod(this, MT_UNM, closure)) { + Push(o); + if(!CallMetaMethod(closure, MT_UNM, 1, temp_reg)) return false; + _Swap(trg,temp_reg); + return true; + + } + } + default:break; //shutup compiler + } + Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o)); + return false; +} + +#define _RET_SUCCEED(exp) { result = (exp); return true; } +bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) +{ + SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); + if(t1 == t2) { + if(_rawval(o1) == _rawval(o2))_RET_SUCCEED(0); + SQObjectPtr res; + switch(t1){ + case OT_STRING: + _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2))); + case OT_INTEGER: + _RET_SUCCEED((_integer(o1)<_integer(o2))?-1:1); + case OT_FLOAT: + _RET_SUCCEED((_float(o1)<_float(o2))?-1:1); + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o1)->_delegate) { + SQObjectPtr closure; + if(_delegable(o1)->GetMetaMethod(this, MT_CMP, closure)) { + Push(o1);Push(o2); + if(CallMetaMethod(closure,MT_CMP,2,res)) { + if(sq_type(res) != OT_INTEGER) { + Raise_Error(_SC("_cmp must return an integer")); + return false; + } + _RET_SUCCEED(_integer(res)) + } + return false; + } + } + //continues through (no break needed) + default: + _RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 ); + } + assert(0); + //if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; } + // _RET_SUCCEED(_integer(res)); + + } + else{ + if(sq_isnumeric(o1) && sq_isnumeric(o2)){ + if((t1==OT_INTEGER) && (t2==OT_FLOAT)) { + if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); } + else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); } + _RET_SUCCEED(1); + } + else{ + if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); } + else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); } + _RET_SUCCEED(1); + } + } + else if(t1==OT_NULL) {_RET_SUCCEED(-1);} + else if(t2==OT_NULL) {_RET_SUCCEED(1);} + else { Raise_CompareError(o1,o2); return false; } + + } + assert(0); + _RET_SUCCEED(0); //cannot happen +} + +bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res) +{ + SQInteger r; + if(ObjCmp(o1,o2,r)) { + switch(op) { + case CMP_G: res = (r > 0); return true; + case CMP_GE: res = (r >= 0); return true; + case CMP_L: res = (r < 0); return true; + case CMP_LE: res = (r <= 0); return true; + case CMP_3W: res = r; return true; + } + assert(0); + } + return false; +} + +bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) +{ + switch(sq_type(o)) { + case OT_STRING: + res = o; + return true; + case OT_FLOAT: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR),_SC("%g"),_float(o)); + break; + case OT_INTEGER: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR),_PRINT_INT_FMT,_integer(o)); + break; + case OT_BOOL: + scsprintf(_sp(sq_rsl(6)),sq_rsl(6),_integer(o)?_SC("true"):_SC("false")); + break; + case OT_NULL: + scsprintf(_sp(sq_rsl(5)),sq_rsl(5),_SC("null")); + break; + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o)->_delegate) { + SQObjectPtr closure; + if(_delegable(o)->GetMetaMethod(this, MT_TOSTRING, closure)) { + Push(o); + if(CallMetaMethod(closure,MT_TOSTRING,1,res)) { + if(sq_type(res) == OT_STRING) + return true; + } + else { + return false; + } + } + } + default: + scsprintf(_sp(sq_rsl((sizeof(void*)*2)+NUMBER_MAX_CHAR)),sq_rsl((sizeof(void*)*2)+NUMBER_MAX_CHAR),_SC("(%s : 0x%p)"),GetTypeName(o),(void*)_rawval(o)); + } + res = SQString::Create(_ss(this),_spval); + return true; +} + + +bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest) +{ + SQObjectPtr a, b; + if(!ToString(str, a)) return false; + if(!ToString(obj, b)) return false; + SQInteger l = _string(a)->_len , ol = _string(b)->_len; + SQChar *s = _sp(sq_rsl(l + ol + 1)); + memcpy(s, _stringval(a), sq_rsl(l)); + memcpy(s + l, _stringval(b), sq_rsl(ol)); + dest = SQString::Create(_ss(this), _spval, l + ol); + return true; +} + +bool SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest) +{ + if(is_delegable(obj1) && _delegable(obj1)->_delegate) { + SQObjectPtr closure; + if(_delegable(obj1)->GetMetaMethod(this, MT_TYPEOF, closure)) { + Push(obj1); + return CallMetaMethod(closure,MT_TYPEOF,1,dest); + } + } + dest = SQString::Create(_ss(this),GetTypeName(obj1)); + return true; +} + +bool SQVM::Init(SQVM *friendvm, SQInteger stacksize) +{ + _stack.resize(stacksize); + _alloccallsstacksize = 4; + _callstackdata.resize(_alloccallsstacksize); + _callsstacksize = 0; + _callsstack = &_callstackdata[0]; + _stackbase = 0; + _top = 0; + if(!friendvm) { + _roottable = SQTable::Create(_ss(this), 0); + sq_base_register(this); + } + else { + _roottable = friendvm->_roottable; + _errorhandler = friendvm->_errorhandler; + _debughook = friendvm->_debughook; + _debughook_native = friendvm->_debughook_native; + _debughook_closure = friendvm->_debughook_closure; + } + + + return true; +} + + +bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQInteger stackbase,bool tailcall) +{ + SQFunctionProto *func = closure->_function; + + SQInteger paramssize = func->_nparameters; + const SQInteger newtop = stackbase + func->_stacksize; + SQInteger nargs = args; + if(func->_varparams) + { + paramssize--; + if (nargs < paramssize) { + Raise_Error(_SC("wrong number of parameters (%d passed, at least %d required)"), + (int)nargs, (int)paramssize); + return false; + } + + //dumpstack(stackbase); + SQInteger nvargs = nargs - paramssize; + SQArray *arr = SQArray::Create(_ss(this),nvargs); + SQInteger pbase = stackbase+paramssize; + for(SQInteger n = 0; n < nvargs; n++) { + arr->_values[n] = _stack._vals[pbase]; + _stack._vals[pbase].Null(); + pbase++; + + } + _stack._vals[stackbase+paramssize] = arr; + //dumpstack(stackbase); + } + else if (paramssize != nargs) { + SQInteger ndef = func->_ndefaultparams; + SQInteger diff; + if(ndef && nargs < paramssize && (diff = paramssize - nargs) <= ndef) { + for(SQInteger n = ndef - diff; n < ndef; n++) { + _stack._vals[stackbase + (nargs++)] = closure->_defaultparams[n]; + } + } + else { + Raise_Error(_SC("wrong number of parameters (%d passed, %d required)"), + (int)nargs, (int)paramssize); + return false; + } + } + + if(closure->_env) { + _stack._vals[stackbase] = closure->_env->_obj; + } + + if(!EnterFrame(stackbase, newtop, tailcall)) return false; + + ci->_closure = closure; + ci->_literals = func->_literals; + ci->_ip = func->_instructions; + ci->_target = (SQInt32)target; + + if (_debughook) { + CallDebugHook(_SC('c')); + } + + if (closure->_function->_bgenerator) { + SQFunctionProto *f = closure->_function; + SQGenerator *gen = SQGenerator::Create(_ss(this), closure); + if(!gen->Yield(this,f->_stacksize)) + return false; + SQObjectPtr temp; + Return(1, target, temp); + STK(target) = gen; + } + + + return true; +} + +bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval) +{ + SQBool _isroot = ci->_root; + SQInteger callerbase = _stackbase - ci->_prevstkbase; + + if (_debughook) { + for(SQInteger i=0; i_ncalls; i++) { + CallDebugHook(_SC('r')); + } + } + + SQObjectPtr *dest; + if (_isroot) { + dest = &(retval); + } else if (ci->_target == -1) { + dest = NULL; + } else { + dest = &_stack._vals[callerbase + ci->_target]; + } + if (dest) { + if(_arg0 != 0xFF) { + *dest = _stack._vals[_stackbase+_arg1]; + } + else { + dest->Null(); + } + //*dest = (_arg0 != 0xFF) ? _stack._vals[_stackbase+_arg1] : _null_; + } + LeaveFrame(); + return _isroot ? true : false; +} + +#define _RET_ON_FAIL(exp) { if(!exp) return false; } + +bool SQVM::PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr) +{ + SQObjectPtr trg; + _RET_ON_FAIL(ARITH_OP( op , trg, a, incr)); + target = a; + a = trg; + return true; +} + +bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix,SQInteger selfidx) +{ + SQObjectPtr tmp, tself = self, tkey = key; + if (!Get(tself, tkey, tmp, 0, selfidx)) { return false; } + _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr)) + if (!Set(tself, tkey, target,selfidx)) { return false; } + if (postfix) target = tmp; + return true; +} + +#define arg0 (_i_._arg0) +#define sarg0 ((SQInteger)*((const signed char *)&_i_._arg0)) +#define arg1 (_i_._arg1) +#define sarg1 (*((const SQInt32 *)&_i_._arg1)) +#define arg2 (_i_._arg2) +#define arg3 (_i_._arg3) +#define sarg3 ((SQInteger)*((const signed char *)&_i_._arg3)) + +SQRESULT SQVM::Suspend() +{ + if (_suspended) + return sq_throwerror(this, _SC("cannot suspend an already suspended vm")); + if (_nnativecalls!=2) + return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods")); + return SQ_SUSPEND_FLAG; +} + + +#define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; } +bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr +&o3,SQObjectPtr &o4,SQInteger SQ_UNUSED_ARG(arg_2),int exitpos,int &jump) +{ + SQInteger nrefidx; + switch(sq_type(o1)) { + case OT_TABLE: + if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos); + o4 = (SQInteger)nrefidx; _FINISH(1); + case OT_ARRAY: + if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(exitpos); + o4 = (SQInteger) nrefidx; _FINISH(1); + case OT_STRING: + if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); + o4 = (SQInteger)nrefidx; _FINISH(1); + case OT_CLASS: + if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); + o4 = (SQInteger)nrefidx; _FINISH(1); + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o1)->_delegate) { + SQObjectPtr itr; + SQObjectPtr closure; + if(_delegable(o1)->GetMetaMethod(this, MT_NEXTI, closure)) { + Push(o1); + Push(o4); + if(CallMetaMethod(closure, MT_NEXTI, 2, itr)) { + o4 = o2 = itr; + if(sq_type(itr) == OT_NULL) _FINISH(exitpos); + if(!Get(o1, itr, o3, 0, DONT_FALL_BACK)) { + Raise_Error(_SC("_nexti returned an invalid idx")); // cloud be changed + return false; + } + _FINISH(1); + } + else { + return false; + } + } + Raise_Error(_SC("_nexti failed")); + return false; + } + break; + case OT_GENERATOR: + if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos); + if(_generator(o1)->_state == SQGenerator::eSuspended) { + SQInteger idx = 0; + if(sq_type(o4) == OT_INTEGER) { + idx = _integer(o4) + 1; + } + o2 = idx; + o4 = idx; + _generator(o1)->Resume(this, o3); + _FINISH(0); + } + default: + Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1)); + } + return false; //cannot be hit(just to avoid warnings) +} + +#define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1)) + +#define SQ_THROW() { goto exception_trap; } + +#define _GUARD(exp) { if(!exp) { SQ_THROW();} } + +bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func) +{ + SQInteger nouters; + SQClosure *closure = SQClosure::Create(_ss(this), func,_table(_roottable)->GetWeakRef(OT_TABLE)); + if((nouters = func->_noutervalues)) { + for(SQInteger i = 0; i_outervalues[i]; + switch(v._type){ + case otLOCAL: + FindOuter(closure->_outervalues[i], &STK(_integer(v._src))); + break; + case otOUTER: + closure->_outervalues[i] = _closure(ci->_closure)->_outervalues[_integer(v._src)]; + break; + } + } + } + SQInteger ndefparams; + if((ndefparams = func->_ndefaultparams)) { + for(SQInteger i = 0; i < ndefparams; i++) { + SQInteger spos = func->_defaultparams[i]; + closure->_defaultparams[i] = _stack._vals[_stackbase + spos]; + } + } + target = closure; + return true; + +} + + +bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes) +{ + SQClass *base = NULL; + SQObjectPtr attrs; + if(baseclass != -1) { + if(sq_type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; } + base = _class(_stack._vals[_stackbase + baseclass]); + } + if(attributes != MAX_FUNC_STACKSIZE) { + attrs = _stack._vals[_stackbase+attributes]; + } + target = SQClass::Create(_ss(this),base); + if(sq_type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) { + int nparams = 2; + SQObjectPtr ret; + Push(target); Push(attrs); + if(!Call(_class(target)->_metamethods[MT_INHERITED],nparams,_top - nparams, ret, false)) { + Pop(nparams); + return false; + } + Pop(nparams); + } + _class(target)->_attributes = attrs; + return true; +} + +bool SQVM::IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res) +{ + SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); + if(t1 == t2) { + if (t1 == OT_FLOAT) { + res = (_float(o1) == _float(o2)); + } + else { + res = (_rawval(o1) == _rawval(o2)); + } + } + else { + if(sq_isnumeric(o1) && sq_isnumeric(o2)) { + res = (tofloat(o1) == tofloat(o2)); + } + else { + res = false; + } + } + return true; +} + +bool SQVM::IsFalse(SQObjectPtr &o) +{ + if(((sq_type(o) & SQOBJECT_CANBEFALSE) + && ( ((sq_type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0))) )) +#if !defined(SQUSEDOUBLE) || (defined(SQUSEDOUBLE) && defined(_SQ64)) + || (_integer(o) == 0) ) //OT_NULL|OT_INTEGER|OT_BOOL +#else + || (((sq_type(o) != OT_FLOAT) && (_integer(o) == 0))) ) //OT_NULL|OT_INTEGER|OT_BOOL +#endif + { + return true; + } + return false; +} +extern SQInstructionDesc g_InstrDesc[]; +bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et) +{ + if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; } + _nnativecalls++; + AutoDec ad(&_nnativecalls); + SQInteger traps = 0; + CallInfo *prevci = ci; + + switch(et) { + case ET_CALL: { + temp_reg = closure; + if(!StartCall(_closure(temp_reg), _top - nargs, nargs, stackbase, false)) { + //call the handler if there are no calls in the stack, if not relies on the previous node + if(ci == NULL) CallErrorHandler(_lasterror); + return false; + } + if(ci == prevci) { + outres = STK(_top-nargs); + return true; + } + ci->_root = SQTrue; + } + break; + case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, outres); ci->_root = SQTrue; traps += ci->_etraps; break; + case ET_RESUME_VM: + case ET_RESUME_THROW_VM: + traps = _suspended_traps; + ci->_root = _suspended_root; + _suspended = SQFalse; + if(et == ET_RESUME_THROW_VM) { SQ_THROW(); } + break; + } + +exception_restore: + // + { + for(;;) + { + const SQInstruction &_i_ = *ci->_ip++; + //dumpstack(_stackbase); + //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); + switch(_i_.op) + { + case _OP_LINE: if (_debughook) CallDebugHook(_SC('l'),arg1); continue; + case _OP_LOAD: TARGET = ci->_literals[arg1]; continue; + case _OP_LOADINT: +#ifndef _SQ64 + TARGET = (SQInteger)arg1; continue; +#else + TARGET = (SQInteger)((SQInt32)arg1); continue; +#endif + case _OP_LOADFLOAT: TARGET = *((const SQFloat *)&arg1); continue; + case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; + case _OP_TAILCALL:{ + SQObjectPtr &t = STK(arg1); + if (sq_type(t) == OT_CLOSURE + && (!_closure(t)->_function->_bgenerator)){ + SQObjectPtr clo = t; + SQInteger last_top = _top; + if(_openouters) CloseOuters(&(_stack._vals[_stackbase])); + for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i); + _GUARD(StartCall(_closure(clo), ci->_target, arg3, _stackbase, true)); + if (last_top >= _top) { + _top = last_top; + } + continue; + } + } + case _OP_CALL: { + SQObjectPtr clo = STK(arg1); + switch (sq_type(clo)) { + case OT_CLOSURE: + _GUARD(StartCall(_closure(clo), sarg0, arg3, _stackbase+arg2, false)); + continue; + case OT_NATIVECLOSURE: { + bool suspend; + bool tailcall; + _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo, (SQInt32)sarg0, suspend, tailcall)); + if(suspend){ + _suspended = SQTrue; + _suspended_target = sarg0; + _suspended_root = ci->_root; + _suspended_traps = traps; + outres = clo; + return true; + } + if(sarg0 != -1 && !tailcall) { + STK(arg0) = clo; + } + } + continue; + case OT_CLASS:{ + SQObjectPtr inst; + _GUARD(CreateClassInstance(_class(clo),inst,clo)); + if(sarg0 != -1) { + STK(arg0) = inst; + } + SQInteger stkbase; + switch(sq_type(clo)) { + case OT_CLOSURE: + stkbase = _stackbase+arg2; + _stack._vals[stkbase] = inst; + _GUARD(StartCall(_closure(clo), -1, arg3, stkbase, false)); + break; + case OT_NATIVECLOSURE: + bool dummy; + stkbase = _stackbase+arg2; + _stack._vals[stkbase] = inst; + _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo, -1, dummy, dummy)); + break; + default: break; //shutup GCC 4.x + } + } + break; + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE:{ + SQObjectPtr closure; + if(_delegable(clo)->_delegate && _delegable(clo)->GetMetaMethod(this,MT_CALL,closure)) { + Push(clo); + for (SQInteger i = 0; i < arg3; i++) Push(STK(arg2 + i)); + if(!CallMetaMethod(closure, MT_CALL, arg3+1, clo)) SQ_THROW(); + if(sarg0 != -1) { + STK(arg0) = clo; + } + break; + } + + //Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo)); + //SQ_THROW(); + } + default: + Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo)); + SQ_THROW(); + } + } + continue; + case _OP_PREPCALL: + case _OP_PREPCALLK: { + SQObjectPtr &key = _i_.op == _OP_PREPCALLK?(ci->_literals)[arg1]:STK(arg1); + SQObjectPtr &o = STK(arg2); + if (!Get(o, key, temp_reg,0,arg2)) { + SQ_THROW(); + } + STK(arg3) = o; + _Swap(TARGET,temp_reg);//TARGET = temp_reg; + } + continue; + case _OP_GETK: + if (!Get(STK(arg2), ci->_literals[arg1], temp_reg, 0,arg2)) { SQ_THROW();} + _Swap(TARGET,temp_reg);//TARGET = temp_reg; + continue; + case _OP_MOVE: TARGET = STK(arg1); continue; + case _OP_NEWSLOT: + _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),false)); + if(arg0 != 0xFF) TARGET = STK(arg3); + continue; + case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue; + case _OP_SET: + if (!Set(STK(arg1), STK(arg2), STK(arg3),arg1)) { SQ_THROW(); } + if (arg0 != 0xFF) TARGET = STK(arg3); + continue; + case _OP_GET: + if (!Get(STK(arg1), STK(arg2), temp_reg, 0,arg1)) { SQ_THROW(); } + _Swap(TARGET,temp_reg);//TARGET = temp_reg; + continue; + case _OP_EQ:{ + bool res; + if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); } + TARGET = res?true:false; + }continue; + case _OP_NE:{ + bool res; + if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); } + TARGET = (!res)?true:false; + } continue; + case _OP_ADD: _ARITH_(+,TARGET,STK(arg2),STK(arg1)); continue; + case _OP_SUB: _ARITH_(-,TARGET,STK(arg2),STK(arg1)); continue; + case _OP_MUL: _ARITH_(*,TARGET,STK(arg2),STK(arg1)); continue; + case _OP_DIV: _ARITH_NOZERO(/,TARGET,STK(arg2),STK(arg1),_SC("division by zero")); continue; + case _OP_MOD: ARITH_OP('%',TARGET,STK(arg2),STK(arg1)); continue; + case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue; + case _OP_RETURN: + if((ci)->_generator) { + (ci)->_generator->Kill(); + } + if(Return(arg0, arg1, temp_reg)){ + assert(traps==0); + //outres = temp_reg; + _Swap(outres,temp_reg); + return true; + } + continue; + case _OP_LOADNULLS:{ for(SQInt32 n=0; n < arg1; n++) STK(arg0+n).Null(); }continue; + case _OP_LOADROOT: { + SQWeakRef *w = _closure(ci->_closure)->_root; + if(sq_type(w->_obj) != OT_NULL) { + TARGET = w->_obj; + } else { + TARGET = _roottable; //shoud this be like this? or null + } + } + continue; + case _OP_LOADBOOL: TARGET = arg1?true:false; continue; + case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue; + case _OP_JMP: ci->_ip += (sarg1); continue; + //case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue; + case _OP_JCMP: + _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg0),temp_reg)); + if(IsFalse(temp_reg)) ci->_ip+=(sarg1); + continue; + case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue; + case _OP_GETOUTER: { + SQClosure *cur_cls = _closure(ci->_closure); + SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); + TARGET = *(otr->_valptr); + } + continue; + case _OP_SETOUTER: { + SQClosure *cur_cls = _closure(ci->_closure); + SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); + *(otr->_valptr) = STK(arg2); + if(arg0 != 0xFF) { + TARGET = STK(arg2); + } + } + continue; + case _OP_NEWOBJ: + switch(arg3) { + case NOT_TABLE: TARGET = SQTable::Create(_ss(this), arg1); continue; + case NOT_ARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue; + case NOT_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue; + default: assert(0); continue; + } + case _OP_APPENDARRAY: + { + SQObject val; + val._unVal.raw = 0; + switch(arg2) { + case AAT_STACK: + val = STK(arg1); break; + case AAT_LITERAL: + val = ci->_literals[arg1]; break; + case AAT_INT: + val._type = OT_INTEGER; +#ifndef _SQ64 + val._unVal.nInteger = (SQInteger)arg1; +#else + val._unVal.nInteger = (SQInteger)((SQInt32)arg1); +#endif + break; + case AAT_FLOAT: + val._type = OT_FLOAT; + val._unVal.fFloat = *((const SQFloat *)&arg1); + break; + case AAT_BOOL: + val._type = OT_BOOL; + val._unVal.nInteger = arg1; + break; + default: val._type = OT_INTEGER; assert(0); break; + + } + _array(STK(arg0))->Append(val); continue; + } + case _OP_COMPARITH: { + SQInteger selfidx = (((SQUnsignedInteger)arg1&0xFFFF0000)>>16); + _GUARD(DerefInc(arg3, TARGET, STK(selfidx), STK(arg2), STK(arg1&0x0000FFFF), false, selfidx)); + } + continue; + case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false, arg1));} continue; + case _OP_INCL: { + SQObjectPtr &a = STK(arg1); + if(sq_type(a) == OT_INTEGER) { + a._unVal.nInteger = _integer(a) + sarg3; + } + else { + SQObjectPtr o(sarg3); //_GUARD(LOCAL_INC('+',TARGET, STK(arg1), o)); + _ARITH_(+,a,a,o); + } + } continue; + case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true, arg1));} continue; + case _OP_PINCL: { + SQObjectPtr &a = STK(arg1); + if(sq_type(a) == OT_INTEGER) { + TARGET = a; + a._unVal.nInteger = _integer(a) + sarg3; + } + else { + SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o)); + } + + } continue; + case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue; + case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_RAW, DONT_FALL_BACK) ? true : false; continue; + case _OP_INSTANCEOF: + if(sq_type(STK(arg1)) != OT_CLASS) + {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();} + TARGET = (sq_type(STK(arg2)) == OT_INSTANCE) ? (_instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?true:false) : false; + continue; + case _OP_AND: + if(IsFalse(STK(arg2))) { + TARGET = STK(arg2); + ci->_ip += (sarg1); + } + continue; + case _OP_OR: + if(!IsFalse(STK(arg2))) { + TARGET = STK(arg2); + ci->_ip += (sarg1); + } + continue; + case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue; + case _OP_NOT: TARGET = IsFalse(STK(arg1)); continue; + case _OP_BWNOT: + if(sq_type(STK(arg1)) == OT_INTEGER) { + SQInteger t = _integer(STK(arg1)); + TARGET = SQInteger(~t); + continue; + } + Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1))); + SQ_THROW(); + case _OP_CLOSURE: { + SQClosure *c = ci->_closure._unVal.pClosure; + SQFunctionProto *fp = c->_function; + if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); } + continue; + } + case _OP_YIELD:{ + if(ci->_generator) { + if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1); + if (_openouters) CloseOuters(&_stack._vals[_stackbase]); + _GUARD(ci->_generator->Yield(this,arg2)); + traps -= ci->_etraps; + if(sarg1 != MAX_FUNC_STACKSIZE) _Swap(STK(arg1),temp_reg);//STK(arg1) = temp_reg; + } + else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();} + if(Return(arg0, arg1, temp_reg)){ + assert(traps == 0); + outres = temp_reg; + return true; + } + + } + continue; + case _OP_RESUME: + if(sq_type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();} + _GUARD(_generator(STK(arg1))->Resume(this, TARGET)); + traps += ci->_etraps; + continue; + case _OP_FOREACH:{ int tojump; + _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,sarg1,tojump)); + ci->_ip += tojump; } + continue; + case _OP_POSTFOREACH: + assert(sq_type(STK(arg0)) == OT_GENERATOR); + if(_generator(STK(arg0))->_state == SQGenerator::eDead) + ci->_ip += (sarg1 - 1); + continue; + case _OP_CLONE: _GUARD(Clone(STK(arg1), TARGET)); continue; + case _OP_TYPEOF: _GUARD(TypeOf(STK(arg1), TARGET)) continue; + case _OP_PUSHTRAP:{ + SQInstruction *_iv = _closure(ci->_closure)->_function->_instructions; + _etraps.push_back(SQExceptionTrap(_top,_stackbase, &_iv[(ci->_ip-_iv)+arg1], arg0)); traps++; + ci->_etraps++; + } + continue; + case _OP_POPTRAP: { + for(SQInteger i = 0; i < arg0; i++) { + _etraps.pop_back(); traps--; + ci->_etraps--; + } + } + continue; + case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue; + case _OP_NEWSLOTA: + _GUARD(NewSlotA(STK(arg1),STK(arg2),STK(arg3),(arg0&NEW_SLOT_ATTRIBUTES_FLAG) ? STK(arg2-1) : SQObjectPtr(),(arg0&NEW_SLOT_STATIC_FLAG)?true:false,false)); + continue; + case _OP_GETBASE:{ + SQClosure *clo = _closure(ci->_closure); + if(clo->_base) { + TARGET = clo->_base; + } + else { + TARGET.Null(); + } + continue; + } + case _OP_CLOSE: + if(_openouters) CloseOuters(&(STK(arg1))); + continue; + } + + } + } +exception_trap: + { + SQObjectPtr currerror = _lasterror; +// dumpstack(_stackbase); +// SQInteger n = 0; + SQInteger last_top = _top; + + if(_ss(this)->_notifyallexceptions || (!traps && raiseerror)) CallErrorHandler(currerror); + + while( ci ) { + if(ci->_etraps > 0) { + SQExceptionTrap &et = _etraps.top(); + ci->_ip = et._ip; + _top = et._stacksize; + _stackbase = et._stackbase; + _stack._vals[_stackbase + et._extarget] = currerror; + _etraps.pop_back(); traps--; ci->_etraps--; + while(last_top >= _top) _stack._vals[last_top--].Null(); + goto exception_restore; + } + else if (_debughook) { + //notify debugger of a "return" + //even if it really an exception unwinding the stack + for(SQInteger i = 0; i < ci->_ncalls; i++) { + CallDebugHook(_SC('r')); + } + } + if(ci->_generator) ci->_generator->Kill(); + bool mustbreak = ci && ci->_root; + LeaveFrame(); + if(mustbreak) break; + } + + _lasterror = currerror; + return false; + } + assert(0); +} + +bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor) +{ + inst = theclass->CreateInstance(); + if(!theclass->GetConstructor(constructor)) { + constructor.Null(); + } + return true; +} + +void SQVM::CallErrorHandler(SQObjectPtr &error) +{ + if(sq_type(_errorhandler) != OT_NULL) { + SQObjectPtr out; + Push(_roottable); Push(error); + Call(_errorhandler, 2, _top-2, out,SQFalse); + Pop(2); + } +} + + +void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) +{ + _debughook = false; + SQFunctionProto *func=_closure(ci->_closure)->_function; + if(_debughook_native) { + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *fname = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + SQInteger line = forcedline?forcedline:func->GetLine(ci->_ip); + _debughook_native(this,type,src,line,fname); + } + else { + SQObjectPtr temp_reg; + SQInteger nparams=5; + Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name); + Call(_debughook_closure,nparams,_top-nparams,temp_reg,SQFalse); + Pop(nparams); + } + _debughook = true; +} + +bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target,bool &suspend, bool &tailcall) +{ + SQInteger nparamscheck = nclosure->_nparamscheck; + SQInteger newtop = newbase + nargs + nclosure->_noutervalues; + + if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { + Raise_Error(_SC("Native stack overflow")); + return false; + } + + if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || + ((nparamscheck < 0) && (nargs < (-nparamscheck))))) + { + Raise_Error(_SC("wrong number of parameters")); + return false; + } + + SQInteger tcs; + SQIntVec &tc = nclosure->_typecheck; + if((tcs = tc.size())) { + for(SQInteger i = 0; i < nargs && i < tcs; i++) { + if((tc._vals[i] != -1) && !(sq_type(_stack._vals[newbase+i]) & tc._vals[i])) { + Raise_ParamTypeError(i,tc._vals[i], sq_type(_stack._vals[newbase+i])); + return false; + } + } + } + + if(!EnterFrame(newbase, newtop, false)) return false; + ci->_closure = nclosure; + ci->_target = target; + + SQInteger outers = nclosure->_noutervalues; + for (SQInteger i = 0; i < outers; i++) { + _stack._vals[newbase+nargs+i] = nclosure->_outervalues[i]; + } + if(nclosure->_env) { + _stack._vals[newbase] = nclosure->_env->_obj; + } + + _nnativecalls++; + SQInteger ret = (nclosure->_function)(this); + _nnativecalls--; + + suspend = false; + tailcall = false; + if (ret == SQ_TAILCALL_FLAG) { + tailcall = true; + return true; + } + else if (ret == SQ_SUSPEND_FLAG) { + suspend = true; + } + else if (ret < 0) { + LeaveFrame(); + Raise_Error(_lasterror); + return false; + } + if(ret) { + retval = _stack._vals[_top-1]; + } + else { + retval.Null(); + } + //retval = ret ? _stack._vals[_top-1] : _null_; + LeaveFrame(); + return true; +} + +bool SQVM::TailCall(SQClosure *closure, SQInteger parambase,SQInteger nparams) +{ + SQInteger last_top = _top; + SQObjectPtr clo = closure; + if (ci->_root) + { + Raise_Error("root calls cannot invoke tailcalls"); + return false; + } + for (SQInteger i = 0; i < nparams; i++) STK(i) = STK(parambase + i); + bool ret = StartCall(closure, ci->_target, nparams, _stackbase, true); + if (last_top >= _top) { + _top = last_top; + } + return ret; +} + +#define FALLBACK_OK 0 +#define FALLBACK_NO_MATCH 1 +#define FALLBACK_ERROR 2 + +bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx) +{ + switch(sq_type(self)){ + case OT_TABLE: + if(_table(self)->Get(key,dest))return true; + break; + case OT_ARRAY: + if (sq_isnumeric(key)) { if (_array(self)->Get(tointeger(key), dest)) { return true; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return false; } + break; + case OT_INSTANCE: + if(_instance(self)->Get(key,dest)) return true; + break; + case OT_CLASS: + if(_class(self)->Get(key,dest)) return true; + break; + case OT_STRING: + if(sq_isnumeric(key)){ + SQInteger n = tointeger(key); + SQInteger len = _string(self)->_len; + if (n < 0) { n += len; } + if (n >= 0 && n < len) { + dest = SQInteger(_stringval(self)[n]); + return true; + } + if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); + return false; + } + break; + default:break; //shut up compiler + } + if ((getflags & GET_FLAG_RAW) == 0) { + switch(FallBackGet(self,key,dest)) { + case FALLBACK_OK: return true; //okie + case FALLBACK_NO_MATCH: break; //keep falling back + case FALLBACK_ERROR: return false; // the metamethod failed + } + if(InvokeDefaultDelegate(self,key,dest)) { + return true; + } + } +//#ifdef ROOT_FALLBACK + if(selfidx == 0) { + SQWeakRef *w = _closure(ci->_closure)->_root; + if(sq_type(w->_obj) != OT_NULL) + { + if(Get(*((const SQObjectPtr *)&w->_obj),key,dest,0,DONT_FALL_BACK)) return true; + } + + } +//#endif + if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); + return false; +} + +bool SQVM::InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) +{ + SQTable *ddel = NULL; + switch(sq_type(self)) { + case OT_CLASS: ddel = _class_ddel; break; + case OT_TABLE: ddel = _table_ddel; break; + case OT_ARRAY: ddel = _array_ddel; break; + case OT_STRING: ddel = _string_ddel; break; + case OT_INSTANCE: ddel = _instance_ddel; break; + case OT_INTEGER:case OT_FLOAT:case OT_BOOL: ddel = _number_ddel; break; + case OT_GENERATOR: ddel = _generator_ddel; break; + case OT_CLOSURE: case OT_NATIVECLOSURE: ddel = _closure_ddel; break; + case OT_THREAD: ddel = _thread_ddel; break; + case OT_WEAKREF: ddel = _weakref_ddel; break; + default: return false; + } + return ddel->Get(key,dest); +} + + +SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) +{ + switch(sq_type(self)){ + case OT_TABLE: + case OT_USERDATA: + //delegation + if(_delegable(self)->_delegate) { + if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,0,DONT_FALL_BACK)) return FALLBACK_OK; + } + else { + return FALLBACK_NO_MATCH; + } + //go through + case OT_INSTANCE: { + SQObjectPtr closure; + if(_delegable(self)->GetMetaMethod(this, MT_GET, closure)) { + Push(self);Push(key); + _nmetamethodscall++; + AutoDec ad(&_nmetamethodscall); + if(Call(closure, 2, _top - 2, dest, SQFalse)) { + Pop(2); + return FALLBACK_OK; + } + else { + Pop(2); + if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) + return FALLBACK_ERROR; + } + } + } + } + break; + default: break;//shutup GCC 4.x + } + // no metamethod or no fallback type + return FALLBACK_NO_MATCH; +} + +bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,SQInteger selfidx) +{ + switch(sq_type(self)){ + case OT_TABLE: + if(_table(self)->Set(key,val)) return true; + break; + case OT_INSTANCE: + if(_instance(self)->Set(key,val)) return true; + break; + case OT_ARRAY: + if(!sq_isnumeric(key)) { Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; } + if(!_array(self)->Set(tointeger(key),val)) { + Raise_IdxError(key); + return false; + } + return true; + case OT_USERDATA: break; // must fall back + default: + Raise_Error(_SC("trying to set '%s'"),GetTypeName(self)); + return false; + } + + switch(FallBackSet(self,key,val)) { + case FALLBACK_OK: return true; //okie + case FALLBACK_NO_MATCH: break; //keep falling back + case FALLBACK_ERROR: return false; // the metamethod failed + } + if(selfidx == 0) { + if(_table(_roottable)->Set(key,val)) + return true; + } + Raise_IdxError(key); + return false; +} + +SQInteger SQVM::FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val) +{ + switch(sq_type(self)) { + case OT_TABLE: + if(_table(self)->_delegate) { + if(Set(_table(self)->_delegate,key,val,DONT_FALL_BACK)) return FALLBACK_OK; + } + //keps on going + case OT_INSTANCE: + case OT_USERDATA:{ + SQObjectPtr closure; + SQObjectPtr t; + if(_delegable(self)->GetMetaMethod(this, MT_SET, closure)) { + Push(self);Push(key);Push(val); + _nmetamethodscall++; + AutoDec ad(&_nmetamethodscall); + if(Call(closure, 3, _top - 3, t, SQFalse)) { + Pop(3); + return FALLBACK_OK; + } + else { + Pop(3); + if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) + return FALLBACK_ERROR; + } + } + } + } + break; + default: break;//shutup GCC 4.x + } + // no metamethod or no fallback type + return FALLBACK_NO_MATCH; +} + +bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target) +{ + SQObjectPtr temp_reg; + SQObjectPtr newobj; + switch(sq_type(self)){ + case OT_TABLE: + newobj = _table(self)->Clone(); + goto cloned_mt; + case OT_INSTANCE: { + newobj = _instance(self)->Clone(_ss(this)); +cloned_mt: + SQObjectPtr closure; + if(_delegable(newobj)->_delegate && _delegable(newobj)->GetMetaMethod(this,MT_CLONED,closure)) { + Push(newobj); + Push(self); + if(!CallMetaMethod(closure,MT_CLONED,2,temp_reg)) + return false; + } + } + target = newobj; + return true; + case OT_ARRAY: + target = _array(self)->Clone(); + return true; + default: + Raise_Error(_SC("cloning a %s"), GetTypeName(self)); + return false; + } +} + +bool SQVM::NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw) +{ + if(sq_type(self) != OT_CLASS) { + Raise_Error(_SC("object must be a class")); + return false; + } + SQClass *c = _class(self); + if(!raw) { + SQObjectPtr &mm = c->_metamethods[MT_NEWMEMBER]; + if(sq_type(mm) != OT_NULL ) { + Push(self); Push(key); Push(val); + Push(attrs); + Push(bstatic); + return CallMetaMethod(mm,MT_NEWMEMBER,5,temp_reg); + } + } + if(!NewSlot(self, key, val,bstatic)) + return false; + if(sq_type(attrs) != OT_NULL) { + c->SetAttributes(key,attrs); + } + return true; +} + +bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) +{ + if(sq_type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; } + switch(sq_type(self)) { + case OT_TABLE: { + bool rawcall = true; + if(_table(self)->_delegate) { + SQObjectPtr res; + if(!_table(self)->Get(key,res)) { + SQObjectPtr closure; + if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { + Push(self);Push(key);Push(val); + if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { + return false; + } + rawcall = false; + } + else { + rawcall = true; + } + } + } + if(rawcall) _table(self)->NewSlot(key,val); //cannot fail + + break;} + case OT_INSTANCE: { + SQObjectPtr res; + SQObjectPtr closure; + if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { + Push(self);Push(key);Push(val); + if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { + return false; + } + break; + } + Raise_Error(_SC("class instances do not support the new slot operator")); + return false; + break;} + case OT_CLASS: + if(!_class(self)->NewSlot(_ss(this),key,val,bstatic)) { + if(_class(self)->_locked) { + Raise_Error(_SC("trying to modify a class that has already been instantiated")); + return false; + } + else { + SQObjectPtr oval = PrintObjVal(key); + Raise_Error(_SC("the property '%s' already exists"),_stringval(oval)); + return false; + } + } + break; + default: + Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); + return false; + break; + } + return true; +} + + + +bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res) +{ + switch(sq_type(self)) { + case OT_TABLE: + case OT_INSTANCE: + case OT_USERDATA: { + SQObjectPtr t; + //bool handled = false; + SQObjectPtr closure; + if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_DELSLOT,closure)) { + Push(self);Push(key); + return CallMetaMethod(closure,MT_DELSLOT,2,res); + } + else { + if(sq_type(self) == OT_TABLE) { + if(_table(self)->Get(key,t)) { + _table(self)->Remove(key); + } + else { + Raise_IdxError((const SQObject &)key); + return false; + } + } + else { + Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self)); + return false; + } + } + res = t; + } + break; + default: + Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self)); + return false; + } + return true; +} + +bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror) +{ +#ifdef _DEBUG +SQInteger prevstackbase = _stackbase; +#endif + switch(sq_type(closure)) { + case OT_CLOSURE: + return Execute(closure, nparams, stackbase, outres, raiseerror); + break; + case OT_NATIVECLOSURE:{ + bool dummy; + return CallNative(_nativeclosure(closure), nparams, stackbase, outres, -1, dummy, dummy); + + } + break; + case OT_CLASS: { + SQObjectPtr constr; + SQObjectPtr temp; + CreateClassInstance(_class(closure),outres,constr); + SQObjectType ctype = sq_type(constr); + if (ctype == OT_NATIVECLOSURE || ctype == OT_CLOSURE) { + _stack[stackbase] = outres; + return Call(constr,nparams,stackbase,temp,raiseerror); + } + return true; + } + break; + default: + Raise_Error(_SC("attempt to call '%s'"), GetTypeName(closure)); + return false; + } +#ifdef _DEBUG + if(!_suspended) { + assert(_stackbase == prevstackbase); + } +#endif + return true; +} + +bool SQVM::CallMetaMethod(SQObjectPtr &closure,SQMetaMethod SQ_UNUSED_ARG(mm),SQInteger nparams,SQObjectPtr &outres) +{ + //SQObjectPtr closure; + + _nmetamethodscall++; + if(Call(closure, nparams, _top - nparams, outres, SQFalse)) { + _nmetamethodscall--; + Pop(nparams); + return true; + } + _nmetamethodscall--; + //} + Pop(nparams); + return false; +} + +void SQVM::FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex) +{ + SQOuter **pp = &_openouters; + SQOuter *p; + SQOuter *otr; + + while ((p = *pp) != NULL && p->_valptr >= stackindex) { + if (p->_valptr == stackindex) { + target = SQObjectPtr(p); + return; + } + pp = &p->_next; + } + otr = SQOuter::Create(_ss(this), stackindex); + otr->_next = *pp; + otr->_idx = (stackindex - _stack._vals); + __ObjAddRef(otr); + *pp = otr; + target = SQObjectPtr(otr); +} + +bool SQVM::EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall) +{ + if( !tailcall ) { + if( _callsstacksize == _alloccallsstacksize ) { + GrowCallStack(); + } + ci = &_callsstack[_callsstacksize++]; + ci->_prevstkbase = (SQInt32)(newbase - _stackbase); + ci->_prevtop = (SQInt32)(_top - _stackbase); + ci->_etraps = 0; + ci->_ncalls = 1; + ci->_generator = NULL; + ci->_root = SQFalse; + } + else { + ci->_ncalls++; + } + + _stackbase = newbase; + _top = newtop; + if(newtop + MIN_STACK_OVERHEAD > (SQInteger)_stack.size()) { + if(_nmetamethodscall) { + Raise_Error(_SC("stack overflow, cannot resize stack while in a metamethod")); + return false; + } + _stack.resize(newtop + (MIN_STACK_OVERHEAD << 2)); + RelocateOuters(); + } + return true; +} + +void SQVM::LeaveFrame() { + SQInteger last_top = _top; + SQInteger last_stackbase = _stackbase; + SQInteger css = --_callsstacksize; + + /* First clean out the call stack frame */ + ci->_closure.Null(); + _stackbase -= ci->_prevstkbase; + _top = _stackbase + ci->_prevtop; + ci = (css) ? &_callsstack[css-1] : NULL; + + if(_openouters) CloseOuters(&(_stack._vals[last_stackbase])); + while (last_top >= _top) { + _stack._vals[last_top--].Null(); + } +} + +void SQVM::RelocateOuters() +{ + SQOuter *p = _openouters; + while (p) { + p->_valptr = _stack._vals + p->_idx; + p = p->_next; + } +} + +void SQVM::CloseOuters(SQObjectPtr *stackindex) { + SQOuter *p; + while ((p = _openouters) != NULL && p->_valptr >= stackindex) { + p->_value = *(p->_valptr); + p->_valptr = &p->_value; + _openouters = p->_next; + __ObjRelease(p); + } +} + +void SQVM::Remove(SQInteger n) { + n = (n >= 0)?n + _stackbase - 1:_top + n; + for(SQInteger i = n; i < _top; i++){ + _stack[i] = _stack[i+1]; + } + _stack[_top].Null(); + _top--; +} + +void SQVM::Pop() { + _stack[--_top].Null(); +} + +void SQVM::Pop(SQInteger n) { + for(SQInteger i = 0; i < n; i++){ + _stack[--_top].Null(); + } +} + +void SQVM::PushNull() { _stack[_top++].Null(); } +void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; } +SQObjectPtr &SQVM::Top() { return _stack[_top-1]; } +SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; } +SQObjectPtr &SQVM::GetUp(SQInteger n) { return _stack[_top+n]; } +SQObjectPtr &SQVM::GetAt(SQInteger n) { return _stack[n]; } + +#ifdef _DEBUG_DUMP +void SQVM::dumpstack(SQInteger stackbase,bool dumpall) +{ + SQInteger size=dumpall?_stack.size():_top; + SQInteger n=0; + scprintf(_SC("\n>>>>stack dump<<<<\n")); + CallInfo &ci=_callsstack[_callsstacksize-1]; + scprintf(_SC("IP: %p\n"),ci._ip); + scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase); + scprintf(_SC("prev top: %d\n"),ci._prevtop); + for(SQInteger i=0;i"));else scprintf(_SC(" ")); + scprintf(_SC("[" _PRINT_INT_FMT "]:"),n); + switch(sq_type(obj)){ + case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break; + case OT_INTEGER: scprintf(_SC("INTEGER " _PRINT_INT_FMT),_integer(obj));break; + case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break; + case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break; + case OT_NULL: scprintf(_SC("NULL")); break; + case OT_TABLE: scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break; + case OT_ARRAY: scprintf(_SC("ARRAY %p"),_array(obj));break; + case OT_CLOSURE: scprintf(_SC("CLOSURE [%p]"),_closure(obj));break; + case OT_NATIVECLOSURE: scprintf(_SC("NATIVECLOSURE"));break; + case OT_USERDATA: scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break; + case OT_GENERATOR: scprintf(_SC("GENERATOR %p"),_generator(obj));break; + case OT_THREAD: scprintf(_SC("THREAD [%p]"),_thread(obj));break; + case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break; + case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break; + case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break; + case OT_WEAKREF: scprintf(_SC("WEAKREF %p"),_weakref(obj));break; + default: + assert(0); + break; + }; + scprintf(_SC("\n")); + ++n; + } +} + + + +#endif diff --git a/mp/src/vscript/squirrel/squirrel/sqvm.h b/mp/src/vscript/squirrel/squirrel/sqvm.h new file mode 100644 index 00000000..a75524da --- /dev/null +++ b/mp/src/vscript/squirrel/squirrel/sqvm.h @@ -0,0 +1,213 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQVM_H_ +#define _SQVM_H_ + +#include "sqopcodes.h" +#include "sqobject.h" +#define MAX_NATIVE_CALLS 100 +#define MIN_STACK_OVERHEAD 15 + +#define SQ_SUSPEND_FLAG -666 +#define SQ_TAILCALL_FLAG -777 +#define DONT_FALL_BACK 666 +//#define EXISTS_FALL_BACK -1 + +#define GET_FLAG_RAW 0x00000001 +#define GET_FLAG_DO_NOT_RAISE_ERROR 0x00000002 +//base lib +void sq_base_register(HSQUIRRELVM v); + +struct SQExceptionTrap{ + SQExceptionTrap() {} + SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;} + SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et; } + SQInteger _stackbase; + SQInteger _stacksize; + SQInstruction *_ip; + SQInteger _extarget; +}; + +#define _INLINE + +typedef sqvector ExceptionsTraps; + +struct SQVM : public CHAINABLE_OBJ +{ + struct CallInfo{ + //CallInfo() { _generator = NULL;} + SQInstruction *_ip; + SQObjectPtr *_literals; + SQObjectPtr _closure; + SQGenerator *_generator; + SQInt32 _etraps; + SQInt32 _prevstkbase; + SQInt32 _prevtop; + SQInt32 _target; + SQInt32 _ncalls; + SQBool _root; + }; + +typedef sqvector CallInfoVec; +public: + void DebugHookProxy(SQInteger type, const SQChar * sourcename, SQInteger line, const SQChar * funcname); + static void _DebugHookProxy(HSQUIRRELVM v, SQInteger type, const SQChar * sourcename, SQInteger line, const SQChar * funcname); + enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM,ET_RESUME_THROW_VM }; + SQVM(SQSharedState *ss); + ~SQVM(); + bool Init(SQVM *friendvm, SQInteger stacksize); + bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL); + //starts a native call return when the NATIVE closure returns + bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); + bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); + //starts a SQUIRREL call in the same "Execution loop" + bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); + bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); + //call a generic closure pure SQUIRREL or NATIVE + bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror); + SQRESULT Suspend(); + + void CallDebugHook(SQInteger type,SQInteger forcedline=0); + void CallErrorHandler(SQObjectPtr &e); + bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx); + SQInteger FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); + bool InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); + bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val, SQInteger selfidx); + SQInteger FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val); + bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic); + bool NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw); + bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res); + bool Clone(const SQObjectPtr &self, SQObjectPtr &target); + bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res); + bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest); + static bool IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res); + bool ToString(const SQObjectPtr &o,SQObjectPtr &res); + SQString *PrintObjVal(const SQObjectPtr &o); + + + void Raise_Error(const SQChar *s, ...); + void Raise_Error(const SQObjectPtr &desc); + void Raise_IdxError(const SQObjectPtr &o); + void Raise_CompareError(const SQObject &o1, const SQObject &o2); + void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type); + + void FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex); + void RelocateOuters(); + void CloseOuters(SQObjectPtr *stackindex); + + bool TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest); + bool CallMetaMethod(SQObjectPtr &closure, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres); + bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest); + bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval); + //new stuff + _INLINE bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); + _INLINE bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); + _INLINE bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1); + _INLINE bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res); + bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func); + bool CLASS_OP(SQObjectPtr &target,SQInteger base,SQInteger attrs); + //return true if the loop is finished + bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump); + //_INLINE bool LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); + _INLINE bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); + _INLINE bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix,SQInteger arg0); +#ifdef _DEBUG_DUMP + void dumpstack(SQInteger stackbase=-1, bool dumpall = false); +#endif + +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + SQObjectType GetType() {return OT_THREAD;} +#endif + void Finalize(); + void GrowCallStack() { + SQInteger newsize = _alloccallsstacksize*2; + _callstackdata.resize(newsize); + _callsstack = &_callstackdata[0]; + _alloccallsstacksize = newsize; + } + bool EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall); + void LeaveFrame(); + void Release(){ sq_delete(this,SQVM); } +//////////////////////////////////////////////////////////////////////////// + //stack functions for the api + void Remove(SQInteger n); + + static bool IsFalse(SQObjectPtr &o); + + void Pop(); + void Pop(SQInteger n); + void Push(const SQObjectPtr &o); + void PushNull(); + SQObjectPtr &Top(); + SQObjectPtr &PopGet(); + SQObjectPtr &GetUp(SQInteger n); + SQObjectPtr &GetAt(SQInteger n); + + SQObjectPtrVec _stack; + + SQInteger _top; + SQInteger _stackbase; + SQOuter *_openouters; + SQObjectPtr _roottable; + SQObjectPtr _lasterror; + SQObjectPtr _errorhandler; + + bool _debughook; + SQDEBUGHOOK _debughook_native; + SQObjectPtr _debughook_closure; + + SQObjectPtr temp_reg; + + + CallInfo* _callsstack; + SQInteger _callsstacksize; + SQInteger _alloccallsstacksize; + sqvector _callstackdata; + + ExceptionsTraps _etraps; + CallInfo *ci; + SQUserPointer _foreignptr; + //VMs sharing the same state + SQSharedState *_sharedstate; + SQInteger _nnativecalls; + SQInteger _nmetamethodscall; + SQRELEASEHOOK _releasehook; + //suspend infos + SQBool _suspended; + SQBool _suspended_root; + SQInteger _suspended_target; + SQInteger _suspended_traps; +}; + +struct AutoDec{ + AutoDec(SQInteger *n) { _n = n; } + ~AutoDec() { (*_n)--; } + SQInteger *_n; +}; + +inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));} + +#define _ss(_vm_) (_vm_)->_sharedstate + +#ifndef NO_GARBAGE_COLLECTOR +#define _opt_ss(_vm_) (_vm_)->_sharedstate +#else +#define _opt_ss(_vm_) NULL +#endif + +#define PUSH_CALLINFO(v,nci){ \ + SQInteger css = v->_callsstacksize; \ + if(css == v->_alloccallsstacksize) { \ + v->GrowCallStack(); \ + } \ + v->ci = &v->_callsstack[css]; \ + *(v->ci) = nci; \ + v->_callsstacksize++; \ +} + +#define POP_CALLINFO(v){ \ + SQInteger css = --v->_callsstacksize; \ + v->ci->_closure.Null(); \ + v->ci = css?&v->_callsstack[css-1]:NULL; \ +} +#endif //_SQVM_H_ diff --git a/mp/src/vscript/vscript.cpp b/mp/src/vscript/vscript.cpp new file mode 100644 index 00000000..af1570ad --- /dev/null +++ b/mp/src/vscript/vscript.cpp @@ -0,0 +1,82 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom implementation of VScript in Source 2013, created from scratch +// using the Alien Swarm SDK as a reference for Valve's library. +// +// Author(s): ReDucTor (header written by Blixibon) +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" + +#include "vscript_bindings_base.h" + +#include "tier1/tier1.h" + +IScriptVM* makeSquirrelVM(); + +int vscript_token = 0; + +class CScriptManager : public CTier1AppSystem +{ +public: + virtual IScriptVM* CreateVM(ScriptLanguage_t language) override + { + IScriptVM* pScriptVM = nullptr; + if (language == SL_SQUIRREL) + { + pScriptVM = makeSquirrelVM(); + } + else + { + return nullptr; + } + + if (pScriptVM == nullptr) + { + return nullptr; + } + + if (!pScriptVM->Init()) + { + delete pScriptVM; + return nullptr; + } + + // Register base bindings for all VMs + RegisterBaseBindings( pScriptVM ); + + return pScriptVM; + } + + virtual void DestroyVM(IScriptVM * pScriptVM) override + { + if (pScriptVM) + { + pScriptVM->Shutdown(); + delete pScriptVM; + } + } + + // Mapbase moves CScriptKeyValues into the library so it could be used elsewhere + virtual HSCRIPT CreateScriptKeyValues( IScriptVM *pVM, KeyValues *pKV, bool bAllowDestruct ) override + { + CScriptKeyValues *pSKV = new CScriptKeyValues( pKV ); + HSCRIPT hSKV = pVM->RegisterInstance( pSKV, bAllowDestruct ); + return hSKV; + } + + virtual KeyValues *GetKeyValuesFromScriptKV( IScriptVM *pVM, HSCRIPT hSKV ) override + { + CScriptKeyValues *pSKV = (hSKV ? (CScriptKeyValues*)pVM->GetInstanceValue( hSKV, GetScriptDesc( (CScriptKeyValues*)NULL ) ) : nullptr); + if (pSKV) + { + return pSKV->m_pKeyValues; + } + + return nullptr; + } +}; + +EXPOSE_SINGLE_INTERFACE(CScriptManager, IScriptManager, VSCRIPT_INTERFACE_VERSION); \ No newline at end of file diff --git a/mp/src/vscript/vscript.vpc b/mp/src/vscript/vscript.vpc new file mode 100644 index 00000000..425afb8a --- /dev/null +++ b/mp/src/vscript/vscript.vpc @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// VSCRIPT.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR ".." + +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;.\squirrel\include" + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] + } +} + +$Project "VScript" +{ + $Folder "Source Files" + { + + $File "vscript.cpp" + $File "vscript_squirrel.cpp" + $File "vscript_squirrel.nut" + + $File "vscript_bindings_base.cpp" + $File "vscript_bindings_base.h" + $File "vscript_bindings_math.cpp" + $File "vscript_bindings_math.h" + + $Folder "squirrel" + { + $File ".\squirrel\sqstdlib\sqstdaux.cpp" \ + ".\squirrel\sqstdlib\sqstdblob.cpp" \ + ".\squirrel\sqstdlib\sqstdio.cpp" \ + ".\squirrel\sqstdlib\sqstdmath.cpp" \ + ".\squirrel\sqstdlib\sqstdrex.cpp" \ + ".\squirrel\sqstdlib\sqstdstream.cpp" \ + ".\squirrel\sqstdlib\sqstdstring.cpp" \ + ".\squirrel\sqstdlib\sqstdsystem.cpp" \ + ".\squirrel\squirrel\sqapi.cpp" \ + ".\squirrel\squirrel\sqbaselib.cpp" \ + ".\squirrel\squirrel\sqclass.cpp" \ + ".\squirrel\squirrel\sqcompiler.cpp" \ + ".\squirrel\squirrel\sqdebug.cpp" \ + ".\squirrel\squirrel\sqfuncstate.cpp" \ + ".\squirrel\squirrel\sqlexer.cpp" \ + ".\squirrel\squirrel\sqmem.cpp" \ + ".\squirrel\squirrel\sqobject.cpp" \ + ".\squirrel\squirrel\sqstate.cpp" \ + ".\squirrel\squirrel\sqtable.cpp" \ + ".\squirrel\squirrel\sqvm.cpp" + { + $Configuration + { + $Compiler + { + // Squirrel third party library is full of warnings + $AdditionalOptions "$BASE /wd4100 /wd4611 /wd4127 /wd4244 /wd4702 /wd4706 /wd4800" + $TreatWarningsAsErrors "No" + } + } + } + } + } +} diff --git a/mp/src/vscript/vscript_bindings_base.cpp b/mp/src/vscript/vscript_bindings_base.cpp new file mode 100644 index 00000000..56da8435 --- /dev/null +++ b/mp/src/vscript/vscript_bindings_base.cpp @@ -0,0 +1,640 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript functions, constants, etc. registered within the library itself. +// +// This is for things which don't have to depend on server/client and can be accessed +// from anywhere. +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" + +#include "tier1/tier1.h" +#include "tier1/fmtstr.h" + +#include +#include "icommandline.h" +#include "worldsize.h" +#include "bspflags.h" + +#include + +#include "vscript_bindings_base.h" +#include "vscript_bindings_math.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IScriptManager *scriptmanager; + +//============================================================================= +// +// Prints +// +//============================================================================= +static void ScriptMsg( const char *msg ) +{ + Msg( "%s", msg ); +} + +static void ScriptColorPrint( int r, int g, int b, const char *pszMsg ) +{ + const Color clr(r, g, b, 255); + ConColorMsg( clr, "%s", pszMsg ); +} + +static void ScriptColorPrintL( int r, int g, int b, const char *pszMsg ) +{ + const Color clr(r, g, b, 255); + ConColorMsg( clr, "%s\n", pszMsg ); +} + +//============================================================================= +// +// Convar Lookup +// +//============================================================================= +class CScriptConvarLookup +{ +public: + + float GetFloat( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetFloat(); + } + + int GetInt( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetInt(); + } + + bool GetBool( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetBool(); + } + + const char *GetStr( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetString(); + } + + const char *GetDefaultValue( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetDefault(); + } + + bool IsFlagSet( const char *pszConVar, int nFlags ) + { + ConVarRef cvar( pszConVar ); + return cvar.IsFlagSet( nFlags ); + } + + void SetFloat( const char *pszConVar, float value ) + { + SetValue( pszConVar, value ); + } + + void SetInt( const char *pszConVar, int value ) + { + SetValue( pszConVar, value ); + } + + void SetBool( const char *pszConVar, bool value ) + { + SetValue( pszConVar, value ); + } + + void SetStr( const char *pszConVar, const char *value ) + { + SetValue( pszConVar, value ); + } + + template + void SetValue( const char *pszConVar, T value ) + { + ConVarRef cvar( pszConVar ); + if (!cvar.IsValid()) + return; + + // FCVAR_NOT_CONNECTED can be used to protect specific convars from nefarious interference + if (cvar.IsFlagSet(FCVAR_NOT_CONNECTED)) + return; + + cvar.SetValue( value ); + } + +private: +} g_ScriptConvarLookup; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarLookup, "CConvars", SCRIPT_SINGLETON "Provides an interface for getting and setting convars." ) + DEFINE_SCRIPTFUNC( GetFloat, "Returns the convar as a float. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetInt, "Returns the convar as an int. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetBool, "Returns the convar as a bool. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetStr, "Returns the convar as a string. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetDefaultValue, "Returns the convar's default value as a string. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( IsFlagSet, "Returns the convar's flags. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( SetFloat, "Sets the value of the convar as a float." ) + DEFINE_SCRIPTFUNC( SetInt, "Sets the value of the convar as an int." ) + DEFINE_SCRIPTFUNC( SetBool, "Sets the value of the convar as a bool." ) + DEFINE_SCRIPTFUNC( SetStr, "Sets the value of the convar as a string." ) +END_SCRIPTDESC(); + +//============================================================================= +// +// Command Line +// +//============================================================================= +class CGlobalSys +{ +public: + const char* ScriptGetCommandLine() + { + return CommandLine()->GetCmdLine(); + } + + bool CommandLineCheck(const char* name) + { + return !!CommandLine()->FindParm(name); + } + + const char* CommandLineCheckStr(const char* name) + { + return CommandLine()->ParmValue(name); + } + + float CommandLineCheckFloat(const char* name) + { + return CommandLine()->ParmValue(name, 0); + } + + int CommandLineCheckInt(const char* name) + { + return CommandLine()->ParmValue(name, 0); + } +} g_ScriptGlobalSys; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CGlobalSys, "CGlobalSys", SCRIPT_SINGLETON "GlobalSys" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetCommandLine, "GetCommandLine", "returns the command line" ) + DEFINE_SCRIPTFUNC( CommandLineCheck, "returns true if the command line param was used, otherwise false." ) + DEFINE_SCRIPTFUNC( CommandLineCheckStr, "returns the command line param as a string." ) + DEFINE_SCRIPTFUNC( CommandLineCheckFloat, "returns the command line param as a float." ) + DEFINE_SCRIPTFUNC( CommandLineCheckInt, "returns the command line param as an int." ) +END_SCRIPTDESC(); + +// ---------------------------------------------------------------------------- +// KeyValues access - CBaseEntity::ScriptGetKeyFromModel returns root KeyValues +// ---------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC_NAMED( ScriptFindKey, "FindKey", "Given a KeyValues object and a key name, find a KeyValues object associated with the key name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstSubKey, "GetFirstSubKey", "Given a KeyValues object, return the first sub key object" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNextKey, "GetNextKey", "Given a KeyValues object, return the next key object in a sub key group" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueInt, "GetKeyInt", "Given a KeyValues object and a key name, return associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueFloat, "GetKeyFloat", "Given a KeyValues object and a key name, return associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBool, "GetKeyBool", "Given a KeyValues object and a key name, return associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueString, "GetKeyString", "Given a KeyValues object and a key name, return associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptIsKeyValueEmpty, "IsKeyEmpty", "Given a KeyValues object and a key name, return true if key name has no value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); + + DEFINE_SCRIPTFUNC( TableToSubKeys, "Converts a script table to KeyValues." ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptFindOrCreateKey, "FindOrCreateKey", "Given a KeyValues object and a key name, find or create a KeyValues object associated with the key name" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetName, "GetName", "Given a KeyValues object, return its name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetInt, "GetInt", "Given a KeyValues object, return its own associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFloat, "GetFloat", "Given a KeyValues object, return its own associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetString, "GetString", "Given a KeyValues object, return its own associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBool, "GetBool", "Given a KeyValues object, return its own associated bool value" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueInt, "SetKeyInt", "Given a KeyValues object and a key name, set associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueFloat, "SetKeyFloat", "Given a KeyValues object and a key name, set associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueBool, "SetKeyBool", "Given a KeyValues object and a key name, set associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueString, "SetKeyString", "Given a KeyValues object and a key name, set associated string value" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetName, "SetName", "Given a KeyValues object, set its name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetInt, "SetInt", "Given a KeyValues object, set its own associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFloat, "SetFloat", "Given a KeyValues object, set its own associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetBool, "SetBool", "Given a KeyValues object, set its own associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetString, "SetString", "Given a KeyValues object, set its own associated string value" ); +END_SCRIPTDESC(); + +HSCRIPT CScriptKeyValues::ScriptFindKey( const char *pszName ) +{ + KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetFirstSubKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetFirstSubKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetNextKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetNextKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +int CScriptKeyValues::ScriptGetKeyValueInt( const char *pszName ) +{ + int i = m_pKeyValues->GetInt( pszName ); + return i; +} + +float CScriptKeyValues::ScriptGetKeyValueFloat( const char *pszName ) +{ + float f = m_pKeyValues->GetFloat( pszName ); + return f; +} + +const char *CScriptKeyValues::ScriptGetKeyValueString( const char *pszName ) +{ + const char *psz = m_pKeyValues->GetString( pszName ); + return psz; +} + +bool CScriptKeyValues::ScriptIsKeyValueEmpty( const char *pszName ) +{ + bool b = m_pKeyValues->IsEmpty( pszName ); + return b; +} + +bool CScriptKeyValues::ScriptGetKeyValueBool( const char *pszName ) +{ + bool b = m_pKeyValues->GetBool( pszName ); + return b; +} + +void CScriptKeyValues::ScriptReleaseKeyValues( ) +{ + m_pKeyValues->deleteThis(); + m_pKeyValues = NULL; +} + +void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) +{ + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &varKey, &varValue )) != -1) + { + switch (varValue.m_type) + { + case FIELD_CSTRING: m_pKeyValues->SetString( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_INTEGER: m_pKeyValues->SetInt( varKey.m_pszString, varValue.m_int ); break; + case FIELD_FLOAT: m_pKeyValues->SetFloat( varKey.m_pszString, varValue.m_float ); break; + case FIELD_BOOLEAN: m_pKeyValues->SetBool( varKey.m_pszString, varValue.m_bool ); break; + case FIELD_VECTOR: m_pKeyValues->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break; + } + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } +} + +HSCRIPT CScriptKeyValues::ScriptFindOrCreateKey( const char *pszName ) +{ + KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName, true); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +const char *CScriptKeyValues::ScriptGetName() +{ + const char *psz = m_pKeyValues->GetName(); + return psz; +} + +int CScriptKeyValues::ScriptGetInt() +{ + int i = m_pKeyValues->GetInt(); + return i; +} + +float CScriptKeyValues::ScriptGetFloat() +{ + float f = m_pKeyValues->GetFloat(); + return f; +} + +const char *CScriptKeyValues::ScriptGetString() +{ + const char *psz = m_pKeyValues->GetString(); + return psz; +} + +bool CScriptKeyValues::ScriptGetBool() +{ + bool b = m_pKeyValues->GetBool(); + return b; +} + + +void CScriptKeyValues::ScriptSetKeyValueInt( const char *pszName, int iValue ) +{ + m_pKeyValues->SetInt( pszName, iValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueFloat( const char *pszName, float flValue ) +{ + m_pKeyValues->SetFloat( pszName, flValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueString( const char *pszName, const char *pszValue ) +{ + m_pKeyValues->SetString( pszName, pszValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueBool( const char *pszName, bool bValue ) +{ + m_pKeyValues->SetBool( pszName, bValue ); +} + +void CScriptKeyValues::ScriptSetName( const char *pszValue ) +{ + m_pKeyValues->SetName( pszValue ); +} + +void CScriptKeyValues::ScriptSetInt( int iValue ) +{ + m_pKeyValues->SetInt( NULL, iValue ); +} + +void CScriptKeyValues::ScriptSetFloat( float flValue ) +{ + m_pKeyValues->SetFloat( NULL, flValue ); +} + +void CScriptKeyValues::ScriptSetString( const char *pszValue ) +{ + m_pKeyValues->SetString( NULL, pszValue ); +} + +void CScriptKeyValues::ScriptSetBool( bool bValue ) +{ + m_pKeyValues->SetBool( NULL, bValue ); +} + + +// constructors +CScriptKeyValues::CScriptKeyValues( KeyValues *pKeyValues = NULL ) +{ + if (pKeyValues == NULL) + { + m_pKeyValues = new KeyValues("CScriptKeyValues"); + } + else + { + m_pKeyValues = pKeyValues; + } +} + +// destructor +CScriptKeyValues::~CScriptKeyValues( ) +{ + if (m_pKeyValues) + { + m_pKeyValues->deleteThis(); + } + m_pKeyValues = NULL; +} + +//============================================================================= +// +// matrix3x4_t +// +//============================================================================= +CScriptColorInstanceHelper g_ColorScriptInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT( Color, "" ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPT_INSTANCE_HELPER( &g_ColorScriptInstanceHelper ) + + DEFINE_SCRIPTFUNC( SetColor, "Sets the color." ) + + DEFINE_SCRIPTFUNC( SetRawColor, "Sets the raw color integer." ) + DEFINE_SCRIPTFUNC( GetRawColor, "Gets the raw color integer." ) + + DEFINE_MEMBERVAR( "r", FIELD_CHARACTER, "Member variable for red." ) + DEFINE_MEMBERVAR( "g", FIELD_CHARACTER, "Member variable for green." ) + DEFINE_MEMBERVAR( "b", FIELD_CHARACTER, "Member variable for blue." ) + DEFINE_MEMBERVAR( "a", FIELD_CHARACTER, "Member variable for alpha. (transparency)" ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- + +bool CScriptColorInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + Color *pClr = ((Color *)p); + V_snprintf( pBuf, bufSize, "(color: (%i, %i, %i, %i))", pClr->r(), pClr->g(), pClr->b(), pClr->a() ); + return true; +} + +bool CScriptColorInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Color *pClr = ((Color *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'r': + variant = pClr->r(); + return true; + case 'g': + variant = pClr->g(); + return true; + case 'b': + variant = pClr->b(); + return true; + case 'a': + variant = pClr->a(); + return true; + } + } + return false; +} + +bool CScriptColorInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Color *pClr = ((Color *)p); + if ( strlen(pszKey) == 1 ) + { + int iVal; + variant.AssignTo( &iVal ); + switch (pszKey[0]) + { + // variant.AssignTo( &(*pClr)[0] ); + case 'r': + (*pClr)[0] = iVal; + return true; + case 'g': + (*pClr)[1] = iVal; + return true; + case 'b': + (*pClr)[2] = iVal; + return true; + case 'a': + (*pClr)[3] = iVal; + return true; + } + } + return false; +} + +//============================================================================= +//============================================================================= + +void RegisterBaseBindings( IScriptVM *pVM ) +{ + ScriptRegisterFunctionNamed( pVM, ScriptMsg, "Msg", "" ); + ScriptRegisterFunctionNamed( pVM, ScriptColorPrint, "printc", "Version of print() which takes a color before the message." ); + ScriptRegisterFunctionNamed( pVM, ScriptColorPrintL, "printcl", "Version of printl() which takes a color before the message." ); + + ScriptRegisterFunction( pVM, GetCPUUsage, "Get CPU usage percentage." ); + + //----------------------------------------------------------------------------- + + pVM->RegisterInstance( &g_ScriptConvarLookup, "Convars" ); + pVM->RegisterInstance( &g_ScriptGlobalSys, "GlobalSys" ); + + //----------------------------------------------------------------------------- + + pVM->RegisterClass( GetScriptDescForClass( CScriptKeyValues ) ); + + pVM->RegisterClass( GetScriptDescForClass( Color ) ); + + //----------------------------------------------------------------------------- + + // + // Math/world + // + ScriptRegisterConstant( pVM, MAX_COORD_FLOAT, "Maximum float coordinate." ); + ScriptRegisterConstant( pVM, MAX_TRACE_LENGTH, "Maximum traceable distance (assumes cubic world and trace from one corner to opposite)." ); + + // + // Trace Contents/Masks + // + ScriptRegisterConstant( pVM, CONTENTS_EMPTY, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_SOLID, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_WINDOW, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_AUX, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_GRATE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_SLIME, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_WATER, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_BLOCKLOS, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_OPAQUE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TESTFOGVOLUME, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TEAM1, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TEAM2, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_IGNORE_NODRAW_OPAQUE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_MOVEABLE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_AREAPORTAL, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_PLAYERCLIP, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_MONSTERCLIP, "Spatial content flags." ); + + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_0, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_90, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_180, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_270, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_UP, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_DOWN, "Spatial content flags." ); + + ScriptRegisterConstant( pVM, CONTENTS_ORIGIN, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_MONSTER, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_DEBRIS, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_DETAIL, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TRANSLUCENT, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_LADDER, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_HITBOX, "Spatial content flags." ); + + ScriptRegisterConstant( pVM, LAST_VISIBLE_CONTENTS, "Contains last visible spatial content flags." ); + ScriptRegisterConstant( pVM, ALL_VISIBLE_CONTENTS, "Contains all visible spatial content flags." ); + + ScriptRegisterConstant( pVM, MASK_SOLID, "Spatial content mask representing solid objects (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_PLAYERSOLID, "Spatial content mask representing objects solid to the player, including player clips (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_NPCSOLID, "Spatial content mask representing objects solid to NPCs, including NPC clips (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_WATER, "Spatial content mask representing water and slime solids (CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_SLIME)" ); + ScriptRegisterConstant( pVM, MASK_OPAQUE, "Spatial content mask representing objects which block lighting (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_OPAQUE)" ); + ScriptRegisterConstant( pVM, MASK_OPAQUE_AND_NPCS, "Spatial content mask equivalent to MASK_OPAQUE, but also including NPCs (MASK_OPAQUE|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( pVM, MASK_BLOCKLOS, "Spatial content mask representing objects which block LOS for AI (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_BLOCKLOS)" ); + ScriptRegisterConstant( pVM, MASK_BLOCKLOS_AND_NPCS, "Spatial content mask equivalent to MASK_BLOCKLOS, but also including NPCs (MASK_BLOCKLOS|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( pVM, MASK_VISIBLE, "Spatial content mask representing objects which block LOS for players (MASK_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE)" ); + ScriptRegisterConstant( pVM, MASK_VISIBLE_AND_NPCS, "Spatial content mask equivalent to MASK_VISIBLE, but also including NPCs (MASK_OPAQUE_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE)" ); + ScriptRegisterConstant( pVM, MASK_SHOT, "Spatial content mask representing objects solid to bullets (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_HITBOX)" ); + ScriptRegisterConstant( pVM, MASK_SHOT_HULL, "Spatial content mask representing objects solid to non-raycasted weapons, including grates (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_SHOT_PORTAL, "Spatial content mask equivalent to MASK_SHOT, but excluding debris and not using expensive hitbox calculations (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( pVM, MASK_SOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_SOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_PLAYERSOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_PLAYERSOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_PLAYERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_NPCSOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_NPCSOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_NPCWORLDSTATIC, "Spatial content mask representing objects static to NPCs, used for nodegraph rebuilding (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_SPLITAREAPORTAL, "Spatial content mask representing objects which can split areaportals (CONTENTS_WATER|CONTENTS_SLIME)" ); + + // + // Misc. General + // + ScriptRegisterConstant( pVM, FCVAR_NONE, "Empty convar flag." ); + ScriptRegisterConstant( pVM, FCVAR_UNREGISTERED, "If this convar flag is set, it isn't added to linked list, etc." ); + ScriptRegisterConstant( pVM, FCVAR_DEVELOPMENTONLY, "If this convar flag is set, it's hidden in \"retail\" DLLs." ); + ScriptRegisterConstant( pVM, FCVAR_GAMEDLL, "This convar flag is defined in server DLL convars." ); + ScriptRegisterConstant( pVM, FCVAR_CLIENTDLL, "This convar flag is defined in client DLL convars." ); + ScriptRegisterConstant( pVM, FCVAR_HIDDEN, "If this convar flag is set, it doesn't appear in the console or any searching tools, but it can still be set." ); + ScriptRegisterConstant( pVM, FCVAR_PROTECTED, "This convar flag prevents convars with secure data (e.g. passwords) from sending full data to clients, only sending 1 if non-zero and 0 otherwise." ); + ScriptRegisterConstant( pVM, FCVAR_SPONLY, "If this convar flag is set, it can't be changed by clients connected to a multiplayer server." ); + ScriptRegisterConstant( pVM, FCVAR_ARCHIVE, "If this convar flag is set, its value will be saved when the game is exited." ); + ScriptRegisterConstant( pVM, FCVAR_NOTIFY, "If this convar flag is set, it will notify players when it is changed." ); + ScriptRegisterConstant( pVM, FCVAR_USERINFO, "If this convar flag is set, it will be marked as info which plays a part in how the server identifies a client." ); + ScriptRegisterConstant( pVM, FCVAR_PRINTABLEONLY, "If this convar flag is set, it cannot contain unprintable characters. Used for player name cvars, etc." ); + ScriptRegisterConstant( pVM, FCVAR_UNLOGGED, "If this convar flag is set, it will not log its changes if a log is being created." ); + ScriptRegisterConstant( pVM, FCVAR_NEVER_AS_STRING, "If this convar flag is set, it will never be printed as a string." ); + ScriptRegisterConstant( pVM, FCVAR_REPLICATED, "If this convar flag is set, it will enforce a serverside value on any clientside counterparts. (also known as FCAR_SERVER)" ); + ScriptRegisterConstant( pVM, FCVAR_DEMO, "If this convar flag is set, it will be recorded when starting a demo file." ); + ScriptRegisterConstant( pVM, FCVAR_DONTRECORD, "If this convar flag is set, it will NOT be recorded when starting a demo file." ); + ScriptRegisterConstant( pVM, FCVAR_RELOAD_MATERIALS, "If this convar flag is set, it will force a material reload when it changes." ); + ScriptRegisterConstant( pVM, FCVAR_RELOAD_TEXTURES, "If this convar flag is set, it will force a texture reload when it changes." ); + ScriptRegisterConstant( pVM, FCVAR_NOT_CONNECTED, "If this convar flag is set, it cannot be changed by a client connected to the server." ); + ScriptRegisterConstant( pVM, FCVAR_MATERIAL_SYSTEM_THREAD, "This convar flag indicates it's read from the material system thread." ); + ScriptRegisterConstant( pVM, FCVAR_ARCHIVE_XBOX, "If this convar flag is set, it will be archived on the Xbox config." ); + ScriptRegisterConstant( pVM, FCVAR_ACCESSIBLE_FROM_THREADS, "If this convar flag is set, it will be accessible from the material system thread." ); + ScriptRegisterConstant( pVM, FCVAR_SERVER_CAN_EXECUTE, "If this convar flag is set, the server will be allowed to execute it as a client command." ); + ScriptRegisterConstant( pVM, FCVAR_SERVER_CANNOT_QUERY, "If this convar flag is set, the server will not be allowed to query its value." ); + ScriptRegisterConstant( pVM, FCVAR_CLIENTCMD_CAN_EXECUTE, "If this convar flag is set, any client will be allowed to execute this command." ); + + //----------------------------------------------------------------------------- + + RegisterMathBaseBindings( pVM ); +} diff --git a/mp/src/vscript/vscript_bindings_base.h b/mp/src/vscript/vscript_bindings_base.h new file mode 100644 index 00000000..a2d9fb9a --- /dev/null +++ b/mp/src/vscript/vscript_bindings_base.h @@ -0,0 +1,75 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_BINDINGS_BASE +#define VSCRIPT_BINDINGS_BASE +#ifdef _WIN32 +#pragma once +#endif + +#include "vscript/ivscript.h" +#include "tier1/KeyValues.h" + +// ---------------------------------------------------------------------------- +// KeyValues access +// ---------------------------------------------------------------------------- +class CScriptKeyValues +{ +public: + CScriptKeyValues( KeyValues *pKeyValues ); + ~CScriptKeyValues( ); + + HSCRIPT ScriptFindKey( const char *pszName ); + HSCRIPT ScriptGetFirstSubKey( void ); + HSCRIPT ScriptGetNextKey( void ); + int ScriptGetKeyValueInt( const char *pszName ); + float ScriptGetKeyValueFloat( const char *pszName ); + const char *ScriptGetKeyValueString( const char *pszName ); + bool ScriptIsKeyValueEmpty( const char *pszName ); + bool ScriptGetKeyValueBool( const char *pszName ); + void ScriptReleaseKeyValues( ); + + // Functions below are new with Mapbase + void TableToSubKeys( HSCRIPT hTable ); + + HSCRIPT ScriptFindOrCreateKey( const char *pszName ); + + const char *ScriptGetName(); + int ScriptGetInt(); + float ScriptGetFloat(); + const char *ScriptGetString(); + bool ScriptGetBool(); + + void ScriptSetKeyValueInt( const char *pszName, int iValue ); + void ScriptSetKeyValueFloat( const char *pszName, float flValue ); + void ScriptSetKeyValueString( const char *pszName, const char *pszValue ); + void ScriptSetKeyValueBool( const char *pszName, bool bValue ); + void ScriptSetName( const char *pszValue ); + void ScriptSetInt( int iValue ); + void ScriptSetFloat( float flValue ); + void ScriptSetString( const char *pszValue ); + void ScriptSetBool( bool bValue ); + + KeyValues *GetKeyValues() { return m_pKeyValues; } + + KeyValues *m_pKeyValues; // actual KeyValue entity +}; + +//----------------------------------------------------------------------------- +// Exposes Color to VScript +//----------------------------------------------------------------------------- +class CScriptColorInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); +}; + +void RegisterBaseBindings( IScriptVM *pVM ); + +#endif diff --git a/mp/src/vscript/vscript_bindings_math.cpp b/mp/src/vscript/vscript_bindings_math.cpp new file mode 100644 index 00000000..cb6c594d --- /dev/null +++ b/mp/src/vscript/vscript_bindings_math.cpp @@ -0,0 +1,456 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript functions, constants, etc. registered within the library itself. +// +// This is for things which don't have to depend on server/client and can be accessed +// from anywhere. +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" + +#include "tier1/tier1.h" + +#include +#include "worldsize.h" + +#include + +#include "vscript_bindings_math.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//============================================================================= +// +// matrix3x4_t +// +//============================================================================= +BEGIN_SCRIPTDESC_ROOT_NAMED( matrix3x4_t, "matrix3x4_t", "A 3x4 matrix transform." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC( Init, "Creates a matrix where the X axis = forward, the Y axis = left, and the Z axis = up." ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptConcatTransforms( HSCRIPT hMat1, HSCRIPT hMat2, HSCRIPT hOut ) +{ + if (!hMat1 || !hMat2 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + ConcatTransforms( *pMat1, *pMat2, *pOut ); +} + +void ScriptMatrixCopy( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixCopy( *pMat1, *pOut ); +} + +void ScriptMatrixInvert( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixInvert( *pMat1, *pOut ); +} + +void ScriptMatricesAreEqual( HSCRIPT hMat1, HSCRIPT hMat2 ) +{ + if (!hMat1 || !hMat2) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + + MatricesAreEqual( *pMat1, *pMat2 ); +} + +const Vector& ScriptMatrixGetColumn( HSCRIPT hMat1, int column ) +{ + static Vector outvec; + outvec.Zero(); + if (!hMat1) + return outvec; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixGetColumn( *pMat1, column, outvec ); + return outvec; +} + +void ScriptMatrixSetColumn( const Vector& vecset, int column, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + static Vector outvec; + MatrixSetColumn( vecset, column, *pMat1 ); +} + +void ScriptMatrixAngles( HSCRIPT hMat1, const QAngle& angset, const Vector& vecset ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixAngles( *pMat1, *const_cast(&angset), *const_cast(&vecset) ); +} + +void ScriptAngleMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleMatrix( angset, vecset, *pMat1 ); +} + +void ScriptAngleIMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleIMatrix( angset, vecset, *pMat1 ); +} + +void ScriptSetIdentityMatrix( HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetIdentityMatrix( *pMat1 ); +} + +void ScriptSetScaleMatrix( float x, float y, float z, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetScaleMatrix( x, y, z, *pMat1 ); +} + +//============================================================================= +// +// Quaternion +// +//============================================================================= +CScriptQuaternionInstanceHelper g_QuaternionScriptInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT_NAMED( Quaternion, "Quaternion", "A quaternion." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPT_INSTANCE_HELPER( &g_QuaternionScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( ScriptInit, "Init", "Creates a quaternion with the given values." ) + + DEFINE_MEMBERVAR( "x", FIELD_FLOAT, "The quaternion's i axis component." ) + DEFINE_MEMBERVAR( "y", FIELD_FLOAT, "The quaternion's j axis component." ) + DEFINE_MEMBERVAR( "z", FIELD_FLOAT, "The quaternion's k axis component." ) + DEFINE_MEMBERVAR( "w", FIELD_FLOAT, "The quaternion's scalar component." ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- + +bool CScriptQuaternionInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + Quaternion *pQuat = ((Quaternion *)p); + V_snprintf( pBuf, bufSize, "(quaternion: (%f, %f, %f, %f))", pQuat->x, pQuat->y, pQuat->z, pQuat->w ); + return true; +} + +bool CScriptQuaternionInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'x': + variant = pQuat->x; + return true; + case 'y': + variant = pQuat->y; + return true; + case 'z': + variant = pQuat->z; + return true; + case 'w': + variant = pQuat->w; + return true; + } + } + return false; +} + +bool CScriptQuaternionInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'x': + variant.AssignTo( &pQuat->x ); + return true; + case 'y': + variant.AssignTo( &pQuat->y ); + return true; + case 'z': + variant.AssignTo( &pQuat->z ); + return true; + case 'w': + variant.AssignTo( &pQuat->w ); + return true; + } + } + return false; +} + +ScriptVariant_t *CScriptQuaternionInstanceHelper::Add( void *p, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + + float flAdd; + variant.AssignTo( &flAdd ); + + (*pQuat)[0] += flAdd; + (*pQuat)[1] += flAdd; + (*pQuat)[2] += flAdd; + (*pQuat)[3] += flAdd; + + static ScriptVariant_t result; + result = (HSCRIPT)p; + return &result; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptQuaternionAdd( HSCRIPT hQuat1, HSCRIPT hQuat2, HSCRIPT hOut ) +{ + if (!hQuat1 || !hQuat2 || !hOut) + return; + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + Quaternion *pQuat2 = ToQuaternion( hQuat2 ); + Quaternion *pOut = ToQuaternion( hOut ); + + QuaternionAdd( *pQuat1, *pQuat2, *pOut ); +} + +void ScriptMatrixQuaternion( HSCRIPT hMat1, HSCRIPT hQuat1 ) +{ + if (!hMat1 || !hQuat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + + MatrixQuaternion( *pMat1, *pQuat1 ); +} + +void ScriptQuaternionMatrix( HSCRIPT hQuat1, HSCRIPT hMat1 ) +{ + if (!hQuat1 || !hMat1) + return; + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + QuaternionMatrix( *pQuat1, *pMat1 ); +} + +QAngle ScriptQuaternionAngles( HSCRIPT hQuat1 ) +{ + if (!hQuat1) + return QAngle(); + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + + QAngle angles; + QuaternionAngles( *pQuat1, angles ); + return angles; +} + +//============================================================================= +// +// Misc. Vector/QAngle functions +// +//============================================================================= + +const Vector& ScriptAngleVectors( const QAngle &angles ) +{ + static Vector forward; + AngleVectors( angles, &forward ); + return forward; +} + +const QAngle& ScriptVectorAngles( const Vector &forward ) +{ + static QAngle angles; + VectorAngles( forward, angles ); + return angles; +} + +const Vector& ScriptVectorRotate( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorRotate( in, *ToMatrix3x4(hMat), out ); + return out; +} + +const Vector& ScriptVectorIRotate( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorIRotate( in, *ToMatrix3x4(hMat), out ); + return out; +} + +const Vector& ScriptVectorTransform( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorTransform( in, *ToMatrix3x4( hMat ), out ); + return out; +} + +const Vector& ScriptVectorITransform( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorITransform( in, *ToMatrix3x4( hMat ), out ); + return out; +} + +const Vector& ScriptCalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point ) +{ + static Vector outvec; + CalcClosestPointOnAABB( mins, maxs, point, outvec ); + return outvec; +} + +const Vector& ScriptCalcClosestPointOnLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLine( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLine( point, vLineA, vLineB ); +} + +const Vector& ScriptCalcClosestPointOnLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLineSegment( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLineSegment( point, vLineA, vLineB ); +} + +void RegisterMathBaseBindings( IScriptVM *pVM ) +{ + ScriptRegisterConstantNamed( pVM, ((float)(180.f / M_PI_F)), "RAD2DEG", "" ); + ScriptRegisterConstantNamed( pVM, ((float)(M_PI_F / 180.f)), "DEG2RAD", "" ); + + ScriptRegisterFunction( pVM, RandomFloat, "Generate a random floating point number within a range, inclusive." ); + ScriptRegisterFunction( pVM, RandomInt, "Generate a random integer within a range, inclusive." ); + //ScriptRegisterFunction( pVM, Approach, "Returns a value which approaches the target value from the input value with the specified speed." ); + ScriptRegisterFunction( pVM, ApproachAngle, "Returns an angle which approaches the target angle from the input angle with the specified speed." ); + ScriptRegisterFunction( pVM, AngleDiff, "Returns the degrees difference between two yaw angles." ); + //ScriptRegisterFunction( pVM, AngleDistance, "Returns the distance between two angles." ); + ScriptRegisterFunction( pVM, AngleNormalize, "Clamps an angle to be in between -360 and 360." ); + ScriptRegisterFunction( pVM, AngleNormalizePositive, "Clamps an angle to be in between 0 and 360." ); + ScriptRegisterFunction( pVM, AnglesAreEqual, "Checks if two angles are equal based on a given tolerance value." ); + + // + // matrix3x4_t + // + pVM->RegisterClass( GetScriptDescForClass( matrix3x4_t ) ); + + ScriptRegisterFunctionNamed( pVM, ScriptFreeMatrixInstance, "FreeMatrixInstance", "Frees an allocated matrix instance." ); + + ScriptRegisterFunctionNamed( pVM, ScriptConcatTransforms, "ConcatTransforms", "Concatenates two transformation matrices into another matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixCopy, "MatrixCopy", "Copies a matrix to another matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixInvert, "MatrixInvert", "Inverts a matrix and copies the result to another matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatricesAreEqual, "MatricesAreEqual", "Checks if two matrices are equal." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixGetColumn, "MatrixGetColumn", "Gets the column of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixSetColumn, "MatrixSetColumn", "Sets the column of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixAngles, "MatrixAngles", "Gets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptAngleMatrix, "AngleMatrix", "Sets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptAngleIMatrix, "AngleIMatrix", "Sets the inverted angles and position of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptSetIdentityMatrix, "SetIdentityMatrix", "Turns a matrix into an identity matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptSetScaleMatrix, "SetScaleMatrix", "Scales a matrix." ); + + // + // Quaternion + // + pVM->RegisterClass( GetScriptDescForClass( Quaternion ) ); + + ScriptRegisterFunctionNamed( pVM, ScriptFreeQuaternionInstance, "FreeQuaternionInstance", "Frees an allocated quaternion instance." ); + + ScriptRegisterFunctionNamed( pVM, ScriptQuaternionAdd, "QuaternionAdd", "Adds two quaternions together into another quaternion." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixQuaternion, "MatrixQuaternion", "Converts a matrix to a quaternion." ); + ScriptRegisterFunctionNamed( pVM, ScriptQuaternionMatrix, "QuaternionMatrix", "Converts a quaternion to a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptQuaternionAngles, "QuaternionAngles", "Converts a quaternion to angles." ); + + // + // Misc. Vector/QAngle functions + // + ScriptRegisterFunctionNamed( pVM, ScriptAngleVectors, "AngleVectors", "Turns an angle into a direction vector." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorAngles, "VectorAngles", "Turns a direction vector into an angle." ); + + ScriptRegisterFunctionNamed( pVM, ScriptVectorRotate, "VectorRotate", "Rotates a vector with a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorIRotate, "VectorIRotate", "Rotates a vector with the inverse of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorTransform, "VectorTransform", "Transforms a vector with a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorITransform, "VectorITransform", "Transforms a vector with the inverse of a matrix." ); + + ScriptRegisterFunction( pVM, CalcSqrDistanceToAABB, "Returns the squared distance to a bounding box." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcClosestPointOnAABB, "CalcClosestPointOnAABB", "Returns the closest point on a bounding box." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcDistanceToLine, "CalcDistanceToLine", "Returns the distance to a line." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcClosestPointOnLine, "CalcClosestPointOnLine", "Returns the closest point on a line." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcDistanceToLineSegment, "CalcDistanceToLineSegment", "Returns the distance to a line segment." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcClosestPointOnLineSegment, "CalcClosestPointOnLineSegment", "Returns the closest point on a line segment." ); +} diff --git a/mp/src/vscript/vscript_bindings_math.h b/mp/src/vscript/vscript_bindings_math.h new file mode 100644 index 00000000..c2d960b5 --- /dev/null +++ b/mp/src/vscript/vscript_bindings_math.h @@ -0,0 +1,62 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: Shared VScript math functions. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_BINDINGS_MATH +#define VSCRIPT_BINDINGS_MATH +#ifdef _WIN32 +#pragma once +#endif + +void RegisterMathBaseBindings( IScriptVM *pVM ); + +// Some base bindings require VM functions +extern IScriptVM *g_pScriptVM; + +//----------------------------------------------------------------------------- +// Exposes matrix3x4_t to VScript +//----------------------------------------------------------------------------- +inline matrix3x4_t *ToMatrix3x4( HSCRIPT hMat ) { return HScriptToClass( hMat ); } + +static void ScriptFreeMatrixInstance( HSCRIPT hMat ) +{ + matrix3x4_t *smatrix = HScriptToClass( hMat ); + if (smatrix) + { + g_pScriptVM->RemoveInstance( hMat ); + delete smatrix; + } +} + +//----------------------------------------------------------------------------- +// Exposes Quaternion to VScript +//----------------------------------------------------------------------------- +class CScriptQuaternionInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); + + ScriptVariant_t *Add( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Subtract( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Multiply( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Divide( void *p, ScriptVariant_t &variant ); +}; + +inline Quaternion *ToQuaternion( HSCRIPT hQuat ) { return HScriptToClass( hQuat ); } + +static void ScriptFreeQuaternionInstance( HSCRIPT hQuat ) +{ + Quaternion *squat = HScriptToClass( hQuat ); + if (squat) + { + g_pScriptVM->RemoveInstance( hQuat ); + delete squat; + } +} + +#endif diff --git a/mp/src/vscript/vscript_squirrel.cpp b/mp/src/vscript/vscript_squirrel.cpp new file mode 100644 index 00000000..7d6795ca --- /dev/null +++ b/mp/src/vscript/vscript_squirrel.cpp @@ -0,0 +1,3557 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom implementation of VScript in Source 2013, created from scratch +// using the Alien Swarm SDK as a reference for Valve's library. +// +// Author(s): ReDucTor (header written by Blixibon) +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlmap.h" +#include "tier1/utlstring.h" + +#include "squirrel.h" +#include "sqstdaux.h" +//#include "sqstdblob.h" +//#include "sqstdsystem.h" +#include "sqstdtime.h" +//#include "sqstdio.h" +#include "sqstdmath.h" +#include "sqstdstring.h" + +// HACK: Include internal parts of squirrel for serialization +#include "squirrel/squirrel/sqobject.h" +#include "squirrel/squirrel/sqstate.h" +#include "squirrel/squirrel/sqtable.h" +#include "squirrel/squirrel/sqclass.h" +#include "squirrel/squirrel/sqfuncproto.h" +#include "squirrel/squirrel/sqvm.h" +#include "squirrel/squirrel/sqclosure.h" + +#include "color.h" +#include "tier1/utlbuffer.h" +#include "tier1/mapbase_con_groups.h" + +#include "vscript_squirrel.nut" + +#include + +struct WriteStateMap +{ + CUtlMap cache; + WriteStateMap() : cache(DefLessFunc(void*)) + {} + + bool CheckCache(CUtlBuffer* pBuffer, void* ptr) + { + auto idx = cache.Find(ptr); + if (idx != cache.InvalidIndex()) + { + pBuffer->PutInt(cache[idx]); + return true; + } + else + { + int newIdx = cache.Count(); + cache.Insert(ptr, newIdx); + pBuffer->PutInt(newIdx); + return false; + } + } +}; + +struct ReadStateMap +{ + CUtlMap cache; + HSQUIRRELVM vm_; + ReadStateMap(HSQUIRRELVM vm) : cache(DefLessFunc(int)), vm_(vm) + {} + + ~ReadStateMap() + { + FOR_EACH_MAP_FAST(cache, i) + { + HSQOBJECT& obj = cache[i]; + sq_release(vm_, &obj); + } + } + + bool CheckCache(CUtlBuffer* pBuffer, HSQOBJECT** obj) + { + int marker = pBuffer->GetInt(); + + auto idx = cache.Find(marker); + if (idx != cache.InvalidIndex()) + { + *obj = &cache[idx]; + return true; + } + else + { + HSQOBJECT temp; + sq_resetobject(&temp); + auto idx = cache.Insert(marker, temp); + *obj = &cache[idx]; + return false; + } + } +}; + +class SquirrelVM : public IScriptVM +{ +public: + virtual bool Init() override; + virtual void Shutdown() override; + + virtual bool ConnectDebugger() override; + virtual void DisconnectDebugger() override; + + virtual ScriptLanguage_t GetLanguage() override; + virtual const char* GetLanguageName() override; + + virtual void AddSearchPath(const char* pszSearchPath) override; + + //-------------------------------------------------------- + + virtual bool Frame(float simTime) override; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run(const char* pszScript, bool bWait = true) override; + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript(const char* pszScript, const char* pszId = NULL) override; + virtual void ReleaseScript(HSCRIPT) override; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run(HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true) override; + virtual ScriptStatus_t Run(HSCRIPT hScript, bool bWait) override; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope(const char* pszScope, HSCRIPT hParent = NULL) override; + virtual void ReleaseScope(HSCRIPT hScript) override; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction(const char* pszFunction, HSCRIPT hScope = NULL) override; + virtual void ReleaseFunction(HSCRIPT hScript) override; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) override; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) override; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass(ScriptClassDesc_t* pClassDesc) override; + + //-------------------------------------------------------- + // External constants + //-------------------------------------------------------- + virtual void RegisterConstant(ScriptConstantBinding_t *pScriptConstant) override; + + //-------------------------------------------------------- + // External enums + //-------------------------------------------------------- + virtual void RegisterEnum(ScriptEnumDesc_t *pEnumDesc) override; + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + + virtual HSCRIPT RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct = false) override; + virtual void SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) override; + virtual void RemoveInstance(HSCRIPT hInstance) override; + + virtual void* GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpectedType = NULL) override; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) override; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists(HSCRIPT hScope, const char* pszKey) override; + + virtual bool SetValue(HSCRIPT hScope, const char* pszKey, const char* pszValue) override; + virtual bool SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVariant_t& value) override; + + virtual void CreateTable(ScriptVariant_t& Table) override; + virtual int GetNumTableEntries(HSCRIPT hScope) override; + virtual int GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey, ScriptVariant_t* pValue) override; + + virtual bool GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) override; + virtual void ReleaseValue(ScriptVariant_t& value) override; + + virtual bool ClearValue(HSCRIPT hScope, const char* pszKey) override; + + // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) override; + virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) override; + + //---------------------------------------------------------------------------- + + virtual void WriteState(CUtlBuffer* pBuffer) override; + virtual void ReadState(CUtlBuffer* pBuffer) override; + virtual void RemoveOrphanInstances() override; + + virtual void DumpState() override; + + virtual void SetOutputCallback(ScriptOutputFunc_t pFunc) override; + virtual void SetErrorCallback(ScriptErrorFunc_t pFunc) override; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException(const char* pszExceptionText) override; + + + void WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQInteger idx); + void ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState); + HSQUIRRELVM vm_ = nullptr; + HSQOBJECT lastError_; + HSQOBJECT vectorClass_; + HSQOBJECT regexpClass_; +}; + +SQUserPointer TYPETAG_VECTOR = "VectorTypeTag"; + +namespace SQVector +{ + SQInteger Construct(HSQUIRRELVM vm) + { + // TODO: There must be a nicer way to store the data with the actual instance, there are + // default slots but they are really just pointers anyway and you need to hold a member + // pointer which is yet another pointer dereference + int numParams = sq_gettop(vm); + + float x = 0; + float y = 0; + float z = 0; + + if ((numParams >= 2 && SQ_FAILED(sq_getfloat(vm, 2, &x))) || + (numParams >= 3 && SQ_FAILED(sq_getfloat(vm, 3, &y))) || + (numParams >= 4 && SQ_FAILED(sq_getfloat(vm, 4, &z)))) + { + return sq_throwerror(vm, "Expected Vector(float x, float y, float z)"); + } + + SQUserPointer p; + sq_getinstanceup(vm, 1, &p, 0); + new (p) Vector(x, y, z); + + return 0; + } + + SQInteger Get(HSQUIRRELVM vm) + { + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected Vector._get(string)"); + } + + if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + { + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + Vector* v = nullptr; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Unable to get Vector"); + } + + int idx = key[0] - 'x'; + sq_pushfloat(vm, (*v)[idx]); + return 1; + } + + SQInteger Set(HSQUIRRELVM vm) + { + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected Vector._set(string)"); + } + + if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + { + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + Vector* v = nullptr; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Unable to get Vector"); + } + + float val = 0; + if (SQ_FAILED(sq_getfloat(vm, 3, &val))) + { + return sqstd_throwerrorf(vm, "Vector.%s expects float", key); + } + + int idx = key[0] - 'x'; + (*v)[idx] = val; + return 0; + } + + SQInteger Add(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) + (*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Sub(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) - (*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Multiply(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + + SQObjectType paramType = sq_gettype(vm, 2); + + float s = 0.0; + Vector* v2 = nullptr; + + if ((paramType & SQOBJECT_NUMERIC) && + SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * s); + sq_remove(vm, -2); + + return 1; + } + else if (paramType == OT_INSTANCE && + SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * (*v2)); + sq_remove(vm, -2); + + return 1; + } + else + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + } + + SQInteger Divide(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + + SQObjectType paramType = sq_gettype(vm, 2); + + float s = 0.0; + Vector* v2 = nullptr; + + if ((paramType & SQOBJECT_NUMERIC) && + SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) / s); + sq_remove(vm, -2); + + return 1; + } + else if (paramType == OT_INSTANCE && + SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) / (*v2)); + sq_remove(vm, -2); + + return 1; + } + else + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + } + + SQInteger Length(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length()); + return 1; + } + + SQInteger LengthSqr(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->LengthSqr()); + return 1; + } + + SQInteger Length2D(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length2D()); + return 1; + } + + SQInteger Length2DSqr(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length2DSqr()); + return 1; + } + + SQInteger Normalized(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1).Normalized()); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Norm(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + float len = v1->NormalizeInPlace(); + sq_pushfloat(vm, len); + + return 1; + } + + SQInteger Scale(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, float)"); + } + + float s = 0.0; + + if (SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * s); + sq_remove(vm, -2); + + return 1; + } + else + { + return sq_throwerror(vm, "Expected (Vector, float)"); + } + } + + SQInteger Dot(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_pushfloat(vm, v1->Dot(*v2)); + return 1; + } + + SQInteger ToKVString(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sqstd_pushstringf(vm, "%f %f %f", v1->x, v1->y, v1->z); + return 1; + } + + SQInteger Cross(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1).Cross(*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger ToString(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sqstd_pushstringf(vm, "(vector: (%f, %f, %f))", v1->x, v1->y, v1->z); + return 1; + } + + SQInteger TypeOf(HSQUIRRELVM vm) + { + sq_pushstring(vm, "Vector", -1); + return 1; + } + + SQInteger Nexti(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm, 2, &obj); + + const char* curkey = nullptr; + + if (sq_isnull(obj)) + { + curkey = "w"; + } + else if (sq_isstring(obj)) + { + curkey = sq_objtostring(&obj); + } + else + { + return sq_throwerror(vm, "Invalid iterator"); + } + + Assert(curkey && curkey[0] >= 'w' && curkey[0] <= 'z'); + + if (curkey[0] == 'z') + { + // Reached the end + sq_pushnull(vm); + return 1; + } + + char newkey = curkey[0] + 1; + sq_pushstring(vm, &newkey, 1); + + return 1; + } + + static const SQRegFunction funcs[] = { + {_SC("constructor"), Construct,0,nullptr}, + {_SC("_get"), Get, 2, _SC(".s")}, + {_SC("_set"), Set, 3, _SC(".sn")}, + {_SC("_add"), Add, 2, _SC("..")}, + {_SC("_sub"), Sub, 2, _SC("..")}, + {_SC("_mul"), Multiply, 2, _SC("..")}, + {_SC("_div"), Divide, 2, _SC("..")}, + {_SC("Length"), Length, 1, _SC(".")}, + {_SC("LengthSqr"), LengthSqr, 1, _SC(".")}, + {_SC("Length2D"), Length2D, 1, _SC(".")}, + {_SC("Length2DSqr"), Length2DSqr, 1, _SC(".")}, + {_SC("Normalized"), Normalized, 1, _SC(".")}, + {_SC("Norm"), Norm, 1, _SC(".")}, + {_SC("Scale"), Scale, 2, _SC("..")}, + {_SC("Dot"), Dot, 2, _SC("..")}, + {_SC("Cross"), Cross, 2, _SC("..")}, + {_SC("ToKVString"), ToKVString, 1, _SC(".")}, + {_SC("_tostring"), ToString, 1, _SC(".")}, + {_SC("_typeof"), TypeOf, 1, _SC(".")}, + {_SC("_nexti"), Nexti, 2, _SC("..")}, + + {nullptr,(SQFUNCTION)0,0,nullptr} + }; + + HSQOBJECT register_class(HSQUIRRELVM v) + { + sq_pushstring(v, _SC("Vector"), -1); + sq_newclass(v, SQFalse); + sq_settypetag(v, -1, TYPETAG_VECTOR); + sq_setclassudsize(v, -1, sizeof(Vector)); + SQInteger i = 0; + while (funcs[i].name != 0) { + const SQRegFunction& f = funcs[i]; + sq_pushstring(v, f.name, -1); + sq_newclosure(v, f.f, 0); + sq_setparamscheck(v, f.nparamscheck, f.typemask); + sq_setnativeclosurename(v, -1, f.name); + sq_newslot(v, -3, SQFalse); + i++; + } + HSQOBJECT klass; + sq_resetobject(&klass); + sq_getstackobj(v, -1, &klass); + sq_addref(v, &klass); + sq_newslot(v, -3, SQFalse); + return klass; + } +} // SQVector + +struct ClassInstanceData +{ + ClassInstanceData(void* instance, ScriptClassDesc_t* desc, const char* instanceId = nullptr, bool allowDestruct = false) : + instance(instance), + desc(desc), + instanceId(instanceId), + allowDestruct(allowDestruct) + {} + + void* instance; + ScriptClassDesc_t* desc; + CUtlString instanceId; + + // Indicates this game-created instance is a weak reference and can be destructed (Blixibon) + bool allowDestruct; +}; + +bool CreateParamCheck(const ScriptFunctionBinding_t& func, char* output) +{ + *output++ = '.'; + for (int i = 0; i < func.m_desc.m_Parameters.Count(); ++i) + { + switch (func.m_desc.m_Parameters[i]) + { + case FIELD_FLOAT: + *output++ = 'n'; // NOTE: Can be int or float + break; + case FIELD_CSTRING: + *output++ = 's'; + break; + case FIELD_VECTOR: + *output++ = 'x'; // Generic instance, we validate on arrival + break; + case FIELD_INTEGER: + *output++ = 'i'; // could use 'n' also which is int or float + break; + case FIELD_BOOLEAN: + *output++ = 'b'; + break; + case FIELD_CHARACTER: + *output++ = 's'; + break; + case FIELD_HSCRIPT: + *output++ = '.'; + break; + default: + Assert(!"Unsupported type"); + return false; + }; + } + *output++ = 0; + return true; +} + +void PushVariant(HSQUIRRELVM vm, const ScriptVariant_t& value) +{ + switch (value.m_type) + { + case FIELD_VOID: + sq_pushnull(vm); + break; + case FIELD_FLOAT: + sq_pushfloat(vm, value); + break; + case FIELD_CSTRING: + if ( value.m_pszString ) + sq_pushstring(vm, value, -1); + else + sq_pushnull(vm); + break; + case FIELD_VECTOR: + { + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + sq_pushobject(vm, pSquirrelVM->vectorClass_); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(value); + sq_remove(vm, -2); + break; + } + case FIELD_INTEGER: + sq_pushinteger(vm, value.m_int); + break; + case FIELD_BOOLEAN: + sq_pushbool(vm, value.m_bool); + break; + case FIELD_CHARACTER: + { + char buf[2] = { value.m_char, 0 }; + sq_pushstring(vm, buf, 1); + break; + } + case FIELD_HSCRIPT: + if (value.m_hScript) + { + sq_pushobject(vm, *((HSQOBJECT*)value.m_hScript)); + } + else + { + sq_pushnull(vm); + } + break; + } +} + +void GetVariantScriptString(const ScriptVariant_t& value, char *szValue, int iSize) +{ + switch (value.m_type) + { + case FIELD_VOID: + V_strncpy( szValue, "null", iSize ); + break; + case FIELD_FLOAT: + V_snprintf( szValue, iSize, "%f", value.m_float ); + break; + case FIELD_CSTRING: + V_snprintf( szValue, iSize, "\"%s\"", value.m_pszString ); + break; + case FIELD_VECTOR: + V_snprintf( szValue, iSize, "Vector( %f, %f, %f )", value.m_pVector->x, value.m_pVector->y, value.m_pVector->z ); + break; + case FIELD_INTEGER: + V_snprintf( szValue, iSize, "%i", value.m_int ); + break; + case FIELD_BOOLEAN: + V_snprintf( szValue, iSize, "%d", value.m_bool ); + break; + case FIELD_CHARACTER: + //char buf[2] = { value.m_char, 0 }; + V_snprintf( szValue, iSize, "\"%c\"", value.m_char ); + break; + } +} + +bool getVariant(HSQUIRRELVM vm, SQInteger idx, ScriptVariant_t& variant) +{ + switch (sq_gettype(vm, idx)) + { + case OT_NULL: + { + // TODO: Should this be (HSCRIPT)nullptr + variant.m_type = FIELD_VOID; + return true; + } + case OT_INTEGER: + { + SQInteger val; + if (SQ_FAILED(sq_getinteger(vm, idx, &val))) + { + return false; + } + variant = (int)val; + return true; + } + case OT_FLOAT: + { + SQFloat val; + if (SQ_FAILED(sq_getfloat(vm, idx, &val))) + { + return false; + } + variant = (float)val; + return true; + } + case OT_BOOL: + { + SQBool val; + if (SQ_FAILED(sq_getbool(vm, idx, &val))) + { + return false; + } + variant = val ? true : false; + return true; + } + case OT_STRING: + { + const char* val; + SQInteger size = 0; + if (SQ_FAILED(sq_getstringandsize(vm, idx, &val, &size))) + { + return false; + } + char* buffer = new char[size + 1]; + V_memcpy(buffer, val, size); + buffer[size] = 0; + variant = buffer; + variant.m_flags |= SV_FREE; + return true; + } + case OT_INSTANCE: + { + Vector* v = nullptr; + SQUserPointer tag; + if (SQ_SUCCEEDED(sq_gettypetag(vm, idx, &tag)) && + tag == TYPETAG_VECTOR && + SQ_SUCCEEDED(sq_getinstanceup(vm, idx, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + variant = new Vector(*v); + variant.m_flags |= SV_FREE; + return true; + } + // fall-through for non-vector + } + default: + { + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm, idx, obj); + sq_addref(vm, obj); + variant = (HSCRIPT)obj; + } + }; + + + return true; +} + +SQInteger function_stub(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + SQUserPointer userptr = nullptr; + sq_getuserpointer(vm, top, &userptr); + + Assert(userptr); + + ScriptFunctionBinding_t* pFunc = (ScriptFunctionBinding_t*)userptr; + + auto nargs = pFunc->m_desc.m_Parameters.Count(); + + if (nargs > top) + { + // TODO: Handle optional parameters? + return sq_throwerror(vm, "Invalid number of parameters"); + } + + CUtlVector params; + params.SetCount(nargs); + + for (int i = 0; i < nargs; ++i) + { + switch (pFunc->m_desc.m_Parameters[i]) + { + case FIELD_FLOAT: + { + float val = 0.0; + if (SQ_FAILED(sq_getfloat(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected float"); + params[i] = val; + break; + } + case FIELD_CSTRING: + { + const char* val; + if (SQ_FAILED(sq_getstring(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + params[i] = val; + break; + } + case FIELD_VECTOR: + { + Vector* val; + if (SQ_FAILED(sq_getinstanceup(vm, i + 2, (SQUserPointer*)&val, TYPETAG_VECTOR))) + return sq_throwerror(vm, "Expected Vector"); + params[i] = *val; + break; + } + case FIELD_INTEGER: + { + SQInteger val = 0; + if (SQ_FAILED(sq_getinteger(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected integer"); + params[i] = (int)val; + break; + } + case FIELD_BOOLEAN: + { + SQBool val = 0; + if (SQ_FAILED(sq_getbool(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected bool"); + params[i] = val ? true : false; + break; + } + case FIELD_CHARACTER: + { + const char* val; + if (SQ_FAILED(sq_getstring(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + params[i] = val[i]; + break; + } + case FIELD_HSCRIPT: + { + HSQOBJECT val; + if (SQ_FAILED(sq_getstackobj(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected handle"); + + if (sq_isnull(val)) + { + params[i] = (HSCRIPT)nullptr; + } + else + { + HSQOBJECT* pObject = new HSQOBJECT; + *pObject = val; + sq_addref(vm, pObject); + params[i] = (HSCRIPT)pObject; + } + break; + } + default: + Assert(!"Unsupported type"); + return false; + } + } + + void* instance = nullptr; + + if (pFunc->m_flags & SF_MEMBER_FUNC) + { + SQUserPointer self; + sq_getinstanceup(vm, 1, &self, nullptr); + + if (!self) + { + return sq_throwerror(vm, "Accessed null instance"); + } + + instance = ((ClassInstanceData*)self)->instance; + } + + ScriptVariant_t retval; + + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + + sq_resetobject(&pSquirrelVM->lastError_); + + (*pFunc->m_pfnBinding)(pFunc->m_pFunction, instance, params.Base(), nargs, + pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval); + + if (!sq_isnull(pSquirrelVM->lastError_)) + { + sq_pushobject(vm, pSquirrelVM->lastError_); + sq_resetobject(&pSquirrelVM->lastError_); + return sq_throwobject(vm); + } + + PushVariant(vm, retval); + + return pFunc->m_desc.m_ReturnType != FIELD_VOID; +} + + +SQInteger destructor_stub(SQUserPointer p, SQInteger size) +{ + auto classInstanceData = (ClassInstanceData*)p; + + if (classInstanceData->desc->m_pfnDestruct) + classInstanceData->desc->m_pfnDestruct(classInstanceData->instance); + + classInstanceData->~ClassInstanceData(); + return 0; +} + +SQInteger destructor_stub_instance(SQUserPointer p, SQInteger size) +{ + auto classInstanceData = (ClassInstanceData*)p; + // We don't call destructor here because this is owned by the game + classInstanceData->~ClassInstanceData(); + return 0; +} + +SQInteger constructor_stub(HSQUIRRELVM vm) +{ + ScriptClassDesc_t* pClassDesc = nullptr; + sq_gettypetag(vm, 1, (SQUserPointer*)&pClassDesc); + + if (!pClassDesc->m_pfnConstruct) + { + return sqstd_throwerrorf(vm, "Unable to construct instances of %s", pClassDesc->m_pszScriptName); + } + + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + + sq_resetobject(&pSquirrelVM->lastError_); + + void* instance = pClassDesc->m_pfnConstruct(); + + if (!sq_isnull(pSquirrelVM->lastError_)) + { + sq_pushobject(vm, pSquirrelVM->lastError_); + sq_resetobject(&pSquirrelVM->lastError_); + return sq_throwobject(vm); + } + + { + SQUserPointer p; + sq_getinstanceup(vm, 1, &p, 0); + new(p) ClassInstanceData(instance, pClassDesc, nullptr, true); + } + + sq_setreleasehook(vm, 1, &destructor_stub); + + return 0; +} + +SQInteger tostring_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + char buffer[128] = ""; + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->ToString(classInstanceData->instance, buffer, sizeof(buffer))) + { + sq_pushstring(vm, buffer, -1); + } + else if (classInstanceData) + { + sqstd_pushstringf(vm, "(%s : 0x%p)", classInstanceData->desc->m_pszScriptName, classInstanceData->instance); + } + else + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm, 1, &obj); + // Semi-based on SQVM::ToString default case + sqstd_pushstringf(vm, "(%s: 0x%p)", IdType2Name(obj._type), (void*)_rawval(obj)); + } + + return 1; +} + +SQInteger get_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected _get(string)"); + } + + ScriptVariant_t var; + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->Get(classInstanceData->instance, key, var)) + { + PushVariant(vm, var); + } + else + { + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + return 1; +} + +SQInteger set_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected _set(string)"); + } + + ScriptVariant_t var; + getVariant( vm, -1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->Set(classInstanceData->instance, key, var)) + { + sq_pop(vm, 1); + } + else + { + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + return 0; +} + +SQInteger add_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op +"); +} + +SQInteger sub_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Subtract( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op -"); +} + +SQInteger mul_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper ) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op *"); +} + +SQInteger div_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper ) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op /"); +} + +SQInteger IsValid_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + sq_pushbool(vm, classInstanceData != nullptr); + return 1; +} + +struct SquirrelSafeCheck +{ + SquirrelSafeCheck(HSQUIRRELVM vm, int outputCount = 0) : + vm_{ vm }, + top_{ sq_gettop(vm) }, + outputCount_{ outputCount } + {} + + ~SquirrelSafeCheck() + { + if (top_ != (sq_gettop(vm_) - outputCount_)) + { + Assert(!"Squirrel VM stack is not consistent"); + Error("Squirrel VM stack is not consistent\n"); + } + + // TODO: Handle error state checks + } + + HSQUIRRELVM vm_; + SQInteger top_; + SQInteger outputCount_; +}; + + +void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) +{ + va_list args; + char buffer[2048]; + va_start(args, format); + V_vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + CGMsg(0, CON_GROUP_VSCRIPT_PRINT, "%s", buffer); +} + +void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) +{ + va_list args; + char buffer[2048]; + va_start(args, format); + V_vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + Warning("%s", buffer); +} + +const char * ScriptDataTypeToName(ScriptDataType_t datatype) +{ + switch (datatype) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "string"; + case FIELD_VECTOR: return "Vector"; + case FIELD_INTEGER: return "int"; + case FIELD_BOOLEAN: return "bool"; + case FIELD_CHARACTER: return "char"; + case FIELD_HSCRIPT: return "handle"; + case FIELD_VARIANT: return "variant"; + default: return ""; + } +} + +void RegisterDocumentation(HSQUIRRELVM vm, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, "::"); + } + + V_strcat_safe(name, pFuncDesc.m_pszScriptName); + + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s(", ScriptDataTypeToName(pFuncDesc.m_ReturnType), name); + + for (int i = 0; i < pFuncDesc.m_Parameters.Count(); ++i) + { + if (i != 0) + V_strcat_safe(signature, ", "); + + V_strcat_safe(signature, ScriptDataTypeToName(pFuncDesc.m_Parameters[i])); + } + + V_strcat_safe(signature, ")"); + + // RegisterHelp(name, signature, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterClassDocumentation(HSQUIRRELVM vm, const ScriptClassDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm); + + const char *name = pClassDesc->m_pszScriptName; + const char *base = ""; + if (pClassDesc->m_pBaseDesc) + { + base = pClassDesc->m_pBaseDesc->m_pszScriptName; + } + + const char *description = pClassDesc->m_pszDescription; + if (description) + { + if (description[0] == SCRIPT_HIDE[0]) + return; + if (description[0] == SCRIPT_SINGLETON[0]) + description++; + } + else + { + description = ""; + } + + // RegisterClassHelp(name, base, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterClassHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, base, -1); + sq_pushstring(vm, description, -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterEnumDocumentation(HSQUIRRELVM vm, const ScriptEnumDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pClassDesc->m_pszDescription && pClassDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + const char *name = pClassDesc->m_pszScriptName; + + // RegisterEnumHelp(name, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterEnumHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushinteger(vm, pClassDesc->m_ConstantBindings.Count()); + sq_pushstring(vm, pClassDesc->m_pszDescription ? pClassDesc->m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterConstantDocumentation( HSQUIRRELVM vm, const ScriptConstantBinding_t* pConstDesc, const char *pszAsString, ScriptEnumDesc_t* pEnumDesc = nullptr ) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pConstDesc->m_pszDescription && pConstDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pEnumDesc) + { + V_strcat_safe(name, pEnumDesc->m_pszScriptName); + V_strcat_safe(name, "."); + } + + V_strcat_safe(name, pConstDesc->m_pszScriptName); + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s (%s)", pszAsString, ScriptDataTypeToName(pConstDesc->m_data.m_type)); + + // RegisterConstHelp(name, signature, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterConstHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pConstDesc->m_pszDescription ? pConstDesc->m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterHookDocumentation(HSQUIRRELVM vm, const ScriptHook_t* pHook, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, " -> "); + } + + V_strcat_safe(name, pFuncDesc.m_pszScriptName); + + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s(", ScriptDataTypeToName(pFuncDesc.m_ReturnType), name); + + for (int i = 0; i < pFuncDesc.m_Parameters.Count(); ++i) + { + if (i != 0) + V_strcat_safe(signature, ", "); + + V_strcat_safe(signature, ScriptDataTypeToName(pFuncDesc.m_Parameters[i])); + V_strcat_safe(signature, " ["); + V_strcat_safe(signature, pHook->m_pszParameterNames[i]); + V_strcat_safe(signature, "]"); + } + + V_strcat_safe(signature, ")"); + + // RegisterHookHelp(name, signature, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterHookHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterMemberDocumentation(HSQUIRRELVM vm, const ScriptMemberDesc_t& pDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pDesc.m_pszDescription && pDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, "."); + } + + if (pDesc.m_pszScriptName) + V_strcat_safe(name, pDesc.m_pszScriptName); + + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s", ScriptDataTypeToName(pDesc.m_ReturnType), name); + + // RegisterHookHelp(name, signature, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterMemberHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pDesc.m_pszDescription ? pDesc.m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + + +bool SquirrelVM::Init() +{ + vm_ = sq_open(1024); //creates a VM with initial stack size 1024 + + if (vm_ == nullptr) + return false; + + sq_setforeignptr(vm_, this); + sq_resetobject(&lastError_); + + sq_setprintfunc(vm_, printfunc, errorfunc); + + + { + sq_pushroottable(vm_); + + sqstd_register_mathlib(vm_); + sqstd_register_stringlib(vm_); + vectorClass_ = SQVector::register_class(vm_); + + // We exclude these libraries as they create a security risk on the client even + // though I'm sure if someone tried hard enough they could achieve all sorts of + // things with the other APIs, this just makes it a little bit harder for a map + // created by someone in the community causing a bunch of security vulnerablilties. + // + // Pretty sure DoIncludeScript() is already a vulnerability vector here, however + // that also depends on compile errors not showing up and relies on IFilesystem with + // a path prefix. + // + //sqstd_register_bloblib(vm_); + //sqstd_register_iolib(vm_); + //sqstd_register_systemlib(vm_); + + // There is no vulnerability in getting time. + sqstd_register_timelib(vm_); + + + sqstd_seterrorhandlers(vm_); + + { + // Unfortunately we can not get the pattern from a regexp instance + // so we need to wrap it with our own to get it. + if (Run(R"script( + class regexp extends regexp + { + constructor(pattern) + { + base.constructor(pattern); + this.pattern_ = pattern; + } + pattern_ = ""; + } + )script") == SCRIPT_ERROR) + { + this->Shutdown(); + return false; + } + + sq_resetobject(®expClass_); + sq_pushstring(vm_, "regexp", -1); + sq_rawget(vm_, -2); + sq_getstackobj(vm_, -1, ®expClass_); + sq_addref(vm_, ®expClass_); + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + } + + if (Run(g_Script_vscript_squirrel) != SCRIPT_DONE) + { + this->Shutdown(); + return false; + } + + return true; +} + +void SquirrelVM::Shutdown() +{ + if (vm_) + { + sq_release(vm_, &vectorClass_); + sq_release(vm_, ®expClass_); + + sq_close(vm_); + vm_ = nullptr; + } +} + +bool SquirrelVM::ConnectDebugger() +{ + // TODO: Debugger support + return false; +} + +void SquirrelVM::DisconnectDebugger() +{ + // TODO: Debugger support +} + +ScriptLanguage_t SquirrelVM::GetLanguage() +{ + return SL_SQUIRREL; +} + +const char* SquirrelVM::GetLanguageName() +{ + return "squirrel"; +} + +void SquirrelVM::AddSearchPath(const char* pszSearchPath) +{ + // TODO: Search path support +} + +bool SquirrelVM::Frame(float simTime) +{ + // TODO: Frame support + return false; +} + +ScriptStatus_t SquirrelVM::Run(const char* pszScript, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), "", SQTrue))) + { + return SCRIPT_ERROR; + } + + sq_pushroottable(vm_); + if (SQ_FAILED(sq_call(vm_, 1, SQFalse, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +HSCRIPT SquirrelVM::CompileScript(const char* pszScript, const char* pszId) +{ + SquirrelSafeCheck safeCheck(vm_); + + Assert(vm_); + if (pszId == nullptr) pszId = ""; + if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), pszId, SQTrue))) + { + return nullptr; + } + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + return (HSCRIPT)obj; +} + +void SquirrelVM::ReleaseScript(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; +} + +ScriptStatus_t SquirrelVM::Run(HSCRIPT hScript, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + auto result = sq_call(vm_, 1, false, true); + sq_pop(vm_, 1); + if (SQ_FAILED(result)) + { + return SCRIPT_ERROR; + } + return SCRIPT_DONE; +} + +ScriptStatus_t SquirrelVM::Run(HSCRIPT hScript, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + sq_pushroottable(vm_); + auto result = sq_call(vm_, 1, false, true); + sq_pop(vm_, 1); + if (SQ_FAILED(result)) + { + return SCRIPT_ERROR; + } + return SCRIPT_DONE; +} + +HSCRIPT SquirrelVM::CreateScope(const char* pszScope, HSCRIPT hParent) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_newtable(vm_); + + if (hParent) + { + HSQOBJECT* parent = (HSQOBJECT*)hParent; + Assert(hParent != INVALID_HSCRIPT); + sq_pushobject(vm_, *parent); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszScope, -1); + sq_push(vm_, -3); + sq_rawset(vm_, -3); + + sq_pushstring(vm_, "__vname", -1); + sq_pushstring(vm_, pszScope, -1); + sq_rawset(vm_, -4); + + if (SQ_FAILED(sq_setdelegate(vm_, -2))) + { + sq_pop(vm_, 2); + return nullptr; + } + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + return (HSCRIPT)obj; +} + +void SquirrelVM::ReleaseScope(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + + sq_getdelegate(vm_, -1); + + sq_pushstring(vm_, "__vname", -1); + sq_rawdeleteslot(vm_, -2, SQFalse); + + sq_pop(vm_, 2); + + sq_release(vm_, obj); + delete obj; +} + +HSCRIPT SquirrelVM::LookupFunction(const char* pszFunction, HSCRIPT hScope) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + sq_pushstring(vm_, _SC(pszFunction), -1); + + HSQOBJECT obj; + sq_resetobject(&obj); + + if (sq_get(vm_, -2) == SQ_OK) + { + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + sq_pop(vm_, 1); + } + sq_pop(vm_, 1); + + if (!sq_isclosure(obj)) + { + sq_release(vm_, &obj); + return nullptr; + } + + HSQOBJECT* pObj = new HSQOBJECT; + *pObj = obj; + return (HSCRIPT)pObj; +} + +void SquirrelVM::ReleaseFunction(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; +} + +ScriptStatus_t SquirrelVM::ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hFunction) + return SCRIPT_ERROR; + + if (hFunction == INVALID_HSCRIPT) + return SCRIPT_ERROR; + + HSQOBJECT* pFunc = (HSQOBJECT*)hFunction; + sq_pushobject(vm_, *pFunc); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + for (int i = 0; i < nArgs; ++i) + { + PushVariant(vm_, pArgs[i]); + } + + bool hasReturn = pReturn != nullptr; + + if (SQ_FAILED(sq_call(vm_, nArgs + 1, hasReturn, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + if (hasReturn) + { + if (!getVariant(vm_, -1, *pReturn)) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +void SquirrelVM::RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pScriptFunction); + + if (!pScriptFunction) + return; + + char typemask[64]; + if (!CreateParamCheck(*pScriptFunction, typemask)) + { + return; + } + + sq_pushroottable(vm_); + + sq_pushstring(vm_, pScriptFunction->m_desc.m_pszScriptName, -1); + + sq_pushuserpointer(vm_, pScriptFunction); + sq_newclosure(vm_, function_stub, 1); + + sq_setnativeclosurename(vm_, -1, pScriptFunction->m_desc.m_pszScriptName); + + sq_setparamscheck(vm_, pScriptFunction->m_desc.m_Parameters.Count() + 1, typemask); + bool isStatic = false; + sq_newslot(vm_, -3, isStatic); + + sq_pop(vm_, 1); + + RegisterDocumentation(vm_, pScriptFunction->m_desc); +} + +bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_pushroottable(vm_); + sq_pushstring(vm_, pClassDesc->m_pszScriptName, -1); + + // Check if class name is already taken + if (sq_get(vm_, -2) == SQ_OK) + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + if (!sq_isnull(obj)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 1); + } + + // Register base in case it doesn't exist + if (pClassDesc->m_pBaseDesc) + { + RegisterClass(pClassDesc->m_pBaseDesc); + + // Check if the base class can be + sq_pushstring(vm_, pClassDesc->m_pBaseDesc->m_pszScriptName, -1); + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + } + + if (SQ_FAILED(sq_newclass(vm_, pClassDesc->m_pBaseDesc ? SQTrue : SQFalse))) + { + sq_pop(vm_, 2 + (pClassDesc->m_pBaseDesc ? 1 : 0)); + return false; + } + + sq_settypetag(vm_, -1, pClassDesc); + + sq_setclassudsize(vm_, -1, sizeof(ClassInstanceData)); + + sq_pushstring(vm_, "constructor", -1); + sq_newclosure(vm_, constructor_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_tostring", -1); + sq_newclosure(vm_, tostring_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_get", -1); + sq_newclosure(vm_, get_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_set", -1); + sq_newclosure(vm_, set_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_add", -1); + sq_newclosure(vm_, add_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_sub", -1); + sq_newclosure(vm_, sub_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_mul", -1); + sq_newclosure(vm_, mul_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_div", -1); + sq_newclosure(vm_, div_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "IsValid", -1); + sq_newclosure(vm_, IsValid_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + + for (int i = 0; i < pClassDesc->m_FunctionBindings.Count(); ++i) + { + auto& scriptFunction = pClassDesc->m_FunctionBindings[i]; + + char typemask[64]; + if (!CreateParamCheck(scriptFunction, typemask)) + { + Warning("Unable to create param check for %s.%s\n", + pClassDesc->m_pszClassname, scriptFunction.m_desc.m_pszFunction); + break; + } + + sq_pushstring(vm_, scriptFunction.m_desc.m_pszScriptName, -1); + + sq_pushuserpointer(vm_, &scriptFunction); + sq_newclosure(vm_, function_stub, 1); + + sq_setnativeclosurename(vm_, -1, scriptFunction.m_desc.m_pszScriptName); + + sq_setparamscheck(vm_, scriptFunction.m_desc.m_Parameters.Count() + 1, typemask); + bool isStatic = false; + sq_newslot(vm_, -3, isStatic); + + RegisterDocumentation(vm_, scriptFunction.m_desc, pClassDesc); + } + + for (int i = 0; i < pClassDesc->m_Hooks.Count(); ++i) + { + auto& scriptHook = pClassDesc->m_Hooks[i]; + + RegisterHookDocumentation(vm_, scriptHook, scriptHook->m_desc, pClassDesc); + } + + for (int i = 0; i < pClassDesc->m_Members.Count(); ++i) + { + auto& member = pClassDesc->m_Members[i]; + + RegisterMemberDocumentation(vm_, member, pClassDesc); + } + + sq_pushstring(vm_, pClassDesc->m_pszScriptName, -1); + sq_push(vm_, -2); + + if (SQ_FAILED(sq_newslot(vm_, -4, SQFalse))) + { + sq_pop(vm_, 4); + return false; + } + + sq_pop(vm_, 2); + + RegisterClassDocumentation(vm_, pClassDesc); + + return true; +} + +void SquirrelVM::RegisterConstant(ScriptConstantBinding_t* pScriptConstant) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pScriptConstant); + + if (!pScriptConstant) + return; + + sq_pushconsttable(vm_); + sq_pushstring(vm_, pScriptConstant->m_pszScriptName, -1); + + PushVariant(vm_, pScriptConstant->m_data); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + + char szValue[64]; + GetVariantScriptString( pScriptConstant->m_data, szValue, sizeof(szValue) ); + RegisterConstantDocumentation(vm_, pScriptConstant, szValue); +} + +void SquirrelVM::RegisterEnum(ScriptEnumDesc_t* pEnumDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pEnumDesc); + + if (!pEnumDesc) + return; + + sq_pushconsttable(vm_); + sq_pushstring(vm_, pEnumDesc->m_pszScriptName, -1); + + // Check if class name is already taken + if (sq_get(vm_, -2) == SQ_OK) + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + if (!sq_isnull(obj)) + { + sq_pop(vm_, 2); + return; + } + } + + sq_pop(vm_, 1); + + // HACKHACK: I have no idea how to declare enums with the current API. + // For now, we'll just cram everything into a script buffer and compile it. (Blixibon) + char szScript[2048]; + V_snprintf( szScript, sizeof(szScript), "enum %s {\n", pEnumDesc->m_pszScriptName ); + + for (int i = 0; i < pEnumDesc->m_ConstantBindings.Count(); ++i) + { + auto& scriptConstant = pEnumDesc->m_ConstantBindings[i]; + + char szValue[64]; + GetVariantScriptString( scriptConstant.m_data, szValue, sizeof(szValue) ); + + V_snprintf( szScript, sizeof(szScript), "%s%s = %s\n", szScript, scriptConstant.m_pszScriptName, szValue ); + + RegisterConstantDocumentation(vm_, &scriptConstant, szValue, pEnumDesc); + } + + V_strcat_safe( szScript, "}" ); + + Run( szScript ); + + RegisterEnumDocumentation(vm_, pEnumDesc); +} + +HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct) +{ + SquirrelSafeCheck safeCheck(vm_); + + this->RegisterClass(pDesc); + + sq_pushroottable(vm_); + sq_pushstring(vm_, pDesc->m_pszScriptName, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return nullptr; + } + + if (SQ_FAILED(sq_createinstance(vm_, -1))) + { + sq_pop(vm_, 2); + return nullptr; + } + + { + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) ClassInstanceData(pInstance, pDesc, nullptr, bAllowDestruct); + } + + sq_setreleasehook(vm_, -1, bAllowDestruct ? &destructor_stub : &destructor_stub_instance); + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 3); + + return (HSCRIPT)obj; +} + +void SquirrelVM::SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + sq_pushobject(vm_, *obj); + + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + + auto classInstanceData = (ClassInstanceData*)self; + + classInstanceData->instanceId = pszId; + + sq_poptop(vm_); +} + +void SquirrelVM::RemoveInstance(HSCRIPT hInstance) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + sq_pushobject(vm_, *obj); + + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + + ((ClassInstanceData*)self)->~ClassInstanceData(); + + sq_setinstanceup(vm_, -1, nullptr); + sq_setreleasehook(vm_, -1, nullptr); + sq_pop(vm_, 1); + + sq_release(vm_, obj); + delete obj; +} + +void* SquirrelVM::GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpectedType) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return nullptr; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + + if (pExpectedType) + { + sq_pushroottable(vm_); + sq_pushstring(vm_, pExpectedType->m_pszScriptName, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return nullptr; + } + + sq_pushobject(vm_, *obj); + + if (sq_instanceof(vm_) != SQTrue) + { + sq_pop(vm_, 3); + return nullptr; + } + + sq_pop(vm_, 3); + } + + sq_pushobject(vm_, *obj); + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + sq_pop(vm_, 1); + + auto classInstanceData = (ClassInstanceData*)self; + + if (!classInstanceData) + { + return nullptr; + } + + + return classInstanceData->instance; +} + +bool SquirrelVM::GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) +{ + static int keyIdx = 0; + // This gets used for script scope, still confused why it needs to be inside IScriptVM + // is it just to be a compatible name for CreateScope? + SquirrelSafeCheck safeCheck(vm_); + V_snprintf(pBuf, nBufSize, "%08X_%s", ++keyIdx, pszRoot); + return true; +} + +bool SquirrelVM::ValueExists(HSCRIPT hScope, const char* pszKey) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 2); + return true; +} + +bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const char* pszValue) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + sq_pushstring(vm_, pszValue, -1); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + return true; +} + +bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVariant_t& value) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + PushVariant(vm_, value); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + return true; +} + +void SquirrelVM::CreateTable(ScriptVariant_t& Table) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_newtable(vm_); + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + Table = (HSCRIPT)obj; +} + +int SquirrelVM::GetNumTableEntries(HSCRIPT hScope) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hScope) + { + // TODO: This is called hScope but seems like just a table so + // lets not fallback to root table + return 0; + } + + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + + int count = sq_getsize(vm_, -1); + + sq_pop(vm_, 1); + + return count; +} + +int SquirrelVM::GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey, ScriptVariant_t* pValue) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + if (nIterator == -1) + { + sq_pushnull(vm_); + } + else + { + sq_pushinteger(vm_, nIterator); + } + + SQInteger nextiter = -1; + + if (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + if (pKey) getVariant(vm_, -2, *pKey); + if (pValue) getVariant(vm_, -1, *pValue); + + sq_getinteger(vm_, -3, &nextiter); + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + return nextiter; +} + +bool SquirrelVM::GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) +{ + SquirrelSafeCheck safeCheck(vm_); + + Assert(pValue); + if (!pValue) + { + return false; + } + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + if (!getVariant(vm_, -1, *pValue)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 2); + + return true; +} + +void SquirrelVM::ReleaseValue(ScriptVariant_t& value) +{ + SquirrelSafeCheck safeCheck(vm_); + if (value.m_type == FIELD_HSCRIPT) + { + HSCRIPT hScript = value; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; + } + else + { + value.Free(); + } + + // Let's prevent this being called again and giving some UB + value.m_type = FIELD_VOID; +} + +bool SquirrelVM::ClearValue(HSCRIPT hScope, const char* pszKey) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + if (SQ_FAILED(sq_deleteslot(vm_, -2, SQFalse))) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 1); + return true; +} +/* +void SquirrelVM::CreateArray(ScriptVariant_t &arr, int size) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT *obj = new HSQOBJECT; + sq_resetobject(obj); + + sq_newarray(vm_,size); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + arr = (HSCRIPT)obj; +} +*/ +bool SquirrelVM::ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT *arr = (HSQOBJECT*)hArray; + + sq_pushobject(vm_, *arr); + PushVariant(vm_, val); + bool ret = sq_arrayappend(vm_, -2) == SQ_OK; + sq_pop(vm_, 1); + + return ret; +} + +enum ClassType +{ + VectorClassType = 0, + NativeClassType = 1, + ScriptClassType = 2 +}; + +SQInteger closure_write(SQUserPointer file, SQUserPointer p, SQInteger size) +{ + ((CUtlBuffer*)file)->Put(p, size); + return size; +} + +void SquirrelVM::WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQInteger idx) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, idx, &obj); + + switch (obj._type) + { + case OT_NULL: + { + pBuffer->PutInt(OT_NULL); + break; + } + case OT_INTEGER: + { + pBuffer->PutInt(OT_INTEGER); + pBuffer->PutInt64(sq_objtointeger(&obj)); + break; + } + case OT_FLOAT: + { + pBuffer->PutInt(OT_FLOAT); + pBuffer->PutFloat(sq_objtofloat(&obj)); + break; + } + case OT_BOOL: + { + pBuffer->PutInt(OT_BOOL); + pBuffer->PutChar(sq_objtobool(&obj)); + break; + } + case OT_STRING: + { + pBuffer->PutInt(OT_STRING); + const char* val = nullptr; + SQInteger size = 0; + sq_getstringandsize(vm_, idx, &val, &size); + pBuffer->PutInt(size); + pBuffer->Put(val, size); + break; + } + case OT_TABLE: + { + pBuffer->PutInt(OT_TABLE); + if (writeState.CheckCache(pBuffer, obj._unVal.pTable)) + { + break; + } + sq_getdelegate(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + int count = sq_getsize(vm_, idx); + sq_push(vm_, idx); + sq_pushnull(vm_); + pBuffer->PutInt(count); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); + break; + } + case OT_ARRAY: + { + pBuffer->PutInt(OT_ARRAY); + if (writeState.CheckCache(pBuffer, obj._unVal.pArray)) + { + break; + } + int count = sq_getsize(vm_, idx); + pBuffer->PutInt(count); + sq_push(vm_, idx); + sq_pushnull(vm_); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); + break; + } + case OT_CLOSURE: + { + pBuffer->PutInt(OT_CLOSURE); + if (writeState.CheckCache(pBuffer, obj._unVal.pClosure)) + { + break; + } + + SQInteger nparams = 0, nfreevars = 0; + sq_getclosureinfo(vm_, idx, &nparams, &nfreevars); + if (nfreevars == 0 && _closure(obj)->_function->_defaultparams == 0) + { + pBuffer->PutChar(0); + + sq_push(vm_, idx); + if (SQ_FAILED(sq_writeclosure(vm_, closure_write, pBuffer))) + { + Error("Failed to write closure"); + } + sq_pop(vm_, 1); + } + else + { + // Unfortunately we can't use sq_writeclosure because it doesn't work well with + // outer variables + + pBuffer->PutChar(1); + + if (!_closure(obj)->Save(vm_, pBuffer, closure_write)) + { + Error("Failed to write closure\n"); + } + + int noutervalues = _closure(obj)->_function->_noutervalues; + for (int i = 0; i < noutervalues; ++i) + { + sq_pushobject(vm_, _closure(obj)->_outervalues[i]); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + } + + int ndefaultparams = _closure(obj)->_function->_ndefaultparams; + for (int i = 0; i < ndefaultparams; ++i) + { + sq_pushobject(vm_, _closure(obj)->_defaultparams[i]); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + } + } + + if (_closure(obj)->_env) + { + sq_pushobject(vm_, _closure(obj)->_env->_obj); + } + else + { + sq_pushnull(vm_); + } + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + + break; + } + case OT_NATIVECLOSURE: + { + pBuffer->PutInt(OT_NATIVECLOSURE); + sq_getclosurename(vm_, idx); + + const char* name = nullptr; + sq_getstring(vm_, -1, &name); + pBuffer->PutString(name); + + sq_pop(vm_, 1); + break; + } + case OT_CLASS: + { + pBuffer->PutInt(OT_CLASS); + if (writeState.CheckCache(pBuffer, obj._unVal.pClass)) + { + break; + } + SQUserPointer typetag = nullptr; + sq_gettypetag(vm_, idx, &typetag); + if (typetag == TYPETAG_VECTOR) + { + pBuffer->PutInt(VectorClassType); + } + else if (typetag != nullptr) + { + // Seems so dangerous to treat typetag as ScriptClassDesc_t* + // however we don't really have an option without some sort of tagged + // pointer. + pBuffer->PutInt(NativeClassType); + pBuffer->PutString(((ScriptClassDesc_t*)typetag)->m_pszScriptName); + } + else + { + // HACK: We can't easily identify when the type is a builtin to exclude + // so we just check against the only class we need to deal with at the moment + // which is "regexp" + const char* builtinName = nullptr; + if (_class(obj) == _class(regexpClass_)) + { + builtinName = "regexp"; + } + + if (builtinName) + { + pBuffer->PutInt(NativeClassType); + pBuffer->PutString(builtinName); + break; + } + + pBuffer->PutInt(ScriptClassType); + + sq_getbase(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + sq_push(vm_, idx); + sq_pushnull(vm_); + sq_getattributes(vm_, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + sq_pushnull(vm_); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + pBuffer->PutChar(1); + // TODO: Member Attributes + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + { + // HACK: Meta-methods are not included in an iterator of OT_CLASS + SQObjectPtrVec& metamethods = *(_ss(vm_)->_metamethods); + for (int i = 0; i < MT_LAST; ++i) + { + if (sq_type(_class(obj)->_metamethods[i]) != OT_NULL) + { + pBuffer->PutChar(1); + sq_pushobject(vm_, metamethods[i]); + sq_pushobject(vm_, _class(obj)->_metamethods[i]); + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + } + } + } + + pBuffer->PutChar(0); + } + break; + } + case OT_INSTANCE: + { + pBuffer->PutInt(OT_INSTANCE); + if (writeState.CheckCache(pBuffer, obj._unVal.pInstance)) + { + break; + } + sq_getclass(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + if (_instance(obj)->_class == _class(regexpClass_)) + { + sq_push(vm_, idx); + sq_pushstring(vm_, "pattern_", -1); + sq_rawget(vm_, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + break; + } + + { + // HACK: No way to get the default values part from accessing the class directly + SQUnsignedInteger nvalues = _instance(obj)->_class->_defaultvalues.size(); + for (SQUnsignedInteger n = 0; n < nvalues; n++) { + sq_pushobject(vm_, _instance(obj)->_values[n]); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + } + } + + SQUserPointer typetag; + sq_gettypetag(vm_, idx, &typetag); + + if (typetag == TYPETAG_VECTOR) + { + Vector* v = nullptr; + sq_getinstanceup(vm_, idx, (SQUserPointer*)&v, TYPETAG_VECTOR); + Assert(v); + pBuffer->PutFloat(v->x); + pBuffer->PutFloat(v->y); + pBuffer->PutFloat(v->z); + } + else if (typetag) + { + ClassInstanceData* pClassInstanceData; + sq_getinstanceup(vm_, idx, (SQUserPointer*)&pClassInstanceData, typetag); + + if (pClassInstanceData) + { + if (pClassInstanceData->desc->m_pszDescription[0] == SCRIPT_SINGLETON[0]) + { + // Do nothing, singleton should be created from just the class + } + else if (!pClassInstanceData->instanceId.IsEmpty()) + { + pBuffer->PutString(pClassInstanceData->instanceId); + + pBuffer->PutChar(pClassInstanceData->allowDestruct ? 1 : 0); + } + else + { + Warning("SquirrelVM::WriteObject: Unable to find instanceID for object of type %s, unable to serialize\n", + pClassInstanceData->desc->m_pszClassname); + pBuffer->PutString(""); + } + } + else + { + pBuffer->PutString(""); + } + } + + break; + } + case OT_WEAKREF: + { + pBuffer->PutInt(OT_WEAKREF); + sq_getweakrefval(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + break; + } + case OT_FUNCPROTO: //internal usage only + { + pBuffer->PutInt(OT_FUNCPROTO); + + if (writeState.CheckCache(pBuffer, obj._unVal.pFunctionProto)) + { + break; + } + + _funcproto(obj)->Save(vm_, pBuffer, closure_write); + } + case OT_OUTER: //internal usage only + { + pBuffer->PutInt(OT_OUTER); + + if (writeState.CheckCache(pBuffer, obj._unVal.pOuter)) + { + break; + } + + sq_pushobject(vm_, *_outer(obj)->_valptr); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + + break; + } + // case OT_USERDATA: + // case OT_GENERATOR: + // case OT_USERPOINTER: + // case OT_THREAD: + // + default: + Warning("SquirrelVM::WriteObject: Unexpected type %d", sq_gettype(vm_, idx)); + // Save a null instead + pBuffer->PutInt(OT_NULL); + } +} + +void SquirrelVM::WriteState(CUtlBuffer* pBuffer) +{ + SquirrelSafeCheck safeCheck(vm_); + + WriteStateMap writeState; + + sq_pushroottable(vm_); + + // Not really a check cache, but adds the root + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + writeState.CheckCache(pBuffer, _table(obj)); + + int count = sq_getsize(vm_, 1); + sq_pushnull(vm_); + pBuffer->PutInt(count); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); +} + +SQInteger closure_read(SQUserPointer file, SQUserPointer buf, SQInteger size) +{ + CUtlBuffer* pBuffer = (CUtlBuffer*)file; + pBuffer->Get(buf, size); + return pBuffer->IsValid() ? size : -1; +} + +void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) +{ + SquirrelSafeCheck safeCheck(vm_, 1); + + int thisType = pBuffer->GetInt(); + + switch (thisType) + { + case OT_NULL: + { + sq_pushnull(vm_); + break; + } + case OT_INTEGER: + { + sq_pushinteger(vm_, pBuffer->GetInt64()); + break; + } + case OT_FLOAT: + { + sq_pushfloat(vm_, pBuffer->GetFloat()); + break; + } + case OT_BOOL: + { + sq_pushbool(vm_, pBuffer->GetChar()); + break; + } + case OT_STRING: + { + int size = pBuffer->GetInt(); + char* buffer = new char[size + 1]; + pBuffer->Get(buffer, size); + buffer[size] = 0; + sq_pushstring(vm_, buffer, size); + delete[] buffer; + break; + } + case OT_TABLE: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ReadObject(pBuffer, readState); + + int count = pBuffer->GetInt(); + sq_newtableex(vm_, count); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_push(vm_, -2); + sq_setdelegate(vm_, -2); + + sq_remove(vm_, -2); + + for (int i = 0; i < count; ++i) + { + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + sq_rawset(vm_, -3); + } + + break; + } + case OT_ARRAY: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + int count = pBuffer->GetInt(); + sq_newarray(vm_, count); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + for (int i = 0; i < count; ++i) + { + sq_pushinteger(vm_, i); + ReadObject(pBuffer, readState); + sq_rawset(vm_, -3); + } + break; + } + case OT_CLOSURE: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + if (pBuffer->GetChar() == 0) + { + if (SQ_FAILED(sq_readclosure(vm_, closure_read, pBuffer))) + { + Error("Failed to read closure\n"); + sq_pushnull(vm_); + break; + } + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + else + { + SQObjectPtr ret; + if (!SQClosure::Load(vm_, pBuffer, closure_read, ret)) + { + Error("Failed to read closure\n"); + sq_pushnull(vm_); + break; + } + + int noutervalues = _closure(ret)->_function->_noutervalues; + for (int i = 0; i < noutervalues; ++i) + { + ReadObject(pBuffer, readState); + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + _closure(ret)->_outervalues[i] = obj; + sq_poptop(vm_); + } + + int ndefaultparams = _closure(ret)->_function->_ndefaultparams; + for (int i = 0; i < ndefaultparams; ++i) + { + ReadObject(pBuffer, readState); + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + _closure(ret)->_defaultparams[i] = obj; + sq_poptop(vm_); + } + + *obj = ret; + sq_addref(vm_, obj); + sq_pushobject(vm_, *obj); + } + + ReadObject(pBuffer, readState); + HSQOBJECT env; + sq_resetobject(&env); + sq_getstackobj(vm_, -1, &env); + if (!sq_isnull(env)) + { + if (_closure( *obj ) == nullptr) + Warning("Closure is null\n"); + else + _closure(*obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); + } + sq_poptop(vm_); + + break; + } + case OT_NATIVECLOSURE: + { + char closureName[128] = ""; + pBuffer->GetString(closureName); + + sq_pushroottable(vm_); + sq_pushstring(vm_, closureName, -1); + if (SQ_FAILED(sq_get(vm_, -2))) + { + Warning("SquirrelVM::ReadObject: Failed to find native closure\n"); + sq_pop(vm_, 1); + sq_pushnull(vm_); + } + sq_remove(vm_, -2); + + break; + } + case OT_CLASS: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ClassType classType = (ClassType)pBuffer->GetInt(); + + if (classType == VectorClassType) + { + sq_pushobject(vm_, vectorClass_); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + else if (classType == NativeClassType) + { + char className[128] = ""; + pBuffer->GetString(className); + + sq_pushroottable(vm_); + sq_pushstring(vm_, className, -1); + if (SQ_FAILED(sq_get(vm_, -2))) + { + Warning("SquirrelVM::ReadObject: Failed to find native class: %s\n", className); + sq_pushnull(vm_); + } + sq_remove(vm_, -2); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + else if (classType == ScriptClassType) + { + ReadObject(pBuffer, readState); + bool hasBase = sq_gettype(vm_, -1) != OT_NULL; + if (!hasBase) + { + sq_poptop(vm_); + } + + sq_newclass(vm_, hasBase); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_pushnull(vm_); + ReadObject(pBuffer, readState); + sq_setattributes(vm_, -3); + sq_poptop(vm_); // Returns the old attributes + + while (pBuffer->GetChar()) + { + // TODO: Member Attributes + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + sq_newslot(vm_, -3, false); + } + } + else + { + Error("SquirrelVM::ReadObject: Unknown class type\n"); + sq_pushnull(vm_); + } + break; + } + case OT_INSTANCE: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ReadObject(pBuffer, readState); + + HSQOBJECT klass; + sq_resetobject(&klass); + sq_getstackobj(vm_, -1, &klass); + if (_class(klass) == _class(regexpClass_)) + { + sq_pushnull(vm_); + ReadObject(pBuffer, readState); + sq_call(vm_, 2, SQTrue, SQFalse); + + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_remove(vm_, -2); + + break; + } + + SQUserPointer typetag; + sq_gettypetag(vm_, -1, &typetag); + + if (typetag && typetag != TYPETAG_VECTOR && + ((ScriptClassDesc_t*)typetag)->m_pszDescription[0] == SCRIPT_SINGLETON[0]) + { + sq_poptop(vm_); + + Assert(sq_isclass(klass)); + + // singleton, lets find an equivlent in the root + bool foundSingleton = false; + sq_pushroottable(vm_); + sq_pushnull(vm_); + HSQOBJECT singleton; + sq_resetobject(&singleton); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + sq_getstackobj(vm_, -1, &singleton); + if (sq_isinstance(singleton) && _instance(singleton)->_class == _class(klass)) + { + foundSingleton = true; + *obj = singleton; + sq_addref(vm_, obj); + sq_pop(vm_, 2); + break; + } + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + if (!foundSingleton) + { + Warning("SquirrelVM::ReadObject: Failed to find singleton for %s\n", + ((ScriptClassDesc_t*)typetag)->m_pszScriptName); + } + + sq_pushobject(vm_, *obj); + break; + } + + sq_createinstance(vm_, -1); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_remove(vm_, -2); + + { + // HACK: No way to get the default values part from accessing the class directly + SQUnsignedInteger nvalues = _instance(*obj)->_class->_defaultvalues.size(); + for (SQUnsignedInteger n = 0; n < nvalues; n++) { + ReadObject(pBuffer, readState); + HSQOBJECT val; + sq_resetobject(&val); + sq_getstackobj(vm_, -1, &val); + _instance(*obj)->_values[n] = val; + sq_pop(vm_, 1); + } + } + + if (typetag == TYPETAG_VECTOR) + { + float x = pBuffer->GetFloat(); + float y = pBuffer->GetFloat(); + float z = pBuffer->GetFloat(); + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) Vector(x, y, z); + } + else if (typetag) + { + ScriptClassDesc_t* pClassDesc = (ScriptClassDesc_t*)typetag; + + char instanceName[128] = ""; + pBuffer->GetString(instanceName); + + HSQOBJECT* hinstance = new HSQOBJECT; + sq_resetobject(hinstance); + sq_getstackobj(vm_, -1, hinstance); + sq_addref(vm_, hinstance); + + if (*instanceName) + { + bool allowDestruct = (pBuffer->GetChar() == 1); + + auto instance = pClassDesc->pHelper->BindOnRead((HSCRIPT)hinstance, nullptr, instanceName); + if (instance == nullptr) + { + sq_release(vm_, hinstance); + delete hinstance; + sq_poptop(vm_); + sq_pushnull(vm_); + break; + } + + { + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) ClassInstanceData(instance, pClassDesc, instanceName, allowDestruct); + } + sq_setreleasehook(vm_, -1, allowDestruct ? &destructor_stub : &destructor_stub_instance); + } + else + { + sq_setinstanceup(vm_, -1, nullptr); + } + } + + break; + } + case OT_WEAKREF: + { + ReadObject(pBuffer, readState); + sq_weakref(vm_, -1); + break; + } + case OT_FUNCPROTO: //internal usage only + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + SQObjectPtr ret; + if (!SQFunctionProto::Load(vm_, pBuffer, closure_read, ret)) + { + Error("Failed to deserialize OT_FUNCPROTO\n"); + sq_pushnull(vm_); + break; + } + + vm_->Push(ret); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + case OT_OUTER: //internal usage only + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ReadObject(pBuffer, readState); + HSQOBJECT inner; + sq_resetobject(&inner); + sq_getstackobj(vm_, -1, &inner); + SQOuter* outer = SQOuter::Create(_ss(vm_), nullptr); + outer->_value = inner; + outer->_valptr = &(outer->_value); + sq_poptop(vm_); + vm_->Push(outer); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + break; + } + // case OT_USERDATA: + // case OT_GENERATOR: + // case OT_USERPOINTER: + // case OT_THREAD: + // + default: + Error("SquirrelVM::ReadObject: Unexpected type %d", thisType); + } +} + +void SquirrelVM::ReadState(CUtlBuffer* pBuffer) +{ + SquirrelSafeCheck safeCheck(vm_); + + ReadStateMap readState(vm_); + + sq_pushroottable(vm_); + + HSQOBJECT* obj = nullptr; + readState.CheckCache(pBuffer, &obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + int count = pBuffer->GetInt(); + + for (int i = 0; i < count; ++i) + { + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + + sq_rawset(vm_, -3); + } + + sq_pop(vm_, 1); +} + +void SquirrelVM::RemoveOrphanInstances() +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Is this the right thing to do here? It's not really removing orphan instances + sq_collectgarbage(vm_); +} + +void SquirrelVM::DumpState() +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Dump state +} + +void SquirrelVM::SetOutputCallback(ScriptOutputFunc_t pFunc) +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Support output callbacks +} + +void SquirrelVM::SetErrorCallback(ScriptErrorFunc_t pFunc) +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Support error callbacks +} + +bool SquirrelVM::RaiseException(const char* pszExceptionText) +{ + SquirrelSafeCheck safeCheck(vm_); + sq_pushstring(vm_, pszExceptionText, -1); + sq_resetobject(&lastError_); + sq_getstackobj(vm_, -1, &lastError_); + sq_addref(vm_, &lastError_); + sq_pop(vm_, 1); + return true; +} + + +IScriptVM* makeSquirrelVM() +{ + return new SquirrelVM; +} diff --git a/mp/src/vscript/vscript_squirrel.nut b/mp/src/vscript/vscript_squirrel.nut new file mode 100644 index 00000000..f4d28227 --- /dev/null +++ b/mp/src/vscript/vscript_squirrel.nut @@ -0,0 +1,336 @@ +static char g_Script_vscript_squirrel[] = R"vscript( +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +//=============================================================================// + +Warning <- error; + +function clamp(val,min,max) +{ + if ( max < min ) + return max; + else if( val < min ) + return min; + else if( val > max ) + return max; + else + return val; +} + +function max(a,b) return a > b ? a : b + +function min(a,b) return a < b ? a : b + +function RemapVal(val, A, B, C, D) +{ + if ( A == B ) + return val >= B ? D : C; + return C + (D - C) * (val - A) / (B - A); +} + +function RemapValClamped(val, A, B, C, D) +{ + if ( A == B ) + return val >= B ? D : C; + local cVal = (val - A) / (B - A); + cVal = (cVal < 0.0) ? 0.0 : (1.0 < cVal) ? 1.0 : cVal; + return C + (D - C) * cVal; +} + +function Approach( target, value, speed ) +{ + local delta = target - value + + if( delta > speed ) + value += speed + else if( delta < (-speed) ) + value -= speed + else + value = target + + return value +} + +function AngleDistance( next, cur ) +{ + local delta = next - cur + + if ( delta < (-180.0) ) + delta += 360.0 + else if ( delta > 180.0 ) + delta -= 360.0 + + return delta +} + +function printl( text ) +{ + return ::print(text + "\n"); +} + +class CSimpleCallChainer +{ + constructor(prefixString, scopeForThis, exactMatch) + { + prefix = prefixString; + scope = scopeForThis; + chain = []; + scope["Dispatch" + prefixString] <- Call.bindenv(this); + } + + function PostScriptExecute() + { + local func; + try { + func = scope[prefix]; + } catch(e) { + return; + } + if (typeof(func) != "function") + return; + chain.push(func); + } + + function Call() + { + foreach (func in chain) + { + func.pcall(scope); + } + } + + prefix = null; + scope = null; + chain = null; +} + +DocumentedFuncs <- {} +DocumentedClasses <- {} +DocumentedEnums <- {} +DocumentedConsts <- {} +DocumentedHooks <- {} +DocumentedMembers <- {} + +function AddAliasedToTable(name, signature, description, table) +{ + // This is an alias function, could use split() if we could guarantee + // that ':' would not occur elsewhere in the description and Squirrel had + // a convience join() function -- It has split() + local colon = description.find(":"); + if (colon == null) + colon = description.len(); + local alias = description.slice(1, colon); + description = description.slice(colon + 1); + name = alias; + signature = null; + + table[name] <- [signature, description]; +} + +function RegisterHelp(name, signature, description) +{ + if (description.len() && description[0] == '#') + { + AddAliasedToTable(name, signature, description, DocumentedFuncs) + } + else + { + DocumentedFuncs[name] <- [signature, description]; + } +} + +function RegisterClassHelp(name, baseclass, description) +{ + DocumentedClasses[name] <- [baseclass, description]; +} + +function RegisterEnumHelp(name, num_elements, description) +{ + DocumentedEnums[name] <- [num_elements, description]; +} + +function RegisterConstHelp(name, signature, description) +{ + if (description.len() && description[0] == '#') + { + AddAliasedToTable(name, signature, description, DocumentedConsts) + } + else + { + DocumentedConsts[name] <- [signature, description]; + } +} + +function RegisterHookHelp(name, signature, description) +{ + DocumentedHooks[name] <- [signature, description]; +} + +function RegisterMemberHelp(name, signature, description) +{ + DocumentedMembers[name] <- [signature, description]; +} + +function printdoc( text ) +{ + return ::printc(200,224,255,text); +} + +function printdocl( text ) +{ + return printdoc(text + "\n"); +} + +function PrintClass(name, doc) +{ + local text = "=====================================\n"; + text += ("Class: " + name + "\n"); + text += ("Base: " + doc[0] + "\n"); + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + text += "=====================================\n\n"; + + printdoc(text); +} + +function PrintFunc(name, doc) +{ + local text = "Function: " + name + "\n" + + if (doc[0] == null) + { + // Is an aliased function + text += ("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos().parameters) + { + if (k == 0 && v == "this") continue; + if (k > 1) text += (", "); + text += (v); + } + text += (")\n"); + } + else + { + text += ("Signature: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); +} + +function PrintMember(name, doc) +{ + local text = ("Member: " + name + "\n"); + text += ("Signature: " + doc[0] + "\n"); + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); +} + +function PrintEnum(name, doc) +{ + local text = "=====================================\n"; + text += ("Enum: " + name + "\n"); + text += ("Elements: " + doc[0] + "\n"); + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + text += "=====================================\n\n"; + + printdoc(text); +} + +function PrintConst(name, doc) +{ + local text = ("Constant: " + name + "\n"); + if (doc[0] == null) + { + text += ("Value: null\n"); + } + else + { + text += ("Value: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); +} + +function PrintHook(name, doc) +{ + local text = ("Hook: " + name + "\n"); + if (doc[0] == null) + { + // Is an aliased function + text += ("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos().parameters) + { + if (k == 0 && v == "this") continue; + if (k > 1) text += (", "); + text += (v); + } + text += (")\n"); + } + else + { + text += ("Signature: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); +} + +function PrintMatchesInDocList(pattern, list, printfunc) +{ + local foundMatches = false; + + foreach(name, doc in list) + { + if (pattern == "*" || name.tolower().find(pattern) != null || (doc[1].len() && doc[1].tolower().find(pattern) != null)) + { + foundMatches = true; + printfunc(name, doc) + } + } + + return foundMatches; +} + +function PrintHelp(pattern = "*") +{ + local foundMatches = false; + local patternLower = pattern.tolower(); + + // Have a specific order + foundMatches = ( PrintMatchesInDocList( patternLower, DocumentedEnums, PrintEnum ) || foundMatches ); + foundMatches = ( PrintMatchesInDocList( patternLower, DocumentedConsts, PrintConst ) || foundMatches ); + foundMatches = ( PrintMatchesInDocList( patternLower, DocumentedClasses, PrintClass ) || foundMatches ); + foundMatches = ( PrintMatchesInDocList( patternLower, DocumentedFuncs, PrintFunc ) || foundMatches ); + foundMatches = ( PrintMatchesInDocList( patternLower, DocumentedMembers, PrintMember ) || foundMatches ); + foundMatches = ( PrintMatchesInDocList( patternLower, DocumentedHooks, PrintHook ) || foundMatches ); + + if (!foundMatches) + printdocl("Pattern " + pattern + " not found"); +} + +// Vector documentation +RegisterClassHelp( "Vector", "", "Basic 3-float Vector class." ); +RegisterHelp( "Vector::Length", "float Vector::Length()", "Return the vector's length." ); +RegisterHelp( "Vector::LengthSqr", "float Vector::LengthSqr()", "Return the vector's squared length." ); +RegisterHelp( "Vector::Length2D", "float Vector::Length2D()", "Return the vector's 2D length." ); +RegisterHelp( "Vector::Length2DSqr", "float Vector::Length2DSqr()", "Return the vector's squared 2D length." ); + +RegisterHelp( "Vector::Normalized", "float Vector::Normalized()", "Return a normalized version of the vector." ); +RegisterHelp( "Vector::Norm", "void Vector::Norm()", "Normalize the vector in place." ); +RegisterHelp( "Vector::Scale", "vector Vector::Scale(float)", "Scale the vector's magnitude and return the result." ); +RegisterHelp( "Vector::Dot", "float Vector::Dot(vector)", "Return the dot/scalar product of two vectors." ); +RegisterHelp( "Vector::Cross", "float Vector::Cross(vector)", "Return the vector product of two vectors." ); + +RegisterHelp( "Vector::ToKVString", "string Vector::ToKVString()", "Return a vector as a string in KeyValue form, without separation commas." ); + +RegisterMemberHelp( "Vector.x", "float Vector.x", "The vector's X coordinate on the cartesian X axis." ); +RegisterMemberHelp( "Vector.y", "float Vector.y", "The vector's Y coordinate on the cartesian Y axis." ); +RegisterMemberHelp( "Vector.z", "float Vector.z", "The vector's Z coordinate on the cartesian Z axis." ); + +)vscript"; \ No newline at end of file