From c19dccff63c77258c48560f03b387175d918b6df Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 18 Jan 2023 11:58:02 -0600 Subject: [PATCH] Updated Mapbase MP port to Mapbase v7.1 --- mp/src/devtools/makefile_base_posix.mak | 7 +- mp/src/fgdlib/gamedata.cpp | 4 + mp/src/game/client/C_Env_Projected_Texture.h | 1 + mp/src/game/client/c_baseanimating.cpp | 302 +- mp/src/game/client/c_baseanimating.h | 61 +- mp/src/game/client/c_basecombatcharacter.cpp | 36 + mp/src/game/client/c_basecombatcharacter.h | 9 + mp/src/game/client/c_basecombatweapon.cpp | 49 +- mp/src/game/client/c_baseentity.cpp | 125 +- mp/src/game/client/c_baseentity.h | 62 +- mp/src/game/client/c_baselesson.cpp | 37 +- mp/src/game/client/c_baselesson.h | 2 +- mp/src/game/client/c_baseplayer.cpp | 73 +- mp/src/game/client/c_baseplayer.h | 19 +- mp/src/game/client/c_baseviewmodel.cpp | 4 +- mp/src/game/client/c_colorcorrection.cpp | 208 +- mp/src/game/client/c_colorcorrection.h | 88 + .../game/client/c_colorcorrectionvolume.cpp | 116 + mp/src/game/client/c_effects.cpp | 115 +- mp/src/game/client/c_effects.h | 4 + mp/src/game/client/c_env_global_light.cpp | 20 +- mp/src/game/client/c_env_projectedtexture.cpp | 9 +- mp/src/game/client/c_env_screenoverlay.cpp | 32 +- mp/src/game/client/c_gameinstructor.h | 2 +- mp/src/game/client/c_movie_display.cpp | 27 + mp/src/game/client/c_movie_display.h | 36 + mp/src/game/client/c_particle_system.cpp | 44 +- .../game/client/c_point_commentary_node.cpp | 1371 +++++- mp/src/game/client/c_props.cpp | 5 + mp/src/game/client/c_rope.cpp | 40 + mp/src/game/client/c_rope.h | 13 + mp/src/game/client/c_vguiscreen.h | 9 + mp/src/game/client/c_world.cpp | 82 +- mp/src/game/client/c_world.h | 13 +- mp/src/game/client/cdll_client_int.cpp | 18 + mp/src/game/client/cdll_util.h | 1 - mp/src/game/client/client_base.vpc | 6 +- mp/src/game/client/client_mapbase.vpc | 17 +- mp/src/game/client/clientmode_shared.cpp | 42 +- mp/src/game/client/clientmode_shared.h | 9 + mp/src/game/client/clientshadowmgr.cpp | 27 + mp/src/game/client/colorcorrectionmgr.cpp | 125 + mp/src/game/client/colorcorrectionmgr.h | 36 + mp/src/game/client/convarproxy.cpp | 113 + .../game/client/episodic/c_prop_scalable.cpp | 57 + mp/src/game/client/flashlighteffect.cpp | 4 +- mp/src/game/client/fx_impact.cpp | 90 +- mp/src/game/client/fx_impact.h | 9 + .../client/game_controls/baseviewport.cpp | 26 + .../game/client/game_controls/baseviewport.h | 4 + mp/src/game/client/glow_outline_effect.h | 4 + mp/src/game/client/hl2/c_basehlplayer.cpp | 28 + mp/src/game/client/hl2/c_basehlplayer.h | 16 +- mp/src/game/client/hl2/hud_credits.cpp | 435 +- .../game/client/hl2/hud_weaponselection.cpp | 76 + mp/src/game/client/hud_closecaption.cpp | 195 +- mp/src/game/client/hud_closecaption.h | 16 +- mp/src/game/client/hud_lcd.cpp | 24 +- mp/src/game/client/hud_locator_target.cpp | 2 +- mp/src/game/client/hud_locator_target.h | 2 +- mp/src/game/client/hud_pdump.cpp | 6 + mp/src/game/client/hudelement.h | 3 + mp/src/game/client/iclientmode.h | 5 + mp/src/game/client/in_joystick.cpp | 2 + mp/src/game/client/iviewrender.h | 7 + .../mapbase/c_func_fake_worldportal.cpp | 93 +- .../client/mapbase/c_func_fake_worldportal.h | 6 +- .../client/mapbase/c_vgui_text_display.cpp | 282 ++ .../client/mapbase/mapbase_autocubemap.cpp | 280 ++ mp/src/game/client/mapbase/vscript_vgui.cpp | 3891 +++++++++++++++++ mp/src/game/client/mapbase/vscript_vgui.h | 16 + mp/src/game/client/mapbase/vscript_vgui.nut | 394 ++ mp/src/game/client/panelmetaclassmgr.cpp | 8 - mp/src/game/client/particlemgr.h | 2 +- mp/src/game/client/physics_main_client.cpp | 2 +- mp/src/game/client/physpropclientside.cpp | 150 + mp/src/game/client/proxyplayer.cpp | 101 +- mp/src/game/client/ragdoll.cpp | 33 + mp/src/game/client/text_message.cpp | 2 +- mp/src/game/client/vgui_debugoverlaypanel.cpp | 4 +- mp/src/game/client/vgui_movie_display.cpp | 442 ++ mp/src/game/client/vgui_video.cpp | 361 +- mp/src/game/client/vgui_video.h | 23 +- mp/src/game/client/view.cpp | 21 +- mp/src/game/client/viewdebug.cpp | 108 + mp/src/game/client/viewrender.cpp | 349 +- mp/src/game/client/viewrender.h | 13 +- mp/src/game/client/vscript_client.cpp | 226 +- mp/src/game/client/vscript_client.nut | 18 +- mp/src/game/client/worldlight.cpp | 188 +- mp/src/game/client/worldlight.h | 15 + mp/src/game/server/AI_Criteria.h | 4 + mp/src/game/server/AI_ResponseSystem.cpp | 3 +- mp/src/game/server/AI_ResponseSystem.h | 4 + mp/src/game/server/BaseAnimatingOverlay.cpp | 112 + mp/src/game/server/BaseAnimatingOverlay.h | 28 + mp/src/game/server/BasePropDoor.h | 14 + mp/src/game/server/CommentarySystem.cpp | 337 +- mp/src/game/server/EntityFlame.cpp | 5 + mp/src/game/server/EnvMessage.cpp | 40 + mp/src/game/server/ai_activity.cpp | 655 ++- mp/src/game/server/ai_baseactor.cpp | 34 +- mp/src/game/server/ai_basenpc.cpp | 979 +++-- mp/src/game/server/ai_basenpc.h | 71 +- mp/src/game/server/ai_basenpc_schedule.cpp | 85 +- mp/src/game/server/ai_behavior.cpp | 18 + mp/src/game/server/ai_behavior.h | 64 + mp/src/game/server/ai_behavior_follow.cpp | 6 +- mp/src/game/server/ai_behavior_follow.h | 4 + mp/src/game/server/ai_behavior_lead.h | 7 + mp/src/game/server/ai_behavior_standoff.cpp | 153 +- mp/src/game/server/ai_behavior_standoff.h | 11 + mp/src/game/server/ai_blended_movement.cpp | 7 + mp/src/game/server/ai_expresserfollowup.cpp | 509 +++ mp/src/game/server/ai_hint.cpp | 109 +- mp/src/game/server/ai_hint.h | 38 + mp/src/game/server/ai_initutils.cpp | 21 +- mp/src/game/server/ai_initutils.h | 5 + mp/src/game/server/ai_memory.cpp | 23 - mp/src/game/server/ai_memory.h | 23 - mp/src/game/server/ai_motor.cpp | 210 +- mp/src/game/server/ai_motor.h | 7 + mp/src/game/server/ai_navigator.cpp | 55 +- mp/src/game/server/ai_network.cpp | 35 + mp/src/game/server/ai_networkmanager.cpp | 36 + mp/src/game/server/ai_playerally.cpp | 50 +- mp/src/game/server/ai_speech.cpp | 30 +- mp/src/game/server/ai_speech.h | 12 + mp/src/game/server/ai_speech_new.cpp | 1777 ++++++++ mp/src/game/server/ai_speech_new.h | 713 +++ mp/src/game/server/ai_speechqueue.cpp | 495 +++ mp/src/game/server/ai_speechqueue.h | 239 + mp/src/game/server/ai_tacticalservices.cpp | 5 + mp/src/game/server/baseanimating.cpp | 60 +- mp/src/game/server/baseanimating.h | 9 + mp/src/game/server/basebludgeonweapon.cpp | 4 + mp/src/game/server/basecombatcharacter.cpp | 220 +- mp/src/game/server/basecombatcharacter.h | 20 +- mp/src/game/server/basecombatweapon.cpp | 9 + mp/src/game/server/baseentity.cpp | 525 ++- mp/src/game/server/baseentity.h | 78 +- mp/src/game/server/baseflex.cpp | 31 +- mp/src/game/server/baseflex.h | 2 + mp/src/game/server/basemultiplayerplayer.cpp | 7 + mp/src/game/server/bmodels.cpp | 39 +- mp/src/game/server/cbase.cpp | 107 +- mp/src/game/server/cbase.h | 7 + mp/src/game/server/colorcorrection.cpp | 193 +- mp/src/game/server/colorcorrection.h | 147 + mp/src/game/server/colorcorrectionvolume.cpp | 13 + mp/src/game/server/effects.cpp | 15 +- mp/src/game/server/entity_tools_server.cpp | 7 + mp/src/game/server/entitylist.cpp | 263 ++ mp/src/game/server/entitylist.h | 36 +- mp/src/game/server/env_instructor_hint.cpp | 96 +- mp/src/game/server/env_projectedtexture.cpp | 9 + mp/src/game/server/env_projectedtexture.h | 3 + mp/src/game/server/env_screenoverlay.cpp | 12 + mp/src/game/server/env_tonemap_controller.cpp | 333 +- mp/src/game/server/env_tonemap_controller.h | 140 + mp/src/game/server/envmicrophone.cpp | 131 +- mp/src/game/server/envmicrophone.h | 17 + mp/src/game/server/episodic/npc_hunter.cpp | 23 + mp/src/game/server/episodic/npc_magnusson.cpp | 5 + mp/src/game/server/eventqueue.h | 8 +- mp/src/game/server/explode.cpp | 2 +- mp/src/game/server/filters.cpp | 92 +- mp/src/game/server/fogvolume.cpp | 153 + mp/src/game/server/fogvolume.h | 74 + mp/src/game/server/func_break.cpp | 4 + mp/src/game/server/func_lod.cpp | 4 + mp/src/game/server/gameinterface.cpp | 4 + mp/src/game/server/genericactor.cpp | 3 +- .../game/server/hl2/ai_behavior_actbusy.cpp | 15 +- mp/src/game/server/hl2/ai_behavior_actbusy.h | 4 + .../game/server/hl2/ai_behavior_functank.cpp | 22 + mp/src/game/server/hl2/ai_behavior_functank.h | 2 + mp/src/game/server/hl2/ai_behavior_police.cpp | 10 + mp/src/game/server/hl2/basehlcombatweapon.cpp | 4 + mp/src/game/server/hl2/cbasehelicopter.h | 4 + mp/src/game/server/hl2/combine_mine.cpp | 73 +- mp/src/game/server/hl2/combine_mine.h | 13 +- mp/src/game/server/hl2/env_speaker.cpp | 60 + mp/src/game/server/hl2/func_tank.cpp | 12 +- mp/src/game/server/hl2/hl2_client.cpp | 2 +- mp/src/game/server/hl2/hl2_player.cpp | 356 +- mp/src/game/server/hl2/hl2_player.h | 21 +- mp/src/game/server/hl2/item_battery.cpp | 24 +- mp/src/game/server/hl2/item_healthkit.cpp | 147 + mp/src/game/server/hl2/npc_BaseZombie.cpp | 26 +- mp/src/game/server/hl2/npc_alyx.h | 3 + mp/src/game/server/hl2/npc_alyx_episodic.cpp | 96 +- mp/src/game/server/hl2/npc_alyx_episodic.h | 9 + mp/src/game/server/hl2/npc_antlion.cpp | 18 +- mp/src/game/server/hl2/npc_antlionguard.cpp | 4 +- mp/src/game/server/hl2/npc_attackchopper.cpp | 25 + mp/src/game/server/hl2/npc_barnacle.cpp | 10 +- mp/src/game/server/hl2/npc_barney.cpp | 7 + mp/src/game/server/hl2/npc_basescanner.cpp | 8 + mp/src/game/server/hl2/npc_breen.cpp | 5 + mp/src/game/server/hl2/npc_citizen17.cpp | 114 +- mp/src/game/server/hl2/npc_citizen17.h | 12 +- mp/src/game/server/hl2/npc_combine.cpp | 96 +- mp/src/game/server/hl2/npc_combine.h | 18 +- .../game/server/hl2/npc_combinedropship.cpp | 16 +- mp/src/game/server/hl2/npc_eli.cpp | 5 + mp/src/game/server/hl2/npc_enemyfinder.cpp | 14 + mp/src/game/server/hl2/npc_fastzombie.cpp | 14 +- mp/src/game/server/hl2/npc_fisherman.cpp | 4 +- mp/src/game/server/hl2/npc_headcrab.cpp | 12 +- mp/src/game/server/hl2/npc_kleiner.cpp | 5 + mp/src/game/server/hl2/npc_manhack.cpp | 6 +- mp/src/game/server/hl2/npc_metropolice.cpp | 79 +- mp/src/game/server/hl2/npc_metropolice.h | 7 +- mp/src/game/server/hl2/npc_monk.cpp | 43 + mp/src/game/server/hl2/npc_mossman.cpp | 5 + .../game/server/hl2/npc_playercompanion.cpp | 137 +- mp/src/game/server/hl2/npc_playercompanion.h | 19 +- mp/src/game/server/hl2/npc_rollermine.cpp | 38 +- mp/src/game/server/hl2/npc_scanner.cpp | 16 +- mp/src/game/server/hl2/npc_stalker.cpp | 4 +- mp/src/game/server/hl2/npc_strider.cpp | 12 + mp/src/game/server/hl2/npc_strider.h | 3 + mp/src/game/server/hl2/npc_turret_ceiling.cpp | 4 +- mp/src/game/server/hl2/npc_turret_ground.cpp | 4 +- .../server/hl2/npc_vortigaunt_episodic.cpp | 26 +- .../game/server/hl2/npc_vortigaunt_episodic.h | 7 + mp/src/game/server/hl2/npc_zombie.cpp | 17 +- mp/src/game/server/hl2/proto_sniper.cpp | 84 + mp/src/game/server/hl2/vehicle_apc.cpp | 12 +- mp/src/game/server/hl2/weapon_357.cpp | 127 + mp/src/game/server/hl2/weapon_alyxgun.cpp | 37 +- mp/src/game/server/hl2/weapon_alyxgun.h | 10 + mp/src/game/server/hl2/weapon_annabelle.cpp | 65 + mp/src/game/server/hl2/weapon_ar1.cpp | 81 + mp/src/game/server/hl2/weapon_ar2.cpp | 56 +- mp/src/game/server/hl2/weapon_ar2.h | 3 + mp/src/game/server/hl2/weapon_bugbait.cpp | 27 + .../game/server/hl2/weapon_citizenpackage.cpp | 14 + mp/src/game/server/hl2/weapon_crossbow.cpp | 114 +- mp/src/game/server/hl2/weapon_crowbar.cpp | 23 + mp/src/game/server/hl2/weapon_crowbar.h | 6 + mp/src/game/server/hl2/weapon_frag.cpp | 27 + mp/src/game/server/hl2/weapon_physcannon.cpp | 46 + mp/src/game/server/hl2/weapon_pistol.cpp | 136 + mp/src/game/server/hl2/weapon_rpg.cpp | 89 + mp/src/game/server/hl2/weapon_rpg.h | 3 + mp/src/game/server/hl2/weapon_shotgun.cpp | 109 + mp/src/game/server/hl2/weapon_smg1.cpp | 34 + mp/src/game/server/hl2/weapon_smg2.cpp | 71 + mp/src/game/server/hl2/weapon_sniperrifle.cpp | 82 +- mp/src/game/server/hltvdirector.h | 2 +- mp/src/game/server/items.h | 9 +- mp/src/game/server/logic_playmovie.cpp | 136 + mp/src/game/server/logicentities.cpp | 2 +- mp/src/game/server/mapbase/GlobalStrings.h | 10 +- mp/src/game/server/mapbase/ai_grenade.cpp | 2 + mp/src/game/server/mapbase/ai_grenade.h | 203 +- mp/src/game/server/mapbase/ai_monitor.cpp | 51 +- .../server/mapbase/func_fake_worldportal.cpp | 4 +- .../server/mapbase/logic_externaldata.cpp | 4 +- .../game/server/mapbase/logic_substring.cpp | 110 + .../game/server/mapbase/vgui_text_display.cpp | 437 ++ mp/src/game/server/movie_display.cpp | 378 ++ mp/src/game/server/nav_merge.cpp | 2 +- mp/src/game/server/particle_system.cpp | 31 +- mp/src/game/server/particle_system.h | 13 + mp/src/game/server/physics_main.cpp | 16 +- mp/src/game/server/physics_prop_ragdoll.cpp | 40 + mp/src/game/server/player.cpp | 345 +- mp/src/game/server/player.h | 35 +- mp/src/game/server/playerlocaldata.h | 3 + mp/src/game/server/point_camera.cpp | 52 + mp/src/game/server/point_camera.h | 5 + mp/src/game/server/point_spotlight.cpp | 37 + mp/src/game/server/props.cpp | 206 +- mp/src/game/server/props.h | 9 + mp/src/game/server/rope.cpp | 49 + mp/src/game/server/rope.h | 5 + mp/src/game/server/sceneentity.cpp | 222 +- mp/src/game/server/sceneentity.h | 4 + mp/src/game/server/scripted.cpp | 12 + mp/src/game/server/scripted.h | 4 + mp/src/game/server/server_base.vpc | 15 +- mp/src/game/server/server_mapbase.vpc | 15 + mp/src/game/server/triggers.cpp | 92 +- mp/src/game/server/triggers.h | 5 + mp/src/game/server/vscript_server.cpp | 748 +--- mp/src/game/server/vscript_server.h | 12 - mp/src/game/server/vscript_server.nut | 36 +- mp/src/game/server/world.cpp | 15 +- mp/src/game/server/world.h | 2 +- mp/src/game/shared/GameEventListener.h | 2 +- mp/src/game/shared/SoundEmitterSystem.cpp | 80 +- mp/src/game/shared/activitylist.cpp | 478 +- mp/src/game/shared/ai_activity.h | 563 ++- mp/src/game/shared/ai_criteria_new.cpp | 38 + mp/src/game/shared/ai_criteria_new.h | 41 + mp/src/game/shared/ai_responsesystem_new.cpp | 1388 ++++++ mp/src/game/shared/ai_responsesystem_new.h | 29 + mp/src/game/shared/ai_speechconcept.cpp | 28 + mp/src/game/shared/ai_speechconcept.h | 45 + mp/src/game/shared/base_playeranimstate.cpp | 19 + .../game/shared/basecombatweapon_shared.cpp | 243 +- mp/src/game/shared/basecombatweapon_shared.h | 16 +- mp/src/game/shared/baseentity_shared.cpp | 431 +- mp/src/game/shared/baseviewmodel_shared.cpp | 98 + mp/src/game/shared/beam_shared.cpp | 16 + mp/src/game/shared/gamemovement.cpp | 8 +- .../shared/hl2/basehlcombatweapon_shared.cpp | 8 + mp/src/game/shared/hl2/hl2_usermessages.cpp | 3 +- mp/src/game/shared/hl2mp/weapon_357.cpp | 13 + mp/src/game/shared/hl2mp/weapon_ar2.cpp | 13 + mp/src/game/shared/hl2mp/weapon_ar2.h | 3 + mp/src/game/shared/hl2mp/weapon_pistol.cpp | 13 + mp/src/game/shared/hl2mp/weapon_shotgun.cpp | 13 + mp/src/game/shared/hl2mp/weapon_slam.cpp | 4 + mp/src/game/shared/hl2mp/weapon_stunstick.cpp | 21 + mp/src/game/shared/hl2mp/weapon_stunstick.h | 6 + mp/src/game/shared/mapbase/MapEdit.cpp | 14 +- .../shared/mapbase/logic_script_client.cpp | 276 ++ mp/src/game/shared/mapbase/mapbase_shared.cpp | 349 +- .../shared/mapbase/mapbase_usermessages.cpp | 35 + mp/src/game/shared/mapbase/matchers.cpp | 185 - mp/src/game/shared/mapbase/matchers.h | 45 +- .../shared/mapbase/singleplayer_animstate.cpp | 669 +++ .../shared/mapbase/singleplayer_animstate.h | 110 + .../shared/mapbase/vscript_consts_shared.cpp | 116 +- .../shared/mapbase/vscript_funcs_shared.cpp | 529 ++- .../shared/mapbase/vscript_funcs_shared.h | 302 +- .../shared/mapbase/vscript_singletons.cpp | 2780 ++++++++++-- .../game/shared/mapbase/vscript_singletons.h | 143 +- .../shared/mapbase/weapon_custom_scripted.cpp | 14 +- .../shared/mapbase/weapon_custom_scripted.h | 6 +- mp/src/game/shared/movevars_shared.cpp | 12 +- mp/src/game/shared/ragdoll_shared.cpp | 100 +- mp/src/game/shared/ragdoll_shared.h | 29 + mp/src/game/shared/shareddefs.h | 23 + mp/src/game/shared/takedamageinfo.h | 4 +- mp/src/game/shared/usercmd.cpp | 36 + mp/src/game/shared/usercmd.h | 37 +- mp/src/game/shared/usermessages.cpp | 9 + mp/src/game/shared/util_shared.cpp | 2 +- mp/src/game/shared/util_shared.h | 2 +- mp/src/game/shared/vscript_shared.cpp | 459 +- mp/src/game/shared/vscript_shared.h | 13 + mp/src/game/shared/weapon_parse.cpp | 19 + mp/src/game/shared/weapon_parse.h | 11 + .../stdshaders/BaseVSShader.cpp | 3 + .../stdshaders/SDK_core_ps2x.fxc | 222 + .../stdshaders/SDK_core_vs20.fxc | 103 + .../stdshaders/SDK_teeth_bump_vs20.fxc | 9 +- .../SDK_vertexlit_and_unlit_generic_ps20b.fxc | 28 +- .../SDK_vertexlit_and_unlit_generic_ps2x.fxc | 31 +- .../stdshaders/SDK_windowimposter_ps2x.fxc | 8 +- .../stdshaders/SDK_windowimposter_vs20.fxc | 8 +- mp/src/materialsystem/stdshaders/core_dx9.cpp | 307 ++ .../stdshaders/fxctmp9/SDK_core_ps20.inc | 162 + .../stdshaders/fxctmp9/SDK_core_ps20b.inc | 187 + .../stdshaders/fxctmp9/SDK_core_vs20.inc | 112 + .../SDK_vertexlit_and_unlit_generic_ps20.inc | 31 +- .../SDK_vertexlit_and_unlit_generic_ps20b.inc | 29 +- .../SDK_vertexlit_and_unlit_generic_ps30.inc | 24 +- .../stdshaders/fxctmp9/engine_post_ps20b.inc | 362 ++ .../stdshaders/game_shader_dx9_mapbase.vpc | 2 + .../lightmappedgeneric_dx9_helper.cpp | 7 +- mp/src/materialsystem/stdshaders/teeth.cpp | 14 +- .../stdshaders/unlitgeneric_dx9.cpp | 14 + .../stdshaders/vertexlitgeneric_dx9.cpp | 10 + .../vertexlitgeneric_dx9_helper.cpp | 102 +- .../stdshaders/vertexlitgeneric_dx9_helper.h | 11 +- .../stdshaders/windowimposter_dx90.cpp | 6 +- mp/src/mathlib/polyhedron.cpp | 22 +- mp/src/public/ScratchPadUtils.cpp | 16 +- mp/src/public/bitmap/imageformat.h | 4 +- mp/src/public/bone_setup.cpp | 28 +- mp/src/public/datamap.h | 8 + mp/src/public/dt_utlvector_send.cpp | 2 +- mp/src/public/dt_utlvector_send.h | 2 +- mp/src/public/haptics/haptic_utils.cpp | 6 + mp/src/public/keyframe/keyframe.cpp | 16 +- mp/src/public/keyframe/keyframe.h | 4 +- .../materialsystem/MaterialSystemUtil.h | 4 + mp/src/public/networkvar.h | 4 +- .../responserules/response_host_interface.h | 66 + mp/src/public/responserules/response_types.h | 480 ++ .../public/responserules/rr_speechconcept.h | 57 + mp/src/public/saverestoretypes.h | 26 +- mp/src/public/scratchpad3d.h | 2 +- mp/src/public/sentence.cpp | 14 +- mp/src/public/studio.cpp | 26 +- mp/src/public/studio.h | 6 +- mp/src/public/tier0/basetypes.h | 65 +- mp/src/public/tier0/dbg.h | 6 +- mp/src/public/tier0/memalloc.h | 5 + mp/src/public/tier0/memdbgon.h | 8 +- mp/src/public/tier0/memoverride.cpp | 163 +- mp/src/public/tier0/platform.h | 100 +- mp/src/public/tier1/UtlSortVector.h | 2 +- mp/src/public/tier1/byteswap.h | 2 +- mp/src/public/tier1/convar.h | 9 +- mp/src/public/tier1/interval.h | 20 + mp/src/public/tier1/mapbase_con_groups.h | 49 +- mp/src/public/tier1/mapbase_matchers_base.h | 61 + mp/src/public/tier1/strtools.h | 10 + mp/src/public/vgui/MouseCode.h | 9 + mp/src/public/vgui_controls/Panel.h | 4 +- mp/src/public/vgui_controls/TextImage.h | 5 + mp/src/public/vphysics_interface.h | 24 - mp/src/public/vscript/ivscript.h | 483 +- mp/src/public/vscript/vscript_templates.h | 46 +- mp/src/raytrace/raytrace.cpp | 6 +- mp/src/raytrace/trace2.cpp | 10 +- mp/src/responserules/runtime/criteriaset.cpp | 479 ++ .../responserules/runtime/response_rules.vpc | 41 + .../responserules/runtime/response_system.cpp | 2959 +++++++++++++ .../responserules/runtime/response_system.h | 336 ++ .../responserules/runtime/response_types.cpp | 281 ++ .../runtime/response_types_internal.cpp | 172 + .../runtime/response_types_internal.h | 560 +++ mp/src/responserules/runtime/rr_convars.cpp | 14 + mp/src/responserules/runtime/rr_response.cpp | 387 ++ .../runtime/rr_speechconcept.cpp | 73 + mp/src/responserules/runtime/rrbase.h | 59 + mp/src/responserules/runtime/rrrlib.cpp | 13 + mp/src/responserules/runtime/stdafx.cpp | 11 + mp/src/tier1/KeyValues.cpp | 25 + mp/src/tier1/bitbuf.cpp | 10 +- mp/src/tier1/convar.cpp | 3 + mp/src/tier1/interval.cpp | 59 + mp/src/tier1/mapbase_con_groups.cpp | 192 +- mp/src/tier1/strtools.cpp | 8 +- mp/src/tier1/tier1.vpc | 3 + .../utils/captioncompiler/captioncompiler.vpc | 3 +- mp/src/utils/vbsp/map.cpp | 34 +- mp/src/utils/vbsp/staticprop.cpp | 41 + mp/src/utils/vbsp/vbsp.cpp | 12 +- mp/src/utils/vbsp/vbsp.h | 6 + mp/src/utils/vbsp/vscript_funcs_vmfs.cpp | 1 + mp/src/utils/vbsp/vscript_vbsp.cpp | 36 + .../vgui_controls/AnimationController.cpp | 45 +- mp/src/vgui2/vgui_controls/Button.cpp | 6 +- mp/src/vgui2/vgui_controls/Panel.cpp | 33 +- .../vgui2/vgui_controls/ScrollBarSlider.cpp | 2 + mp/src/vgui2/vgui_controls/TextImage.cpp | 114 + mp/src/vgui2/vgui_controls/Tooltip.cpp | 4 +- mp/src/vgui2/vgui_controls/TreeView.cpp | 2 +- mp/src/vpc_scripts/groups.vgc | 22 + mp/src/vpc_scripts/projects.vgc | 9 +- mp/src/vpc_scripts/source_base.vpc | 15 +- mp/src/vpc_scripts/source_dll_win32_base.vpc | 14 +- .../vpc_scripts/source_exe_win_win32_base.vpc | 10 +- mp/src/vpc_scripts/source_lib_win32_base.vpc | 3 +- mp/src/vpc_scripts/source_win32_base.vpc | 21 +- .../doc/source/stdlib/stdstringlib.rst | 8 +- .../vscript/squirrel/sqstdlib/sqstdblob.cpp | 4 +- .../vscript/squirrel/sqstdlib/sqstdstring.cpp | 42 +- .../vscript/squirrel/squirrel/sqbaselib.cpp | 14 +- mp/src/vscript/squirrel/squirrel/sqclass.cpp | 3 + mp/src/vscript/squirrel/squirrel/sqclass.h | 1 + mp/src/vscript/squirrel/squirrel/sqtable.h | 2 +- mp/src/vscript/vscript_bindings_base.cpp | 115 +- mp/src/vscript/vscript_bindings_base.h | 1 + mp/src/vscript/vscript_bindings_math.cpp | 65 +- mp/src/vscript/vscript_squirrel.cpp | 955 +++- mp/src/vscript/vscript_squirrel.nut | 692 ++- 466 files changed, 44286 insertions(+), 5102 deletions(-) create mode 100644 mp/src/game/client/c_colorcorrection.h create mode 100644 mp/src/game/client/c_movie_display.cpp create mode 100644 mp/src/game/client/c_movie_display.h create mode 100644 mp/src/game/client/convarproxy.cpp create mode 100644 mp/src/game/client/mapbase/c_vgui_text_display.cpp create mode 100644 mp/src/game/client/mapbase/mapbase_autocubemap.cpp create mode 100644 mp/src/game/client/mapbase/vscript_vgui.cpp create mode 100644 mp/src/game/client/mapbase/vscript_vgui.h create mode 100644 mp/src/game/client/mapbase/vscript_vgui.nut create mode 100644 mp/src/game/client/vgui_movie_display.cpp create mode 100644 mp/src/game/server/ai_expresserfollowup.cpp create mode 100644 mp/src/game/server/ai_speech_new.cpp create mode 100644 mp/src/game/server/ai_speech_new.h create mode 100644 mp/src/game/server/ai_speechqueue.cpp create mode 100644 mp/src/game/server/ai_speechqueue.h create mode 100644 mp/src/game/server/colorcorrection.h create mode 100644 mp/src/game/server/env_tonemap_controller.h create mode 100644 mp/src/game/server/fogvolume.cpp create mode 100644 mp/src/game/server/fogvolume.h create mode 100644 mp/src/game/server/logic_playmovie.cpp create mode 100644 mp/src/game/server/mapbase/logic_substring.cpp create mode 100644 mp/src/game/server/mapbase/vgui_text_display.cpp create mode 100644 mp/src/game/server/movie_display.cpp create mode 100644 mp/src/game/shared/ai_criteria_new.cpp create mode 100644 mp/src/game/shared/ai_criteria_new.h create mode 100644 mp/src/game/shared/ai_responsesystem_new.cpp create mode 100644 mp/src/game/shared/ai_responsesystem_new.h create mode 100644 mp/src/game/shared/ai_speechconcept.cpp create mode 100644 mp/src/game/shared/ai_speechconcept.h create mode 100644 mp/src/game/shared/mapbase/logic_script_client.cpp create mode 100644 mp/src/game/shared/mapbase/mapbase_usermessages.cpp create mode 100644 mp/src/game/shared/mapbase/singleplayer_animstate.cpp create mode 100644 mp/src/game/shared/mapbase/singleplayer_animstate.h create mode 100644 mp/src/materialsystem/stdshaders/SDK_core_ps2x.fxc create mode 100644 mp/src/materialsystem/stdshaders/SDK_core_vs20.fxc create mode 100644 mp/src/materialsystem/stdshaders/core_dx9.cpp create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20b.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_vs20.inc create mode 100644 mp/src/materialsystem/stdshaders/fxctmp9/engine_post_ps20b.inc create mode 100644 mp/src/public/responserules/response_host_interface.h create mode 100644 mp/src/public/responserules/response_types.h create mode 100644 mp/src/public/responserules/rr_speechconcept.h create mode 100644 mp/src/public/tier1/interval.h create mode 100644 mp/src/public/tier1/mapbase_matchers_base.h create mode 100644 mp/src/responserules/runtime/criteriaset.cpp create mode 100644 mp/src/responserules/runtime/response_rules.vpc create mode 100644 mp/src/responserules/runtime/response_system.cpp create mode 100644 mp/src/responserules/runtime/response_system.h create mode 100644 mp/src/responserules/runtime/response_types.cpp create mode 100644 mp/src/responserules/runtime/response_types_internal.cpp create mode 100644 mp/src/responserules/runtime/response_types_internal.h create mode 100644 mp/src/responserules/runtime/rr_convars.cpp create mode 100644 mp/src/responserules/runtime/rr_response.cpp create mode 100644 mp/src/responserules/runtime/rr_speechconcept.cpp create mode 100644 mp/src/responserules/runtime/rrbase.h create mode 100644 mp/src/responserules/runtime/rrrlib.cpp create mode 100644 mp/src/responserules/runtime/stdafx.cpp create mode 100644 mp/src/tier1/interval.cpp diff --git a/mp/src/devtools/makefile_base_posix.mak b/mp/src/devtools/makefile_base_posix.mak index 9dc88cf9..ec50d73a 100644 --- a/mp/src/devtools/makefile_base_posix.mak +++ b/mp/src/devtools/makefile_base_posix.mak @@ -6,10 +6,11 @@ MAKEFILE_LINK:=$(THISFILE).link -include $(MAKEFILE_LINK) -$(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 ;\ +# depend on CXX so the correct makefile can be selected when the system is updated +$(MAKEFILE_LINK): $(shell which $(CXX)) $(THISFILE) $(SRCROOT)/devtools/gcc9+support.cpp + @ if [ "$(shell printf "$(shell $(CXX) -dumpversion)\n8" | sort -Vr | head -1)" = 8 ]; then \ ln -sf $(MAKEFILE_BASE).default $@ ;\ else \ + $(COMPILE.cpp) -m32 -o $(SRCROOT)/devtools/gcc9+support.o $(SRCROOT)/devtools/gcc9+support.cpp &&\ ln -sf $(MAKEFILE_BASE).gcc8 $@ ;\ fi diff --git a/mp/src/fgdlib/gamedata.cpp b/mp/src/fgdlib/gamedata.cpp index 4138ab4b..3d1d75e6 100644 --- a/mp/src/fgdlib/gamedata.cpp +++ b/mp/src/fgdlib/gamedata.cpp @@ -813,7 +813,11 @@ bool GameData::RemapNameField( const char *pszInValue, char *pszOutValue, TNameF { strcpy( pszOutValue, pszInValue ); +#ifdef MAPBASE + if ( pszInValue[ 0 ] && pszInValue[ 0 ] != '@' && pszInValue[ 0 ] != '!' ) +#else if ( pszInValue[ 0 ] && pszInValue[ 0 ] != '@' ) +#endif { // ! at the start of a value means it is global and should not be remaped switch( NameFixup ) { diff --git a/mp/src/game/client/C_Env_Projected_Texture.h b/mp/src/game/client/C_Env_Projected_Texture.h index 6fbb6f12..cb626814 100644 --- a/mp/src/game/client/C_Env_Projected_Texture.h +++ b/mp/src/game/client/C_Env_Projected_Texture.h @@ -92,6 +92,7 @@ private: float m_flLinearAtten; float m_flQuadraticAtten; float m_flShadowAtten; + float m_flShadowFilter; bool m_bAlwaysDraw; //bool m_bProjectedTextureVersion; diff --git a/mp/src/game/client/c_baseanimating.cpp b/mp/src/game/client/c_baseanimating.cpp index 4fb2e679..244399dd 100644 --- a/mp/src/game/client/c_baseanimating.cpp +++ b/mp/src/game/client/c_baseanimating.cpp @@ -264,6 +264,9 @@ LINK_ENTITY_TO_CLASS( client_ragdoll, C_ClientRagdoll ); BEGIN_DATADESC( C_ClientRagdoll ) DEFINE_FIELD( m_bFadeOut, FIELD_BOOLEAN ), DEFINE_FIELD( m_bImportant, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_FIELD( m_flForcedRetireTime, FIELD_FLOAT ), +#endif DEFINE_FIELD( m_iCurrentFriction, FIELD_INTEGER ), DEFINE_FIELD( m_iMinFriction, FIELD_INTEGER ), DEFINE_FIELD( m_iMaxFriction, FIELD_INTEGER ), @@ -286,6 +289,22 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( C_ClientRagdoll, C_BaseAnimating, "Client-side ragdolls" ) + + DEFINE_SCRIPTFUNC_NAMED( SUB_Remove, "FadeOut", "Fades out the ragdoll and removes it from the client." ) + + // TODO: Proper shared ragdoll funcs? + 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(); + +ScriptHook_t C_BaseAnimating::g_Hook_OnClientRagdoll; +ScriptHook_t C_BaseAnimating::g_Hook_FireEvent; +//ScriptHook_t C_BaseAnimating::g_Hook_BuildTransformations; +#endif + 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" ) @@ -293,6 +312,22 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si 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_NAMED( ScriptLookupAttachment, "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" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentMatrix, "GetAttachmentMatrix", "Get the attachement id's matrix transform" ) + + DEFINE_SCRIPTFUNC( LookupBone, "Get the named bone id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneTransform, "GetBoneTransform", "Get the transform for the specified bone" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetBoneTransform, "SetBoneTransform", "Set the transform for the specified bone" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAttachEntityToBone, "AttachEntityToBone", "Attaches this entity to the specified target and bone. Also allows for optional local position offset" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveBoneAttachment, "RemoveBoneAttachment", "Removes the specified bone attachment" ) + //DEFINE_SCRIPTFUNC( RemoveBoneAttachments, "Removes all bone attachments" ) + DEFINE_SCRIPTFUNC( DestroyBoneAttachments, "Destroys all bone attachments" ) + DEFINE_SCRIPTFUNC( GetNumBoneAttachments, "Gets the number of bone attachments" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneAttachment, "GetBoneAttachment", "Gets the specified bone attachment" ) + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") DEFINE_SCRIPTFUNC( GetBodygroup, "Gets a bodygroup" ) DEFINE_SCRIPTFUNC( GetBodygroupName, "Gets a bodygroup name" ) @@ -302,12 +337,43 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si DEFINE_SCRIPTFUNC( GetSequence, "Gets the current sequence" ) DEFINE_SCRIPTFUNC( SetSequence, "Sets the current sequence" ) - DEFINE_SCRIPTFUNC( SequenceLoops, "Loops the current sequence" ) + DEFINE_SCRIPTFUNC( SequenceLoops, "Does the current sequence loop?" ) 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( ScriptGetSequenceMoveDist, "GetSequenceMoveDist", "Gets the move distance of the specified sequence" ) 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( GetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( SetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( GetCycle, "" ) + DEFINE_SCRIPTFUNC( SetCycle, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSkin, "GetSkin", "Gets the model's skin" ) + DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) + + DEFINE_SCRIPTFUNC( GetForceBone, "Gets the entity's force bone, which is used to determine which bone a ragdoll should apply its force to." ) + DEFINE_SCRIPTFUNC( SetForceBone, "Sets the entity's force bone, which is used to determine which bone a ragdoll should apply its force to." ) + DEFINE_SCRIPTFUNC( GetRagdollForce, "Gets the entity's ragdoll force, which is used to apply velocity to a ragdoll." ) + DEFINE_SCRIPTFUNC( SetRagdollForce, "Sets the entity's ragdoll force, which is used to apply velocity to a ragdoll." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptBecomeRagdollOnClient, "BecomeRagdollOnClient", "" ) + DEFINE_SCRIPTFUNC( IsRagdoll, "" ) + + BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_OnClientRagdoll, "OnClientRagdoll", FIELD_VOID, "Called when this entity turns into a client-side ragdoll." ) + DEFINE_SCRIPTHOOK_PARAM( "ragdoll", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_FireEvent, "FireEvent", FIELD_BOOLEAN, "Called when handling animation events. Return false to cancel base handling." ) + DEFINE_SCRIPTHOOK_PARAM( "origin", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "angles", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "event", FIELD_INTEGER ) + DEFINE_SCRIPTHOOK_PARAM( "options", FIELD_CSTRING ) + END_SCRIPTHOOK() + + //BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_BuildTransformations, "BuildTransformations", FIELD_VOID, "Called when building bone transformations. Allows VScript to read/write any bone with Get/SetBoneTransform." ) + //END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -319,6 +385,9 @@ C_ClientRagdoll::C_ClientRagdoll( bool bRestoring ) m_bFadeOut = false; m_bFadingOut = false; m_bImportant = false; +#ifdef MAPBASE + m_flForcedRetireTime = 0.0f; +#endif m_bNoModelParticles = false; SetClassname("client_ragdoll"); @@ -399,7 +468,11 @@ void C_ClientRagdoll::OnRestore( void ) if ( m_bFadeOut == true ) { +#ifdef MAPBASE + s_RagdollLRU.MoveToTopOfLRU( this, m_bImportant, m_flForcedRetireTime ); +#else s_RagdollLRU.MoveToTopOfLRU( this, m_bImportant ); +#endif } NoteRagdollCreationTick( this ); @@ -674,6 +747,24 @@ void C_ClientRagdoll::Release( void ) BaseClass::Release(); } +#ifdef MAPBASE_VSCRIPT +HSCRIPT C_ClientRagdoll::ScriptGetRagdollObject( int iIndex ) +{ + if (iIndex < 0 || iIndex > m_pRagdoll->RagdollBoneCount()) + { + Warning("%s GetRagdollObject: Index %i not valid (%i objects)\n", GetDebugName(), iIndex, m_pRagdoll->RagdollBoneCount()); + return NULL; + } + + return g_pScriptVM->RegisterInstance( m_pRagdoll->GetElement(iIndex) ); +} + +int C_ClientRagdoll::ScriptGetRagdollObjectCount() +{ + return m_pRagdoll->RagdollBoneCount(); +} +#endif + //----------------------------------------------------------------------------- // Incremented each frame in InvalidateModelBones. Models compare this value to what it // was last time they setup their bones to determine if they need to re-setup their bones. @@ -711,6 +802,10 @@ C_BaseAnimating::C_BaseAnimating() : m_nPrevSequence = -1; m_nRestoreSequence = -1; m_pRagdoll = NULL; + m_pClientsideRagdoll = NULL; +#ifdef MAPBASE + m_pServerRagdoll = NULL; +#endif m_builtRagdoll = false; m_hitboxBoneCacheHandle = 0; int i; @@ -1461,6 +1556,94 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping ) } #ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: Returns the world location and world angles of an attachment to vscript caller +// Input : attachment name +// Output : location and angles +//----------------------------------------------------------------------------- +const Vector& C_BaseAnimating::ScriptGetAttachmentOrigin( int iAttachment ) +{ + + static Vector absOrigin; + static QAngle qa; + + C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + + return absOrigin; +} + +const Vector& C_BaseAnimating::ScriptGetAttachmentAngles( int iAttachment ) +{ + + static Vector absOrigin; + static Vector absAngles; + static QAngle qa; + + C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + absAngles.x = qa.x; + absAngles.y = qa.y; + absAngles.z = qa.z; + return absAngles; +} + +HSCRIPT C_BaseAnimating::ScriptGetAttachmentMatrix( int iAttachment ) +{ + static matrix3x4_t matrix; + + C_BaseAnimating::GetAttachment( iAttachment, matrix ); + return g_pScriptVM->RegisterInstance( &matrix ); +} + +void C_BaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ) +{ + matrix3x4_t *matTransform = HScriptToClass( hTransform ); + if (matTransform == NULL) + return; + + GetBoneTransform( iBone, *matTransform ); +} + +void C_BaseAnimating::ScriptSetBoneTransform( int iBone, HSCRIPT hTransform ) +{ + matrix3x4_t *matTransform = HScriptToClass( hTransform ); + if (matTransform == NULL) + return; + + MatrixCopy( *matTransform, GetBoneForWrite( iBone ) ); +} + +void C_BaseAnimating::ScriptAttachEntityToBone( HSCRIPT attachTarget, int boneIndexAttached, const Vector &bonePosition, const QAngle &boneAngles ) +{ + C_BaseEntity *pTarget = ToEnt( attachTarget ); + if (pTarget == NULL) + return; + + AttachEntityToBone( pTarget->GetBaseAnimating(), boneIndexAttached, bonePosition, boneAngles ); +} + +void C_BaseAnimating::ScriptRemoveBoneAttachment( HSCRIPT boneAttachment ) +{ + C_BaseEntity *pTarget = ToEnt( boneAttachment ); + if (pTarget == NULL) + return; + + RemoveBoneAttachment( pTarget->GetBaseAnimating() ); +} + +HSCRIPT C_BaseAnimating::ScriptGetBoneAttachment( int i ) +{ + return ToHScript( GetBoneAttachment( i ) ); +} + +HSCRIPT C_BaseAnimating::ScriptBecomeRagdollOnClient() +{ + C_BaseAnimating *pRagdoll = BecomeRagdollOnClient(); + if (!pRagdoll) + return NULL; + + return pRagdoll->GetScriptInstance(); +} + float C_BaseAnimating::ScriptGetPoseParameter( const char* szName ) { CStudioHdr* pHdr = GetModelPtr(); @@ -1638,7 +1821,23 @@ void C_BaseAnimating::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quater } } - +#ifdef MAPBASE_VSCRIPT + //if (m_ScriptScope.IsInitialized() && g_Hook_BuildTransformations.CanRunInScope(m_ScriptScope)) + //{ + // int oldWritableBones = m_BoneAccessor.GetWritableBones(); + // int oldReadableBones = m_BoneAccessor.GetReadableBones(); + // m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING ); + // m_BoneAccessor.SetReadableBones( BONE_USED_BY_ANYTHING ); + // + // // No parameters + // //ScriptVariant_t args[] = {}; + // //ScriptVariant_t returnValue; + // g_Hook_BuildTransformations.Call( m_ScriptScope, NULL, NULL /*&returnValue, args*/ ); + // + // m_BoneAccessor.SetWritableBones( oldWritableBones ); + // m_BoneAccessor.SetReadableBones( oldReadableBones ); + //} +#endif } //----------------------------------------------------------------------------- @@ -1844,6 +2043,10 @@ CollideType_t C_BaseAnimating::GetCollideType( void ) return BaseClass::GetCollideType(); } +#ifdef MAPBASE +ConVar ai_death_pose_enabled( "ai_death_pose_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, which cancels sequence transitions while a NPC is ragdolling." ); +#endif + //----------------------------------------------------------------------------- // Purpose: if the active sequence changes, keep track of the previous ones and decay them based on their decay rate //----------------------------------------------------------------------------- @@ -1860,6 +2063,14 @@ void C_BaseAnimating::MaintainSequenceTransitions( IBoneSetup &boneSetup, float return; } +#ifdef MAPBASE + if ( IsAboutToRagdoll() && ai_death_pose_enabled.GetBool() ) + { + m_nPrevNewSequenceParity = m_nNewSequenceParity; + return; + } +#endif + m_SequenceTransitioner.CheckForSequenceChange( boneSetup.GetStudioHdr(), GetSequence(), @@ -2659,14 +2870,29 @@ void C_BaseAnimating::CalculateIKLocks( float currentTime ) // debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 ); - float d = (pTarget->est.pos - origin).Length(); + Vector vecDelta = (origin - pTarget->est.pos); + float d = vecDelta.Length(); if ( d >= flDist) continue; flDist = d; - pTarget->SetPos( origin ); - pTarget->SetAngles( angles ); +#ifdef MAPBASE + // For blending purposes, IK attachments should obey weight + if ( pTarget->est.flWeight < 1.0f ) + { + Quaternion qTarget; + AngleQuaternion( angles, qTarget ); + + QuaternionSlerp( pTarget->est.q, qTarget, pTarget->est.flWeight, pTarget->est.q ); + pTarget->SetPos( pTarget->est.pos + (vecDelta * pTarget->est.flWeight) ); + } + else +#endif + { + pTarget->SetPos( origin ); + pTarget->SetAngles( angles ); + } // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 ); } @@ -3611,7 +3837,11 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) flEventCycle, gpGlobals->curtime ); } - + +#ifdef MAPBASE_VSCRIPT + if (ScriptHookFireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ) == false) + continue; +#endif FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); } @@ -3644,6 +3874,11 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) gpGlobals->curtime ); } +#ifdef MAPBASE_VSCRIPT + if (ScriptHookFireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ) == false) + continue; +#endif + FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); } } @@ -3651,6 +3886,26 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) m_flPrevEventCycle = flEventCycle; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseAnimating::ScriptHookFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) +{ + if (m_ScriptScope.IsInitialized() && g_Hook_FireEvent.CanRunInScope(m_ScriptScope)) + { + // origin, angles, event, options + ScriptVariant_t args[] = { origin, angles, event, options }; + ScriptVariant_t returnValue = true; + g_Hook_FireEvent.Call( m_ScriptScope, &returnValue, args ); + + return returnValue.m_bool; + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Parses a muzzle effect event and sends it out for drawing // Input : *options - event parameters in text format @@ -4716,12 +4971,18 @@ bool C_BaseAnimating::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matri return bSuccess; } +C_ClientRagdoll *C_BaseAnimating::CreateClientRagdoll( bool bRestoring ) +{ + //DevMsg( "Creating ragdoll at tick %d\n", gpGlobals->tickcount ); + return new C_ClientRagdoll( bRestoring ); +} + C_BaseAnimating *C_BaseAnimating::CreateRagdollCopy() { //Adrian: We now create a separate entity that becomes this entity's ragdoll. //That way the server side version of this entity can go away. //Plus we can hook save/restore code to these ragdolls so they don't fall on restore anymore. - C_ClientRagdoll *pRagdoll = new C_ClientRagdoll( false ); + C_ClientRagdoll *pRagdoll = CreateClientRagdoll( false ); if ( pRagdoll == NULL ) return NULL; @@ -4789,9 +5050,9 @@ C_BaseAnimating *C_BaseAnimating::BecomeRagdollOnClient() { MoveToLastReceivedPosition( true ); GetAbsOrigin(); + m_pClientsideRagdoll = CreateRagdollCopy(); - C_BaseAnimating *pRagdoll = CreateRagdollCopy(); - if ( pRagdoll ) + if ( m_pClientsideRagdoll ) { matrix3x4_t boneDelta0[MAXSTUDIOBONES]; matrix3x4_t boneDelta1[MAXSTUDIOBONES]; @@ -4803,19 +5064,29 @@ C_BaseAnimating *C_BaseAnimating::BecomeRagdollOnClient() if ( bInitBoneArrays ) { - bInitAsClient = pRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); + bInitAsClient = m_pClientsideRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); } if ( !bInitAsClient || !bInitBoneArrays ) { Warning( "C_BaseAnimating::BecomeRagdollOnClient failed. pRagdoll:%p bInitBoneArrays:%d bInitAsClient:%d\n", - pRagdoll, bInitBoneArrays, bInitAsClient ); - pRagdoll->Release(); + m_pClientsideRagdoll, bInitBoneArrays, bInitAsClient ); + m_pClientsideRagdoll->Release(); return NULL; } + +#ifdef MAPBASE_VSCRIPT + // Hook for ragdolling + if (m_ScriptScope.IsInitialized() && g_Hook_OnClientRagdoll.CanRunInScope( m_ScriptScope )) + { + // ragdoll + ScriptVariant_t args[] = { ScriptVariant_t( m_pClientsideRagdoll->GetScriptInstance() ) }; + g_Hook_OnClientRagdoll.Call( m_ScriptScope, NULL, args ); + } +#endif } - return pRagdoll; + return m_pClientsideRagdoll; } bool C_BaseAnimating::InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints ) @@ -5281,6 +5552,11 @@ void C_BaseAnimating::StudioFrameAdvance() if ( flNewCycle < 0.0f || flNewCycle >= 1.0f ) { + if (flNewCycle >= 1.0f) + { + ReachedEndOfSequence(); + } + if ( IsSequenceLooping( hdr, GetSequence() ) ) { flNewCycle -= (int)(flNewCycle); diff --git a/mp/src/game/client/c_baseanimating.h b/mp/src/game/client/c_baseanimating.h index af382aec..01e84efb 100644 --- a/mp/src/game/client/c_baseanimating.h +++ b/mp/src/game/client/c_baseanimating.h @@ -38,6 +38,7 @@ class C_BaseClientShader */ class IRagdoll; +class C_ClientRagdoll; class CIKContext; class CIKState; class ConVar; @@ -79,7 +80,7 @@ public: QAngle m_angRotation; Vector m_vOriginVelocity; int m_nLastFramecount : 31; - int m_bAnglesComputed : 1; + bool m_bAnglesComputed : 1; }; @@ -164,6 +165,10 @@ public: virtual void FireObsoleteEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); virtual const char* ModifyEventParticles( const char* token ) { return token; } +#ifdef MAPBASE_VSCRIPT + bool ScriptHookFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); +#endif + #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 @@ -297,6 +302,7 @@ public: bool IsRagdoll() const; bool IsAboutToRagdoll() const; virtual C_BaseAnimating *BecomeRagdollOnClient(); + virtual C_ClientRagdoll *CreateClientRagdoll( bool bRestoring = false ); C_BaseAnimating *CreateRagdollCopy(); bool InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints=false ); void IgniteRagdoll( C_BaseAnimating *pSource ); @@ -350,6 +356,8 @@ public: void ClientSideAnimationChanged(); virtual unsigned int ComputeClientSideAnimationFlags(); + virtual void ReachedEndOfSequence() { return; } + virtual void ResetClientsideFrame( void ) { SetCycle( 0 ); } void SetCycle( float flCycle ); @@ -456,6 +464,37 @@ public: virtual void UpdateOnRemove( void ); #ifdef MAPBASE_VSCRIPT + int ScriptLookupAttachment( const char *pAttachmentName ) { return LookupAttachment( pAttachmentName ); } + const Vector& ScriptGetAttachmentOrigin(int iAttachment); + const Vector& ScriptGetAttachmentAngles(int iAttachment); + HSCRIPT ScriptGetAttachmentMatrix(int iAttachment); + + void ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ); + void ScriptSetBoneTransform( int iBone, HSCRIPT hTransform ); + + void ScriptAttachEntityToBone( HSCRIPT attachTarget, int boneIndexAttached, const Vector &bonePosition, const QAngle &boneAngles ); + void ScriptRemoveBoneAttachment( HSCRIPT boneAttachment ); + HSCRIPT ScriptGetBoneAttachment( int i ); + + int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); } + float ScriptGetSequenceMoveDist( int iSequence ) { return GetSequenceMoveDist( GetModelPtr(), iSequence ); } + int ScriptSelectWeightedSequence( int activity ) { return SelectWeightedSequence( (Activity)activity ); } + + // For VScript + int ScriptGetSkin() { return GetSkin(); } + void SetSkin( int iSkin ) { m_nSkin = iSkin; } + + int GetForceBone() { return m_nForceBone; } + void SetForceBone( int iBone ) { m_nForceBone = iBone; } + const Vector& GetRagdollForce() { return m_vecForce; } + void SetRagdollForce( const Vector &vecForce ) { m_vecForce = vecForce; } + + HSCRIPT ScriptBecomeRagdollOnClient(); + + static ScriptHook_t g_Hook_OnClientRagdoll; + static ScriptHook_t g_Hook_FireEvent; + //static ScriptHook_t g_Hook_BuildTransformations; // UNDONE: Thread access issues + float ScriptGetPoseParameter(const char* szName); #endif void ScriptSetPoseParameter(const char* szName, float fValue); @@ -477,10 +516,6 @@ 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; } @@ -498,6 +533,10 @@ private: public: CRagdoll *m_pRagdoll; + C_BaseAnimating *m_pClientsideRagdoll; // From Alien Swarm SDK +#ifdef MAPBASE + C_BaseAnimating *m_pServerRagdoll; // Not from Alien Swarm SDK (note that this can exist without the entity having died) +#endif // Texture group to use int m_nSkin; @@ -669,6 +708,9 @@ public: C_ClientRagdoll( bool bRestoring = true ); DECLARE_CLASS( C_ClientRagdoll, C_BaseAnimating ); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif // inherited from IPVSNotify virtual void OnPVSStatusChanged( bool bInPVS ); @@ -690,8 +732,17 @@ public: void FadeOut( void ); virtual float LastBoneChangedTime(); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetRagdollObject( int iIndex ); + int ScriptGetRagdollObjectCount(); +#endif + bool m_bFadeOut; bool m_bImportant; +#ifdef MAPBASE + // Required to save/restore Alien Swarm SDK ragdoll LRU forced fade + float m_flForcedRetireTime; +#endif float m_flEffectTime; private: diff --git a/mp/src/game/client/c_basecombatcharacter.cpp b/mp/src/game/client/c_basecombatcharacter.cpp index 846901a5..5c560d28 100644 --- a/mp/src/game/client/c_basecombatcharacter.cpp +++ b/mp/src/game/client/c_basecombatcharacter.cpp @@ -195,3 +195,39 @@ BEGIN_PREDICTION_DATA( C_BaseCombatCharacter ) DEFINE_PRED_ARRAY( m_hMyWeapons, FIELD_EHANDLE, MAX_WEAPONS, FTYPEDESC_INSENDTABLE ), END_PREDICTION_DATA() + +#ifdef MAPBASE_VSCRIPT + +BEGIN_ENT_SCRIPTDESC( C_BaseCombatCharacter, CBaseEntity, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAmmoCount, "GetAmmoCount", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActiveWeapon, "GetActiveWeapon", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "" ) +END_SCRIPTDESC(); + + +int C_BaseCombatCharacter::ScriptGetAmmoCount( int i ) +{ + Assert( i == -1 || i < MAX_AMMO_SLOTS ); + + if ( i < 0 || i >= MAX_AMMO_SLOTS ) + return NULL; + + return GetAmmoCount( i ); +} + +HSCRIPT C_BaseCombatCharacter::ScriptGetActiveWeapon() +{ + return ToHScript( GetActiveWeapon() ); +} + +HSCRIPT C_BaseCombatCharacter::ScriptGetWeapon( int i ) +{ + Assert( i >= 0 && i < MAX_WEAPONS ); + + if ( i < 0 || i >= MAX_WEAPONS ) + return NULL; + + return ToHScript( GetWeapon(i) ); +} + +#endif diff --git a/mp/src/game/client/c_basecombatcharacter.h b/mp/src/game/client/c_basecombatcharacter.h index 0a135b05..2009f955 100644 --- a/mp/src/game/client/c_basecombatcharacter.h +++ b/mp/src/game/client/c_basecombatcharacter.h @@ -29,6 +29,9 @@ class C_BaseCombatCharacter : public C_BaseFlex public: DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif C_BaseCombatCharacter( void ); virtual ~C_BaseCombatCharacter( void ); @@ -103,6 +106,12 @@ public: bool IsClientSideGlowEnabled( void ){ return m_bClientSideGlowEnabled; } #endif // GLOWS_ENABLE +#ifdef MAPBASE_VSCRIPT + int ScriptGetAmmoCount( int i ); + HSCRIPT ScriptGetActiveWeapon(); + HSCRIPT ScriptGetWeapon( int i ); +#endif + public: float m_flNextAttack; diff --git a/mp/src/game/client/c_basecombatweapon.cpp b/mp/src/game/client/c_basecombatweapon.cpp index 03373db5..553050b3 100644 --- a/mp/src/game/client/c_basecombatweapon.cpp +++ b/mp/src/game/client/c_basecombatweapon.cpp @@ -125,9 +125,15 @@ void C_BaseCombatWeapon::OnRestore() int C_BaseCombatWeapon::GetWorldModelIndex( void ) { +#ifdef MAPBASE + int iIndex = GetOwner() ? m_iWorldModelIndex.Get() : m_iDroppedModelIndex.Get(); +#else + int iIndex = m_iWorldModelIndex.Get(); +#endif + if ( GameRules() ) { - const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( m_iWorldModelIndex ) ); + const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( iIndex ) ); const char *pTranslatedName = GameRules()->TranslateEffectForVisionFilter( "weapons", pBaseName ); if ( pTranslatedName != pBaseName ) @@ -136,7 +142,7 @@ int C_BaseCombatWeapon::GetWorldModelIndex( void ) } } - return m_iWorldModelIndex; + return iIndex; } //----------------------------------------------------------------------------- @@ -505,25 +511,44 @@ int C_BaseCombatWeapon::DrawModel( int flags ) // check if local player chases owner of this weapon in first person C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); - if ( localplayer && localplayer->IsObserver() && GetOwner() ) + if ( localplayer ) { #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 ); + if ( (!localplayer->InFirstPersonView() || (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA)) && (viewID != VIEW_SHADOW_DEPTH_TEXTURE || !localplayer->IsEffectActive(EF_DIMLIGHT)) ) + { + // TODO: Is this inefficient? + int nModelIndex = GetModelIndex(); + int nWorldModelIndex = GetWorldModelIndex(); + if (nModelIndex != nWorldModelIndex) + { + SetModelIndex(nWorldModelIndex); + } + + int iDraw = BaseClass::DrawModel(flags); + + if (nModelIndex != nWorldModelIndex) + { + SetModelIndex(nModelIndex); + } + + return iDraw; + } } #endif - - // don't draw weapon if chasing this guy as spectator - // we don't check that in ShouldDraw() since this may change - // without notification + if ( localplayer->IsObserver() && GetOwner() ) + { + // don't draw weapon if chasing this guy as spectator + // we don't check that in ShouldDraw() since this may change + // without notification - if ( localplayer->GetObserverMode() == OBS_MODE_IN_EYE && - localplayer->GetObserverTarget() == GetOwner() ) - return false; + if ( localplayer->GetObserverMode() == OBS_MODE_IN_EYE && + localplayer->GetObserverTarget() == GetOwner() ) + return false; + } } return BaseClass::DrawModel( flags ); diff --git a/mp/src/game/client/c_baseentity.cpp b/mp/src/game/client/c_baseentity.cpp index 02516130..e6dbaabb 100644 --- a/mp/src/game/client/c_baseentity.cpp +++ b/mp/src/game/client/c_baseentity.cpp @@ -428,7 +428,13 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst ) RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ), END_RECV_TABLE() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t C_BaseEntity::g_Hook_UpdateOnRemove; +ScriptHook_t C_BaseEntity::g_Hook_ModifyEmitSoundParams; +#endif + BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) #ifdef MAPBASE_VSCRIPT @@ -442,6 +448,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities #ifdef MAPBASE_VSCRIPT DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetOrCreatePrivateScriptScope, "Create and retrieve the script-side data associated with an entity" ) DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) DEFINE_SCRIPTFUNC( GetHealth, "" ) @@ -449,6 +456,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopSound, "StopSound", "Stops a sound from this entity." ) 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." ) @@ -456,23 +464,106 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC( GetClassname, "" ) DEFINE_SCRIPTFUNC_NAMED( GetEntityName, "GetName", "" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsOrigin, "SetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) + + DEFINE_SCRIPTFUNC( GetLocalOrigin, "GetLocalOrigin" ) + DEFINE_SCRIPTFUNC( SetLocalOrigin, "SetLocalOrigin" ) + DEFINE_SCRIPTFUNC( GetLocalAngles, "GetLocalAngles" ) + DEFINE_SCRIPTFUNC( SetLocalAngles, "SetLocalAngles" ) + 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( ScriptEyeAngles, "EyeAngles", "Get eye pitch, yaw, roll as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsAngles, "SetAngles", "Set entity pitch, yaw, roll" ) + DEFINE_SCRIPTFUNC( 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( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + + DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetParent, "SetParent", "" ) 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( 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( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + + 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( ScriptGetRenderMode, "GetRenderMode", "Get render mode" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetRenderMode, "SetRenderMode", "Set render mode" ) + 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_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( 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( SetModel, "Set client-only entity model" ) + //DEFINE_SCRIPTFUNC_NAMED( ScriptInitializeAsClientEntity, "InitializeAsClientEntity", "" ) + DEFINE_SCRIPTFUNC_NAMED( Remove, "Destroy", "Remove clientside entity" ) DEFINE_SCRIPTFUNC_NAMED( GetEntityIndex, "entindex", "" ) -#endif + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetContextThink, "SetContextThink", "Set a think function on this entity." ) + + + DEFINE_SIMPLE_SCRIPTHOOK( C_BaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." ) + + BEGIN_SCRIPTHOOK( C_BaseEntity::g_Hook_ModifyEmitSoundParams, "ModifyEmitSoundParams", FIELD_VOID, "Called every time a sound is emitted on this entity, allowing for its parameters to be modified." ) + DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + +#endif // MAPBASE_VSCRIPT + END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION @@ -507,6 +598,7 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt(RECVINFO(m_clrRender)), #ifdef MAPBASE RecvPropInt(RECVINFO(m_iViewHideFlags)), + RecvPropBool(RECVINFO(m_bDisableFlashlight)), #endif RecvPropInt(RECVINFO(m_iTeamNum)), RecvPropInt(RECVINFO(m_CollisionGroup)), @@ -1272,8 +1364,23 @@ void C_BaseEntity::Term() if ( m_hScriptInstance ) { +#ifdef MAPBASE_VSCRIPT + if ( m_ScriptScope.IsInitialized() && g_Hook_UpdateOnRemove.CanRunInScope( m_ScriptScope ) ) + { + g_Hook_UpdateOnRemove.Call( m_ScriptScope, NULL, NULL ); + } +#endif g_pScriptVM->RemoveInstance( m_hScriptInstance ); m_hScriptInstance = NULL; + +#ifdef MAPBASE_VSCRIPT + FOR_EACH_VEC( m_ScriptThinkFuncs, i ) + { + HSCRIPT h = m_ScriptThinkFuncs[i]->m_hfnThink; + if ( h ) g_pScriptVM->ReleaseScript( h ); + } + m_ScriptThinkFuncs.PurgeAndDeleteElements(); +#endif } } @@ -1611,6 +1718,11 @@ bool C_BaseEntity::ShouldReceiveProjectedTextures( int flags ) if ( IsEffectActive( EF_NODRAW ) ) return false; +#ifdef MAPBASE + if ( m_bDisableFlashlight ) + return false; +#endif + if( flags & SHADOW_FLAGS_FLASHLIGHT ) { if ( GetRenderMode() > kRenderNormal && GetRenderColor().a == 0 ) @@ -4829,9 +4941,15 @@ C_BaseEntity *C_BaseEntity::Instance( int iEnt ) } #ifdef WIN32 + +#if _MSC_VER < 1900 #pragma warning( push ) #include #pragma warning( pop ) +#else +#include +#endif + #endif //----------------------------------------------------------------------------- @@ -6069,6 +6187,9 @@ BEGIN_DATADESC_NO_BASE( C_BaseEntity ) DEFINE_FIELD( m_angAbsRotation, FIELD_VECTOR ), DEFINE_ARRAY( m_rgflCoordinateFrame, FIELD_FLOAT, 12 ), // NOTE: MUST BE IN LOCAL SPACE, NOT POSITION_VECTOR!!! (see CBaseEntity::Restore) DEFINE_FIELD( m_fFlags, FIELD_INTEGER ), +#ifdef MAPBASE_VSCRIPT + DEFINE_FIELD( m_iszScriptId, FIELD_STRING ), +#endif END_DATADESC() //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/c_baseentity.h b/mp/src/game/client/c_baseentity.h index 18331915..8b259818 100644 --- a/mp/src/game/client/c_baseentity.h +++ b/mp/src/game/client/c_baseentity.h @@ -162,6 +162,15 @@ struct thinkfunc_t int m_nLastThinkTick; }; +#ifdef MAPBASE_VSCRIPT +struct scriptthinkfunc_t +{ + float m_flNextThink; + HSCRIPT m_hfnThink; + unsigned m_iContextHash; +}; +#endif + #define CREATE_PREDICTED_ENTITY( className ) \ C_BaseEntity::CreatePredictedEntityByName( className, __FILE__, __LINE__ ); @@ -267,6 +276,7 @@ public: bool ValidateScriptScope(); bool CallScriptFunction( const char* pFunctionName, ScriptVariant_t* pFunctionReturn ); + HSCRIPT GetOrCreatePrivateScriptScope(); HSCRIPT GetScriptScope() { return m_ScriptScope; } HSCRIPT LookupScriptFunction(const char* pFunctionName); @@ -276,12 +286,18 @@ public: bool RunScript( const char* pScriptText, const char* pDebugFilename = "C_BaseEntity::RunScript" ); #endif + HSCRIPT GetScriptOwnerEntity(); + virtual void SetScriptOwnerEntity(HSCRIPT pOwner); + HSCRIPT GetScriptInstance(); HSCRIPT m_hScriptInstance; string_t m_iszScriptId; #ifdef MAPBASE_VSCRIPT CScriptScope m_ScriptScope; + + static ScriptHook_t g_Hook_UpdateOnRemove; + static ScriptHook_t g_Hook_ModifyEmitSoundParams; #endif // IClientUnknown overrides. @@ -388,7 +404,7 @@ public: #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(); } + inline int GetEntityIndex() const { return entindex(); } #endif // This works for client-only entities and returns the GetEntryIndex() of the entity's handle, @@ -1153,6 +1169,11 @@ public: bool IsFollowingEntity(); CBaseEntity *GetFollowedEntity(); +#ifdef MAPBASE_VSCRIPT + void ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ); + HSCRIPT ScriptGetFollowedEntity(); +#endif + // For shadows rendering the correct body + sequence... virtual int GetBody() { return 0; } virtual int GetSkin() { return 0; } @@ -1168,21 +1189,46 @@ public: #ifdef MAPBASE_VSCRIPT const char* ScriptGetModelName( void ) const { return STRING(GetModelName()); } + void ScriptStopSound(const char* soundname); 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 QAngle& ScriptEyeAngles(void) { static QAngle ang; ang = EyeAngles(); return ang; } + void ScriptSetForward( const Vector& v ) { QAngle angles; VectorAngles( v, angles ); SetAbsAngles( angles ); } const Vector& ScriptGetBoundingMins( void ) { return m_Collision.OBBMins(); } const Vector& ScriptGetBoundingMaxs( void ) { return m_Collision.OBBMaxs(); } + HSCRIPT ScriptEntityToWorldTransform( void ); + + HSCRIPT ScriptGetPhysicsObject( void ); + + void ScriptSetParent( HSCRIPT hParent, const char *szAttachment ); HSCRIPT ScriptGetMoveParent( void ); HSCRIPT ScriptGetRootMoveParent(); HSCRIPT ScriptFirstMoveChild( void ); HSCRIPT ScriptNextMovePeer( void ); + + 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 ); } #endif // Stubs on client @@ -1222,7 +1268,7 @@ public: #ifdef _DEBUG void FunctionCheck( void *pFunction, const char *name ); - ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) + ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, const char *name ) { //COMPILE_TIME_ASSERT( sizeof(func) == 4 ); m_pfnTouch = func; @@ -1359,6 +1405,7 @@ public: #ifdef MAPBASE int m_iViewHideFlags; + bool m_bDisableFlashlight; #endif private: @@ -1500,6 +1547,15 @@ protected: CUtlVector< thinkfunc_t > m_aThinkFunctions; int m_iCurrentThinkContext; +#ifdef MAPBASE_VSCRIPT +public: + void ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float time ); + void ScriptContextThink(); +private: + CUtlVector< scriptthinkfunc_t* > m_ScriptThinkFuncs; +public: +#endif + // Object eye position Vector m_vecViewOffset; diff --git a/mp/src/game/client/c_baselesson.cpp b/mp/src/game/client/c_baselesson.cpp index 8a2d9617..ec01575b 100644 --- a/mp/src/game/client/c_baselesson.cpp +++ b/mp/src/game/client/c_baselesson.cpp @@ -15,7 +15,7 @@ #include "ammodef.h" #include "vprof.h" #include "view.h" -#include "vstdlib/ikeyvaluessystem.h" +#include "vstdlib/IKeyValuesSystem.h" #ifdef MAPBASE #include "usermessages.h" #endif @@ -666,7 +666,8 @@ void CIconLesson::UpdateInactive() 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() ) ); + bf_read msg( msg_data.Base(), msg_data.TellPut() ); + usermessages->DispatchUserMessage( usermessages->LookupUserMessage( "KeyHintText" ), msg ); } #endif @@ -1039,40 +1040,40 @@ Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget ) #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## ); +#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##, +#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##(); +#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##() \ + void Init_##_varEnum() \ { \ - iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ varType = LessonParamTypeFromString( #_varType ); \ } #define LESSON_VARIABLE_INIT_INFO_BOOL( _varEnum, _varName, _varType ) \ - void Init_##_varEnum##() \ + void Init_##_varEnum() \ { \ - iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ varType = FIELD_BOOLEAN; \ } #define LESSON_VARIABLE_INIT_INFO_EHANDLE( _varEnum, _varName, _varType ) \ - void Init_##_varEnum##() \ + void Init_##_varEnum() \ { \ - iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ varType = FIELD_EHANDLE; \ } #define LESSON_VARIABLE_INIT_INFO_STRING( _varEnum, _varName, _varType ) \ - void Init_##_varEnum##() \ + void Init_##_varEnum() \ { \ - iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ varType = FIELD_STRING; \ } @@ -1094,15 +1095,15 @@ Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget ) // Process the element action on this variable #define PROCESS_LESSON_ACTION( _varEnum, _varName, _varType ) \ - case LESSON_VARIABLE_##_varEnum##:\ + 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##:\ + 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##:\ + 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) @@ -2957,7 +2958,7 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch { if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) { - ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName ); ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); @@ -2969,7 +2970,7 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) { - ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName ); ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", pVar->HealthFraction() ); ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); diff --git a/mp/src/game/client/c_baselesson.h b/mp/src/game/client/c_baselesson.h index 2cad41ca..b413d282 100644 --- a/mp/src/game/client/c_baselesson.h +++ b/mp/src/game/client/c_baselesson.h @@ -426,7 +426,7 @@ private: LessonEvent_t * AddUpdateEvent( void ); private: - static CUtlDict< int, int > CScriptedIconLesson::LessonActionMap; + static CUtlDict< int, int > LessonActionMap; EHANDLE m_hLocalPlayer; float m_fOutput; diff --git a/mp/src/game/client/c_baseplayer.cpp b/mp/src/game/client/c_baseplayer.cpp index 284da6cc..6f25cced 100644 --- a/mp/src/game/client/c_baseplayer.cpp +++ b/mp/src/game/client/c_baseplayer.cpp @@ -284,6 +284,7 @@ END_RECV_TABLE() RecvPropInt ( RECVINFO( m_spawnflags ), 0, RecvProxy_ShiftPlayerSpawnflags ), RecvPropBool ( RECVINFO( m_bDrawPlayerModelExternally ) ), + RecvPropBool ( RECVINFO( m_bInTriggerFall ) ), #endif END_RECV_TABLE() @@ -334,7 +335,10 @@ END_RECV_TABLE() RecvPropString( RECVINFO(m_szLastPlaceName) ), - RecvPropEHandle(RECVINFO(m_hPostProcessCtrl)), // Send to everybody - for spectating +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropEHandle( RECVINFO( m_hPostProcessCtrl ) ), // Send to everybody - for spectating + RecvPropEHandle( RECVINFO( m_hColorCorrectionCtrl ) ), // Send to everybody - for spectating +#endif #if defined USES_ECON_ITEMS RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ), @@ -496,14 +500,14 @@ C_BasePlayer::~C_BasePlayer() if ( this == s_pLocalPlayer ) { s_pLocalPlayer = NULL; - } #ifdef MAPBASE_VSCRIPT - if ( IsLocalPlayer() && g_pScriptVM ) - { - g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); - } + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } #endif + } delete m_pFlashlight; } @@ -861,6 +865,14 @@ void C_BasePlayer::PostDataUpdate( DataUpdateType_t updateType ) // changed level, which would cause the snd_soundmixer to be left modified. ConVar *pVar = (ConVar *)cvar->FindVar( "snd_soundmixer" ); pVar->Revert(); + +#ifdef MAPBASE_VSCRIPT + // Moved here from LevelInitPostEntity, which is executed before local player is spawned. + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } +#endif } } @@ -1373,6 +1385,10 @@ void C_BasePlayer::AddEntity( void ) // Add in lighting effects CreateLightEffects(); + +#ifdef MAPBASE + SetLocalAnglesDim( X_INDEX, 0 ); +#endif } extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); @@ -1495,8 +1511,9 @@ int C_BasePlayer::DrawModel( int flags ) if (m_bDrawPlayerModelExternally) { // Draw the player in any view except the main or "intro" view, both of which are default first-person views. + // HACKHACK: Also don't draw in shadow depth textures if the player's flashlight is on, as that causes the playermodel to block it. view_id_t viewID = CurrentViewID(); - if (viewID == VIEW_MAIN || viewID == VIEW_INTRO_CAMERA) + if (viewID == VIEW_MAIN || viewID == VIEW_INTRO_CAMERA || (viewID == VIEW_SHADOW_DEPTH_TEXTURE && IsEffectActive(EF_DIMLIGHT))) { // Make sure the player model wouldn't draw anyway... if (!ShouldDrawThisPlayer()) @@ -1519,6 +1536,38 @@ int C_BasePlayer::DrawModel( int flags ) return BaseClass::DrawModel( flags ); } +#ifdef MAPBASE +ConVar cl_player_allow_thirdperson_projtex( "cl_player_allow_thirdperson_projtex", "1", FCVAR_NONE, "Allows players to receive projected textures if they're non-local or in third person." ); +ConVar cl_player_allow_thirdperson_rttshadows( "cl_player_allow_thirdperson_rttshadows", "0", FCVAR_NONE, "Allows players to cast RTT shadows if they're non-local or in third person." ); +ConVar cl_player_allow_firstperson_projtex( "cl_player_allow_firstperson_projtex", "1", FCVAR_NONE, "Allows players to receive projected textures even if they're in first person." ); +ConVar cl_player_allow_firstperson_rttshadows( "cl_player_allow_firstperson_rttshadows", "0", FCVAR_NONE, "Allows players to cast RTT shadows even if they're in first person." ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ShadowType_t C_BasePlayer::ShadowCastType() +{ + if ( (!IsLocalPlayer() || ShouldDraw()) ? !cl_player_allow_thirdperson_rttshadows.GetBool() : !cl_player_allow_firstperson_rttshadows.GetBool() ) + return SHADOWS_NONE; + + if ( !IsVisible() ) + return SHADOWS_NONE; + + return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC; +} + +//----------------------------------------------------------------------------- +// Should this object receive shadows? +//----------------------------------------------------------------------------- +bool C_BasePlayer::ShouldReceiveProjectedTextures( int flags ) +{ + if ( (!IsLocalPlayer() || ShouldDraw()) ? !cl_player_allow_thirdperson_projtex.GetBool() : !cl_player_allow_firstperson_projtex.GetBool() ) + return false; + + return BaseClass::ShouldReceiveProjectedTextures( flags ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2934,6 +2983,7 @@ void C_BasePlayer::UpdateFogBlend( void ) } } +#ifdef MAPBASE // From Alien Swarm SDK //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -2942,6 +2992,15 @@ C_PostProcessController* C_BasePlayer::GetActivePostProcessController() const return m_hPostProcessCtrl.Get(); } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +C_ColorCorrection* C_BasePlayer::GetActiveColorCorrection() const +{ + return m_hColorCorrectionCtrl.Get(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/c_baseplayer.h b/mp/src/game/client/c_baseplayer.h index 528c8408..2a29b65a 100644 --- a/mp/src/game/client/c_baseplayer.h +++ b/mp/src/game/client/c_baseplayer.h @@ -23,7 +23,10 @@ #include "hintsystem.h" #include "SoundEmitterSystem/isoundemittersystembase.h" #include "c_env_fog_controller.h" +#ifdef MAPBASE // From Alien Swarm SDK #include "c_postprocesscontroller.h" +#include "c_colorcorrection.h" +#endif #include "igameevents.h" #include "GameEventListener.h" @@ -204,6 +207,11 @@ public: void SetMaxSpeed( float flMaxSpeed ) { m_flMaxspeed = flMaxSpeed; } float MaxSpeed() const { return m_flMaxspeed; } +#ifdef MAPBASE + // See c_baseplayer.cpp + virtual ShadowType_t ShadowCastType(); + virtual bool ShouldReceiveProjectedTextures( int flags ); +#else // Should this object cast shadows? virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; } @@ -211,6 +219,7 @@ public: { return false; } +#endif bool IsLocalPlayer( void ) const; @@ -381,7 +390,10 @@ public: void UpdateFogController( void ); void UpdateFogBlend( void ); +#ifdef MAPBASE // From Alien Swarm SDK C_PostProcessController* GetActivePostProcessController() const; + C_ColorCorrection* GetActiveColorCorrection() const; +#endif float GetFOVTime( void ){ return m_flFOVTime; } @@ -458,6 +470,8 @@ public: // Allows the player's model to draw on non-main views, like monitors or mirrors. bool m_bDrawPlayerModelExternally; + + bool m_bInTriggerFall; #endif protected: @@ -646,7 +660,10 @@ private: // One for left and one for right side of step StepSoundCache_t m_StepSoundCache[ 2 ]; - CNetworkHandle(C_PostProcessController, m_hPostProcessCtrl); // active postprocessing controller +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkHandle( C_PostProcessController, m_hPostProcessCtrl ); // active postprocessing controller + CNetworkHandle( C_ColorCorrection, m_hColorCorrectionCtrl ); // active FXVolume color correction +#endif public: diff --git a/mp/src/game/client/c_baseviewmodel.cpp b/mp/src/game/client/c_baseviewmodel.cpp index 28c804fb..e0d81a06 100644 --- a/mp/src/game/client/c_baseviewmodel.cpp +++ b/mp/src/game/client/c_baseviewmodel.cpp @@ -35,7 +35,7 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#ifdef CSTRIKE_DLL +#if defined(CSTRIKE_DLL) || defined (MAPBASE) ConVar cl_righthand( "cl_righthand", "1", FCVAR_ARCHIVE, "Use right-handed view models." ); #endif @@ -197,7 +197,7 @@ bool C_BaseViewModel::Interpolate( float currentTime ) bool C_BaseViewModel::ShouldFlipViewModel() { -#ifdef CSTRIKE_DLL +#if defined(CSTRIKE_DLL) || defined (MAPBASE) // If cl_righthand is set, then we want them all right-handed. CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); if ( pWeapon ) diff --git a/mp/src/game/client/c_colorcorrection.cpp b/mp/src/game/client/c_colorcorrection.cpp index 6960031d..12e29768 100644 --- a/mp/src/game/client/c_colorcorrection.cpp +++ b/mp/src/game/client/c_colorcorrection.cpp @@ -6,6 +6,7 @@ //===========================================================================// #include "cbase.h" +#include "c_colorcorrection.h" #include "filesystem.h" #include "cdll_client_int.h" #include "colorcorrectionmgr.h" @@ -17,45 +18,27 @@ static ConVar mat_colcorrection_disableentities( "mat_colcorrection_disableentities", "0", FCVAR_NONE, "Disable map color-correction entities" ); - -//------------------------------------------------------------------------------ -// Purpose : Color correction entity with radial falloff -//------------------------------------------------------------------------------ -class C_ColorCorrection : public C_BaseEntity -{ -public: - DECLARE_CLASS( C_ColorCorrection, C_BaseEntity ); - - DECLARE_CLIENTCLASS(); - - C_ColorCorrection(); - virtual ~C_ColorCorrection(); - - void OnDataChanged(DataUpdateType_t updateType); - bool ShouldDraw(); - - void ClientThink(); - -private: - Vector m_vecOrigin; - - float m_minFalloff; - float m_maxFalloff; - float m_flCurWeight; - char m_netLookupFilename[MAX_PATH]; - - bool m_bEnabled; - - ClientCCHandle_t m_CCHandle; -}; +#ifdef MAPBASE // From Alien Swarm SDK +static ConVar mat_colcorrection_forceentitiesclientside( "mat_colcorrection_forceentitiesclientside", "0", FCVAR_CHEAT, "Forces color correction entities to be updated on the client" ); +#endif IMPLEMENT_CLIENTCLASS_DT(C_ColorCorrection, DT_ColorCorrection, CColorCorrection) RecvPropVector( RECVINFO(m_vecOrigin) ), RecvPropFloat( RECVINFO(m_minFalloff) ), RecvPropFloat( RECVINFO(m_maxFalloff) ), RecvPropFloat( RECVINFO(m_flCurWeight) ), +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropFloat( RECVINFO(m_flMaxWeight) ), + RecvPropFloat( RECVINFO(m_flFadeInDuration) ), + RecvPropFloat( RECVINFO(m_flFadeOutDuration) ), +#endif RecvPropString( RECVINFO(m_netLookupFilename) ), RecvPropBool( RECVINFO(m_bEnabled) ), +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropBool( RECVINFO(m_bMaster) ), + RecvPropBool( RECVINFO(m_bClientSide) ), + RecvPropBool( RECVINFO(m_bExclusive) ) +#endif END_RECV_TABLE() @@ -65,14 +48,43 @@ END_RECV_TABLE() //------------------------------------------------------------------------------ C_ColorCorrection::C_ColorCorrection() { +#ifdef MAPBASE // From Alien Swarm SDK + m_minFalloff = -1.0f; + m_maxFalloff = -1.0f; + m_flFadeInDuration = 0.0f; + m_flFadeOutDuration = 0.0f; + m_flCurWeight = 0.0f; + m_flMaxWeight = 1.0f; + m_netLookupFilename[0] = '\0'; + m_bEnabled = false; + m_bMaster = false; + m_bExclusive = false; +#endif m_CCHandle = INVALID_CLIENT_CCHANDLE; + +#ifdef MAPBASE // From Alien Swarm SDK + m_bFadingIn = false; + m_flFadeStartWeight = 0.0f; + m_flFadeStartTime = 0.0f; + m_flFadeDuration = 0.0f; +#endif } C_ColorCorrection::~C_ColorCorrection() { +#ifdef MAPBASE // From Alien Swarm SDK + g_pColorCorrectionMgr->RemoveColorCorrectionEntity( this, m_CCHandle ); +#else g_pColorCorrectionMgr->RemoveColorCorrection( m_CCHandle ); +#endif } +#ifdef MAPBASE // From Alien Swarm SDK +bool C_ColorCorrection::IsClientSide() const +{ + return m_bClientSide || mat_colcorrection_forceentitiesclientside.GetBool(); +} +#endif //------------------------------------------------------------------------------ // Purpose : @@ -87,11 +99,21 @@ void C_ColorCorrection::OnDataChanged(DataUpdateType_t updateType) { if ( m_CCHandle == INVALID_CLIENT_CCHANDLE ) { +#ifdef MAPBASE // From Alien Swarm SDK + // forming a unique name without extension + char cleanName[MAX_PATH]; + V_StripExtension( m_netLookupFilename, cleanName, sizeof( cleanName ) ); + char name[MAX_PATH]; + Q_snprintf( name, MAX_PATH, "%s_%d", cleanName, entindex() ); + + m_CCHandle = g_pColorCorrectionMgr->AddColorCorrectionEntity( this, name, m_netLookupFilename ); +#else char filename[MAX_PATH]; Q_strncpy( filename, m_netLookupFilename, MAX_PATH ); m_CCHandle = g_pColorCorrectionMgr->AddColorCorrection( filename ); SetNextClientThink( ( m_CCHandle != INVALID_CLIENT_CCHANDLE ) ? CLIENT_THINK_ALWAYS : CLIENT_THINK_NEVER ); +#endif } } } @@ -104,6 +126,129 @@ bool C_ColorCorrection::ShouldDraw() return false; } +#ifdef MAPBASE // From Alien Swarm SDK +void C_ColorCorrection::Update( C_BasePlayer *pPlayer, float ccScale ) +{ + Assert( m_CCHandle != INVALID_CLIENT_CCHANDLE ); + + if ( mat_colcorrection_disableentities.GetInt() ) + { + // Allow the colorcorrectionui panel (or user) to turn off color-correction entities + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f, m_bExclusive ); + return; + } + + // fade weight on client + if ( IsClientSide() ) + { + m_flCurWeight = Lerp( GetFadeRatio(), m_flFadeStartWeight, m_bFadingIn ? m_flMaxWeight : 0.0f ); + } + + if( !m_bEnabled && m_flCurWeight == 0.0f ) + { + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f, m_bExclusive ); + return; + } + + Vector playerOrigin = pPlayer->GetAbsOrigin(); + + float weight = 0; + if ( ( m_minFalloff != -1 ) && ( m_maxFalloff != -1 ) && m_minFalloff != m_maxFalloff ) + { + float dist = (playerOrigin - m_vecOrigin).Length(); + weight = (dist-m_minFalloff) / (m_maxFalloff-m_minFalloff); + if ( weight<0.0f ) weight = 0.0f; + if ( weight>1.0f ) weight = 1.0f; + } + + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_flCurWeight * ( 1.0 - weight ) * ccScale, m_bExclusive ); +} + +void C_ColorCorrection::EnableOnClient( bool bEnable, bool bSkipFade ) +{ + if ( !IsClientSide() ) + { + return; + } + + m_bFadingIn = bEnable; + + // initialize countdown timer + m_flFadeStartWeight = m_flCurWeight; + float flFadeTimeScale = 1.0f; + if ( m_flMaxWeight != 0.0f ) + { + flFadeTimeScale = m_flCurWeight / m_flMaxWeight; + } + + if ( m_bFadingIn ) + { + flFadeTimeScale = 1.0f - flFadeTimeScale; + } + + if ( bSkipFade ) + { + flFadeTimeScale = 0.0f; + } + + StartFade( flFadeTimeScale * ( m_bFadingIn ? m_flFadeInDuration : m_flFadeOutDuration ) ); + + // update the clientside weight once here, in case the fade duration is 0 + m_flCurWeight = Lerp( GetFadeRatio(), m_flFadeStartWeight, m_bFadingIn ? m_flMaxWeight : 0.0f ); +} + +Vector C_ColorCorrection::GetOrigin() +{ + return m_vecOrigin; +} + +float C_ColorCorrection::GetMinFalloff() +{ + return m_minFalloff; +} + +float C_ColorCorrection::GetMaxFalloff() +{ + return m_maxFalloff; +} + +void C_ColorCorrection::SetWeight( float fWeight ) +{ + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, fWeight, false ); +} + +void C_ColorCorrection::StartFade( float flDuration ) +{ + m_flFadeStartTime = gpGlobals->curtime; + m_flFadeDuration = MAX( flDuration, 0.0f ); +} + +float C_ColorCorrection::GetFadeRatio() const +{ + float flRatio = 1.0f; + + if ( m_flFadeDuration != 0.0f ) + { + flRatio = ( gpGlobals->curtime - m_flFadeStartTime ) / m_flFadeDuration; + flRatio = clamp( flRatio, 0.0f, 1.0f ); + } + return flRatio; +} + +bool C_ColorCorrection::IsFadeTimeElapsed() const +{ + return ( ( gpGlobals->curtime - m_flFadeStartTime ) > m_flFadeDuration ) || + ( ( gpGlobals->curtime - m_flFadeStartTime ) < 0.0f ); +} + +void UpdateColorCorrectionEntities( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrection **pList, int listCount ) +{ + for ( int i = 0; i < listCount; i++ ) + { + pList[i]->Update(pPlayer, ccScale); + } +} +#else void C_ColorCorrection::ClientThink() { if ( m_CCHandle == INVALID_CLIENT_CCHANDLE ) @@ -141,6 +286,7 @@ void C_ColorCorrection::ClientThink() BaseClass::ClientThink(); } +#endif diff --git a/mp/src/game/client/c_colorcorrection.h b/mp/src/game/client/c_colorcorrection.h new file mode 100644 index 00000000..63149a0a --- /dev/null +++ b/mp/src/game/client/c_colorcorrection.h @@ -0,0 +1,88 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013. +// Although technically a new Mapbase file, it only serves to move otherwise identical code, +// so most code and repo conventions will pretend it was always there. +// +// -------------------------------------------------------------------- +// +// Purpose: Color correction entity with simple radial falloff +// +//=============================================================================// + +#ifndef C_COLORCORRECTION_H +#define C_COLORCORRECTION_H +#ifdef _WIN32 +#pragma once +#endif + +#include "colorcorrectionmgr.h" + +//------------------------------------------------------------------------------ +// Purpose : Color correction entity with radial falloff +//------------------------------------------------------------------------------ +class C_ColorCorrection : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_ColorCorrection, C_BaseEntity ); + + DECLARE_CLIENTCLASS(); + + C_ColorCorrection(); + virtual ~C_ColorCorrection(); + + void OnDataChanged(DataUpdateType_t updateType); + bool ShouldDraw(); + +#ifdef MAPBASE // From Alien Swarm SDK + virtual void Update(C_BasePlayer *pPlayer, float ccScale); + + bool IsMaster() const { return m_bMaster; } + bool IsClientSide() const; + bool IsExclusive() const { return m_bExclusive; } + + void EnableOnClient( bool bEnable, bool bSkipFade = false ); + + Vector GetOrigin(); + float GetMinFalloff(); + float GetMaxFalloff(); + + void SetWeight( float fWeight ); + +protected: + void StartFade( float flDuration ); + float GetFadeRatio() const; + bool IsFadeTimeElapsed() const; +#else + void ClientThink(); + +private: +#endif + Vector m_vecOrigin; + + float m_minFalloff; + float m_maxFalloff; + float m_flCurWeight; + char m_netLookupFilename[MAX_PATH]; + + bool m_bEnabled; + +#ifdef MAPBASE // From Alien Swarm SDK + float m_flFadeInDuration; + float m_flFadeOutDuration; + float m_flMaxWeight; + + bool m_bMaster; + bool m_bClientSide; + bool m_bExclusive; + + bool m_bFadingIn; + float m_flFadeStartWeight; + float m_flFadeStartTime; + float m_flFadeDuration; +#endif + + ClientCCHandle_t m_CCHandle; +}; + +#endif diff --git a/mp/src/game/client/c_colorcorrectionvolume.cpp b/mp/src/game/client/c_colorcorrectionvolume.cpp index 4bbcea94..f7e33708 100644 --- a/mp/src/game/client/c_colorcorrectionvolume.cpp +++ b/mp/src/game/client/c_colorcorrectionvolume.cpp @@ -36,9 +36,26 @@ public: void OnDataChanged(DataUpdateType_t updateType); bool ShouldDraw(); +#ifdef MAPBASE // From Alien Swarm SDK + void Update( C_BasePlayer *pPlayer, float ccScale ); + + void StartTouch( C_BaseEntity *pOther ); + void EndTouch( C_BaseEntity *pOther ); +#else void ClientThink(); +#endif private: +#ifdef MAPBASE // From Alien Swarm SDK + float m_LastEnterWeight; + float m_LastEnterTime; + + float m_LastExitWeight; + float m_LastExitTime; + bool m_bEnabled; + float m_MaxWeight; + float m_FadeDuration; +#endif float m_Weight; char m_lookupFilename[MAX_PATH]; @@ -46,6 +63,11 @@ private: }; IMPLEMENT_CLIENTCLASS_DT(C_ColorCorrectionVolume, DT_ColorCorrectionVolume, CColorCorrectionVolume) +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropBool( RECVINFO( m_bEnabled ) ), + RecvPropFloat( RECVINFO( m_MaxWeight ) ), + RecvPropFloat( RECVINFO( m_FadeDuration ) ), +#endif RecvPropFloat( RECVINFO(m_Weight) ), RecvPropString( RECVINFO(m_lookupFilename) ), END_RECV_TABLE() @@ -82,11 +104,21 @@ void C_ColorCorrectionVolume::OnDataChanged(DataUpdateType_t updateType) { if ( m_CCHandle == INVALID_CLIENT_CCHANDLE ) { +#ifdef MAPBASE // From Alien Swarm SDK + // forming a unique name without extension + char cleanName[MAX_PATH]; + V_StripExtension( m_lookupFilename, cleanName, sizeof( cleanName ) ); + char name[MAX_PATH]; + Q_snprintf( name, MAX_PATH, "%s_%d", cleanName, entindex() ); + + m_CCHandle = g_pColorCorrectionMgr->AddColorCorrectionVolume( this, name, m_lookupFilename ); +#else char filename[MAX_PATH]; Q_strncpy( filename, m_lookupFilename, MAX_PATH ); m_CCHandle = g_pColorCorrectionMgr->AddColorCorrection( filename ); SetNextClientThink( ( m_CCHandle != INVALID_CLIENT_CCHANDLE ) ? CLIENT_THINK_ALWAYS : CLIENT_THINK_NEVER ); +#endif } } } @@ -99,11 +131,95 @@ bool C_ColorCorrectionVolume::ShouldDraw() return false; } +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +void C_ColorCorrectionVolume::StartTouch( CBaseEntity *pEntity ) +{ + m_LastEnterTime = gpGlobals->curtime; + m_LastEnterWeight = m_Weight; +} + + +//-------------------------------------------------------------------------------------------------------- +void C_ColorCorrectionVolume::EndTouch( CBaseEntity *pEntity ) +{ + m_LastExitTime = gpGlobals->curtime; + m_LastExitWeight = m_Weight; +} + + +void C_ColorCorrectionVolume::Update( C_BasePlayer *pPlayer, float ccScale ) +{ + if ( pPlayer ) + { + bool isTouching = CollisionProp()->IsPointInBounds( pPlayer->EyePosition() ); + bool wasTouching = m_LastEnterTime > m_LastExitTime; + + if ( isTouching && !wasTouching ) + { + StartTouch( pPlayer ); + } + else if ( !isTouching && wasTouching ) + { + EndTouch( pPlayer ); + } + } + + if( !m_bEnabled ) + { + m_Weight = 0.0f; + } + else + { + if( m_LastEnterTime > m_LastExitTime ) + { + // we most recently entered the volume + + if( m_Weight < 1.0f ) + { + float dt = gpGlobals->curtime - m_LastEnterTime; + float weight = m_LastEnterWeight + dt / ((1.0f-m_LastEnterWeight)*m_FadeDuration); + if( weight>1.0f ) + weight = 1.0f; + + m_Weight = weight; + } + } + else + { + // we most recently exitted the volume + + if( m_Weight > 0.0f ) + { + float dt = gpGlobals->curtime - m_LastExitTime; + float weight = (1.0f-m_LastExitWeight) + dt / (m_LastExitWeight*m_FadeDuration); + if( weight>1.0f ) + weight = 1.0f; + + m_Weight = 1.0f - weight; + } + } + } + + // Vector entityPosition = GetAbsOrigin(); + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_Weight * ccScale ); +} + + +void UpdateColorCorrectionVolumes( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrectionVolume **pList, int listCount ) +{ + for ( int i = 0; i < listCount; i++ ) + { + pList[i]->Update(pPlayer, ccScale); + } +} +#else void C_ColorCorrectionVolume::ClientThink() { Vector entityPosition = GetAbsOrigin(); g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_Weight ); } +#endif diff --git a/mp/src/game/client/c_effects.cpp b/mp/src/game/client/c_effects.cpp index afd5bcf9..a273cb28 100644 --- a/mp/src/game/client/c_effects.cpp +++ b/mp/src/game/client/c_effects.cpp @@ -40,7 +40,10 @@ ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // 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_RainParticleClampOffset_Rain( "r_RainParticleClampOffset_Rain", "120", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain' type." ); +ConVar r_RainParticleClampOffset_Ash( "r_RainParticleClampOffset_Ash", "300", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Ash' type." ); +ConVar r_RainParticleClampOffset_RainStorm( "r_RainParticleClampOffset_RainStorm", "112", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain Storm' type." ); +ConVar r_RainParticleClampOffset_Snow( "r_RainParticleClampOffset_Snow", "300", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Snow' type." ); ConVar r_RainParticleClampDebug( "r_RainParticleClampDebug", "0", FCVAR_NONE, "Enables debug code for precipitation particle system clamping" ); #endif @@ -951,6 +954,70 @@ void CClient_Precipitation::CreateParticlePrecip( void ) UpdateParticlePrecip( pPlayer ); } +#ifdef MAPBASE +void CClient_Precipitation::ClampParticlePosition( Vector &vPlayerPos, Vector &vOffsetPos, Vector &vOffsetPosNear, Vector &vOffsetPosFar ) +{ + 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(); + float flMax = 0; + switch (m_nPrecipType) + { + case PRECIPITATION_TYPE_PARTICLERAIN: + flMax = r_RainParticleClampOffset_Rain.GetFloat(); + break; + + case PRECIPITATION_TYPE_PARTICLEASH: + flMax = r_RainParticleClampOffset_Ash.GetFloat(); + break; + + case PRECIPITATION_TYPE_PARTICLERAINSTORM: + flMax = r_RainParticleClampOffset_RainStorm.GetFloat(); + break; + + case PRECIPITATION_TYPE_PARTICLESNOW: + flMax = r_RainParticleClampOffset_Snow.GetFloat(); + break; + } + + 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 + void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer ) { if ( !pPlayer ) @@ -980,44 +1047,7 @@ void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer ) #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 ); + ClampParticlePosition( vPlayerPos, vOffsetPos, vOffsetPosNear, vOffsetPosFar ); } #endif @@ -1236,6 +1266,13 @@ void CClient_Precipitation::DispatchInnerParticlePrecip( C_BasePlayer *pPlayer, 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_CLAMP) + { + ClampParticlePosition( vPlayerPos, vOffsetPos, vOffsetPosNear, vOffsetPosFar ); + } +#endif + #ifdef MAPBASE if (!(m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER)) #endif diff --git a/mp/src/game/client/c_effects.h b/mp/src/game/client/c_effects.h index 6012dff7..5f7e02a5 100644 --- a/mp/src/game/client/c_effects.h +++ b/mp/src/game/client/c_effects.h @@ -130,6 +130,10 @@ private: void CreateAshParticle( void ); void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); +#ifdef MAPBASE + void ClampParticlePosition( Vector &vPlayerPos, Vector &vOffsetPos, Vector &vOffsetPosNear, Vector &vOffsetPosFar ); +#endif + // Information helpful in creating and rendering particles IMaterial *m_MatHandle; // material used diff --git a/mp/src/game/client/c_env_global_light.cpp b/mp/src/game/client/c_env_global_light.cpp index c48803ae..b143a79d 100644 --- a/mp/src/game/client/c_env_global_light.cpp +++ b/mp/src/game/client/c_env_global_light.cpp @@ -25,6 +25,11 @@ ConVar cl_globallight_freeze( "cl_globallight_freeze", "0" ); // You can set these as KV anyway. ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "0" ); ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "0" ); + +static ConVar cl_globallight_slopescaledepthbias_shadowmap( "cl_globallight_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); +static ConVar cl_globallight_shadowfiltersize( "cl_globallight_shadowfiltersize", "0.1", FCVAR_CHEAT ); +static ConVar cl_globallight_depthbias_shadowmap( "cl_globallight_depthbias_shadowmap", "0.00001", FCVAR_CHEAT ); +static ConVar cl_globallight_depthres( "cl_globallight_depthres", "8192", FCVAR_CHEAT ); #else ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "-800" ); ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "1600" ); @@ -286,16 +291,21 @@ void C_GlobalLight::ClientThink() state.m_bOrtho = false; } -#ifndef MAPBASE // Don't draw that huge debug thing +#ifdef MAPBASE + //state.m_bDrawShadowFrustum = true; // Don't draw that huge debug thing + state.m_flShadowMapResolution = cl_globallight_depthres.GetFloat(); + state.m_flShadowFilterSize = cl_globallight_shadowfiltersize.GetFloat(); + state.m_flShadowSlopeScaleDepthBias = cl_globallight_slopescaledepthbias_shadowmap.GetFloat(); + state.m_flShadowDepthBias = cl_globallight_depthbias_shadowmap.GetFloat(); + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = m_SpotlightTexture; + state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; +#else 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 diff --git a/mp/src/game/client/c_env_projectedtexture.cpp b/mp/src/game/client/c_env_projectedtexture.cpp index c78cddb2..66449652 100644 --- a/mp/src/game/client/c_env_projectedtexture.cpp +++ b/mp/src/game/client/c_env_projectedtexture.cpp @@ -25,8 +25,8 @@ #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 ); +extern ConVarRef mat_slopescaledepthbias_shadowmap; +extern ConVarRef mat_depthbias_shadowmap; float C_EnvProjectedTexture::m_flVisibleBBoxMinHeight = -FLT_MAX; @@ -60,6 +60,7 @@ IMPLEMENT_CLIENTCLASS_DT( C_EnvProjectedTexture, DT_EnvProjectedTexture, CEnvPro RecvPropFloat( RECVINFO( m_flLinearAtten ) ), RecvPropFloat( RECVINFO( m_flQuadraticAtten ) ), RecvPropFloat( RECVINFO( m_flShadowAtten ) ), + RecvPropFloat( RECVINFO( m_flShadowFilter ) ), RecvPropBool( RECVINFO( m_bAlwaysDraw ) ), // Not needed on the client right now, change when it actually is needed @@ -97,6 +98,7 @@ C_EnvProjectedTexture *C_EnvProjectedTexture::Create( ) pEnt->m_flLinearAtten = 100.0f; pEnt->m_flQuadraticAtten = 0.0f; pEnt->m_flShadowAtten = 0.0f; + pEnt->m_flShadowFilter = 0.5f; //pEnt->m_bProjectedTextureVersion = 1; #endif @@ -283,6 +285,8 @@ void C_EnvProjectedTexture::UpdateLight( void ) // VectorNormalize( vRight ); // VectorNormalize( vUp ); + + VectorVectors( vForward, vRight, vUp ); } } else @@ -401,6 +405,7 @@ void C_EnvProjectedTexture::UpdateLight( void ) state.m_flShadowSlopeScaleDepthBias = mat_slopescaledepthbias_shadowmap.GetFloat(); state.m_flShadowDepthBias = mat_depthbias_shadowmap.GetFloat(); state.m_flShadowAtten = m_flShadowAtten; + state.m_flShadowFilterSize = m_flShadowFilter; #else state.m_fQuadraticAtten = 0.0; state.m_fLinearAtten = 100; diff --git a/mp/src/game/client/c_env_screenoverlay.cpp b/mp/src/game/client/c_env_screenoverlay.cpp index 5496d453..a3125882 100644 --- a/mp/src/game/client/c_env_screenoverlay.cpp +++ b/mp/src/game/client/c_env_screenoverlay.cpp @@ -48,6 +48,9 @@ protected: int m_iCachedDesiredOverlay; int m_iCurrentOverlay; float m_flCurrentOverlayTime; +#ifdef MAPBASE + int m_iOverlayIndex; +#endif }; IMPLEMENT_CLIENTCLASS_DT( C_EnvScreenOverlay, DT_EnvScreenOverlay, CEnvScreenOverlay ) @@ -56,6 +59,9 @@ IMPLEMENT_CLIENTCLASS_DT( C_EnvScreenOverlay, DT_EnvScreenOverlay, CEnvScreenOve RecvPropFloat( RECVINFO( m_flStartTime ) ), RecvPropInt( RECVINFO( m_iDesiredOverlay ) ), RecvPropBool( RECVINFO( m_bIsActive ) ), +#ifdef MAPBASE + RecvPropInt( RECVINFO( m_iOverlayIndex ) ), +#endif END_RECV_TABLE() //----------------------------------------------------------------------------- @@ -77,7 +83,11 @@ void C_EnvScreenOverlay::PostDataUpdate( DataUpdateType_t updateType ) BaseClass::PostDataUpdate( updateType ); // If we have a start time now, start the overlays going +#ifdef MAPBASE + if ( m_bIsActive && m_flStartTime > 0 && (view->GetScreenOverlayMaterial() == NULL || (m_iOverlayIndex != -1 && view->GetIndexedScreenOverlayMaterial(m_iOverlayIndex) == NULL)) ) +#else if ( m_bIsActive && m_flStartTime > 0 && view->GetScreenOverlayMaterial() == NULL ) +#endif { StartOverlays(); } @@ -111,7 +121,16 @@ void C_EnvScreenOverlay::StopOverlays( void ) if ( m_bWasActive && !m_bIsActive ) { - view->SetScreenOverlayMaterial( NULL ); +#ifdef MAPBASE + if (m_iOverlayIndex != -1) + { + view->SetIndexedScreenOverlayMaterial( m_iOverlayIndex, NULL ); + } + else +#endif + { + view->SetScreenOverlayMaterial( NULL ); + } } } @@ -163,7 +182,16 @@ void C_EnvScreenOverlay::StartCurrentOverlay( void ) IMaterial *pMaterial = materials->FindMaterial( m_iszOverlayNames[m_iCurrentOverlay], TEXTURE_GROUP_CLIENT_EFFECTS, false ); if ( !IsErrorMaterial( pMaterial ) ) { - view->SetScreenOverlayMaterial( pMaterial ); +#ifdef MAPBASE + if (m_iOverlayIndex != -1) + { + view->SetIndexedScreenOverlayMaterial( m_iOverlayIndex, pMaterial ); + } + else +#endif + { + view->SetScreenOverlayMaterial( pMaterial ); + } } else { diff --git a/mp/src/game/client/c_gameinstructor.h b/mp/src/game/client/c_gameinstructor.h index 00c97c66..14ae908c 100644 --- a/mp/src/game/client/c_gameinstructor.h +++ b/mp/src/game/client/c_gameinstructor.h @@ -9,7 +9,7 @@ #include "GameEventListener.h" -#include "vgui_controls/phandle.h" +#include "vgui_controls/PHandle.h" class CBaseLesson; diff --git a/mp/src/game/client/c_movie_display.cpp b/mp/src/game/client/c_movie_display.cpp new file mode 100644 index 00000000..27327403 --- /dev/null +++ b/mp/src/game/client/c_movie_display.cpp @@ -0,0 +1,27 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// +//=====================================================================================// +#include "cbase.h" +#include "c_movie_display.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT( C_MovieDisplay, DT_MovieDisplay, CMovieDisplay ) + RecvPropBool( RECVINFO( m_bEnabled ) ), + RecvPropBool( RECVINFO( m_bLooping ) ), + RecvPropBool( RECVINFO( m_bMuted ) ), + RecvPropString( RECVINFO( m_szMovieFilename ) ), + RecvPropString( RECVINFO( m_szGroupName ) ), +END_RECV_TABLE() + +C_MovieDisplay::C_MovieDisplay() +{ +} + +C_MovieDisplay::~C_MovieDisplay() +{ +} diff --git a/mp/src/game/client/c_movie_display.h b/mp/src/game/client/c_movie_display.h new file mode 100644 index 00000000..55d0211f --- /dev/null +++ b/mp/src/game/client/c_movie_display.h @@ -0,0 +1,36 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#ifndef C_MOVIE_DISPLAY_H +#define C_MOVIE_DISPLAY_H + +#include "cbase.h" + +class C_MovieDisplay : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_MovieDisplay, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_MovieDisplay(); + ~C_MovieDisplay(); + + bool IsEnabled( void ) const { return m_bEnabled; } + bool IsLooping( void ) const { return m_bLooping; } + bool IsMuted(void) const { return m_bMuted; } + + const char *GetMovieFilename( void ) const { return m_szMovieFilename; } + const char *GetGroupName( void ) const { return m_szGroupName; } + +private: + bool m_bEnabled; + bool m_bLooping; + bool m_bMuted; + char m_szMovieFilename[128]; + char m_szGroupName[128]; +}; + +#endif //C_MOVIE_DISPLAY_H \ No newline at end of file diff --git a/mp/src/game/client/c_particle_system.cpp b/mp/src/game/client/c_particle_system.cpp index 2ac9ef61..74ac224f 100644 --- a/mp/src/game/client/c_particle_system.cpp +++ b/mp/src/game/client/c_particle_system.cpp @@ -42,6 +42,7 @@ protected: EHANDLE m_hControlPointEnts[kMAXCONTROLPOINTS]; + Vector m_vControlPointVecs[kMAXCONTROLPOINTS]; // SendPropArray3( SENDINFO_ARRAY3(m_iControlPointParents), SendPropInt( SENDINFO_ARRAY(m_iControlPointParents), 3, SPROP_UNSIGNED ) ), unsigned char m_iControlPointParents[kMAXCONTROLPOINTS]; @@ -65,6 +66,7 @@ BEGIN_RECV_TABLE_NOBASE( C_ParticleSystem, DT_ParticleSystem ) RecvPropFloat( RECVINFO( m_flStartTime ) ), RecvPropArray3( RECVINFO_ARRAY(m_hControlPointEnts), RecvPropEHandle( RECVINFO( m_hControlPointEnts[0] ) ) ), + RecvPropArray3( RECVINFO_ARRAY(m_vControlPointVecs), RecvPropVector( RECVINFO( m_vControlPointVecs[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iControlPointParents), RecvPropInt( RECVINFO(m_iControlPointParents[0]))), RecvPropBool( RECVINFO( m_bWeatherEffect ) ), END_RECV_TABLE(); @@ -150,21 +152,41 @@ void C_ParticleSystem::ClientThink( void ) AssertMsg1( pEffect, "Particle system couldn't make %s", pszName ); if (pEffect) { - for ( int i = 0 ; i < kMAXCONTROLPOINTS ; ++i ) + if (m_vControlPointVecs[0] != GetAbsOrigin() && m_hControlPointEnts[0] == NULL) { - CBaseEntity *pOnEntity = m_hControlPointEnts[i].Get(); - if ( pOnEntity ) + // we are using info_particle_system_coordinate + for (int i = 0; i < kMAXCONTROLPOINTS; ++i) { - ParticleProp()->AddControlPoint( pEffect, i + 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW ); + ParticleProp()->AddControlPoint(pEffect, i + 1, this, PATTACH_WORLDORIGIN, 0, m_vControlPointVecs[i] - GetAbsOrigin()); + + AssertMsg2(m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS, + "Particle system specified bogus control point parent (%d) for point %d.", + m_iControlPointParents[i], i); + + if (m_iControlPointParents[i] != 0) + { + pEffect->SetControlPointParent(i + 1, m_iControlPointParents[i]); + } } - - AssertMsg2( m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS , - "Particle system specified bogus control point parent (%d) for point %d.", - m_iControlPointParents[i], i ); - - if (m_iControlPointParents[i] != 0) + } + else + { + for ( int i = 0 ; i < kMAXCONTROLPOINTS ; ++i ) { - pEffect->SetControlPointParent(i+1, m_iControlPointParents[i]); + CBaseEntity *pOnEntity = m_hControlPointEnts[i].Get(); + if ( pOnEntity ) + { + ParticleProp()->AddControlPoint( pEffect, i + 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW ); + } + + AssertMsg2( m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS , + "Particle system specified bogus control point parent (%d) for point %d.", + m_iControlPointParents[i], i ); + + if (m_iControlPointParents[i] != 0) + { + pEffect->SetControlPointParent(i+1, m_iControlPointParents[i]); + } } } diff --git a/mp/src/game/client/c_point_commentary_node.cpp b/mp/src/game/client/c_point_commentary_node.cpp index 47ea96ef..d65896b5 100644 --- a/mp/src/game/client/c_point_commentary_node.cpp +++ b/mp/src/game/client/c_point_commentary_node.cpp @@ -18,6 +18,16 @@ #include "convar.h" #include "hud_closecaption.h" #include "in_buttons.h" +#ifdef MAPBASE +#include "vgui_controls/Label.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/AnimationController.h" +#include "filesystem.h" +#include "scenefilecache/ISceneFileCache.h" +#include "choreoscene.h" +#include "c_sceneentity.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -37,6 +47,17 @@ bool IsInCommentaryMode( void ) static bool g_bTracingVsCommentaryNodes = false; +#ifdef MAPBASE +ConVar commentary_type_force( "commentary_type_force", "-1", FCVAR_NONE, "Forces all commentary nodes to use the specified type." ); +ConVar commentary_type_text_endtime( "commentary_type_text_endtime", "120" ); +ConVar commentary_type_image_endtime( "commentary_type_image_endtime", "120" ); +ConVar commentary_audio_element_below_cc( "commentary_audio_element_below_cc", "1", FCVAR_NONE, "Allows commentary audio elements to display even when CC is enabled (although this is done by inverting their Y axis)" ); +ConVar commentary_audio_element_below_cc_margin( "commentary_audio_element_below_cc_margin", "4" ); +ConVar commentary_combine_speaker_and_printname( "commentary_combine_speaker_and_printname", "1" ); +ConVar commentary_footnote_offset_x( "commentary_footnote_offset_x", "16" ); +ConVar commentary_footnote_offset_y( "commentary_footnote_offset_y", "8" ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -52,12 +73,28 @@ public: virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); void StartCommentary( C_PointCommentaryNode *pNode, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); +#ifdef MAPBASE + void StartTextCommentary( C_PointCommentaryNode *pNode, const char *pszText, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); + void StartImageCommentary( C_PointCommentaryNode *pNode, const char *pszImage, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); + void StartSceneCommentary( C_PointCommentaryNode *pNode, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); +#endif void StopCommentary( void ); bool IsTheActiveNode( C_PointCommentaryNode *pNode ) { return (pNode == m_hActiveNode); } +#ifdef MAPBASE + void FixupCommentaryLabels( const char *pszPrintName, const char *pszSpeakers, const char *pszFootnote ); + void RepositionAndFollowCloseCaption( int yOffset = 0 ); +#endif + // vgui overrides virtual void Paint( void ); virtual bool ShouldDraw( void ); +#ifdef MAPBASE + virtual void PerformLayout(); + void ResolveBounds( int width, int height ); + + virtual void LevelShutdown(); +#endif private: CHandle m_hActiveNode; @@ -68,6 +105,25 @@ private: wchar_t m_szCount[MAX_COUNT_STRING]; CMaterialReference m_matIcon; bool m_bHiding; +#ifdef MAPBASE + int m_iCommentaryType; + float m_flPanelScale; + float m_flOverrideX; + float m_flOverrideY; + + vgui::Label *m_pLabel; + vgui::ImagePanel *m_pImage; + vgui::HFont m_hFont; + + vgui::Label *m_pFootnoteLabel; + vgui::HFont m_hSmallFont; + + // HACKHACK: Needed as a failsafe to prevent desync + int m_iCCDefaultY; + float m_flCCAnimTime; + + bool m_bShouldRepositionSubtitles; +#endif // Painting CPanelAnimationVarAliasType( int, m_iBarX, "bar_xpos", "8", "proportional_int" ); @@ -84,24 +140,65 @@ private: CPanelAnimationVarAliasType( int, m_iIconTall, "icon_height", "8", "proportional_int" ); CPanelAnimationVarAliasType( int, m_nIconTextureId, "icon_texture", "vgui/hud/icon_commentary", "textureid" ); +#ifdef MAPBASE + CPanelAnimationVarAliasType( int, m_iTypeAudioX, "type_audio_xpos", "190", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeAudioY, "type_audio_ypos", "350", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeAudioW, "type_audio_wide", "380", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeAudioT, "type_audio_tall", "40", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextX, "type_text_xpos", "180", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextY, "type_text_ypos", "150", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextW, "type_text_wide", "400", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextT, "type_text_tall", "200", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextCountXFR, "type_text_count_xpos_from_right", "10", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextCountYFB, "type_text_count_ypos_from_bottom", "10", "proportional_int" ); + CPanelAnimationVar( Color, m_TextBackgroundColor, "BackgroundColorTextContent", "0 0 0 224" ); + CPanelAnimationVar( Color, m_TypeTextContentColor, "TextContentColor", "255 230 180 255" ); + CPanelAnimationVar( int, m_iTextBorderSpace, "type_text_border_space", "8" ); +#endif + CPanelAnimationVar( bool, m_bUseScriptBGColor, "use_script_bgcolor", "0" ); +#ifdef MAPBASE + CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "Panel.BgColor" ); + CPanelAnimationVar( Color, m_ForegroundColor, "ForegroundColor", "255 170 0 255" ); +#else CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "0 0 0 0" ); +#endif CPanelAnimationVar( Color, m_BGOverrideColor, "BackgroundOverrideColor", "Panel.BgColor" ); }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -class C_PointCommentaryNode : public C_BaseAnimating +class C_PointCommentaryNode : public C_BaseAnimating, public IChoreoEventCallback { DECLARE_CLASS( C_PointCommentaryNode, C_BaseAnimating ); public: DECLARE_CLIENTCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif virtual void OnPreDataChanged( DataUpdateType_t type ); virtual void OnDataChanged( DataUpdateType_t type ); + void StartAudioCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); +#ifdef MAPBASE + void StartTextCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); + void StartImageCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); + void StartSceneCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); + + // From IChoreoEventCallback + virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ); +#else + virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) {} +#endif + virtual void EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) {} + virtual void ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) {} + virtual bool CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) { return true; } + + void ClientThink(); + void OnRestore( void ) { BaseClass::OnRestore(); @@ -149,6 +246,11 @@ public: { int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "commentary" ); gHUD.LockRenderGroup( iRenderGroup ); + +#ifdef MAPBASE + // Special commentary localization file (useful for things like text nodes or print names) + g_pVGuiLocalize->AddFile( "resource/commentary_%language%.txt", "MOD", true ); +#endif } if ( g_CommentaryNodes.Find(this) == g_CommentaryNodes.InvalidIndex() ) @@ -168,6 +270,22 @@ public: } } +#ifdef MAPBASE_VSCRIPT // VScript funcs + bool IsActive() { return m_bActive; } + + int GetCommentaryType() { return m_iCommentaryType; } + void SetCommentaryType( int iType ) { m_iCommentaryType = iType; } + + const char *GetCommentaryFile() { return m_iszCommentaryFile; } + void SetCommentaryFile( const char *pszNewFile ) { Q_strncpy( m_iszCommentaryFile, pszNewFile, sizeof( m_iszCommentaryFile ) ); } + const char *GetSpeakers() { return m_iszSpeakers; } + void SetSpeakers( const char *pszSpeakers ) { Q_strncpy( m_iszSpeakers, pszSpeakers, sizeof( m_iszSpeakers ) ); } + const char *GetPrintName() { return m_iszPrintName; } + void SetPrintName( const char *pszPrintName ) { Q_strncpy( m_iszPrintName, pszPrintName, sizeof( m_iszPrintName ) ); } + const char *GetFootnote() { return m_iszFootnote; } + void SetFootnote( const char *pszFootnote ) { Q_strncpy( m_iszFootnote, pszFootnote, sizeof( m_iszFootnote ) ); } +#endif + public: // Data received from the server bool m_bActive; @@ -181,6 +299,22 @@ public: CSoundPatch *m_sndCommentary; EHANDLE m_hViewPosition; bool m_bRestartAfterRestore; +#ifdef MAPBASE + char m_iszPrintName[MAX_SPEAKER_NAME]; + char m_iszFootnote[MAX_SPEAKER_NAME]; + int m_iCommentaryType; + float m_flPanelScale; + float m_flPanelX; + float m_flPanelY; + + CChoreoScene *m_pScene; + //CHandle m_hScene; + EHANDLE m_hSceneOrigin; +#endif + +#ifdef MAPBASE_VSCRIPT + static ScriptHook_t g_Hook_PreStartCommentaryClient; +#endif }; IMPLEMENT_CLIENTCLASS_DT(C_PointCommentaryNode, DT_PointCommentaryNode, CPointCommentaryNode) @@ -192,6 +326,14 @@ IMPLEMENT_CLIENTCLASS_DT(C_PointCommentaryNode, DT_PointCommentaryNode, CPointCo RecvPropInt( RECVINFO( m_iNodeNumber ) ), RecvPropInt( RECVINFO( m_iNodeNumberMax ) ), RecvPropEHandle( RECVINFO(m_hViewPosition) ), +#ifdef MAPBASE + RecvPropString( RECVINFO( m_iszPrintName ) ), + RecvPropString( RECVINFO( m_iszFootnote ) ), + RecvPropInt( RECVINFO( m_iCommentaryType ) ), + RecvPropFloat( RECVINFO( m_flPanelScale ) ), + RecvPropFloat( RECVINFO( m_flPanelX ) ), + RecvPropFloat( RECVINFO( m_flPanelY ) ), +#endif END_RECV_TABLE() BEGIN_DATADESC( C_PointCommentaryNode ) @@ -200,6 +342,28 @@ BEGIN_DATADESC( C_PointCommentaryNode ) DEFINE_SOUNDPATCH( m_sndCommentary ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t C_PointCommentaryNode::g_Hook_PreStartCommentaryClient; + +BEGIN_ENT_SCRIPTDESC( C_PointCommentaryNode, C_BaseAnimating, "Commentary nodes which play commentary in commentary mode." ) + + DEFINE_SCRIPTFUNC( IsActive, "" ) + DEFINE_SCRIPTFUNC( GetCommentaryFile, "" ) + DEFINE_SCRIPTFUNC( SetCommentaryFile, "" ) + DEFINE_SCRIPTFUNC( GetSpeakers, "" ) + DEFINE_SCRIPTFUNC( SetSpeakers, "" ) + DEFINE_SCRIPTFUNC( GetPrintName, "" ) + DEFINE_SCRIPTFUNC( SetPrintName, "" ) + DEFINE_SCRIPTFUNC( GetFootnote, "" ) + DEFINE_SCRIPTFUNC( SetFootnote, "" ) + DEFINE_SCRIPTFUNC( GetCommentaryType, "" ) + DEFINE_SCRIPTFUNC( SetCommentaryType, "" ) + + DEFINE_SIMPLE_SCRIPTHOOK( C_PointCommentaryNode::g_Hook_PreStartCommentaryClient, "PreStartCommentaryClient", FIELD_BOOLEAN, "Called just before commentary begins on the client. Use this to modify variables or commentary behavior before it begins. Returning false will prevent the commentary from starting." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -229,6 +393,22 @@ void C_PointCommentaryNode::OnDataChanged( DataUpdateType_t updateType ) C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( m_bActive && pPlayer ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_PreStartCommentaryClient.CanRunInScope( m_ScriptScope )) + { + ScriptVariant_t functionReturn; + if (g_Hook_PreStartCommentaryClient.Call( m_ScriptScope, &functionReturn, NULL ) && functionReturn.m_type == FIELD_BOOLEAN) + { + // Don't play the commentary if it returned false + if (functionReturn.m_bool == false) + { + engine->ServerCmd( "commentary_finishnode\n" ); + return; + } + } + } +#endif + // Use the HDR / Non-HDR version based on whether we're running HDR or not char *pszCommentaryFile; if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE && m_iszCommentaryFileNoHDR && m_iszCommentaryFileNoHDR[0] ) @@ -245,58 +425,33 @@ void C_PointCommentaryNode::OnDataChanged( DataUpdateType_t updateType ) return; } - EmitSound_t es; - es.m_nChannel = CHAN_STATIC; - es.m_pSoundName = pszCommentaryFile; - es.m_SoundLevel = SNDLVL_GUNFIRE; - es.m_nFlags = SND_SHOULDPAUSE; +#ifdef MAPBASE + int iCommentaryType = m_iCommentaryType; + if (commentary_type_force.GetInt() != -1) + iCommentaryType = commentary_type_force.GetInt(); - CBaseEntity *pSoundEntity; - if ( m_hViewPosition ) + switch (iCommentaryType) { - pSoundEntity = m_hViewPosition; - } - else if ( render->GetViewEntity() ) - { - pSoundEntity = cl_entitylist->GetEnt( render->GetViewEntity() ); - es.m_SoundLevel = SNDLVL_NONE; - } - else - { - pSoundEntity = pPlayer; - } - CSingleUserRecipientFilter filter( pPlayer ); - m_sndCommentary = (CSoundEnvelopeController::GetController()).SoundCreate( filter, pSoundEntity->entindex(), es ); - if ( m_sndCommentary ) - { - (CSoundEnvelopeController::GetController()).SoundSetCloseCaptionDuration( m_sndCommentary, -1 ); - (CSoundEnvelopeController::GetController()).Play( m_sndCommentary, 1.0f, 100, m_flStartTime ); - } + case COMMENTARY_TYPE_TEXT: + StartTextCommentary( pszCommentaryFile, pPlayer ); + break; - // Get the duration so we know when it finishes - float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + case COMMENTARY_TYPE_IMAGE: + StartImageCommentary( pszCommentaryFile, pPlayer ); + break; - CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if ( pHudCloseCaption ) - { - // This is where we play the commentary close caption (and lock the other captions out). - // Also, if close captions are off we force a caption in non-English - if ( closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) - { - // Clear the close caption element in preparation - pHudCloseCaption->Reset(); + case COMMENTARY_TYPE_SCENE: + StartSceneCommentary( pszCommentaryFile, pPlayer ); + break; - // Process the commentary caption - pHudCloseCaption->ProcessCaptionDirect( pszCommentaryFile, flDuration ); - - // Find the close caption hud element & lock it - pHudCloseCaption->Lock(); - } + default: + case COMMENTARY_TYPE_AUDIO: + StartAudioCommentary( pszCommentaryFile, pPlayer ); + break; } - - // Tell the HUD element - CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); - pHudCommentary->StartCommentary( this, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +#else + StartAudioCommentary( pszCommentaryFile, pPlayer ); +#endif } else if ( m_bWasActive ) { @@ -312,6 +467,356 @@ void C_PointCommentaryNode::OnDataChanged( DataUpdateType_t updateType ) m_bRestartAfterRestore = false; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartAudioCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + EmitSound_t es; + es.m_nChannel = CHAN_STATIC; + es.m_pSoundName = pszCommentaryFile; + es.m_SoundLevel = SNDLVL_GUNFIRE; + es.m_nFlags = SND_SHOULDPAUSE; + + CBaseEntity *pSoundEntity; + if ( m_hViewPosition ) + { + pSoundEntity = m_hViewPosition; + } + else if ( render->GetViewEntity() ) + { + pSoundEntity = cl_entitylist->GetEnt( render->GetViewEntity() ); + es.m_SoundLevel = SNDLVL_NONE; + } + else + { + pSoundEntity = pPlayer; + } + CSingleUserRecipientFilter filter( pPlayer ); + m_sndCommentary = (CSoundEnvelopeController::GetController()).SoundCreate( filter, pSoundEntity->entindex(), es ); + if ( m_sndCommentary ) + { + (CSoundEnvelopeController::GetController()).SoundSetCloseCaptionDuration( m_sndCommentary, -1 ); + (CSoundEnvelopeController::GetController()).Play( m_sndCommentary, 1.0f, 100, m_flStartTime ); + } + + // Get the duration so we know when it finishes + float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + bool bSubtitlesEnabled = false; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption ) + { + // This is where we play the commentary close caption (and lock the other captions out). + // Also, if close captions are off we force a caption in non-English + if ( closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) + { + // Clear the close caption element in preparation + pHudCloseCaption->Reset(); + + // Process the commentary caption + pHudCloseCaption->ProcessCaptionDirect( pszCommentaryFile, flDuration ); + + // Find the close caption hud element & lock it + pHudCloseCaption->Lock(); + + bSubtitlesEnabled = true; + } + } + + char *pszSpeakers = m_iszSpeakers; + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartCommentary( this, pszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartTextCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + // Get the duration so we know when it finishes + //float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + + // TODO: Determine from text length? + float flDuration = commentary_type_text_endtime.GetFloat(); + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartTextCommentary( this, pszCommentaryFile, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartImageCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + // Get the duration so we know when it finishes + //float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + + float flDuration = commentary_type_image_endtime.GetFloat(); + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartImageCommentary( this, pszCommentaryFile, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +} + +extern CChoreoStringPool g_ChoreoStringPool; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartSceneCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + char loadfile[MAX_PATH]; + Q_strncpy( loadfile, pszCommentaryFile, sizeof( loadfile ) ); + Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) ); + Q_FixSlashes( loadfile ); + + // + // Raw scene file support + // + void *pBuffer = 0; + size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile ); + if ( bufsize > 0 ) + { + // Definitely in scenes.image + pBuffer = malloc( bufsize ); + if ( !scenefilecache->GetSceneData( pszCommentaryFile, (byte *)pBuffer, bufsize ) ) + { + free( pBuffer ); + } + + + if ( IsBufferBinaryVCD( (char*)pBuffer, bufsize ) ) + { + m_pScene = new CChoreoScene( NULL ); + CUtlBuffer buf( pBuffer, bufsize, CUtlBuffer::READ_ONLY ); + if ( !m_pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) ) + { + Warning( "Unable to restore scene '%s'\n", loadfile ); + delete m_pScene; + m_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); + m_pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf ); + } + + free( pBuffer ); + + if( m_pScene ) + { + m_pScene->SetPrintFunc( Scene_Printf ); + m_pScene->SetEventCallbackInterface( this ); + } + else + { + // Cancel commentary (TODO: clean up?) + return; + } + + int types[ 2 ]; + types[ 0 ] = CChoreoEvent::SPEAK; + //types[ 1 ] = CChoreoEvent::GENERIC; // TODO: Support for the game_text event? + m_pScene->RemoveEventsExceptTypes( types, 1 ); + + // Iterate events and precache necessary resources + for ( int i = 0; i < m_pScene->GetNumEvents(); i++ ) + { + CChoreoEvent *event = m_pScene->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; + } + } + + PrecacheScriptSound( "AI_BaseNPC.SentenceStop" ); + + if ( m_hViewPosition ) + { + m_hSceneOrigin = m_hViewPosition; + } + else if ( render->GetViewEntity() ) + { + m_hSceneOrigin = cl_entitylist->GetEnt( render->GetViewEntity() ); + } + else + { + m_hSceneOrigin = pPlayer; + } + + // Get the duration so we know when it finishes + float flDuration = m_pScene->GetDuration(); + + // Add a tiny amount of time at the end to ensure audio doesn't get cut off + flDuration += 0.5f; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption ) + { + // This is where we play the commentary close caption (and lock the other captions out). + // Also, if close captions are off we force a caption in non-English + if ( closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) + { + // Clear the close caption element in preparation + pHudCloseCaption->Reset(); + + // Find the close caption hud element & lock it + pHudCloseCaption->Lock(); + } + } + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartSceneCommentary( this, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); + + // Start thinking for the scene + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: All events are leading edge triggered +// Input : currenttime - +// *event - +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + Assert( event ); + + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + { + return; + } + + //Msg("Starting event \"%s\" (%s)\n", event->GetName(), event->GetParameters()); + + // load any necessary data + switch (event->GetType() ) + { + default: + break; + case CChoreoEvent::SPEAK: + { + CSingleUserRecipientFilter filter( C_BasePlayer::GetLocalPlayer() ); + + CSoundParameters soundParams; + bool bSoundscript = (g_pSoundEmitterSystem->GetParametersForSound( event->GetParameters(), soundParams, GENDER_NONE, false )); + EmitSound_t es( soundParams ); + if (bSoundscript) + { + } + else + { + es.m_pSoundName = event->GetParameters(); + es.m_flVolume = 1; + } + + // TODO: This is supposed to make sure actors don't interrupt each other, but it doesn't seem to work + es.m_nChannel = CHAN_USER_BASE + scene->FindActorIndex( event->GetActor() ); + es.m_SoundLevel = SNDLVL_GUNFIRE; + es.m_nFlags = SND_SHOULDPAUSE; + + es.m_bEmitCloseCaption = false; + + // Just in case + if (!m_hSceneOrigin) + m_hSceneOrigin = C_BasePlayer::GetLocalPlayer(); + + EmitSound( filter, m_hSceneOrigin->entindex(), es ); + + // Close captioning only on master token no matter what... + // Also, if close captions are off we force a caption in non-English + if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER && closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) + { + char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; + bool validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ); + if ( validtoken ) + { + CRC32_t tokenCRC; + CRC32_Init( &tokenCRC ); + + char lowercase[ 256 ]; + Q_strncpy( lowercase, tok, sizeof( lowercase ) ); + Q_strlower( lowercase ); + + CRC32_ProcessBuffer( &tokenCRC, lowercase, Q_strlen( lowercase ) ); + CRC32_Final( &tokenCRC ); + + float endtime = event->GetLastSlaveEndTime(); + float durationShort = event->GetDuration(); + float durationLong = endtime - event->GetStartTime(); + float duration = MAX( durationShort, durationLong ); + + CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); + if ( hudCloseCaption ) + { + hudCloseCaption->ProcessCaptionDirect( lowercase, duration ); + } + } + + } + } + break; + // TODO: Support for the game_text event? + /* + case CChoreoEvent::GENERIC: + { + + } + break; + */ + } + + event->m_flPrevTime = currenttime; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::ClientThink() +{ + BaseClass::ClientThink(); + +#ifdef MAPBASE + if (m_iCommentaryType == COMMENTARY_TYPE_SCENE && m_pScene) + { + m_pScene->Think( gpGlobals->curtime - m_flStartTime ); + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } +#endif +} + //----------------------------------------------------------------------------- // Purpose: Shut down the commentary //----------------------------------------------------------------------------- @@ -322,6 +827,29 @@ void C_PointCommentaryNode::StopLoopingSounds( void ) (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndCommentary ); m_sndCommentary = NULL; } + +#ifdef MAPBASE + if ( m_pScene ) + { + // Must do this to terminate audio + if (m_hSceneOrigin) + { + CSingleUserRecipientFilter filter( C_BasePlayer::GetLocalPlayer() ); + + for (int i = 0; i < m_pScene->GetNumActors(); i++) + { + EmitSound_t es; + es.m_nChannel = CHAN_USER_BASE + i; + es.m_pSoundName = "common/null.wav"; + + EmitSound( filter, m_hSceneOrigin->entindex(), es ); + } + } + + delete m_pScene; + m_pScene = NULL; + } +#endif } //----------------------------------------------------------------------------- @@ -374,6 +902,17 @@ CHudCommentary::CHudCommentary( const char *name ) : vgui::Panel( NULL, "HudComm m_hActiveNode = NULL; m_bShouldPaint = true; + +#ifdef MAPBASE + m_pLabel = new vgui::Label( this, "HudCommentaryTextLabel", L"Textual commentary" ); + m_pImage = new vgui::ImagePanel( this, "HudCommentaryImagePanel" ); + m_pImage->SetShouldScaleImage( true ); + + m_pFootnoteLabel = new vgui::Label( this, "HudCommentaryFootnoteLabel", L"Commentary footnote" ); + + m_iCCDefaultY = 0; + m_flCCAnimTime = 0.0f; +#endif } void CHudCommentary::ApplySchemeSettings( vgui::IScheme *pScheme ) @@ -384,6 +923,14 @@ void CHudCommentary::ApplySchemeSettings( vgui::IScheme *pScheme ) { SetBgColor( m_BGOverrideColor ); } + +#ifdef MAPBASE + m_pLabel->SetPaintBackgroundType( 2 ); + m_pLabel->SetSize( 0, GetTall() ); + + m_pFootnoteLabel->SetPaintBackgroundType( 2 ); + m_pFootnoteLabel->SetSize( 0, GetTall() ); +#endif } //----------------------------------------------------------------------------- @@ -405,6 +952,17 @@ void CHudCommentary::Paint() if ( pHudCloseCaption ) { pHudCloseCaption->Reset(); + +#ifdef MAPBASE + // Reset close caption element if needed + if (pHudCloseCaption->IsUsingCommentaryDimensions()) + { + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +#endif } } } @@ -413,6 +971,22 @@ void CHudCommentary::Paint() // Detect the end of the commentary if ( flPercentage >= 1 && m_hActiveNode ) { +#ifdef MAPBASE + // Ensure that the scene is terminated + if (m_iCommentaryType == COMMENTARY_TYPE_SCENE) + m_hActiveNode->StopLoopingSounds(); + + // Reset close caption element if needed + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + { + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +#endif + m_hActiveNode = NULL; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HideCommentary" ); @@ -426,27 +1000,62 @@ void CHudCommentary::Paint() int x, y, wide, tall; GetBounds( x, y, wide, tall ); - int xOffset = m_iBarX; + int xOffset = m_iBarX; int yOffset = m_iBarY; // Find our fade based on our time shown - Color clr = Color( 255, 170, 0, GetAlpha() ); + Color clr = m_ForegroundColor; +#ifdef MAPBASE + switch (m_iCommentaryType) + { + case COMMENTARY_TYPE_TEXT: + { + // Figure out the size before setting bounds + int lW, lT; + m_pLabel->GetContentSize( lW, lT ); + + lT += (m_iTextBorderSpace * 2); + + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset + (m_iBarWide * m_flPanelScale), yOffset + (lT /** m_flPanelScale*/) ); //m_iTypeTextT - (yOffset /*+ m_iBarTall*/) ); + } break; + + case COMMENTARY_TYPE_IMAGE: + { + // Figure out the size before setting bounds + int iW, iT; + m_pImage->GetSize( iW, iT ); + //vgui::surface()->DrawGetTextureSize( m_pImage->GetImage()->GetID(), iW, iT ); + + iW += (m_iTextBorderSpace * 2); + iT += (m_iTextBorderSpace * 2); + + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset + iW, yOffset + iT ); //m_iTypeTextT - (yOffset /*+ m_iBarTall*/) ); + } break; + + default: + case COMMENTARY_TYPE_SCENE: + case COMMENTARY_TYPE_AUDIO: + { + // Draw the progress bar + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset+(m_iBarWide*m_flPanelScale), yOffset+m_iBarTall ); + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawFilledRect( xOffset+2, yOffset+2, xOffset+(int)(flPercentage*(m_iBarWide*m_flPanelScale))-2, yOffset+m_iBarTall-2 ); + } break; + } +#else // Draw the progress bar vgui::surface()->DrawSetColor( clr ); vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset+m_iBarWide, yOffset+m_iBarTall ); vgui::surface()->DrawSetColor( clr ); vgui::surface()->DrawFilledRect( xOffset+2, yOffset+2, xOffset+(int)(flPercentage*m_iBarWide)-2, yOffset+m_iBarTall-2 ); +#endif // Draw the speaker names - // Get our scheme and font information - vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); - vgui::HFont hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); - if ( !hFont ) - { - hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); - } - vgui::surface()->DrawSetTextFont( hFont ); + vgui::surface()->DrawSetTextFont( m_hFont ); vgui::surface()->DrawSetTextColor( clr ); vgui::surface()->DrawSetTextPos( m_iSpeakersX, m_iSpeakersY ); vgui::surface()->DrawPrintText( m_szSpeakers, wcslen(m_szSpeakers) ); @@ -469,7 +1078,7 @@ void CHudCommentary::Paint() { int w, h; UTIL_ReplaceKeyBindings( pszText, 0, wzFinal, sizeof( wzFinal ) ); - vgui::surface()->GetTextSize( hFont, wzFinal, w, h ); + vgui::surface()->GetTextSize( m_hFont, wzFinal, w, h ); vgui::surface()->DrawSetTextPos( m_iBarX + m_iBarWide - w, iY ); vgui::surface()->DrawPrintText( wzFinal, wcslen(wzFinal) ); } @@ -478,9 +1087,45 @@ void CHudCommentary::Paint() // Draw the commentary count // Determine our text size, and move that far in from the right hand size (plus the offset) int iCountWide, iCountTall; - vgui::surface()->GetTextSize( hFont, m_szCount, iCountWide, iCountTall ); + vgui::surface()->GetTextSize( m_hFont, m_szCount, iCountWide, iCountTall ); + +#ifdef MAPBASE + if (m_pFootnoteLabel->IsEnabled()) + { + // Raise the count's position so that it doesn't get in the way + //iCountTall *= 2; + + int x, y; + m_pFootnoteLabel->GetPos(x, y); + + // + // Draw a bullet next to each footnote + // + CUtlVector pLineCoords; + pLineCoords.AddToTail( 0 ); // First line + + m_pFootnoteLabel->GetTextImage()->GetNewlinePositions( &pLineCoords, true ); + + int iBulletX = x - commentary_footnote_offset_x.GetInt(); + int iBulletY = y; + + vgui::surface()->DrawSetTextFont( m_hFont ); + vgui::surface()->DrawSetTextColor( clr ); + + for (int i = 0; i < pLineCoords.Count(); i++) + { + vgui::surface()->DrawSetTextPos( iBulletX, iBulletY + pLineCoords[i] ); + vgui::surface()->DrawUnicodeChar( L'\u2022' ); + } + } + + if (m_iCommentaryType != COMMENTARY_TYPE_AUDIO && m_iCommentaryType != COMMENTARY_TYPE_SCENE) + vgui::surface()->DrawSetTextPos( wide - m_iTypeTextCountXFR - iCountWide, tall - m_iTypeTextCountYFB - iCountTall ); + else +#endif vgui::surface()->DrawSetTextPos( wide - m_iCountXFR - iCountWide, m_iCountY ); - vgui::surface()->DrawPrintText( m_szCount, wcslen(m_szCount) ); + + vgui::surface()->DrawPrintText( m_szCount, wcslen( m_szCount ) ); // Draw the icon vgui::surface()->DrawSetColor( Color(255,170,0,GetAlpha()) ); @@ -496,12 +1141,207 @@ bool CHudCommentary::ShouldDraw() return ( m_hActiveNode || GetAlpha() > 0 ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::PerformLayout() +{ + BaseClass::PerformLayout(); + + // Don't do anything if we shouldn't draw + if (!m_hActiveNode) // !ShouldDraw() + return; + + int extraWidth = 0, extraHeight = 0; + + // The dimensions of a progress bar, text card, etc. + int contentWidth = 0, contentHeight = 0; + + int xOffset = m_iBarX; + int yOffset = m_iBarY; + + // Footnotes can add more space to the bottom if they have newlines. + if (m_pFootnoteLabel->IsEnabled()) + { + m_pFootnoteLabel->SetBounds( xOffset, yOffset, (float)(m_iBarWide * m_flPanelScale), GetTall() ); + + int iNoteWide, iNoteTall; + m_pFootnoteLabel->GetContentSize( iNoteWide, iNoteTall ); + + m_pFootnoteLabel->SetTall( iNoteTall ); + + extraHeight += iNoteTall; + } + + switch (m_iCommentaryType) + { + case COMMENTARY_TYPE_TEXT: + { + m_pLabel->SetBounds( + xOffset + m_iTextBorderSpace, yOffset + m_iTextBorderSpace, + (float)(m_iBarWide * m_flPanelScale) - m_iTextBorderSpace, GetTall() ); + + // Figure out the size before setting bounds + int lW, lT; + m_pLabel->GetContentSize( lW, lT ); + + //lT = (float)lT * m_flPanelScale; // Don't affect height when scaling + + m_pLabel->SetTall( lT ); + + lW += (m_iTextBorderSpace * 2); + lT += (m_iTextBorderSpace * 2); + + contentWidth = lW, contentHeight = lT; + + lW += (xOffset * 2); + lT += (yOffset * 2); + + ResolveBounds( lW + extraWidth, lT + extraHeight ); + } break; + + case COMMENTARY_TYPE_IMAGE: + { + // Figure out the size before setting bounds + int iW, iT; + //m_pImage->GetImage()->GetSize( iW, iT ); + vgui::surface()->DrawGetTextureSize( m_pImage->GetImage()->GetID(), iW, iT ); + if (iW <= 0) + iW = 1; + + int iTargetSize = (m_iBarWide - m_iTextBorderSpace); + iT *= (iTargetSize / iW); + iW = iTargetSize; + + iW = (float)iW * m_flPanelScale; + iT = (float)iT * m_flPanelScale; + + m_pImage->SetBounds( + xOffset + m_iTextBorderSpace, + yOffset + m_iTextBorderSpace, + iW, iT ); + + iW += (m_iTextBorderSpace * 2); + iT += (m_iTextBorderSpace * 2); + + contentWidth = iW, contentHeight = iT; + + iW += (xOffset * 2); + iT += (yOffset * 2); + + ResolveBounds( iW + extraWidth, iT + extraHeight ); + } break; + + default: + case COMMENTARY_TYPE_SCENE: + case COMMENTARY_TYPE_AUDIO: + + // Keep the box centered + SetBounds( m_iTypeAudioX, m_iTypeAudioY - extraHeight, m_iTypeAudioW + extraWidth, m_iTypeAudioT + extraHeight ); + + // Reposition the subtitles to be above the commentary dialog + if (m_bShouldRepositionSubtitles) + { + RepositionAndFollowCloseCaption( extraHeight ); + } + + contentWidth = (m_iBarWide * m_flPanelScale), contentHeight = m_iBarTall; + + break; + } + + // Move the footnote to be at the bottom + if (m_pFootnoteLabel->IsEnabled()) + { + m_pFootnoteLabel->SetPos( m_iSpeakersX + commentary_footnote_offset_x.GetInt(), yOffset+contentHeight+ commentary_footnote_offset_y.GetInt() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Resolves position on screen; Heavily borrows from CHudMessage::XPosition/YPosition +//----------------------------------------------------------------------------- +void CHudCommentary::ResolveBounds( int width, int height ) +{ + int xPos; + int yPos; + + // ====== X ====== + if ( m_flOverrideX == -1 ) + { + xPos = (ScreenWidth() - width) * 0.5f; + } + else + { + if ( m_flOverrideX < 0 ) + xPos = (1.0 + m_flOverrideX) * ScreenWidth() - width; // Align to right + else + xPos = m_flOverrideX * (ScreenWidth() - width); + } + + // Clamp to edge of screen + if ( xPos + width > ScreenWidth() ) + xPos = ScreenWidth() - width; + else if ( xPos < 0 ) + xPos = 0; + + // ====== Y ====== + if ( m_flOverrideY == -1 ) + { + yPos = (ScreenHeight() - height) * 0.5f; + } + else + { + if ( m_flOverrideY < 0 ) + yPos = (1.0 + m_flOverrideY) * ScreenHeight() - height; // Align to bottom + else + yPos = m_flOverrideY * (ScreenHeight() - height); + } + + // Clamp to edge of screen + if ( yPos + height > ScreenHeight() ) + yPos = ScreenHeight() - height; + else if ( yPos < 0 ) + yPos = 0; + + SetBounds( xPos, yPos, width, height ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::LevelShutdown( void ) +{ + if (m_iCCDefaultY != 0) + { + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + { + int ccX, ccY; + pHudCloseCaption->GetPos( ccX, ccY ); + + if (m_iCCDefaultY != ccY) + { + DevMsg( "CHudCommentary had to reset misaligned CC element Y (%i) to default Y (%i)\n", ccY, m_iCCDefaultY ); + pHudCloseCaption->SetPos( ccX, m_iCCDefaultY ); + } + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudCommentary::Init( void ) { m_matIcon.Init( "vgui/hud/icon_commentary", TEXTURE_GROUP_VGUI ); + +#ifdef MAPBASE + SetProportional( true ); +#endif } //----------------------------------------------------------------------------- @@ -511,6 +1351,9 @@ void CHudCommentary::VidInit( void ) { SetAlpha(0); StopCommentary(); +#ifdef MAPBASE + m_iCCDefaultY = 0; +#endif } //----------------------------------------------------------------------------- @@ -525,7 +1368,38 @@ void CHudCommentary::StartCommentary( C_PointCommentaryNode *pNode, char *pszSpe m_flStartTime = flStartTime; m_flEndTime = flEndTime; m_bHiding = false; - g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof(m_szSpeakers) ); +#ifdef MAPBASE + m_iCommentaryType = COMMENTARY_TYPE_AUDIO; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; +#endif + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + +#ifdef MAPBASE + SetBounds( m_iTypeAudioX, m_iTypeAudioY, m_iTypeAudioW, m_iTypeAudioT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_BackgroundColor ); + + m_pLabel->SetPaintEnabled( false ); + m_pImage->SetPaintEnabled( false ); + m_pImage->EvictImage(); + + m_pFootnoteLabel->SetEnabled( false ); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + m_hSmallFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentarySmall" ); + if ( !m_hSmallFont) + { + m_hSmallFont = m_hFont; + } +#endif // Don't draw the element itself if closecaptions are on (and captions are always on in non-english mode) ConVarRef pCVar( "closecaption" ); @@ -537,6 +1411,95 @@ void CHudCommentary::StartCommentary( C_PointCommentaryNode *pNode, char *pszSpe { m_bShouldPaint = true; } + +#ifdef MAPBASE + if (!m_bShouldPaint && commentary_audio_element_below_cc.GetBool()) + { + m_bShouldPaint = true; + m_bShouldRepositionSubtitles = true; + + // Ensure we perform layout later + InvalidateLayout(); + } + else + m_bShouldRepositionSubtitles = false; + + FixupCommentaryLabels( pNode->m_iszPrintName, pNode->m_iszSpeakers, pNode->m_iszFootnote ); +#endif + + SetPaintBackgroundEnabled( m_bShouldPaint ); + + char sz[MAX_COUNT_STRING]; + Q_snprintf( sz, sizeof(sz), "%d \\ %d", iNode, iNodeMax ); + g_pVGuiLocalize->ConvertANSIToUnicode( sz, m_szCount, sizeof(m_szCount) ); + + // If the commentary just started, play the commentary fade in. + if ( fabs(flStartTime - gpGlobals->curtime) < 1.0 ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ShowCommentary" ); + } + else + { + // We're reloading a savegame that has an active commentary going in it. Don't fade in. + SetAlpha( 255 ); + } +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::StartTextCommentary( C_PointCommentaryNode *pNode, const char *pszText, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ) +{ + if ( (flEndTime - flStartTime) <= 0 ) + return; + + m_hActiveNode = pNode; + m_flStartTime = flStartTime; + m_flEndTime = flEndTime; + m_bHiding = false; + m_iCommentaryType = COMMENTARY_TYPE_TEXT; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + + SetBounds( m_iTypeTextX, m_iTypeTextY, m_iTypeTextW, m_iTypeTextT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_TextBackgroundColor ); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + m_hSmallFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentarySmall" ); + if ( !m_hSmallFont) + { + m_hSmallFont = m_hFont; + } + + m_pLabel->SetText( pszText ); + m_pLabel->SetFont( m_hFont ); + m_pLabel->SetWrap( true ); + m_pLabel->SetPaintEnabled( true ); + m_pLabel->SetPaintBackgroundEnabled( false ); + m_pLabel->SetPaintBorderEnabled( false ); + //m_pLabel->SizeToContents(); + m_pLabel->SetContentAlignment( vgui::Label::a_northwest ); + m_pLabel->SetFgColor( m_TypeTextContentColor ); + + m_pImage->SetPaintEnabled( false ); + m_pImage->EvictImage(); + + m_pFootnoteLabel->SetEnabled( false ); + + m_bShouldPaint = true; + + FixupCommentaryLabels( pNode->m_iszPrintName, pNode->m_iszSpeakers, pNode->m_iszFootnote ); + SetPaintBackgroundEnabled( m_bShouldPaint ); char sz[MAX_COUNT_STRING]; @@ -555,14 +1518,300 @@ void CHudCommentary::StartCommentary( C_PointCommentaryNode *pNode, char *pszSpe } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::StartImageCommentary( C_PointCommentaryNode *pNode, const char *pszImage, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ) +{ + if ( (flEndTime - flStartTime) <= 0 ) + return; + + m_hActiveNode = pNode; + m_flStartTime = flStartTime; + m_flEndTime = flEndTime; + m_bHiding = false; + m_iCommentaryType = COMMENTARY_TYPE_IMAGE; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + + SetBounds( m_iTypeTextX, m_iTypeTextY, m_iTypeTextW, m_iTypeTextT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_TextBackgroundColor ); + + m_pLabel->SetPaintEnabled( false ); + + m_pImage->SetPaintEnabled( true ); + m_pImage->SetImage( pszImage ); + m_pImage->SetWide( m_iBarWide - m_iTextBorderSpace ); + + m_pFootnoteLabel->SetEnabled( false ); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + m_hSmallFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentarySmall" ); + if ( !m_hSmallFont) + { + m_hSmallFont = m_hFont; + } + + m_bShouldPaint = true; + + FixupCommentaryLabels( pNode->m_iszPrintName, pNode->m_iszSpeakers, pNode->m_iszFootnote ); + + SetPaintBackgroundEnabled( m_bShouldPaint ); + + char sz[MAX_COUNT_STRING]; + Q_snprintf( sz, sizeof(sz), "%d \\ %d", iNode, iNodeMax ); + g_pVGuiLocalize->ConvertANSIToUnicode( sz, m_szCount, sizeof(m_szCount) ); + + // If the commentary just started, play the commentary fade in. + if ( fabs(flStartTime - gpGlobals->curtime) < 1.0 ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ShowCommentary" ); + } + else + { + // We're reloading a savegame that has an active commentary going in it. Don't fade in. + SetAlpha( 255 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::StartSceneCommentary( C_PointCommentaryNode *pNode, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ) +{ + if ( (flEndTime - flStartTime) <= 0 ) + return; + + m_hActiveNode = pNode; + m_flStartTime = flStartTime; + m_flEndTime = flEndTime; + m_bHiding = false; + m_iCommentaryType = COMMENTARY_TYPE_SCENE; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + + SetBounds( m_iTypeAudioX, m_iTypeAudioY, m_iTypeAudioW, m_iTypeAudioT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_BackgroundColor ); + + m_pLabel->SetPaintEnabled( false ); + m_pImage->SetPaintEnabled( false ); + m_pImage->EvictImage(); + + m_pFootnoteLabel->SetEnabled( false ); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + m_hSmallFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentarySmall" ); + if ( !m_hSmallFont) + { + m_hSmallFont = m_hFont; + } + + // Don't draw the element itself if closecaptions are on (and captions are always on in non-english mode) + ConVarRef pCVar( "closecaption" ); + if ( pCVar.IsValid() ) + { + m_bShouldPaint = ( !pCVar.GetBool() && english.GetBool() ); + } + else + { + m_bShouldPaint = true; + } + + if (!m_bShouldPaint && commentary_audio_element_below_cc.GetBool()) + { + m_bShouldPaint = true; + m_bShouldRepositionSubtitles = true; + + // Ensure we perform layout later + InvalidateLayout(); + } + else + m_bShouldRepositionSubtitles = false; + + FixupCommentaryLabels( pNode->m_iszPrintName, pNode->m_iszSpeakers, pNode->m_iszFootnote ); + + SetPaintBackgroundEnabled( m_bShouldPaint ); + + char sz[MAX_COUNT_STRING]; + Q_snprintf( sz, sizeof(sz), "%d \\ %d", iNode, iNodeMax ); + g_pVGuiLocalize->ConvertANSIToUnicode( sz, m_szCount, sizeof(m_szCount) ); + + // If the commentary just started, play the commentary fade in. + if ( fabs(flStartTime - gpGlobals->curtime) < 1.0 ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ShowCommentary" ); + } + else + { + // We're reloading a savegame that has an active commentary going in it. Don't fade in. + SetAlpha( 255 ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudCommentary::StopCommentary( void ) { m_hActiveNode = NULL; + +#ifdef MAPBASE + // Reset close caption element if needed + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + { + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::FixupCommentaryLabels( const char *pszPrintName, const char *pszSpeakers, const char *pszFootnote ) +{ + if (commentary_combine_speaker_and_printname.GetBool() && pszPrintName[0] != '\0') + { + wchar_t *pszLocal = g_pVGuiLocalize->Find( pszPrintName ); + if (m_szSpeakers[0] == '\0' || !m_bShouldPaint) // Use m_bShouldPaint as an indicator of whether or not we use subtitles + { + if (pszPrintName[0] == '#' && pszLocal) + wcsncpy( m_szSpeakers, pszLocal, sizeof( m_szSpeakers ) / sizeof( wchar_t ) ); + else + g_pVGuiLocalize->ConvertANSIToUnicode( pszPrintName, m_szSpeakers, sizeof( m_szSpeakers ) ); + } + else + { + static wchar_t iszSpeakersLocalized[MAX_SPEAKER_NAME] = { 0 }; + static wchar_t iszPrintNameLocalized[MAX_SPEAKER_NAME] = { 0 }; + + wcsncpy( iszSpeakersLocalized, m_szSpeakers, sizeof( iszSpeakersLocalized ) / sizeof( wchar_t ) ); + + if (m_szSpeakers[0] == '#') + { + wchar_t *pwszSpeakers = g_pVGuiLocalize->Find( pszSpeakers ); + if (pwszSpeakers) + wcsncpy( iszSpeakersLocalized, pwszSpeakers, sizeof( iszSpeakersLocalized ) / sizeof( wchar_t ) ); + } + + if (pszPrintName[0] == '#' && pszLocal) + wcsncpy( iszPrintNameLocalized, pszLocal, sizeof( iszPrintNameLocalized ) / sizeof( wchar_t ) ); + else + g_pVGuiLocalize->ConvertANSIToUnicode( pszPrintName, iszPrintNameLocalized, sizeof( iszPrintNameLocalized ) ); + + V_snwprintf( m_szSpeakers, sizeof( m_szSpeakers ), L"%ls ~ %ls", iszSpeakersLocalized, iszPrintNameLocalized ); + } + } + + if (pszFootnote[0] != '\0' && m_bShouldPaint) + { + m_pFootnoteLabel->SetText( pszFootnote ); + m_pFootnoteLabel->SetFont( m_hSmallFont ); + m_pFootnoteLabel->SetWrap( true ); + m_pFootnoteLabel->SetEnabled( true ); + m_pFootnoteLabel->SetPaintEnabled( true ); + m_pFootnoteLabel->SetPaintBackgroundEnabled( false ); + m_pFootnoteLabel->SetPaintBorderEnabled( false ); + //m_pFootnoteLabel->SizeToContents(); + m_pFootnoteLabel->SetContentAlignment( vgui::Label::a_northwest ); + m_pFootnoteLabel->SetFgColor( m_ForegroundColor ); + } + else + { + m_pFootnoteLabel->SetPaintEnabled( false ); + m_pFootnoteLabel->SetEnabled( false ); + } + + // Reset close caption element if it's still using commentary dimensions + // (fixes problems with switching from node to node) + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + { + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::RepositionAndFollowCloseCaption( int yOffset ) +{ + // Invert the Y axis + //SetPos( m_iTypeAudioX, ScreenHeight() - m_iTypeAudioY ); + + // Place underneath the close caption element + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption /*&& !pHudCloseCaption->IsUsingCommentaryDimensions()*/) + { + int ccX, ccY; + pHudCloseCaption->GetPos( ccX, ccY ); + + // Save the default position in case we need to do a hard reset + // (this usually happens when players begin commentary before the CC element's return animation command is finished) + if (m_iCCDefaultY == 0) + { + m_iCCDefaultY = ccY; + } + + if (!pHudCloseCaption->IsUsingCommentaryDimensions()) + { + if (m_iCCDefaultY != ccY /*&& !pHudCloseCaption->IsUsingCommentaryDimensions()*/) + { + DevMsg( "CHudCommentary had to reset misaligned CC element Y (%i) to default Y (%i)\n", ccY, m_iCCDefaultY ); + ccY = m_iCCDefaultY; + } + + ccY -= m_iTypeAudioT; + + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", ccY - yOffset, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_DEACCEL ); + //pHudCloseCaption->SetPos( ccX, ccY ); + m_flCCAnimTime = gpGlobals->curtime + 0.2f; + + pHudCloseCaption->SetUsingCommentaryDimensions( true ); + } + else if (gpGlobals->curtime > m_flCCAnimTime && ccY != m_iCCDefaultY - m_iTypeAudioT - yOffset) + { + DevMsg( "CHudCommentary had to correct misaligned CC element offset (%i != %i)\n", m_iCCDefaultY - ccY, yOffset ); + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY - m_iTypeAudioT - yOffset, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_DEACCEL ); + m_flCCAnimTime = gpGlobals->curtime + 0.2f; + } + + SetPos( ccX, ccY + pHudCloseCaption->GetTall() + commentary_audio_element_below_cc_margin.GetInt() ); + + m_flPanelScale = (float)pHudCloseCaption->GetWide() / (float)GetWide(); + SetWide( pHudCloseCaption->GetWide() ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/c_props.cpp b/mp/src/game/client/c_props.cpp index 07f5710d..7981fe2c 100644 --- a/mp/src/game/client/c_props.cpp +++ b/mp/src/game/client/c_props.cpp @@ -24,6 +24,11 @@ BEGIN_NETWORK_TABLE( CDynamicProp, DT_DynamicProp ) RecvPropBool(RECVINFO(m_bUseHitboxesForRenderBox)), END_NETWORK_TABLE() +#ifdef MAPBASE_VSCRIPT +// Allows client-side VScript to create dynamic props via CreateProp() +LINK_ENTITY_TO_CLASS( prop_dynamic, C_DynamicProp ); +#endif + C_DynamicProp::C_DynamicProp( void ) { m_iCachedFrameCount = -1; diff --git a/mp/src/game/client/c_rope.cpp b/mp/src/game/client/c_rope.cpp index 0898857e..4bd288ac 100644 --- a/mp/src/game/client/c_rope.cpp +++ b/mp/src/game/client/c_rope.cpp @@ -74,6 +74,27 @@ IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_RopeKeyframe, DT_RopeKeyframe, CRopeKeyframe RecvPropInt( RECVINFO( m_iParentAttachment ) ), END_RECV_TABLE() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( C_RopeKeyframe, C_BaseEntity, "The clientside class of move_rope and keyframe_rope" ) + DEFINE_SCRIPTFUNC( GetNodePosition, "Gets the position of the specified node index" ) + DEFINE_SCRIPTFUNC( GetNumNodes, "Gets the number of nodes available" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetStartEntity, "GetStartEntity", "Gets the rope's start entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEndEntity, "GetEndEntity", "Gets the rope's end entity" ) + + DEFINE_SCRIPTFUNC( SetupHangDistance, "Sets the rope's hang distance" ) + DEFINE_SCRIPTFUNC( SetSlack, "Sets the rope's slack value (extra length)" ) + DEFINE_SCRIPTFUNC( GetRopeFlags, "Gets the rope's flags" ) + DEFINE_SCRIPTFUNC( SetRopeFlags, "Sets the rope's flags" ) + + DEFINE_SCRIPTFUNC( SetColorMod, "Sets the rope's color mod value" ) + + DEFINE_SCRIPTFUNC( ShakeRope, "Shakes the rope with the specified center, radius, and magnitude" ) + + DEFINE_SCRIPTFUNC( AnyPointsMoved, "Returns true if any points have moved recently" ) +END_SCRIPTDESC(); +#endif + #define ROPE_IMPULSE_SCALE 20 #define ROPE_IMPULSE_DECAY 0.95 @@ -2032,6 +2053,25 @@ bool C_RopeKeyframe::GetAttachment( int number, Vector &origin, QAngle &angles ) return false; } +#ifdef MAPBASE +const Vector &C_RopeKeyframe::GetNodePosition( int index ) +{ + int nNodes = m_RopePhysics.NumNodes(); + if ( index >= nNodes || nNodes < 2 ) + { + Warning( "C_RopeKeyframe::GetNodePosition(): Invalid node index %i (number of nodes is %i)\n", index, nNodes ); + return vec3_origin; + } + + return m_RopePhysics.GetNode( index )->m_vPredicted; +} + +int C_RopeKeyframe::GetNumNodes() +{ + return m_RopePhysics.NumNodes(); +} +#endif + bool C_RopeKeyframe::AnyPointsMoved() { #ifdef MAPBASE diff --git a/mp/src/game/client/c_rope.h b/mp/src/game/client/c_rope.h index 3d821081..204b412b 100644 --- a/mp/src/game/client/c_rope.h +++ b/mp/src/game/client/c_rope.h @@ -33,6 +33,9 @@ public: DECLARE_CLASS( C_RopeKeyframe, C_BaseEntity ); DECLARE_CLIENTCLASS(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif private: @@ -142,6 +145,11 @@ public: virtual bool GetAttachment( int number, Vector &origin ); virtual bool GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel ); +#ifdef MAPBASE + const Vector &GetNodePosition( int index ); + int GetNumNodes(); +#endif + private: void FinishInit( const char *pMaterialName ); @@ -166,6 +174,11 @@ private: void ReceiveMessage( int classID, bf_read &msg ); bool CalculateEndPointAttachment( C_BaseEntity *pEnt, int iAttachment, Vector &vPos, QAngle *pAngles ); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetStartEntity() { return ToHScript( GetStartEntity() ); } + HSCRIPT ScriptGetEndEntity() { return ToHScript( GetEndEntity() ); } +#endif + private: // Track which links touched something last frame. Used to prevent wind from gusting on them. diff --git a/mp/src/game/client/c_vguiscreen.h b/mp/src/game/client/c_vguiscreen.h index d304f5c9..71780093 100644 --- a/mp/src/game/client/c_vguiscreen.h +++ b/mp/src/game/client/c_vguiscreen.h @@ -112,6 +112,15 @@ public: C_BasePlayer *GetPlayerOwner( void ); bool IsInputOnlyToOwner( void ); +#ifdef MAPBASE + void GetSize( float &width, float &height ) const { width = m_flWidth; height = m_flHeight; } + void GetPixelSize( int &width, int &height ) const { width = m_nPixelWidth; height = m_nPixelHeight; } + void SetWidth( float flWidth ) { m_flWidth = flWidth; } + void SetHeight( float flHeight ) { m_flHeight = flHeight; } + void SetPixelWidth( int nWidth ) { m_nPixelWidth = nWidth; } + void SetPixelHeight( int nHeight ) { m_nPixelHeight = nHeight; } +#endif + private: // Vgui screen management void CreateVguiScreen( const char *pTypeName ); diff --git a/mp/src/game/client/c_world.cpp b/mp/src/game/client/c_world.cpp index cf48d47c..938db241 100644 --- a/mp/src/game/client/c_world.cpp +++ b/mp/src/game/client/c_world.cpp @@ -13,6 +13,9 @@ #include "ivieweffects.h" #include "shake.h" #include "eventlist.h" +#ifdef MAPBASE +#include "mapentities_shared.h" +#endif // NVNT haptic include for notification of world precache #include "haptics/haptic_utils.h" // memdbgon must be the last include file in a .cpp file!!! @@ -62,9 +65,6 @@ BEGIN_RECV_TABLE( C_World, DT_World ) #ifdef MAPBASE RecvPropString(RECVINFO(m_iszChapterTitle)), #endif -#ifdef MAPBASE_VSCRIPT - RecvPropInt(RECVINFO(m_iScriptLanguageClient)), -#endif END_RECV_TABLE() #ifdef MAPBASE_VSCRIPT @@ -86,6 +86,11 @@ bool C_World::Init( int entnum, int iSerialNum ) ActivityList_Init(); EventList_Init(); +#ifdef MAPBASE_VSCRIPT + m_iScriptLanguageServer = SL_NONE; + m_iScriptLanguageClient = SL_NONE; +#endif + return BaseClass::Init( entnum, iSerialNum ); } @@ -129,11 +134,6 @@ 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 } } @@ -199,6 +199,72 @@ void C_World::Spawn( void ) Precache(); } +//----------------------------------------------------------------------------- +// Parse data from a map file +//----------------------------------------------------------------------------- +bool C_World::KeyValue( const char *szKeyName, const char *szValue ) +{ +#ifdef MAPBASE_VSCRIPT + if ( FStrEq( szKeyName, "vscriptlanguage" ) ) + { + m_iScriptLanguageServer = atoi( szValue ); + } + else if ( FStrEq( szKeyName, "vscriptlanguage_client" ) ) + { + m_iScriptLanguageClient = atoi( szValue ); + } + else +#endif + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Parses worldspawn data from BSP on the client +//----------------------------------------------------------------------------- +void C_World::ParseWorldMapData( const char *pMapData ) +{ + char szTokenBuffer[MAPKEY_MAXLENGTH]; + for ( ; true; pMapData = MapEntity_SkipToNextEntity(pMapData, szTokenBuffer) ) + { + // + // Parse the opening brace. + // + char token[MAPKEY_MAXLENGTH]; + pMapData = MapEntity_ParseToken( pMapData, token ); + + // + // Check to see if we've finished or not. + // + if (!pMapData) + break; + + if (token[0] != '{') + { + Error( "MapEntity_ParseAllEntities: found %s when expecting {", token); + continue; + } + + CEntityMapData entData( (char*)pMapData ); + char className[MAPKEY_MAXLENGTH]; + + if (!entData.ExtractValue( "classname", className )) + { + Error( "classname missing from entity!\n" ); + } + + if ( !Q_strcmp( className, "worldspawn" ) ) + { + // Set up keyvalues. + ParseMapData( &entData ); + return; + } + } +} +#endif + C_World *GetClientWorldEntity() diff --git a/mp/src/game/client/c_world.h b/mp/src/game/client/c_world.h index a0cc315c..12366490 100644 --- a/mp/src/game/client/c_world.h +++ b/mp/src/game/client/c_world.h @@ -31,6 +31,7 @@ public: virtual void Precache(); virtual void Spawn(); + virtual bool KeyValue( const char *szKeyName, const char *szValue ); // Don't worry about adding the world to the collision list; it's already there virtual CollideType_t GetCollideType( void ) { return ENTITY_SHOULD_NOT_COLLIDE; } @@ -41,8 +42,17 @@ public: float GetWaveHeight() const; const char *GetDetailSpriteMaterial() const; +#ifdef MAPBASE + // A special function which parses map data for the client world entity before LevelInitPreEntity(). + // This can be used to access keyvalues early and without transmitting from the server. + void ParseWorldMapData( const char *pMapData ); +#endif + #ifdef MAPBASE_VSCRIPT - ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)m_iScriptLanguageClient; } + void ClientThink() { ScriptContextThink(); } + + // -2 = Use server language + ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)(m_iScriptLanguageClient != -2 ? m_iScriptLanguageClient : m_iScriptLanguageServer); } #endif public: @@ -64,6 +74,7 @@ public: char m_iszChapterTitle[64]; #endif #ifdef MAPBASE_VSCRIPT + int m_iScriptLanguageServer; int m_iScriptLanguageClient; #endif diff --git a/mp/src/game/client/cdll_client_int.cpp b/mp/src/game/client/cdll_client_int.cpp index af3e7334..5d118085 100644 --- a/mp/src/game/client/cdll_client_int.cpp +++ b/mp/src/game/client/cdll_client_int.cpp @@ -148,6 +148,10 @@ #include "fbxsystem/fbxsystem.h" #endif +#ifdef MAPBASE_VSCRIPT +#include "vscript_client.h" +#endif + extern vgui::IInputInternal *g_InputInternal; //============================================================================= @@ -271,6 +275,8 @@ void ProcessCacheUsedMaterials() } } +void VGui_ClearVideoPanels(); + // String tables INetworkStringTable *g_pStringTableParticleEffectNames = NULL; INetworkStringTable *g_StringTableEffectDispatch = NULL; @@ -1108,6 +1114,9 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physi g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetViewEffectsRestoreBlockHandler() ); +#ifdef MAPBASE_VSCRIPT + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() ); +#endif ClientWorldFactoryInit(); @@ -1214,12 +1223,17 @@ void CHLClient::Shutdown( void ) g_pSixenseInput = NULL; #endif + VGui_ClearVideoPanels(); + C_BaseAnimating::ShutdownBoneSetupThreadPool(); ClientWorldFactoryShutdown(); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetViewEffectsRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() ); +#ifdef MAPBASE_VSCRIPT + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() ); +#endif ClientVoiceMgr_Shutdown(); @@ -1639,6 +1653,10 @@ void CHLClient::LevelInitPreEntity( char const* pMapName ) tempents->LevelInit(); ResetToneMapping(1.0); +#ifdef MAPBASE + GetClientWorldEntity()->ParseWorldMapData( engine->GetMapEntitiesString() ); +#endif + IGameSystem::LevelInitPreEntityAllSystems(pMapName); #ifdef USES_ECON_ITEMS diff --git a/mp/src/game/client/cdll_util.h b/mp/src/game/client/cdll_util.h index 44f559b0..26332551 100644 --- a/mp/src/game/client/cdll_util.h +++ b/mp/src/game/client/cdll_util.h @@ -166,7 +166,6 @@ inline bool FStrEq(const char *sz1, const char *sz2) { #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 ); diff --git a/mp/src/game/client/client_base.vpc b/mp/src/game/client/client_base.vpc index 9d222159..3228cbfc 100644 --- a/mp/src/game/client/client_base.vpc +++ b/mp/src/game/client/client_base.vpc @@ -535,7 +535,6 @@ $Project "$SRCDIR\public\dt_utlvector_recv.cpp" \ "$SRCDIR\public\filesystem_helpers.cpp" \ "$SRCDIR\public\interpolatortypes.cpp" \ - "$SRCDIR\game\shared\interval.cpp" \ "$SRCDIR\common\language.cpp" \ "$SRCDIR\public\networkvar.cpp" \ "$SRCDIR\common\randoverride.cpp" \ @@ -1106,6 +1105,7 @@ $Project $File "$SRCDIR\public\vgui_controls\WizardSubPanel.h" $File "$SRCDIR\public\worldsize.h" $File "$SRCDIR\public\zip_uncompressed.h" + $File "$SRCDIR\public\tier1\interval.h" //Haptics $File "$SRCDIR\public\haptics\ihaptics.h" [$WIN32] $File "$SRCDIR\public\haptics\haptic_utils.h" [$WIN32] @@ -1162,7 +1162,6 @@ $Project $File "$SRCDIR\game\shared\igamesystem.h" $File "$SRCDIR\game\shared\imovehelper.h" $File "$SRCDIR\game\shared\in_buttons.h" - $File "$SRCDIR\game\shared\interval.h" $File "$SRCDIR\game\shared\iplayeranimstate.h" $File "$SRCDIR\game\shared\ipredictionsystem.h" $File "$SRCDIR\game\shared\itempents.h" @@ -1260,6 +1259,9 @@ $Project $Lib vtf $ImpLib steam_api + // Discord integration + $Lib "$LIBPUBLIC\discord-rpc" [$MAPBASE_RPC] + $Libexternal $LIBCOMMON/libcrypto [$OSXALL] $Libexternal "$SRCDIR\lib\common\$(CRYPTOPPDIR)\libcrypto" [$LINUXALL] diff --git a/mp/src/game/client/client_mapbase.vpc b/mp/src/game/client/client_mapbase.vpc index 3c2eb00f..f8860e24 100644 --- a/mp/src/game/client/client_mapbase.vpc +++ b/mp/src/game/client/client_mapbase.vpc @@ -12,6 +12,7 @@ $Configuration $PreprocessorDefinitions "$BASE;MAPBASE_RPC;DISCORD_RPC;STEAM_RPC" [$MAPBASE_RPC] $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] + $PreprocessorDefinitions "$BASE;NEW_RESPONSE_SYSTEM" [$NEW_RESPONSE_SYSTEM] } } @@ -31,16 +32,23 @@ $Project $File "c_postprocesscontroller.cpp" $File "c_postprocesscontroller.h" $File "c_env_dof_controller.cpp" + $File "c_movie_display.cpp" + $File "c_movie_display.h" + $File "vgui_movie_display.cpp" + $File "convarproxy.cpp" $Folder "Mapbase" { $File "$SRCDIR\game\shared\mapbase\mapbase_shared.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_usermessages.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\singleplayer_animstate.cpp" + $File "$SRCDIR\game\shared\mapbase\singleplayer_animstate.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] @@ -50,11 +58,18 @@ $Project $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 "$SRCDIR\game\shared\mapbase\logic_script_client.cpp" [$MAPBASE_VSCRIPT] + + $File "mapbase\vscript_vgui.cpp" [$MAPBASE_VSCRIPT] + $File "mapbase\vscript_vgui.h" [$MAPBASE_VSCRIPT] + $File "mapbase\vscript_vgui.nut" [$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" + $File "mapbase\c_vgui_text_display.cpp" + $File "mapbase\mapbase_autocubemap.cpp" } $Folder "HL2 DLL" diff --git a/mp/src/game/client/clientmode_shared.cpp b/mp/src/game/client/clientmode_shared.cpp index 827a8207..fed16af0 100644 --- a/mp/src/game/client/clientmode_shared.cpp +++ b/mp/src/game/client/clientmode_shared.cpp @@ -298,8 +298,11 @@ ClientModeShared::ClientModeShared() m_pWeaponSelection = NULL; m_nRootSize[ 0 ] = m_nRootSize[ 1 ] = -1; +#ifdef MAPBASE // From Alien Swarm SDK m_pCurrentPostProcessController = NULL; m_PostProcessLerpTimer.Invalidate(); + m_pCurrentColorCorrection = NULL; +#endif #if defined( REPLAY_ENABLED ) m_pReplayReminderPanel = NULL; @@ -656,6 +659,43 @@ void ClientModeShared::Update() } } +#ifdef MAPBASE // From Alien Swarm SDK +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ClientModeShared::OnColorCorrectionWeightsReset( void ) +{ + C_ColorCorrection *pNewColorCorrection = NULL; + C_ColorCorrection *pOldColorCorrection = m_pCurrentColorCorrection; + C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pPlayer ) + { + pNewColorCorrection = pPlayer->GetActiveColorCorrection(); + } + + if ( pNewColorCorrection != pOldColorCorrection ) + { + if ( pOldColorCorrection ) + { + pOldColorCorrection->EnableOnClient( false ); + } + if ( pNewColorCorrection ) + { + pNewColorCorrection->EnableOnClient( true, pOldColorCorrection == NULL ); + } + m_pCurrentColorCorrection = pNewColorCorrection; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float ClientModeShared::GetColorCorrectionScale( void ) const +{ + return 1.0f; +} +#endif + //----------------------------------------------------------------------------- // This processes all input before SV Move messages are sent //----------------------------------------------------------------------------- @@ -1280,7 +1320,7 @@ void ClientModeShared::FireGameEvent( IGameEvent *event ) } } - if ( team == 0 && GetLocalTeam() > 0 ) + if ( team == 0 && GetLocalTeam() ) { bValidTeam = false; } diff --git a/mp/src/game/client/clientmode_shared.h b/mp/src/game/client/clientmode_shared.h index 76ec95c8..b77e0c23 100644 --- a/mp/src/game/client/clientmode_shared.h +++ b/mp/src/game/client/clientmode_shared.h @@ -91,6 +91,11 @@ public: virtual void ProcessInput(bool bActive); virtual bool CreateMove( float flInputSampleTime, CUserCmd *cmd ); virtual void Update(); +#ifdef MAPBASE // From Alien Swarm SDK + virtual void OnColorCorrectionWeightsReset( void ); + virtual float GetColorCorrectionScale( void ) const; + virtual void ClearCurrentColorCorrection() { m_pCurrentColorCorrection = NULL; } +#endif // Input virtual int KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ); @@ -165,12 +170,16 @@ private: CBaseHudWeaponSelection *m_pWeaponSelection; int m_nRootSize[2]; +#ifdef MAPBASE // From Alien Swarm SDK void UpdatePostProcessingEffects(); const C_PostProcessController* m_pCurrentPostProcessController; PostProcessParameters_t m_CurrentPostProcessParameters; PostProcessParameters_t m_LerpStartPostProcessParameters, m_LerpEndPostProcessParameters; CountdownTimer m_PostProcessLerpTimer; + + CHandle m_pCurrentColorCorrection; +#endif }; #endif // CLIENTMODE_NORMAL_H diff --git a/mp/src/game/client/clientshadowmgr.cpp b/mp/src/game/client/clientshadowmgr.cpp index fcd0155d..43ee5538 100644 --- a/mp/src/game/client/clientshadowmgr.cpp +++ b/mp/src/game/client/clientshadowmgr.cpp @@ -125,6 +125,11 @@ ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "1" ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" ); #endif +#ifdef MAPBASE +ConVarRef mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap" ); +ConVarRef mat_depthbias_shadowmap( "mat_depthbias_shadowmap" ); +#endif + #ifdef _WIN32 #pragma warning( disable: 4701 ) #endif @@ -1424,6 +1429,15 @@ bool CClientShadowMgr::Init() materials->AddRestoreFunc( ShadowRestoreFunc ); +#ifdef MAPBASE + // These need to be referenced here since the cvars don't exist in the initial declaration + mat_slopescaledepthbias_shadowmap = ConVarRef( "mat_slopescaledepthbias_shadowmap" ); + mat_depthbias_shadowmap = ConVarRef( "mat_depthbias_shadowmap" ); + + mat_slopescaledepthbias_shadowmap.SetValue( "16" ); // Would do something like 2 here, but it causes citizens to look weird under flashlights + mat_depthbias_shadowmap.SetValue( "0.00005" ); +#endif + return true; } @@ -4473,13 +4487,18 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) } CViewSetup shadowView; +#ifndef MAPBASE shadowView.m_flAspectRatio = 1.0f; +#endif 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; +#ifdef MAPBASE + shadowView.m_flAspectRatio = (flashlightState.m_fHorizontalFOVDegrees / flashlightState.m_fVerticalFOVDegrees); +#endif // MAPBASE #endif // Copy flashlight parameters @@ -4487,6 +4506,10 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) if ( !flashlightState.m_bOrtho ) { shadowView.m_bOrtho = false; + +#ifdef MAPBASE + shadowView.m_flAspectRatio = (flashlightState.m_fHorizontalFOVDegrees / flashlightState.m_fVerticalFOVDegrees); +#endif // MAPBASE } else { @@ -4495,6 +4518,10 @@ void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup ) shadowView.m_OrthoTop = flashlightState.m_fOrthoTop; shadowView.m_OrthoRight = flashlightState.m_fOrthoRight; shadowView.m_OrthoBottom = flashlightState.m_fOrthoBottom; + +#ifdef MAPBASE + shadowView.m_flAspectRatio = 1.0f; +#endif } shadowView.m_bDoBloomAndToneMapping = false; diff --git a/mp/src/game/client/colorcorrectionmgr.cpp b/mp/src/game/client/colorcorrectionmgr.cpp index 770354b0..cf1210ac 100644 --- a/mp/src/game/client/colorcorrectionmgr.cpp +++ b/mp/src/game/client/colorcorrectionmgr.cpp @@ -8,6 +8,12 @@ #include "cbase.h" #include "tier0/vprof.h" #include "colorcorrectionmgr.h" +#ifdef MAPBASE // From Alien Swarm SDK +#include "clientmode_shared.h" //"clientmode.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" +#endif //------------------------------------------------------------------------------ @@ -16,6 +22,13 @@ static CColorCorrectionMgr s_ColorCorrectionMgr; CColorCorrectionMgr *g_pColorCorrectionMgr = &s_ColorCorrectionMgr; +#ifdef MAPBASE // From Alien Swarm SDK +static ConVar mat_colcorrection_editor( "mat_colcorrection_editor", "0" ); + +static CUtlVector g_ColorCorrectionList; +static CUtlVector g_ColorCorrectionVolumeList; +#endif + //------------------------------------------------------------------------------ // Constructor @@ -62,10 +75,89 @@ void CColorCorrectionMgr::RemoveColorCorrection( ClientCCHandle_t h ) } } +#ifdef MAPBASE // From Alien Swarm SDK +ClientCCHandle_t CColorCorrectionMgr::AddColorCorrectionEntity( C_ColorCorrection *pEntity, const char *pName, const char *pFileName ) +{ + ClientCCHandle_t h = AddColorCorrection(pName, pFileName); + if ( h != INVALID_CLIENT_CCHANDLE ) + { + Assert(g_ColorCorrectionList.Find(pEntity) == -1); + g_ColorCorrectionList.AddToTail(pEntity); + } + return h; +} + +void CColorCorrectionMgr::RemoveColorCorrectionEntity( C_ColorCorrection *pEntity, ClientCCHandle_t h) +{ + RemoveColorCorrection(h); + g_ColorCorrectionList.FindAndFastRemove(pEntity); +} + +ClientCCHandle_t CColorCorrectionMgr::AddColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, const char *pName, const char *pFileName ) +{ + ClientCCHandle_t h = AddColorCorrection(pName, pFileName); + if ( h != INVALID_CLIENT_CCHANDLE ) + { + Assert(g_ColorCorrectionVolumeList.Find(pVolume) == -1); + g_ColorCorrectionVolumeList.AddToTail(pVolume); + } + return h; +} + +void CColorCorrectionMgr::RemoveColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, ClientCCHandle_t h) +{ + RemoveColorCorrection(h); + g_ColorCorrectionVolumeList.FindAndFastRemove(pVolume); +} +#endif //------------------------------------------------------------------------------ // Modify color correction weights //------------------------------------------------------------------------------ +#ifdef MAPBASE // From Alien Swarm SDK +void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight, bool bExclusive ) +{ + if ( h != INVALID_CLIENT_CCHANDLE ) + { + SetWeightParams_t params = { h, flWeight, bExclusive }; + m_colorCorrectionWeights.AddToTail( params ); + if( bExclusive && m_bHaveExclusiveWeight && ( flWeight != 0.0f ) ) + { + DevWarning( "Found multiple active color_correction entities with exclusive setting enabled. This is invalid.\n" ); + } + if ( bExclusive ) + { + m_bHaveExclusiveWeight = true; + m_flExclusiveWeight = flWeight; + } + } +} + +void CColorCorrectionMgr::CommitColorCorrectionWeights() +{ + CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); + + for ( int i = 0; i < m_colorCorrectionWeights.Count(); i++ ) + { + ColorCorrectionHandle_t ccHandle = reinterpret_cast( m_colorCorrectionWeights[i].handle ); + float flWeight = m_colorCorrectionWeights[i].flWeight; + if ( !m_colorCorrectionWeights[i].bExclusive ) + { + flWeight = (1.0f - m_flExclusiveWeight ) * m_colorCorrectionWeights[i].flWeight; + } + pRenderContext->SetLookupWeight( ccHandle, flWeight ); + + // FIXME: NOTE! This doesn't work if the same handle has + // its weight set twice with no intervening calls to ResetColorCorrectionWeights + // which, at the moment, is true + if ( flWeight != 0.0f ) + { + ++m_nActiveWeightCount; + } + } + m_colorCorrectionWeights.RemoveAll(); +} +#else void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight ) { if ( h != INVALID_CLIENT_CCHANDLE ) @@ -83,6 +175,7 @@ void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float fl } } } +#endif void CColorCorrectionMgr::ResetColorCorrectionWeights() { @@ -93,6 +186,11 @@ void CColorCorrectionMgr::ResetColorCorrectionWeights() CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); pRenderContext->ResetLookupWeights(); m_nActiveWeightCount = 0; +#ifdef MAPBASE // From Alien Swarm SDK + m_bHaveExclusiveWeight = false; + m_flExclusiveWeight = 0.0f; + m_colorCorrectionWeights.RemoveAll(); +#endif } void CColorCorrectionMgr::SetResetable( ClientCCHandle_t h, bool bResetable ) @@ -113,7 +211,34 @@ void CColorCorrectionMgr::SetResetable( ClientCCHandle_t h, bool bResetable ) //------------------------------------------------------------------------------ // Is color correction active? //------------------------------------------------------------------------------ +#ifdef MAPBASE // From Alien Swarm SDK +bool CColorCorrectionMgr::HasNonZeroColorCorrectionWeights() const +{ + return ( m_nActiveWeightCount != 0 ) || mat_colcorrection_editor.GetBool(); +} + +void CColorCorrectionMgr::UpdateColorCorrection() +{ + ResetColorCorrectionWeights(); + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + IClientMode *pClientMode = GetClientModeNormal(); //GetClientMode(); + + Assert( pClientMode ); + if ( !pPlayer || !pClientMode ) + { + return; + } + + pClientMode->OnColorCorrectionWeightsReset(); + float ccScale = pClientMode->GetColorCorrectionScale(); + + UpdateColorCorrectionEntities( pPlayer, ccScale, g_ColorCorrectionList.Base(), g_ColorCorrectionList.Count() ); + UpdateColorCorrectionVolumes( pPlayer, ccScale, g_ColorCorrectionVolumeList.Base(), g_ColorCorrectionVolumeList.Count() ); + CommitColorCorrectionWeights(); +} +#else bool CColorCorrectionMgr::HasNonZeroColorCorrectionWeights() const { return ( m_nActiveWeightCount != 0 ); } +#endif diff --git a/mp/src/game/client/colorcorrectionmgr.h b/mp/src/game/client/colorcorrectionmgr.h index 3d5271db..3eba0f8c 100644 --- a/mp/src/game/client/colorcorrectionmgr.h +++ b/mp/src/game/client/colorcorrectionmgr.h @@ -14,6 +14,10 @@ #include "igamesystem.h" +#ifdef MAPBASE // From Alien Swarm SDK +class C_ColorCorrection; +class C_ColorCorrectionVolume; +#endif //------------------------------------------------------------------------------ // Purpose : Singleton manager for color correction on the client @@ -35,8 +39,21 @@ public: ClientCCHandle_t AddColorCorrection( const char *pName, const char *pFileName = NULL ); void RemoveColorCorrection( ClientCCHandle_t ); +#ifdef MAPBASE // From Alien Swarm SDK + ClientCCHandle_t AddColorCorrectionEntity( C_ColorCorrection *pEntity, const char *pName, const char *pFileName = NULL ); + void RemoveColorCorrectionEntity( C_ColorCorrection *pEntity, ClientCCHandle_t ); + + ClientCCHandle_t AddColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, const char *pName, const char *pFileName = NULL ); + void RemoveColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, ClientCCHandle_t ); +#endif + // Modify color correction weights +#ifdef MAPBASE // From Alien Swarm SDK + void SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight, bool bExclusive = false ); + void UpdateColorCorrection(); +#else void SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight ); +#endif void ResetColorCorrectionWeights(); void SetResetable( ClientCCHandle_t h, bool bResetable ); @@ -45,8 +62,27 @@ public: private: int m_nActiveWeightCount; +#ifdef MAPBASE // From Alien Swarm SDK + bool m_bHaveExclusiveWeight; + float m_flExclusiveWeight; + + struct SetWeightParams_t + { + ClientCCHandle_t handle; + float flWeight; + bool bExclusive; + }; + + CUtlVector< SetWeightParams_t > m_colorCorrectionWeights; + + void CommitColorCorrectionWeights(); +#endif }; +#ifdef MAPBASE // From Alien Swarm SDK +void UpdateColorCorrectionEntities( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrection **pList, int listCount ); +void UpdateColorCorrectionVolumes( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrectionVolume **pList, int listCount ); +#endif //------------------------------------------------------------------------------ // Singleton access diff --git a/mp/src/game/client/convarproxy.cpp b/mp/src/game/client/convarproxy.cpp new file mode 100644 index 00000000..6b08bafc --- /dev/null +++ b/mp/src/game/client/convarproxy.cpp @@ -0,0 +1,113 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Material proxy to stuff a convar into a material var. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +// identifier was truncated to '255' characters in the debug information +//#pragma warning(disable: 4786) + +#include "convar.h" +#include "materialsystem/imaterialproxy.h" +#include "materialsystem/imaterialvar.h" +//#include "imaterialproxydict.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +class CConVarMaterialProxy: public IMaterialProxy +{ +public: + CConVarMaterialProxy() + : m_pResult( NULL ), + m_conVarRef( "", true ) + { + } + + virtual ~CConVarMaterialProxy() + { + } + + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ) + { + const char *pResult = pKeyValues->GetString( "resultVar" ); + if ( !pResult ) + return false; + + bool found; + m_pResult = pMaterial->FindVar( pResult, &found ); + if ( !found ) + { + m_pResult = NULL; + return false; + } + + /* + if ( !Q_stricmp( pResult, "$alpha" ) ) + { + pMaterial->SetMaterialVarFlag( MATERIAL_VAR_ALPHA_MODIFIED_BY_PROXY, true ); + } + */ + + pResult = pKeyValues->GetString( "convar" ); + if( !pResult ) + { + return false; + } + + m_conVarRef.Init( pResult, false ); + if ( !m_conVarRef.IsValid() ) + { + return false; + } + + return true; + } + + virtual void OnBind( void* ) + { + switch( m_pResult->GetType() ) + { + case MATERIAL_VAR_TYPE_VECTOR: + { + float f = m_conVarRef.GetFloat(); + Vector4D vec( f, f, f, f ); + m_pResult->SetVecValue( vec.Base(), m_pResult->VectorSize() ); + } + break; + +#ifdef MAPBASE + case MATERIAL_VAR_TYPE_STRING: + m_pResult->SetStringValue( m_conVarRef.GetString() ); + break; +#endif + + case MATERIAL_VAR_TYPE_INT: + m_pResult->SetIntValue( m_conVarRef.GetInt() ); + break; + + case MATERIAL_VAR_TYPE_FLOAT: + default: + m_pResult->SetFloatValue( m_conVarRef.GetFloat() ); + break; + } + } + + virtual IMaterial *GetMaterial() + { + return m_pResult->GetOwningMaterial(); + } + + virtual void Release() + { + } + +protected: + IMaterialVar *m_pResult; + ConVarRef m_conVarRef; +}; + +EXPOSE_INTERFACE( CConVarMaterialProxy, IMaterialProxy, "ConVar" IMATERIAL_PROXY_INTERFACE_VERSION ); diff --git a/mp/src/game/client/episodic/c_prop_scalable.cpp b/mp/src/game/client/episodic/c_prop_scalable.cpp index d3902db3..b3134460 100644 --- a/mp/src/game/client/episodic/c_prop_scalable.cpp +++ b/mp/src/game/client/episodic/c_prop_scalable.cpp @@ -5,6 +5,10 @@ //============================================================================= #include "cbase.h" +#ifdef MAPBASE +#include "proxyentity.h" +#include "materialsystem/imaterialvar.h" +#endif class C_PropScalable : public C_BaseAnimating { @@ -194,3 +198,56 @@ void C_PropScalable::GetRenderBounds( Vector &theMins, Vector &theMaxs ) Assert( theMins.IsValid() && theMaxs.IsValid() ); } + +#ifdef MAPBASE +ConVar r_coreball_update_sphere_center( "r_coreball_update_sphere_center", "1", FCVAR_NONE, "Allows prop_coreball to update its center to the entity's origin" ); + +class CCoreBallUpdateMaterialProxy : public CEntityMaterialProxy +{ +public: + CCoreBallUpdateMaterialProxy() + { + m_pMaterial = NULL; + m_pSphereCenter = NULL; + } + virtual ~CCoreBallUpdateMaterialProxy() + { + } + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ) + { + m_pMaterial = pMaterial; + bool found; + m_pSphereCenter = m_pMaterial->FindVar( "$spherecenter", &found ); + if( !found ) + { + m_pSphereCenter = NULL; + return false; + } + return true; + } + virtual void OnBind( C_BaseEntity *pC_BaseEntity ) + { + if (r_coreball_update_sphere_center.GetBool()) + { + const Vector &origin = pC_BaseEntity->GetAbsOrigin(); + m_pSphereCenter->SetVecValue( origin.x, origin.y, origin.z ); + } + else + { + // Just continuously bind the old hacked value (TODO: Optimize so it's not just assigning the same value constantly?) + m_pSphereCenter->SetVecValue( 2688.0, 12139.0, 5170.0 ); + } + } + + virtual IMaterial *GetMaterial() + { + return m_pMaterial; + } + +protected: + IMaterial *m_pMaterial; + IMaterialVar *m_pSphereCenter; +}; + +EXPOSE_INTERFACE( CCoreBallUpdateMaterialProxy, IMaterialProxy, "CoreBallUpdate" IMATERIAL_PROXY_INTERFACE_VERSION ); +#endif diff --git a/mp/src/game/client/flashlighteffect.cpp b/mp/src/game/client/flashlighteffect.cpp index 6733fc56..9a4af817 100644 --- a/mp/src/game/client/flashlighteffect.cpp +++ b/mp/src/game/client/flashlighteffect.cpp @@ -52,8 +52,8 @@ static ConVar r_flashlightladderdist( "r_flashlightladderdist", "40.0", FCVAR_CH 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 ); +extern ConVarRef mat_slopescaledepthbias_shadowmap; +extern ConVarRef mat_depthbias_shadowmap; #endif #ifdef MAPBASE static ConVar r_flashlighttextureoverride( "r_flashlighttextureoverride", "", FCVAR_CHEAT ); diff --git a/mp/src/game/client/fx_impact.cpp b/mp/src/game/client/fx_impact.cpp index 682d5435..236e39b4 100644 --- a/mp/src/game/client/fx_impact.cpp +++ b/mp/src/game/client/fx_impact.cpp @@ -25,6 +25,13 @@ extern ConVar r_drawmodeldecals; ImpactSoundRouteFn g_pImpactSoundRouteFn = NULL; +#ifdef MAPBASE +ConVar g_ragdoll_steal_impacts_client( "g_ragdoll_steal_impacts_client", "1", FCVAR_NONE, "Allows clientside death ragdolls to \"steal\" impacts from their source entities. This fixes issues with NPCs dying before decals are applied." ); +ConVar g_ragdoll_steal_impacts_server( "g_ragdoll_steal_impacts_server", "1", FCVAR_NONE, "Allows serverside death ragdolls to \"steal\" impacts from their source entities. This fixes issues with NPCs dying before decals are applied." ); + +ConVar g_ragdoll_client_impact_decals( "g_ragdoll_client_impact_decals", "1", FCVAR_NONE, "Applies decals to clientside ragdolls when they are hit." ); +#endif + //========================================================================================================================== // RAGDOLL ENUMERATOR //========================================================================================================================== @@ -32,7 +39,11 @@ CRagdollEnumerator::CRagdollEnumerator( Ray_t& shot, int iDamageType ) { m_rayShot = shot; m_iDamageType = iDamageType; +#ifdef MAPBASE + m_pHitEnt = NULL; +#else m_bHit = false; +#endif } IterationRetval_t CRagdollEnumerator::EnumElement( IHandleEntity *pHandleEntity ) @@ -57,7 +68,11 @@ IterationRetval_t CRagdollEnumerator::EnumElement( IHandleEntity *pHandleEntity if ( tr.fraction < 1.0 ) { pModel->ImpactTrace( &tr, m_iDamageType, NULL ); +#ifdef MAPBASE + m_pHitEnt = pModel; +#else m_bHit = true; +#endif //FIXME: Yes? No? return ITERATION_STOP; @@ -84,6 +99,22 @@ bool FX_AffectRagdolls( Vector vecOrigin, Vector vecStart, int iDamageType ) return ragdollEnum.Hit(); } +#ifdef MAPBASE +C_BaseAnimating *FX_AffectRagdolls_GetHit( Vector vecOrigin, Vector vecStart, int iDamageType ) +{ + // don't do this when lots of ragdolls are simulating + if ( s_RagdollLRU.CountRagdolls(true) > 1 ) + return NULL; + Ray_t shotRay; + shotRay.Init( vecStart, vecOrigin ); + + CRagdollEnumerator ragdollEnum( shotRay, iDamageType ); + partition->EnumerateElementsAlongRay( PARTITION_CLIENT_RESPONSIVE_EDICTS, shotRay, false, &ragdollEnum ); + + return ragdollEnum.GetHit(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : &data - @@ -104,6 +135,22 @@ bool Impact( Vector &vecOrigin, Vector &vecStart, int iMaterial, int iDamageType Assert ( pEntity ); +#ifdef MAPBASE + // If the entity already has a ragdoll that was created on the current tick, use that ragdoll instead. + // This allows the killing damage's decals to show up on the ragdoll. + if (C_BaseAnimating *pAnimating = pEntity->GetBaseAnimating()) + { + if (pAnimating->m_pClientsideRagdoll && WasRagdollCreatedOnCurrentTick( pAnimating->m_pClientsideRagdoll ) && g_ragdoll_steal_impacts_client.GetBool()) + { + pEntity = pAnimating->m_pClientsideRagdoll; + } + else if (pAnimating->m_pServerRagdoll && WasRagdollCreatedOnCurrentTick( pAnimating->m_pServerRagdoll ) && g_ragdoll_steal_impacts_server.GetBool()) + { + pEntity = pAnimating->m_pServerRagdoll; + } + } +#endif + // Clear out the trace memset( &tr, 0, sizeof(trace_t)); tr.fraction = 1.0f; @@ -115,13 +162,52 @@ bool Impact( Vector &vecOrigin, Vector &vecStart, int iMaterial, int iDamageType VectorMA( vecStart, flLength + 8.0f, shotDir, traceExt ); // Attempt to hit ragdolls - + bool bHitRagdoll = false; - + +#ifdef MAPBASE + if ( !pEntity->IsClientCreated() ) + { + C_BaseAnimating *pRagdoll = FX_AffectRagdolls_GetHit( vecOrigin, vecStart, iDamageType ); + if (pRagdoll) + { + bHitRagdoll = true; + + if (g_ragdoll_client_impact_decals.GetBool() && pRagdoll->IsRagdoll()) + { + pEntity = pRagdoll; + + // HACKHACK: Get the ragdoll's nearest bone for its material + int iNearestMaterial = 0; + float flNearestDistSqr = FLT_MAX; + + IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + for ( int i = 0; i < count; i++ ) + { + Vector vecPosition; + QAngle angAngles; + pList[i]->GetPosition( &vecPosition, &angAngles ); + float flDistSqr = (vecStart - vecPosition).LengthSqr(); + if (flDistSqr < flNearestDistSqr) + { + iNearestMaterial = pList[i]->GetMaterialIndex(); + flNearestDistSqr = flDistSqr; + } + } + + // Get the material from the surfaceprop + surfacedata_t *psurfaceData = physprops->GetSurfaceData( iNearestMaterial ); + iMaterial = psurfaceData->game.material; + } + } + } +#else if ( !pEntity->IsClientCreated() ) { bHitRagdoll = FX_AffectRagdolls( vecOrigin, vecStart, iDamageType ); } +#endif if ( (nFlags & IMPACT_NODECAL) == 0 ) { diff --git a/mp/src/game/client/fx_impact.h b/mp/src/game/client/fx_impact.h index ad57f7e7..9c6cb875 100644 --- a/mp/src/game/client/fx_impact.h +++ b/mp/src/game/client/fx_impact.h @@ -58,12 +58,21 @@ public: // Actual work code virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); +#ifdef MAPBASE + bool Hit( void ) const { return m_pHitEnt != NULL; } + C_BaseAnimating *GetHit( void ) { return m_pHitEnt; } +#else bool Hit( void ) const { return m_bHit; } +#endif private: Ray_t m_rayShot; int m_iDamageType; +#ifdef MAPBASE + C_BaseAnimating *m_pHitEnt; +#else bool m_bHit; +#endif }; #endif // FX_IMPACT_H diff --git a/mp/src/game/client/game_controls/baseviewport.cpp b/mp/src/game/client/game_controls/baseviewport.cpp index 31d8dca2..2a69f416 100644 --- a/mp/src/game/client/game_controls/baseviewport.cpp +++ b/mp/src/game/client/game_controls/baseviewport.cpp @@ -156,6 +156,32 @@ bool CBaseViewport::LoadHudAnimations( void ) return true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Reloads HUD animations after loading a map-specific HUD animations file. +//----------------------------------------------------------------------------- +void CBaseViewport::ReloadHudAnimations( void ) +{ + // Force a reload + if ( LoadHudAnimations() == false ) + { + // Fall back to just the main + if ( m_pAnimController->SetScriptFile( GetVPanel(), "scripts/HudAnimations.txt", true ) == false ) + { + Assert(0); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Loads a map-specific HUD animations file. +//----------------------------------------------------------------------------- +bool CBaseViewport::LoadCustomHudAnimations( const char *pszFile ) +{ + return m_pAnimController->SetScriptFile( GetVPanel(), pszFile, true ); +} +#endif + //================================================================ CBaseViewport::CBaseViewport() : vgui::EditablePanel( NULL, "CBaseViewport") { diff --git a/mp/src/game/client/game_controls/baseviewport.h b/mp/src/game/client/game_controls/baseviewport.h index 4e4c64e8..cbc602a0 100644 --- a/mp/src/game/client/game_controls/baseviewport.h +++ b/mp/src/game/client/game_controls/baseviewport.h @@ -73,6 +73,10 @@ public: public: // IGameEventListener: virtual void FireGameEvent( IGameEvent * event); +#ifdef MAPBASE + bool LoadCustomHudAnimations( const char *pszFile ); + void ReloadHudAnimations( void ); +#endif protected: diff --git a/mp/src/game/client/glow_outline_effect.h b/mp/src/game/client/glow_outline_effect.h index aac399d7..11132862 100644 --- a/mp/src/game/client/glow_outline_effect.h +++ b/mp/src/game/client/glow_outline_effect.h @@ -150,6 +150,10 @@ private: static const int ENTRY_IN_USE = -2; }; +#ifdef MAPBASE_VSCRIPT + // For unregistration boundary check +public: +#endif CUtlVector< GlowObjectDefinition_t > m_GlowObjectDefinitions; int m_nFirstFreeSlot; }; diff --git a/mp/src/game/client/hl2/c_basehlplayer.cpp b/mp/src/game/client/hl2/c_basehlplayer.cpp index 17b3cf53..15a33ea8 100644 --- a/mp/src/game/client/hl2/c_basehlplayer.cpp +++ b/mp/src/game/client/hl2/c_basehlplayer.cpp @@ -31,6 +31,9 @@ ConVar cl_npc_speedmod_outtime( "cl_npc_speedmod_outtime", "1.5", FCVAR_CLIENTDL IMPLEMENT_CLIENTCLASS_DT(C_BaseHLPlayer, DT_HL2_Player, CHL2_Player) RecvPropDataTable( RECVINFO_DT(m_HL2Local),0, &REFERENCE_RECV_TABLE(DT_HL2Local) ), RecvPropBool( RECVINFO( m_fIsSprinting ) ), +#ifdef SP_ANIM_STATE + RecvPropFloat( RECVINFO( m_flAnimRenderYaw ) ), +#endif END_RECV_TABLE() BEGIN_PREDICTION_DATA( C_BaseHLPlayer ) @@ -90,6 +93,13 @@ void C_BaseHLPlayer::OnDataChanged( DataUpdateType_t updateType ) SetNextClientThink( CLIENT_THINK_ALWAYS ); } +#ifdef SP_ANIM_STATE + if (m_flAnimRenderYaw != FLT_MAX) + { + m_angAnimRender = QAngle( 0, m_flAnimRenderYaw, 0 ); + } +#endif + BaseClass::OnDataChanged( updateType ); } @@ -657,3 +667,21 @@ void C_BaseHLPlayer::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quatern BuildFirstPersonMeathookTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed, "ValveBiped.Bip01_Head1" ); } + +#ifdef SP_ANIM_STATE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const QAngle& C_BaseHLPlayer::GetRenderAngles( void ) +{ + if ( m_flAnimRenderYaw != FLT_MAX ) + { + return m_angAnimRender; + } + else + { + return BaseClass::GetRenderAngles(); + } +} +#endif + diff --git a/mp/src/game/client/hl2/c_basehlplayer.h b/mp/src/game/client/hl2/c_basehlplayer.h index fe838cad..368c9bb1 100644 --- a/mp/src/game/client/hl2/c_basehlplayer.h +++ b/mp/src/game/client/hl2/c_basehlplayer.h @@ -15,6 +15,10 @@ #include "c_baseplayer.h" #include "c_hl2_playerlocaldata.h" +#if !defined( HL2MP ) && defined ( MAPBASE ) +#include "mapbase/singleplayer_animstate.h" +#endif + class C_BaseHLPlayer : public C_BasePlayer { public: @@ -58,6 +62,10 @@ public: bool IsWeaponLowered( void ) { return m_HL2Local.m_bWeaponLowered; } +#ifdef SP_ANIM_STATE + virtual const QAngle& GetRenderAngles( void ); +#endif + public: C_HL2PlayerLocalData m_HL2Local; @@ -78,7 +86,13 @@ private: bool m_bPlayUseDenySound; // Signaled by PlayerUse, but can be unset by HL2 ladder code... float m_flSpeedMod; float m_flExitSpeedMod; - + +#ifdef SP_ANIM_STATE + // At the moment, we network the render angles since almost none of the player anim stuff is done on the client in SP. + // If any of this is ever adapted for MP, this method should be replaced with replicating/moving the anim state to the client. + float m_flAnimRenderYaw; + QAngle m_angAnimRender; +#endif friend class CHL2GameMovement; }; diff --git a/mp/src/game/client/hl2/hud_credits.cpp b/mp/src/game/client/hl2/hud_credits.cpp index 280e8a44..fb4354f5 100644 --- a/mp/src/game/client/hl2/hud_credits.cpp +++ b/mp/src/game/client/hl2/hud_credits.cpp @@ -32,6 +32,16 @@ struct creditname_t float flTimeAdd; float flTimeStart; int iSlot; + +#ifdef MAPBASE + // New credits stuff + + Color cColorOverride; + + // Images + int iImageID = -1; + float flImageScale = 1.0f; +#endif }; #define CREDITS_FILE "scripts/credits.txt" @@ -47,6 +57,9 @@ enum #define CREDITS_LOGO 1 #define CREDITS_INTRO 2 #define CREDITS_OUTRO 3 +#ifdef MAPBASE +#define CREDITS_PRECACHE 4 +#endif bool g_bRollingCredits = false; @@ -93,15 +106,27 @@ private: void DrawOutroCreditsName( void ); void DrawIntroCreditsName( void ); void DrawLogo( void ); +#ifdef MAPBASE + void DrawOutroCreditFont( const char *pCreditName, float flYPos, vgui::HFont hTFont, const Color &cColor, int iScreenWidth, int iDivisor = 2 ); + void DrawOutroCreditTexture( int iImageID, float flYPos, float flImageScale, const Color &cColor, int iScreenWidth, int iDivisor = 2 ); +#endif void PrepareLogo( float flTime ); void PrepareOutroCredits( void ); void PrepareIntroCredits( void ); +#ifdef MAPBASE + void PrecacheCredits(); +#endif + float FadeBlend( float fadein, float fadeout, float hold, float localTime ); void PrepareLine( vgui::HFont hFont, char const *pchLine ); +#ifdef MAPBASE + int GetOrAllocateImageID( const char *szFileName ); +#endif + CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "Default" ); CPanelAnimationVar( Color, m_TextColor, "TextColor", "FgColor" ); @@ -110,7 +135,10 @@ private: float m_flScrollTime; float m_flSeparation; #ifdef MAPBASE - int m_iEndLines; + int m_iEndLines; + float m_flEndLinesFadeHoldTime; + bool m_bAllowColumns; + CUtlDict m_ImageDict; #endif float m_flFadeTime; bool m_bLastOneInPlace; @@ -141,6 +169,9 @@ private: char m_szCreditsFile[MAX_PATH]; char m_szLogoFont[64]; + char m_szLogo2Font[64]; + Color m_cLogoColor; + Color m_cLogo2Color; #endif }; @@ -202,6 +233,20 @@ void CHudCredits::Clear( void ) m_bLastOneInPlace = false; m_Alpha = m_TextColor[3]; m_iLogoState = LOGO_FADEOFF; + +#ifdef MAPBASE + if ( surface() ) + { + for (int i = m_ImageDict.Count()-1; i >= 0; i--) + { + if (m_ImageDict[i] != -1) + { + surface()->DestroyTextureID( m_ImageDict[i] ); + m_ImageDict.RemoveAt( i ); + } + } + } +#endif } //----------------------------------------------------------------------------- @@ -229,7 +274,11 @@ void CHudCredits::ReadNames( KeyValues *pKeyValue ) { creditname_t Credits; V_strcpy_safe( Credits.szCreditName, pKVNames->GetName() ); +#ifdef MAPBASE + V_strcpy_safe( Credits.szFontName, pKVNames->GetString( (const char *)NULL, "Default" ) ); +#else V_strcpy_safe( Credits.szFontName, pKeyValue->GetString( Credits.szCreditName, "Default" ) ); +#endif m_CreditsList.AddToTail( Credits ); pKVNames = pKVNames->GetNextKey(); @@ -248,6 +297,8 @@ void CHudCredits::ReadParams( KeyValues *pKeyValue ) m_flSeparation = pKeyValue->GetFloat( "separation", 5 ); #ifdef MAPBASE m_iEndLines = pKeyValue->GetInt( "endlines", 1 ); + m_flEndLinesFadeHoldTime = pKeyValue->GetFloat( "endlines_fadeholdtime", ( IsConsole() ? 2.0f : 10.0f ) ); // "360 certification requires that we not hold a static image too long." + m_bAllowColumns = pKeyValue->GetBool( "allow_columns", false ); #endif m_flFadeInTime = pKeyValue->GetFloat( "fadeintime", 1 ); @@ -268,6 +319,10 @@ void CHudCredits::ReadParams( KeyValues *pKeyValue ) #ifdef MAPBASE Q_strncpy( m_szLogoFont, pKeyValue->GetString( "logofont", "" ), sizeof( m_szLogoFont ) ); + Q_strncpy( m_szLogo2Font, pKeyValue->GetString( "logo2font", "" ), sizeof( m_szLogo2Font ) ); + + m_cLogoColor = pKeyValue->GetColor( "logocolor" ); + m_cLogo2Color = pKeyValue->GetColor( "logo2color" ); #endif } @@ -300,10 +355,46 @@ void CHudCredits::DrawOutroCreditsName( void ) if ( pCredit == NULL ) continue; +#ifdef MAPBASE + vgui::HScheme scheme = GetScheme(); +#else vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); - vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true ); +#endif + vgui::HFont m_hTFont = INVALID_FONT; - int iFontTall = surface()->GetFontTall ( m_hTFont ); + int iFontTall = 1; + +#ifdef MAPBASE + if (pCredit->iImageID != -1) + { + // Get the size of the tallest image if there's multiple + int iFontWide; + if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" )) + { + CUtlStringList outStrings; + V_SplitString( pCredit->szCreditName, "\t", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + int iTempTall; + surface()->DrawGetTextureSize( GetOrAllocateImageID( outStrings[i] ), iFontWide, iTempTall ); + if (iTempTall > iFontTall) + iFontTall = iTempTall; + } + outStrings.PurgeAndDeleteElements(); + } + else + { + surface()->DrawGetTextureSize( GetOrAllocateImageID( pCredit->szCreditName ), iFontWide, iFontTall ); + } + + iFontTall = ((float)iFontTall * pCredit->flImageScale); + } + else +#endif + { + m_hTFont = vgui::scheme()->GetIScheme( scheme )->GetFont( pCredit->szFontName, true ); + iFontTall = surface()->GetFontTall( m_hTFont ); + } if ( pCredit->flYPos < -iFontTall || pCredit->flYPos > iTall ) { @@ -317,6 +408,9 @@ void CHudCredits::DrawOutroCreditsName( void ) Color cColor = m_TextColor; #ifdef MAPBASE + if (pCredit->cColorOverride.a() > 0) + cColor = pCredit->cColorOverride; + // Some lines should stick around and fade out if ( i >= m_CreditsList.Count()-m_iEndLines ) #else @@ -333,8 +427,12 @@ void CHudCredits::DrawOutroCreditsName( void ) { m_bLastOneInPlace = true; +#ifdef MAPBASE + m_flFadeTime = gpGlobals->curtime + m_flEndLinesFadeHoldTime; +#else // 360 certification requires that we not hold a static image too long. m_flFadeTime = gpGlobals->curtime + ( IsConsole() ? 2.0f : 10.0f ); +#endif } } else @@ -364,6 +462,50 @@ void CHudCredits::DrawOutroCreditsName( void ) if ( pCredit->bActive == false ) continue; +#ifdef MAPBASE + // Credits separated by tabs should appear divided + if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" )) + { + CUtlStringList outStrings; + V_SplitString( pCredit->szCreditName, "\t", outStrings ); + int iDivisor = 1 + outStrings.Count(); + if (pCredit->iImageID != -1) + { + FOR_EACH_VEC( outStrings, i ) + { + int iImageID = GetOrAllocateImageID( outStrings[i] ); + + // Center the image if needed + int iImageWide, iImageTall = 1; + surface()->DrawGetTextureSize( iImageID, iImageWide, iImageTall ); + if (iImageTall < iFontTall) + { + DrawOutroCreditTexture( iImageID, pCredit->flYPos + ((iFontTall * 0.5f) - (iImageTall * 0.5f)), pCredit->flImageScale, cColor, iWidth*(i + 1), iDivisor ); + } + else + { + DrawOutroCreditTexture( iImageID, pCredit->flYPos, pCredit->flImageScale, cColor, iWidth*(i + 1), iDivisor ); + } + } + } + else + { + FOR_EACH_VEC( outStrings, i ) + { + DrawOutroCreditFont( outStrings[i], pCredit->flYPos, m_hTFont, cColor, iWidth*(i + 1), iDivisor ); + } + } + outStrings.PurgeAndDeleteElements(); + } + else if (pCredit->iImageID != -1) + { + DrawOutroCreditTexture( pCredit->iImageID, pCredit->flYPos, pCredit->flImageScale, cColor, iWidth, 2 ); + } + else + { + DrawOutroCreditFont( pCredit->szCreditName, pCredit->flYPos, m_hTFont, cColor, iWidth, 2 ); + } +#else surface()->DrawSetTextFont( m_hTFont ); surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] ); @@ -382,9 +524,56 @@ void CHudCredits::DrawOutroCreditsName( void ) surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), pCredit->flYPos ); surface()->DrawUnicodeString( unicode ); +#endif } } +#ifdef MAPBASE +void CHudCredits::DrawOutroCreditFont( const char *pCreditName, float flYPos, vgui::HFont hTFont, const Color &cColor, int iScreenWidth, int iDivisor ) +{ + surface()->DrawSetTextFont( hTFont ); + surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] ); + + wchar_t unicode[256]; + + if ( pCreditName[0] == '#' ) + { + g_pVGuiLocalize->ConstructString( unicode, sizeof(unicode), g_pVGuiLocalize->Find(pCreditName), 0 ); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode( pCreditName, unicode, sizeof( unicode ) ); + } + + int iStringWidth = GetStringPixelWidth( unicode, hTFont ); + + // ((iScreenWidth*iMultiplier) / iDivisor) + // When needed, just multiply iScreenWidth before sending to the function + surface()->DrawSetTextPos( (iScreenWidth / iDivisor) - (iStringWidth / 2), flYPos ); + surface()->DrawUnicodeString( unicode ); +} + +void CHudCredits::DrawOutroCreditTexture( int iImageID, float flYPos, float flImageScale, const Color &cColor, int iScreenWidth, int iDivisor ) +{ + int iImageWide, iImageTall; + surface()->DrawGetTextureSize( iImageID, iImageWide, iImageTall ); + + // Scale for resolution + flImageScale *= ((float)GetTall() / 900.0f); + + iImageWide = ((float)(iImageWide) * flImageScale); + iImageTall = ((float)(iImageTall) * flImageScale); + + iImageWide /= 2; + //iImageTall /= 2; + iScreenWidth /= iDivisor; + + surface()->DrawSetColor( cColor ); + surface()->DrawSetTexture( iImageID ); + surface()->DrawTexturedRect( iScreenWidth - iImageWide, flYPos, iScreenWidth + iImageWide, flYPos + iImageTall ); +} +#endif + void CHudCredits::DrawLogo( void ) { if( m_iLogoState == LOGO_FADEOFF ) @@ -464,13 +653,22 @@ void CHudCredits::DrawLogo( void ) Q_snprintf( szLogoFont, sizeof( szLogoFont ), "WeaponIcons" ); } +#ifdef MAPBASE + vgui::HScheme scheme = GetScheme(); +#else vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); +#endif vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( szLogoFont ); int iFontTall = surface()->GetFontTall ( m_hTFont ); Color cColor = m_TextColor; cColor[3] = m_Alpha; + +#ifdef MAPBASE + if (m_cLogoColor.a() > 0) + cColor = m_cLogoColor; +#endif surface()->DrawSetTextFont( m_hTFont ); surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] ); @@ -485,6 +683,19 @@ void CHudCredits::DrawLogo( void ) if ( Q_strlen( m_szLogo2 ) > 0 ) { +#ifdef MAPBASE + if (m_szLogo2Font[0] != '\0') + { + m_hTFont = vgui::scheme()->GetIScheme( scheme )->GetFont( m_szLogo2Font ); + iFontTall = surface()->GetFontTall( m_hTFont ); + surface()->DrawSetTextFont( m_hTFont ); + } + if (m_cLogo2Color.a() > 0) + { + surface()->DrawSetTextColor( m_cLogo2Color[0], m_cLogo2Color[1], m_cLogo2Color[2], m_cLogo2Color[3] ); + } +#endif + g_pVGuiLocalize->ConvertANSIToUnicode( m_szLogo2, unicode, sizeof( unicode ) ); iStringWidth = GetStringPixelWidth( unicode, m_hTFont ); @@ -544,14 +755,26 @@ void CHudCredits::DrawIntroCreditsName( void ) if ( pCredit->bActive == false ) continue; - + +#ifdef MAPBASE + vgui::HScheme scheme = GetScheme(); +#else vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); +#endif vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName ); float localTime = gpGlobals->curtime - pCredit->flTimeStart; surface()->DrawSetTextFont( m_hTFont ); +#ifdef MAPBASE + Color cColor = m_cColor; + if (pCredit->cColorOverride.a() > 0) + cColor = pCredit->cColorOverride; + + surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * cColor[3] ); +#else surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * m_cColor[3] ); +#endif wchar_t unicode[256]; g_pVGuiLocalize->ConvertANSIToUnicode( pCredit->szCreditName, unicode, sizeof( unicode ) ); @@ -692,17 +915,119 @@ void CHudCredits::PrepareOutroCredits( void ) if ( pCredit == NULL ) continue; +#ifdef MAPBASE + vgui::HScheme scheme = GetScheme(); +#else vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); - vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true ); +#endif - pCredit->flYPos = iHeight; - pCredit->bActive = false; +#ifdef MAPBASE + if (pCredit->szFontName[0] == '$') + { + if (V_strncmp( pCredit->szFontName + 1, "Image", 5 ) == 0) + { + if (pCredit->szFontName[6] == ';') + { + CUtlStringList outStrings; + V_SplitString( pCredit->szFontName, ";", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + switch (i) + { + // Get scale + case 1: + pCredit->flImageScale = atof( outStrings[i] ); + break; - iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation; + // Get color + case 2: + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + break; + } + } + outStrings.PurgeAndDeleteElements(); + } - PrepareLine( m_hTFont, pCredit->szCreditName ); + // Get the size of the tallest image if there's multiple + int iFontWide, iFontTall = 1; + if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" )) + { + CUtlStringList outStrings; + V_SplitString( pCredit->szCreditName, "\t", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + pCredit->iImageID = GetOrAllocateImageID( outStrings[i] ); + + int iTempTall; + surface()->DrawGetTextureSize( pCredit->iImageID, iFontWide, iTempTall ); + if (iTempTall > iFontTall) + iFontTall = iTempTall; + } + outStrings.PurgeAndDeleteElements(); + } + else + { + pCredit->iImageID = GetOrAllocateImageID( pCredit->szCreditName ); + surface()->DrawGetTextureSize( pCredit->iImageID, iFontWide, iFontTall ); + } + + pCredit->flYPos = iHeight; + pCredit->bActive = false; + + iHeight += ((float)iFontTall * pCredit->flImageScale * ((float)GetTall() / 900.0f)) + m_flSeparation; + + //Msg( "'%s' is image type (image scale is %f)\n", pCredit->szCreditName, pCredit->flImageScale ); + } + else + { + //Msg( "'%s' is not an image type\n", pCredit->szFontName + 1 ); + } + } + else +#endif + { +#ifdef MAPBASE + if (V_strstr( pCredit->szFontName, ";" )) + { + CUtlStringList outStrings; + V_SplitString( pCredit->szFontName, ";", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + switch (i) + { + // Get color + case 1: + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + break; + } + } + + Q_strncpy( pCredit->szFontName, outStrings[0], sizeof( pCredit->szFontName ) ); + outStrings.PurgeAndDeleteElements(); + } +#endif + + vgui::HFont m_hTFont = vgui::scheme()->GetIScheme( scheme )->GetFont( pCredit->szFontName, true ); + + pCredit->flYPos = iHeight; + pCredit->bActive = false; + + iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation; + + PrepareLine( m_hTFont, pCredit->szCreditName ); + } } +#ifdef MAPBASE + // Check if the last line has a color override. If it does, use that as the alpha for the fadeout + if (m_CreditsList.Tail().cColorOverride.a() != 0) + m_Alpha = m_CreditsList.Tail().cColorOverride.a(); +#endif + SetActive( true ); g_iCreditsPixelHeight = iHeight; @@ -721,7 +1046,34 @@ void CHudCredits::PrepareIntroCredits( void ) if ( pCredit == NULL ) continue; +#ifdef MAPBASE + if (V_strstr( pCredit->szFontName, ";" )) + { + CUtlStringList outStrings; + V_SplitString( pCredit->szFontName, ";", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + switch (i) + { + // Get color + case 1: + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + break; + } + } + + Q_strncpy( pCredit->szFontName, outStrings[0], sizeof( pCredit->szFontName ) ); + outStrings.PurgeAndDeleteElements(); + } +#endif + +#ifdef MAPBASE + vgui::HScheme scheme = GetScheme(); +#else vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); +#endif vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName ); pCredit->flYPos = m_flY + ( iSlot * surface()->GetFontTall ( m_hTFont ) ); @@ -749,6 +1101,64 @@ void CHudCredits::PrepareIntroCredits( void ) SetActive( true ); } +#ifdef MAPBASE +void CHudCredits::PrecacheCredits() +{ + PrepareCredits( "OutroCreditsNames" ); + + if ( m_CreditsList.Count() == 0 ) + return; + + for ( int i = 0; i < m_CreditsList.Count(); i++ ) + { + creditname_t *pCredit = &m_CreditsList[i]; + + if ( pCredit == NULL ) + continue; + + if (pCredit->szFontName[0] == '$') + { + if (V_strncmp( pCredit->szFontName + 1, "Image", 5 ) == 0) + { + if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" )) + { + CUtlStringList outStrings; + V_SplitString( pCredit->szCreditName, "\t", outStrings ); + FOR_EACH_VEC( outStrings, i ) + { + GetOrAllocateImageID( outStrings[i] ); + } + outStrings.PurgeAndDeleteElements(); + } + else + { + GetOrAllocateImageID( pCredit->szCreditName ); + } + } + else + { + //Msg( "'%s' is not an image type\n", pCredit->szFontName + 1 ); + } + } + } + + m_CreditsList.RemoveAll(); +} + +int CHudCredits::GetOrAllocateImageID( const char *szFileName ) +{ + int iIndex = m_ImageDict.Find( szFileName ); + if (iIndex == m_ImageDict.InvalidIndex()) + { + iIndex = surface()->CreateNewTextureID(); + m_ImageDict.Insert( szFileName, iIndex ); + surface()->DrawSetTextureFile( iIndex, szFileName, true, false ); + return iIndex; + } + return m_ImageDict[iIndex]; +} +#endif + void CHudCredits::MsgFunc_CreditsMsg( bf_read &msg ) { m_iCreditsType = msg.ReadByte(); @@ -777,6 +1187,13 @@ void CHudCredits::MsgFunc_CreditsMsg( bf_read &msg ) PrepareOutroCredits(); break; } +#ifdef MAPBASE + case CREDITS_PRECACHE: + { + PrecacheCredits(); + break; + } +#endif } } diff --git a/mp/src/game/client/hl2/hud_weaponselection.cpp b/mp/src/game/client/hl2/hud_weaponselection.cpp index f5600705..b83d6429 100644 --- a/mp/src/game/client/hl2/hud_weaponselection.cpp +++ b/mp/src/game/client/hl2/hud_weaponselection.cpp @@ -644,6 +644,15 @@ void CHudWeaponSelection::Paint() // This is a bit of a misnomer... we really are asking "Is this the selected slot"? selectedWeapon = true; } +#ifdef MAPBASE + else if (!hud_showemptyweaponslots.GetBool() && !pWeapon) + { + // Revert the offset + xPos -= ( m_flMediumBoxWide + 5 ) * xModifiers[ i ]; + yPos -= ( m_flMediumBoxTall + 5 ) * yModifiers[ i ]; + continue; + } +#endif // Draw the box with the appropriate icon DrawLargeWeaponBox( pWeapon, @@ -1375,6 +1384,23 @@ void CHudWeaponSelection::PlusTypeFastWeaponSwitch( int iWeaponSlot ) // Changing vertical/horizontal direction. Reset the selected box position to zero. m_iSelectedBoxPosition = 0; m_iSelectedSlot = iWeaponSlot; + +#ifdef MAPBASE + if (!hud_showemptyweaponslots.GetBool()) + { + // Skip empty slots + int i = 0; + while ( i < MAX_WEAPON_POSITIONS ) + { + C_BaseCombatWeapon *pWeapon = GetWeaponInSlot( iWeaponSlot, i ); + if ( pWeapon ) + break; + i++; + } + + m_iSelectedBoxPosition = i; + } +#endif } else { @@ -1385,6 +1411,27 @@ void CHudWeaponSelection::PlusTypeFastWeaponSwitch( int iWeaponSlot ) // Decrementing within the slot. If we're at the zero position in this slot, // jump to the zero position of the opposite slot. This also counts as our increment. increment = -1; +#ifdef MAPBASE + if (!hud_showemptyweaponslots.GetBool()) + { + // Skip empty slots + int iZeroPos = 0; + while ( iZeroPos < MAX_WEAPON_POSITIONS ) + { + C_BaseCombatWeapon *pWeapon = GetWeaponInSlot( m_iSelectedSlot, iZeroPos ); + if ( pWeapon ) + break; + iZeroPos++; + } + + if ( iZeroPos == m_iSelectedBoxPosition ) + { + newSlot = ( m_iSelectedSlot + 2 ) % 4; + m_iSelectedBoxPosition = increment = 0; + } + } + else +#endif if ( 0 == m_iSelectedBoxPosition ) { newSlot = ( m_iSelectedSlot + 2 ) % 4; @@ -1402,6 +1449,35 @@ void CHudWeaponSelection::PlusTypeFastWeaponSwitch( int iWeaponSlot ) lastSlotPos = slotPos; } } + +#ifdef MAPBASE + if (!hud_showemptyweaponslots.GetBool()) + { + // Skip empty slots + int i = m_iSelectedBoxPosition + increment; + while ( i >= 0 && i < lastSlotPos ) + { + C_BaseCombatWeapon *pWeapon = GetWeaponInSlot( newSlot, i ); + if ( !pWeapon ) + { + if (increment < 0) + { + increment--; + i--; + } + else + { + increment++; + i++; + } + } + else + { + break; + } + } + } +#endif // Increment/Decrement the selected box position if ( m_iSelectedBoxPosition + increment <= lastSlotPos ) diff --git a/mp/src/game/client/hud_closecaption.cpp b/mp/src/game/client/hud_closecaption.cpp index 7490d00d..4ed03e5e 100644 --- a/mp/src/game/client/hud_closecaption.cpp +++ b/mp/src/game/client/hud_closecaption.cpp @@ -1303,7 +1303,7 @@ void CHudCloseCaption::Reset( void ) Unlock(); } -bool CHudCloseCaption::SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t *args ) const +bool CHudCloseCaption::SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t *args, int size ) const { const wchar_t *in = *ppIn; const wchar_t *oldin = in; @@ -1318,8 +1318,11 @@ bool CHudCloseCaption::SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t cmd[ 0 ]= 0; wchar_t *out = cmd; in++; - while ( *in != L'\0' && *in != L':' && *in != L'>' && !isspace( *in ) ) + while ( *in != L'\0' && *in != L':' && *in != L'>' && !V_isspace( *in ) ) { + if ( (int)( out - cmd ) + (int)sizeof( wchar_t ) >= size ) + break; + *out++ = *in++; } *out = L'\0'; @@ -1334,6 +1337,9 @@ bool CHudCloseCaption::SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t out = args; while ( *in != L'\0' && *in != L'>' ) { + if ( (int)( out - args ) + (int)sizeof( wchar_t ) >= size ) + break; + *out++ = *in++; } *out = L'\0'; @@ -1361,7 +1367,7 @@ bool CHudCloseCaption::GetFloatCommandValue( const wchar_t *stream, const wchar_ wchar_t cmd[ 256 ]; wchar_t args[ 256 ]; - if ( SplitCommand( &curpos, cmd, args ) ) + if ( SplitCommand( &curpos, cmd, args, sizeof( cmd ) ) ) { if ( !wcscmp( cmd, findcmd ) ) { @@ -1385,7 +1391,7 @@ bool CHudCloseCaption::StreamHasCommand( const wchar_t *stream, const wchar_t *f wchar_t cmd[ 256 ]; wchar_t args[ 256 ]; - if ( SplitCommand( &curpos, cmd, args ) ) + if ( SplitCommand( &curpos, cmd, args, sizeof( cmd ) ) ) { if ( !wcscmp( cmd, findcmd ) ) { @@ -1424,7 +1430,7 @@ bool CHudCloseCaption::StreamHasCommand( const wchar_t *stream, const wchar_t *s wchar_t cmd[ 256 ]; wchar_t args[ 256 ]; - if ( SplitCommand( &curpos, cmd, args ) ) + if ( SplitCommand( &curpos, cmd, args, sizeof( cmd ) ) ) { if ( !wcscmp( cmd, search ) ) { @@ -1516,7 +1522,7 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha const wchar_t *prevpos = curpos; - if ( SplitCommand( &curpos, cmd, args ) ) + if ( SplitCommand( &curpos, cmd, args, sizeof( cmd ) ) ) { if ( !wcscmp( cmd, L"delay" ) ) { @@ -1613,6 +1619,9 @@ struct WorkUnitParams clr = Color( 255, 255, 255, 255 ); newline = false; font = 0; +#ifdef MAPBASE + customFont = false; +#endif } ~WorkUnitParams() @@ -1658,6 +1667,9 @@ struct WorkUnitParams Color clr; bool newline; vgui::HFont font; +#ifdef MAPBASE + bool customFont; +#endif }; void CHudCloseCaption::AddWorkUnit( CCloseCaptionItem *item, @@ -1717,7 +1729,7 @@ void CHudCloseCaption::ComputeStreamWork( int available_width, CCloseCaptionItem wchar_t cmd[ 256 ]; wchar_t args[ 256 ]; - if ( SplitCommand( &curpos, cmd, args ) ) + if ( SplitCommand( &curpos, cmd, args, sizeof( cmd ) ) ) { if ( !wcscmp( cmd, L"cr" ) ) { @@ -1772,27 +1784,58 @@ void CHudCloseCaption::ComputeStreamWork( int available_width, CCloseCaptionItem { AddWorkUnit( item, params ); params.italic = !params.italic; +#ifdef MAPBASE + params.customFont = false; +#endif } else if ( !wcscmp( cmd, L"B" ) ) { AddWorkUnit( item, params ); params.bold = !params.bold; +#ifdef MAPBASE + params.customFont = false; +#endif } +#ifdef MAPBASE + else if ( !wcscmp( cmd, L"font" ) ) + { + AddWorkUnit( item, params ); + vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + + if ( args[0] != 0 ) + { + char font[64]; + g_pVGuiLocalize->ConvertUnicodeToANSI( args, font, sizeof( font ) ); + params.font = pScheme->GetFont( font ); + params.customFont = true; + } + else + { + params.customFont = false; + } + } +#endif continue; } - int font; - if ( IsPC() ) + vgui::HFont useF = params.font; +#ifdef MAPBASE + if (params.customFont == false) +#endif { - font = params.GetFontNumber(); + int font; + if ( IsPC() ) + { + font = params.GetFontNumber(); + } + else + { + font = streamlen >= cc_smallfontlength.GetInt() ? CCFONT_SMALL : CCFONT_NORMAL; + } + useF = m_hFonts[font]; + params.font = useF; } - else - { - font = streamlen >= cc_smallfontlength.GetInt() ? CCFONT_SMALL : CCFONT_NORMAL; - } - vgui::HFont useF = m_hFonts[font]; - params.font = useF; int w, h; @@ -1940,7 +1983,7 @@ bool CHudCloseCaption::GetNoRepeatValue( const wchar_t *caption, float &retval ) wchar_t cmd[ 256 ]; wchar_t args[ 256 ]; - if ( SplitCommand( &curpos, cmd, args ) ) + if ( SplitCommand( &curpos, cmd, args, sizeof( cmd ) ) ) { if ( !wcscmp( cmd, L"norepeat" ) ) { @@ -2631,6 +2674,124 @@ void CHudCloseCaption::InitCaptionDictionary( const char *dbfile ) g_AsyncCaptionResourceManager.SetDbInfo( m_AsyncCaptions ); } +#ifdef MAPBASE +void CHudCloseCaption::AddAdditionalCaptionDictionary( const char *dbfile, CUtlVector &outPathSymbols ) +{ + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Adding additional caption dictionary \"%s\"\n", dbfile ); + + g_AsyncCaptionResourceManager.Clear(); + + char searchPaths[4096]; + filesystem->GetSearchPath( "MOD", true, searchPaths, sizeof( searchPaths ) ); + + for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) ) + { + if ( IsX360() && ( filesystem->GetDVDMode() == DVDMODE_STRICT ) && !V_stristr( path, ".zip" ) ) + { + // only want zip paths + continue; + } + + char fullpath[MAX_PATH]; + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", path, dbfile ); + Q_FixSlashes( fullpath ); + + if ( IsX360() ) + { + char fullpath360[MAX_PATH]; + UpdateOrCreateCaptionFile( fullpath, fullpath360, sizeof( fullpath360 ) ); + Q_strncpy( fullpath, fullpath360, sizeof( fullpath ) ); + } + + // Seach for this dictionary. If it already exists, remove it. + for (int i = 0; i < m_AsyncCaptions.Count(); ++i) + { + if (FStrEq( m_AsyncCaptions[i].m_DataBaseFile.String(), fullpath )) + { + m_AsyncCaptions.Remove( i ); + break; + } + } + + FileHandle_t fh = filesystem->Open( fullpath, "rb" ); + if ( FILESYSTEM_INVALID_HANDLE != fh ) + { + MEM_ALLOC_CREDIT(); + + CUtlBuffer dirbuffer; + + AsyncCaption_t& entry = m_AsyncCaptions[ m_AsyncCaptions.AddToTail() ]; + + // Read the header + filesystem->Read( &entry.m_Header, sizeof( entry.m_Header ), fh ); + if ( entry.m_Header.magic != COMPILED_CAPTION_FILEID ) + Error( "Invalid file id for %s\n", fullpath ); + if ( entry.m_Header.version != COMPILED_CAPTION_VERSION ) + Error( "Invalid file version for %s\n", fullpath ); + if ( entry.m_Header.directorysize < 0 || entry.m_Header.directorysize > 64 * 1024 ) + Error( "Invalid directory size %d for %s\n", entry.m_Header.directorysize, fullpath ); + //if ( entry.m_Header.blocksize != MAX_BLOCK_SIZE ) + // Error( "Invalid block size %d, expecting %d for %s\n", entry.m_Header.blocksize, MAX_BLOCK_SIZE, fullpath ); + + int directoryBytes = entry.m_Header.directorysize * sizeof( CaptionLookup_t ); + entry.m_CaptionDirectory.EnsureCapacity( entry.m_Header.directorysize ); + dirbuffer.EnsureCapacity( directoryBytes ); + + filesystem->Read( dirbuffer.Base(), directoryBytes, fh ); + filesystem->Close( fh ); + + entry.m_CaptionDirectory.CopyArray( (const CaptionLookup_t *)dirbuffer.PeekGet(), entry.m_Header.directorysize ); + entry.m_CaptionDirectory.RedoSort( true ); + + entry.m_DataBaseFile = fullpath; + outPathSymbols.AddToTail( entry.m_DataBaseFile ); + } + } + + g_AsyncCaptionResourceManager.SetDbInfo( m_AsyncCaptions ); +} + +void CHudCloseCaption::AddCustomCaptionFile( char const *file, CUtlVector &outPathSymbols ) +{ + // + // 'file' should be something like "maps/mapbase_demo01_closecaption_%language%" + // + + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Adding custom caption file \"%s\"\n", file ); + + if (!IsX360()) + { + g_pVGuiLocalize->AddFile( file, "MOD", true ); + } + + char uilanguage[64]; + engine->GetUILanguage( uilanguage, sizeof( uilanguage ) ); + + char dbfile[512]; + V_StrSubst( file, "%language%", uilanguage, dbfile, sizeof( dbfile ) ); + V_SetExtension( dbfile, ".dat", sizeof( dbfile ) ); + AddAdditionalCaptionDictionary( dbfile, outPathSymbols ); +} + +void CHudCloseCaption::RemoveCaptionDictionary( const CUtlSymbol &dbFileSymbol ) +{ + // + // 'file' should be something like "maps/mapbase_demo01_closecaption_%language%" + // + + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Removing custom caption file \"%s\"\n", dbFileSymbol.String() ); + + for (int i = 0; i < m_AsyncCaptions.Count(); ++i) + { + if ( m_AsyncCaptions[i].m_DataBaseFile == dbFileSymbol ) + { + m_AsyncCaptions.Remove( i ); + break; + } + } +} +#endif + void CHudCloseCaption::OnFinishAsyncLoad( int nFileIndex, int nBlockNum, AsyncCaptionData_t *pData ) { // Fill in data for all users of pData->m_nBlockNum diff --git a/mp/src/game/client/hud_closecaption.h b/mp/src/game/client/hud_closecaption.h index 180afe9d..8688e60e 100644 --- a/mp/src/game/client/hud_closecaption.h +++ b/mp/src/game/client/hud_closecaption.h @@ -111,6 +111,11 @@ public: void PlayRandomCaption(); void InitCaptionDictionary( char const *dbfile ); +#ifdef MAPBASE + void AddAdditionalCaptionDictionary( char const *dbfile, CUtlVector &outPathSymbols ); + void AddCustomCaptionFile( char const *file, CUtlVector &outPathSymbols ); + void RemoveCaptionDictionary( const CUtlSymbol &dbFileSymbol ); +#endif void OnFinishAsyncLoad( int nFileIndex, int nBlockNum, AsyncCaptionData_t *pData ); void Flush(); @@ -133,6 +138,11 @@ public: void FindSound( char const *pchANSI ); +#ifdef MAPBASE + inline bool IsUsingCommentaryDimensions() const { return m_bUsingCommentaryDimensions; } + inline void SetUsingCommentaryDimensions( bool bToggle ) { m_bUsingCommentaryDimensions = bToggle; } +#endif + public: struct CaptionRepeat @@ -169,7 +179,7 @@ private: void DrawStream( wrect_t& rect, wrect_t &rcWindow, CCloseCaptionItem *item, int iFadeLine, float flFadeLineAlpha ); void ComputeStreamWork( int available_width, CCloseCaptionItem *item ); - bool SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t *args ) const; + bool SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t *args, int size ) const; bool StreamHasCommand( const wchar_t *stream, const wchar_t *findcmd ) const; bool GetFloatCommandValue( const wchar_t *stream, const wchar_t *findcmd, float& value ) const; @@ -212,6 +222,10 @@ private: bool m_bVisibleDueToDirect; bool m_bPaintDebugInfo; CUtlSymbol m_CurrentLanguage; + +#ifdef MAPBASE + bool m_bUsingCommentaryDimensions; +#endif }; #endif // HUD_CLOSECAPTION_H diff --git a/mp/src/game/client/hud_lcd.cpp b/mp/src/game/client/hud_lcd.cpp index 0f0609d6..d0425d6b 100644 --- a/mp/src/game/client/hud_lcd.cpp +++ b/mp/src/game/client/hud_lcd.cpp @@ -528,15 +528,15 @@ void CLCD::ShowItems_R( CLCDPage *page, unsigned int dwCurTime, CUtlVector< CLCD { CLCDItem *newItem = NULL; - CLCDItem *item = ag->m_Definition[ r ]; - switch ( item->m_Type ) + CLCDItem *itemLocl = ag->m_Definition[ r ]; + switch ( itemLocl->m_Type ) { default: break; case LCDITEM_TEXT: { - CLCDItemText *text = static_cast< CLCDItemText * >( item ); + CLCDItemText *text = static_cast< CLCDItemText * >( itemLocl ); CUtlString s; s = text->m_OriginalText; Replace( s, prefix, s1 ); @@ -551,7 +551,7 @@ void CLCD::ShowItems_R( CLCDPage *page, unsigned int dwCurTime, CUtlVector< CLCD // text->m_OriginalText = s; - CLCDItemText *copy = static_cast< CLCDItemText * >( page->Alloc( item->m_Type ) ); + CLCDItemText *copy = static_cast< CLCDItemText * >( page->Alloc( itemLocl->m_Type ) ); *copy = *text; copy->m_bActive = true; copy->m_OriginalText = s; @@ -564,8 +564,8 @@ void CLCD::ShowItems_R( CLCDPage *page, unsigned int dwCurTime, CUtlVector< CLCD break; case LCDITEM_ICON: { - CLCDItemIcon *icon = static_cast< CLCDItemIcon * >( item ); - CLCDItemIcon *copy = static_cast< CLCDItemIcon * >( page->Alloc( item->m_Type ) ); + CLCDItemIcon *icon = static_cast< CLCDItemIcon * >( itemLocl ); + CLCDItemIcon *copy = static_cast< CLCDItemIcon * >( page->Alloc( itemLocl->m_Type ) ); *copy = *icon; copy->m_bActive = true; copy->Create( m_lcd ); @@ -1186,16 +1186,16 @@ void CLCD::DumpPlayer() C_Team *team = player->GetTeam(); if ( team ) { - CDescribeData helper( team ); - helper.DumpDescription( team->GetPredDescMap() ); + CDescribeData helperLocl( team ); + helperLocl.DumpDescription( team->GetPredDescMap() ); } Msg( "(playerresource)\n\n" ); if ( g_PR ) { - CDescribeData helper( g_PR ); - helper.DumpDescription( g_PR->GetPredDescMap() ); + CDescribeData helperLocl( g_PR ); + helperLocl.DumpDescription( g_PR->GetPredDescMap() ); } Msg( "(localplayerweapon)\n\n" ); @@ -1203,8 +1203,8 @@ void CLCD::DumpPlayer() C_BaseCombatWeapon *active = player->GetActiveWeapon(); if ( active ) { - CDescribeData helper( active ); - helper.DumpDescription( active->GetPredDescMap() ); + CDescribeData helperLocl( active ); + helperLocl.DumpDescription( active->GetPredDescMap() ); } Msg( "Other replacements:\n\n" ); diff --git a/mp/src/game/client/hud_locator_target.cpp b/mp/src/game/client/hud_locator_target.cpp index 8f0d4c0c..8479b07e 100644 --- a/mp/src/game/client/hud_locator_target.cpp +++ b/mp/src/game/client/hud_locator_target.cpp @@ -10,7 +10,7 @@ #include "iclientmode.h" #include #include -#include +#include #include #include #include diff --git a/mp/src/game/client/hud_locator_target.h b/mp/src/game/client/hud_locator_target.h index 06799325..69939dbb 100644 --- a/mp/src/game/client/hud_locator_target.h +++ b/mp/src/game/client/hud_locator_target.h @@ -34,7 +34,7 @@ #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" +#include "tier1/utlsymbol.h" // See comments in UtlSymbol on why this is useful DECLARE_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol ); diff --git a/mp/src/game/client/hud_pdump.cpp b/mp/src/game/client/hud_pdump.cpp index 612a18d7..56ac16f4 100644 --- a/mp/src/game/client/hud_pdump.cpp +++ b/mp/src/game/client/hud_pdump.cpp @@ -21,9 +21,15 @@ static CPDumpPanel *g_pPDumpPanel = NULL; // we pragma'd away in platform.h, so this little compiler specific hack will eliminate those warnings while // retaining our own warning setup...ywb #ifdef WIN32 + +#if _MSC_VER < 1900 #pragma warning( push ) #include #pragma warning( pop ) +#else +#include +#endif + #endif using namespace vgui; diff --git a/mp/src/game/client/hudelement.h b/mp/src/game/client/hudelement.h index b903de5d..824cb975 100644 --- a/mp/src/game/client/hudelement.h +++ b/mp/src/game/client/hudelement.h @@ -58,6 +58,9 @@ public: // Hidden bits. // HIDEHUD_ flags that note when this element should be hidden in the HUD virtual void SetHiddenBits( int iBits ); +#ifdef MAPBASE_VSCRIPT + int GetHiddenBits() const { return m_iHiddenBits; } +#endif bool IsParentedToClientDLLRootPanel() const; void SetParentedToClientDLLRootPanel( bool parented ); diff --git a/mp/src/game/client/iclientmode.h b/mp/src/game/client/iclientmode.h index 2b74f625..38b60047 100644 --- a/mp/src/game/client/iclientmode.h +++ b/mp/src/game/client/iclientmode.h @@ -112,6 +112,11 @@ public: virtual bool CanRecordDemo( char *errorMsg, int length ) const = 0; +#ifdef MAPBASE // From Alien Swarm SDK + virtual void OnColorCorrectionWeightsReset( void ) = 0; + virtual float GetColorCorrectionScale( void ) const = 0; +#endif + virtual void ComputeVguiResConditions( KeyValues *pkvConditions ) = 0; //============================================================================= diff --git a/mp/src/game/client/in_joystick.cpp b/mp/src/game/client/in_joystick.cpp index a3b63620..eebe3d83 100644 --- a/mp/src/game/client/in_joystick.cpp +++ b/mp/src/game/client/in_joystick.cpp @@ -25,7 +25,9 @@ #include "tier0/icommandline.h" #include "inputsystem/iinputsystem.h" #include "inputsystem/ButtonCode.h" +#if _MSC_VER < 1900 #include "math.h" +#endif #include "tier1/convar_serverbounded.h" #include "cam_thirdperson.h" diff --git a/mp/src/game/client/iviewrender.h b/mp/src/game/client/iviewrender.h index c66061ae..8d797dea 100644 --- a/mp/src/game/client/iviewrender.h +++ b/mp/src/game/client/iviewrender.h @@ -115,6 +115,13 @@ public: virtual void SetScreenOverlayMaterial( IMaterial *pMaterial ) = 0; virtual IMaterial *GetScreenOverlayMaterial( ) = 0; +#ifdef MAPBASE + virtual void SetIndexedScreenOverlayMaterial( int i, IMaterial *pMaterial ) = 0; + virtual IMaterial *GetIndexedScreenOverlayMaterial( int i ) = 0; + virtual void ResetIndexedScreenOverlays() = 0; + virtual int GetMaxIndexedScreenOverlays() const = 0; +#endif + virtual void WriteSaveGameScreenshot( const char *pFilename ) = 0; virtual void WriteSaveGameScreenshotOfSize( const char *pFilename, int width, int height, bool bCreatePowerOf2Padded = false, bool bWriteVTF = false ) = 0; diff --git a/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp b/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp index 9d92675e..703d5a88 100644 --- a/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp +++ b/mp/src/game/client/mapbase/c_func_fake_worldportal.cpp @@ -1,6 +1,6 @@ //========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // -// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// Purpose: Recreates Portal 2 linked_portal_door visual functionality using SDK code only. // (basically a combination of point_camera and func_reflective_glass) // // $NoKeywords: $ @@ -58,70 +58,11 @@ bool C_FuncFakeWorldPortal::ShouldDraw() } -//----------------------------------------------------------------------------- -// 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 ) +C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, + Vector &vecAbsPlaneNormal, float &flLocalPlaneDist, const Frustum_t &frustum ) { // Early out if no cameras C_FuncFakeWorldPortal *pReflectiveGlass = NULL; @@ -130,8 +71,9 @@ C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const else pReflectiveGlass = pStart->m_pNext; - cplane_t localPlane; - Vector vecOrigin, vecWorld, vecDelta; + cplane_t localPlane, worldPlane; + Vector vecMins, vecMaxs, vecLocalOrigin, vecAbsOrigin, vecDelta; + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) { if ( pReflectiveGlass->IsDormant() ) @@ -140,7 +82,10 @@ C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) continue; - Vector vecMins, vecMaxs; + // Must have valid plane + if ( !pReflectiveGlass->m_hTargetPlane ) + continue; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); if ( R_CullBox( vecMins, vecMaxs, frustum ) ) continue; @@ -151,21 +96,21 @@ C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); for ( int i = 0; i < nCount; ++i ) { - modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecLocalOrigin ); - MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space - VectorTransform( vecOrigin, mat, vecWorld ); + MatrixTransformPlane( mat, localPlane, worldPlane ); // Transform to world space - if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + if ( view.origin.Dot( worldPlane.normal ) <= worldPlane.dist ) // Check for view behind plane continue; - VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull - if ( vecDelta.Dot( plane.normal ) >= 0 ) + VectorTransform( vecLocalOrigin, mat, vecAbsOrigin ); + VectorSubtract( vecAbsOrigin, view.origin, vecDelta ); + + if ( vecDelta.Dot( worldPlane.normal ) >= 0 ) // Backface cull continue; - // Must have valid plane - if ( !pReflectiveGlass->m_hTargetPlane ) - continue; + flLocalPlaneDist = localPlane.dist; + vecAbsPlaneNormal = worldPlane.normal; return pReflectiveGlass; } diff --git a/mp/src/game/client/mapbase/c_func_fake_worldportal.h b/mp/src/game/client/mapbase/c_func_fake_worldportal.h index 3fc4418b..bc1d9e61 100644 --- a/mp/src/game/client/mapbase/c_func_fake_worldportal.h +++ b/mp/src/game/client/mapbase/c_func_fake_worldportal.h @@ -53,10 +53,8 @@ public: //----------------------------------------------------------------------------- // 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 ); +C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, + Vector &vecAbsPlaneNormal, float &flLocalPlaneDist, const Frustum_t &frustum ); #endif // C_FUNC_FAKE_WORLDPORTAL diff --git a/mp/src/game/client/mapbase/c_vgui_text_display.cpp b/mp/src/game/client/mapbase/c_vgui_text_display.cpp new file mode 100644 index 00000000..f0d2032d --- /dev/null +++ b/mp/src/game/client/mapbase/c_vgui_text_display.cpp @@ -0,0 +1,282 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Displays easy, flexible VGui text. Mapbase equivalent of point_worldtext. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "panelmetaclassmgr.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include +#include +#include +#include "ienginevgui.h" +#include "c_vguiscreen.h" +#include "vgui_bitmapbutton.h" +#include "vgui_bitmappanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// vgui_text_display +//----------------------------------------------------------------------------- +class C_VGuiTextDisplay : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_VGuiTextDisplay, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_VGuiTextDisplay(); + ~C_VGuiTextDisplay(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + + bool IsEnabled( void ) const { return m_bEnabled; } + + const char *GetDisplayText( void ) const { return m_szDisplayText; } + const char *GetFontName( void ) const { return m_szFont; } + int GetResolution( void ) const { return m_iResolution; } + vgui::Label::Alignment GetContentAlignment() const { return m_iContentAlignment; } + + bool NeedsTextUpdate() { return m_bTextNeedsUpdate; } + void UpdatedText() { m_bTextNeedsUpdate = false; } + +private: + bool m_bEnabled; + char m_szDisplayText[256]; + vgui::Label::Alignment m_iContentAlignment; + char m_szFont[64]; + int m_iResolution; + + bool m_bTextNeedsUpdate; +}; + +IMPLEMENT_CLIENTCLASS_DT( C_VGuiTextDisplay, DT_VGuiTextDisplay, CVGuiTextDisplay ) + RecvPropBool( RECVINFO( m_bEnabled ) ), + RecvPropString( RECVINFO( m_szDisplayText ) ), + RecvPropInt( RECVINFO( m_iContentAlignment ) ), + RecvPropString( RECVINFO( m_szFont ) ), + RecvPropInt( RECVINFO( m_iResolution ) ), +END_RECV_TABLE() + +C_VGuiTextDisplay::C_VGuiTextDisplay() +{ +} + +C_VGuiTextDisplay::~C_VGuiTextDisplay() +{ +} + +void C_VGuiTextDisplay::PostDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PostDataUpdate( updateType ); + + // For now, always update + m_bTextNeedsUpdate = true; +} + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class C_TextDisplayPanel : public CVGuiScreenPanel +{ + DECLARE_CLASS( C_TextDisplayPanel, CVGuiScreenPanel ); + +public: + C_TextDisplayPanel( vgui::Panel *parent, const char *panelName ); + ~C_TextDisplayPanel( void ); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + void UpdateText(); + + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick( void ); + virtual void Paint( void ); + +private: + + CHandle m_hVGUIScreen; + CHandle m_hScreenEntity; + + // VGUI specifics + Label *m_pDisplayTextLabel; +}; + +DECLARE_VGUI_SCREEN_FACTORY( C_TextDisplayPanel, "text_display_panel" ); + +CUtlVector g_TextDisplays; + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +C_TextDisplayPanel::C_TextDisplayPanel( vgui::Panel *parent, const char *panelName ) +: BaseClass( parent, "C_TextDisplayPanel"/*, vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/WorldTextPanel.res", "WorldTextPanel" )*/ ) +{ + // Add ourselves to the global list of movie displays + g_TextDisplays.AddToTail( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Clean up the movie +//----------------------------------------------------------------------------- +C_TextDisplayPanel::~C_TextDisplayPanel( void ) +{ + // Remove ourselves from the global list of movie displays + g_TextDisplays.FindAndRemove( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup our scheme +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + /* + m_pDisplayTextLabel->SetFgColor( Color( 255, 255, 255, 255 ) ); + m_pDisplayTextLabel->SetText( "" ); + m_pDisplayTextLabel->SetVisible( false ); + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::UpdateText() +{ + color32 clr = m_hScreenEntity->GetRenderColor(); + + m_pDisplayTextLabel->SetFgColor( Color( clr.r, clr.g, clr.b, clr.a ) ); + m_pDisplayTextLabel->SetText( m_hScreenEntity->GetDisplayText() ); + + //SetSize( m_hScreenEntity->GetTextSize(), m_hScreenEntity->GetTextSize() ); + //m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetTextSize(), m_hScreenEntity->GetTextSize() ); + + Label::Alignment iAlignment = m_hScreenEntity->GetContentAlignment(); + + switch (iAlignment) + { + // Use a special scaling method when using a south alignment + case Label::Alignment::a_southwest: + case Label::Alignment::a_south: + case Label::Alignment::a_southeast: + int lW, lT; + m_pDisplayTextLabel->GetContentSize( lW, lT ); + SetSize( m_hScreenEntity->GetResolution(), lT ); + m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetResolution(), lT ); + + float sW, sT; + m_hVGUIScreen->GetSize( sW, sT ); + //Msg( "Screen width: %f, new height: %f\n", sW, sW * (lT / m_hScreenEntity->GetResolution()) ); + m_hVGUIScreen->SetHeight( sW * ((float)lT / (float)m_hScreenEntity->GetResolution()) ); + m_hVGUIScreen->SetPixelHeight( lT ); + break; + + default: + SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() ); + m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() ); + break; + } + + m_pDisplayTextLabel->SetContentAlignment( iAlignment ); + + bool bWrap = true; + bool bCenterWrap = false; + switch (iAlignment) + { + // Center wrap if centered + case Label::Alignment::a_north: + case Label::Alignment::a_center: + case Label::Alignment::a_south: + bCenterWrap = true; + break; + + // HACKHACK: Don't wrap if using an east alignment + case Label::Alignment::a_northeast: + case Label::Alignment::a_east: + case Label::Alignment::a_southeast: + bWrap = false; + break; + } + + m_pDisplayTextLabel->SetWrap( bWrap ); + m_pDisplayTextLabel->SetCenterWrap( bCenterWrap ); + + //Msg( "Resolution is %i\n", m_hScreenEntity->GetResolution() ); + + const char *pszFontName = m_hScreenEntity->GetFontName(); + if (pszFontName && pszFontName[0] != '\0') + { + HFont font = scheme()->GetIScheme( GetScheme() )->GetFont( pszFontName ); + m_pDisplayTextLabel->SetFont( font ); + } + + m_pDisplayTextLabel->SetVisible( true ); +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool C_TextDisplayPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + if ( !BaseClass::Init( pKeyValues, pInitData ) ) + return false; + + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + m_pDisplayTextLabel = dynamic_cast(FindChildByName( "TextDisplay" )); + + // Save this for simplicity later on + m_hVGUIScreen = dynamic_cast( GetEntity() ); + if ( m_hVGUIScreen != NULL ) + { + // Also get the associated entity + m_hScreenEntity = dynamic_cast(m_hVGUIScreen->GetOwnerEntity()); + UpdateText(); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Update the display string +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::OnTick() +{ + if (m_hScreenEntity->NeedsTextUpdate()) + { + UpdateText(); + m_hScreenEntity->UpdatedText(); + } + + BaseClass::OnTick(); +} + +ConVar r_vguitext_bg( "r_vguitext_bg", "0" ); + +//----------------------------------------------------------------------------- +// Purpose: Update and draw the frame +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::Paint( void ) +{ + // Black out the background (we could omit drawing under the video surface, but this is straight-forward) + if ( r_vguitext_bg.GetBool() ) + { + surface()->DrawSetColor( 0, 0, 0, 255 ); + surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); + + //surface()->DrawSetColor( 64, 64, 64, 255 ); + //surface()->DrawFilledRect( 0, 0, m_pDisplayTextLabel->GetWide(), m_pDisplayTextLabel->GetTall() ); + } + + // Parent's turn + BaseClass::Paint(); +} diff --git a/mp/src/game/client/mapbase/mapbase_autocubemap.cpp b/mp/src/game/client/mapbase/mapbase_autocubemap.cpp new file mode 100644 index 00000000..03602ef2 --- /dev/null +++ b/mp/src/game/client/mapbase/mapbase_autocubemap.cpp @@ -0,0 +1,280 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: A utility which automatically generates HDR and LDR cubemaps. +// This has the following purposes: +// +// 1. Allow both HDR and LDR cubemaps to be generated automatically after a map is compiled +// 2. Have a way to batch build cubemaps for several levels at once +// +// Author: Blixibon +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "tier0/icommandline.h" +#include "igamesystem.h" +#include "filesystem.h" +#include "utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern const char *g_MapName; + +ConVar autocubemap_hdr_do_both( "autocubemap_hdr_do_both", "1" ); +ConVar autocubemap_hdr_value( "autocubemap_hdr_value", "2" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CAutoCubemapSystem : public CAutoGameSystem +{ +public: + CAutoCubemapSystem() : CAutoGameSystem( "CAutoCubemapSystem" ) + { + } + + virtual bool Init() + { + const char *pszFile = NULL; + if (CommandLine()->CheckParm( "-autocubemap", &pszFile )) + { + if (!pszFile || pszFile[0] == '\0') + { + // Assume that we just want to autocubemap the first map we load + // (no code here for now) + } + else + { + LoadFile( pszFile ); + } + + // Begin autocubemap with the first level we load + m_bAutoCubemapOnFirstLevel = true; + } + + return true; + } + + virtual void LevelInitPostEntity() + { + if (m_bAutoCubemapActive) + { + if (m_bAutoCubemapBuildingCubemaps) + { + // Check if we need to do the other HDR level + if (autocubemap_hdr_do_both.GetBool() && !m_bAutoCubemapDoingBoth) + { + m_bAutoCubemapBuildingCubemaps = false; + m_bAutoCubemapDoingBoth = true; + + // Change the HDR level and restart the map + //ConVarRef mat_hdr_level( "mat_hdr_level" ); + engine->ClientCmd_Unrestricted( VarArgs( "toggle mat_hdr_level 0 %i; restart", autocubemap_hdr_value.GetInt() ) ); + } + else + { + // Go to the next map + m_bAutoCubemapBuildingCubemaps = false; + m_bAutoCubemapDoingBoth = false; + + m_AutoCubemapMapsIndex++; + if (m_AutoCubemapMapsIndex < m_AutoCubemapMaps.Count()) + { + engine->ClientCmd_Unrestricted( VarArgs( "map %s", m_AutoCubemapMaps[m_AutoCubemapMapsIndex] ) ); + } + else + { + // CUBEMAPPER FINISHED + m_AutoCubemapMaps.PurgeAndDeleteElements(); + m_AutoCubemapMapsIndex = 0; + m_bAutoCubemapActive = false; + + Msg( "CUBEMAPPER FINISHED\n" ); + + if (autocubemap_hdr_do_both.GetBool()) + { + engine->ClientCmd_Unrestricted( VarArgs( "mat_hdr_level %i", m_iAutoCubemapUserHDRLevel ) ); + } + } + } + } + else + { + // Build cubemaps for this map + m_bAutoCubemapBuildingCubemaps = true; + engine->ClientCmd_Unrestricted( "exec buildcubemaps_prep; buildcubemaps" ); + } + } + else if (m_bAutoCubemapOnFirstLevel) + { + // Start autocubemap now + StartAutoCubemap(); + m_bAutoCubemapOnFirstLevel = false; + } + } + + //------------------------------------------------------------------------------------- + + void StartAutoCubemap() + { + if (m_AutoCubemapMaps.Count() <= 0) + { + //Msg("No maps to cubemap with!\n"); + //return; + + // Just do this map + m_AutoCubemapMaps.AddToTail( strdup( g_MapName ) ); + } + + if (autocubemap_hdr_do_both.GetBool()) + { + // Save the user's HDR level + ConVarRef mat_hdr_level( "mat_hdr_level" ); + m_iAutoCubemapUserHDRLevel = mat_hdr_level.GetInt(); + } + + m_bAutoCubemapActive = true; + m_AutoCubemapMapsIndex = 0; + + if (FStrEq( m_AutoCubemapMaps[m_AutoCubemapMapsIndex], g_MapName )) + { + // Build cubemaps right here, right now + m_bAutoCubemapBuildingCubemaps = true; + engine->ClientCmd_Unrestricted( "exec buildcubemaps_prep; buildcubemaps" ); + } + else + { + // Go to that map + engine->ClientCmd_Unrestricted( VarArgs( "map %s", m_AutoCubemapMaps[m_AutoCubemapMapsIndex] ) ); + } + } + + void LoadFile( const char *pszFile ) + { + KeyValues *pKV = new KeyValues( "AutoCubemap" ); + + if ( pKV->LoadFromFile( filesystem, pszFile, NULL ) ) + { + KeyValues *pSubKey = pKV->GetFirstSubKey(); + + while ( pSubKey ) + { + m_AutoCubemapMaps.AddToTail( strdup(pSubKey->GetName()) ); + pSubKey = pSubKey->GetNextKey(); + } + + Msg( "Initted autocubemap\n" ); + } + else + { + Warning( "Unable to load autocubemap file \"%s\"\n", pszFile ); + } + + pKV->deleteThis(); + } + + void Clear() + { + m_bAutoCubemapActive = false; + m_bAutoCubemapBuildingCubemaps = false; + m_bAutoCubemapDoingBoth = false; + + m_AutoCubemapMaps.PurgeAndDeleteElements(); + m_AutoCubemapMapsIndex = 0; + } + + void PrintState() + { + char szCmd[1024] = { 0 }; + + if (m_AutoCubemapMaps.Count() > 0) + { + Q_strncpy( szCmd, "=== CUBEMAPPER MAP LIST ===\n", sizeof( szCmd ) ); + + FOR_EACH_VEC( m_AutoCubemapMaps, i ) + { + Q_snprintf( szCmd, sizeof( szCmd ), "%s%s\n", szCmd, m_AutoCubemapMaps[i] ); + } + + Q_strncat( szCmd, "========================", sizeof( szCmd ), COPY_ALL_CHARACTERS ); + + Q_snprintf( szCmd, sizeof( szCmd ), "%s\nNumber of maps: %i (starting at %i)\n", szCmd, m_AutoCubemapMaps.Count(), m_AutoCubemapMapsIndex ); + } + else + { + Q_strncat( szCmd, "========================\n", sizeof( szCmd ), COPY_ALL_CHARACTERS ); + Q_strncat( szCmd, "There are no maps selected. Use 'autocubemap_init' to load a map list.\nIf 'autocubemap_start' is executed while no maps are selected, only the current map will have cubemaps generated.\n", sizeof( szCmd ), COPY_ALL_CHARACTERS ); + Q_strncat( szCmd, "========================\n", sizeof( szCmd ), COPY_ALL_CHARACTERS ); + } + + Msg( "%s", szCmd ); + } + + //------------------------------------------------------------------------------------- + + bool m_bAutoCubemapActive = false; + bool m_bAutoCubemapBuildingCubemaps = false; + bool m_bAutoCubemapDoingBoth = false; + int m_iAutoCubemapUserHDRLevel; // For setting the user back to the right HDR level when we're finished + + // Start autocubemap with the first level we load (used for launch parameter) + bool m_bAutoCubemapOnFirstLevel = false; + + CUtlVector m_AutoCubemapMaps; + int m_AutoCubemapMapsIndex; +}; + +CAutoCubemapSystem g_AutoCubemapSystem; + +CON_COMMAND( autocubemap_init, "Inits autocubemap" ) +{ + if (gpGlobals->maxClients > 1) + { + Msg( "Can't run autocubemap in multiplayer\n" ); + return; + } + + if (args.ArgC() <= 1) + { + Msg("Format: autocubemap_init \n"); + return; + } + + g_AutoCubemapSystem.LoadFile( args.Arg( 1 ) ); +} + +CON_COMMAND( autocubemap_print, "Prints current autocubemap information" ) +{ + if (gpGlobals->maxClients > 1) + { + Msg("Can't run autocubemap in multiplayer\n"); + return; + } + + g_AutoCubemapSystem.PrintState(); +} + +CON_COMMAND( autocubemap_clear, "Clears autocubemap stuff" ) +{ + if (gpGlobals->maxClients > 1) + { + Msg("Can't run autocubemap in multiplayer\n"); + return; + } + + g_AutoCubemapSystem.Clear(); +} + +CON_COMMAND( autocubemap_start, "Begins the autocubemap (it's recommended to check 'autocubemap_print' before running this command)" ) +{ + if (gpGlobals->maxClients > 1) + { + Msg("Can't run autocubemap in multiplayer\n"); + return; + } + + g_AutoCubemapSystem.StartAutoCubemap(); +} diff --git a/mp/src/game/client/mapbase/vscript_vgui.cpp b/mp/src/game/client/mapbase/vscript_vgui.cpp new file mode 100644 index 00000000..8d08e8be --- /dev/null +++ b/mp/src/game/client/mapbase/vscript_vgui.cpp @@ -0,0 +1,3891 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +// +// Author: samisalreadytaken +// +//=============================================================================// + + +#include "cbase.h" +#include "tier1/utlcommon.h" + +#include "inputsystem/iinputsystem.h" +#include "iinput.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "matsys_controls/matsyscontrols.h" +#include "VGuiMatSurface/IMatSystemSurface.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +//#include + +#if VGUI_TGA_IMAGE_PANEL +#include "bitmap/tgaloader.h" +#endif + +#if !defined(NO_STEAM) +#include "steam/steam_api.h" +#include "vgui_avatarimage.h" +#endif + +#include "view.h" +#include "hudelement.h" +//#include "iclientmode.h" // g_pClientMode->GetViewport() + +#include "vscript_vgui.h" +#include "vscript_vgui.nut" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//============================================================================= +// +// Exposing a new panel class (e.g. vgui::FileOpenDialog): +// +// 1. Create C++ bindings using 'CLASS_HELPER_INTERFACE( FileOpenDialog, Frame ){};' +// 2. Define script bindings using '#define DEFINE_VGUI_SCRIPTFUNC_FileOpenDialog()' +// 3. Create 'class CScript_FileOpenDialog : FileOpenDialog' with vgui message callbacks and overrides if needed +// 4. Create script helper using 'BEGIN_VGUI_HELPER( FileOpenDialog )', 'END_VGUI_HELPER()'. This determines the script class name. +// 5. Register script bindings with 'BEGIN_SCRIPTDESC_VGUI( FileOpenDialog )', 'END_SCRIPTDESC()' +// 6. Add new condition in CScriptVGUI::CreatePanel() +// +// +// +// CScript_FileOpenDialog_Helper +// ^^ +// IScript_FileOpenDialog << CScript_FileOpenDialog +// ^^ ^^ +// IScript_Frame FileOpenDialog +// ^^ ^^ +// IScript_Panel Frame +// ^^ ^^ +// CScriptVGUIObject Panel +// +//============================================================================= + + +// When enabled, script panels will be parented to custom root panels. +// When disabled, script panels will be parented to engine root panels, and allow Z values for script panels to be interplaced amongst non-script panels. +// Changing this is not backwards compatible, as existing top level script panel depth would then change relative to non-script panels. +#define SCRIPT_ENGINE_ROOT_PANELS 1 + +// NOTE: causes rendering issues +#define ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL 0 + +#define ALLOW_SCRIPT_GAMEUI_ROOT_PANEL 0 + +// On level transitions Restore is called up to 4 times in a row (due to .hl? client state files), each time +// trying to restore script panels from pre and post transitions, failing every time because script panels are +// destroyed on level shutdown but after client state files are written. +// +// Script variables are also reset between each OnRestore callback, causing duplicate panels if user scripts create panels +// by checking restored script variables. +// +// The workaround hack is to queue OnRestore callbacks with a think function. +// +// This code is left here for testing. +#define SCRIPT_VGUI_SAVERESTORE 0 + +#define SCRIPT_VGUI_SIGNAL_INTERFACE 0 + + + +#ifdef _DEBUG +#define DebugMsg(...) ConColorMsg( Color(196, 196, 156, 255), __VA_ARGS__ ) +#define DebugWarning(...) Warning( __VA_ARGS__ ) +#define DebugDevMsg(...) DevMsg( __VA_ARGS__ ) + +#define DBG_PARAM(...) __VA_ARGS__ +#else +#define DebugMsg(...) (void)(0) +#define DebugWarning(...) (void)(0) +#define DebugDevMsg(...) (void)(0) + +#define DBG_PARAM(...) +#endif + + + +template< typename T > +class CCopyableUtlVectorConservative : public CUtlVectorConservative< T > +{ + typedef CUtlVectorConservative< T > BaseClass; +public: + explicit CCopyableUtlVectorConservative( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} + explicit CCopyableUtlVectorConservative( T* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} + CCopyableUtlVectorConservative( CCopyableUtlVectorConservative const& vec ) { this->CopyArray( vec.Base(), vec.Count() ); } +}; + + +using namespace vgui; +class IScriptVGUIObject; +struct FontData_t; + +// Aliases contain only one font definition unless 'yres' was defined +typedef CCopyableUtlVectorConservative< FontData_t > fontalias_t; +typedef CUtlDict< fontalias_t > CFontDict; + + +CFontDict g_ScriptFonts( k_eDictCompareTypeCaseSensitive ); +CUtlVector< int > g_ScriptTextureIDs; +CUtlLinkedList< IScriptVGUIObject*, unsigned short > g_ScriptPanels; + + +// Boundary is not checked in Surface, keep count manually to sanitise user input. +static int g_nFontCount = 0; + +static inline HFont IntToFontHandle( int i ) +{ + if ( i < 0 || i > g_nFontCount ) + return INVALID_FONT; + return static_cast< unsigned int >(i); +} + +// vscript does not support unsigned int, +// but the representation of the handle does not matter, +// and these handles are CUtlVector indices +static inline int HandleToInt( unsigned int i ) +{ + return static_cast< int >(i); +} + + +struct FontData_t +{ + HFont font; + char *name; + int tall; + int weight; + int blur; + int scanlines; + int flags; + //int range_min; + //int range_max; + int yres_min; + int yres_max; + bool proportional; +}; + +static const char *GetFixedFontName( const char *name, bool proportional ) +{ + static char fontName[64]; + V_snprintf( fontName, sizeof(fontName), "%s-%s", name, proportional ? "p" : "no" ); + return fontName; +} + +CON_COMMAND( vgui_spew_fonts_script, "" ) +{ + char fontName[64]; + + FOR_EACH_DICT_FAST( g_ScriptFonts, i ) + { + const FontData_t &data = g_ScriptFonts[i].Head(); + const char *name = surface()->GetFontName( data.font ); + const char *alias = g_ScriptFonts.GetElementName(i); + + // Strip off the appendix "-p" / "-no" + V_StrLeft( alias, V_strlen(alias) - (data.proportional ? 2 : 3), fontName, sizeof(fontName) ); + + Msg( " %2d: HFont:0x%8.8lx, %s, %s, font:%s, tall:%d(%d) {%d}\n", + i, + data.font, + fontName, + alias, + name ? name : "??", + surface()->GetFontTall( data.font ), + surface()->GetFontTallRequested( data.font ), + g_ScriptFonts[i].Count() ); + } +} + +bool LoadFont( const FontData_t &font DBG_PARAM(, const char *fontAlias) ) +{ + if ( font.yres_min ) + { + int nScreenWide, nScreenTall; + surface()->GetScreenSize( nScreenWide, nScreenTall ); + + if ( nScreenTall < font.yres_min ) + return false; + + if ( font.yres_max && nScreenTall > font.yres_max ) + return false; + } + + int tall = font.tall; + int blur = font.blur; + int scanlines = font.scanlines; + + if ( font.proportional && !font.yres_min ) + { + tall = scheme()->GetProportionalScaledValue( tall ); + blur = scheme()->GetProportionalScaledValue( blur ); + scanlines = scheme()->GetProportionalScaledValue( scanlines ); + } + + bool bSuccess = surface()->SetFontGlyphSet( + font.font, + font.name, + tall, + font.weight, + blur, + scanlines, + font.flags ); + + NOTE_UNUSED( bSuccess ); + if ( bSuccess ) + { + if ( font.yres_min ) + DebugMsg( "Load font [%li]%s [%d %d]\n", font.font, fontAlias, font.yres_min, font.yres_max ); + else + DebugMsg( "Load font [%li]%s\n", font.font, fontAlias ); + } + else + { + DebugWarning( "Failed to load font [%li]%s\n", font.font, fontAlias ); + } + + return true; +} + +void ReloadScriptFontGlyphs() +{ + // Invalidate cached values + if ( g_pScriptVM ) + g_pScriptVM->Run( "ISurface.__OnScreenSizeChanged()" ); + + FOR_EACH_DICT_FAST( g_ScriptFonts, i ) + { + const fontalias_t &alias = g_ScriptFonts[i]; + for ( int j = 0; j < alias.Count(); ++j ) + { + if ( LoadFont( alias.Element(j) DBG_PARAM(, g_ScriptFonts.GetElementName(i)) ) ) + break; + } + } +} + + +static inline void InitRootPanel( Panel *p, VGuiPanel_t parent, const char *name ) +{ + int w, h; + surface()->GetScreenSize( w, h ); + p->Init( 0, 0, w, h ); + p->SetName( name ); + p->SetVisible( true ); + p->SetPaintEnabled( false ); + p->SetPaintBackgroundEnabled( false ); + p->SetPaintBorderEnabled( false ); + p->SetPostChildPaintEnabled( false ); + p->SetParent( enginevgui->GetPanel( parent ) ); +} + +class CScriptRootPanel : public Panel +{ +public: + CScriptRootPanel() + { + InitRootPanel( this, PANEL_ROOT, "VScriptRoot" ); + } + + void OnTick() + { + if ( m_nLastFrame == gpGlobals->framecount ) + return; + + ReloadScriptFontGlyphs(); + ivgui()->RemoveTickSignal( GetVPanel() ); + } + + // Used as a callback to font invalidation. + // Ideally script fonts would be loaded along with others in engine. + // In that case CScriptRootPanel would be removed, and + // g_pScriptRootPanel would be CScriptRootDLLPanel inside #if SCRIPT_ENGINE_ROOT_PANELS + void OnScreenSizeChanged( int w, int t ) + { + // Reload fonts in the next vgui frame + ivgui()->AddTickSignal( GetVPanel() ); + m_nLastFrame = gpGlobals->framecount; + + // Invalidate cached values + if ( g_pScriptVM ) + g_pScriptVM->Run( "ISurface.__OnScreenSizeChanged()" ); + + Panel::OnScreenSizeChanged( w, t ); + } + +private: + int m_nLastFrame; +}; + +CScriptRootPanel *g_pScriptRootPanel = NULL; + +#if SCRIPT_ENGINE_ROOT_PANELS +class CScriptRootDLLPanel : public Panel +{ +public: + CScriptRootDLLPanel( VGuiPanel_t parent, const char *name ) + { + InitRootPanel( this, parent, name ); + } +}; + +CScriptRootDLLPanel *g_pScriptClientDLLPanel = NULL; +#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL +CScriptRootDLLPanel *g_pScriptGameUIDLLPanel = NULL; +#endif +#endif + +void VGUI_DestroyScriptRootPanels() +{ + if ( g_pScriptRootPanel ) + { + delete g_pScriptRootPanel; + g_pScriptRootPanel = NULL; + } +#if SCRIPT_ENGINE_ROOT_PANELS + if ( g_pScriptClientDLLPanel ) + { + delete g_pScriptClientDLLPanel; + g_pScriptClientDLLPanel = NULL; + } +#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL + if ( g_pScriptGameUIDLLPanel ) + { + delete g_pScriptGameUIDLLPanel; + g_pScriptGameUIDLLPanel = NULL; + } +#endif +#endif +} + +VPANEL VGUI_GetScriptRootPanel( VGuiPanel_t type ) +{ +#if !SCRIPT_ENGINE_ROOT_PANELS + if ( !g_pScriptRootPanel ) + g_pScriptRootPanel = new CScriptRootPanel(); + + return enginevgui->GetPanel( type ); +#else + switch ( type ) + { + case PANEL_ROOT: + { + if ( !g_pScriptRootPanel ) + g_pScriptRootPanel = new CScriptRootPanel(); + + return g_pScriptRootPanel->GetVPanel(); + } + case PANEL_CLIENTDLL: + { + if ( !g_pScriptClientDLLPanel ) + g_pScriptClientDLLPanel = new CScriptRootDLLPanel( PANEL_CLIENTDLL, "VScriptClient" ); + + return g_pScriptClientDLLPanel->GetVPanel(); + } +#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL + case PANEL_GAMEUIDLL: + { + if ( !g_pScriptGameUIDLLPanel ) + g_pScriptGameUIDLLPanel = new CScriptRootDLLPanel( PANEL_GAMEUIDLL, "VScriptGameUI" ); + + return g_pScriptGameUIDLLPanel->GetVPanel(); + } +#endif + } + return NULL; +#endif +} + + +// +// Escapes "vgui/" prepended to the file name in CSchemeManager::GetImage(). +// +IImage *vgui_GetImage( const char *imageName, bool hardwareFilter ) +{ + char fileName[MAX_PATH]; + V_snprintf( fileName, sizeof( fileName ), "../%s", imageName ); + + return scheme()->GetImage( fileName, hardwareFilter ); +} + + +//-------------------------------------------------------------- +// +//-------------------------------------------------------------- +class CScriptSurface +{ +public: + void PlaySound( const char* sound ); + void SetColor( int r, int g, int b, int a ); + void DrawFilledRect( int x0, int y0, int width, int height ); + void DrawFilledRectFade( int x0, int y0, int width, int height, int a0, int a1, bool bHorz ); + void DrawOutlinedRect( int x0, int y0, int width, int height, int thickness ); + void DrawLine( int x0, int y0, int x1, int y1 ); + void DrawOutlinedCircle( int x, int y, int radius, int segments ); + + void SetTextColor( int r, int g, int b, int a ); + void SetTextPos( int x, int y ); + void SetTextFont( int font ); + void DrawText( const char *text, int drawType/* = FONT_DRAW_DEFAULT*/ ); + void DrawUnicodeChar( int ch, int drawType/* = FONT_DRAW_DEFAULT*/ ); + + int GetFont( const char* name, bool proportional, const char* schema ); + int GetTextWidth( int font, const char* text ); + int GetFontTall( int font ); + int GetCharacterWidth( int font, int ch ); + + void CreateFont( const char *customName, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int yresMin, int yresMax, bool proportional ); + bool AddCustomFontFile( const char *fontFileName ); + + int GetTextureID( char const *filename ); + int ValidateTexture( const char *filename, bool hardwareFilter, bool forceReload, bool procedural ); + void SetTextureFile( int id, const char *filename, bool hardwareFilter ); + int GetTextureWide( int id ); + int GetTextureTall( int id ); + void SetTexture( int id ); + + void DrawTexturedRect( int x0, int y0, int width, int height ); + void DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ); + + // ------------------------------------------------------------ + // Utility functions + // ------------------------------------------------------------ + + void DrawTexturedBox( int texture, int x, int y, int wide, int tall, int r, int g, int b, int a ); + void DrawColoredText( int font, int x, int y, int r, int g, int b, int a, const char *text ); + void DrawColoredTextRect( int font, int x, int y, int w, int h, int r, int g, int b, int a, const char *text ); + void DrawTexturedRectRotated( int x, int y, int w, int t, float yaw ); + +} script_surface; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptSurface, "ISurface", SCRIPT_SINGLETON ) + DEFINE_SCRIPTFUNC( PlaySound, "" ) + + DEFINE_SCRIPTFUNC( SetColor, "" ) + DEFINE_SCRIPTFUNC( DrawFilledRect, "" ) + DEFINE_SCRIPTFUNC( DrawFilledRectFade, "" ) + DEFINE_SCRIPTFUNC( DrawOutlinedRect, "" ) + DEFINE_SCRIPTFUNC( DrawLine, "" ) + DEFINE_SCRIPTFUNC( DrawOutlinedCircle, "" ) + + DEFINE_SCRIPTFUNC( SetTextColor, "" ) + DEFINE_SCRIPTFUNC( SetTextPos, "" ) + DEFINE_SCRIPTFUNC( SetTextFont, "" ) + DEFINE_SCRIPTFUNC( DrawText, "" ) + DEFINE_SCRIPTFUNC( DrawUnicodeChar, "" ) + + DEFINE_SCRIPTFUNC( GetFont, "" ) + DEFINE_SCRIPTFUNC( GetTextWidth, "" ) + DEFINE_SCRIPTFUNC( GetFontTall, "" ) + DEFINE_SCRIPTFUNC( GetCharacterWidth, "" ) + + DEFINE_SCRIPTFUNC( CreateFont, SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC( AddCustomFontFile, "" ) + + DEFINE_SCRIPTFUNC( GetTextureID, "" ) + DEFINE_SCRIPTFUNC( ValidateTexture, "" ) + DEFINE_SCRIPTFUNC( SetTextureFile, "" ) + DEFINE_SCRIPTFUNC( GetTextureWide, "" ) + DEFINE_SCRIPTFUNC( GetTextureTall, "" ) + DEFINE_SCRIPTFUNC( SetTexture, "" ) + + DEFINE_SCRIPTFUNC( DrawTexturedRect, "" ) + DEFINE_SCRIPTFUNC( DrawTexturedSubRect, "" ) + + DEFINE_SCRIPTFUNC( DrawTexturedBox, "" ) + DEFINE_SCRIPTFUNC( DrawColoredText, "" ) + DEFINE_SCRIPTFUNC( DrawColoredTextRect, "" ) + DEFINE_SCRIPTFUNC( DrawTexturedRectRotated, "" ) +END_SCRIPTDESC() + + +void CScriptSurface::PlaySound( const char* sound ) +{ + surface()->PlaySound(sound); +} + +void CScriptSurface::SetColor( int r, int g, int b, int a ) +{ + surface()->DrawSetColor( r, g, b, a ); +} + +void CScriptSurface::DrawFilledRect( int x0, int y0, int width, int height ) +{ + surface()->DrawFilledRect( x0, y0, x0 + width, y0 + height ); +} + +void CScriptSurface::DrawFilledRectFade( int x0, int y0, int width, int height, int a0, int a1, bool bHorz ) +{ + surface()->DrawFilledRectFade( x0, y0, x0 + width, y0 + height, a0, a1, bHorz ); +} + +void CScriptSurface::DrawOutlinedRect( int x0, int y0, int width, int height, int thickness ) +{ + int x1 = x0 + width; + int y1 = y0 + height - thickness; + y0 += thickness; + + surface()->DrawFilledRect( x0, y0 - thickness, x1, y0 ); // top + surface()->DrawFilledRect( x1 - thickness, y0, x1, y1 ); // right + surface()->DrawFilledRect( x0, y1, x1, y1 + thickness ); // bottom + surface()->DrawFilledRect( x0, y0, x0 + thickness, y1 ); // left +} + +void CScriptSurface::DrawLine( int x0, int y0, int x1, int y1 ) +{ + surface()->DrawLine( x0, y0, x1, y1 ); +} +#if 0 +void CScriptSurface::DrawPolyLine( HSCRIPT ax, HSCRIPT ay, int count ) +{ + if (count < 1) + return; + + if (count > 4096) + count = 4096; + + int *px = (int*)stackalloc( count * sizeof(int) ); + int *py = (int*)stackalloc( count * sizeof(int) ); + ScriptVariant_t vx, vy; + + int i = count; + while ( i-- ) + { + g_pScriptVM->GetValue( ax, i, &vx ); + g_pScriptVM->GetValue( ay, i, &vy ); + + px[i] = vx.m_int; + py[i] = vy.m_int; + } + + surface()->DrawPolyLine( px, py, count ); +} +#endif +void CScriptSurface::DrawOutlinedCircle( int x, int y, int radius, int segments ) +{ + surface()->DrawOutlinedCircle( x, y, radius, segments ); +} + +void CScriptSurface::SetTextColor( int r, int g, int b, int a ) +{ + surface()->DrawSetTextColor( r, g, b, a ); +} + +void CScriptSurface::SetTextPos( int x, int y ) +{ + surface()->DrawSetTextPos( x, y ); +} + +void CScriptSurface::SetTextFont( int font ) +{ + surface()->DrawSetTextFont( IntToFontHandle(font) ); +} + +void CScriptSurface::DrawText( const char *text, int drawType ) +{ + wchar_t wcs[512]; + g_pVGuiLocalize->ConvertANSIToUnicode( text, wcs, sizeof(wcs) ); + surface()->DrawPrintText( wcs, wcslen(wcs), (FontDrawType_t)drawType ); +} + +void CScriptSurface::DrawUnicodeChar( int ch, int drawType ) +{ + surface()->DrawUnicodeChar( (wchar_t)ch, (FontDrawType_t)drawType ); +} + +int CScriptSurface::GetFont( const char* name, bool proportional, const char* schema ) +{ + HFont font = INVALID_FONT; + + if ( !schema || !schema[0] ) + { + int idx = g_ScriptFonts.Find( GetFixedFontName( name, proportional ) ); + if ( idx != g_ScriptFonts.InvalidIndex() ) + { + font = g_ScriptFonts[idx].Head().font; + } + } + else + { + HScheme sch = scheme()->GetScheme( schema ); + font = scheme()->GetIScheme(sch)->GetFont( name, proportional ); + + // Update known count + if ( font > (unsigned int)g_nFontCount ) + g_nFontCount = font; + } + + return HandleToInt( font ); +} + +int CScriptSurface::GetTextWidth( int font, const char* text ) +{ + int w, t; + wchar_t wcs[512]; + g_pVGuiLocalize->ConvertANSIToUnicode( text, wcs, sizeof(wcs) ); + surface()->GetTextSize( IntToFontHandle(font), wcs, w, t ); + return w; +} + +int CScriptSurface::GetFontTall( int font ) +{ + return surface()->GetFontTall( IntToFontHandle(font) ); +} + +int CScriptSurface::GetCharacterWidth( int font, int ch ) +{ + return surface()->GetCharacterWidth( IntToFontHandle(font), ch ); +} + +void CScriptSurface::CreateFont( const char *customName, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int yresMin, int yresMax, bool proportional ) +{ + if ( flags & ISurface::FONTFLAG_BITMAP ) + { + AssertMsg( 0, "Bitmap fonts are not supported!" ); + return; + } + + if ( proportional && yresMin ) + { + AssertMsg( 0, "Resolution cannot be defined on a proportional font!" ); + return; + } + + if ( (yresMin < 0 || yresMax < 0) || (!!yresMin != !!yresMax) ) + { + AssertMsg( 0, "Invalid resolution!" ); + return; + } + + const char *fontAlias = GetFixedFontName( customName, proportional ); + + int idx = g_ScriptFonts.Find( fontAlias ); + if ( idx != g_ScriptFonts.InvalidIndex() ) + { + fontalias_t &alias = g_ScriptFonts[idx]; + // TODO: One proportional font to fall back to amongst resolution filtered fonts. + +#ifdef _DEBUG + if ( !yresMin && !yresMax ) + { + // There must be only one font registered. + Assert( alias.Count() == 1 ); + + HFont font = alias.Head().font; + int oldTall = surface()->GetFontTallRequested( font ); + int newTall = proportional ? scheme()->GetProportionalScaledValue( tall ) : tall; + const char *oldName = surface()->GetFontName( font ); + + // Font changes will not be applied. + Assert( oldTall == newTall ); + if ( oldName ) // can be null + Assert( !V_stricmp( oldName, windowsFontName ) ); + } +#endif + + // if input resolutions match any of the existing fonts, + // then this must be a duplicate call. + for ( int i = 0; i < alias.Count(); ++i ) + { + FontData_t &data = alias.Element(i); + + if ( yresMin == data.yres_min && yresMax == data.yres_max ) + return; + } + + DebugMsg( "Create font add '%s' [%d %d]\n", fontAlias, yresMin, yresMax ); + + FontData_t &newFont = alias.Element( alias.AddToTail() ); + newFont.font = alias.Head().font; + newFont.name = strdup( windowsFontName ); + newFont.tall = tall; + newFont.weight = weight; + newFont.blur = blur; + newFont.scanlines = scanlines; + newFont.flags = flags; + newFont.yres_min = yresMin; + newFont.yres_max = yresMax; + newFont.proportional = proportional; + + LoadFont( newFont DBG_PARAM(, fontAlias) ); + } + else + { + HFont font = surface()->CreateFont(); + + // Sanity check + Assert( font > (unsigned int)g_nFontCount && font < INT_MAX ); + + // Update known count + if ( font > (unsigned int)g_nFontCount ) + g_nFontCount = font; + + if ( yresMax && yresMin > yresMax ) + { + int t = yresMin; + yresMin = yresMax; + yresMax = t; + } + + if ( yresMin ) + DebugMsg( "Create font new '%s' [%d %d]\n", fontAlias, yresMin, yresMax ); + else + DebugMsg( "Create font new '%s'\n", fontAlias ); + + fontalias_t &alias = g_ScriptFonts.Element( g_ScriptFonts.Insert( fontAlias ) ); + FontData_t &newFont = alias.Element( alias.AddToTail() ); + newFont.font = font; + newFont.name = strdup( windowsFontName ); + newFont.tall = tall; + newFont.weight = weight; + newFont.blur = blur; + newFont.scanlines = scanlines; + newFont.flags = flags; + newFont.yres_min = yresMin; + newFont.yres_max = yresMax; + newFont.proportional = proportional; + + LoadFont( newFont DBG_PARAM(, fontAlias) ); + } +} + +bool CScriptSurface::AddCustomFontFile( const char *fontFileName ) +{ + return surface()->AddCustomFontFile( NULL, fontFileName ); +} + +int CScriptSurface::GetTextureID( char const *filename ) +{ + return surface()->DrawGetTextureId( filename ); +} + +// Create texture if it does not already exist +int CScriptSurface::ValidateTexture( const char *filename, bool hardwareFilter, bool forceReload, bool procedural ) +{ + int id = surface()->DrawGetTextureId( filename ); + if ( id <= 0 ) + { + id = surface()->CreateNewTextureID( procedural ); + g_ScriptTextureIDs.AddToTail( id ); + + surface()->DrawSetTextureFile( id, filename, hardwareFilter, forceReload ); + +#ifdef _DEBUG + char tex[MAX_PATH]; + surface()->DrawGetTextureFile( id, tex, sizeof(tex)-1 ); + if ( !V_stricmp( filename, tex ) ) + { + DebugMsg( "Create texture [%i]%s\n", id, filename ); + } + else + { + DebugWarning( "Create texture [%i]%s(%s)\n", id, tex, filename ); + } +#endif + } + else if ( forceReload && g_ScriptTextureIDs.HasElement( id ) ) + { + surface()->DrawSetTextureFile( id, filename, hardwareFilter, forceReload ); + } + else + { + surface()->DrawSetTexture( id ); + } + + return id; +} + +// Replace existing texture +void CScriptSurface::SetTextureFile( int id, const char *filename, bool hardwareFilter ) +{ + if ( g_ScriptTextureIDs.HasElement(id) ) + { + Assert( surface()->IsTextureIDValid(id) ); + surface()->DrawSetTextureFile( id, filename, hardwareFilter, true ); + +#ifdef _DEBUG + char tex[MAX_PATH]; + surface()->DrawGetTextureFile( id, tex, sizeof(tex)-1 ); + if ( !V_stricmp( filename, tex ) ) + { + DebugMsg( "Set texture [%i]%s\n", id, filename ); + } + else + { + DebugWarning( "Set texture [%i]%s(%s)\n", id, tex, filename ); + } +#endif + } + +#ifdef _DEBUG + if ( !g_ScriptTextureIDs.HasElement(id) && surface()->IsTextureIDValid(id) ) + { + DebugWarning( "Tried to set non-script created texture! [%i]%s\n", id, filename ); + } + + if ( !surface()->IsTextureIDValid(id) ) + { + DebugWarning( "Tried to set invalid texture id! [%i]%s\n", id, filename ); + } +#endif +} +#if 0 +void CScriptSurface::SetTextureMaterial( int id, HSCRIPT hMaterial ) +{ + IMaterial *pMaterial = (IMaterial*)HScriptToClass< IScriptMaterial >( hMaterial ); + if ( !IsValid( pMaterial ) ) + return; + + if ( g_ScriptTextureIDs.HasElement(id) ) + { + Assert( surface()->IsTextureIDValid(id) ); + MatSystemSurface()->DrawSetTextureMaterial( id, pMaterial ); + + DebugMsg( "Set texture [%i]%s\n", id, pMaterial->GetName() ); + } + +#ifdef _DEBUG + if ( !g_ScriptTextureIDs.HasElement(id) && surface()->IsTextureIDValid(id) ) + { + DebugWarning( "Tried to set non-script created texture! [%i]\n", id ); + } + + if ( !surface()->IsTextureIDValid(id) ) + { + DebugWarning( "Tried to set invalid texture id! [%i]\n", id ); + } +#endif +} +#endif +int CScriptSurface::GetTextureWide( int id ) +{ + int w, t; + surface()->DrawGetTextureSize( id, w, t ); + return w; +} + +int CScriptSurface::GetTextureTall( int id ) +{ + int w, t; + surface()->DrawGetTextureSize( id, w, t ); + return t; +} + +void CScriptSurface::SetTexture( int id ) +{ + surface()->DrawSetTexture( id ); +} + +void CScriptSurface::DrawTexturedRect( int x0, int y0, int width, int height ) +{ + surface()->DrawTexturedRect( x0, y0, x0 + width, y0 + height ); +} + +void CScriptSurface::DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ) +{ + surface()->DrawTexturedSubRect( x0, y0, x1, y1, texs0, text0, texs1, text1 ); +} + +void CScriptSurface::DrawTexturedRectRotated( int x, int y, int w, int t, float yaw ) +{ + Vertex_t verts[4]; + Vector2D axis[2]; + + float sy, cy; + SinCos( DEG2RAD( -yaw ), &sy, &cy ); + + axis[0].x = cy; + axis[0].y = sy; + axis[1].x = -axis[0].y; + axis[1].y = axis[0].x; + + verts[0].m_TexCoord.Init( 0, 0 ); + Vector2DMA( Vector2D( x + w * 0.5f, y + t * 0.5f ), w * -0.5f, axis[0], verts[0].m_Position ); + Vector2DMA( verts[0].m_Position, t * -0.5f, axis[1], verts[0].m_Position ); + + verts[1].m_TexCoord.Init( 1, 0 ); + Vector2DMA( verts[0].m_Position, w, axis[0], verts[1].m_Position ); + + verts[2].m_TexCoord.Init( 1, 1 ); + Vector2DMA( verts[1].m_Position, t, axis[1], verts[2].m_Position ); + + verts[3].m_TexCoord.Init( 0, 1 ); + Vector2DMA( verts[0].m_Position, t, axis[1], verts[3].m_Position ); + + surface()->DrawTexturedPolygon( 4, verts ); +} + +void CScriptSurface::DrawTexturedBox( int texture, int x, int y, int wide, int tall, int r, int g, int b, int a ) +{ + surface()->DrawSetColor( r, g, b, a ); + surface()->DrawSetTexture( texture ); + surface()->DrawTexturedRect( x, y, x + wide, y + tall ); +} + +void CScriptSurface::DrawColoredText( int font, int x, int y, int r, int g, int b, int a, const char *text ) +{ + wchar_t wcs[512]; + g_pVGuiLocalize->ConvertANSIToUnicode( text, wcs, sizeof(wcs) ); + + surface()->DrawSetTextFont( IntToFontHandle(font) ); + surface()->DrawSetTextColor( r, g, b, a ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawPrintText( wcs, wcslen(wcs) ); +} + +void CScriptSurface::DrawColoredTextRect( int font, int x, int y, int w, int h, int r, int g, int b, int a, const char *text ) +{ + MatSystemSurface()->DrawColoredTextRect( IntToFontHandle(font), x, y, w, h, r, g, b, a, text ); +} + + +//============================================================== +//============================================================== + +#define __base() this->_base + +#define BEGIN_SCRIPTDESC_VGUI( panelClass )\ + BEGIN_SCRIPTDESC_NAMED( CScript_##panelClass##_Helper, IScriptVGUIObject, #panelClass, "" )\ + DEFINE_VGUI_SCRIPTFUNC_##panelClass() + +// +// Script helpers are wrappers that only redirect to VGUI panels (such as CScript_Panel : Panel), +// these macros help to simplify definitions. +// + +// +// BEGIN_VGUI_HELPER() assumes the VGUI panel class has the prefix 'CScript_' +// Use BEGIN_VGUI_HELPER_EX() to manually define VGUI panel class name. +// +#define BEGIN_VGUI_HELPER( panelClass )\ + BEGIN_VGUI_HELPER_EX( panelClass, CScript_##panelClass ) + +#define BEGIN_VGUI_HELPER_DEFAULT_TEXT( panelClass )\ + BEGIN_VGUI_HELPER_DEFAULT_TEXT_EX( panelClass, CScript_##panelClass ) + +#define BEGIN_VGUI_HELPER_EX( panelClass, baseClass )\ + class CScript_##panelClass##_Helper : public IScript_##panelClass< baseClass >\ + {\ + void Create( const char *panelName ) override\ + {\ + Assert( !_base && !_vpanel );\ + _base = new baseClass( NULL, panelName );\ + }\ +\ + public: + +#define BEGIN_VGUI_HELPER_DEFAULT_TEXT_EX( panelClass, baseClass )\ + class CScript_##panelClass##_Helper : public IScript_##panelClass< baseClass >\ + {\ + void Create( const char *panelName ) override\ + {\ + Assert( !_base && !_vpanel );\ + _base = new baseClass( NULL, panelName, (const char*)NULL );\ + }\ +\ + public: +#define END_VGUI_HELPER()\ + }; + + +#define CLASS_HELPER_INTERFACE_ROOT( panelClass )\ + template \ + class IScript_##panelClass : public CScriptVGUIObject + +#define CLASS_HELPER_INTERFACE( panelClass, baseClass )\ + template \ + class IScript_##panelClass : public IScript_##baseClass + + +#ifdef _DEBUG +#define DEBUG_DESTRUCTOR( panelClass, baseClass )\ + panelClass()\ + {\ + DebugDestructor( baseClass )\ + } + +#define DebugDestructor( panelClass )\ + {\ + DebugDevMsg( " ~" #panelClass "() '%s'\n", GetName() );\ + } +#else +#define DEBUG_DESTRUCTOR( panelClass, baseClass ) +#define DebugDestructor( panelClass ) +#endif + +#define DECLARE_SCRIPTVGUI_CLASS( baseClass )\ + DECLARE_SCRIPTVGUI_CLASS_EX( CScript_##baseClass, baseClass )\ + DEBUG_DESTRUCTOR( ~CScript_##baseClass, baseClass ) + +#define DECLARE_SCRIPTVGUI_CLASS_EX( panelClass, baseClass )\ + typedef baseClass BaseClass;\ + typedef panelClass ThisClass;\ +public:\ + void OnDelete()\ + {\ + DebugMsg( #baseClass "::OnDelete() '%s'\n", GetName() );\ + int i;\ + IScriptVGUIObject *obj = FindInScriptPanels( GetVPanel(), i );\ + if ( obj )\ + {\ + obj->Destroy( i );\ + }\ + BaseClass::OnDelete();\ + } + +// +// Definitions for 'empty' vgui objects that do not have any script specific implementation - overrides or callbacks. +// These are required to shutdown script objects on panel death +// (on save restore where panel destructor is called after the VM is restarted while HSCRIPT members are invalid but not nullified, +// and on C++ deletion where IScriptVGUIObject::Destroy() is not automatically called). +// +#define DEFINE_VGUI_CLASS_EMPTY( panelClass )\ + class CScript_##panelClass : public panelClass\ + {\ + DECLARE_SCRIPTVGUI_CLASS( panelClass )\ + void Shutdown() {}\ +\ + public:\ + CScript_##panelClass( Panel *parent, const char *name )\ + : BaseClass( parent, name )\ + {}\ + };\ +\ + BEGIN_VGUI_HELPER( panelClass )\ + END_VGUI_HELPER()\ +\ + BEGIN_SCRIPTDESC_VGUI( panelClass )\ + END_SCRIPTDESC() + +#define DEFINE_VGUI_CLASS_EMPTY_DEFAULT_TEXT( panelClass )\ + class CScript_##panelClass : public panelClass\ + {\ + DECLARE_SCRIPTVGUI_CLASS( panelClass )\ + void Shutdown() {}\ +\ + public:\ + CScript_##panelClass( Panel *parent, const char *name, const char *text )\ + : BaseClass( parent, name, text )\ + {}\ + };\ +\ + BEGIN_VGUI_HELPER_DEFAULT_TEXT( panelClass )\ + END_VGUI_HELPER()\ +\ + BEGIN_SCRIPTDESC_VGUI( panelClass )\ + END_SCRIPTDESC() + +class IScriptVGUIObject +{ +public: + virtual ~IScriptVGUIObject() {} + +#ifdef _DEBUG + virtual const char *GetName() = 0; +#endif + //----------------------------------------------------- + // Free the VGUI panel and script instance. + //----------------------------------------------------- + virtual void Destroy( int ) = 0; + + //----------------------------------------------------- + // Create new panel + //----------------------------------------------------- + virtual void Create( const char *panelName ) = 0; + +public: + VPANEL GetVPanel() { return _vpanel; } + HSCRIPT GetScriptInstance() { return m_hScriptInstance; } + +protected: + VPANEL _vpanel; + HSCRIPT m_hScriptInstance; + + // Called on deletion + static void ResolveChildren_r( VPANEL panel DBG_PARAM(, int level) ); + +public: +#if SCRIPT_VGUI_SAVERESTORE + IScriptVGUIObject() {} + void SetScriptInstance( HSCRIPT h ) { m_hScriptInstance = h; } + char m_pszScriptId[16]; +#endif + +#ifdef _DEBUG + #if SCRIPT_VGUI_SAVERESTORE + const char *GetDebugName() { return m_pszScriptId; } + #else + const char *GetDebugName() { return ""; } + #endif +#endif +}; + +BEGIN_SCRIPTDESC_ROOT( IScriptVGUIObject, SCRIPT_HIDE ) +END_SCRIPTDESC() + + +#if SCRIPT_VGUI_SAVERESTORE +class CScriptVGUIScriptInstanceHelper : public IScriptInstanceHelper +{ + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) + { + DebugMsg( "BindOnRead (0x%p) (%s) (count %d)\n", (uint)hInstance, pszId, g_ScriptPanels.Count() ); + + FOR_EACH_LL( g_ScriptPanels, i ) + { + IScriptVGUIObject *pPanel = g_ScriptPanels[i]; + // DebugMsg( " cmp (%s)\n", pPanel->m_pszScriptId ); + if ( !V_stricmp( pPanel->m_pszScriptId, pszId ) ) + { + pPanel->SetScriptInstance( hInstance ); + DebugMsg( " ret (%s)\n", pPanel->m_pszScriptId ); + return pPanel; + } + } + DebugMsg( " ret (null)\n" ); + return NULL; + } +}; + +static CScriptVGUIScriptInstanceHelper g_ScriptVGUIScriptInstanceHelper; + +#define DEFINE_VGUI_SCRIPT_INSTANCE_HELPER() DEFINE_SCRIPT_INSTANCE_HELPER( &g_ScriptVGUIScriptInstanceHelper ) +#else +#define DEFINE_VGUI_SCRIPT_INSTANCE_HELPER() +#endif + + +IScriptVGUIObject *ToScriptVGUIObj( HSCRIPT inst ) +{ + return (IScriptVGUIObject *)g_pScriptVM->GetInstanceValue( inst, ::GetScriptDesc( (IScriptVGUIObject *)0 ) ); +} + +template < typename T > inline T* AllocScriptPanel() +{ + return new T; +} + +inline IScriptVGUIObject *FindInScriptPanels( VPANEL panel, int &I ) +{ + for ( int i = g_ScriptPanels.Head(); i != g_ScriptPanels.InvalidIndex(); i = g_ScriptPanels.Next(i) ) + { + IScriptVGUIObject *obj = g_ScriptPanels[i]; + if ( obj->GetVPanel() == panel ) + { + I = i; + return obj; + } + } + return NULL; +} + +void IScriptVGUIObject::ResolveChildren_r( VPANEL panel DBG_PARAM(, int level = 0) ) +{ +#ifdef _DEBUG + char indent[32]; + + int l = level, c = 0; + if ( l > 15 ) + l = 15; + + while ( l-- ) + { + indent[c++] = ' '; + indent[c++] = ' '; + } + indent[c] = 0; + + if ( level > 15 ) + { + indent[c-1] = '.'; + indent[c-2] = '.'; + } +#endif + + CUtlVector< VPANEL > &children = ipanel()->GetChildren( panel ); + FOR_EACH_VEC_BACK( children, i ) + { + VPANEL child = children[i]; + int j; + IScriptVGUIObject *obj = FindInScriptPanels( child, j ); + if ( obj ) + { + if ( ipanel()->IsAutoDeleteSet(child) ) + { + DebugMsg( " %sResolveChildren: '%s' (autodelete)\n", indent, obj->GetName() ); + + if ( g_pScriptVM ) + g_pScriptVM->RemoveInstance( obj->m_hScriptInstance ); + g_ScriptPanels.Remove( j ); + delete obj; + + ResolveChildren_r( child DBG_PARAM(, level+1) ); + } + else + { + DebugMsg( " %sResolveChildren: '%s'\n", indent, obj->GetName() ); + + // Panel::SetAutoDelete should not be added until + // what to do on their parent death is finalised. + // + // This assert will be hit if a deleted panel has + // C++ created and autodelete disabled children who are + // also registered to script. + Assert(0); + } + } + } +} + +template +class CScriptVGUIObject : public IScriptVGUIObject +{ +public: + T *_base; + + CScriptVGUIObject() : _base(0) + { + _vpanel = 0; + m_hScriptInstance = 0; + } + + void Destroy( int i = -1 ) + { + if ( i != -1 ) + { + Assert( g_ScriptPanels.IsValidIndex(i) ); + Assert( g_ScriptPanels[i] == this ); + + g_ScriptPanels.Remove( i ); + } + else + { + Assert( g_ScriptPanels.Find( this ) != g_ScriptPanels.InvalidIndex() ); + + g_ScriptPanels.FindAndRemove( this ); + } + + if ( GetVPanel() ) + { + DebugMsg( " Destroy panel '%s' %s\n", _base->GetName(), GetDebugName() ); + _base->Shutdown(); + ResolveChildren_r( _vpanel ); + _base->MarkForDeletion(); + } + + if ( m_hScriptInstance ) + { + if ( g_pScriptVM ) + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + } + + delete this; + } + + template + void CreateFromScript( HSCRIPT parent, const char *panelName, int root ) + { + Assert( !_vpanel && !m_hScriptInstance && !g_ScriptPanels.IsValidIndex( g_ScriptPanels.Find( this ) ) ); + + Create( panelName && *panelName ? panelName : NULL ); + _vpanel = _base->GetVPanel(); + m_hScriptInstance = g_pScriptVM->RegisterInstance< CHelper >( static_cast< CHelper* >( this ) ); + +#if SCRIPT_VGUI_SAVERESTORE + g_pScriptVM->GenerateUniqueKey( "", m_pszScriptId, sizeof(m_pszScriptId) ); + g_pScriptVM->SetInstanceUniqeId( m_hScriptInstance, m_pszScriptId ); +#endif + + if ( parent ) + { + IScriptVGUIObject *obj = ToScriptVGUIObj( parent ); + if ( obj ) + { + // Insert this after the parent to make sure children come after their parents, + // and their removal is done inside ResolveChildren_r(), not by individual Destroy() calls from LevelShutdown. + unsigned short parentIdx = g_ScriptPanels.Find( obj ); + + // My parent can't not be in the list. + Assert( parentIdx != g_ScriptPanels.InvalidIndex() && g_ScriptPanels.IsInList( parentIdx ) ); + + g_ScriptPanels.InsertAfter( parentIdx, this ); + + _base->SetParent( obj->GetVPanel() ); + return; + } + + AssertMsg( 0, "invalid parent" ); + } + + g_ScriptPanels.AddToTail( this ); + + // Script specified root panel - a cheap alternative to registering uneditable panel instances. + // Match the values to vscript_vgui.nut. + // + // This parameter is hidden in script, and is defined by the return value of dummy functions. + VPANEL vparent = 0; + switch ( root ) + { + case 0: + vparent = VGUI_GetScriptRootPanel( PANEL_ROOT ); + break; +#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL + case 1: + vparent = VGUI_GetScriptRootPanel( PANEL_GAMEUIDLL ); + break; +#endif + case 2: + vparent = VGUI_GetScriptRootPanel( PANEL_CLIENTDLL ); + break; +#if ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL + // Hud viewport + case 10: + Assert( g_pClientMode && g_pClientMode->GetViewport() ); + vparent = g_pClientMode->GetViewport()->GetVPanel(); + break; +#endif + default: UNREACHABLE(); // Invalid parent panel + } + + _base->SetParent( vparent ); + } +}; + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE_ROOT( Panel ) +{ +public: + void Destroy() + { + CScriptVGUIObject::Destroy(); + } + + void MakeReadyForUse() + { + __base()->MakeReadyForUse(); + } + + const char *GetName() + { + return __base()->GetName(); + } + + void AddTickSignal( int i ) + { + ivgui()->AddTickSignal( this->GetVPanel(), i ); + } +#if SCRIPT_VGUI_SIGNAL_INTERFACE + void AddActionSignalTarget( HSCRIPT messageTarget ) + { + IScriptVGUIObject *obj = ToScriptVGUIObj( messageTarget ); + if ( obj ) + { + __base()->AddActionSignalTarget( obj->GetVPanel() ); + } + } +#endif + //----------------------------------------------------- + // Get script created parent + //----------------------------------------------------- + HSCRIPT GetParent() + { + VPANEL parent = ipanel()->GetParent( this->GetVPanel() ); + if ( !parent ) + return NULL; + + int i; + IScriptVGUIObject* obj = FindInScriptPanels( parent, i ); + if ( obj ) + { + // My parent can't be invalid. + Assert( ToScriptVGUIObj( obj->GetScriptInstance() ) ); + + return obj->GetScriptInstance(); + } + +#ifdef _DEBUG + // Is my parent one of the root panels? + bool bRootParent = false; +#if SCRIPT_ENGINE_ROOT_PANELS + if ( ( parent == g_pScriptRootPanel->GetVPanel() ) + #if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL + || ( g_pScriptGameUIDLLPanel && parent == g_pScriptGameUIDLLPanel->GetVPanel() ) + #endif + || ( g_pScriptClientDLLPanel && parent == g_pScriptClientDLLPanel->GetVPanel() ) + ) + { + bRootParent = true; + } + else +#endif + for ( int i = PANEL_ROOT; i <= PANEL_CLIENTDLL_TOOLS; ++i ) + { + if ( parent == enginevgui->GetPanel( (VGuiPanel_t)i ) ) + { + bRootParent = true; + break; + } + } +#if ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL + if ( g_pClientMode && g_pClientMode->GetViewport() && ( parent == g_pClientMode->GetViewport()->GetVPanel() ) ) + bRootParent = true; +#endif + // My parent wasn't registered. + AssertMsg1( bRootParent, "'%s'", ipanel()->GetName(parent) ); +#endif + + return NULL; + } + + //----------------------------------------------------- + // Set script created parent + //----------------------------------------------------- + void SetParent( HSCRIPT parent ) + { + if ( !parent ) + { + __base()->SetParent( (VPANEL)NULL ); + return; + } + + IScriptVGUIObject *obj = ToScriptVGUIObj( parent ); + if ( obj ) + { + __base()->SetParent( obj->GetVPanel() ); + return; + } + + AssertMsg( 0, "invalid parent" ); + } + + void GetChildren( HSCRIPT arr ) + { + CUtlVector< VPANEL > &children = ipanel()->GetChildren( this->GetVPanel() ); + FOR_EACH_VEC( children, i ) + { + VPANEL child = children[i]; + int j; + IScriptVGUIObject* obj = FindInScriptPanels( child, j ); + if ( obj ) + { + g_pScriptVM->ArrayAppend( arr, obj->GetScriptInstance() ); + } + // Beware of dangling pointers if C++ created children are to be registered + } + } + + int GetXPos() + { + int x, y; + ipanel()->GetPos( this->GetVPanel(), x, y ); + return x; + } + + int GetYPos() + { + int x, y; + ipanel()->GetPos( this->GetVPanel(), x, y ); + return y; + } + + void SetPos( int x, int y ) + { + ipanel()->SetPos( this->GetVPanel(), x, y ); + } + + void SetZPos( int i ) + { + ipanel()->SetZPos( this->GetVPanel(), i ); + } + + int GetZPos() + { + return ipanel()->GetZPos( this->GetVPanel() ); + } + + void SetSize( int w, int t ) + { + ipanel()->SetSize( this->GetVPanel(), w, t ); + } + + void SetWide( int w ) + { + ipanel()->SetSize( this->GetVPanel(), w, GetTall() ); + } + + int GetWide() + { + int w, t; + ipanel()->GetSize( this->GetVPanel(), w, t ); + return w; + } + + void SetTall( int t ) + { + ipanel()->SetSize( this->GetVPanel(), GetWide(), t ); + } + + int GetTall() + { + int w, t; + ipanel()->GetSize( this->GetVPanel(), w, t ); + return t; + } + + int GetAlpha() + { + return __base()->GetAlpha(); + } + + void SetAlpha( int i ) + { + __base()->SetAlpha( i ); + } + + void SetVisible( bool i ) + { + ipanel()->SetVisible( this->GetVPanel(), i ); + } + + bool IsVisible() + { + return ipanel()->IsVisible( this->GetVPanel() ); + } +#if BUILD_GROUPS_ENABLED + void SetProportional( bool i ) + { + __base()->SetProportional(i); + } +#endif +#if 0 + void LocalToScreen( HSCRIPT out ) + { + int px, py; + ipanel()->GetAbsPos( this->GetVPanel(), px, py ); + + ScriptVariant_t x, y; + g_pScriptVM->GetValue( out, (ScriptVariant_t)0, &x ); + g_pScriptVM->GetValue( out, 1, &y ); + + g_pScriptVM->SetValue( out, (ScriptVariant_t)0, x.m_int + px ); + g_pScriptVM->SetValue( out, 1, y.m_int + py ); + } + + void ScreenToLocal( HSCRIPT out ) + { + int px, py; + ipanel()->GetAbsPos( this->GetVPanel(), px, py ); + + ScriptVariant_t x, y; + g_pScriptVM->GetValue( out, (ScriptVariant_t)0, &x ); + g_pScriptVM->GetValue( out, 1, &y ); + + g_pScriptVM->SetValue( out, (ScriptVariant_t)0, x.m_int - px ); + g_pScriptVM->SetValue( out, 1, y.m_int - py ); + } +#endif + bool IsWithin( int x, int y ) + { + return __base()->IsWithin( x, y ); + } + + void SetEnabled( bool i ) + { + __base()->SetEnabled(i); + } + + bool IsEnabled() + { + return __base()->IsEnabled(); + } + + void SetPaintEnabled( bool i ) + { + __base()->SetPaintEnabled(i); + } + + void SetPaintBackgroundEnabled( bool i ) + { + __base()->SetPaintBackgroundEnabled(i); + } + + void SetPaintBorderEnabled( bool i ) + { + __base()->SetPaintBorderEnabled(i); + } + + void SetPostChildPaintEnabled( bool i ) + { + __base()->SetPostChildPaintEnabled(i); + } + + // 0 for normal(opaque), 1 for single texture from Texture1, and 2 for rounded box w/ four corner textures + void SetPaintBackgroundType( int i ) + { + __base()->SetPaintBackgroundType(i); + } + + void SetFgColor( int r, int g, int b, int a ) + { + __base()->SetFgColor( Color( r, g, b, a ) ); + } + + void SetBgColor( int r, int g, int b, int a ) + { + __base()->SetBgColor( Color( r, g, b, a ) ); + } +#if 0 + void SetScheme( const char *tag ) + { + return __base()->SetScheme( tag ); + } +#endif + void SetCursor( int cursor ) + { + AssertMsg( cursor >= 0 && cursor < dc_last, "invalid cursor" ); + + // do nothing + if ( cursor < 0 || cursor >= dc_last ) + return; + + return __base()->SetCursor( (HCursor)cursor ); + } + + bool IsCursorOver() + { + return __base()->IsCursorOver(); + } + + bool HasFocus() + { + return __base()->HasFocus(); + } + + void RequestFocus() + { + __base()->RequestFocus(); + } + + void MakePopup() + { + __base()->MakePopup(); + } + + void MoveToFront() + { + __base()->MoveToFront(); + } + + void SetMouseInputEnabled( bool i ) + { + __base()->SetMouseInputEnabled(i); + } + + void SetKeyBoardInputEnabled( bool i ) + { + __base()->SetKeyBoardInputEnabled(i); + } + + // ----------------------- + // Drawing utility + // ----------------------- + //void SetRoundedCorners( int cornerFlags ) + //{ + // __base()->SetRoundedCorners( cornerFlags & 0xff ); + //} + + void DrawBox( int x, int y, int wide, int tall, int r, int g, int b, int a, bool hollow ) + { + __base()->DrawBox( x, y, wide, tall, Color(r, g, b, a), 1.0f, hollow ); + } + + void DrawBoxFade( int x, int y, int wide, int tall, int r, int g, int b, int a, int alpha0, int alpha1, bool bHorizontal, bool hollow ) + { + __base()->DrawBoxFade( x, y, wide, tall, Color(r, g, b, a), 1.0f, alpha0, alpha1, bHorizontal, hollow ); + } +#if 0 + // ----------------------- + // drag drop + // ----------------------- + void SetDragEnabled( bool i ) + { + __base()->SetDragEnabled(i); + } + + bool IsDragEnabled() + { + return __base()->IsDragEnabled(); + } + + void SetDropEnabled( bool i ) + { + __base()->SetDropEnabled( i, 0.0f ); + } + + bool IsDropEnabled() + { + return __base()->IsDropEnabled(); + } + + void SetShowDragHelper( int i ) + { + __base()->SetShowDragHelper(i); + } + + int GetDragStartTolerance() + { + return __base()->GetDragStartTolerance(); + } + + void SetDragStartTolerance( int i ) + { + __base()->SetDragSTartTolerance(i); + } +#endif +#if 0 + void SetTooltip( const char *text ) + { + __base()->GetTooltip()->SetText( text ); + } + + void SetTooltipDelay( int delay ) + { + __base()->GetTooltip()->SetTooltipDelay( delay ); + } +#endif +}; + +#define DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_VGUI_SCRIPT_INSTANCE_HELPER()\ +\ + DEFINE_SCRIPTFUNC( Destroy, "" )\ + DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\ + DEFINE_SCRIPTFUNC( GetName, "" )\ + DEFINE_SCRIPTFUNC( AddTickSignal, "" )\ +\ + DEFINE_SCRIPTFUNC( GetParent, "" )\ + DEFINE_SCRIPTFUNC( SetParent, "" )\ + DEFINE_SCRIPTFUNC( GetChildren, "" )\ +\ + DEFINE_SCRIPTFUNC( GetXPos, "" )\ + DEFINE_SCRIPTFUNC( GetYPos, "" )\ + DEFINE_SCRIPTFUNC( SetPos, "" )\ +\ + DEFINE_SCRIPTFUNC( GetZPos, "" )\ + DEFINE_SCRIPTFUNC( SetZPos, "" )\ +\ + DEFINE_SCRIPTFUNC( SetSize, "" )\ + DEFINE_SCRIPTFUNC( GetWide, "" )\ + DEFINE_SCRIPTFUNC( SetWide, "" )\ +\ + DEFINE_SCRIPTFUNC( GetTall, "" )\ + DEFINE_SCRIPTFUNC( SetTall, "" )\ +\ + DEFINE_SCRIPTFUNC( GetAlpha, "" )\ + DEFINE_SCRIPTFUNC( SetAlpha, "" )\ +\ + DEFINE_SCRIPTFUNC( SetVisible, "" )\ + DEFINE_SCRIPTFUNC( IsVisible, "" )\ +\ + DEFINE_SCRIPTFUNC( IsWithin, "" )\ +\ + DEFINE_SCRIPTFUNC( SetEnabled, "" )\ + DEFINE_SCRIPTFUNC( IsEnabled, "" )\ +\ + DEFINE_SCRIPTFUNC( SetPaintEnabled, "" )\ + DEFINE_SCRIPTFUNC( SetPaintBackgroundEnabled, "" )\ + DEFINE_SCRIPTFUNC( SetPaintBorderEnabled, "" )\ + DEFINE_SCRIPTFUNC( SetPostChildPaintEnabled, "" )\ + DEFINE_SCRIPTFUNC( SetPaintBackgroundType, "" )\ +\ + DEFINE_SCRIPTFUNC( SetFgColor, "" )\ + DEFINE_SCRIPTFUNC( SetBgColor, "" )\ +\ + DEFINE_SCRIPTFUNC( SetCursor, "" )\ + DEFINE_SCRIPTFUNC( IsCursorOver, "" )\ + DEFINE_SCRIPTFUNC( HasFocus, "" )\ + DEFINE_SCRIPTFUNC( RequestFocus, "" )\ + DEFINE_SCRIPTFUNC( MakePopup, "" )\ + DEFINE_SCRIPTFUNC( MoveToFront, "" )\ + DEFINE_SCRIPTFUNC( SetMouseInputEnabled, "" )\ + DEFINE_SCRIPTFUNC( SetKeyBoardInputEnabled, "" )\ +\ + DEFINE_SCRIPTFUNC( DrawBox, "" )\ + DEFINE_SCRIPTFUNC( DrawBoxFade, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// These need more testing. +// TODO: IScript_Panel::FindChildByName() +// TODO: DECLARE_BUILD_FACTORY_SCRIPT() to create overridable script panels from controls file +// TODO: CScript_EditablePanel::ApplySchemeSettings() callback +// (IScheme parameter can be passed as null until schemes are also tested) +#if BUILD_GROUPS_ENABLED +CLASS_HELPER_INTERFACE( EditablePanel, Panel ) +{ +public: + // Call on creation or on ApplySchemeSettings() + void LoadControlSettings( const char *resName ) + { + __base()->LoadControlSettings( resName ); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_EditablePanel()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( LoadControlSettings, "" ) +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE( Label, Panel ) +{ +public: + void SetText( const char *text ) + { + wchar_t wcs[512]; + g_pVGuiLocalize->ConvertANSIToUnicode( text, wcs, sizeof(wcs) ); + __base()->SetText( wcs ); + } + + void SetFont( int i ) + { + __base()->SetFont( IntToFontHandle(i) ); + } + + void SetAllCaps( bool i ) + { + __base()->SetAllCaps(i); + } + + void SetWrap( bool i ) + { + __base()->SetWrap(i); + } + + void SetCenterWrap( bool i ) + { + __base()->SetCenterWrap(i); + } + + void SetContentAlignment( int i ) + { + __base()->SetContentAlignment( (Label::Alignment)i ); + } + + void SetTextInset( int x, int y ) + { + __base()->SetTextInset( x, y ); + } + + void SizeToContents() + { + __base()->SizeToContents(); + } + + void SetAssociatedControl( HSCRIPT control ) + { + IScriptVGUIObject *obj = ToScriptVGUIObj( control ); + if ( obj ) + { + __base()->SetAssociatedControl( ipanel()->GetPanel( obj->GetVPanel(), GetControlsModuleName() ) ); + } + } + + void AddColorChange( int r, int g, int b, int a, int iTextStreamIndex ) + { + __base()->GetTextImage()->AddColorChange( Color( r, g, b, a ), iTextStreamIndex ); + } + + void ClearColorChangeStream() + { + __base()->GetTextImage()->ClearColorChangeStream(); + } +#if 0 + void SetTextImageIndex( int index ) + { + __base()->SetTextImageIndex( index ); + } + + void SetImageAtIndex( int index, const char *imageName, bool hardwareFilter, int offset ) + { + return __base()->SetImageAtIndex( index, vgui_GetImage( imageName, hardwareFilter ), offset ); + } + + int AddImage( const char *imageName, bool hardwareFilter, int offset ) + { + return __base()->AddImage( vgui_GetImage( imageName, hardwareFilter ), offset ); + } +#endif +}; + +#define DEFINE_VGUI_SCRIPTFUNC_Label()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetText, "" )\ + DEFINE_SCRIPTFUNC( SetFont, "" )\ + DEFINE_SCRIPTFUNC( SetAllCaps, "" )\ + DEFINE_SCRIPTFUNC( SetWrap, "" )\ + DEFINE_SCRIPTFUNC( SetCenterWrap, "" )\ + DEFINE_SCRIPTFUNC( SetContentAlignment, "" )\ + DEFINE_SCRIPTFUNC( SetTextInset, "" )\ + DEFINE_SCRIPTFUNC( SizeToContents, "" )\ + DEFINE_SCRIPTFUNC( SetAssociatedControl, "" )\ + DEFINE_SCRIPTFUNC( AddColorChange, "" )\ + DEFINE_SCRIPTFUNC( ClearColorChangeStream, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE( Button, Label ) +{ +public: +#if SCRIPT_VGUI_SIGNAL_INTERFACE + // Sets the command message to send to the action signal target when the button is pressed + void SetCommand( const char *command ) + { + if ( !V_strnicmp( command, "url ", 4 ) ) + { + __base()->SetCommand( (KeyValues*)NULL ); + + g_pScriptVM->RaiseException("invalid button command"); + return; + } + + __base()->SetCommand( command ); + } +#endif + void SetButtonActivationType( int activationType ) + { + __base()->SetButtonActivationType( (Button::ActivationType_t)activationType ); + } + + bool IsArmed() + { + return __base()->IsArmed(); + } + + void SetArmed( bool state ) + { + __base()->SetArmed(state); + } + + bool IsSelected() + { + return __base()->IsSelected(); + } + + void SetSelected( bool state ) + { + __base()->SetSelected(state); + } + + bool IsDepressed() + { + return __base()->IsDepressed(); + } + + void ForceDepressed( bool state ) + { + __base()->ForceDepressed(state); + } + + void SetMouseClickEnabled( int code, bool state ) + { + __base()->SetMouseClickEnabled( (MouseCode)code, state ); + } + + bool IsMouseClickEnabled( int code ) + { + return __base()->IsMouseClickEnabled( (MouseCode)code ); + } + + void SetDefaultColor( int fr, int fg, int fb, int fa, int br, int bg, int bb, int ba ) + { + __base()->SetDefaultColor( Color(fr, fg, fb, fa), Color(br, bg, bb, ba) ); + } + + void SetArmedColor( int fr, int fg, int fb, int fa, int br, int bg, int bb, int ba ) + { + __base()->SetArmedColor( Color(fr, fg, fb, fa), Color(br, bg, bb, ba) ); + } + + void SetSelectedColor( int fr, int fg, int fb, int fa, int br, int bg, int bb, int ba ) + { + __base()->SetSelectedColor( Color(fr, fg, fb, fa), Color(br, bg, bb, ba) ); + } + + void SetDepressedColor( int fr, int fg, int fb, int fa, int br, int bg, int bb, int ba ) + { + __base()->SetDepressedColor( Color(fr, fg, fb, fa), Color(br, bg, bb, ba) ); + } + + void SetArmedSound( const char *sound ) + { + __base()->SetArmedSound( sound ); + } + + void SetDepressedSound( const char *sound ) + { + __base()->SetDepressedSound( sound ); + } + + void SetReleasedSound( const char *sound ) + { + __base()->SetReleasedSound( sound ); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_Button()\ + DEFINE_VGUI_SCRIPTFUNC_Label()\ + DEFINE_SCRIPTFUNC( SetButtonActivationType, "" )\ + DEFINE_SCRIPTFUNC( IsArmed, "" )\ + DEFINE_SCRIPTFUNC( SetArmed, "" )\ + DEFINE_SCRIPTFUNC( IsSelected, "" )\ + DEFINE_SCRIPTFUNC( SetSelected, "" )\ + DEFINE_SCRIPTFUNC( IsDepressed, "" )\ + DEFINE_SCRIPTFUNC( ForceDepressed, "" )\ + DEFINE_SCRIPTFUNC( SetMouseClickEnabled, "" )\ + DEFINE_SCRIPTFUNC( IsMouseClickEnabled, "" )\ + DEFINE_SCRIPTFUNC( SetDefaultColor, "" )\ + DEFINE_SCRIPTFUNC( SetArmedColor, "" )\ + DEFINE_SCRIPTFUNC( SetSelectedColor, "" )\ + DEFINE_SCRIPTFUNC( SetDepressedColor, "" )\ + DEFINE_SCRIPTFUNC( SetArmedSound, "" )\ + DEFINE_SCRIPTFUNC( SetDepressedSound, "" )\ + DEFINE_SCRIPTFUNC( SetReleasedSound, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE( ImagePanel, Panel ) +{ +public: + void SetImage( const char *imageName, bool hardwareFilter ) + { + __base()->EvictImage(); + __base()->SetImage( vgui_GetImage( imageName, hardwareFilter ) ); + } + + void SetDrawColor( int r, int g, int b, int a ) + { + __base()->SetDrawColor( Color( r, g, b, a ) ); + } + + void SetTileImage( bool bTile ) + { + __base()->SetTileImage( bTile ); + } + + void SetShouldScaleImage( bool state ) + { + __base()->SetShouldScaleImage( state ); + } +#if 0 + void SetFrame( int nFrame ) + { + __base()->SetFrame( nFrame ); + } +#endif +}; + +#define DEFINE_VGUI_SCRIPTFUNC_ImagePanel()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetImage, "" )\ + DEFINE_SCRIPTFUNC( SetDrawColor, "" )\ + DEFINE_SCRIPTFUNC( SetTileImage, "" )\ + DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE( Frame, Panel ) +{ +public: + void SetMinimumSize( int wide, int tall ) + { + __base()->SetMinimumSize( wide, tall ); + } + + void SetTitle( const char* titel ) + { + __base()->SetTitle( titel, false ); + } + + void Close() + { + __base()->Close(); + } + + void SetDeleteSelfOnClose( bool state ) + { + __base()->SetDeleteSelfOnClose( state ); + } + + void SetMoveable( bool state ) + { + __base()->SetMoveable( state ); + } + + void SetSizeable( bool state ) + { + __base()->SetSizeable( state ); + } + + void SetCloseButtonVisible( bool state ) + { + __base()->SetCloseButtonVisible( state ); + } + + void SetTitleBarVisible( bool state ) + { + __base()->SetTitleBarVisible( state ); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_Frame()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetMinimumSize, "" )\ + DEFINE_SCRIPTFUNC( SetTitle, "" )\ + DEFINE_SCRIPTFUNC( Close, "" )\ + DEFINE_SCRIPTFUNC( SetDeleteSelfOnClose, "" )\ + DEFINE_SCRIPTFUNC( SetMoveable, "" )\ + DEFINE_SCRIPTFUNC( SetSizeable, "" )\ + DEFINE_SCRIPTFUNC( SetCloseButtonVisible, "" )\ + DEFINE_SCRIPTFUNC( SetTitleBarVisible, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE( RichText, Panel ) +{ +public: + void SetText( const char* text ) + { + __base()->SetText( text ); + } + + void SetFont( int font ) + { + __base()->SetFont( IntToFontHandle(font) ); + } + + void InsertString( const char* text ) + { + __base()->InsertString( text ); + } + + void SetPanelInteractive( bool bInteractive ) + { + __base()->SetPanelInteractive( bInteractive ); + } + + void SetUnusedScrollbarInvisible( bool bInvis ) + { + __base()->SetUnusedScrollbarInvisible( bInvis ); + } + + void GotoTextStart() + { + __base()->GotoTextStart(); + } + + void GotoTextEnd() + { + __base()->GotoTextEnd(); + } + + void SetMaximumCharCount( int maxChars ) + { + __base()->SetMaximumCharCount( maxChars ); + } + + void InsertColorChange( int r, int g, int b, int a ) + { + __base()->InsertColorChange( Color( r, g, b, a ) ); + } + + int GetNumLines() + { + return __base()->GetNumLines(); + } + + void SetDrawTextOnly() + { + __base()->SetDrawTextOnly(); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_RichText()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetText, "" )\ + DEFINE_SCRIPTFUNC( SetFont, "" )\ + DEFINE_SCRIPTFUNC( InsertString, "" )\ + DEFINE_SCRIPTFUNC( SetPanelInteractive, "" )\ + DEFINE_SCRIPTFUNC( SetUnusedScrollbarInvisible, "" )\ + DEFINE_SCRIPTFUNC( GotoTextStart, "" )\ + DEFINE_SCRIPTFUNC( GotoTextEnd, "" )\ + DEFINE_SCRIPTFUNC( SetMaximumCharCount, "" )\ + DEFINE_SCRIPTFUNC( InsertColorChange, "" )\ + DEFINE_SCRIPTFUNC( GetNumLines, "" )\ + DEFINE_SCRIPTFUNC( SetDrawTextOnly, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +CLASS_HELPER_INTERFACE( TextEntry, Panel ) +{ +public: + void SetText( const char* text ) + { + wchar_t wcs[512]; + g_pVGuiLocalize->ConvertANSIToUnicode( text, wcs, sizeof(wcs) ); + __base()->SetText( wcs ); + } + + const char *GetText() + { + static char sz[512]; + __base()->GetText( sz, sizeof(sz) ); + return sz; + } + + void SetFont( int font ) + { + __base()->SetFont( IntToFontHandle(font) ); + } + + void SetEditable( bool state ) + { + __base()->SetEditable( state ); + } + + void GotoTextStart() + { + __base()->GotoTextStart(); + } + + void GotoTextEnd() + { + __base()->GotoTextEnd(); + } + + void InsertString( const char* text ) + { + __base()->InsertString( text ); + } + + void SelectNone() + { + __base()->SelectNone(); + } + + void SetMultiline( bool state ) + { + __base()->SetMultiline( state ); + } + + void SetVerticalScrollbar( bool state ) + { + __base()->SetVerticalScrollbar( state ); + } +#if 0 + void SetHorizontalScrolling( bool status ) + { + __base()->SetHorizontalScrolling( status ); + } +#endif + void SetCatchEnterKey( bool state ) + { + __base()->SetCatchEnterKey( state ); + } + + void SetMaximumCharCount( int maxChars ) + { + __base()->SetMaximumCharCount( maxChars ); + } +#if 0 + void SetWrap( bool wrap ) + { + __base()->SetWrap( wrap ); + } +#endif + void SetAllowNumericInputOnly( bool state ) + { + __base()->SetAllowNumericInputOnly( state ); + } +#if 0 + void SetDisabledBgColor( int r, int g, int b, int a ) + { + __base()->SetDisabledBgColor( Color( r, g, b, a ) ); + } + + void SetSelectionTextColor( int r, int g, int b, int a ) + { + __base()->SetSelectionTextColor( Color( r, g, b, a ) ); + } + + void SetSelectionBgColor( int r, int g, int b, int a ) + { + __base()->SetSelectionBgColor( Color( r, g, b, a ) ); + } + + void SetSelectionUnfocusedBgColor( int r, int g, int b, int a ) + { + __base()->SetSelectionUnfocusedBgColor( Color( r, g, b, a ) ); + } +#endif +}; + +#define DEFINE_VGUI_SCRIPTFUNC_TextEntry()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetText, "" )\ + DEFINE_SCRIPTFUNC( GetText, "" )\ + DEFINE_SCRIPTFUNC( SetFont, "" )\ + DEFINE_SCRIPTFUNC( SetEditable, "" )\ + DEFINE_SCRIPTFUNC( GotoTextStart, "" )\ + DEFINE_SCRIPTFUNC( GotoTextEnd, "" )\ + DEFINE_SCRIPTFUNC( InsertString, "" )\ + DEFINE_SCRIPTFUNC( SelectNone, "" )\ + DEFINE_SCRIPTFUNC( SetMultiline, "" )\ + DEFINE_SCRIPTFUNC( SetVerticalScrollbar, "" )\ + DEFINE_SCRIPTFUNC( SetCatchEnterKey, "" )\ + DEFINE_SCRIPTFUNC( SetMaximumCharCount, "" )\ + DEFINE_SCRIPTFUNC( SetAllowNumericInputOnly, "" )\ + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if !defined(NO_STEAM) +CLASS_HELPER_INTERFACE( AvatarImage, Panel ) +{ +public: + void SetPlayer( const char *steam2id, int eAvatarSize ) + { + uint32 __SteamInstanceID; + uint32 __SteamLocalUserID_Low32Bits; + uint32 __SteamLocalUserID_High32Bits; + + int c = sscanf( steam2id, "STEAM_%u:%u:%u", + &__SteamInstanceID, &__SteamLocalUserID_High32Bits, &__SteamLocalUserID_Low32Bits ); + + if ( c < 3 ) + return; + + CSteamID id( __SteamLocalUserID_Low32Bits * 2 + __SteamLocalUserID_High32Bits, + k_EUniversePublic, + k_EAccountTypeIndividual ); + + __base()->SetPlayer( id, (EAvatarSize)eAvatarSize ); + } + + void SetPlayerByIndex( int entindex, int eAvatarSize ) + { + if ( !entindex ) + { + __base()->ClearAvatar(); + return; + } + + __base()->SetPlayer( entindex, (EAvatarSize)eAvatarSize ); + } + + void SetDefaultAvatar( const char *imageName ) + { + __base()->SetDefaultAvatar( vgui_GetImage( imageName, false ) ); + } + + void SetShouldScaleImage( bool state ) + { + __base()->SetShouldScaleImage( state ); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_AvatarImage()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetPlayer, "" )\ + DEFINE_SCRIPTFUNC( SetPlayerByIndex, "" )\ + DEFINE_SCRIPTFUNC( SetDefaultAvatar, "" )\ + DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" ) +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if VGUI_TGA_IMAGE_PANEL +CLASS_HELPER_INTERFACE( TGAImagePanel, Panel ) +{ +public: + void SetTGAImage( const char *p ) + { + __base()->SetTGAImage( p ); + } + + void SetDrawColor( int r, int g, int b, int a ) + { + __base()->SetDrawColor( r, g, b, a ); + } + + void SetShouldScaleImage( bool i ) + { + __base()->SetShouldScaleImage( i ); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_TGAImagePanel()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetTGAImage, "" )\ + DEFINE_SCRIPTFUNC( SetDrawColor, "" )\ + DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" ) +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + + +//============================================================== +//============================================================== + + +static inline void SetHScript( HSCRIPT &var, HSCRIPT val ) +{ + if ( var && g_pScriptVM ) + g_pScriptVM->ReleaseScript( var ); + var = val; +} + +#define CheckCallback(s)\ + if ( FStrEq( cb, #s ) )\ + {\ + SetHScript( m_hfn##s, fn );\ + return;\ + } + +//-------------------------------------------------------- +// C++ objects for vgui overrides and messages. +//-------------------------------------------------------- + + +class CScript_Panel : public Panel +{ + DECLARE_SCRIPTVGUI_CLASS( Panel ); + +private: + HSCRIPT m_hfnPaint; + HSCRIPT m_hfnPaintBackground; + HSCRIPT m_hfnPostChildPaint; + + HSCRIPT m_hfnPerformLayout; + HSCRIPT m_hfnOnTick; + HSCRIPT m_hfnOnScreenSizeChanged; + + HSCRIPT m_hfnOnCursorEntered; + HSCRIPT m_hfnOnCursorExited; + HSCRIPT m_hfnOnCursorMoved; + + HSCRIPT m_hfnOnMousePressed; + HSCRIPT m_hfnOnMouseDoublePressed; + HSCRIPT m_hfnOnMouseReleased; + HSCRIPT m_hfnOnMouseWheeled; + + HSCRIPT m_hfnOnKeyCodePressed; + HSCRIPT m_hfnOnKeyCodeReleased; + HSCRIPT m_hfnOnKeyCodeTyped; +#if SCRIPT_VGUI_SIGNAL_INTERFACE + HSCRIPT m_hfnOnCommand; +#endif + +public: + CScript_Panel( Panel *parent, const char *name ) : + BaseClass( parent, name ), + + m_hfnPaint(NULL), + m_hfnPaintBackground(NULL), + m_hfnPostChildPaint(NULL), + + m_hfnPerformLayout(NULL), + m_hfnOnTick(NULL), + m_hfnOnScreenSizeChanged(NULL), + + m_hfnOnCursorEntered(NULL), + m_hfnOnCursorExited(NULL), + m_hfnOnCursorMoved(NULL), + + m_hfnOnMousePressed(NULL), + m_hfnOnMouseDoublePressed(NULL), + m_hfnOnMouseReleased(NULL), + m_hfnOnMouseWheeled(NULL), + + m_hfnOnKeyCodePressed(NULL), + m_hfnOnKeyCodeReleased(NULL), + m_hfnOnKeyCodeTyped(NULL) +#if SCRIPT_VGUI_SIGNAL_INTERFACE + , + m_hfnOnCommand(NULL) +#endif + {} + + void Shutdown() + { + ivgui()->RemoveTickSignal( GetVPanel() ); + + SetHScript( m_hfnPaint, NULL ); + SetHScript( m_hfnPaintBackground, NULL ); + SetHScript( m_hfnPostChildPaint, NULL ); + + SetHScript( m_hfnPerformLayout, NULL ); + SetHScript( m_hfnOnTick, NULL ); + SetHScript( m_hfnOnScreenSizeChanged, NULL ); + + SetHScript( m_hfnOnCursorEntered, NULL ); + SetHScript( m_hfnOnCursorExited, NULL ); + SetHScript( m_hfnOnCursorMoved, NULL ); + + SetHScript( m_hfnOnMousePressed, NULL ); + SetHScript( m_hfnOnMouseDoublePressed, NULL ); + SetHScript( m_hfnOnMouseReleased, NULL ); + SetHScript( m_hfnOnMouseWheeled, NULL ); + + SetHScript( m_hfnOnKeyCodePressed, NULL ); + SetHScript( m_hfnOnKeyCodeReleased, NULL ); + SetHScript( m_hfnOnKeyCodeTyped, NULL ); +#if SCRIPT_VGUI_SIGNAL_INTERFACE + SetHScript( m_hfnOnCommand, NULL ); +#endif + } + +public: + void Paint() + { + g_pScriptVM->ExecuteFunction( m_hfnPaint, NULL, 0, NULL, NULL, true ); + } + + void PaintBackground() + { + if ( m_hfnPaintBackground ) + { + g_pScriptVM->ExecuteFunction( m_hfnPaintBackground, NULL, 0, NULL, NULL, true ); + } + else + { + BaseClass::PaintBackground(); + } + } + + void PostChildPaint() + { + g_pScriptVM->ExecuteFunction( m_hfnPostChildPaint, NULL, 0, NULL, NULL, true ); + } + + void PerformLayout() + { + BaseClass::PerformLayout(); + + if ( m_hfnPerformLayout ) + { + g_pScriptVM->ExecuteFunction( m_hfnPerformLayout, NULL, 0, NULL, NULL, true ); + } + } + + void OnTick() + { + g_pScriptVM->ExecuteFunction( m_hfnOnTick, NULL, 0, NULL, NULL, true ); + } + + void OnScreenSizeChanged( int oldwide, int oldtall ) + { + BaseClass::OnScreenSizeChanged( oldwide, oldtall ); + + if ( m_hfnOnScreenSizeChanged ) + { + ScriptVariant_t args[2] = { oldwide, oldtall }; + g_pScriptVM->ExecuteFunction( m_hfnOnScreenSizeChanged, args, 2, NULL, NULL, true ); + } + } +#if SCRIPT_VGUI_SIGNAL_INTERFACE + void OnCommand( const char *command ) + { + if ( m_hfnOnCommand ) + { + ScriptVariant_t ret, arg = command; + g_pScriptVM->ExecuteFunction( m_hfnOnCommand, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnCommand( command ); + } +#endif + void OnCursorEntered() + { + if ( m_hfnOnCursorEntered ) + { + g_pScriptVM->ExecuteFunction( m_hfnOnCursorEntered, NULL, 0, NULL, NULL, true ); + } + } + + void OnCursorExited() + { + if ( m_hfnOnCursorExited ) + { + g_pScriptVM->ExecuteFunction( m_hfnOnCursorExited, NULL, 0, NULL, NULL, true ); + } + } + + void OnCursorMoved( int x, int y ) + { + if ( m_hfnOnCursorMoved ) + { + ScriptVariant_t args[2] = { x, y }; + g_pScriptVM->ExecuteFunction( m_hfnOnCursorMoved, args, 2, NULL, NULL, true ); + } + else + { + Assert( !ParentNeedsCursorMoveEvents() ); + } + } + + void OnMousePressed( MouseCode code ) + { + if ( m_hfnOnMousePressed ) + { + ScriptVariant_t arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnMousePressed, &arg, 1, NULL, NULL, true ); + } + } + + void OnMouseDoublePressed( MouseCode code ) + { + if ( m_hfnOnMouseDoublePressed ) + { + ScriptVariant_t arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnMouseDoublePressed, &arg, 1, NULL, NULL, true ); + } + } + + void OnMouseReleased( MouseCode code ) + { + if ( m_hfnOnMouseReleased ) + { + ScriptVariant_t arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnMouseReleased, &arg, 1, NULL, NULL, true ); + } + } + + void OnMouseWheeled( int delta ) + { + if ( m_hfnOnMouseWheeled ) + { + ScriptVariant_t arg = (int)delta; + g_pScriptVM->ExecuteFunction( m_hfnOnMouseWheeled, &arg, 1, NULL, NULL, true ); + } + } + + void OnKeyCodePressed( KeyCode code ) + { + if ( m_hfnOnKeyCodePressed ) + { + ScriptVariant_t ret, arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnKeyCodePressed, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnKeyCodePressed( code ); + } + + void OnKeyCodeReleased( KeyCode code ) + { + if ( m_hfnOnKeyCodeReleased ) + { + ScriptVariant_t ret, arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnKeyCodeReleased, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnKeyCodeReleased( code ); + } + + void OnKeyCodeTyped( KeyCode code ) + { + if ( m_hfnOnKeyCodeTyped ) + { + ScriptVariant_t ret, arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnKeyCodeTyped, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnKeyCodeTyped( code ); + } + +public: + void SetCallback( const char* cb, HSCRIPT fn ) + { + CheckCallback( Paint ); + CheckCallback( PaintBackground ); + CheckCallback( PostChildPaint ); + + CheckCallback( PerformLayout ); + CheckCallback( OnTick ); + CheckCallback( OnScreenSizeChanged ); + + CheckCallback( OnCursorEntered ); + CheckCallback( OnCursorExited ); + CheckCallback( OnCursorMoved ); + + CheckCallback( OnMousePressed ); + CheckCallback( OnMouseDoublePressed ); + CheckCallback( OnMouseReleased ); + CheckCallback( OnMouseWheeled ); + + CheckCallback( OnKeyCodePressed ); + CheckCallback( OnKeyCodeReleased ); + CheckCallback( OnKeyCodeTyped ); +#if SCRIPT_VGUI_SIGNAL_INTERFACE + CheckCallback( OnCommand ); +#endif + + g_pScriptVM->RaiseException("invalid callback"); + } +}; + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +class CScript_Frame : public Frame +{ + DECLARE_SCRIPTVGUI_CLASS( Frame ); + +private: + HSCRIPT m_hfnPaint; + HSCRIPT m_hfnPaintBackground; + + HSCRIPT m_hfnPerformLayout; + HSCRIPT m_hfnOnTick; + HSCRIPT m_hfnOnScreenSizeChanged; + + HSCRIPT m_hfnOnCursorEntered; + HSCRIPT m_hfnOnCursorExited; + HSCRIPT m_hfnOnCursorMoved; + + HSCRIPT m_hfnOnMousePressed; + HSCRIPT m_hfnOnMouseDoublePressed; + HSCRIPT m_hfnOnMouseReleased; + HSCRIPT m_hfnOnMouseWheeled; + + HSCRIPT m_hfnOnKeyCodePressed; + HSCRIPT m_hfnOnKeyCodeReleased; + HSCRIPT m_hfnOnKeyCodeTyped; +#if SCRIPT_VGUI_SIGNAL_INTERFACE + HSCRIPT m_hfnOnCommand; +#endif + +public: + CScript_Frame( Panel *parent, const char *name ) : + + // Start without popup + BaseClass( parent, name, false, false ), + + m_hfnPaint(NULL), + m_hfnPaintBackground(NULL), + + m_hfnPerformLayout(NULL), + m_hfnOnTick(NULL), + m_hfnOnScreenSizeChanged(NULL), + + m_hfnOnCursorEntered(NULL), + m_hfnOnCursorExited(NULL), + m_hfnOnCursorMoved(NULL), + + m_hfnOnMousePressed(NULL), + m_hfnOnMouseDoublePressed(NULL), + m_hfnOnMouseReleased(NULL), + m_hfnOnMouseWheeled(NULL), + + m_hfnOnKeyCodePressed(NULL), + m_hfnOnKeyCodeReleased(NULL), + m_hfnOnKeyCodeTyped(NULL) +#if SCRIPT_VGUI_SIGNAL_INTERFACE + , + m_hfnOnCommand(NULL) +#endif + { + SetFadeEffectDisableOverride( true ); + } + + void Shutdown() + { + ivgui()->RemoveTickSignal( GetVPanel() ); + + SetHScript( m_hfnPaint, NULL ); + SetHScript( m_hfnPaintBackground, NULL ); + + SetHScript( m_hfnPerformLayout, NULL ); + SetHScript( m_hfnOnTick, NULL ); + SetHScript( m_hfnOnScreenSizeChanged, NULL ); + + SetHScript( m_hfnOnMousePressed, NULL ); + SetHScript( m_hfnOnMouseDoublePressed, NULL ); + SetHScript( m_hfnOnMouseReleased, NULL ); + SetHScript( m_hfnOnMouseWheeled, NULL ); + + SetHScript( m_hfnOnKeyCodePressed, NULL ); + SetHScript( m_hfnOnKeyCodeReleased, NULL ); + SetHScript( m_hfnOnKeyCodeTyped, NULL ); +#if SCRIPT_VGUI_SIGNAL_INTERFACE + SetHScript( m_hfnOnCommand, NULL ); +#endif + } + +public: + void Paint() + { + g_pScriptVM->ExecuteFunction( m_hfnPaint, NULL, 0, NULL, NULL, true ); + } + + void PaintBackground() + { + if ( m_hfnPaintBackground ) + { + g_pScriptVM->ExecuteFunction( m_hfnPaintBackground, NULL, 0, NULL, NULL, true ); + } + else + { + BaseClass::PaintBackground(); + } + } + + void PerformLayout() + { + BaseClass::PerformLayout(); + + if ( m_hfnPerformLayout ) + { + g_pScriptVM->ExecuteFunction( m_hfnPerformLayout, NULL, 0, NULL, NULL, true ); + } + } + + void OnTick() + { + g_pScriptVM->ExecuteFunction( m_hfnOnTick, NULL, 0, NULL, NULL, true ); + } + + void OnScreenSizeChanged( int oldwide, int oldtall ) + { + BaseClass::OnScreenSizeChanged( oldwide, oldtall ); + + if ( m_hfnOnScreenSizeChanged ) + { + ScriptVariant_t args[2] = { oldwide, oldtall }; + g_pScriptVM->ExecuteFunction( m_hfnOnScreenSizeChanged, args, 2, NULL, NULL, true ); + } + } +#if SCRIPT_VGUI_SIGNAL_INTERFACE + void OnCommand( const char *command ) + { + if ( m_hfnOnCommand ) + { + ScriptVariant_t ret, arg = command; + g_pScriptVM->ExecuteFunction( m_hfnOnCommand, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnCommand( command ); + } +#endif + void OnCursorEntered() + { + if ( m_hfnOnCursorEntered ) + { + g_pScriptVM->ExecuteFunction( m_hfnOnCursorEntered, NULL, 0, NULL, NULL, true ); + } + } + + void OnCursorExited() + { + if ( m_hfnOnCursorExited ) + { + g_pScriptVM->ExecuteFunction( m_hfnOnCursorExited, NULL, 0, NULL, NULL, true ); + } + } + + void OnCursorMoved( int x, int y ) + { + if ( m_hfnOnCursorMoved ) + { + ScriptVariant_t args[2] = { x, y }; + g_pScriptVM->ExecuteFunction( m_hfnOnCursorMoved, args, 2, NULL, NULL, true ); + } + else + { + Assert( !ParentNeedsCursorMoveEvents() ); + } + } + + void OnMousePressed( MouseCode code ) + { + BaseClass::OnMousePressed( code ); + + if ( m_hfnOnMousePressed ) + { + ScriptVariant_t arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnMousePressed, &arg, 1, NULL, NULL, true ); + } + } + + void OnMouseDoublePressed( MouseCode code ) + { + if ( m_hfnOnMouseDoublePressed ) + { + ScriptVariant_t arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnMouseDoublePressed, &arg, 1, NULL, NULL, true ); + } + } + + void OnMouseReleased( MouseCode code ) + { + if ( m_hfnOnMouseReleased ) + { + ScriptVariant_t arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnMouseReleased, &arg, 1, NULL, NULL, true ); + } + } + + void OnMouseWheeled( int delta ) + { + if ( m_hfnOnMouseWheeled ) + { + ScriptVariant_t arg = (int)delta; + g_pScriptVM->ExecuteFunction( m_hfnOnMouseWheeled, &arg, 1, NULL, NULL, true ); + } + } + + void OnKeyCodePressed( KeyCode code ) + { + if ( m_hfnOnKeyCodePressed ) + { + ScriptVariant_t ret, arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnKeyCodePressed, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnKeyCodePressed( code ); + } + + void OnKeyCodeReleased( KeyCode code ) + { + if ( m_hfnOnKeyCodeReleased ) + { + ScriptVariant_t ret, arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnKeyCodeReleased, &arg, 1, &ret, NULL, true ); + + // Return true to swallow + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + } + + BaseClass::OnKeyCodeReleased( code ); + } + + void OnKeyCodeTyped( KeyCode code ) + { + if ( m_hfnOnKeyCodeTyped ) + { + ScriptVariant_t ret, arg = (int)code; + g_pScriptVM->ExecuteFunction( m_hfnOnKeyCodeTyped, &arg, 1, &ret, NULL, true ); + + // Return true to swallow the CanChainKeysToParent() override check and fallback, + // which by default swallows the input. + if ( ret.m_type == FIELD_BOOLEAN && ret.m_bool ) + return; + + if ( CanChainKeysToParent() ) + { + BaseClass::OnKeyCodeTyped( code ); + } + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } + } + +public: + void SetCallback( const char* cb, HSCRIPT fn ) + { + CheckCallback( Paint ); + CheckCallback( PaintBackground ); + + CheckCallback( PerformLayout ); + CheckCallback( OnTick ); + CheckCallback( OnScreenSizeChanged ); + + CheckCallback( OnCursorEntered ); + CheckCallback( OnCursorExited ); + CheckCallback( OnCursorMoved ); + + CheckCallback( OnMousePressed ); + CheckCallback( OnMouseDoublePressed ); + CheckCallback( OnMouseReleased ); + CheckCallback( OnMouseWheeled ); + + CheckCallback( OnKeyCodePressed ); + CheckCallback( OnKeyCodeReleased ); + CheckCallback( OnKeyCodeTyped ); +#if SCRIPT_VGUI_SIGNAL_INTERFACE + CheckCallback( OnCommand ); +#endif + + g_pScriptVM->RaiseException("invalid callback"); + } +}; + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +class CScript_Button : public Button +{ + DECLARE_SCRIPTVGUI_CLASS( Button ); + +private: + HSCRIPT m_hfnPaint; + HSCRIPT m_hfnPaintBackground; + HSCRIPT m_hfnDoClick; + +public: + CScript_Button( Panel *parent, const char *name, const char *text ) : + BaseClass( parent, name, text ), + + m_hfnPaint(NULL), + m_hfnPaintBackground(NULL), + + m_hfnDoClick(NULL) + {} + + void Shutdown() + { + SetHScript( m_hfnPaint, NULL ); + SetHScript( m_hfnPaintBackground, NULL ); + + SetHScript( m_hfnDoClick, NULL ); + } + +public: + void Paint() + { + if ( m_hfnPaint ) + { + g_pScriptVM->ExecuteFunction( m_hfnPaint, NULL, 0, NULL, NULL, true ); + } + else + { + BaseClass::Paint(); + } + } + + void PaintBackground() + { + if ( m_hfnPaintBackground ) + { + g_pScriptVM->ExecuteFunction( m_hfnPaintBackground, NULL, 0, NULL, NULL, true ); + } + else + { + BaseClass::PaintBackground(); + } + } + + void DoClick() + { + BaseClass::DoClick(); + + if ( m_hfnDoClick ) + { + g_pScriptVM->ExecuteFunction( m_hfnDoClick, NULL, 0, NULL, NULL, true ); + } + } + +public: + void SetCallback( const char* cb, HSCRIPT fn ) + { + CheckCallback( Paint ); + CheckCallback( PaintBackground ); + CheckCallback( DoClick ); + + g_pScriptVM->RaiseException("invalid callback"); + } +}; + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +class CScript_TextEntry : public TextEntry +{ + DECLARE_SCRIPTVGUI_CLASS( TextEntry ); + +private: + HSCRIPT m_hfnTextChanged; + +public: + CScript_TextEntry( Panel *parent, const char *name ) : + BaseClass( parent, name ), + + m_hfnTextChanged(NULL) + {} + + void Shutdown() + { + SetHScript( m_hfnTextChanged, NULL ); + } + +public: + //--------------------------------------------- + // On "TextMessage" message. + // Used for responding to user input as it is typed. + //--------------------------------------------- + void FireActionSignal() + { + BaseClass::FireActionSignal(); + + if ( m_hfnTextChanged ) + { + g_pScriptVM->ExecuteFunction( m_hfnTextChanged, NULL, 0, NULL, NULL, true ); + } + } + +public: + void SetCallback( const char* cb, HSCRIPT fn ) + { + CheckCallback( TextChanged ); + + g_pScriptVM->RaiseException("invalid callback"); + } +}; + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if !defined(NO_STEAM) +class CScript_AvatarImage : public CAvatarImagePanel +{ + DECLARE_SCRIPTVGUI_CLASS_EX( CScript_AvatarImage, CAvatarImagePanel ); + +public: + CScript_AvatarImage( Panel *parent, const char *name ) : + BaseClass( parent, name ) + { + SetShouldDrawFriendIcon( false ); + } + + DEBUG_DESTRUCTOR( ~CScript_AvatarImage, CAvatarImagePanel ) + + void Shutdown() {} +}; +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if VGUI_TGA_IMAGE_PANEL +class CTGAImagePanel : public Panel +{ + DECLARE_SCRIPTVGUI_CLASS_EX( CTGAImagePanel, Panel ); + +private: + int m_iTextureID; + int m_nWidth; + int m_nHeight; + Color m_ImageColor; + bool m_bScaleImage; + +public: + CTGAImagePanel( Panel *parent, const char *name ) : + BaseClass( parent, name ), + m_iTextureID(-1), + m_bScaleImage(0), + m_ImageColor( 255, 255, 255, 255 ) + { + SetPaintBackgroundEnabled( false ); + } + + ~CTGAImagePanel() + { + DebugDestructor( CTGAImagePanel ); + + if ( m_iTextureID != -1 ) + { + surface()->DestroyTextureID( m_iTextureID ); + } + } + + void Shutdown() {} + +public: + void Paint() + { + if ( m_iTextureID != -1 ) + { + surface()->DrawSetColor( m_ImageColor ); + surface()->DrawSetTexture( m_iTextureID ); + + if ( m_bScaleImage ) + { + int w, t; + GetSize( w, t ); + surface()->DrawTexturedRect( 0, 0, w, t ); + } + else + { + surface()->DrawTexturedRect( 0, 0, m_nWidth, m_nHeight ); + } + } + else + { + int w, t; + GetSize( w, t ); + surface()->DrawSetColor( 200, 50, 150, 255 ); + surface()->DrawFilledRect( 0, 0, w, t ); + } + } + +public: + void SetTGAImage( const char *fileName ) + { + if ( V_stricmp( V_GetFileExtension( fileName ), "tga" ) != 0 ) + return; + + CUtlMemory< unsigned char > tga; + + if ( TGALoader::LoadRGBA8888( fileName, tga, m_nWidth, m_nHeight ) ) + { + if ( m_iTextureID == -1 ) + { + m_iTextureID = surface()->CreateNewTextureID( true ); + } + + surface()->DrawSetTextureRGBA( m_iTextureID, tga.Base(), m_nWidth, m_nHeight, false, false ); + } + else + { + Warning( "Failed to load TGA image: '%s'\n", fileName ); + } + } + + void SetDrawColor( int r, int g, int b, int a ) + { + m_ImageColor.SetColor( r, g, b, a ); + } + + void SetShouldScaleImage( bool state ) + { + m_bScaleImage = state; + } +}; +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + + +//============================================================== +//============================================================== + +//-------------------------------------------------------- +// Script objects +//-------------------------------------------------------- + +DEFINE_VGUI_CLASS_EMPTY_DEFAULT_TEXT( Label ) +DEFINE_VGUI_CLASS_EMPTY( ImagePanel ) +DEFINE_VGUI_CLASS_EMPTY( RichText ) + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +BEGIN_VGUI_HELPER( Panel ) + void SetCallback( const char *a, HSCRIPT b ) { __base()->SetCallback( a, b ); } +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( Panel ) + DEFINE_SCRIPTFUNC( SetCallback, "" ) +END_SCRIPTDESC() + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +BEGIN_VGUI_HELPER( Frame ) + void SetCallback( const char *a, HSCRIPT b ) { __base()->SetCallback( a, b ); } +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( Frame ) + DEFINE_SCRIPTFUNC( SetCallback, "" ) +END_SCRIPTDESC() + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +BEGIN_VGUI_HELPER_DEFAULT_TEXT( Button ) + void SetCallback( const char *a, HSCRIPT b ) { __base()->SetCallback( a, b ); } +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( Button ) + DEFINE_SCRIPTFUNC( SetCallback, "" ) +END_SCRIPTDESC() + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +BEGIN_VGUI_HELPER( TextEntry ) + void SetCallback( const char *a, HSCRIPT b ) { __base()->SetCallback( a, b ); } +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( TextEntry ) + DEFINE_SCRIPTFUNC( SetCallback, "" ) +END_SCRIPTDESC() + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if !defined(NO_STEAM) +BEGIN_VGUI_HELPER( AvatarImage ) +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( AvatarImage ) +END_SCRIPTDESC() +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if VGUI_TGA_IMAGE_PANEL +BEGIN_VGUI_HELPER_EX( TGAImagePanel, CTGAImagePanel ) +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( TGAImagePanel ) +END_SCRIPTDESC() +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + + +//============================================================== +//============================================================== + + +struct hudelementcache_t +{ + CUtlConstString name; + int bits; +}; +CUtlVector< hudelementcache_t > m_HudElementCache; + +// Check if hud elements were changed in this level to shortcut on level shutdown +bool m_bHudVisiblityChangedThisLevel = false; + + + +class CScriptVGUI : public CAutoGameSystem +{ +public: + void LevelShutdownPostEntity(); + void Shutdown(); + +public: + HSCRIPT CreatePanel( const char* panelClass, HSCRIPT parent, const char* panelName, int root ); + //void LoadSchemeFromFile( const char *filename, const char *tag ); + +} script_vgui; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptVGUI, "IVGui", SCRIPT_SINGLETON ) + DEFINE_SCRIPTFUNC( CreatePanel, SCRIPT_HIDE ) +END_SCRIPTDESC() + + +HSCRIPT CScriptVGUI::CreatePanel( const char* panelClass, HSCRIPT parent, const char* panelName, int root ) +{ + if ( (unsigned)g_ScriptPanels.Count() >= (unsigned)g_ScriptPanels.InvalidIndex()-1 ) + { + Warning( "CScriptVGUI::CreatePanel() exhausted vgui panel storage!\n" ); + return NULL; + } + +#define Check( _name )\ + if ( FStrEq( panelClass, #_name ) )\ + {\ + CScript_##_name##_Helper *helper = AllocScriptPanel< CScript_##_name##_Helper >();\ + helper->CreateFromScript< CScript_##_name##_Helper >( (HSCRIPT)parent, panelName, root );\ + DebugDevMsg( "%3d | Create vgui %s '%s' %s\n", g_ScriptPanels.Count(), panelClass, panelName, helper->GetDebugName() );\ + return helper->GetScriptInstance();\ + } + + Check( Panel ); + Check( Label ); + Check( Button ); + Check( ImagePanel ); + Check( Frame ); + Check( RichText ); + Check( TextEntry ); +#if !defined(NO_STEAM) + Check( AvatarImage ); +#endif +#if VGUI_TGA_IMAGE_PANEL + Check( TGAImagePanel ); +#endif + + g_pScriptVM->RaiseException("invalid vgui class"); + return NULL; + +#undef Check +} + +void CScriptVGUI::LevelShutdownPostEntity() +{ + DebugMsg( "LevelShutdownPostEntity()\n" ); + + while ( g_ScriptPanels.Count() ) + { + Assert( g_ScriptPanels.Head() != g_ScriptPanels.InvalidIndex() ); + + int head = g_ScriptPanels.Head(); + g_ScriptPanels[ head ]->Destroy( head ); + } + g_ScriptPanels.Purge(); + + FOR_EACH_VEC( g_ScriptTextureIDs, i ) + { +#ifdef _DEBUG + char tex[MAX_PATH]; + surface()->DrawGetTextureFile( g_ScriptTextureIDs[i], tex, sizeof(tex)-1 ); + DebugMsg( "Destroy texture [%i]%s\n", g_ScriptTextureIDs[i], tex ); +#endif + surface()->DestroyTextureID( g_ScriptTextureIDs[i] ); + } + g_ScriptTextureIDs.Purge(); + + // + // Reset hud element visibility + // + if ( m_bHudVisiblityChangedThisLevel ) + { + m_bHudVisiblityChangedThisLevel = false; + + FOR_EACH_VEC( m_HudElementCache, i ) + { + const hudelementcache_t &cache = m_HudElementCache[i]; + Assert( !cache.name.IsEmpty() ); + CHudElement *elem = gHUD.FindElement( cache.name ); + Assert( elem ); + if ( elem ) + { + elem->SetHiddenBits( cache.bits ); + } + } + } +} + +void CScriptVGUI::Shutdown() +{ + VGUI_DestroyScriptRootPanels(); + + FOR_EACH_DICT_FAST( g_ScriptFonts, i ) + { + fontalias_t &alias = g_ScriptFonts[i]; + for ( int j = 0; j < alias.Count(); ++j ) + { + char *pName = alias.Element(j).name; + if ( pName ) + { + free( pName ); + alias.Element(j).name = NULL; + } + } + + alias.Purge(); + } + + g_ScriptFonts.Purge(); + + m_HudElementCache.Purge(); +} + + +void SetHudElementVisible( const char *name, bool state ) +{ + CHudElement *elem = gHUD.FindElement( name ); + if ( !elem ) + return; + + int iOldBits = -2; + + FOR_EACH_VEC( m_HudElementCache, i ) + { + const hudelementcache_t &cache = m_HudElementCache[i]; + if ( !V_stricmp( cache.name, name ) ) + { + iOldBits = cache.bits; + break; + } + } + + if ( iOldBits == -2 ) + { + if ( state ) // no change + return; + + // First time setting the visibility of this element, save the original bits + hudelementcache_t &cache = m_HudElementCache.Element( m_HudElementCache.AddToTail() ); + cache.name.Set( name ); + cache.bits = elem->GetHiddenBits(); + } + + elem->SetHiddenBits( state ? iOldBits : -1 ); + + m_bHudVisiblityChangedThisLevel = true; +} + +#ifdef _DEBUG +CON_COMMAND( dump_hud_elements, "" ) +{ + int size = gHUD.m_HudList.Size(); + + CUtlVector< const char* > list( 0, size ); + + for ( int i = 0; i < size; i++ ) + { + list.AddToTail( gHUD.m_HudList[i]->GetName() ); + } + + struct _cmp + { + static int __cdecl fn( const char * const *a, const char * const *b ) { return strcmp( *a, *b ); } + }; + + list.Sort( _cmp::fn ); + + for ( int i = 0; i < size; i++ ) + { + Msg( "%s\n", list[i] ); + } +} +#endif + + +class CScriptIInput +{ +public: + void MakeWeaponSelection( HSCRIPT weapon ) + { + ::input->MakeWeaponSelection( HScriptToClass< C_BaseCombatWeapon >( weapon ) ); + } +#if 0 + int GetButtonBits() + { + return ::input->GetButtonBits(0); + } + + void ClearInputButton( int i ) + { + return ::input->ClearInputButton(i); + } +#endif + void SetCursorPos( int x, int y ) + { + vgui::input()->SetCursorPos( x, y ); + } + + int GetAnalogValue( int code ) + { + Assert( code >= 0 && code < ANALOG_CODE_LAST ); + + if ( code < 0 || code >= ANALOG_CODE_LAST ) + return 0; + + return inputsystem->GetAnalogValue( (AnalogCode_t)code ); + } + + int GetAnalogDelta( int code ) + { + Assert( code >= 0 && code < ANALOG_CODE_LAST ); + + if ( code < 0 || code >= ANALOG_CODE_LAST ) + return 0; + + return inputsystem->GetAnalogDelta( (AnalogCode_t)code ); + } + + bool IsButtonDown( int code ) + { + Assert( code >= BUTTON_CODE_NONE && code < BUTTON_CODE_LAST ); + + if ( code < BUTTON_CODE_NONE || code >= BUTTON_CODE_LAST ) + return 0; + + return inputsystem->IsButtonDown( (ButtonCode_t)code ); + } + + // key -> button + int StringToButtonCode( const char *key ) + { + return inputsystem->StringToButtonCode( key ); + } + + // button -> key + const char *ButtonCodeToString( int code ) + { + Assert( code >= BUTTON_CODE_NONE && code < BUTTON_CODE_LAST ); + + if ( code < BUTTON_CODE_NONE || code >= BUTTON_CODE_LAST ) + return 0; + + return inputsystem->ButtonCodeToString( (ButtonCode_t)code ); + } + + // bind -> key + const char *LookupBinding( const char *bind ) + { + return engine->Key_LookupBinding( bind ); + } + + // button -> bind + const char *BindingForKey( int code ) + { + return engine->Key_BindingForKey( (ButtonCode_t)code ); + } +#if 0 + const char *GetIMELanguageShortCode() + { + static char ret[5]; + wchar_t get[5]; + get[0] = L'\0'; + vgui::input()->GetIMELanguageShortCode( get, wcslen(get) ); + g_pVGuiLocalize->ConvertUnicodeToANSI( get, ret, sizeof(ret) ); + return ret; + } +#endif +} script_input; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptIInput, "IInput", SCRIPT_SINGLETON ) + DEFINE_SCRIPTFUNC( MakeWeaponSelection, "" ) + + DEFINE_SCRIPTFUNC( SetCursorPos, "" ) + + DEFINE_SCRIPTFUNC( GetAnalogValue, "" ) + DEFINE_SCRIPTFUNC( GetAnalogDelta, "" ) + DEFINE_SCRIPTFUNC( IsButtonDown, "" ) + + DEFINE_SCRIPTFUNC( StringToButtonCode, "" ) + DEFINE_SCRIPTFUNC( ButtonCodeToString, "" ) + DEFINE_SCRIPTFUNC( LookupBinding, "" ) + DEFINE_SCRIPTFUNC( BindingForKey, "" ) +END_SCRIPTDESC() + + +void SetClipboardText( const char *text ) +{ + system()->SetClipboardText( text, V_strlen(text) ); +} + +//============================================================== +//============================================================== + +#if 0 +//----------------------------------------------------------------------------- +// Get world position in screen space [0,1]. Return true if on screen. +//----------------------------------------------------------------------------- +inline bool WorldToScreen( const Vector &pos, int &ix, int &iy ) +{ + int scrw, scrh; + surface()->GetScreenSize( scrw, scrh ); + + const VMatrix &worldToScreen = engine->WorldToScreenMatrix(); + bool bOnScreen; + + // VMatrix * Vector (position projective) + vec_t w = worldToScreen[3][0] * pos[0] + worldToScreen[3][1] * pos[1] + worldToScreen[3][2] * pos[2] + worldToScreen[3][3]; + vec_t fx = worldToScreen[0][0] * pos[0] + worldToScreen[0][1] * pos[1] + worldToScreen[0][2] * pos[2] + worldToScreen[0][3]; + vec_t fy = worldToScreen[1][0] * pos[0] + worldToScreen[1][1] * pos[1] + worldToScreen[1][2] * pos[2] + worldToScreen[1][3]; + + if ( w < 0.001f ) + { + fx *= 1e5f; + fy *= 1e5f; + bOnScreen = false; + } + else + { + w = 1.0f / w; + fx *= w; + fy *= w; + bOnScreen = true; + } + + ix = (int)( scrw * 0.5f * ( 1.0f + fx ) + 0.5f ); + iy = (int)( scrh * 0.5f * ( 1.0f - fy ) + 0.5f ); + + return bOnScreen; +} +#endif +//----------------------------------------------------------------------------- +// Get screen pixel position [0,1] in world space. +//----------------------------------------------------------------------------- +inline void ScreenToWorld( int x, int y, Vector &out ) +{ + int scrw, scrh; + surface()->GetScreenSize( scrw, scrh ); + float scrx = (float)x / (float)scrw; + float scry = (float)y / (float)scrh; + + vec_t tmp[2]; + tmp[0] = 2.0f * scrx - 1.0f; + tmp[1] = 1.0f - 2.0f * scry; + //tmp[2] = 1.0f; + //tmp[3] = 1.0f; + + VMatrix screenToWorld; + MatrixInverseGeneral( engine->WorldToScreenMatrix(), screenToWorld ); + + // VMatrix * Vector (position projective) + vec_t iw = 1.0f / ( screenToWorld[3][0] * tmp[0] + screenToWorld[3][1] * tmp[1] + screenToWorld[3][2] + screenToWorld[3][3] ); + out[0] = iw * ( screenToWorld[0][0] * tmp[0] + screenToWorld[0][1] * tmp[1] + screenToWorld[0][2] + screenToWorld[0][3] ); + out[1] = iw * ( screenToWorld[1][0] * tmp[0] + screenToWorld[1][1] * tmp[1] + screenToWorld[1][2] + screenToWorld[1][3] ); + out[2] = iw * ( screenToWorld[2][0] * tmp[0] + screenToWorld[2][1] * tmp[1] + screenToWorld[2][2] + screenToWorld[2][3] ); +} + +#if 0 +static bool ScriptWorldToScreen( const Vector &pos, HSCRIPT out ) +{ + int ix, iy; + bool r = WorldToScreen( pos, ix, iy ); + + g_pScriptVM->SetValue( out, (ScriptVariant_t)0, ix ); + g_pScriptVM->SetValue( out, 1, iy ); + return r; +} +#endif +static const Vector& ScriptScreenToWorld( int x, int y ) +{ + static Vector out; + ScreenToWorld( x, y, out ); + return out; +} + +static const Vector& ScreenToRay( int x, int y ) +{ + static Vector out; + ScreenToWorld( x, y, out ); + VectorSubtract( out, CurrentViewOrigin(), out ); + VectorNormalize( out ); + return out; +} + +//----------------------------------------------------------------------------- +// Get world position normalised in screen space. Return true if on screen. +//----------------------------------------------------------------------------- +int ScreenTransform( const Vector& point, Vector& screen ); +static bool ScriptScreenTransform( const Vector &pos, HSCRIPT out ) +{ + Vector v; + bool r = ScreenTransform( pos, v ); + float x = 0.5f * ( 1.0f + v[0] ); + float y = 0.5f * ( 1.0f - v[1] ); + + g_pScriptVM->SetValue( out, (ScriptVariant_t)0, x ); + g_pScriptVM->SetValue( out, 1, y ); + return !r; +} + +int ScriptScreenWidth() +{ + int w, h; + surface()->GetScreenSize( w, h ); + return w; +} + +int ScriptScreenHeight() +{ + int w, h; + surface()->GetScreenSize( w, h ); + return h; +} + +// +// Saving the static (ScreenWidth/640) ratio in a script closure +// messes up on save/restore at differing resolutions - +// the closure and the user script funcs retain the ratio at the time of the save. +// It is not possible to update restored script closure outer variables without writing language specific functions. +// +// NOTE: Returns int! int usage is more common than float operations. +// +static int ScriptXRES( float x ) +{ + return x * ( (float)ScriptScreenWidth() / 640.0f ); +} + +static int ScriptYRES( float y ) +{ + return y * ( (float)ScriptScreenHeight() / 480.0f ); +} + +vgui::HFont GetScriptFont( const char *name, bool proportional ) +{ + return script_surface.GetFont( name, proportional, NULL ); +} + + +void RegisterScriptVGUI() +{ + ScriptRegisterFunction( g_pScriptVM, SetHudElementVisible, "" ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptXRES, "XRES", "" ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptYRES, "YRES", "" ); + + ScriptRegisterFunction( g_pScriptVM, SetClipboardText, "" ); + //ScriptRegisterFunctionNamed( g_pScriptVM, ScriptWorldToScreen, "WorldToScreen", "Get world position in screen space [0,1]. Return true if on screen." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptScreenToWorld, "ScreenToWorld", "Get screen pixel position [0,1] in world space." ); + ScriptRegisterFunction( g_pScriptVM, ScreenToRay, "Get a ray from screen pixel position to world space." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptScreenTransform, "ScreenTransform", "Get world position normalised in screen space. Return true if on screen." ); + + g_pScriptVM->Run( g_Script_vgui_init ); + + g_pScriptVM->RegisterInstance( &script_surface, "surface" ); + g_pScriptVM->RegisterInstance( &script_input, "input" ); + g_pScriptVM->RegisterInstance( &script_vgui, "vgui" ); +} diff --git a/mp/src/game/client/mapbase/vscript_vgui.h b/mp/src/game/client/mapbase/vscript_vgui.h new file mode 100644 index 00000000..d5214073 --- /dev/null +++ b/mp/src/game/client/mapbase/vscript_vgui.h @@ -0,0 +1,16 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VSCRIPT_VGUI_H +#define VSCRIPT_VGUI_H +#ifdef _WIN32 +#pragma once +#endif + +void RegisterScriptVGUI(); + +#endif diff --git a/mp/src/game/client/mapbase/vscript_vgui.nut b/mp/src/game/client/mapbase/vscript_vgui.nut new file mode 100644 index 00000000..a4452705 --- /dev/null +++ b/mp/src/game/client/mapbase/vscript_vgui.nut @@ -0,0 +1,394 @@ +static const char* g_Script_vgui_init = R"script( +local DoCreateFont = ISurface.CreateFont; +ISurface.CreateFont <- function( name, props ) +{ + if ( !("name" in props) || typeof props.name != "string" ) + throw "invalid parameter 'name'"; + + if ( !("tall" in props) || typeof props.tall != "integer" || !props.tall ) + throw "invalid parameter 'tall'"; + + if ( !("weight" in props) || typeof props.weight != "integer" ) + throw "invalid parameter 'weight'"; + + local yres_min = 0, yres_max = 0; + + if ( "yres" in props && typeof props.yres == "string" ) + { + local ss = ::split( props.yres, " " ); + try + { + yres_min = ss[0].tointeger(); + yres_max = ss[1].tointeger(); + } + catch(x) + { + throw "invalid parameter 'yres'"; + } + } + + if ( ( (!("proportional" in props) || typeof props.proportional != "bool") ) && !yres_min ) + { + throw "parameter 'proportional' or 'yres' not found"; + } + else if ( "proportional" in props && props.proportional && yres_min ) + { + throw "resolution definition on a proportional font" + } + + local blur = 0, scanlines = 0, proportional = false, flags = 0; + + if ( "blur" in props && typeof props.blur == "integer" ) + blur = props.blur; + + if ( "scanlines" in props && typeof props.scanlines == "integer" ) + scanlines = props.scanlines; + + if ( "proportional" in props && typeof props.proportional == "bool" ) + proportional = props.proportional; + + if ( "italic" in props && props.italic == true ) + flags = flags | 0x001; + + if ( "underline" in props && props.underline == true ) + flags = flags | 0x002; + + if ( "strikeout" in props && props.strikeout == true ) + flags = flags | 0x004; + + if ( "symbol" in props && props.symbol == true ) + flags = flags | 0x008; + + if ( "antialias" in props && props.antialias == true ) + flags = flags | 0x010; + + if ( "gaussianblur" in props && props.gaussianblur == true ) + flags = flags | 0x020; + + if ( "rotary" in props && props.rotary == true ) + flags = flags | 0x040; + + if ( "dropshadow" in props && props.dropshadow == true ) + flags = flags | 0x080; + + if ( "additive" in props && props.additive == true ) + flags = flags | 0x100; + + if ( "outline" in props && props.outline == true ) + flags = flags | 0x200; + + if ( "custom" in props && props.custom == true ) + flags = flags | 0x400; + + if ( "bitmap" in props && props.bitmap == true ) + flags = flags | 0x800; + + return DoCreateFont( name, props.name, props.tall, props.weight, blur, scanlines, flags, yres_min, yres_max, proportional ); +} + +local _Schemes = {} +local _FontTall = {} +local DoGetFont = ISurface.DoGetFont <- ISurface.GetFont; +local DoGetFontTall = ISurface.GetFontTall; + +ISurface.GetFont <- function( name, proportional, sch = "" ) +{ + if ( sch in _Schemes ) + { + local fonts = _Schemes[sch][proportional.tointeger()]; + if ( name in fonts ) + return fonts[name]; + } + else + { + if ( typeof sch != "string" ) + throw "invalid parameter 'scheme'"; + _Schemes[sch] <- [{}, {}]; + } + + local id = DoGetFont( name, proportional, sch ); + if ( id > 0 ) + _Schemes[sch][proportional.tointeger()][name] <- id; + + return id; +} + +ISurface.GetFontTall <- function( id ) +{ + if ( id in _FontTall ) + return _FontTall[id]; + return _FontTall[id] <- DoGetFontTall( id ); +} + +local _Textures = {} +local DoGetTextureID = ISurface.GetTextureID; +local DoValidateTexture = ISurface.ValidateTexture; +local DoSetTextureFile = ISurface.SetTextureFile; + +ISurface.ValidateTexture <- function( filename, hardwareFilter, forceReload = false, procedural = false ) +{ + return DoValidateTexture( filename, hardwareFilter, forceReload, procedural ); +} + +ISurface.SetTextureFile <- function( id, filename, hardwareFilter ) +{ + if ( filename in _Textures ) + delete _Textures[filename]; + + return DoSetTextureFile( id, filename, hardwareFilter ); +} + +ISurface.GetTextureID <- function( name ) +{ + if ( name in _Textures ) + return _Textures[name]; + + local id = DoGetTextureID( name ); + if ( id > 0 ) + _Textures[name] <- id; + + return id; +} + +// Forward compatibility +IVGui.GetRootPanel <- function() { return 1000 } +//IVGui.GetGameUIRootPanel <- function() { return 1001 } +IVGui.GetClientDLLRootPanel <- function() { return 1002 } +//IVGui.GetHudViewportPanel <- function() { return 1010 } + +local CreatePanel = IVGui.CreatePanel; +IVGui.CreatePanel <- function( type, parent, name ) +{ + if ( !parent ) + throw "invalid parent"; + + local root = 0; + + if ( typeof parent == "integer" ) + { + switch ( parent ) + { + case 1000: + root = 0; + break; + + case 1002: + root = 2; + break; + + default: + throw "invalid parent"; + } + parent = null; + } + return CreatePanel( type, parent, name, root ); +} + +ISurface.__OnScreenSizeChanged <- function() +{ + _FontTall.clear(); +} + +// MAX_JOYSTICKS = 1 // ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) +// MAX_JOYSTICK_AXES = 6 // X,Y,Z,R,U,V +// JOYSTICK_MAX_BUTTON_COUNT = 32 +// JOYSTICK_POV_BUTTON_COUNT = 4 +// JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2 + +enum ButtonCode +{ + KEY_FIRST = 0 + KEY_0 = 1 + KEY_1 = 2 + KEY_2 = 3 + KEY_3 = 4 + KEY_4 = 5 + KEY_5 = 6 + KEY_6 = 7 + KEY_7 = 8 + KEY_8 = 9 + KEY_9 = 10 + KEY_A = 11 + KEY_B = 12 + KEY_C = 13 + KEY_D = 14 + KEY_E = 15 + KEY_F = 16 + KEY_G = 17 + KEY_H = 18 + KEY_I = 19 + KEY_J = 20 + KEY_K = 21 + KEY_L = 22 + KEY_M = 23 + KEY_N = 24 + KEY_O = 25 + KEY_P = 26 + KEY_Q = 27 + KEY_R = 28 + KEY_S = 29 + KEY_T = 30 + KEY_U = 31 + KEY_V = 32 + KEY_W = 33 + KEY_X = 34 + KEY_Y = 35 + KEY_Z = 36 + KEY_PAD_0 = 37 + KEY_PAD_1 = 38 + KEY_PAD_2 = 39 + KEY_PAD_3 = 40 + KEY_PAD_4 = 41 + KEY_PAD_5 = 42 + KEY_PAD_6 = 43 + KEY_PAD_7 = 44 + KEY_PAD_8 = 45 + KEY_PAD_9 = 46 + KEY_PAD_DIVIDE = 47 + KEY_PAD_MULTIPLY = 48 + KEY_PAD_MINUS = 49 + KEY_PAD_PLUS = 50 + KEY_PAD_ENTER = 51 + KEY_PAD_DECIMAL = 52 + KEY_LBRACKET = 53 + KEY_RBRACKET = 54 + KEY_SEMICOLON = 55 + KEY_APOSTROPHE = 56 + KEY_BACKQUOTE = 57 + KEY_COMMA = 58 + KEY_PERIOD = 59 + KEY_SLASH = 60 + KEY_BACKSLASH = 61 + KEY_MINUS = 62 + KEY_EQUAL = 63 + KEY_ENTER = 64 + KEY_SPACE = 65 + KEY_BACKSPACE = 66 + KEY_TAB = 67 + KEY_CAPSLOCK = 68 + KEY_NUMLOCK = 69 + KEY_ESCAPE = 70 + KEY_SCROLLLOCK = 71 + KEY_INSERT = 72 + KEY_DELETE = 73 + KEY_HOME = 74 + KEY_END = 75 + KEY_PAGEUP = 76 + KEY_PAGEDOWN = 77 + KEY_BREAK = 78 + KEY_LSHIFT = 79 + KEY_RSHIFT = 80 + KEY_LALT = 81 + KEY_RALT = 82 + KEY_LCONTROL = 83 + KEY_RCONTROL = 84 + KEY_LWIN = 85 + KEY_RWIN = 86 + KEY_APP = 87 + KEY_UP = 88 + KEY_LEFT = 89 + KEY_DOWN = 90 + KEY_RIGHT = 91 + KEY_F1 = 92 + KEY_F2 = 93 + KEY_F3 = 94 + KEY_F4 = 95 + KEY_F5 = 96 + KEY_F6 = 97 + KEY_F7 = 98 + KEY_F8 = 99 + KEY_F9 = 100 + KEY_F10 = 101 + KEY_F11 = 102 + KEY_F12 = 103 + KEY_CAPSLOCKTOGGLE = 104 + KEY_NUMLOCKTOGGLE = 105 + KEY_SCROLLLOCKTOGGLE = 106 + KEY_LAST = 106 + + MOUSE_FIRST = 107 + MOUSE_LEFT = 107 + MOUSE_RIGHT = 108 + MOUSE_MIDDLE = 109 + MOUSE_4 = 110 + MOUSE_5 = 111 + MOUSE_WHEEL_UP = 112 + MOUSE_WHEEL_DOWN = 113 + MOUSE_LAST = 113 + + JOYSTICK_FIRST = 114 + JOYSTICK_FIRST_BUTTON = 114 + JOYSTICK_LAST_BUTTON = 145 + JOYSTICK_FIRST_POV_BUTTON = 146 + JOYSTICK_LAST_POV_BUTTON = 149 + JOYSTICK_FIRST_AXIS_BUTTON = 150 + JOYSTICK_LAST_AXIS_BUTTON = 161 + JOYSTICK_LAST = 161 +} + +enum AnalogCode +{ + MOUSE_X = 0 + MOUSE_Y = 1 + MOUSE_XY = 2 + MOUSE_WHEEL = 3 + + JOYSTICK_FIRST_AXIS = 4 + JOYSTICK_LAST_AXIS = 9 +} + +enum CursorCode +{ + dc_none = 1 + dc_arrow = 2 + dc_ibeam = 3 + dc_hourglass = 4 + dc_waitarrow = 5 + dc_crosshair = 6 + dc_up = 7 + dc_sizenwse = 8 + dc_sizenesw = 9 + dc_sizewe = 10 + dc_sizens = 11 + dc_sizeall = 12 + dc_no = 13 + dc_hand = 14 + dc_blank = 15 +} + +enum Alignment +{ + northwest = 0 + north = 1 + northeast = 2 + west = 3 + center = 4 + east = 5 + southwest = 6 + south = 7 + southeast = 8 +} + +if ( __Documentation.RegisterHelp != dummy ) +{ + local RegEnum = function( e ) + { + local K = getconsttable()[e]; + __Documentation.RegisterEnumHelp( e, K.len(), "" ); + e += "."; + foreach ( s, v in K ) + { + __Documentation.RegisterConstHelp( e+s, v, "" ); + } + } + RegEnum( "ButtonCode" ); + RegEnum( "AnalogCode" ); + RegEnum( "CursorCode" ); + RegEnum( "Alignment" ); + + __Documentation.RegisterHelp( "ISurface::CreateFont", "void ISurface::CreateFont(string, handle)", "" ); + __Documentation.RegisterHelp( "IVGui::CreatePanel", "handle IVGui::CreatePanel(string, handle, string)", "" ); + __Documentation.RegisterHelp( "IVGui::GetRootPanel", "handle IVGui::GetRootPanel()", "" ); + __Documentation.RegisterHelp( "IVGui::GetClientDLLRootPanel", "handle IVGui::GetClientDLLRootPanel()", "" ); +} +)script"; diff --git a/mp/src/game/client/panelmetaclassmgr.cpp b/mp/src/game/client/panelmetaclassmgr.cpp index 2b36cc44..6f0b50d0 100644 --- a/mp/src/game/client/panelmetaclassmgr.cpp +++ b/mp/src/game/client/panelmetaclassmgr.cpp @@ -234,14 +234,6 @@ 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/particlemgr.h b/mp/src/game/client/particlemgr.h index d4537f35..91df0286 100644 --- a/mp/src/game/client/particlemgr.h +++ b/mp/src/game/client/particlemgr.h @@ -119,7 +119,7 @@ entities. Each one is useful under different conditions. #include "tier0/fasttimer.h" #include "utllinkedlist.h" #include "utldict.h" -#ifdef WIN32 +#if defined(WIN32) && _MSC_VER < 1900 #include #else #include diff --git a/mp/src/game/client/physics_main_client.cpp b/mp/src/game/client/physics_main_client.cpp index b1af5b23..ebc392e2 100644 --- a/mp/src/game/client/physics_main_client.cpp +++ b/mp/src/game/client/physics_main_client.cpp @@ -6,7 +6,7 @@ //=============================================================================// #include "cbase.h" #include "c_baseentity.h" -#ifdef WIN32 +#if defined(WIN32) && _MSC_VER < 1900 #include #endif #include "tier0/vprof.h" diff --git a/mp/src/game/client/physpropclientside.cpp b/mp/src/game/client/physpropclientside.cpp index 3dd2d7b5..3db90618 100644 --- a/mp/src/game/client/physpropclientside.cpp +++ b/mp/src/game/client/physpropclientside.cpp @@ -704,9 +704,157 @@ void C_PhysPropClientside::ParseAllEntities(const char *pMapData) } } +#ifdef MAPBASE +CBaseAnimating *BreakModelCreate_Ragdoll( CBaseEntity *pOwnerEnt, breakmodel_t *pModel, const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity ) +{ + C_BaseAnimating *pOwner = dynamic_cast( pOwnerEnt ); + if ( !pOwner ) + return NULL; + + C_ClientRagdoll *pRagdoll = new C_ClientRagdoll( false ); + if ( pRagdoll == NULL ) + return NULL; + + const char *pModelName = pModel->modelName; + if ( pRagdoll->InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false ) + { + pRagdoll->Release(); + return NULL; + } + + pRagdoll->SetAbsOrigin( position ); + pRagdoll->SetAbsAngles( angles ); + + matrix3x4_t boneDelta0[MAXSTUDIOBONES]; + matrix3x4_t boneDelta1[MAXSTUDIOBONES]; + matrix3x4_t currentBones[MAXSTUDIOBONES]; + const float boneDt = 0.1f; + + pRagdoll->SetParent( pOwner ); + pRagdoll->ForceSetupBonesAtTime( boneDelta0, gpGlobals->curtime - boneDt ); + pRagdoll->ForceSetupBonesAtTime( boneDelta1, gpGlobals->curtime ); + pRagdoll->ForceSetupBonesAtTime( currentBones, gpGlobals->curtime ); + pRagdoll->SetParent( NULL ); + + // We need to take these from the entity + //pRagdoll->SetAbsOrigin( position ); + //pRagdoll->SetAbsAngles( angles ); + + pRagdoll->IgniteRagdoll( pOwner ); + pRagdoll->TransferDissolveFrom( pOwner ); + pRagdoll->InitModelEffects(); + + if ( pOwner->IsEffectActive( EF_NOSHADOW ) ) + { + pRagdoll->AddEffects( EF_NOSHADOW ); + } + + pRagdoll->m_nRenderFX = kRenderFxRagdoll; + pRagdoll->SetRenderMode( pOwner->GetRenderMode() ); + pRagdoll->SetRenderColor( pOwner->GetRenderColor().r, pOwner->GetRenderColor().g, pOwner->GetRenderColor().b, pOwner->GetRenderColor().a ); + //pRagdoll->SetGlobalFadeScale( pOwner->GetGlobalFadeScale() ); + + pRagdoll->SetSkin( pOwner->GetSkin() ); + //pRagdoll->m_vecForce = pOwner->m_vecForce; + //pRagdoll->m_nForceBone = 0; //pOwner->m_nForceBone; + pRagdoll->SetNextClientThink( CLIENT_THINK_ALWAYS ); + + pRagdoll->SetModelName( AllocPooledString( pModelName ) ); + pRagdoll->ResetSequence( 0 ); + pRagdoll->SetModelScale( pOwner->GetModelScale() ); + pRagdoll->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + //pRagdoll->m_builtRagdoll = true; + + CStudioHdr *hdr = pRagdoll->GetModelPtr(); + if ( !hdr ) + { + pRagdoll->Release(); + Warning( "Couldn't create ragdoll gib for %s (no model pointer)\n", pModel->modelName ); + return NULL; + } + + pRagdoll->m_pRagdoll = CreateRagdoll( + pRagdoll, + hdr, + vec3_origin, + 0, + boneDelta0, + boneDelta1, + currentBones, + boneDt ); + + if ( !pRagdoll->m_pRagdoll ) + { + pRagdoll->Release(); + Warning( "Couldn't create ragdoll gib for %s\n", pModel->modelName ); + return NULL; + } + + IPhysicsObject *pPhysicsObject = pRagdoll->VPhysicsGetObject(); + if ( pPhysicsObject ) + { + // randomize velocity by 5% + float rndf = RandomFloat( -0.025, 0.025 ); + Vector rndVel = velocity + rndf*velocity; + + pPhysicsObject->AddVelocity( &rndVel, &angVelocity ); + } + pRagdoll->ApplyLocalAngularVelocityImpulse( angVelocity ); + + if ( pRagdoll->m_pRagdoll ) + { + pRagdoll->m_bImportant = false; + pRagdoll->m_flForcedRetireTime = pModel->fadeTime > 0.0f ? gpGlobals->curtime + pModel->fadeTime : 0.0f; + s_RagdollLRU.MoveToTopOfLRU( pRagdoll, pRagdoll->m_bImportant, pRagdoll->m_flForcedRetireTime ); + pRagdoll->m_bFadeOut = true; + } + + // Cause the entity to recompute its shadow type and make a + // version which only updates when physics state changes + // NOTE: We have to do this after m_pRagdoll is assigned above + // because that's what ShadowCastType uses to figure out which type of shadow to use. + pRagdoll->DestroyShadow(); + pRagdoll->CreateShadow(); + + pRagdoll->SetAbsOrigin( position ); + pRagdoll->SetAbsAngles( angles ); + + pRagdoll->SetPlaybackRate( 0 ); + pRagdoll->SetCycle( 0 ); + + // put into ACT_DIERAGDOLL if it exists, otherwise use sequence 0 + int nSequence = pRagdoll->SelectWeightedSequence( ACT_DIERAGDOLL ); + if ( nSequence < 0 ) + { + pRagdoll->ResetSequence( 0 ); + } + else + { + pRagdoll->ResetSequence( nSequence ); + } + + pRagdoll->UpdatePartitionListEntry(); + pRagdoll->MarkRenderHandleDirty(); + + NoteRagdollCreationTick( pRagdoll ); + + //pRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); + + return pRagdoll; +} +#endif + CBaseEntity *BreakModelCreateSingle( CBaseEntity *pOwner, breakmodel_t *pModel, const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, int nSkin, const breakablepropparams_t ¶ms ) { +#ifdef MAPBASE + if ( pModel->isRagdoll ) + { + CBaseEntity *pEntity = BreakModelCreate_Ragdoll( pOwner, pModel, position, angles, velocity, angVelocity ); + return pEntity; + } +#endif + C_PhysPropClientside *pEntity = C_PhysPropClientside::CreateNew(); if ( !pEntity ) @@ -778,10 +926,12 @@ CBaseEntity *BreakModelCreateSingle( CBaseEntity *pOwner, breakmodel_t *pModel, pEntity->SetFadeMinMax( pModel->fadeMinDist, pModel->fadeMaxDist ); } +#ifndef MAPBASE if ( pModel->isRagdoll ) { DevMsg( "BreakModelCreateSingle: clientside doesn't support ragdoll breakmodels.\n" ); } +#endif IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject(); diff --git a/mp/src/game/client/proxyplayer.cpp b/mp/src/game/client/proxyplayer.cpp index d6521cc9..5fa7f1bc 100644 --- a/mp/src/game/client/proxyplayer.cpp +++ b/mp/src/game/client/proxyplayer.cpp @@ -12,6 +12,9 @@ #include "materialsystem/imaterialsystem.h" #include "functionproxy.h" #include "toolframework_client.h" +#ifdef MAPBASE +#include "view.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -521,4 +524,100 @@ void CPlayerLogoOnModelProxy::OnBind( void *pC_BaseEntity ) } EXPOSE_INTERFACE( CPlayerLogoOnModelProxy, IMaterialProxy, "PlayerLogoOnModel" IMATERIAL_PROXY_INTERFACE_VERSION ); -*/ \ No newline at end of file +*/ + + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Returns the proximity of the current view to the entity +//----------------------------------------------------------------------------- +class CViewProximityProxy : public CResultProxy +{ +public: + bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + void OnBind( void *pC_BaseEntity ); + +private: + float m_Factor; +}; + +bool CViewProximityProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + if (!CResultProxy::Init( pMaterial, pKeyValues )) + return false; + + m_Factor = pKeyValues->GetFloat( "scale", 0.002 ); + return true; +} + +void CViewProximityProxy::OnBind( void *pC_BaseEntity ) +{ + if (!pC_BaseEntity) + return; + + // Find the distance between the player and this entity.... + C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity ); + + Vector delta; + VectorSubtract( pEntity->WorldSpaceCenter(), CurrentViewOrigin(), delta ); + + Assert( m_pResult ); + SetFloatResult( delta.Length() * m_Factor ); + + if ( ToolsEnabled() ) + { + ToolFramework_RecordMaterialParams( GetMaterial() ); + } +} + +EXPOSE_INTERFACE( CViewProximityProxy, IMaterialProxy, "ViewProximity" IMATERIAL_PROXY_INTERFACE_VERSION ); + +//----------------------------------------------------------------------------- +// Returns the current view direction +//----------------------------------------------------------------------------- +class CViewDirectionProxy : public CResultProxy +{ +public: + bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + void OnBind( void *pC_BaseEntity ); + +private: + float m_Factor; +}; + +bool CViewDirectionProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + if (!CResultProxy::Init( pMaterial, pKeyValues )) + return false; + + m_Factor = pKeyValues->GetFloat( "scale", 2 ); + return true; +} + +void CViewDirectionProxy::OnBind( void *pC_BaseEntity ) +{ + if (!pC_BaseEntity) + return; + + // Find the view angle between the player and this entity.... + C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity ); + + Vector delta; + Vector forward; + + VectorSubtract( pEntity->WorldSpaceCenter(), CurrentViewOrigin(), delta ); + VectorNormalize( delta ); + + forward = CurrentViewForward(); + + Assert( m_pResult ); + SetFloatResult( DotProduct( forward, delta ) * m_Factor ); + + if ( ToolsEnabled() ) + { + ToolFramework_RecordMaterialParams( GetMaterial() ); + } +} + +EXPOSE_INTERFACE( CViewDirectionProxy, IMaterialProxy, "ViewDirection" IMATERIAL_PROXY_INTERFACE_VERSION ); +#endif diff --git a/mp/src/game/client/ragdoll.cpp b/mp/src/game/client/ragdoll.cpp index 3457b054..1e044a1d 100644 --- a/mp/src/game/client/ragdoll.cpp +++ b/mp/src/game/client/ragdoll.cpp @@ -69,6 +69,16 @@ BEGIN_SIMPLE_DATADESC( CRagdoll ) DEFINE_RAGDOLL_ELEMENT( 21 ), DEFINE_RAGDOLL_ELEMENT( 22 ), DEFINE_RAGDOLL_ELEMENT( 23 ), +#ifdef MAPBASE + DEFINE_RAGDOLL_ELEMENT( 24 ), + DEFINE_RAGDOLL_ELEMENT( 25 ), + DEFINE_RAGDOLL_ELEMENT( 26 ), + DEFINE_RAGDOLL_ELEMENT( 27 ), + DEFINE_RAGDOLL_ELEMENT( 28 ), + DEFINE_RAGDOLL_ELEMENT( 29 ), + DEFINE_RAGDOLL_ELEMENT( 30 ), + DEFINE_RAGDOLL_ELEMENT( 31 ), +#endif END_DATADESC() @@ -477,6 +487,9 @@ int C_ServerRagdoll::InternalDrawModel( int flags ) return ret; } +#ifdef MAPBASE +static ConVar g_ragdoll_server_snatch_instance( "g_ragdoll_server_snatch_instance", "1", FCVAR_NONE, "Allows serverside ragdolls to snatch their source entities' model instances in the same way clientside ragdolls do, thereby retaining decals." ); +#endif CStudioHdr *C_ServerRagdoll::OnNewModel( void ) { @@ -499,6 +512,26 @@ CStudioHdr *C_ServerRagdoll::OnNewModel( void ) m_iv_ragAngles.SetMaxCount( m_elementCount ); } +#ifdef MAPBASE + if ( GetOwnerEntity() ) + { + if (GetOwnerEntity()->GetModelName() == GetModelName()) + { + // TODO: Is there a better place for this? + if (GetOwnerEntity()->GetBaseAnimating()) + GetOwnerEntity()->GetBaseAnimating()->m_pServerRagdoll = this; + + if (g_ragdoll_server_snatch_instance.GetBool()) + { + GetOwnerEntity()->SnatchModelInstance( this ); + } + } + } + + // Add server ragdolls to the creation tick list + NoteRagdollCreationTick( this ); +#endif + return hdr; } diff --git a/mp/src/game/client/text_message.cpp b/mp/src/game/client/text_message.cpp index 2251556b..33bef81f 100644 --- a/mp/src/game/client/text_message.cpp +++ b/mp/src/game/client/text_message.cpp @@ -121,7 +121,7 @@ char *CHudTextMessage::BufferedLocaliseTextString( const char *msg ) char *CHudTextMessage::LookupString( const char *msg, int *msg_dest ) { if ( !msg ) - return ""; + return (char*)""; // '#' character indicates this is a reference to a string in titles.txt, and not the string itself if ( msg[0] == '#' ) diff --git a/mp/src/game/client/vgui_debugoverlaypanel.cpp b/mp/src/game/client/vgui_debugoverlaypanel.cpp index 32a7ee83..58886877 100644 --- a/mp/src/game/client/vgui_debugoverlaypanel.cpp +++ b/mp/src/game/client/vgui_debugoverlaypanel.cpp @@ -129,7 +129,7 @@ void CDebugOverlay::Paint() { float xPos = screenPos[0]; float yPos = screenPos[1]+ (pCurrText->lineOffset*13); // Line spacing; - g_pMatSystemSurface->DrawColoredText( m_hFont, xPos, yPos, r, g, b, a, pCurrText->text ); + g_pMatSystemSurface->DrawColoredText( m_hFont, xPos, yPos, r, g, b, a, "%s", pCurrText->text ); } } else @@ -138,7 +138,7 @@ void CDebugOverlay::Paint() { float xPos = screenPos[0]; float yPos = screenPos[1]+ (pCurrText->lineOffset*13); // Line spacing; - g_pMatSystemSurface->DrawColoredText( m_hFont, xPos, yPos, r, g, b, a, pCurrText->text ); + g_pMatSystemSurface->DrawColoredText( m_hFont, xPos, yPos, r, g, b, a, "%s", pCurrText->text ); } } } diff --git a/mp/src/game/client/vgui_movie_display.cpp b/mp/src/game/client/vgui_movie_display.cpp new file mode 100644 index 00000000..7bc5bb62 --- /dev/null +++ b/mp/src/game/client/vgui_movie_display.cpp @@ -0,0 +1,442 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "cbase.h" +#include "c_vguiscreen.h" +#include "vgui_controls/Label.h" +#include "vgui_bitmappanel.h" +#include +#include "c_slideshow_display.h" +#include "ienginevgui.h" +#include "fmtstr.h" +#include "vgui_controls/ImagePanel.h" +#include +#include "video/ivideoservices.h" +#include "engine/IEngineSound.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "c_movie_display.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +using namespace vgui; + +struct VideoPlaybackInfo_t +{ + VideoPlaybackInfo_t( void ) : + m_pMaterial ( NULL ), + m_nSourceHeight(0), m_nSourceWidth(0), + m_flU(0.0f),m_flV(0.0f) {} + + IMaterial *m_pMaterial; + int m_nSourceHeight, m_nSourceWidth; // Source movie's dimensions + float m_flU, m_flV; // U,V ranges for video on its sheet +}; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class CMovieDisplayScreen : public CVGuiScreenPanel +{ + DECLARE_CLASS( CMovieDisplayScreen, CVGuiScreenPanel ); + +public: + CMovieDisplayScreen( vgui::Panel *parent, const char *panelName ); + ~CMovieDisplayScreen( void ); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick( void ); + virtual void Paint( void ); + +private: + bool IsActive( void ); + + void SetupMovie( void ); + void UpdateMovie( void ); + bool BeginPlayback( const char *pFilename ); + void CalculatePlaybackDimensions( int nSrcWidth, int nSrcHeight ); + + inline void GetPanelPos( int &xpos, int &ypos ) + { + xpos = ( (float) ( GetWide() - m_nPlaybackWidth ) / 2 ); + ypos = ( (float) ( GetTall() - m_nPlaybackHeight ) / 2 ); + } + +private: + + // BINK playback info + IVideoMaterial *m_VideoMaterial; + VideoPlaybackInfo_t m_playbackInfo; + CHandle m_hVGUIScreen; + CHandle m_hScreenEntity; + + int m_nTextureId; + int m_nPlaybackHeight; // Playback dimensions (proper ration adjustments) + int m_nPlaybackWidth; + bool m_bBlackBackground; + bool m_bSlaved; + bool m_bInitialized; + bool m_bLastActiveState; // HACK: I'd rather get a real callback... + + // VGUI specifics + Label *m_pDisplayTextLabel; + + Color m_cDefault; + Color m_cInvisible; + + bool bIsAlreadyVisible; +}; + +DECLARE_VGUI_SCREEN_FACTORY( CMovieDisplayScreen, "movie_display_screen" ); + +CUtlVector g_MovieDisplays; + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +CMovieDisplayScreen::CMovieDisplayScreen( vgui::Panel *parent, const char *panelName ) +: BaseClass( parent, "CMovieDisplayScreen", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/MovieDisplayScreen.res", "MovieDisplayScreen" ) ) +{ + m_pDisplayTextLabel = new vgui::Label( this, "NumberDisplay", "testing!"); + + m_VideoMaterial = NULL; + m_nTextureId = -1; + m_bBlackBackground = true; + m_bSlaved = false; + m_bInitialized = false; + // Add ourselves to the global list of movie displays + g_MovieDisplays.AddToTail( this ); + //m_VideoMaterial->SetMuted(true); + m_bLastActiveState = IsActive(); +} + +//----------------------------------------------------------------------------- +// Purpose: Clean up the movie +//----------------------------------------------------------------------------- +CMovieDisplayScreen::~CMovieDisplayScreen( void ) +{ + if ( g_pVideo != NULL && m_VideoMaterial != NULL ) + { + g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); + m_VideoMaterial = NULL; + } + + // Clean up our texture reference + g_pMatSystemSurface->DestroyTextureID( m_nTextureId ); + + // Remove ourselves from the global list of movie displays + g_MovieDisplays.FindAndRemove( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup our scheme +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::ApplySchemeSettings( IScheme *pScheme ) +{ + assert( pScheme ); + + m_cDefault = Color( 255, 255, 255, 255 ); + m_cInvisible = Color( 0, 0, 0, 0 ); + + m_pDisplayTextLabel->SetFgColor( m_cDefault ); + m_pDisplayTextLabel->SetText( "" ); + m_pDisplayTextLabel->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + if ( !BaseClass::Init( pKeyValues, pInitData ) ) + return false; + + // Save this for simplicity later on + m_hVGUIScreen = dynamic_cast( GetEntity() ); + if ( m_hVGUIScreen != NULL ) + { + // Also get the associated entity + m_hScreenEntity = dynamic_cast(m_hVGUIScreen->GetOwnerEntity()); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Helper function to check our active state +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::IsActive( void ) +{ + bool bScreenActive = false; + if ( m_hVGUIScreen != NULL ) + { + bScreenActive = m_hVGUIScreen->IsActive(); + } + + return bScreenActive; +} + +//----------------------------------------------------------------------------- +// Purpose: Either become the master of a group of screens, or become a slave to another +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::SetupMovie( void ) +{ + // Only bother if we haven't been setup yet + if ( m_bInitialized ) + return; + + const char *szGroupName = m_hScreenEntity->GetGroupName(); + + CMovieDisplayScreen *pMasterScreen = NULL; + for ( int i = 0; i < g_MovieDisplays.Count(); i++ ) + { + // Must be valid and not us + if ( g_MovieDisplays[i] == NULL || g_MovieDisplays[i] == this ) + continue; + + // Must have an associated movie entity + if ( g_MovieDisplays[i]->m_hScreenEntity == NULL ) + continue; + + // Must have a group name to care + if ( szGroupName[0] == NULL ) + continue; + + // Group names must match! + // FIXME: Use an ID instead? + const char *szTestGroupName = g_MovieDisplays[i]->m_hScreenEntity->GetGroupName(); + if ( Q_strnicmp( szTestGroupName, szGroupName, 128 ) ) + continue; + + // See if we've found a master display + if ( g_MovieDisplays[i]->m_bInitialized && g_MovieDisplays[i]->m_bSlaved == false ) + { + m_bSlaved = true; + + // Share the info from the master + m_playbackInfo = g_MovieDisplays[i]->m_playbackInfo; + + // We need to calculate our own playback dimensions as we may be a different size than our parent + CalculatePlaybackDimensions( m_playbackInfo.m_nSourceWidth, m_playbackInfo.m_nSourceHeight ); + + // Bind our texture + m_nTextureId = surface()->CreateNewTextureID( true ); + g_pMatSystemSurface->DrawSetTextureMaterial( m_nTextureId, m_playbackInfo.m_pMaterial ); + + // Hold this as the master screen + pMasterScreen = g_MovieDisplays[i]; + break; + } + } + + // We need to try again, we have no screen entity! + if ( m_hScreenEntity == NULL ) + return; + + // No master found, become one + if ( pMasterScreen == NULL ) + { + const char *szFilename = m_hScreenEntity->GetMovieFilename(); + BeginPlayback( szFilename ); + m_bSlaved = false; + } + + // Done + m_bInitialized = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Deal with the details of the video playback +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::UpdateMovie( void ) +{ + // Only the master in a group updates the bink file + if ( m_bSlaved ) + return; + + if ( m_VideoMaterial == NULL ) + return; + + // Get the current activity state of the screen + bool bScreenActive = IsActive(); + + // Pause if the game has paused + if ( engine->IsPaused() || engine->Con_IsVisible() ) + { + bScreenActive = false; + } + + // See if we've changed our activity state + if ( bScreenActive != m_bLastActiveState ) + { + m_VideoMaterial->SetPaused( !bScreenActive ); + } + + // Updated + m_bLastActiveState = bScreenActive; + + // Update the frame if we're currently enabled + if ( bScreenActive ) + { + // Update our frame + if ( m_VideoMaterial->Update() == false ) + { + // Issue a close command + // OnVideoOver(); + // StopPlayback(); + } + + if (!m_hScreenEntity->IsMuted()) + { + m_VideoMaterial->SetMuted(false); + } + } +} + +//----------------------------------------------------------------------------- +// Update the display string +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::OnTick() +{ + BaseClass::OnTick(); + + // Create our playback or slave to another screen already playing + SetupMovie(); + + // Now update the movie + UpdateMovie(); +} + +//----------------------------------------------------------------------------- +// Purpose: Adjust the playback dimensions to properly account for our screen dimensions +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::CalculatePlaybackDimensions( int nSrcWidth, int nSrcHeight ) +{ + float flFrameRatio = ( (float) GetWide() / (float) GetTall() ); + float flVideoRatio = ( (float) nSrcWidth / (float) nSrcHeight ); + + if ( flVideoRatio > flFrameRatio ) + { + m_nPlaybackWidth = GetWide(); + m_nPlaybackHeight = ( GetWide() / flVideoRatio ); + } + else if ( flVideoRatio < flFrameRatio ) + { + m_nPlaybackWidth = ( GetTall() * flVideoRatio ); + m_nPlaybackHeight = GetTall(); + } + else + { + m_nPlaybackWidth = GetWide(); + m_nPlaybackHeight = GetTall(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Begins playback of a movie +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::BeginPlayback( const char *pFilename ) +{ + // need working video services + if ( g_pVideo == NULL ) + return false; + + // Create a new video material + if ( m_VideoMaterial != NULL ) + { + g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); + m_VideoMaterial = NULL; + } + + // Create a globally unique name for this material + char szMaterialName[256]; + + // Append our group name if we have one + const char *szGroupName = m_hScreenEntity->GetGroupName(); + if ( szGroupName[0] != NULL ) + { + Q_snprintf( szMaterialName, sizeof(szMaterialName), "%s_%s", pFilename, szGroupName ); + } + else + { + Q_snprintf( szMaterialName, sizeof(szMaterialName), "%s_%s", pFilename, m_hScreenEntity->GetEntityName() ); + } + + m_VideoMaterial = g_pVideo->CreateVideoMaterial( szMaterialName, pFilename, "GAME", + VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS, + VideoSystem::DETERMINE_FROM_FILE_EXTENSION/*, m_bAllowAlternateMedia*/ ); + + if ( m_VideoMaterial == NULL ) + return false; + + + + m_VideoMaterial->SetMuted(true); // FIXME: Allow? + + + if ( m_hScreenEntity->IsLooping() ) + { + m_VideoMaterial->SetLooping( true ); + } + + if ( m_VideoMaterial->HasAudio()) + { + // We want to be the sole audio source + enginesound->NotifyBeginMoviePlayback(); + } + + // Get our basic info from the movie + m_VideoMaterial->GetVideoImageSize( &m_playbackInfo.m_nSourceWidth, &m_playbackInfo.m_nSourceHeight ); + m_VideoMaterial->GetVideoTexCoordRange( &m_playbackInfo.m_flU, &m_playbackInfo.m_flV ); + m_playbackInfo.m_pMaterial = m_VideoMaterial->GetMaterial(); + + // Get our playback dimensions + CalculatePlaybackDimensions( m_playbackInfo.m_nSourceWidth, m_playbackInfo.m_nSourceHeight ); + + // Bind our texture + m_nTextureId = surface()->CreateNewTextureID( true ); + g_pMatSystemSurface->DrawSetTextureMaterial( m_nTextureId, m_playbackInfo.m_pMaterial ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update and draw the frame +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::Paint( void ) +{ + // Masters must keep the video updated + if ( m_bSlaved == false && m_VideoMaterial == NULL ) + { + BaseClass::Paint(); + return; + } + + // Sit in the "center" + int xpos, ypos; + GetPanelPos( xpos, ypos ); + + // Black out the background (we could omit drawing under the video surface, but this is straight-forward) + if ( m_bBlackBackground ) + { + surface()->DrawSetColor( 0, 0, 0, 255 ); + surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); + } + + // Draw it + surface()->DrawSetTexture( m_nTextureId ); + surface()->DrawSetColor( 255, 255, 255, 255 ); + surface()->DrawTexturedSubRect( xpos, ypos, xpos+m_nPlaybackWidth, ypos+m_nPlaybackHeight, 0.0f, 0.0f, m_playbackInfo.m_flU, m_playbackInfo.m_flV ); + + // Parent's turn + BaseClass::Paint(); +} diff --git a/mp/src/game/client/vgui_video.cpp b/mp/src/game/client/vgui_video.cpp index 3aa08ccd..ae637e42 100644 --- a/mp/src/game/client/vgui_video.cpp +++ b/mp/src/game/client/vgui_video.cpp @@ -16,20 +16,59 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +using namespace vgui; + +static CUtlVector< VideoPanel * > g_vecVideoPanels; + +// Thiis is a hack due to the fact that the user can type quit with the video panel up, but it's parented to the GameUI dll root panel, which is already gone so +// we would crash in the destructor +void VGui_ClearVideoPanels() +{ + for ( int i = g_vecVideoPanels.Count() - 1; i >= 0; --i ) + { + if ( g_vecVideoPanels[ i ] ) + { + delete g_vecVideoPanels[ i ]; + } + } + g_vecVideoPanels.RemoveAll(); +} + +struct VideoPanelParms_t +{ + VideoPanelParms_t( bool _interrupt = true, bool _loop = false, bool _mute = false ) + { + bAllowInterrupt = _interrupt; + bLoop = _loop; + bMute = _mute; + } + + bool bAllowInterrupt; + bool bLoop; + bool bMute; + + //float flFadeIn; + //float flFadeOut; +}; VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHeight, unsigned int nWidth, bool allowAlternateMedia ) : BaseClass( NULL, "VideoPanel" ), m_VideoMaterial( NULL ), m_nPlaybackWidth( 0 ), m_nPlaybackHeight( 0 ), - m_bAllowAlternateMedia( allowAlternateMedia ) + m_nShutdownCount( 0 ), + m_bLooping( false ), + m_bStopAllSounds( true ), + m_bAllowInterruption( true ), + m_bAllowAlternateMedia( allowAlternateMedia ), + m_bStarted( false ) { - #ifdef MAPBASE vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_ROOT ); #else vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_GAMEUIDLL ); #endif + SetParent( pParent ); SetVisible( false ); @@ -53,6 +92,11 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe SetScheme(vgui::scheme()->LoadSchemeFromFile( "resource/VideoPanelScheme.res", "VideoPanelScheme")); LoadControlSettings("resource/UI/VideoPanel.res"); + + // Let us update + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + g_vecVideoPanels.AddToTail( this ); } //----------------------------------------------------------------------------- @@ -60,6 +104,8 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe //----------------------------------------------------------------------------- VideoPanel::~VideoPanel( void ) { + g_vecVideoPanels.FindAndRemove( this ); + SetParent( (vgui::Panel *) NULL ); // Shut down this video, destroy the video material @@ -70,13 +116,39 @@ VideoPanel::~VideoPanel( void ) } } +//----------------------------------------------------------------------------- +// Purpose: Keeps a tab on when the movie is ending and allows a frame to pass to prevent threading issues +//----------------------------------------------------------------------------- +void VideoPanel::OnTick( void ) +{ + if ( m_nShutdownCount > 0 ) + { + m_nShutdownCount++; + + if ( m_nShutdownCount > 10 ) + { + OnClose(); + m_nShutdownCount = 0; + } + } + + BaseClass::OnTick(); +} + +void VideoPanel::OnVideoOver() +{ + StopPlayback(); +} + //----------------------------------------------------------------------------- // Purpose: Begins playback of a movie // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool VideoPanel::BeginPlayback( const char *pFilename ) { - // Who the heck hacked this in? + if ( !pFilename || pFilename[ 0 ] == '\0' ) + return false; + #ifdef _X360 XVIDEO_MODE videoMode; XGetVideoMode( &videoMode ); @@ -106,9 +178,25 @@ bool VideoPanel::BeginPlayback( const char *pFilename ) if ( m_VideoMaterial == NULL ) return false; + if ( m_bLooping ) + { + m_VideoMaterial->SetLooping( true ); + } + +#ifdef MAPBASE + if ( m_bMuted ) + { + m_VideoMaterial->SetMuted( true ); + } +#endif + + m_bStarted = true; + // We want to be the sole audio source - // FIXME: This may not always be true! - enginesound->NotifyBeginMoviePlayback(); + if ( m_bStopAllSounds ) + { + enginesound->NotifyBeginMoviePlayback(); + } int nWidth, nHeight; m_VideoMaterial->GetVideoImageSize( &nWidth, &nHeight ); @@ -168,9 +256,10 @@ void VideoPanel::DoModal( void ) //----------------------------------------------------------------------------- void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) { - if ( code == KEY_ESCAPE ) + bool bInterruptKeyPressed = ( code == KEY_ESCAPE ); + if ( m_bAllowInterruption && bInterruptKeyPressed ) { - OnClose(); + StopPlayback(); } else { @@ -181,34 +270,54 @@ void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) //----------------------------------------------------------------------------- // Purpose: Handle keys that should cause us to close //----------------------------------------------------------------------------- -void VideoPanel::OnKeyCodePressed( vgui::KeyCode code ) +void VideoPanel::OnKeyCodePressed( vgui::KeyCode keycode ) { + vgui::KeyCode code = GetBaseButtonCode( keycode ); + + // All these keys will interrupt playback + bool bInterruptKeyPressed = ( code == KEY_ESCAPE || + code == KEY_BACKQUOTE || + code == KEY_SPACE || + code == KEY_ENTER || + code == KEY_XBUTTON_A || + code == KEY_XBUTTON_B || + code == KEY_XBUTTON_X || + code == KEY_XBUTTON_Y || + code == KEY_XBUTTON_START || + code == KEY_XBUTTON_BACK ); + // These keys cause the panel to shutdown - if ( code == KEY_ESCAPE || - code == KEY_BACKQUOTE || - code == KEY_SPACE || - code == KEY_ENTER || - code == KEY_XBUTTON_A || - code == KEY_XBUTTON_B || - code == KEY_XBUTTON_X || - code == KEY_XBUTTON_Y || - code == KEY_XBUTTON_START || - code == KEY_XBUTTON_BACK ) + if ( m_bAllowInterruption && bInterruptKeyPressed ) { - OnClose(); + StopPlayback(); } else { - BaseClass::OnKeyCodePressed( code ); + BaseClass::OnKeyCodePressed( keycode ); } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VideoPanel::StopPlayback( void ) +{ + SetVisible( false ); + + // Start the deferred shutdown process + m_nShutdownCount = 1; +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void VideoPanel::OnClose( void ) { - enginesound->NotifyEndMoviePlayback(); + if ( m_bStopAllSounds ) + { + enginesound->NotifyEndMoviePlayback(); + } + BaseClass::OnClose(); if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) @@ -224,7 +333,6 @@ void VideoPanel::OnClose( void ) engine->ClientCmd( m_szExitCommand ); } - SetVisible( false ); MarkForDeletion(); } @@ -247,26 +355,52 @@ void VideoPanel::Paint( void ) if ( m_VideoMaterial == NULL ) return; + float alpha = ((float)GetFgColor()[3]/255.0f); +#ifdef MAPBASE + if (m_flFadeIn != 0.0f || m_flFadeOut != 0.0f) + { + // GetCurrentVideoTime() and GetVideoDuration() are borked + float flFrameCount = m_VideoMaterial->GetFrameCount(); + float flEnd = flFrameCount / m_VideoMaterial->GetVideoFrameRate().GetFPS(); + float flTime = ((float)(m_VideoMaterial->GetCurrentFrame()) / flFrameCount) * flEnd; + float flFadeOutDelta = (flEnd - m_flFadeOut); + + if (flTime <= m_flFadeIn) + { + alpha = (flTime / m_flFadeIn); + } + else if (flTime >= flFadeOutDelta) + { + alpha = (1.0f - ((flTime - flFadeOutDelta) / m_flFadeOut)); + } + } +#endif + if ( m_VideoMaterial->Update() == false ) { // Issue a close command OnVideoOver(); - OnClose(); + //OnClose(); } // Sit in the "center" int xpos, ypos; GetPanelPos( xpos, ypos ); + LocalToScreen( xpos, ypos ); // Black out the background (we could omit drawing under the video surface, but this is straight-forward) if ( m_bBlackBackground ) { - vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawSetColor( 0, 0, 0, alpha * 255.0f ); vgui::surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); } // Draw the polys to draw this out CMatRenderContextPtr pRenderContext( materials ); + +#ifdef MAPBASE + pRenderContext->ClearColor4ub( 255, 255, 255, alpha * 255.0f ); +#endif pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->PushMatrix(); @@ -306,8 +440,6 @@ void VideoPanel::Paint( void ) flTopY = FLerp( 1, -1, 0, vh ,flTopY ); flBottomY = FLerp( 1, -1, 0, vh, flBottomY ); - float alpha = ((float)GetFgColor()[3]/255.0f); - for ( int corner=0; corner<4; corner++ ) { bool bLeft = (corner==0) || (corner==3); @@ -340,16 +472,37 @@ void VideoPanel::Paint( void ) bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, unsigned int nWidth, unsigned int nHeight, const char *pVideoFilename, - const char *pExitCommand /*= NULL*/) + const char *pExitCommand /*= NULL*/, + const VideoPanelParms_t &parms ) { // Create the base video panel - VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth, false ); + VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth ); if ( pVideoPanel == NULL ) return false; + // Toggle if we want the panel to allow interruption + pVideoPanel->SetAllowInterrupt( parms.bAllowInterrupt ); + // Set the command we'll call (if any) when the video is interrupted or completes pVideoPanel->SetExitCommand( pExitCommand ); +#ifdef MAPBASE + // Toggle if we want the panel to loop (inspired by Portal 2) + pVideoPanel->SetLooping( parms.bLoop ); + + // Toggle if we want the panel to be muted + pVideoPanel->SetMuted( parms.bMute ); + + // TODO: Unique "Stop All Sounds" parameter + if (parms.bMute) + { + pVideoPanel->SetStopAllSounds( false ); + } + + // Fade parameters (unfinished) + //pVideoPanel->SetFade( parms.flFadeIn, parms.flFadeOut ); +#endif + // Start it going if ( pVideoPanel->BeginPlayback( pVideoFilename ) == false ) { @@ -365,8 +518,29 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, } //----------------------------------------------------------------------------- -// Purpose: Used to launch a video playback (Debug) - -// user must include file extension +// Purpose: Create a video panel with the supplied commands +//----------------------------------------------------------------------------- +void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, int nWidth, int nHeight, VideoPanelParms_t &parms ) +{ + char strFullpath[MAX_PATH]; + Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory + char strFilename[MAX_PATH]; + Q_StripExtension( lpszFilename, strFilename, MAX_PATH ); + Q_strncat( strFullpath, lpszFilename, MAX_PATH ); + + // Use the full screen size if they haven't specified an override + unsigned int nScreenWidth = ( nWidth != 0 ) ? nWidth : ScreenWidth(); + unsigned int nScreenHeight = ( nHeight != 0 ) ? nHeight : ScreenHeight(); + + // Create the panel and go! + if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, lpszExitCommand, parms ) == false ) + { + Warning( "Unable to play video: %s\n", strFullpath ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback //----------------------------------------------------------------------------- CON_COMMAND( playvideo, "Plays a video: [width height]" ) @@ -376,30 +550,32 @@ CON_COMMAND( playvideo, "Plays a video: [width height]" ) unsigned int nScreenWidth = Q_atoi( args[2] ); unsigned int nScreenHeight = Q_atoi( args[3] ); - - char strFullpath[MAX_PATH]; - Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory - char strFilename[MAX_PATH]; - Q_StripExtension( args[1], strFilename, MAX_PATH ); - Q_strncat( strFullpath, args[1], MAX_PATH ); - - if ( nScreenWidth == 0 ) - { - nScreenWidth = ScreenWidth(); - } - - if ( nScreenHeight == 0 ) - { - nScreenHeight = ScreenHeight(); - } - // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath ) == false ) - { - Warning( "Unable to play video: %s\n", strFullpath ); - } + // New struct; functionally identical + VideoPanelParms_t parms; + + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, parms ); } +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_nointerrupt, "Plays a video without ability to skip: [width height]" ) +{ + if ( args.ArgC() < 2 ) + return; + + unsigned int nScreenWidth = Q_atoi( args[2] ); + unsigned int nScreenHeight = Q_atoi( args[3] ); + + // New struct; functionally identical + VideoPanelParms_t parms( false ); + + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, parms ); +} + + //----------------------------------------------------------------------------- // Purpose: Used to launch a video playback and fire a command on completion //----------------------------------------------------------------------------- @@ -409,21 +585,78 @@ CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command wh if ( args.ArgC() < 2 ) return; - unsigned int nScreenWidth = ScreenWidth(); - unsigned int nScreenHeight = ScreenHeight(); - - char strFullpath[MAX_PATH]; - Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory - char strFilename[MAX_PATH]; - Q_StripExtension( args[1], strFilename, MAX_PATH ); - Q_strncat( strFullpath, args[1], MAX_PATH ); - + // Pull out the exit command we want to use char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); - // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, pExitCommand ) == false ) + // New struct; functionally identical + VideoPanelParms_t parms; + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback and fire a command on completion +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_exitcommand_nointerrupt, "Plays a video (without interruption) and fires and exit command when it is stopped or finishes: " ) +{ + if ( args.ArgC() < 2 ) + return; + + // Pull out the exit command we want to use + char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); + + // New struct; functionally identical + VideoPanelParms_t parms( false ); + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} + +//----------------------------------------------------------------------------- +// Purpose: Cause all playback to stop +//----------------------------------------------------------------------------- + +CON_COMMAND( stopvideos, "Stops all videos playing to the screen" ) +{ + FOR_EACH_VEC( g_vecVideoPanels, itr ) { - Warning( "Unable to play video: %s\n", strFullpath ); - engine->ClientCmd( pExitCommand ); + g_vecVideoPanels[itr]->StopPlayback(); } } + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback and fire a command on completion +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_complex, "Plays a video with various parameters to simplify logic_playmovie: " ) +{ + if ( args.ArgC() < 2 ) + return; + + // Pull out the exit command we want to use + char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); + + // Parameters + VideoPanelParms_t parms; + + if (args.ArgC() >= 3) + parms.bAllowInterrupt = atoi( args[3] ) != 0; + if (args.ArgC() >= 4) + parms.bLoop = atoi( args[4] ) != 0; + if (args.ArgC() >= 5) + parms.bMute = atoi( args[5] ) != 0; + + //if (args.ArgC() >= 5) + // parms.flFadeIn = atof( args[5] ); + //if (args.ArgC() >= 6) + // parms.flFadeOut = atof( args[6] ); + + // Stop a softlock + if (parms.bAllowInterrupt == false && parms.bLoop) + { + Warning( "WARNING: Tried to play video set to be uninterruptible and looping. This would cause a softlock because the video loops forever and there's no way to stop it.\n" ); + return; + } + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} diff --git a/mp/src/game/client/vgui_video.h b/mp/src/game/client/vgui_video.h index 61a980b1..b413d832 100644 --- a/mp/src/game/client/vgui_video.h +++ b/mp/src/game/client/vgui_video.h @@ -45,14 +45,22 @@ public: } bool BeginPlayback( const char *pFilename ); + void StopPlayback( void ); void SetBlackBackground( bool bBlack ){ m_bBlackBackground = bBlack; } + void SetAllowInterrupt( bool bAllowInterrupt ) { m_bAllowInterruption = bAllowInterrupt; } + void SetStopAllSounds( bool bStopAllSounds ) { m_bStopAllSounds = bStopAllSounds; } +#ifdef MAPBASE + void SetLooping( bool bLooping ) { m_bLooping = bLooping; } + void SetMuted( bool bMuted ) { m_bMuted = bMuted; } + void SetFade( float flStartFade, float flEndFade ) { m_flFadeIn = flStartFade; m_flFadeOut = flEndFade; } +#endif protected: - virtual void OnTick( void ) { BaseClass::OnTick(); } + virtual void OnTick( void ); virtual void OnCommand( const char *pcCommand ) { BaseClass::OnCommand( pcCommand ); } - virtual void OnVideoOver(){} + virtual void OnVideoOver(); protected: IVideoMaterial *m_VideoMaterial; @@ -65,8 +73,19 @@ protected: float m_flU; // U,V ranges for video on its sheet float m_flV; + bool m_bLooping; +#ifdef MAPBASE + float m_flFadeIn; + float m_flFadeOut; + bool m_bMuted; +#endif + bool m_bStopAllSounds; + bool m_bAllowInterruption; bool m_bBlackBackground; bool m_bAllowAlternateMedia; + int m_nShutdownCount; + + bool m_bStarted; }; diff --git a/mp/src/game/client/view.cpp b/mp/src/game/client/view.cpp index 04e37e3a..24440a25 100644 --- a/mp/src/game/client/view.cpp +++ b/mp/src/game/client/view.cpp @@ -107,10 +107,11 @@ extern ConVar cl_forwardspeed; static ConVar v_centermove( "v_centermove", "0.15"); static ConVar v_centerspeed( "v_centerspeed","500" ); -#ifdef TF_CLIENT_DLL +#if defined(TF_CLIENT_DLL) || defined(MAPBASE) // 54 degrees approximates a 35mm camera - we determined that this makes the viewmodels // and motions look the most natural. ConVar v_viewmodel_fov( "viewmodel_fov", "54", FCVAR_ARCHIVE, "Sets the field-of-view for the viewmodel.", true, 0.1, true, 179.9 ); +ConVar v_viewmodel_fov_script_override( "viewmodel_fov_script_override", "0", FCVAR_NONE, "If nonzero, overrides the viewmodel FOV of weapon scripts which override the viewmodel FOV." ); #else ConVar v_viewmodel_fov( "viewmodel_fov", "54", FCVAR_CHEAT, "Sets the field-of-view for the viewmodel.", true, 0.1, true, 179.9 ); #endif @@ -675,6 +676,10 @@ void CViewRender::SetUpViews() Vector ViewModelOrigin; QAngle ViewModelAngles; +#ifdef MAPBASE + view.fovViewmodel = g_pClientMode->GetViewModelFOV(); +#endif + if ( engine->IsHLTV() ) { HLTVCamera()->CalcView( view.origin, view.angles, view.fov ); @@ -710,6 +715,18 @@ void CViewRender::SetUpViews() bCalcViewModelView = true; ViewModelOrigin = view.origin; ViewModelAngles = view.angles; + +#ifdef MAPBASE + // Allow weapons to override viewmodel FOV + C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); + if (pWeapon && pWeapon->GetViewmodelFOVOverride() != 0.0f) + { + if (v_viewmodel_fov_script_override.GetFloat() > 0.0f) + view.fovViewmodel = v_viewmodel_fov_script_override.GetFloat(); + else + view.fovViewmodel = pWeapon->GetViewmodelFOVOverride(); + } +#endif } else { @@ -745,7 +762,7 @@ void CViewRender::SetUpViews() //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; + view.fovViewmodel = max(0.001f, view.fovViewmodel - flFOVOffset); #else view.fovViewmodel = g_pClientMode->GetViewModelFOV() - flFOVOffset; #endif diff --git a/mp/src/game/client/viewdebug.cpp b/mp/src/game/client/viewdebug.cpp index 619993fa..94988d9f 100644 --- a/mp/src/game/client/viewdebug.cpp +++ b/mp/src/game/client/viewdebug.cpp @@ -29,6 +29,9 @@ static ConVar mat_wateroverlaysize( "mat_wateroverlaysize", "256" ); static ConVar mat_showframebuffertexture( "mat_showframebuffertexture", "0", FCVAR_CHEAT ); static ConVar mat_framebuffercopyoverlaysize( "mat_framebuffercopyoverlaysize", "256" ); static ConVar mat_showcamerarendertarget( "mat_showcamerarendertarget", "0", FCVAR_CHEAT ); +#ifdef MAPBASE +static ConVar mat_showcamerarendertarget_all( "mat_showcamerarendertarget_all", "0", FCVAR_CHEAT ); +#endif static ConVar mat_camerarendertargetoverlaysize( "mat_camerarendertargetoverlaysize", "256", FCVAR_CHEAT ); static ConVar mat_hsv( "mat_hsv", "0", FCVAR_CHEAT ); static ConVar mat_yuv( "mat_yuv", "0", FCVAR_CHEAT ); @@ -178,6 +181,11 @@ void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float fl pMaterial = materials->FindMaterial( pszMaterialName, TEXTURE_GROUP_OTHER, true ); if( !IsErrorMaterial( pMaterial ) ) { +#ifdef MAPBASE + // HACKHACK + pMaterial->IncrementReferenceCount(); +#endif + CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( pMaterial ); IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); @@ -203,6 +211,11 @@ void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float fl meshBuilder.End(); pMesh->Draw(); + +#ifdef MAPBASE + // HACKHACK + pMaterial->DecrementReferenceCount(); +#endif } } @@ -214,7 +227,11 @@ static void OverlayFrameBufferTexture( int nFrameBufferIndex ) IMaterial *pMaterial; char buf[MAX_PATH]; Q_snprintf( buf, MAX_PATH, "debug/debugfbtexture%d", nFrameBufferIndex ); +#ifdef MAPBASE + pMaterial = materials->FindMaterial( buf, NULL, true ); +#else pMaterial = materials->FindMaterial( buf, TEXTURE_GROUP_OTHER, true ); +#endif if( !IsErrorMaterial( pMaterial ) ) { CMatRenderContextPtr pRenderContext( materials ); @@ -586,12 +603,52 @@ void CDebugViewRender::Draw2DDebuggingInfo( const CViewSetup &view ) if ( mat_showcamerarendertarget.GetBool() ) { +#ifdef MAPBASE + float w = mat_camerarendertargetoverlaysize.GetFloat(); + float h = mat_camerarendertargetoverlaysize.GetFloat(); +#else float w = mat_wateroverlaysize.GetFloat(); float h = mat_wateroverlaysize.GetFloat(); +#endif #ifdef PORTAL g_pPortalRender->OverlayPortalRenderTargets( w, h ); #else + +#ifdef MAPBASE + int iCameraNum = mat_showcamerarendertarget.GetInt(); + + if (iCameraNum == 1) // Display the default camera + { + OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h ); + } + else if (mat_showcamerarendertarget_all.GetBool()) // Display all cameras + { + OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h ); + + // Already showed one camera + iCameraNum--; + + // Show Mapbase's cameras + char szTextureName[48]; + for (int i = 0; i < iCameraNum; i++) + { + V_snprintf( szTextureName, sizeof( szTextureName ), "debug/debugcamerarendertarget_camera%i", i ); + + // Show them vertically if the cvar is set to 2 + if (mat_showcamerarendertarget_all.GetInt() == 2) + OverlayCameraRenderTarget( szTextureName, 0, h * (i + 1), w, h ); + else + OverlayCameraRenderTarget( szTextureName, w * (i + 1), 0, w, h ); + } + } + else // Display one of the new cameras + { + OverlayCameraRenderTarget( VarArgs( "debug/debugcamerarendertarget_camera%i", iCameraNum-2 ), 0, 0, w, h ); + } +#else OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h ); +#endif + #endif } @@ -655,6 +712,57 @@ CON_COMMAND_F( r_screenoverlay, "Draw specified material as an overlay", FCVAR_C } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// The same as above, but using the new indexed overlays +//----------------------------------------------------------------------------- +CON_COMMAND_F( r_screenoverlay_indexed, "Draw specified material as an overlay in the specified index", FCVAR_CHEAT|FCVAR_SERVER_CAN_EXECUTE ) +{ + if( args.ArgC() == 3 ) + { + int index = atoi( args[1] ); + if (index < 0 || index >= MAX_SCREEN_OVERLAYS) + { + Warning( "r_screenoverlay_indexed: '%i' is out of range (should be 0-9)\n", index ); + return; + } + + if ( !Q_stricmp( "off", args[2] ) ) + { + view->SetIndexedScreenOverlayMaterial( index, NULL ); + } + else + { + IMaterial *pMaterial = materials->FindMaterial( args[2], TEXTURE_GROUP_OTHER, false ); + if ( !IsErrorMaterial( pMaterial ) ) + { + view->SetIndexedScreenOverlayMaterial( index, pMaterial ); + } + else + { + view->SetIndexedScreenOverlayMaterial( index, NULL ); + } + } + } + else if ( args.ArgC() == 2 ) + { + int index = atoi( args[1] ); + if (index < 0 || index >= MAX_SCREEN_OVERLAYS) + { + Warning( "r_screenoverlay_indexed: '%i' is out of range (should be 0-9)\n", index ); + return; + } + + IMaterial *pMaterial = view->GetIndexedScreenOverlayMaterial( index ); + Warning( "r_screenoverlay_indexed %i: %s\n", index, pMaterial ? pMaterial->GetName() : "off" ); + } + else + { + Warning( "Format: r_screenoverlay_indexed []\n" ); + } +} +#endif + // Used to verify frame syncing. void CDebugViewRender::GenerateOverdrawForTesting() { diff --git a/mp/src/game/client/viewrender.cpp b/mp/src/game/client/viewrender.cpp index e512a07a..7f97aa80 100644 --- a/mp/src/game/client/viewrender.cpp +++ b/mp/src/game/client/viewrender.cpp @@ -76,6 +76,7 @@ #ifdef MAPBASE #include "mapbase/c_func_fake_worldportal.h" +#include "colorcorrectionmgr.h" #endif // Projective textures @@ -408,10 +409,6 @@ 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; @@ -1218,6 +1215,73 @@ IMaterial *CViewRender::GetScreenOverlayMaterial( ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets the screen space effect material (can't be done during rendering) +//----------------------------------------------------------------------------- +void CViewRender::SetIndexedScreenOverlayMaterial( int i, IMaterial *pMaterial ) +{ + if (i < 0 || i >= MAX_SCREEN_OVERLAYS) + return; + + m_IndexedScreenOverlayMaterials[i].Init( pMaterial ); + + if (pMaterial == NULL) + { + // Check if we should set to false + int i; + for (i = 0; i < MAX_SCREEN_OVERLAYS; i++) + { + if (m_IndexedScreenOverlayMaterials[i] != NULL) + break; + } + + if (i == MAX_SCREEN_OVERLAYS) + m_bUsingIndexedScreenOverlays = false; + } + else + { + m_bUsingIndexedScreenOverlays = true; + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +IMaterial *CViewRender::GetIndexedScreenOverlayMaterial( int i ) +{ + if (i < 0 || i >= MAX_SCREEN_OVERLAYS) + return NULL; + + return m_IndexedScreenOverlayMaterials[i]; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CViewRender::ResetIndexedScreenOverlays() +{ + for (int i = 0; i < MAX_SCREEN_OVERLAYS; i++) + { + m_IndexedScreenOverlayMaterials[i].Init( NULL ); + } + + m_bUsingIndexedScreenOverlays = false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CViewRender::GetMaxIndexedScreenOverlays( ) const +{ + return MAX_SCREEN_OVERLAYS; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Performs screen space effects, if any //----------------------------------------------------------------------------- @@ -1254,6 +1318,44 @@ void CViewRender::PerformScreenOverlay( int x, int y, int w, int h ) render->ViewDrawFade( color, m_ScreenOverlayMaterial ); } } + +#ifdef MAPBASE + if (m_bUsingIndexedScreenOverlays) + { + for (int i = 0; i < MAX_SCREEN_OVERLAYS; i++) + { + if (!m_IndexedScreenOverlayMaterials[i]) + continue; + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + if ( m_IndexedScreenOverlayMaterials[i]->NeedsFullFrameBufferTexture() ) + { + // FIXME: check with multi/sub-rect renders. Should this be 0,0,w,h instead? + DrawScreenEffectMaterial( m_IndexedScreenOverlayMaterials[i], x, y, w, h ); + } + else if ( m_IndexedScreenOverlayMaterials[i]->NeedsPowerOfTwoFrameBufferTexture() ) + { + // First copy the FB off to the offscreen texture + UpdateRefractTexture( x, y, w, h, true ); + + // Now draw the entire screen using the material... + CMatRenderContextPtr pRenderContext( materials ); + ITexture *pTexture = GetPowerOfTwoFrameBufferTexture( ); + int sw = pTexture->GetActualWidth(); + int sh = pTexture->GetActualHeight(); + // Note - don't offset by x,y - already done by the viewport. + pRenderContext->DrawScreenSpaceRectangle( m_IndexedScreenOverlayMaterials[i], 0, 0, w, h, + 0, 0, sw-1, sh-1, sw, sh ); + } + else + { + byte color[4] = { 255, 255, 255, 255 }; + render->ViewDrawFade( color, m_IndexedScreenOverlayMaterials[i] ); + } + } + } +#endif } void CViewRender::DrawUnderwaterOverlay( void ) @@ -2004,19 +2106,18 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT 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 ); + Vector vecAbsPlaneNormal; + float flLocalPlaneDist; + C_FuncFakeWorldPortal *pPortalEnt = NextFakeWorldPortal( NULL, view, vecAbsPlaneNormal, flLocalPlaneDist, 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 ); + DrawFakeWorldPortal( pCameraTarget, pPortalEnt, viewMiddle, C_BasePlayer::GetLocalPlayer(), 0, 0, width, height, view, vecAbsPlaneNormal, flLocalPlaneDist ); - pPortalEnt = NextFakeWorldPortal( pPortalEnt, view, portalPlane, frustum ); + pPortalEnt = NextFakeWorldPortal( pPortalEnt, view, vecAbsPlaneNormal, flLocalPlaneDist, frustum ); } #endif } @@ -2027,6 +2128,10 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT // Must be first render->SceneBegin(); +#ifdef MAPBASE // From Alien Swarm SDK + g_pColorCorrectionMgr->UpdateColorCorrection(); +#endif + pRenderContext.GetFrom( materials ); pRenderContext->TurnOnToneMapping(); pRenderContext.SafeRelease(); @@ -3430,12 +3535,9 @@ bool CViewRender::DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_Poin } #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. +// Based on code from monitors, mirrors, and logic_measure_movement. // // Input : cameraNum - // &cameraView @@ -3449,7 +3551,7 @@ ConVar r_fakeworldportal_debug("r_fakeworldportal_debug", "0"); //----------------------------------------------------------------------------- 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 ) + const CViewSetup &mainView, const Vector &vecAbsPlaneNormal, float flLocalPlaneDist ) { #ifdef USE_MONITORS VPROF_INCREMENT_COUNTER( "cameras rendered", 1 ); @@ -3480,105 +3582,64 @@ bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldP } } - 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.width = width; + monitorView.height = height; + monitorView.m_bOrtho = mainView.m_bOrtho; monitorView.fov = mainView.fov; - monitorView.m_bOrtho = false; - monitorView.m_flAspectRatio = 0.0f; + monitorView.m_flAspectRatio = mainView.m_flAspectRatio; monitorView.m_bViewToProjectionOverride = false; + matrix3x4_t worldToView; + AngleIMatrix( mainView.angles, mainView.origin, worldToView ); + + matrix3x4_t targetToWorld; + { + // NOTE: m_PlaneAngles is angle offset + QAngle targetAngles = pCameraEnt->m_hTargetPlane->GetAbsAngles() - pCameraEnt->m_PlaneAngles; + AngleMatrix( targetAngles, pCameraEnt->m_hTargetPlane->GetAbsOrigin(), targetToWorld ); + } + + matrix3x4_t portalToWorld; + { + Vector left, up; + VectorVectors( vecAbsPlaneNormal, left, up ); + VectorNegate( left ); + portalToWorld.Init( vecAbsPlaneNormal, left, up, pCameraEnt->GetAbsOrigin() ); + } + + matrix3x4_t portalToView; + ConcatTransforms( worldToView, portalToWorld, portalToView ); + + if ( pCameraEnt->m_flScale > 0.0f ) + { + portalToView[0][3] /= pCameraEnt->m_flScale; + portalToView[1][3] /= pCameraEnt->m_flScale; + portalToView[2][3] /= pCameraEnt->m_flScale; + } + + matrix3x4_t viewToPortal; + MatrixInvert( portalToView, viewToPortal ); + + matrix3x4_t newViewToWorld; + ConcatTransforms( targetToWorld, viewToPortal, newViewToWorld ); + + MatrixAngles( newViewToWorld, monitorView.angles, monitorView.origin ); + + // @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 + // Sky handling // - if ( pCameraEnt->SkyMode() == SKYBOX_3DSKYBOX_VISIBLE ) + SkyboxVisibility_t nSkyMode = pCameraEnt->SkyMode(); + if ( nSkyMode == SKYBOX_3DSKYBOX_VISIBLE ) { // if the 3d skybox world is drawn, then don't draw the normal skybox CSkyboxView *pSkyView = new CSkyboxView( this ); @@ -3591,14 +3652,15 @@ bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldP Vector4D plane; - // Combine the target angles and the plane angles - Vector vecAnglesNormal( angTargetAngles.x, angTargetAngles.y, angTargetAngles.z ); - VectorNormalize( vecAnglesNormal ); - VectorCopy( vecAnglesNormal, plane.AsVector3D() ); + // target direction + MatrixGetColumn( targetToWorld, 0, plane.AsVector3D() ); + VectorNormalize( plane.AsVector3D() ); + VectorNegate( 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; + plane.w = + MatrixColumnDotProduct( targetToWorld, 3, plane.AsVector3D() ) // target clip plane distance + - flLocalPlaneDist // portal plane distance on the brush. This distance needs to be accounted for while placing the exit target + - 0.1; CMatRenderContextPtr pRenderContext( materials ); pRenderContext->PushCustomClipPlane( plane.Base() ); @@ -5293,10 +5355,16 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR // 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() ); + const matrix3x4_t &matSky = m_pSky3dParams->skycamera->EntityToWorldTransform(); + matrix3x4_t matView; + AngleMatrix( angles, origin, matView ); + ConcatTransforms( matSky, matView, matView ); + MatrixAngles( matView, angles, origin ); + } + else + { + VectorAdd( origin, m_pSky3dParams->skycamera->GetAbsOrigin(), origin ); } - - VectorAdd( origin, m_pSky3dParams->skycamera->GetAbsOrigin(), origin ); } else { @@ -5304,10 +5372,16 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR m_pSky3dParams->angles.GetY() != 0 || m_pSky3dParams->angles.GetZ() != 0) { - CalculateSkyAngles( m_pSky3dParams->angles.Get() ); + matrix3x4_t matSky, matView; + AngleMatrix( m_pSky3dParams->angles, m_pSky3dParams->origin, matSky ); + AngleMatrix( angles, origin, matView ); + ConcatTransforms( matSky, matView, matView ); + MatrixAngles( matView, angles, origin ); + } + else + { + VectorAdd( origin, m_pSky3dParams->origin, origin ); } - - VectorAdd( origin, m_pSky3dParams->origin, origin ); } #else VectorAdd( origin, m_pSky3dParams->origin, origin ); @@ -5424,55 +5498,6 @@ 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 - //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- diff --git a/mp/src/game/client/viewrender.h b/mp/src/game/client/viewrender.h index 2312c4d5..3ba6cd9f 100644 --- a/mp/src/game/client/viewrender.h +++ b/mp/src/game/client/viewrender.h @@ -454,7 +454,7 @@ private: #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 ); + const CViewSetup &mainView, const Vector &vecAbsPlaneNormal, float flLocalPlaneDist ); #endif // Drawing primitives @@ -468,6 +468,13 @@ private: IMaterial *GetScreenOverlayMaterial( ); void PerformScreenOverlay( int x, int y, int w, int h ); +#ifdef MAPBASE + void SetIndexedScreenOverlayMaterial( int i, IMaterial *pMaterial ); + IMaterial *GetIndexedScreenOverlayMaterial( int i ); + void ResetIndexedScreenOverlays(); + int GetMaxIndexedScreenOverlays() const; +#endif + void DrawUnderwaterOverlay( void ); // Water-related methods @@ -511,6 +518,10 @@ private: CMaterialReference m_TranslucentSingleColor; CMaterialReference m_ModulateSingleColor; CMaterialReference m_ScreenOverlayMaterial; +#ifdef MAPBASE + CMaterialReference m_IndexedScreenOverlayMaterials[MAX_SCREEN_OVERLAYS]; + bool m_bUsingIndexedScreenOverlays; +#endif CMaterialReference m_UnderWaterOverlayMaterial; Vector m_vecLastFacing; diff --git a/mp/src/game/client/vscript_client.cpp b/mp/src/game/client/vscript_client.cpp index 3f7e7cea..6c8c5371 100644 --- a/mp/src/game/client/vscript_client.cpp +++ b/mp/src/game/client/vscript_client.cpp @@ -15,11 +15,14 @@ #include "gamerules.h" #include "vscript_client.nut" #ifdef MAPBASE_VSCRIPT -#include "mapbase/matchers.h" +#include "view.h" #include "c_world.h" #include "proxyentity.h" #include "materialsystem/imaterial.h" #include "materialsystem/imaterialvar.h" +#include "mapbase/matchers.h" +#include "mapbase/vscript_singletons.h" +#include "mapbase/vscript_vgui.h" #endif extern IScriptManager *scriptmanager; @@ -40,12 +43,20 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #endif // VMPROFILE #ifdef MAPBASE_VSCRIPT +static ScriptHook_t g_Hook_OnEntityCreated; +static ScriptHook_t g_Hook_OnEntityDeleted; + //----------------------------------------------------------------------------- // Purpose: A clientside variant of CScriptEntityIterator. //----------------------------------------------------------------------------- -class CScriptClientEntityIterator +class CScriptClientEntityIterator : public IClientEntityListener { public: + HSCRIPT GetLocalPlayer() + { + return ToHScript( C_BasePlayer::GetLocalPlayer() ); + } + HSCRIPT First() { return Next(NULL); } HSCRIPT Next( HSCRIPT hStartEntity ) @@ -90,15 +101,59 @@ public: return NULL; } + void EnableEntityListening() + { + // Start getting entity updates! + ClientEntityList().AddListenerEntity( this ); + } + + void DisableEntityListening() + { + // Stop getting entity updates! + ClientEntityList().RemoveListenerEntity( this ); + } + + void OnEntityCreated( CBaseEntity *pEntity ) + { + if ( g_pScriptVM && GetScriptHookManager().IsEventHooked( "OnEntityCreated" ) ) + { + // entity + ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) }; + g_Hook_OnEntityCreated.Call( NULL, NULL, args ); + } + }; + + void OnEntityDeleted( CBaseEntity *pEntity ) + { + if ( g_pScriptVM && GetScriptHookManager().IsEventHooked( "OnEntityDeleted" ) ) + { + // entity + ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) }; + g_Hook_OnEntityDeleted.Call( NULL, NULL, args ); + } + }; + private: } g_ScriptEntityIterator; BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptClientEntityIterator, "CEntities", SCRIPT_SINGLETON "The global list of entities" ) + DEFINE_SCRIPTFUNC( GetLocalPlayer, "Get local player" ) 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( EnableEntityListening, "Enables the 'OnEntity' hooks. This function must be called before using them." ) + DEFINE_SCRIPTFUNC( DisableEntityListening, "Disables the 'OnEntity' hooks." ) + + BEGIN_SCRIPTHOOK( g_Hook_OnEntityCreated, "OnEntityCreated", FIELD_VOID, "Called when an entity is created. Requires EnableEntityListening() to be fired beforehand." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_OnEntityDeleted, "OnEntityDeleted", FIELD_VOID, "Called when an entity is deleted. Requires EnableEntityListening() to be fired beforehand." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + END_SCRIPTHOOK() END_SCRIPTDESC(); //----------------------------------------------------------------------------- @@ -158,6 +213,8 @@ public: void SetVarFloat( int i, float value ); void SetVarVector( int i, const Vector &value ); + const char *GetVarName( int i ); + private: IMaterialVar *m_MaterialVars[SCRIPT_MAT_PROXY_MAX_VARS]; @@ -169,7 +226,16 @@ private: HSCRIPT m_hFuncOnBind; }; +class CMaterialProxyScriptInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); +}; + +CMaterialProxyScriptInstanceHelper g_MaterialProxyScriptInstanceHelper; + BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_MaterialProxyScriptInstanceHelper ) 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" ) @@ -178,12 +244,16 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Mate 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" ) + + DEFINE_SCRIPTFUNC( GetVarName, "Gets a material var's name" ) END_SCRIPTDESC(); CScriptMaterialProxy::CScriptMaterialProxy() { m_hScriptInstance = NULL; m_hFuncOnBind = NULL; + + V_memset( m_MaterialVars, 0, sizeof(m_MaterialVars) ); } CScriptMaterialProxy::~CScriptMaterialProxy() @@ -300,26 +370,25 @@ void CScriptMaterialProxy::TermScript() void CScriptMaterialProxy::OnBind( void *pRenderable ) { - if( !pRenderable ) - return; - if (m_hFuncOnBind != NULL) { - IClientRenderable *pRend = ( IClientRenderable* )pRenderable; - C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity(); - if ( pEnt ) + C_BaseEntity *pEnt = NULL; + if (pRenderable) { - g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() ); + IClientRenderable *pRend = (IClientRenderable*)pRenderable; + pEnt = pRend->GetIClientUnknown()->GetBaseEntity(); + if ( pEnt ) + { + g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() ); + } } - else + + if (!pEnt) { - // 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 { @@ -398,8 +467,29 @@ void CScriptMaterialProxy::SetVarVector( int i, const Vector &value ) return m_MaterialVars[i]->SetVecValue( value.Base(), 3 ); } +const char *CScriptMaterialProxy::GetVarName( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return NULL; + + return m_MaterialVars[i]->GetName(); +} + EXPOSE_INTERFACE( CScriptMaterialProxy, IMaterialProxy, "VScriptProxy" IMATERIAL_PROXY_INTERFACE_VERSION ); -#endif + +bool CMaterialProxyScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + CScriptMaterialProxy *pProxy = (CScriptMaterialProxy *)p; + V_snprintf( pBuf, bufSize, "(proxy: %s)", pProxy->GetMaterial() != NULL ? pProxy->GetMaterial()->GetName() : "" ); + return true; +} + +void *CMaterialProxyScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) +{ + // TODO: Material proxy save/restore? + return NULL; +} +#endif // MAPBASE_VSCRIPT //----------------------------------------------------------------------------- // @@ -431,6 +521,53 @@ bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) return true; } +#ifdef MAPBASE_VSCRIPT +int ScriptScreenWidth(); +int ScriptScreenHeight(); + +static float FrameTime() +{ + return gpGlobals->frametime; +} + +static bool Con_IsVisible() +{ + return engine->Con_IsVisible(); +} + +static bool IsWindowedMode() +{ + return engine->IsWindowedMode(); +} + +// Creates a client-side prop +HSCRIPT CreateProp( const char *pszEntityName, const Vector &vOrigin, const char *pszModelName, int iAnim ) +{ + C_BaseAnimating *pBaseEntity = (C_BaseAnimating *)CreateEntityByName( pszEntityName ); + if (!pBaseEntity) + return NULL; + + pBaseEntity->SetAbsOrigin( vOrigin ); + pBaseEntity->SetModelName( pszModelName ); + if (!pBaseEntity->InitializeAsClientEntity( pszModelName, RENDER_GROUP_OPAQUE_ENTITY )) + { + Warning("Can't initialize %s as client entity\n", pszEntityName); + return NULL; + } + + pBaseEntity->SetPlaybackRate( 1.0f ); + + int iSequence = pBaseEntity->SelectWeightedSequence( (Activity)iAnim ); + + if ( iSequence != -1 ) + { + pBaseEntity->SetSequence( iSequence ); + } + + return ToHScript( pBaseEntity ); +} +#endif + bool VScriptClientInit() { VMPROF_START @@ -491,10 +628,40 @@ bool VScriptClientInit() #else Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); #endif + +#ifdef MAPBASE_VSCRIPT + GetScriptHookManager().OnInit(); +#endif + ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); + 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." ) ); ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); - +#ifdef MAPBASE_VSCRIPT + ScriptRegisterFunction( g_pScriptVM, FrameTime, "Get the time spent on the client in the last frame" ); + ScriptRegisterFunction( g_pScriptVM, Con_IsVisible, "Returns true if the console is visible" ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptScreenWidth, "ScreenWidth", "Width of the screen in pixels" ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptScreenHeight, "ScreenHeight", "Height of the screen in pixels" ); + ScriptRegisterFunction( g_pScriptVM, IsWindowedMode, "" ); + + ScriptRegisterFunction( g_pScriptVM, MainViewOrigin, "" ); + ScriptRegisterFunction( g_pScriptVM, MainViewAngles, "" ); + ScriptRegisterFunction( g_pScriptVM, PrevMainViewOrigin, "" ); + ScriptRegisterFunction( g_pScriptVM, PrevMainViewAngles, "" ); + ScriptRegisterFunction( g_pScriptVM, MainViewForward, "" ); + ScriptRegisterFunction( g_pScriptVM, MainViewRight, "" ); + ScriptRegisterFunction( g_pScriptVM, MainViewUp, "" ); + + ScriptRegisterFunction( g_pScriptVM, CurrentViewOrigin, "" ); + ScriptRegisterFunction( g_pScriptVM, CurrentViewAngles, "" ); + ScriptRegisterFunction( g_pScriptVM, CurrentViewForward, "" ); + ScriptRegisterFunction( g_pScriptVM, CurrentViewRight, "" ); + ScriptRegisterFunction( g_pScriptVM, CurrentViewUp, "" ); + + ScriptRegisterFunction( g_pScriptVM, CreateProp, "Create an animating prop" ); +#endif + + if ( GameRules() ) { GameRules()->RegisterScriptFunctions(); @@ -510,6 +677,7 @@ bool VScriptClientInit() RegisterSharedScriptConstants(); RegisterSharedScriptFunctions(); + RegisterScriptVGUI(); #else //g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); #endif @@ -519,8 +687,13 @@ bool VScriptClientInit() g_pScriptVM->Run( g_Script_vscript_client ); } + VScriptRunScript( "vscript_client", true ); VScriptRunScript( "mapspawn", false ); +#ifdef MAPBASE_VSCRIPT + RunAddonScripts(); +#endif + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); return true; @@ -530,6 +703,12 @@ bool VScriptClientInit() CGWarning( 1, CON_GROUP_VSCRIPT, "VM Did not start!\n" ); } } +#ifdef MAPBASE_VSCRIPT + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "VSCRIPT CLIENT: Not starting because language is set to 'none'\n" ); + } +#endif } else { @@ -571,28 +750,21 @@ public: 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 ) { +#ifdef MAPBASE_VSCRIPT + g_ScriptNetMsg->LevelShutdownPreVM(); + + GetScriptHookManager().OnShutdown(); +#endif VScriptClientTerm(); } diff --git a/mp/src/game/client/vscript_client.nut b/mp/src/game/client/vscript_client.nut index 5fce6ff5..7b4f2810 100644 --- a/mp/src/game/client/vscript_client.nut +++ b/mp/src/game/client/vscript_client.nut @@ -5,18 +5,32 @@ static char g_Script_vscript_client[] = R"vscript( // //============================================================================= +local DoUniqueString = DoUniqueString +local DoDispatchParticleEffect = DoDispatchParticleEffect + function UniqueString( string = "" ) { - return DoUniqueString( string.tostring() ); + return DoUniqueString( "" + string ); } function IncludeScript( name, scope = null ) { - if ( scope == null ) + if ( !scope ) { scope = this; } return ::DoIncludeScript( name, scope ); } +function DispatchParticleEffect( particleName, origin, angles, entity = null ) +{ + return DoDispatchParticleEffect( particleName, origin, angles, entity ); +} + +function ImpulseScale( flTargetMass, flDesiredSpeed ) +{ + return flTargetMass * flDesiredSpeed; +} +__Documentation.RegisterHelp( "ImpulseScale", "float ImpulseScale(float, float)", "Returns an impulse scale required to push an object." ); + )vscript"; \ No newline at end of file diff --git a/mp/src/game/client/worldlight.cpp b/mp/src/game/client/worldlight.cpp index 66488b11..29e383a7 100644 --- a/mp/src/game/client/worldlight.cpp +++ b/mp/src/game/client/worldlight.cpp @@ -27,6 +27,10 @@ static IVEngineServer *g_pEngineServer = NULL; +#ifdef MAPBASE +ConVar cl_worldlight_use_new_method("cl_worldlight_use_new_method", "1", FCVAR_NONE, "Uses the new world light iteration method which splits lights into multiple lists for each cluster."); +#endif + //----------------------------------------------------------------------------- // Singleton exposure //----------------------------------------------------------------------------- @@ -192,6 +196,83 @@ void CWorldLights::LevelInitPreEntity() g_pFullFileSystem->Close(hFile); DevMsg("CWorldLights: load successful (%d lights at 0x%p)\n", m_nWorldLights, m_pWorldLights); + +#ifdef MAPBASE + // Now that the lights have been gathered, begin separating them into lists for each PVS cluster. + // This code is adapted from the soundscape cluster list code (see soundscape_system.cpp) and is intended to + // reduce frame drops in large maps which use dynamic RTT shadow angles. + CUtlVector clusterbounds; + int clusterCount = g_pEngineServer->GetClusterCount(); + clusterbounds.SetCount( clusterCount ); + g_pEngineServer->GetAllClusterBounds( clusterbounds.Base(), clusterCount ); + m_WorldLightsInCluster.SetCount(clusterCount); + for ( int i = 0; i < clusterCount; i++ ) + { + m_WorldLightsInCluster[i].lightCount = 0; + m_WorldLightsInCluster[i].firstLight = 0; + } + unsigned char myPVS[16 * 1024]; + CUtlVector clusterIndexList; + CUtlVector lightIndexList; + + // Find the clusters visible from each light, then add it to those clusters' light lists + // (Also try to clip for radius if possible) + for (int i = 0; i < m_nWorldLights; ++i) + { + dworldlight_t *light = &m_pWorldLights[i]; + + // Assign the sun to its own pointer + if (light->type == emit_skylight) + { + m_iSunIndex = i; + continue; + } + + float radiusSq = light->radius * light->radius; + if (radiusSq == 0.0f) + { + // TODO: Use intensity instead? + radiusSq = FLT_MAX; + } + + g_pEngineServer->GetPVSForCluster( light->cluster, sizeof( myPVS ), myPVS ); + for ( int j = 0; j < clusterCount; j++ ) + { + if ( myPVS[ j >> 3 ] & (1<<(j&7)) ) + { + float distSq = CalcSqrDistanceToAABB( clusterbounds[j].mins, clusterbounds[j].maxs, light->origin ); + if ( distSq < radiusSq ) + { + m_WorldLightsInCluster[j].lightCount++; + clusterIndexList.AddToTail(j); + lightIndexList.AddToTail(i); + } + } + } + } + + m_WorldLightsIndexList.SetCount(lightIndexList.Count()); + + // Compute the starting index of each cluster + int firstLight = 0; + for ( int i = 0; i < clusterCount; i++ ) + { + m_WorldLightsInCluster[i].firstLight = firstLight; + firstLight += m_WorldLightsInCluster[i].lightCount; + m_WorldLightsInCluster[i].lightCount = 0; + } + + // Now add each light index to the appropriate cluster's list + for ( int i = 0; i < lightIndexList.Count(); i++ ) + { + int cluster = clusterIndexList[i]; + int outIndex = m_WorldLightsInCluster[cluster].lightCount + m_WorldLightsInCluster[cluster].firstLight; + m_WorldLightsInCluster[cluster].lightCount++; + m_WorldLightsIndexList[outIndex] = lightIndexList[i]; + } + + //DevMsg( "CWorldLights: Light clusters list has %i elements; Light index list has %i\n", m_WorldLightsInCluster.Count(), m_WorldLightsIndexList.Count() ); +#endif } //----------------------------------------------------------------------------- @@ -208,6 +289,25 @@ bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &ve // Find the size of the PVS for our current position int nCluster = g_pEngineServer->GetClusterForOrigin(vecPosition); + +#ifdef MAPBASE + if (cl_worldlight_use_new_method.GetBool()) + { + FindBrightestLightSourceNew( vecPosition, vecLightPos, vecLightBrightness, nCluster ); + } + else +#endif + { + FindBrightestLightSourceOld( vecPosition, vecLightPos, vecLightBrightness, nCluster ); + } + + //engine->Con_NPrintf(m_nWorldLights, "result: %d", !vecLightBrightness.IsZero()); + return !vecLightBrightness.IsZero(); +} + +void CWorldLights::FindBrightestLightSourceOld( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster ) +{ + // Find the size of the PVS for our current position int nPVSSize = g_pEngineServer->GetPVSForCluster(nCluster, 0, NULL); // Get the PVS at our position @@ -257,7 +357,7 @@ bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &ve delete[] pvs; - return false; + return; } // Calculate square distance to this worldlight @@ -308,7 +408,87 @@ bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &ve } delete[] pvs; +} - //engine->Con_NPrintf(m_nWorldLights, "result: %d", !vecLightBrightness.IsZero()); - return !vecLightBrightness.IsZero(); -} +#ifdef MAPBASE +void CWorldLights::FindBrightestLightSourceNew( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster ) +{ + // Handle sun + if (m_iSunIndex != -1) + { + dworldlight_t *light = &m_pWorldLights[m_iSunIndex]; + + // 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()) + { + // 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)) + { + // Act like we didn't find any valid worldlights, so the shadow + // manager uses the default shadow direction instead (should be the + // sun direction) + + return; + } + } + } + + // Iterate through all the worldlights + if ( nCluster >= 0 && nCluster < m_WorldLightsInCluster.Count() ) + { + // find all soundscapes that could possibly attach to this player and update them + for ( int j = 0; j < m_WorldLightsInCluster[nCluster].lightCount; j++ ) + { + int ssIndex = m_WorldLightsIndexList[m_WorldLightsInCluster[nCluster].firstLight + j]; + dworldlight_t *light = &m_pWorldLights[ssIndex]; + + // 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; + } + + // 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()); + } + } +} +#endif diff --git a/mp/src/game/client/worldlight.h b/mp/src/game/client/worldlight.h index 65a13ea7..b621ce1f 100644 --- a/mp/src/game/client/worldlight.h +++ b/mp/src/game/client/worldlight.h @@ -27,7 +27,9 @@ public: // Find the brightest light source at a point //------------------------------------------------------------------------- bool GetBrightestLightSource(const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness); + void FindBrightestLightSourceOld( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster ); #ifdef MAPBASE + void FindBrightestLightSourceNew(const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster); bool GetCumulativeLightSource(const Vector &vecPosition, Vector &vecLightPos, float flMinBrightnessSqr); #endif @@ -42,6 +44,19 @@ private: int m_nWorldLights; dworldlight_t *m_pWorldLights; + +#ifdef MAPBASE + int m_iSunIndex = -1; // The sun's personal index + + struct clusterLightList_t + { + unsigned short lightCount; + unsigned short firstLight; + }; + + CUtlVector m_WorldLightsInCluster; + CUtlVector m_WorldLightsIndexList; +#endif }; //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/AI_Criteria.h b/mp/src/game/server/AI_Criteria.h index e7f84deb..65413605 100644 --- a/mp/src/game/server/AI_Criteria.h +++ b/mp/src/game/server/AI_Criteria.h @@ -4,6 +4,9 @@ // //=============================================================================// +#ifdef NEW_RESPONSE_SYSTEM +#include "ai_criteria_new.h" +#else #ifndef AI_CRITERIA_H #define AI_CRITERIA_H #ifdef _WIN32 @@ -278,3 +281,4 @@ private: }; #endif // AI_CRITERIA_H +#endif diff --git a/mp/src/game/server/AI_ResponseSystem.cpp b/mp/src/game/server/AI_ResponseSystem.cpp index 43fc871b..a46ddb36 100644 --- a/mp/src/game/server/AI_ResponseSystem.cpp +++ b/mp/src/game/server/AI_ResponseSystem.cpp @@ -2025,7 +2025,8 @@ void CResponseSystem::ParseInclude( CStringPool &includedFiles ) if (len+1 != strlen(scriptfile)) { - Q_snprintf(includefile, sizeof(includefile), "%s/%s", includefile, token); + Q_strncat( includefile, "/", sizeof( includefile ) ); + Q_strncat( includefile, token, sizeof( includefile ) ); } else includefile[0] = '\0'; diff --git a/mp/src/game/server/AI_ResponseSystem.h b/mp/src/game/server/AI_ResponseSystem.h index a7b3a797..f1d031e4 100644 --- a/mp/src/game/server/AI_ResponseSystem.h +++ b/mp/src/game/server/AI_ResponseSystem.h @@ -4,6 +4,9 @@ // //=============================================================================// +#ifdef NEW_RESPONSE_SYSTEM +#include "ai_responsesystem_new.h" +#else #ifndef AI_RESPONSESYSTEM_H #define AI_RESPONSESYSTEM_H @@ -40,3 +43,4 @@ class ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler( class ISaveRestoreOps *GetResponseSystemSaveRestoreOps(); #endif // AI_RESPONSESYSTEM_H +#endif diff --git a/mp/src/game/server/BaseAnimatingOverlay.cpp b/mp/src/game/server/BaseAnimatingOverlay.cpp index 84be6fba..6367f779 100644 --- a/mp/src/game/server/BaseAnimatingOverlay.cpp +++ b/mp/src/game/server/BaseAnimatingOverlay.cpp @@ -57,6 +57,45 @@ BEGIN_DATADESC( CBaseAnimatingOverlay ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseAnimatingOverlay, CBaseAnimating, "Animating models which support dynamic animation layers/overlays." ) + + DEFINE_SCRIPTFUNC( GetNumAnimOverlays, "Gets the current number of animation layers." ) + DEFINE_SCRIPTFUNC( RemoveAllGestures, "Removes all animation layers." ) + + DEFINE_SCRIPTFUNC( IsValidLayer, "Returns true if the specified layer index is valid." ) + DEFINE_SCRIPTFUNC( HasActiveLayer, "Returns true if there is currently an active layer." ) + DEFINE_SCRIPTFUNC( RemoveLayer, "Removes the specified layer index with the specified kill rate and delay." ) + DEFINE_SCRIPTFUNC( FastRemoveLayer, "Removes the specified layer index immediately." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddGesture, "AddGesture", "Adds a new animation layer using the specified activity name." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddGestureID, "AddGestureID", "Adds a new animation layer using the specified activity index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddGestureSequence, "AddGestureSequence", "Adds a new animation layer using the specified activity name." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddGestureSequenceID, "AddGestureSequenceID", "Adds a new animation layer using the specified sequence index." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptFindGestureLayer, "FindGestureLayer", "Finds and returns the first active animation layer which uses the specified activity name." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFindGestureLayerByID, "FindGestureLayerByID", "Finds and returns the first active animation layer which uses the specified activity index." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLayerActivity, "GetLayerActivity", "Gets the activity name of the specified layer index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLayerActivityID, "GetLayerActivityID", "Gets the activity index of the specified layer index." ) + DEFINE_SCRIPTFUNC( GetLayerSequence, "Gets the sequence index of the specified layer index." ) + DEFINE_SCRIPTFUNC( SetLayerDuration, "Sets the duration of the specified layer index." ) + DEFINE_SCRIPTFUNC( GetLayerDuration, "Gets the duration of the specified layer index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetLayerCycle, "SetLayerCycle", "Sets the cycle of the specified layer index." ) + DEFINE_SCRIPTFUNC( GetLayerCycle, "Gets the cycle of the specified layer index." ) + DEFINE_SCRIPTFUNC( SetLayerPlaybackRate, "Sets the playback rate of the specified layer index." ) + DEFINE_SCRIPTFUNC( SetLayerWeight, "Sets the weight of the specified layer index." ) + DEFINE_SCRIPTFUNC( GetLayerWeight, "Gets the weight of the specified layer index." ) + DEFINE_SCRIPTFUNC( SetLayerBlendIn, "Sets the fade-in of the specified layer index, with the fade being a 0-1 fraction of the cycle." ) + DEFINE_SCRIPTFUNC( SetLayerBlendOut, "Sets the fade-out of the specified layer index, with the fade being a 0-1 fraction of the cycle." ) + DEFINE_SCRIPTFUNC( SetLayerAutokill, "Sets whether or not the specified layer index should remove itself when it's finished playing." ) + DEFINE_SCRIPTFUNC( SetLayerLooping, "Sets whether or not the specified layer index should loop." ) + DEFINE_SCRIPTFUNC( SetLayerNoRestore, "Sets whether or not the specified layer index should restore after a save is loaded." ) + DEFINE_SCRIPTFUNC( SetLayerNoEvents, "Sets whether or not the specified layer index should fire animation events." ) + +END_SCRIPTDESC(); +#endif + #define ORDER_BITS 4 #define WEIGHT_BITS 8 @@ -354,7 +393,11 @@ void CBaseAnimatingOverlay::DispatchAnimEvents ( CBaseAnimating *eventHandler ) for ( int i = 0; i < m_AnimOverlay.Count(); i++ ) { +#ifdef MAPBASE // From Alien Swarm SDK + if (m_AnimOverlay[ i ].IsActive() && !m_AnimOverlay[ i ].NoEvents()) +#else if (m_AnimOverlay[ i ].IsActive()) +#endif { m_AnimOverlay[ i ].DispatchAnimEvents( eventHandler, this ); } @@ -427,6 +470,11 @@ void CAnimationLayer::DispatchAnimEvents( CBaseAnimating *eventHandler, CBaseAni event.eventtime = pOwner->m_flAnimTime + (flCycle - m_flCycle) / flCycleRate + pOwner->GetAnimTimeInterval(); } +#ifdef MAPBASE_VSCRIPT + if (eventHandler->m_ScriptScope.IsInitialized() && eventHandler->ScriptHookHandleAnimEvent( &event ) == false) + continue; +#endif + // Msg( "dispatch %d (%d : %.2f)\n", index - 1, event.event, event.eventtime ); eventHandler->HandleAnimEvent( &event ); } @@ -1066,6 +1114,38 @@ void CBaseAnimatingOverlay::SetLayerNoRestore( int iLayer, bool bNoRestore ) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// From Alien Swarm SDK +//----------------------------------------------------------------------------- +void CBaseAnimatingOverlay::SetLayerNoEvents( int iLayer, bool bNoEvents ) +{ + if (!IsValidLayer( iLayer )) + return; + + if (bNoEvents) + { + m_AnimOverlay[iLayer].m_fFlags |= ANIM_LAYER_NOEVENTS; + } + else + { + m_AnimOverlay[iLayer].m_fFlags &= ~ANIM_LAYER_NOEVENTS; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseAnimatingOverlay::IsLayerFinished( int iLayer ) +{ + if (!IsValidLayer( iLayer )) + return true; + + return m_AnimOverlay[iLayer].m_bSequenceFinished; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1163,4 +1243,36 @@ bool CBaseAnimatingOverlay::HasActiveLayer( void ) return false; } +#ifdef MAPBASE_VSCRIPT +int CBaseAnimatingOverlay::ScriptAddGesture( const char *pszActivity, bool autokill ) +{ + return AddGesture( (Activity)CAI_BaseNPC::GetActivityID( pszActivity ), autokill ); +} + +int CBaseAnimatingOverlay::ScriptAddGestureID( int iActivity, bool autokill ) +{ + return AddGesture( (Activity)iActivity, autokill ); +} + +int CBaseAnimatingOverlay::ScriptFindGestureLayer( const char *pszActivity ) +{ + return FindGestureLayer( (Activity)CAI_BaseNPC::GetActivityID( pszActivity ) ); +} + +int CBaseAnimatingOverlay::ScriptFindGestureLayerByID( int iActivity ) +{ + return FindGestureLayer( (Activity)iActivity ); +} + +const char *CBaseAnimatingOverlay::ScriptGetLayerActivity( int iLayer ) +{ + return CAI_BaseNPC::GetActivityName( GetLayerActivity( iLayer ) ); +} + +int CBaseAnimatingOverlay::ScriptGetLayerActivityID( int iLayer ) +{ + return GetLayerActivity( iLayer ); +} +#endif + //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/BaseAnimatingOverlay.h b/mp/src/game/server/BaseAnimatingOverlay.h index 5184eac3..a428caa3 100644 --- a/mp/src/game/server/BaseAnimatingOverlay.h +++ b/mp/src/game/server/BaseAnimatingOverlay.h @@ -43,6 +43,9 @@ public: #define ANIM_LAYER_DONTRESTORE 0x0008 #define ANIM_LAYER_CHECKACCESS 0x0010 #define ANIM_LAYER_DYING 0x0020 +#ifdef MAPBASE // From Alien Swarm SDK +#define ANIM_LAYER_NOEVENTS 0x0040 +#endif int m_fFlags; @@ -80,6 +83,9 @@ public: void Dying( void ) { m_fFlags |= ANIM_LAYER_DYING; } bool IsDying( void ) { return ((m_fFlags & ANIM_LAYER_DYING) != 0); } void Dead( void ) { m_fFlags &= ~ANIM_LAYER_DYING; } +#ifdef MAPBASE // From Alien Swarm SDK + bool NoEvents( void ) { return ((m_fFlags & ANIM_LAYER_NOEVENTS) != 0); } +#endif bool IsAbandoned( void ); void MarkActive( void ); @@ -176,6 +182,11 @@ public: void SetLayerAutokill( int iLayer, bool bAutokill ); void SetLayerLooping( int iLayer, bool bLooping ); void SetLayerNoRestore( int iLayer, bool bNoRestore ); +#ifdef MAPBASE + void SetLayerNoEvents( int iLayer, bool bNoEvents ); // From Alien Swarm SDK + + bool IsLayerFinished( int iLayer ); +#endif Activity GetLayerActivity( int iLayer ); int GetLayerSequence( int iLayer ); @@ -196,9 +207,26 @@ public: private: int AllocateLayer( int iPriority = 0 ); // lower priorities are processed first +#ifdef MAPBASE_VSCRIPT + int ScriptAddGesture( const char *pszActivity, bool autokill ); + int ScriptAddGestureID( int iActivity, bool autokill ); + int ScriptAddGestureSequence( const char *pszSequence, bool autokill ) { return AddGestureSequence( LookupSequence( pszSequence ), autokill ); } + int ScriptAddGestureSequenceID( int iSequence, bool autokill ) { return AddGestureSequence( iSequence, autokill ); } + + int ScriptFindGestureLayer( const char *pszActivity ); + int ScriptFindGestureLayerByID( int iActivity ); + const char *ScriptGetLayerActivity( int iLayer ); + int ScriptGetLayerActivityID( int iLayer ); + + void ScriptSetLayerCycle( int iLayer, float flCycle ) { SetLayerCycle( iLayer, flCycle ); } +#endif + DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; EXTERN_SEND_TABLE(DT_BaseAnimatingOverlay); diff --git a/mp/src/game/server/BasePropDoor.h b/mp/src/game/server/BasePropDoor.h index d96c75c5..7e8400d2 100644 --- a/mp/src/game/server/BasePropDoor.h +++ b/mp/src/game/server/BasePropDoor.h @@ -77,6 +77,8 @@ public: #ifdef MAPBASE virtual bool PassesDoorFilter(CBaseEntity *pEntity) { return true; } + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); #endif protected: @@ -103,6 +105,12 @@ protected: inline CBaseEntity *GetActivator(); +#ifdef MAPBASE + inline float GetNPCOpenDistance() { return m_flNPCOpenDistance; } + inline Activity GetNPCOpenFrontActivity() { return m_eNPCOpenFrontActivity; } + inline Activity GetNPCOpenBackActivity() { return m_eNPCOpenBackActivity; } +#endif + private: // Implement these in your leaf class. @@ -196,6 +204,12 @@ private: string_t m_SoundOpen; string_t m_SoundClose; +#ifdef MAPBASE + float m_flNPCOpenDistance; + Activity m_eNPCOpenFrontActivity; + Activity m_eNPCOpenBackActivity; +#endif + // dvs: FIXME: can we remove m_flSpeed from CBaseEntity? //float m_flSpeed; // Rotation speed when opening or closing in degrees per second. diff --git a/mp/src/game/server/CommentarySystem.cpp b/mp/src/game/server/CommentarySystem.cpp index 37fe0c86..2a5edf26 100644 --- a/mp/src/game/server/CommentarySystem.cpp +++ b/mp/src/game/server/CommentarySystem.cpp @@ -24,6 +24,8 @@ #include "Sprite.h" #ifdef MAPBASE #include "mapbase/SystemConvarMod.h" +#include +#include #endif // memdbgon must be the last include file in a .cpp file!!! @@ -72,8 +74,23 @@ class CPointCommentaryNode : public CBaseAnimating DECLARE_CLASS( CPointCommentaryNode, CBaseAnimating ); public: DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif DECLARE_SERVERCLASS(); + CPointCommentaryNode() + { +#ifdef MAPBASE + m_flViewTargetSpeedScale = 1.0f; + m_flViewPositionSpeedScale = 1.0f; + m_flReturnSpeedScale = 0.0f; + m_flPanelScale = 1.0f; + m_flPanelX = -1.0f; + m_flPanelY = -1.0f; +#endif + } + void Spawn( void ); void Precache( void ); void Activate( void ); @@ -102,11 +119,39 @@ public: void TeleportTo( CBasePlayer *pPlayer ); bool CanTeleportTo( void ); +#ifdef MAPBASE + bool IsActive() { return m_bActive; } + bool IsDisabled() { return m_bDisabled; } + + int GetCommentaryType() { return m_iCommentaryType; } + void SetCommentaryType( int iType ) { m_iCommentaryType = iType; } + + const char *GetCommentaryFile() { return STRING( m_iszCommentaryFile.Get() ); } + void SetCommentaryFile( const char *pszNewFile ) { m_iszCommentaryFile.Set( AllocPooledString( pszNewFile ) ); } + const char *GetSpeakers() { return STRING( m_iszSpeakers.Get() ); } + void SetSpeakers( const char *pszSpeakers ) { m_iszSpeakers.Set( AllocPooledString( pszSpeakers ) ); } + const char *GetPrintName() { return STRING( m_iszPrintName.Get() ); } + void SetPrintName( const char *pszPrintName ) { m_iszPrintName.Set( AllocPooledString( pszPrintName ) ); } + const char *GetFootnote() { return STRING( m_iszFootnote.Get() ); } + void SetFootnote( const char *pszFootnote ) { m_iszFootnote.Set( AllocPooledString( pszFootnote ) ); } +#endif + // Inputs void InputStartCommentary( inputdata_t &inputdata ); void InputStartUnstoppableCommentary( inputdata_t &inputdata ); void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetViewTarget( inputdata_t &inputdata ); + void InputSetViewPosition( inputdata_t &inputdata ); + void InputSetViewTargetSpeed( inputdata_t &inputdata ); + void InputSetViewPositionSpeed( inputdata_t &inputdata ); + void InputSetReturnSpeed( inputdata_t &inputdata ); +#endif + +#ifdef MAPBASE_VSCRIPT + static ScriptHook_t g_Hook_PreStartCommentary; +#endif private: string_t m_iszPreCommands; @@ -119,6 +164,14 @@ private: string_t m_iszViewPosition; CNetworkVar( EHANDLE, m_hViewPosition ); EHANDLE m_hViewPositionMover; // Entity used to blend the view to the viewposition entity +#ifdef MAPBASE + float m_flViewTargetSpeedScale; + float m_flViewPositionSpeedScale; + float m_flReturnSpeedScale; + CNetworkVar( string_t, m_iszPrintName ); + CNetworkVar( string_t, m_iszFootnote ); + float m_flViewPositionChangedTime; // View position now blends relative to this value. Mainly needed for when SetViewPosition is used +#endif bool m_bPreventMovement; bool m_bUnderCrosshair; bool m_bUnstoppable; @@ -138,6 +191,13 @@ private: CNetworkVar( string_t, m_iszSpeakers ); CNetworkVar( int, m_iNodeNumber ); CNetworkVar( int, m_iNodeNumberMax ); + +#ifdef MAPBASE + CNetworkVar( int, m_iCommentaryType ); + CNetworkVar( float, m_flPanelScale ); + CNetworkVar( float, m_flPanelX ); + CNetworkVar( float, m_flPanelY ); +#endif }; BEGIN_DATADESC( CPointCommentaryNode ) @@ -166,6 +226,18 @@ BEGIN_DATADESC( CPointCommentaryNode ) DEFINE_FIELD( m_bPreventChangesWhileMoving, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "start_disabled" ), DEFINE_KEYFIELD( m_vecTeleportOrigin, FIELD_VECTOR, "teleport_origin" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flViewTargetSpeedScale, FIELD_FLOAT, "viewtarget_speed" ), + DEFINE_KEYFIELD( m_flViewPositionSpeedScale, FIELD_FLOAT, "viewposition_speed" ), + DEFINE_KEYFIELD( m_flReturnSpeedScale, FIELD_FLOAT, "return_speed" ), + DEFINE_KEYFIELD( m_iszPrintName, FIELD_STRING, "printname" ), + DEFINE_KEYFIELD( m_iszFootnote, FIELD_STRING, "footnote" ), + DEFINE_FIELD( m_flViewPositionChangedTime, FIELD_TIME ), + DEFINE_KEYFIELD( m_iCommentaryType, FIELD_INTEGER, "type" ), + DEFINE_KEYFIELD( m_flPanelScale, FIELD_FLOAT, "panelscale" ), + DEFINE_KEYFIELD( m_flPanelX, FIELD_FLOAT, "x" ), + DEFINE_KEYFIELD( m_flPanelY, FIELD_FLOAT, "y" ), +#endif // Outputs DEFINE_OUTPUT( m_pOnCommentaryStarted, "OnCommentaryStarted" ), @@ -176,6 +248,13 @@ BEGIN_DATADESC( CPointCommentaryNode ) DEFINE_INPUTFUNC( FIELD_VOID, "StartUnstoppableCommentary", InputStartUnstoppableCommentary ), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetViewTarget", InputSetViewTarget ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetViewPosition", InputSetViewPosition ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetViewTargetSpeed", InputSetViewTargetSpeed ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetViewPositionSpeed", InputSetViewPositionSpeed ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetReturnSpeed", InputSetReturnSpeed ), +#endif // Functions DEFINE_THINKFUNC( SpinThink ), @@ -183,6 +262,37 @@ BEGIN_DATADESC( CPointCommentaryNode ) DEFINE_THINKFUNC( UpdateViewPostThink ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT + +ScriptHook_t CPointCommentaryNode::g_Hook_PreStartCommentary; + +BEGIN_ENT_SCRIPTDESC( CPointCommentaryNode, CBaseAnimating, "Commentary nodes which play commentary in commentary mode." ) + DEFINE_SCRIPTFUNC( IsDisabled, "" ) + DEFINE_SCRIPTFUNC( SetDisabled, "" ) + + DEFINE_SCRIPTFUNC( IsActive, "" ) + DEFINE_SCRIPTFUNC( GetCommentaryFile, "" ) + DEFINE_SCRIPTFUNC( SetCommentaryFile, "" ) + DEFINE_SCRIPTFUNC( GetSpeakers, "" ) + DEFINE_SCRIPTFUNC( SetSpeakers, "" ) + DEFINE_SCRIPTFUNC( GetPrintName, "" ) + DEFINE_SCRIPTFUNC( SetPrintName, "" ) + DEFINE_SCRIPTFUNC( GetFootnote, "" ) + DEFINE_SCRIPTFUNC( SetFootnote, "" ) + DEFINE_SCRIPTFUNC( GetCommentaryType, "" ) + DEFINE_SCRIPTFUNC( SetCommentaryType, "" ) + + DEFINE_SCRIPTFUNC( HasViewTarget, "" ) + DEFINE_SCRIPTFUNC( PreventsMovement, "" ) + DEFINE_SCRIPTFUNC( CannotBeStopped, "" ) + + DEFINE_SCRIPTFUNC( AbortPlaying, "Stops playing the node and snaps out of its camera control immediately. The game uses this function to shut down commentary while in the middle of playing a node, as it can't smoothly blend out (since the commentary entities need to be removed)." ) + + DEFINE_SIMPLE_SCRIPTHOOK( CPointCommentaryNode::g_Hook_PreStartCommentary, "PreStartCommentary", FIELD_BOOLEAN, "Called just before commentary begins. Use this to modify variables or commentary behavior before it begins. Returning false will prevent the commentary from starting." ) +END_SCRIPTDESC(); + +#endif // MAPBASE_VSCRIPT + IMPLEMENT_SERVERCLASS_ST( CPointCommentaryNode, DT_PointCommentaryNode ) SendPropBool( SENDINFO(m_bActive) ), SendPropStringT( SENDINFO(m_iszCommentaryFile) ), @@ -192,6 +302,14 @@ IMPLEMENT_SERVERCLASS_ST( CPointCommentaryNode, DT_PointCommentaryNode ) SendPropInt( SENDINFO(m_iNodeNumber), 8, SPROP_UNSIGNED ), SendPropInt( SENDINFO(m_iNodeNumberMax), 8, SPROP_UNSIGNED ), SendPropEHandle( SENDINFO(m_hViewPosition) ), +#ifdef MAPBASE + SendPropStringT( SENDINFO( m_iszPrintName ) ), + SendPropStringT( SENDINFO( m_iszFootnote ) ), + SendPropInt( SENDINFO( m_iCommentaryType ), 2, SPROP_UNSIGNED ), + SendPropFloat( SENDINFO( m_flPanelScale ) ), + SendPropFloat( SENDINFO( m_flPanelX ) ), + SendPropFloat( SENDINFO( m_flPanelY ) ), +#endif END_SEND_TABLE() LINK_ENTITY_TO_CLASS( point_commentary_node, CPointCommentaryNode ); @@ -673,6 +791,11 @@ public: #endif engine->LockNetworkStringTables( oldLock ); + +#ifdef MAPBASE + // Special commentary localization file (useful for things like text nodes or print names) + g_pVGuiLocalize->AddFile( "resource/commentary_%language%.txt", "MOD" ); +#endif } void ShutDownCommentary( void ) @@ -892,10 +1015,32 @@ bool IsListeningToCommentary( void ) void CPointCommentaryNode::Spawn( void ) { // No model specified? - char *szModel = (char *)STRING( GetModelName() ); + const char *szModel = STRING( GetModelName() ); if (!szModel || !*szModel) { +#ifdef MAPBASE + switch (m_iCommentaryType) + { + case COMMENTARY_TYPE_TEXT: + szModel = "models/extras/info_text.mdl"; + break; + + case COMMENTARY_TYPE_IMAGE: + szModel = "models/extras/info_image.mdl"; + break; + + case COMMENTARY_TYPE_SCENE: + szModel = "models/extras/info_scene.mdl"; + break; + + default: + case COMMENTARY_TYPE_AUDIO: + szModel = "models/extras/info_speech.mdl"; + break; + } +#else szModel = "models/extras/info_speech.mdl"; +#endif SetModelName( AllocPooledString(szModel) ); } @@ -906,6 +1051,12 @@ void CPointCommentaryNode::Spawn( void ) AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST ); AddEffects( EF_NOSHADOW ); +#ifdef MAPBASE + // Default to view position speed scale (which in turn defaults to 1.0) + if (m_flReturnSpeedScale == 0.0f) + m_flReturnSpeedScale = m_flViewPositionSpeedScale; +#endif + // Setup for animation ResetSequence( LookupSequence("idle") ); SetThink( &CPointCommentaryNode::SpinThink ); @@ -1119,6 +1270,19 @@ void CPointCommentaryNode::StartCommentary( void ) if ( !pPlayer ) return; +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_PreStartCommentary.CanRunInScope( m_ScriptScope )) + { + ScriptVariant_t functionReturn; + if ( g_Hook_PreStartCommentary.Call( m_ScriptScope, &functionReturn, NULL ) && functionReturn.m_type == FIELD_BOOLEAN ) + { + // Don't play the commentary if it returned false + if (functionReturn.m_bool == false) + return; + } + } +#endif + m_bActive = true; m_flAnimTime = gpGlobals->curtime; @@ -1140,6 +1304,21 @@ void CPointCommentaryNode::StartCommentary( void ) // Start the commentary m_flStartTime = gpGlobals->curtime; +#ifdef MAPBASE + if (m_hViewPosition.Get()) + { + m_flViewPositionChangedTime = gpGlobals->curtime; + } + else + { + m_flViewPositionChangedTime = -1.0f; + } + + // This is now used in certain places to denote the "last blend to" origin + m_vecFinishOrigin = pPlayer->EyePosition(); + m_vecFinishAngles = pPlayer->EyeAngles(); +#endif + // If we have a view target, start blending towards it if ( m_hViewTarget || m_hViewPosition.Get() ) { @@ -1214,6 +1393,10 @@ void CPointCommentaryNode::UpdateViewThink( void ) float dx = AngleDiff( angGoal.x, angCurrent.x ); float dy = AngleDiff( angGoal.y, angCurrent.y ); float mod = 1.0 - ExponentialDecay( 0.5, 0.3, gpGlobals->frametime ); +#ifdef MAPBASE + if (m_flViewTargetSpeedScale != 1.0f) + mod *= m_flViewTargetSpeedScale; +#endif float dxmod = dx * mod; float dymod = dy * mod; @@ -1254,16 +1437,85 @@ void CPointCommentaryNode::UpdateViewThink( void ) } // Blend to the target position over time. - float flCurTime = (gpGlobals->curtime - m_flStartTime); +#ifdef MAPBASE + float flCurTime = (gpGlobals->curtime - m_flViewPositionChangedTime); + if (m_flViewPositionSpeedScale != 1.0f) + flCurTime *= m_flViewPositionSpeedScale; +#else + float flCurTime = (gpGlobals->curtime - m_flStartTime); +#endif float flBlendPerc = clamp( flCurTime * 0.5f, 0.f, 1.f ); // Figure out the current view position Vector vecCurEye; +#ifdef MAPBASE + VectorLerp( m_vecFinishOrigin, m_hViewPosition.Get()->GetAbsOrigin(), flBlendPerc, vecCurEye ); +#else VectorLerp( pPlayer->EyePosition(), m_hViewPosition.Get()->GetAbsOrigin(), flBlendPerc, vecCurEye ); +#endif m_hViewPositionMover->SetAbsOrigin( vecCurEye ); SetNextThink( gpGlobals->curtime, s_pCommentaryUpdateViewThink ); } +#ifdef MAPBASE + else if ( m_flViewPositionChangedTime != -1.0f && m_hViewPositionMover ) + { + // Blend back to the player's position over time. + float flCurTime = (gpGlobals->curtime - m_flViewPositionChangedTime); + if (m_flViewPositionSpeedScale != 1.0f) + flCurTime *= m_flViewPositionSpeedScale; + + //float flTimeToBlend = MIN( 2.0, m_flViewPositionChangedTime - m_flStartTime ); + //float flBlendPerc = 1.0f - clamp( flCurTime / flTimeToBlend, 0.f, 1.f ); + float flBlendPerc = 1.0f - clamp( flCurTime * 0.5f, 0.f, 1.f ); + + //Msg("OUT: CurTime %.2f, BlendTime: %.2f, Blend: %.3f\n", flCurTime, flTimeToBlend, flBlendPerc ); + + // Only do this while we're still moving + if ( flBlendPerc > 0 ) + { + // Figure out the current view position + Vector vecPlayerPos = pPlayer->EyePosition(); + Vector vecToPosition = (m_vecFinishOrigin - vecPlayerPos); + Vector vecCurEye = pPlayer->EyePosition() + (vecToPosition * flBlendPerc); + m_hViewPositionMover->SetAbsOrigin( vecCurEye ); + + if ( m_hViewTarget ) + { + Quaternion quatFinish; + Quaternion quatOriginal; + Quaternion quatCurrent; + AngleQuaternion( m_vecOriginalAngles, quatOriginal ); + AngleQuaternion( m_vecFinishAngles, quatFinish ); + QuaternionSlerp( quatFinish, quatOriginal, 1.0 - flBlendPerc, quatCurrent ); + QAngle angCurrent; + QuaternionAngles( quatCurrent, angCurrent ); + m_hViewPositionMover->SetAbsAngles( angCurrent ); + } + + SetNextThink( gpGlobals->curtime, s_pCommentaryUpdateViewThink ); + return; + } + else + { + pPlayer->SnapEyeAngles( m_hViewPositionMover->GetAbsAngles() ); + + // Try to clean up the view position stuff without ending the commentary + if ( !m_hViewTargetAngles && pPlayer->GetActiveWeapon() ) + { + pPlayer->GetActiveWeapon()->Deploy(); + } + + if (pPlayer->GetViewEntity() == m_hViewPositionMover) + { + pPlayer->SetViewEntity( NULL ); + } + UTIL_Remove( m_hViewPositionMover ); + + m_flViewPositionChangedTime = -1.0f; + } + } +#endif } //----------------------------------------------------------------------------- @@ -1279,6 +1531,10 @@ void CPointCommentaryNode::UpdateViewPostThink( void ) { // Blend back to the player's position over time. float flCurTime = (gpGlobals->curtime - m_flFinishedTime); +#ifdef MAPBASE + if (m_flReturnSpeedScale != 1.0f) + flCurTime *= m_flReturnSpeedScale; +#endif float flTimeToBlend = MIN( 2.0, m_flFinishedTime - m_flStartTime ); float flBlendPerc = 1.0f - clamp( flCurTime / flTimeToBlend, 0.f, 1.f ); @@ -1440,6 +1696,79 @@ void CPointCommentaryNode::InputDisable( inputdata_t &inputdata ) SetDisabled( true ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCommentaryNode::InputSetViewTarget( inputdata_t &inputdata ) +{ + m_hViewTarget = inputdata.value.Entity(); + + // Do not let Activate() reassign this + m_iszViewTarget = NULL_STRING; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCommentaryNode::InputSetViewPosition( inputdata_t &inputdata ) +{ + if (m_hViewPosition.Get() && m_hViewPositionMover) + { + // In case the view position is being cleared, assign the "finish" vectors + m_vecFinishOrigin = m_hViewPositionMover->GetAbsOrigin(); + m_vecFinishAngles = m_hViewPositionMover->GetAbsAngles(); + } + else + { + CBasePlayer *pPlayer = GetCommentaryPlayer(); + if (pPlayer) + { + // And in case it's a new view position coming from the player, assign the "finish" vectors to the player + m_vecFinishOrigin = pPlayer->EyePosition(); + m_vecFinishAngles = m_vecOriginalAngles = pPlayer->EyeAngles(); + } + } + + m_hViewPosition = inputdata.value.Entity(); + + // Do not let Activate() reassign this + m_iszViewPosition = NULL_STRING; + + m_flViewPositionChangedTime = gpGlobals->curtime; + + // If we have a view target, start blending towards it + if ( m_hViewPosition.Get() ) + { + SetContextThink( &CPointCommentaryNode::UpdateViewThink, gpGlobals->curtime, s_pCommentaryUpdateViewThink ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCommentaryNode::InputSetViewTargetSpeed( inputdata_t &inputdata ) +{ + m_flViewTargetSpeedScale = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCommentaryNode::InputSetViewPositionSpeed( inputdata_t &inputdata ) +{ + m_flViewPositionSpeedScale = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointCommentaryNode::InputSetReturnSpeed( inputdata_t &inputdata ) +{ + m_flReturnSpeedScale = inputdata.value.Float(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1501,7 +1830,11 @@ void CPointCommentaryNode::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways bool CPointCommentaryNode::PreventsMovement( void ) { // If we're moving the player's view at all, prevent movement +#ifdef MAPBASE + if ( m_hViewPosition.Get() || m_flViewPositionChangedTime != -1.0f ) +#else if ( m_hViewPosition.Get() ) +#endif return true; return m_bPreventMovement; diff --git a/mp/src/game/server/EntityFlame.cpp b/mp/src/game/server/EntityFlame.cpp index d3a1be10..80217efb 100644 --- a/mp/src/game/server/EntityFlame.cpp +++ b/mp/src/game/server/EntityFlame.cpp @@ -242,7 +242,12 @@ void CEntityFlame::FlameThink( void ) } CAI_BaseNPC *pNPC = m_hEntAttached->MyNPCPointer(); +#ifdef MAPBASE + // Don't extingish if the NPC is still dying + if ( pNPC && !pNPC->IsAlive() && pNPC->m_lifeState != LIFE_DYING ) +#else if ( pNPC && !pNPC->IsAlive() ) +#endif { UTIL_Remove( this ); // Notify the NPC that it's no longer burning! diff --git a/mp/src/game/server/EnvMessage.cpp b/mp/src/game/server/EnvMessage.cpp index 3f47c2ea..7725b67d 100644 --- a/mp/src/game/server/EnvMessage.cpp +++ b/mp/src/game/server/EnvMessage.cpp @@ -154,6 +154,9 @@ public: DECLARE_DATADESC(); void Spawn( void ); +#ifdef MAPBASE + void PrecacheCreditsThink(); +#endif void InputRollCredits( inputdata_t &inputdata ); void InputRollOutroCredits( inputdata_t &inputdata ); void InputShowLogo( inputdata_t &inputdata ); @@ -186,6 +189,8 @@ BEGIN_DATADESC( CCredits ) #ifdef MAPBASE DEFINE_KEYFIELD( m_iszCreditsFile, FIELD_STRING, "CreditsFile" ), + + DEFINE_THINKFUNC( PrecacheCreditsThink ), #endif DEFINE_FIELD( m_bRolledOutroCredits, FIELD_BOOLEAN ), @@ -196,8 +201,35 @@ void CCredits::Spawn( void ) { SetSolid( SOLID_NONE ); SetMoveType( MOVETYPE_NONE ); + +#ifdef MAPBASE + // Ensures the player has time to spawn + SetContextThink( &CCredits::PrecacheCreditsThink, gpGlobals->curtime + 0.5f, "PrecacheCreditsContext" ); +#endif } +#ifdef MAPBASE +void CCredits::PrecacheCreditsThink() +{ + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (!pPlayer) + { + Warning( "%s: No player\n", GetDebugName() ); + return; + } + + CSingleUserRecipientFilter user( pPlayer ); + user.MakeReliable(); + + UserMessageBegin( user, "CreditsMsg" ); + WRITE_BYTE( 4 ); + WRITE_STRING( STRING(m_iszCreditsFile) ); + MessageEnd(); + + SetContextThink( NULL, TICK_NEVER_THINK, "PrecacheCreditsContext" ); +} +#endif + static void CreditsDone_f( void ) { CCredits *pCredits = (CCredits*)gEntList.FindEntityByClassname( NULL, "env_credits" ); @@ -212,6 +244,10 @@ static ConCommand creditsdone("creditsdone", CreditsDone_f ); extern ConVar sv_unlockedchapters; +#ifdef MAPBASE +extern int Mapbase_GetChapterCount(); +#endif + void CCredits::OnRestore() { BaseClass::OnRestore(); @@ -226,6 +262,10 @@ void CCredits::OnRestore() void CCredits::RollOutroCredits() { +#ifdef MAPBASE + // Don't set this if we're using Mapbase chapters or if sv_unlockedchapters is already greater than 15 + if (Mapbase_GetChapterCount() <= 0 && sv_unlockedchapters.GetInt() < 15) +#endif sv_unlockedchapters.SetValue( "15" ); CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); diff --git a/mp/src/game/server/ai_activity.cpp b/mp/src/game/server/ai_activity.cpp index 74c312f7..23423568 100644 --- a/mp/src/game/server/ai_activity.cpp +++ b/mp/src/game/server/ai_activity.cpp @@ -2211,15 +2211,22 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) 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_RELOAD_AR2_LOW ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR2 ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_AR2_LOW ); #endif -#ifdef SHARED_COMBINE_ACTIVITIES +#if 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_COMBINE_THROW_GRENADE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK1 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK2 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_ADVANCE ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_FORWARD ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_GROUP ); @@ -2229,8 +2236,650 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_TAKECOVER ); #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#if EXPANDED_HL2_WEAPON_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_IDLE_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_WALK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RUN_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_REVOLVER ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_WALK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_CROSSBOW ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_CROSSBOW_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_CROSSBOW_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_CROSSBOW_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROSSBOW_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_CROSSBOW_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROSSBOW_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_CROSSBOW_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_CROSSBOW_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_CROSSBOW_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_PISTOL_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_AIM_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_AIM_PISTOL ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_SHOTGUN_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_LOW ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SHOTGUN_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SHOTGUN_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SHOTGUN_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_RPG ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_ANNABELLE ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_ANNABELLE_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_ANNABELLE ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_ANNABELLE ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_ANNABELLE_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_ANNABELLE ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_MELEE ); + ADD_ACTIVITY_TO_SR( ACT_RUN_MELEE ); + + ADD_ACTIVITY_TO_SR( ACT_RUN_PACKAGE ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SUITCASE ); + ADD_ACTIVITY_TO_SR( ACT_ARM_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_ARM_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_ARM_RPG ); + ADD_ACTIVITY_TO_SR( ACT_ARM_MELEE ); ADD_ACTIVITY_TO_SR( ACT_DISARM_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_RPG ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_MELEE ); +#endif + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR1 ); + //ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR1_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR1_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_AR1_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR1_LOW ); + //ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR1 ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR1_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR1_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR1_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR1_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_AR1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_AR1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR1_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_AR3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR3 ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR3_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_AR3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR3_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR3_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AR3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AR3_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_AR3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_AR3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR3_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SMG2 ); + //ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG2_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_SMG2_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_SMG2_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG2_LOW ); + //ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_SMG2 ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SMG2_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_SMG2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SMG2_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SMG2_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SMG2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SMG2_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SMG2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SMG2_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SMG2_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SMG3 ); + //ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_SMG3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_SMG3_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG3_LOW ); + //ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_SMG3 ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SMG3_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_SMG3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SMG3_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SMG3_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SMG3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SMG3_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SMG3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SMG3_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SMG3_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_HMG1 ); + //ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_HMG1_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_HMG1_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_HMG1_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_HMG1_LOW ); + //ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_HMG1 ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_HMG1_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_HMG1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_HMG1_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_HMG1_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_HMG1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_HMG1_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_HMG1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_HMG1_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_HMG1_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SNIPER_RIFLE ); + //ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_SNIPER_RIFLE_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_SNIPER_RIFLE_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SNIPER_RIFLE_LOW ); + //ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_SNIPER_RIFLE ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SNIPER_RIFLE_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_SNIPER_RIFLE_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SNIPER_RIFLE_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SNIPER_RIFLE_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SNIPER_RIFLE_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SNIPER_RIFLE_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SNIPER_RIFLE_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SNIPER_RIFLE_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SNIPER_RIFLE_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_WALK_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_RUN_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_DUAL_PISTOLS_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_DUAL_PISTOLS_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_DUAL_PISTOLS_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_DUAL_PISTOLS_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_DUAL_PISTOLS ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_DUAL_PISTOLS_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_DUAL_PISTOLS_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_DUAL_PISTOLS_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_DUAL_PISTOLS_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_DUAL_PISTOLS_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_DUAL_PISTOLS_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_DUAL_PISTOLS_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_DUAL_PISTOLS_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_DUAL_PISTOLS_STIMULATED ); +#endif + +#if EXPANDED_NAVIGATION_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_CLIMB_ALL ); + ADD_ACTIVITY_TO_SR( ACT_CLIMB_IDLE ); + + ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_TOP ); + ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_BOTTOM ); + ADD_ACTIVITY_TO_SR( ACT_CLIMB_DISMOUNT_BOTTOM ); +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_MED ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SHOTGUN_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_PISTOL_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_MED ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_PISTOL_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_MED ); + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR3_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR3_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG3_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG3_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_HMG1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_HMG1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SNIPER_RIFLE_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SNIPER_RIFLE_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_DUAL_PISTOLS_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_DUAL_PISTOLS_MED ); +#endif + + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_RIFLE ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_PISTOL ); +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_SMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_PHYSGUN ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_GRENADE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_RPG ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_MELEE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_SLAM ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_PHYSGUN ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_REVOLVER ); + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_AR1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_AR1 ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_AR3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_AR3 ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_SMG2 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_SMG2 ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_SMG3 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_SMG3 ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_HMG1 ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_HMG1 ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_SNIPER_RIFLE ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_DUAL_PISTOLS ); +#endif + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_USE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_USE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_USE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_USE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_USE ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_USE ); + + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_USE_HEAVY ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_USE_HEAVY ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_USE_HEAVY ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_CROUCH_USE_HEAVY ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_WALK_CROUCH_USE_HEAVY ); + ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_USE_HEAVY ); #endif } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: This is a multi-purpose table which links NPC activities to their gesture variants. +//----------------------------------------------------------------------------- +CAI_BaseNPC::actlink_t CAI_BaseNPC::gm_ActivityGestureLinks[] = +{ + { ACT_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK1 }, + { ACT_RANGE_ATTACK2, ACT_GESTURE_RANGE_ATTACK2 }, + { ACT_MELEE_ATTACK1, ACT_GESTURE_MELEE_ATTACK1 }, + { ACT_MELEE_ATTACK2, ACT_GESTURE_MELEE_ATTACK2 }, + { ACT_RELOAD, ACT_GESTURE_RELOAD }, + + { ACT_RANGE_ATTACK1_LOW, ACT_GESTURE_RANGE_ATTACK1 }, // NOTE: ACT_GESTURE_RANGE_ATTACK1_LOW exists, but isn't used + { ACT_RANGE_ATTACK2_LOW, ACT_GESTURE_RANGE_ATTACK2 }, // NOTE: ACT_GESTURE_RANGE_ATTACK2_LOW exists, but isn't used + { ACT_RELOAD_LOW, ACT_GESTURE_RELOAD }, + + { ACT_MELEE_ATTACK_SWING, ACT_GESTURE_MELEE_ATTACK_SWING }, + + // ----------------------------------------------------------- + + { ACT_RANGE_ATTACK_AR2, ACT_GESTURE_RANGE_ATTACK_AR2 }, + { ACT_RANGE_ATTACK_AR2_LOW, ACT_GESTURE_RANGE_ATTACK_AR2 }, + { ACT_RANGE_ATTACK_SMG1, ACT_GESTURE_RANGE_ATTACK_SMG1 }, + { ACT_RANGE_ATTACK_SMG1_LOW, ACT_GESTURE_RANGE_ATTACK_SMG1 }, + { ACT_RANGE_ATTACK_SHOTGUN, ACT_GESTURE_RANGE_ATTACK_SHOTGUN }, + { ACT_RANGE_ATTACK_SHOTGUN_LOW, ACT_GESTURE_RANGE_ATTACK_SHOTGUN }, + { ACT_RANGE_ATTACK_PISTOL, ACT_GESTURE_RANGE_ATTACK_PISTOL }, + { ACT_RANGE_ATTACK_PISTOL_LOW, ACT_GESTURE_RANGE_ATTACK_PISTOL }, + + // ----------------------------------------------------------- + + { ACT_SMALL_FLINCH, ACT_GESTURE_SMALL_FLINCH }, + { ACT_BIG_FLINCH, ACT_GESTURE_BIG_FLINCH }, + { ACT_FLINCH_HEAD, ACT_GESTURE_FLINCH_HEAD }, + { ACT_FLINCH_CHEST, ACT_GESTURE_FLINCH_CHEST }, + { ACT_FLINCH_STOMACH, ACT_GESTURE_FLINCH_STOMACH }, + { ACT_FLINCH_LEFTARM, ACT_GESTURE_FLINCH_LEFTARM }, + { ACT_FLINCH_RIGHTARM, ACT_GESTURE_FLINCH_RIGHTARM }, + { ACT_FLINCH_LEFTLEG, ACT_GESTURE_FLINCH_LEFTLEG }, + { ACT_FLINCH_RIGHTLEG, ACT_GESTURE_FLINCH_RIGHTLEG }, + + // ----------------------------------------------------------- + +#if AR2_ACTIVITY_FIX == 1 + { ACT_RELOAD_AR2, ACT_GESTURE_RELOAD_AR2 }, + { ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2 }, +#endif + { ACT_RELOAD_SMG1, ACT_GESTURE_RELOAD_SMG1 }, + { ACT_RELOAD_SMG1_LOW, ACT_GESTURE_RELOAD_SMG1 }, + { ACT_RELOAD_SHOTGUN, ACT_GESTURE_RELOAD_SHOTGUN }, + { ACT_RELOAD_SHOTGUN_LOW, ACT_GESTURE_RELOAD_SHOTGUN }, + { ACT_RELOAD_PISTOL, ACT_GESTURE_RELOAD_PISTOL }, + { ACT_RELOAD_PISTOL_LOW, ACT_GESTURE_RELOAD_PISTOL }, + +#if SHARED_COMBINE_ACTIVITIES + { ACT_SPECIAL_ATTACK1, ACT_GESTURE_SPECIAL_ATTACK1 }, + { ACT_SPECIAL_ATTACK2, ACT_GESTURE_SPECIAL_ATTACK2 }, + { ACT_COMBINE_THROW_GRENADE, ACT_GESTURE_COMBINE_THROW_GRENADE }, + { ACT_COMBINE_AR2_ALTFIRE, ACT_GESTURE_COMBINE_AR2_ALTFIRE }, + + { ACT_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_ADVANCE }, + { ACT_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_FORWARD }, + { ACT_SIGNAL_GROUP, ACT_GESTURE_SIGNAL_GROUP }, + { ACT_SIGNAL_HALT, ACT_GESTURE_SIGNAL_HALT }, + { ACT_SIGNAL_LEFT, ACT_GESTURE_SIGNAL_LEFT }, + { ACT_SIGNAL_RIGHT, ACT_GESTURE_SIGNAL_RIGHT }, + { ACT_SIGNAL_TAKECOVER, ACT_GESTURE_SIGNAL_TAKECOVER }, +#endif + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK_REVOLVER, ACT_GESTURE_RANGE_ATTACK_REVOLVER }, + { ACT_RANGE_ATTACK_REVOLVER_LOW, ACT_GESTURE_RANGE_ATTACK_REVOLVER }, + { ACT_RANGE_ATTACK_CROSSBOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW }, + { ACT_RANGE_ATTACK_CROSSBOW_LOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW }, + { ACT_RANGE_ATTACK_RPG, ACT_GESTURE_RANGE_ATTACK_RPG }, + { ACT_RANGE_ATTACK_RPG_LOW, ACT_GESTURE_RANGE_ATTACK_RPG }, + { ACT_RANGE_ATTACK_ANNABELLE, ACT_GESTURE_RANGE_ATTACK_ANNABELLE }, + { ACT_RANGE_ATTACK_ANNABELLE_LOW, ACT_GESTURE_RANGE_ATTACK_ANNABELLE }, + + { ACT_RELOAD_REVOLVER, ACT_GESTURE_RELOAD_REVOLVER }, + { ACT_RELOAD_REVOLVER_LOW, ACT_GESTURE_RELOAD_REVOLVER }, + { ACT_RELOAD_CROSSBOW, ACT_GESTURE_RELOAD_CROSSBOW }, + { ACT_RELOAD_CROSSBOW_LOW, ACT_GESTURE_RELOAD_CROSSBOW }, + { ACT_RELOAD_ANNABELLE, ACT_GESTURE_RELOAD_ANNABELLE }, + { ACT_RELOAD_ANNABELLE_LOW, ACT_GESTURE_RELOAD_ANNABELLE }, +#endif + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK_AR1, ACT_GESTURE_RANGE_ATTACK_AR1 }, + { ACT_RANGE_ATTACK_AR1_LOW, ACT_GESTURE_RANGE_ATTACK_AR1 }, + { ACT_RANGE_ATTACK_AR3, ACT_GESTURE_RANGE_ATTACK_AR3 }, + { ACT_RANGE_ATTACK_AR3_LOW, ACT_GESTURE_RANGE_ATTACK_AR3 }, + { ACT_RANGE_ATTACK_AR2_GRENADE, ACT_GESTURE_RANGE_ATTACK_AR2_GRENADE }, + { ACT_RANGE_ATTACK_HMG1, ACT_GESTURE_RANGE_ATTACK_HMG1 }, + { ACT_RANGE_ATTACK_HMG1_LOW, ACT_GESTURE_RANGE_ATTACK_HMG1 }, + { ACT_RANGE_ATTACK_ML, ACT_GESTURE_RANGE_ATTACK_ML }, + { ACT_RANGE_ATTACK_SMG2, ACT_GESTURE_RANGE_ATTACK_SMG2 }, + { ACT_RANGE_ATTACK_SMG2_LOW, ACT_GESTURE_RANGE_ATTACK_SMG2 }, + { ACT_RANGE_ATTACK_SMG3, ACT_GESTURE_RANGE_ATTACK_SMG3 }, + { ACT_RANGE_ATTACK_SMG3_LOW, ACT_GESTURE_RANGE_ATTACK_SMG3 }, + { ACT_RANGE_ATTACK_SLAM, ACT_GESTURE_RANGE_ATTACK_SLAM }, + { ACT_RANGE_ATTACK_TRIPWIRE, ACT_GESTURE_RANGE_ATTACK_TRIPWIRE }, + { ACT_RANGE_ATTACK_THROW, ACT_GESTURE_RANGE_ATTACK_THROW }, + { ACT_RANGE_ATTACK_SNIPER_RIFLE, ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE }, + { ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW, ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE }, + + { ACT_RELOAD_AR1, ACT_GESTURE_RELOAD_AR1 }, + { ACT_RELOAD_AR1_LOW, ACT_GESTURE_RELOAD_AR1 }, + { ACT_RELOAD_AR3, ACT_GESTURE_RELOAD_AR3 }, + { ACT_RELOAD_AR3_LOW, ACT_GESTURE_RELOAD_AR3 }, + { ACT_RELOAD_SMG2, ACT_GESTURE_RELOAD_SMG2 }, + { ACT_RELOAD_SMG2_LOW, ACT_GESTURE_RELOAD_SMG2 }, + { ACT_RELOAD_SMG3, ACT_GESTURE_RELOAD_SMG3 }, + { ACT_RELOAD_SMG3_LOW, ACT_GESTURE_RELOAD_SMG3 }, + { ACT_RELOAD_HMG1, ACT_GESTURE_RELOAD_HMG1 }, + { ACT_RELOAD_HMG1_LOW, ACT_GESTURE_RELOAD_HMG1 }, + { ACT_RELOAD_SNIPER_RIFLE, ACT_GESTURE_RELOAD_SNIPER_RIFLE }, + { ACT_RELOAD_SNIPER_RIFLE_LOW, ACT_GESTURE_RELOAD_SNIPER_RIFLE }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_ATTACK1_MED, ACT_GESTURE_RANGE_ATTACK1 }, + { ACT_RANGE_ATTACK2_MED, ACT_GESTURE_RANGE_ATTACK2 }, + + { ACT_RANGE_ATTACK_AR2_MED, ACT_GESTURE_RANGE_ATTACK_AR2 }, + { ACT_RANGE_ATTACK_SMG1_MED, ACT_GESTURE_RANGE_ATTACK_SMG1 }, + { ACT_RANGE_ATTACK_SHOTGUN_MED, ACT_GESTURE_RANGE_ATTACK_SHOTGUN }, + { ACT_RANGE_ATTACK_PISTOL_MED, ACT_GESTURE_RANGE_ATTACK_PISTOL }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK_RPG_MED, ACT_GESTURE_RANGE_ATTACK_RPG }, + { ACT_RANGE_ATTACK_REVOLVER_MED, ACT_GESTURE_RANGE_ATTACK_REVOLVER }, + { ACT_RANGE_ATTACK_CROSSBOW_MED, ACT_GESTURE_RANGE_ATTACK_CROSSBOW }, +#endif +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK_AR1_MED, ACT_GESTURE_RANGE_ATTACK_AR1 }, + { ACT_RANGE_ATTACK_AR3_MED, ACT_GESTURE_RANGE_ATTACK_AR3 }, + { ACT_RANGE_ATTACK_SMG2_MED, ACT_GESTURE_RANGE_ATTACK_SMG2 }, + { ACT_RANGE_ATTACK_SMG3_MED, ACT_GESTURE_RANGE_ATTACK_SMG3 }, + { ACT_RANGE_ATTACK_HMG1_MED, ACT_GESTURE_RANGE_ATTACK_HMG1 }, + { ACT_RANGE_ATTACK_SNIPER_RIFLE_MED, ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE }, +#endif +#endif +}; + +Activity CAI_BaseNPC::GetGestureVersionOfActivity( Activity inActivity ) +{ + actlink_t *pTable = gm_ActivityGestureLinks; + int actCount = ARRAYSIZE( gm_ActivityGestureLinks ); + + for ( int i = 0; i < actCount; i++, pTable++ ) + { + if ( inActivity == pTable->sequence ) + { + return pTable->gesture; + } + } + + return ACT_INVALID; +} + +Activity CAI_BaseNPC::GetSequenceVersionOfGesture( Activity inActivity ) +{ + actlink_t *pTable = gm_ActivityGestureLinks; + int actCount = ARRAYSIZE( gm_ActivityGestureLinks ); + + for (int i = 0; i < actCount; i++, pTable++) + { + if (inActivity == pTable->gesture) + { + return pTable->sequence; + } + } + + return ACT_INVALID; +} +#endif diff --git a/mp/src/game/server/ai_baseactor.cpp b/mp/src/game/server/ai_baseactor.cpp index 2ddc60f7..df0a5aaa 100644 --- a/mp/src/game/server/ai_baseactor.cpp +++ b/mp/src/game/server/ai_baseactor.cpp @@ -101,8 +101,8 @@ 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." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTarget, "AddLookTarget", "Add a potential look target for this actor with the specified importance, duration, and ramp." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTargetPos, "AddLookTargetPos", "Add a potential look target position for this actor with the specified importance, duration, and ramp." ) END_SCRIPTDESC(); #endif @@ -835,7 +835,11 @@ void CAI_BaseActor::UpdateLatchedValues( ) // set head latch m_fLatchedPositions |= HUMANOID_LATCHED_HEAD; +#ifdef MAPBASE // From Alien Swarm SDK + if ( CanSkipAnimation() || !GetAttachment( "eyes", m_latchedEyeOrigin, &m_latchedHeadDirection )) +#else if (!HasCondition( COND_IN_PVS ) || !GetAttachment( "eyes", m_latchedEyeOrigin, &m_latchedHeadDirection )) +#endif { m_latchedEyeOrigin = BaseClass::EyePosition( ); AngleVectors( GetLocalAngles(), &m_latchedHeadDirection ); @@ -1138,6 +1142,24 @@ void CAI_BaseActor::UpdateHeadControl( const Vector &vHeadTarget, float flHeadIn ConcatTransforms( worldToForward, targetXform, headXform ); MatrixAngles( headXform, vTargetAngles ); +#ifdef MAPBASE + // This is here to cover an edge case where pose parameters set to NaN invalidate the model. + if (!vTargetAngles.IsValid()) + { + Warning( "================================================================================\n" + "!!!!! %s tried to set a NaN head angle (can happen when look targets have >1 importance) !!!!!\n" + "================================================================================\n", GetDebugName() ); + m_goalHeadCorrection.Init(); + Set( m_FlexweightHeadRightLeft, 0.0f ); + Set( m_FlexweightHeadUpDown, 0.0f ); + Set( m_FlexweightHeadTilt, 0.0f ); + Set( m_ParameterHeadYaw, 0.0f ); + Set( m_ParameterHeadPitch, 0.0f ); + Set( m_ParameterHeadRoll, 0.0f ); + return; + } +#endif + // partially debounce head goal float s0 = 1.0 - flHeadInfluence + GetHeadDebounce() * flHeadInfluence; float s1 = (1.0 - s0); @@ -1626,7 +1648,11 @@ void CAI_BaseActor::MaintainLookTargets( float flInterval ) } // don't bother with any of the rest if the player can't see you +#ifdef MAPBASE // From Alien Swarm SDK + if ( CanSkipAnimation() ) +#else if (!HasCondition( COND_IN_PVS )) +#endif { return; } @@ -2033,7 +2059,11 @@ bool CAI_BaseActor::UseSemaphore( void ) CAI_Expresser *CAI_BaseActor::CreateExpresser() { +#ifdef NEW_RESPONSE_SYSTEM + m_pExpresser = new CAI_ExpresserWithFollowup(this); +#else m_pExpresser = new CAI_Expresser(this); +#endif return m_pExpresser; } diff --git a/mp/src/game/server/ai_basenpc.cpp b/mp/src/game/server/ai_basenpc.cpp index 1e831080..fdef2b40 100644 --- a/mp/src/game/server/ai_basenpc.cpp +++ b/mp/src/game/server/ai_basenpc.cpp @@ -97,6 +97,12 @@ #ifdef MAPBASE #include "mapbase/matchers.h" +#include "items.h" +#include "point_camera.h" +#endif + +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_funcs_shared.h" #endif #include "env_debughistory.h" @@ -164,6 +170,8 @@ extern ConVar ai_vehicle_avoidance; extern ISoundEmitterSystemBase *soundemitterbase; ConVar ai_dynint_always_enabled( "ai_dynint_always_enabled", "0", FCVAR_NONE, "Makes the \"Don't Care\" setting equivalent to \"Yes\"." ); + +ConVar ai_debug_fake_sequence_gestures_always_play( "ai_debug_fake_sequence_gestures_always_play", "0", FCVAR_NONE, "Always plays fake sequence gestures." ); #endif #ifndef _RETAIL @@ -303,10 +311,13 @@ 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; +ScriptHook_t CAI_BaseNPC::g_Hook_QueryHearSound; +ScriptHook_t CAI_BaseNPC::g_Hook_QuerySeeEntity; +ScriptHook_t CAI_BaseNPC::g_Hook_TranslateActivity; +ScriptHook_t CAI_BaseNPC::g_Hook_TranslateSchedule; +ScriptHook_t CAI_BaseNPC::g_Hook_GetActualShootPosition; +ScriptHook_t CAI_BaseNPC::g_Hook_OverrideMove; +ScriptHook_t CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture; #endif // @@ -662,7 +673,7 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo #ifdef HL2_EPISODIC CBasePlayer *pPlayer = AI_GetSinglePlayer(); - if ( pPlayer->IRelationType( this ) != D_LI ) + if ( pPlayer && pPlayer->IRelationType( this ) != D_LI ) { CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx(); @@ -727,7 +738,7 @@ HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT pEnemy ) AI_EnemyInfo_t *info = GetEnemies()->Find( ToEnt(pEnemy) ); if (info) { - hScript = g_pScriptVM->RegisterInstance( info ); + hScript = g_pScriptVM->RegisterInstance( reinterpret_cast(info) ); } return hScript; @@ -987,29 +998,27 @@ int CAI_BaseNPC::OnTakeDamage_Alive( const CTakeDamageInfo &info ) // only fire once per frame m_OnDamaged.FireOutput( info.GetAttacker(), this); - if( info.GetAttacker()->IsPlayer() ) + if ( info.GetAttacker() ) { - m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this ); - - // This also counts as being harmed by player's squad. - m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); - } - 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 ) + if( info.GetAttacker()->IsPlayer() ) { - if( pAttacker->GetSquad() != NULL && pAttacker->IsInPlayerSquad() ) + m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this ); + + // This also counts as being harmed by player's squad. + m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); + } + else + { + // See if the person that injured me is an NPC. + CAI_BaseNPC *pAttacker = info.GetAttacker()->MyNPCPointer(); + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + + if( pAttacker && pAttacker->IsAlive() && pPlayer ) { - m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); + if( pAttacker->GetSquad() != NULL && pAttacker->IsInPlayerSquad() ) + { + m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); + } } } } @@ -4293,6 +4302,12 @@ bool CAI_BaseNPC::CheckPVSCondition() { bool bInPVS = ( UTIL_FindClientInPVS( edict() ) != NULL ) || (UTIL_ClientPVSIsExpanded() && UTIL_FindClientInVisibilityPVS( edict() )); +#ifdef MAPBASE + // We can be in a player's PVS if there is an active point_camera nearby (fixes issues with choreo) + if (!bInPVS && UTIL_FindRTCameraInEntityPVS( edict() )) + bInPVS = true; +#endif + if ( bInPVS ) SetCondition( COND_IN_PVS ); else @@ -6505,7 +6520,7 @@ CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float dur //----------------------------------------------------------------------------- Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity ) { - if (CapabilitiesGet() & bits_CAP_DUCK) + if (CapabilitiesGet() & bits_CAP_DUCK && CanTranslateCrouchActivity()) { // ======================================================================== // The main issue with cover hint nodes is that crouch activities are not translated at the right time @@ -6537,10 +6552,18 @@ Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity ) CAI_Hint *pHint = GetHintNode(); if (pHint) { - if (pHint->HintType() == HINT_TACTICAL_COVER_LOW || pHint->HintType() == HINT_TACTICAL_COVER_MED) + if (pHint->HintType() == HINT_TACTICAL_COVER_LOW) { nCoverActivity = ACT_RANGE_ATTACK1_LOW; } + else if (pHint->HintType() == HINT_TACTICAL_COVER_MED) + { +#if EXPANDED_HL2_COVER_ACTIVITIES + nCoverActivity = ACT_RANGE_ATTACK1_MED; +#else + nCoverActivity = ACT_RANGE_ATTACK1_LOW; +#endif + } } } } @@ -6582,15 +6605,36 @@ 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 one climbing animation isn't available, use the other + if (eNewActivity == ACT_CLIMB_DOWN) + return ACT_CLIMB_UP; + else if (eNewActivity == ACT_CLIMB_UP) + return ACT_CLIMB_DOWN; + + // --------------------------------------------- + + switch (eNewActivity) + { + case ACT_COVER_MED: eNewActivity = ACT_COVER_LOW; break; +#if EXPANDED_HL2_COVER_ACTIVITIES + case ACT_RANGE_AIM_MED: eNewActivity = ACT_RANGE_AIM_LOW; break; + case ACT_RANGE_ATTACK1_MED: eNewActivity = ACT_RANGE_ATTACK1_LOW; break; +#endif + } + //if (eNewActivity == ACT_COVER) // return TranslateActivity(ACT_IDLE); @@ -6605,6 +6649,14 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity ) //----------------------------------------------------------------------------- Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) { +#if EXPANDED_NAVIGATION_ACTIVITIES + if ( GetNavType() == NAV_CLIMB && eNewActivity == ACT_IDLE ) + { + // Schedules which break into idle activities should try to maintain the climbing animation. + return ACT_CLIMB_IDLE; + } +#endif + #ifdef MAPBASE Assert( eNewActivity != ACT_INVALID ); @@ -6614,15 +6666,7 @@ Activity CAI_BaseNPC::NPC_TranslateActivity( Activity 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; + case ACT_IDLE: eNewActivity = ACT_RANGE_AIM_LOW; break; // ACT_CROUCHIDLE is more-or-less deprecated and not friendly to weapon translation } } @@ -6786,7 +6830,11 @@ Activity CAI_BaseNPC::TranslateActivity( Activity idealActivity, Activity *pIdea return baseTranslation; if ( idealWeaponActivity != baseTranslation && HaveSequenceForActivity( idealWeaponActivity ) ) +#ifdef MAPBASE + return idealWeaponActivity; +#else return idealActivity; +#endif if ( idealActivity != idealWeaponActivity && HaveSequenceForActivity( idealActivity ) ) return idealActivity; @@ -6835,7 +6883,12 @@ void CAI_BaseNPC::ResolveActivityToSequence(Activity NewActivity, int &iSequence translatedActivity = TranslateActivity( NewActivity, &weaponActivity ); +#ifdef MAPBASE + // Cover cases where TranslateActivity() returns a sequence by using ACT_SCRIPT_CUSTOM_MOVE + if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE || translatedActivity == ACT_SCRIPT_CUSTOM_MOVE ) +#else if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE ) +#endif { iSequence = GetScriptCustomMoveSequence(); } @@ -7021,6 +7074,23 @@ void CAI_BaseNPC::SetIdealActivity( Activity NewActivity ) // Perform translation in case we need to change sequences within a single activity, // such as between a standing idle and a crouching idle. ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); + +#ifdef MAPBASE + // Check if we need a gesture to imitate this sequence + if ( ShouldPlayFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity ) ) + { + Activity nGesture = SelectFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity ); + if (nGesture != -1) + { + PlayFakeSequenceGesture( nGesture, m_IdealActivity, m_IdealTranslatedActivity ); + } + } + else if (GetFakeSequenceGesture() != -1) + { + // Reset the current gesture sequence if there is one + ResetFakeSequenceGesture(); + } +#endif } @@ -7069,6 +7139,14 @@ void CAI_BaseNPC::AdvanceToIdealActivity(void) //DevMsg("%s: Unable to get from sequence %s to %s!\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); SetActivity(m_IdealActivity); } + +#ifdef MAPBASE + // If there was a gesture imitating a sequence, get rid of it + if ( GetFakeSequenceGesture() != -1 ) + { + ResetFakeSequenceGesture(); + } +#endif } @@ -7126,6 +7204,12 @@ void CAI_BaseNPC::MaintainActivity(void) } // Else a transition sequence is in progress, do nothing. } +#ifdef MAPBASE + else if (GetFakeSequenceGesture() != -1) + { + // Don't advance even if the sequence gesture is finished, as advancing would just play the original activity afterwards + } +#endif // Else get a specific sequence for the activity and try to transition to that. else { @@ -7144,11 +7228,103 @@ void CAI_BaseNPC::MaintainActivity(void) } +#ifdef MAPBASE +bool CAI_BaseNPC::ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ) +{ + // Don't do anything if we're resetting our activity + if (GetActivity() == ACT_RESET) + return false; + + // No need to do this while we're moving or for sequences which will make us move + if (IsMoving()) + return false; + + if (ai_debug_fake_sequence_gestures_always_play.GetBool()) + return true; + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_ShouldPlayFakeSequenceGesture.CanRunInScope(m_ScriptScope)) + { + // activity, translatedActivity + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { GetActivityName( nActivity ), GetActivityName( nTranslatedActivity ) }; + if (g_Hook_ShouldPlayFakeSequenceGesture.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_BOOLEAN) + return functionReturn.m_bool; + } + } +#endif + + if (GetHintNode() && GetHintNode()->HintActivityName() != NULL_STRING) + { + switch (GetHintNode()->HintType()) + { + // Cover nodes with custom activities should allow NPCs to do things like reload while in cover. + case HINT_TACTICAL_COVER_LOW: + case HINT_TACTICAL_COVER_MED: + case HINT_TACTICAL_COVER_CUSTOM: + if (HasMemory( bits_MEMORY_INCOVER )) + { + // Don't attack while using a custom animation in cover + if (nActivity != ACT_RANGE_ATTACK1 && nActivity != ACT_RANGE_ATTACK1_LOW) + return true; + } + break; + } + } + + return false; +} + +Activity CAI_BaseNPC::SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ) +{ + return GetGestureVersionOfActivity( nTranslatedActivity ); +} + +inline void CAI_BaseNPC::PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence ) +{ + RestartGesture( nActivity, true, true ); + m_FakeSequenceGestureLayer = FindGestureLayer( nActivity ); + + switch ( nSequence ) + { + case ACT_RANGE_ATTACK1: + //case ACT_RANGE_ATTACK2: + { + OnRangeAttack1(); + + // FIXME: this seems a bit wacked + Weapon_SetActivity( Weapon_TranslateActivity( nSequence ), 0 ); + } break; + } +} + +inline int CAI_BaseNPC::GetFakeSequenceGesture() +{ + return m_FakeSequenceGestureLayer; +} + +void CAI_BaseNPC::ResetFakeSequenceGesture() +{ + SetLayerCycle( m_FakeSequenceGestureLayer, 1.0f ); + m_FakeSequenceGestureLayer = -1; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Returns true if our ideal activity has finished playing. //----------------------------------------------------------------------------- bool CAI_BaseNPC::IsActivityFinished( void ) { +#ifdef MAPBASE + if (GetFakeSequenceGesture() != -1) + { + return IsLayerFinished( GetFakeSequenceGesture() ); + } +#endif + return (IsSequenceFinished() && (GetSequence() == m_nIdealSequence)); } @@ -7184,6 +7360,15 @@ void CAI_BaseNPC::OnChangeActivity( Activity eNewActivity ) eNewActivity == ACT_WALK ) { Stand(); + +#ifdef MAPBASE + // Unlock custom cover nodes + if (GetHintNode() && GetHintNode()->HintType() == HINT_TACTICAL_COVER_CUSTOM && HasMemory(bits_MEMORY_INCOVER)) + { + GetHintNode()->Unlock( GetHintDelay( GetHintNode()->HintType() ) ); + SetHintNode( NULL ); + } +#endif } } @@ -7460,7 +7645,11 @@ void CAI_BaseNPC::SetHullSizeNormal( bool force ) if ( m_fIsUsingSmallHull || force ) { // Find out what the height difference will be between the versions and adjust our bbox accordingly to keep us level +#ifdef MAPBASE // From Alien Swarm SDK + const float flScale = MIN( 1.0f, GetModelScale() ); // NOTE: Cannot scale NPC bounding box up, as pathfinding will fail (hull needs to match the traces used for the node network) +#else const float flScale = GetModelScale(); +#endif Vector vecMins = ( GetHullMins() * flScale ); Vector vecMaxs = ( GetHullMaxs() * flScale ); @@ -7768,7 +7957,7 @@ int CAI_BaseNPC::HolsterWeapon( void ) if ( IsWeaponHolstered() ) return -1; -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef MAPBASE Activity activity = TranslateActivity( ACT_DISARM ); int iHolsterGesture = FindGestureLayer( activity ); if ( iHolsterGesture != -1 ) @@ -7824,7 +8013,7 @@ int CAI_BaseNPC::UnholsterWeapon( void ) if ( !IsWeaponHolstered() ) return -1; -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef MAPBASE Activity activity = TranslateActivity( ACT_ARM ); int iHolsterGesture = FindGestureLayer( activity ); #else @@ -7853,13 +8042,12 @@ int CAI_BaseNPC::UnholsterWeapon( void ) { SetActiveWeapon( GetWeapon(i) ); -#ifdef COMPANION_HOLSTER_WORKAROUND - int iLayer = AddGesture( activity, true ); - //iLayer = AddGesture( ACT_GESTURE_ARM, true ); +#ifdef MAPBASE + int iLayer = AddGesture( TranslateActivity( ACT_ARM ), true ); #else int iLayer = AddGesture( ACT_ARM, true ); - //iLayer = AddGesture( ACT_GESTURE_ARM, true ); #endif + //iLayer = AddGesture( ACT_GESTURE_ARM, true ); if (iLayer != -1) { @@ -8038,6 +8226,26 @@ bool CAI_BaseNPC::DoUnholster( void ) return false; } +//----------------------------------------------------------------------------- +// Purpose: Returns true if the NPC should be unholstering their weapon +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ShouldUnholsterWeapon( void ) +{ + return GetState() == NPC_STATE_COMBAT; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the NPC can unholster their weapon +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CanUnholsterWeapon( void ) +{ + // Don't unholster during special navigation + if ( GetNavType() == NAV_JUMP || GetNavType() == NAV_CLIMB ) + return false; + + return IsWeaponHolstered(); +} + //------------------------------------------------------------------------------ // Purpose: Give the NPC in question the weapon specified //------------------------------------------------------------------------------ @@ -9114,27 +9322,45 @@ 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 + // NPCs which lack ACT_COVER_MED should fall through to HINT_TACTICAL_COVER_LOW + if (SelectWeightedSequence( ACT_COVER_MED ) != ACTIVITY_NOT_AVAILABLE) + { + nCoverActivity = ACT_COVER_MED; + } +#else + nCoverActivity = ACT_COVER_MED; break; - } #endif + } case HINT_TACTICAL_COVER_LOW: { #ifdef MAPBASE - if (pHint->HintActivityName() != NULL_STRING) - nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) ); - else -#endif + // Make sure nCoverActivity wasn't already assigned above, then fall through to HINT_TACTICAL_COVER_CUSTOM + if (nCoverActivity == ACT_INVALID) + nCoverActivity = ACT_COVER_LOW; +#else nCoverActivity = ACT_COVER_LOW; break; +#endif } + +#ifdef MAPBASE + case HINT_TACTICAL_COVER_CUSTOM: + { + if (pHint->HintActivityName() != NULL_STRING) + { + nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) ); + if (nCoverActivity == ACT_INVALID) + { + m_iszSceneCustomMoveSeq = pHint->HintActivityName(); + nCoverActivity = ACT_SCRIPT_CUSTOM_MOVE; + } + } + break; + } +#endif } } @@ -9174,6 +9400,13 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget ) return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); } +#ifdef MAPBASE + // Allow hint nodes to override the yaw without needing to control AI + else if (GetHintNode() && GetHintNode()->OverridesNPCYaw( this )) + { + return GetHintNode()->Yaw(); + } +#endif else { return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() ); @@ -9514,7 +9747,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) if ( GetActiveWeapon() ) { #ifdef MAPBASE - GetActiveWeapon()->Reload_NPC(); + GetActiveWeapon()->Reload_NPC( true ); #else GetActiveWeapon()->WeaponSound( RELOAD_NPC ); GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); @@ -9538,8 +9771,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) case EVENT_WEAPON_RELOAD_FILL_CLIP: { if ( GetActiveWeapon() ) - { + { +#ifdef MAPBASE + GetActiveWeapon()->Reload_NPC( false ); +#else GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); +#endif ClearCondition(COND_LOW_PRIMARY_AMMO); ClearCondition(COND_NO_PRIMARY_AMMO); ClearCondition(COND_NO_SECONDARY_AMMO); @@ -9554,7 +9791,11 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) case NPC_EVENT_OPEN_DOOR: { +#ifdef MAPBASE + CBasePropDoor *pDoor = m_hOpeningDoor; +#else CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetNavigator()->GetPath()->GetCurWaypoint()->GetEHandleData(); +#endif if (pDoor != NULL) { OpenPropDoorNow( pDoor ); @@ -10699,6 +10940,20 @@ void CAI_BaseNPC::CollectShotStats( const Vector &vecShootOrigin, const Vector & //----------------------------------------------------------------------------- Vector CAI_BaseNPC::GetActualShootPosition( const Vector &shootOrigin ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope)) + { + // shootOrigin, target + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { shootOrigin, ToHScript( GetEnemy() ) }; + if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f) + return *functionReturn.m_pVector; + } + } +#endif + // Project the target's location into the future. Vector vecEnemyLKP = GetEnemyLKP(); Vector vecEnemyOffset = GetEnemy()->BodyTarget( shootOrigin ) - GetEnemy()->GetAbsOrigin(); @@ -11488,6 +11743,14 @@ bool CAI_BaseNPC::ChooseEnemy( void ) //========================================================= void CAI_BaseNPC::PickupWeapon( CBaseCombatWeapon *pWeapon ) { +#ifdef MAPBASE + if ( pWeapon->VPhysicsGetObject() && pWeapon->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) + { + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + pPlayer->ForceDropOfCarriedPhysObjects( pWeapon ); + } +#endif + pWeapon->OnPickedUp( this ); Weapon_Equip( pWeapon ); m_iszPendingWeapon = NULL_STRING; @@ -11507,17 +11770,9 @@ void CAI_BaseNPC::PickupItem( CBaseEntity *pItem ) m_OnItemPickup.Set( pItem, pItem, this ); Assert( pItem != NULL ); - if( FClassnameIs( pItem, "item_healthkit" ) ) + if( FClassnameIs( pItem, "item_health*" ) ) // item_healthkit, item_healthvial, item_healthkit_custom, etc. { - if ( TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) ) - { - RemoveAllDecals(); - UTIL_Remove( pItem ); - } - } - else if( FClassnameIs( pItem, "item_healthvial" ) ) - { - if ( TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) ) + if ( TakeHealth( static_cast(pItem)->GetItemAmount(), DMG_GENERIC ) ) { RemoveAllDecals(); UTIL_Remove( pItem ); @@ -11577,6 +11832,10 @@ CBaseEntity *CAI_BaseNPC::DropItem ( const char *pszItemName, Vector vecPos, QAn pItem->ApplyLocalAngularVelocityImpulse( AngularImpulse( 0, random->RandomFloat( 0, 100 ), 0 ) ); } +#ifdef MAPBASE + m_OnItemDrop.Set( pItem, pItem, this ); +#endif + return pItem; } else @@ -11899,6 +12158,7 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_KEYFIELD( m_FriendlyFireOverride, FIELD_INTEGER, "FriendlyFireOverride" ), DEFINE_KEYFIELD( m_flSpeedModifier, FIELD_FLOAT, "BaseSpeedModifier" ), + DEFINE_FIELD( m_FakeSequenceGestureLayer, FIELD_INTEGER ), #endif // Satisfy classcheck @@ -11944,6 +12204,7 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_OUTPUT( m_OnForcedInteractionFinished, "OnForcedInteractionFinished" ), #ifdef MAPBASE DEFINE_OUTPUT( m_OnItemPickup, "OnItemPickup" ), + DEFINE_OUTPUT( m_OnItemDrop, "OnItemDrop" ), #endif // Inputs @@ -12055,6 +12316,14 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivityID, "SetActivityID", "Set the NPC's current activity ID." ) DEFINE_SCRIPTFUNC( ResetActivity, "Reset the NPC's current activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptTranslateActivity, "TranslateActivity", "Translates the specified activity string and returns the translated activity ID." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptTranslateActivityID, "TranslateActivityID", "Translates the specified activity ID and returns the translated activity ID." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivity, "GetGestureVersionOfActivity", "Get the gesture activity counterpart of the specified sequence activity, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivityID, "GetGestureVersionOfActivityID", "Get the gesture activity ID counterpart of the specified sequence activity ID, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGesture, "GetSequenceVersionOfGesture", "Get the sequence activity counterpart of the specified gesture activity, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGestureID, "GetSequenceVersionOfGestureID", "Get the sequence activity ID counterpart of the specified gesture activity ID, if one exists." ) + 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." ) @@ -12083,24 +12352,39 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC 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." ) + + DEFINE_SCRIPTFUNC( IsCrouching, "Returns true if the NPC is crouching." ) + DEFINE_SCRIPTFUNC( Crouch, "Tells the NPC to crouch." ) + DEFINE_SCRIPTFUNC( Stand, "Tells the NPC to stand if it is crouching." ) // // Hooks // - BEGIN_SCRIPTHOOK( g_Hook_QueryHearSound, "QueryHearSound", FIELD_BOOLEAN, "Called when the NPC is deciding whether to hear a CSound or not." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::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." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::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." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::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." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::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() + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VECTOR, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" ) + DEFINE_SCRIPTHOOK_PARAM( "shootOrigin", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "target", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_OverrideMove, "OverrideMove", FIELD_VOID, "Called when the NPC runs movement code, allowing the NPC's movement to be overridden by some other method. (NOTE: NPCs which override this themselves might not always use this hook!)" ) + DEFINE_SCRIPTHOOK_PARAM( "interval", FIELD_FLOAT ) + END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture, "ShouldPlayFakeSequenceGesture", FIELD_BOOLEAN, "Called when an activity is set on a NPC. Returning true will make the NPC convert the activity into a gesture (if a gesture is available) and continue their current activity instead." ) + DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING ) + DEFINE_SCRIPTHOOK_PARAM( "translatedActivity", FIELD_CSTRING ) + END_SCRIPTHOOK() END_SCRIPTDESC(); #endif @@ -12751,6 +13035,8 @@ CAI_BaseNPC::CAI_BaseNPC(void) #ifdef MAPBASE m_iDynamicInteractionsAllowed = TRS_NONE; m_flSpeedModifier = 1.0f; + + m_FakeSequenceGestureLayer = -1; #endif } @@ -13407,6 +13693,10 @@ void CAI_BaseNPC::Teleport( const Vector *newPosition, const QAngle *newAngles, CleanupScriptsOnTeleport( false ); BaseClass::Teleport( newPosition, newAngles, newVelocity ); + +#ifdef MAPBASE // From Alien Swarm SDK + CheckPVSCondition(); +#endif } //----------------------------------------------------------------------------- @@ -13530,6 +13820,20 @@ bool CAI_BaseNPC::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInt bool CAI_BaseNPC::OverrideMove( float flInterval ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_OverrideMove.CanRunInScope(m_ScriptScope)) + { + // interval + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { flInterval }; + if (g_Hook_OverrideMove.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_BOOLEAN) + return functionReturn.m_bool; + } + } +#endif + return false; } @@ -13701,6 +14005,10 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, GetNavigator()->GetPath()->PrependWaypoints( pOpenDoorRoute ); +#ifdef MAPBASE + GetNavigator()->SetArrivalDirection( opendata.vecFaceDir ); +#endif + m_hOpeningDoor = pDoor; pMoveGoal->maxDist = distClear; *pResult = AIMR_CHANGE_TYPE; @@ -13721,6 +14029,21 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, //----------------------------------------------------------------------------- void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor ) { +#ifdef MAPBASE + opendata_t opendata; + pDoor->GetNPCOpenData(this, opendata); + + if (HaveSequenceForActivity( opendata.eActivity )) + { + int iLayer = AddGesture( opendata.eActivity ); + float flDuration = GetLayerDuration( iLayer ); + + // Face the door and wait for the activity to finish before trying to move through the doorway. + m_flMoveWaitFinished = gpGlobals->curtime + flDuration + pDoor->GetOpenInterval(); + AddFacingTarget( opendata.vecFaceDir, 1.0, flDuration ); + } + else +#else // dvs: not quite working, disabled for now. //opendata_t opendata; //pDoor->GetNPCOpenData(this, opendata); @@ -13730,6 +14053,7 @@ void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor ) // SetIdealActivity(opendata.eActivity); //} //else +#endif { // We don't have an appropriate sequence, just open the door magically. OpenPropDoorNow( pDoor ); @@ -13749,6 +14073,15 @@ void CAI_BaseNPC::OpenPropDoorNow( CBasePropDoor *pDoor ) // Wait for the door to finish opening before trying to move through the doorway. m_flMoveWaitFinished = gpGlobals->curtime + pDoor->GetOpenInterval(); + +#ifdef MAPBASE + // Remove the door from our waypoint + if (GetNavigator()->GetPath() && GetNavigator()->GetCurWaypointFlags() & bits_WP_TO_DOOR) + { + GetNavigator()->GetPath()->GetCurWaypoint()->ModifyFlags( bits_WP_TO_DOOR, false ); + GetNavigator()->GetPath()->GetCurWaypoint()->m_hData = NULL; + } +#endif } @@ -14405,326 +14738,356 @@ bool CAI_BaseNPC::IsAllowedToDodge( void ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CAI_BaseNPC::ParseScriptedNPCInteractions( void ) +void CAI_BaseNPC::ParseScriptedNPCInteractions(void) { // Already parsed them? - if ( m_ScriptedInteractions.Count() ) + if (m_ScriptedInteractions.Count()) return; // Parse the model's key values and find any dynamic interactions - KeyValues *modelKeyValues = new KeyValues(""); - CUtlBuffer buf( 1024, 0, CUtlBuffer::TEXT_BUFFER ); + KeyValues* modelKeyValues = new KeyValues(""); + CUtlBuffer buf(1024, 0, CUtlBuffer::TEXT_BUFFER); - if (! modelinfo->GetModelKeyValue( GetModel(), buf )) + if (!modelinfo->GetModelKeyValue(GetModel(), buf)) return; - - if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), buf ) ) + + if (modelKeyValues->LoadFromBuffer(modelinfo->GetModelName(GetModel()), buf)) { - // Do we have a dynamic interactions section? - KeyValues *pkvInteractions = modelKeyValues->FindKey("dynamic_interactions"); - if ( pkvInteractions ) - { - KeyValues *pkvNode = pkvInteractions->GetFirstSubKey(); - while ( pkvNode ) - { - 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) + CUtlVector iszUsedNames; + for (KeyValues* pkvModelBlock = modelKeyValues; pkvModelBlock != nullptr; pkvModelBlock = pkvModelBlock->GetNextKey()) + { + // Do we have a dynamic interactions section? + KeyValues* pkvInteractions = pkvModelBlock->FindKey("dynamic_interactions"); + if (pkvInteractions) + { + KeyValues* pkvNode = pkvInteractions->GetFirstSubKey(); + while (pkvNode) { - szName = pCurNode->GetName(); - szValue = pCurNode->GetString(); + ScriptedNPCInteraction_t sInteraction; + sInteraction.iszInteractionName = AllocPooledString(pkvNode->GetName()); - if (!szName || !szValue) + if (iszUsedNames.Find(sInteraction.iszInteractionName) != iszUsedNames.InvalidIndex()) { - DevWarning("ERROR: Invalid dynamic interaction string\n"); + DevMsg(2, "Scripted interaction %s already defined on %s\n", pkvNode->GetName(), GetClassname()); + pkvNode = pkvNode->GetNextKey(); + continue; + } + + // 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 + 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); + } + else if (!Q_strncmp(szName, "end_position", 12)) + { + sInteraction.iFlags |= SCNPC_FLAG_TEST_END_POSITION; + UTIL_StringToVector(sInteraction.vecRelativeEndPos.Base(), szValue); + } + + 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 = const_cast(UTIL_VarArgs("%s,%s:%s", szCriteria, szName, szValue)); + } + pCurNode = pCurNode->GetNextKey(); } - if (!Q_strncmp(szName, "classname", 9)) + if (!bValidInteraction) { - 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; + DevMsg("Scripted interaction %s rejected by %s\n", pkvNode->GetName(), GetClassname()); + pkvNode = pkvNode->GetNextKey(); + continue; } - else if (!Q_strncmp(szName, "needs_weapon", 12)) + if (szCriteria[0] == ',') { - 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; - } + szCriteria += 1; + sInteraction.MiscCriteria = AllocPooledString(szCriteria); } - 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); - } + // Add it to the list + AddScriptedNPCInteraction(&sInteraction); + iszUsedNames.AddToTail(sInteraction.iszInteractionName); - pCurNode = pCurNode->GetNextKey(); - } - - if (!bValidInteraction) - { - DevMsg("Scripted interaction %s rejected by %s\n", pkvNode->GetName(), GetClassname()); + // Move to next interaction pkvNode = pkvNode->GetNextKey(); - continue; - } - - if (szCriteria[0] == ',') - { - szCriteria += 1; - sInteraction.MiscCriteria = AllocPooledString(szCriteria); } + } + } #else +// Do we have a dynamic interactions section? + KeyValues* pkvInteractions = modelKeyValues->FindKey("dynamic_interactions"); + if (pkvInteractions) + { + KeyValues* pkvNode = pkvInteractions->GetFirstSubKey(); + while (pkvNode) + { + ScriptedNPCInteraction_t sInteraction; + sInteraction.iszInteractionName = AllocPooledString(pkvNode->GetName()); + + // Trigger method - const char *pszTrigger = pkvNode->GetString( "trigger", NULL ); - if ( pszTrigger ) + const char* pszTrigger = pkvNode->GetString("trigger", NULL); + if (pszTrigger) { - if ( !Q_strncmp( pszTrigger, "auto_in_combat", 14) ) + if (!Q_strncmp(pszTrigger, "auto_in_combat", 14)) { sInteraction.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; } } // Loop Break trigger method - pszTrigger = pkvNode->GetString( "loop_break_trigger", NULL ); - if ( pszTrigger ) + pszTrigger = pkvNode->GetString("loop_break_trigger", NULL); + if (pszTrigger) { char szTrigger[256]; - Q_strncpy( szTrigger, pszTrigger, sizeof(szTrigger) ); - char *pszParam = strtok( szTrigger, " " ); + Q_strncpy(szTrigger, pszTrigger, sizeof(szTrigger)); + char* pszParam = strtok(szTrigger, " "); while (pszParam) { - if ( !Q_strncmp( pszParam, "on_damage", 9) ) + if (!Q_strncmp(pszParam, "on_damage", 9)) { sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_DAMAGE; } - if ( !Q_strncmp( pszParam, "on_flashlight_illum", 19) ) + if (!Q_strncmp(pszParam, "on_flashlight_illum", 19)) { sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM; } - pszParam = strtok(NULL," "); + pszParam = strtok(NULL, " "); } } // Origin - const char *pszOrigin = pkvNode->GetString( "origin_relative", "0 0 0" ); - UTIL_StringToVector( sInteraction.vecRelativeOrigin.Base(), pszOrigin ); + const char* pszOrigin = pkvNode->GetString("origin_relative", "0 0 0"); + UTIL_StringToVector(sInteraction.vecRelativeOrigin.Base(), pszOrigin); // Angles - const char *pszAngles = pkvNode->GetString( "angles_relative", NULL ); - if ( pszAngles ) + const char* pszAngles = pkvNode->GetString("angles_relative", NULL); + if (pszAngles) { sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; - UTIL_StringToVector( sInteraction.angRelativeAngles.Base(), pszAngles ); + UTIL_StringToVector(sInteraction.angRelativeAngles.Base(), pszAngles); } // Velocity - const char *pszVelocity = pkvNode->GetString( "velocity_relative", NULL ); - if ( pszVelocity ) + const char* pszVelocity = pkvNode->GetString("velocity_relative", NULL); + if (pszVelocity) { sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_VELOCITY; - UTIL_StringToVector( sInteraction.vecRelativeVelocity.Base(), pszVelocity ); + UTIL_StringToVector(sInteraction.vecRelativeVelocity.Base(), pszVelocity); } // Entry Sequence - const char *pszSequence = pkvNode->GetString( "entry_sequence", NULL ); - if ( pszSequence ) + const char* pszSequence = pkvNode->GetString("entry_sequence", NULL); + if (pszSequence) { - sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString( pszSequence ); + sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(pszSequence); } // Entry Activity - const char *pszActivity = pkvNode->GetString( "entry_activity", NULL ); - if ( pszActivity ) + const char* pszActivity = pkvNode->GetString("entry_activity", NULL); + if (pszActivity) { - sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID( pszActivity ); + sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID(pszActivity); } // Sequence - pszSequence = pkvNode->GetString( "sequence", NULL ); - if ( pszSequence ) + pszSequence = pkvNode->GetString("sequence", NULL); + if (pszSequence) { - sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( pszSequence ); + sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(pszSequence); } // Activity - pszActivity = pkvNode->GetString( "activity", NULL ); - if ( pszActivity ) + pszActivity = pkvNode->GetString("activity", NULL); + if (pszActivity) { - sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID( pszActivity ); + sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID(pszActivity); } // Exit Sequence - pszSequence = pkvNode->GetString( "exit_sequence", NULL ); - if ( pszSequence ) + pszSequence = pkvNode->GetString("exit_sequence", NULL); + if (pszSequence) { - sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString( pszSequence ); + sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(pszSequence); } // Exit Activity - pszActivity = pkvNode->GetString( "exit_activity", NULL ); - if ( pszActivity ) + pszActivity = pkvNode->GetString("exit_activity", NULL); + if (pszActivity) { - sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID( pszActivity ); + sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID(pszActivity); } // Delay - sInteraction.flDelay = pkvNode->GetFloat( "delay", 10.0 ); + sInteraction.flDelay = pkvNode->GetFloat("delay", 10.0); // Delta - sInteraction.flDistSqr = pkvNode->GetFloat( "origin_max_delta", (DSS_MAX_DIST * DSS_MAX_DIST) ); + sInteraction.flDistSqr = pkvNode->GetFloat("origin_max_delta", (DSS_MAX_DIST * DSS_MAX_DIST)); // Loop? - if ( pkvNode->GetFloat( "loop_in_action", 0 ) ) + if (pkvNode->GetFloat("loop_in_action", 0)) { sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION; } // Fixup position? - const char *pszDontFixup = pkvNode->GetString( "dont_teleport_at_end", NULL ); - if ( pszDontFixup ) + const char* pszDontFixup = pkvNode->GetString("dont_teleport_at_end", NULL); + if (pszDontFixup) { - if ( !Q_stricmp( pszDontFixup, "me" ) || !Q_stricmp( pszDontFixup, "both" ) ) + if (!Q_stricmp(pszDontFixup, "me") || !Q_stricmp(pszDontFixup, "both")) { sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME; } - else if ( !Q_stricmp( pszDontFixup, "them" ) || !Q_stricmp( pszDontFixup, "both" ) ) + else if (!Q_stricmp(pszDontFixup, "them") || !Q_stricmp(pszDontFixup, "both")) { sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM; } } // Needs a weapon? - const char *pszNeedsWeapon = pkvNode->GetString( "needs_weapon", NULL ); - if ( pszNeedsWeapon ) + const char* pszNeedsWeapon = pkvNode->GetString("needs_weapon", NULL); + if (pszNeedsWeapon) { - if ( !Q_strncmp( pszNeedsWeapon, "ME", 2 ) ) + if (!Q_strncmp(pszNeedsWeapon, "ME", 2)) { sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; } - else if ( !Q_strncmp( pszNeedsWeapon, "THEM", 4 ) ) + else if (!Q_strncmp(pszNeedsWeapon, "THEM", 4)) { sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; } - else if ( !Q_strncmp( pszNeedsWeapon, "BOTH", 4 ) ) + else if (!Q_strncmp(pszNeedsWeapon, "BOTH", 4)) { sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; @@ -14732,27 +15095,28 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions( void ) } // Specific weapon types - const char *pszWeaponName = pkvNode->GetString( "weapon_mine", NULL ); - if ( pszWeaponName ) + const char* pszWeaponName = pkvNode->GetString("weapon_mine", NULL); + if (pszWeaponName) { sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; - sInteraction.iszMyWeapon = AllocPooledString( pszWeaponName ); + sInteraction.iszMyWeapon = AllocPooledString(pszWeaponName); } - pszWeaponName = pkvNode->GetString( "weapon_theirs", NULL ); - if ( pszWeaponName ) + pszWeaponName = pkvNode->GetString("weapon_theirs", NULL); + if (pszWeaponName) { sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; - sInteraction.iszTheirWeapon = AllocPooledString( pszWeaponName ); + sInteraction.iszTheirWeapon = AllocPooledString(pszWeaponName); } -#endif // Add it to the list - AddScriptedNPCInteraction( &sInteraction ); + AddScriptedNPCInteraction(&sInteraction); // Move to next interaction pkvNode = pkvNode->GetNextKey(); } } +#endif // MAPBASE + } modelKeyValues->deleteThis(); @@ -15245,7 +15609,11 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) const char *p = STRING(pInteraction->MiscCriteria); while ( p ) { +#ifdef NEW_RESPONSE_SYSTEM + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING(pInteraction->MiscCriteria) ); +#else p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL ); +#endif index = set.FindCriterionIndex(key); if (index != -1) @@ -15934,6 +16302,26 @@ bool CAI_BaseNPC::CouldShootIfCrouching( CBaseEntity *pTarget ) return bResult; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Check if this position will block our line of sight if aiming low. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist ) +{ + Vector vGunPos = vecPosition; + vGunPos += (GetCrouchGunOffset() + vecRight * 8); + + trace_t tr; + AI_TraceLOS( vGunPos, vGunPos + (vecForward * flDist), this, &tr ); + if (tr.fraction != 1.0) + { + return false; + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -15949,9 +16337,16 @@ bool CAI_BaseNPC::IsCrouchedActivity( Activity activity ) case ACT_COVER_SMG1_LOW: case ACT_RELOAD_SMG1_LOW: #ifdef MAPBASE - //case ACT_RELOAD_AR2_LOW: +#if AR2_ACTIVITY_FIX == 1 + case ACT_COVER_AR2_LOW: + case ACT_RELOAD_AR2_LOW: +#endif case ACT_RELOAD_PISTOL_LOW: case ACT_RELOAD_SHOTGUN_LOW: +#if EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_RELOAD_REVOLVER_LOW: + case ACT_RELOAD_CROSSBOW_LOW: +#endif #endif return true; } diff --git a/mp/src/game/server/ai_basenpc.h b/mp/src/game/server/ai_basenpc.h index 88b43d91..24bcf0b4 100644 --- a/mp/src/game/server/ai_basenpc.h +++ b/mp/src/game/server/ai_basenpc.h @@ -64,7 +64,9 @@ class CBaseGrenade; class CBaseDoor; class CBasePropDoor; struct AI_Waypoint_t; +#ifndef NEW_RESPONSE_SYSTEM class AI_Response; +#endif class CBaseFilter; typedef CBitVec CAI_ScheduleBits; @@ -665,6 +667,7 @@ public: virtual bool ShouldAlwaysThink(); void ForceGatherConditions() { m_bForceConditionsGather = true; SetEfficiency( AIE_NORMAL ); } // Force an NPC out of PVS to call GatherConditions on next think + bool IsForceGatherConditionsSet() { return m_bForceConditionsGather; } virtual float LineOfSightDist( const Vector &vecDir = vec3_invalid, float zEye = FLT_MAX ); @@ -958,7 +961,7 @@ public: void RemoveSleepFlags( int flags ) { m_SleepFlags &= ~flags; } bool HasSleepFlags( int flags ) { return (m_SleepFlags & flags) == flags; } - void UpdateSleepState( bool bInPVS ); + virtual void UpdateSleepState( bool bInPVS ); virtual void Wake( bool bFireOutput = true ); #ifdef MAPBASE // A version of Wake() that takes an activator @@ -995,12 +998,17 @@ public: Activity NPC_TranslateActivity( Activity eNewActivity ); #ifdef MAPBASE Activity TranslateCrouchActivity( Activity baseAct ); + virtual bool CanTranslateCrouchActivity( void ) { return true; } virtual Activity NPC_BackupActivity( Activity eNewActivity ); #endif Activity GetActivity( void ) { return m_Activity; } virtual void SetActivity( Activity NewActivity ); Activity GetIdealActivity( void ) { return m_IdealActivity; } void SetIdealActivity( Activity NewActivity ); +#ifdef MAPBASE + Activity GetTranslatedActivity( void ) { return m_translatedActivity; } + Activity GetIdealTranslatedActivity( void ) { return m_IdealTranslatedActivity; } +#endif void ResetIdealActivity( Activity newIdealActivity ); void SetSequenceByName( const char *szSequence ); void SetSequenceById( int iSequence ); @@ -1017,6 +1025,25 @@ public: void SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity); +#ifdef MAPBASE + //----------------------------------------------------- + + // Returns the gesture variant of an activity (i.e. "ACT_GESTURE_RANGE_ATTACK1") + static Activity GetGestureVersionOfActivity( Activity inActivity ); + + // Returns the sequence variant of a gesture activity + static Activity GetSequenceVersionOfGesture( Activity inActivity ); + + //----------------------------------------------------- + + virtual bool ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ); + virtual Activity SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ); + void PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence ); + + int GetFakeSequenceGesture(); + void ResetFakeSequenceGesture(); +#endif + private: void AdvanceToIdealActivity(void); @@ -1030,6 +1057,10 @@ private: Activity m_IdealTranslatedActivity; // Desired actual translated animation state Activity m_IdealWeaponActivity; // Desired weapon animation state +#ifdef MAPBASE + int m_FakeSequenceGestureLayer; // The gesture layer impersonating a sequence (-1 if invalid) +#endif + CNetworkVar(int, m_iDeathPose ); CNetworkVar(int, m_iDeathFrame ); @@ -1214,6 +1245,8 @@ public: #endif #ifdef MAPBASE_VSCRIPT +private: + // VScript stuff uses "VScript" instead of just "Script" to avoid // confusion with NPC_STATE_SCRIPT or StartScripting HSCRIPT VScriptGetEnemy(); @@ -1237,6 +1270,13 @@ public: int ScriptGetActivityID() { return GetActivity(); } void ScriptSetActivity( const char *szActivity ) { SetActivity( (Activity)GetActivityID( szActivity ) ); } void ScriptSetActivityID( int iActivity ) { SetActivity((Activity)iActivity); } + int ScriptTranslateActivity( const char *szActivity ) { return TranslateActivity( (Activity)GetActivityID( szActivity ) ); } + int ScriptTranslateActivityID( int iActivity ) { return TranslateActivity( (Activity)iActivity ); } + + const char* VScriptGetGestureVersionOfActivity( const char *pszActivity ) { return GetActivityName( GetGestureVersionOfActivity( (Activity)GetActivityID( pszActivity ) ) ); } + int VScriptGetGestureVersionOfActivityID( int iActivity ) { return GetGestureVersionOfActivity( (Activity)iActivity ); } + const char* VScriptGetSequenceVersionOfGesture( const char *pszActivity ) { return GetActivityName( GetSequenceVersionOfGesture( (Activity)GetActivityID( pszActivity ) ) ); } + int VScriptGetSequenceVersionOfGestureID( int iActivity ) { return GetSequenceVersionOfGesture( (Activity)iActivity ); } const char* VScriptGetSchedule(); int VScriptGetScheduleID(); @@ -1726,8 +1766,8 @@ public: virtual bool DoHolster(void); virtual bool DoUnholster(void); - virtual bool ShouldUnholsterWeapon() { return GetState() == NPC_STATE_COMBAT; } - virtual bool CanUnholsterWeapon() { return IsWeaponHolstered(); } + virtual bool ShouldUnholsterWeapon(); + virtual bool CanUnholsterWeapon(); void InputGiveWeaponHolstered( inputdata_t &inputdata ); void InputChangeWeapon( inputdata_t &inputdata ); @@ -2113,6 +2153,7 @@ public: COutputEHANDLE m_OnUnholsterWeapon; COutputEHANDLE m_OnItemPickup; + COutputEHANDLE m_OnItemDrop; COutputInt m_OnStateChange; #endif @@ -2200,6 +2241,10 @@ public: inline void ForceCrouch( void ); inline void ClearForceCrouch( void ); +#ifdef MAPBASE + bool CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist = 48.0f ); +#endif + protected: virtual bool Crouch( void ); virtual bool Stand( void ); @@ -2259,6 +2304,16 @@ private: static CAI_GlobalScheduleNamespace gm_SchedulingSymbols; static CAI_ClassScheduleIdSpace gm_ClassScheduleIdSpace; +#ifdef MAPBASE + typedef struct + { + Activity sequence; + Activity gesture; + } actlink_t; + + static actlink_t gm_ActivityGestureLinks[]; +#endif + public: //---------------------------------------------------- // Debugging tools @@ -2303,6 +2358,16 @@ public: CUtlVector m_ScheduleHistory; #endif//AI_MONITOR_FOR_OSCILLATION +#ifdef MAPBASE_VSCRIPT + static ScriptHook_t g_Hook_QueryHearSound; + static ScriptHook_t g_Hook_QuerySeeEntity; + static ScriptHook_t g_Hook_TranslateActivity; + static ScriptHook_t g_Hook_TranslateSchedule; + static ScriptHook_t g_Hook_GetActualShootPosition; + static ScriptHook_t g_Hook_OverrideMove; + static ScriptHook_t g_Hook_ShouldPlayFakeSequenceGesture; +#endif + private: // Break into pieces! diff --git a/mp/src/game/server/ai_basenpc_schedule.cpp b/mp/src/game/server/ai_basenpc_schedule.cpp index 10bf02e5..61fefb3f 100644 --- a/mp/src/game/server/ai_basenpc_schedule.cpp +++ b/mp/src/game/server/ai_basenpc_schedule.cpp @@ -980,11 +980,12 @@ bool CAI_BaseNPC::FindCoverFromEnemy( bool bNodesOnly, float flMinDistance, floa // FIXME: add to goal if (GetHintNode()) { - GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) ); #ifdef MAPBASE - if (GetHintNode()->GetIgnoreFacing() != HIF_NO) -#endif + GetHintNode()->NPCHandleStartNav( this, true ); +#else + GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) ); GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() ); +#endif } return true; @@ -1363,6 +1364,14 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) break; case TASK_STOP_MOVING: +#ifdef MAPBASE + if ( GetNavType() == NAV_CLIMB ) + { + // Don't clear the goal so that the climb can finish + DbgNavMsg( this, "Start TASK_STOP_MOVING with climb workaround\n" ); + } + else +#endif if ( ( GetNavigator()->IsGoalSet() && GetNavigator()->IsGoalActive() ) || GetNavType() == NAV_JUMP ) { DbgNavMsg( this, "Start TASK_STOP_MOVING\n" ); @@ -3015,7 +3024,16 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) case TASK_ITEM_PICKUP: { - SetIdealActivity( ACT_PICKUP_GROUND ); +#ifdef MAPBASE + if (GetTarget() && fabs( GetTarget()->WorldSpaceCenter().z - GetAbsOrigin().z ) >= 12.0f) + { + SetIdealActivity( ACT_PICKUP_RACK ); + } + else +#endif + { + SetIdealActivity( ACT_PICKUP_GROUND ); + } } break; @@ -3339,8 +3357,40 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // a navigation while in the middle of a climb if (GetNavType() == NAV_CLIMB) { +#ifdef MAPBASE + if (GetActivity() != ACT_CLIMB_DISMOUNT) + { + // Try to just pause the climb, but dismount if we're in SCHED_FAIL + if (IsCurSchedule( SCHED_FAIL, false )) + { + GetMotor()->MoveClimbStop(); + } + else + { + GetMotor()->MoveClimbPause(); + } + + TaskComplete(); + } + else if (IsActivityFinished()) + { + // Dismount complete. + GetMotor()->MoveClimbStop(); + + // Fix up our position if we have to + Vector vecTeleportOrigin; + if (GetMotor()->MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin )) + { + SetLocalOrigin( vecTeleportOrigin ); + } + + TaskComplete(); + } + break; +#else // wait until you reach the end break; +#endif } DbgNavMsg( this, "TASK_STOP_MOVING Complete\n" ); @@ -3385,6 +3435,17 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // If the yaw is locked, this function will not act correctly Assert( GetMotor()->IsYawLocked() == false ); +#ifdef MAPBASE + if ( GetHintNode() && GetHintNode()->OverridesNPCYaw( this ) ) + { + // If the yaw is supposed to use that of a hint node, chain to TASK_FACE_HINTNODE + GetMotor()->SetIdealYaw( GetHintNode()->Yaw() ); + GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw + ChainRunTask( TASK_FACE_HINTNODE, pTask->flTaskData ); + break; + } +#endif + Vector vecEnemyLKP = GetEnemyLKP(); if (!FInAimCone( vecEnemyLKP )) { @@ -3979,10 +4040,16 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // If we have an entry, we have to play it first if ( m_hCine->m_iszEntry != NULL_STRING ) { +#ifdef MAPBASE + m_hCine->OnEntrySequence( this ); +#endif m_hCine->StartSequence( (CAI_BaseNPC *)this, m_hCine->m_iszEntry, true ); } else { +#ifdef MAPBASE + m_hCine->OnActionSequence( this ); +#endif m_hCine->StartSequence( (CAI_BaseNPC *)this, m_hCine->m_iszPlay, true ); } @@ -4265,6 +4332,15 @@ void CAI_BaseNPC::SetTurnActivity ( void ) float flYD; flYD = GetMotor()->DeltaIdealYaw(); +#ifdef MAPBASE + // Allow AddTurnGesture() to decide this + if (GetMotor()->AddTurnGesture( flYD )) + { + SetIdealActivity( ACT_IDLE ); + Remember( bits_MEMORY_TURNING ); + return; + } +#else // FIXME: unknown case, update yaw should catch these /* if (GetMotor()->AddTurnGesture( flYD )) @@ -4274,6 +4350,7 @@ void CAI_BaseNPC::SetTurnActivity ( void ) return; } */ +#endif if( flYD <= -80 && flYD >= -100 && SelectWeightedSequence( ACT_90_RIGHT ) != ACTIVITY_NOT_AVAILABLE ) { diff --git a/mp/src/game/server/ai_behavior.cpp b/mp/src/game/server/ai_behavior.cpp index 8822f750..4730e122 100644 --- a/mp/src/game/server/ai_behavior.cpp +++ b/mp/src/game/server/ai_behavior.cpp @@ -420,6 +420,24 @@ bool CAI_BehaviorBase::CanUnholsterWeapon( void ) return m_pBackBridge->BackBridge_CanUnholsterWeapon(); } + +//------------------------------------- + +bool CAI_BehaviorBase::ShouldPickADeathPose( void ) +{ + Assert( m_pBackBridge != NULL ); + + return m_pBackBridge->BackBridge_ShouldPickADeathPose(); +} + +//------------------------------------- + +bool CAI_BehaviorBase::CanTranslateCrouchActivity( void ) +{ + Assert( m_pBackBridge != NULL ); + + return m_pBackBridge->BackBridge_CanTranslateCrouchActivity(); +} #endif //------------------------------------- diff --git a/mp/src/game/server/ai_behavior.h b/mp/src/game/server/ai_behavior.h index ce26ca61..f4df0640 100644 --- a/mp/src/game/server/ai_behavior.h +++ b/mp/src/game/server/ai_behavior.h @@ -132,6 +132,8 @@ public: void BridgeHandleAnimEvent( animevent_t *pEvent ); #ifdef MAPBASE bool BridgeCanUnholsterWeapon( void ); + bool BridgeShouldPickADeathPose( void ); + bool BridgeCanTranslateCrouchActivity( void ); #endif virtual void GatherConditions(); @@ -220,6 +222,8 @@ protected: virtual void HandleAnimEvent( animevent_t *pEvent ); #ifdef MAPBASE virtual bool CanUnholsterWeapon( void ); + virtual bool ShouldPickADeathPose( void ); + virtual bool CanTranslateCrouchActivity( void ); #endif virtual bool ShouldAlwaysThink(); @@ -370,6 +374,9 @@ public: #ifdef MAPBASE // For func_tank behavior virtual bool BackBridge_CanUnholsterWeapon( void ) = 0; + + virtual bool BackBridge_ShouldPickADeathPose( void ) = 0; + virtual bool BackBridge_CanTranslateCrouchActivity( void ) = 0; #endif //------------------------------------- @@ -470,6 +477,8 @@ public: void HandleAnimEvent( animevent_t *pEvent ); #ifdef MAPBASE bool CanUnholsterWeapon( void ); + bool ShouldPickADeathPose( void ); + bool CanTranslateCrouchActivity( void ); #endif bool ShouldAlwaysThink(); @@ -534,6 +543,9 @@ private: #ifdef MAPBASE // For func_tank behavior bool BackBridge_CanUnholsterWeapon( void ); + + bool BackBridge_ShouldPickADeathPose( void ); + bool BackBridge_CanTranslateCrouchActivity( void ); #endif CAI_BehaviorBase **AccessBehaviors(); @@ -913,6 +925,20 @@ inline bool CAI_BehaviorBase::BridgeCanUnholsterWeapon( void ) { return CanUnholsterWeapon(); } + +//----------------------------------------------------------------------------- + +inline bool CAI_BehaviorBase::BridgeShouldPickADeathPose( void ) +{ + return ShouldPickADeathPose(); +} + +//----------------------------------------------------------------------------- + +inline bool CAI_BehaviorBase::BridgeCanTranslateCrouchActivity( void ) +{ + return CanTranslateCrouchActivity(); +} #endif //----------------------------------------------------------------------------- @@ -1498,6 +1524,22 @@ inline bool CAI_BehaviorHost::BackBridge_CanUnholsterWeapon( void ) { return BaseClass::CanUnholsterWeapon(); } + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_ShouldPickADeathPose( void ) +{ + return BaseClass::ShouldPickADeathPose(); +} + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_CanTranslateCrouchActivity( void ) +{ + return BaseClass::CanTranslateCrouchActivity(); +} #endif //------------------------------------- @@ -1914,6 +1956,28 @@ inline bool CAI_BehaviorHost::CanUnholsterWeapon( void ) return BaseClass::CanUnholsterWeapon(); } + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::ShouldPickADeathPose( void ) +{ + if (m_pCurBehavior) + return m_pCurBehavior->BridgeShouldPickADeathPose(); + + return BaseClass::ShouldPickADeathPose(); +} + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::CanTranslateCrouchActivity( void ) +{ + if (m_pCurBehavior) + return m_pCurBehavior->BridgeCanTranslateCrouchActivity(); + + return BaseClass::CanTranslateCrouchActivity(); +} #endif //------------------------------------- diff --git a/mp/src/game/server/ai_behavior_follow.cpp b/mp/src/game/server/ai_behavior_follow.cpp index bcd254a5..a2ab0cae 100644 --- a/mp/src/game/server/ai_behavior_follow.cpp +++ b/mp/src/game/server/ai_behavior_follow.cpp @@ -408,10 +408,11 @@ bool CAI_FollowBehavior::SetFollowGoal( CAI_FollowGoal *pGoal, bool fFinishCurSc SetFollowTarget( pGoal->GetGoalEntity() ); #ifdef MAPBASE Assert( pGoal->m_iFormation < AIF_NUM_FORMATIONS ); + SetParameters( AI_FollowParams_t( (AI_Formations_t)pGoal->m_iFormation, pGoal->m_bNormalMemoryDiscard ) ); #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 ) ); +#endif m_hFollowGoalEnt = pGoal; m_flTimeUpdatedFollowPosition = 0; return true; @@ -2138,6 +2139,9 @@ bool CAI_FollowBehavior::ShouldAlwaysThink() BEGIN_DATADESC( CAI_FollowGoal ) DEFINE_KEYFIELD( m_iFormation, FIELD_INTEGER, "Formation" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bNormalMemoryDiscard, FIELD_BOOLEAN, "NormalMemoryDiscard" ), +#endif #ifdef HL2_EPISODIC DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ), diff --git a/mp/src/game/server/ai_behavior_follow.h b/mp/src/game/server/ai_behavior_follow.h index 84cb0109..097588c6 100644 --- a/mp/src/game/server/ai_behavior_follow.h +++ b/mp/src/game/server/ai_behavior_follow.h @@ -71,6 +71,10 @@ public: int m_iFormation; +#ifdef MAPBASE + bool m_bNormalMemoryDiscard = false; +#endif + DECLARE_DATADESC(); }; diff --git a/mp/src/game/server/ai_behavior_lead.h b/mp/src/game/server/ai_behavior_lead.h index 2104b1f2..ef4bb025 100644 --- a/mp/src/game/server/ai_behavior_lead.h +++ b/mp/src/game/server/ai_behavior_lead.h @@ -9,12 +9,19 @@ #include "simtimer.h" #include "ai_behavior.h" +#ifdef NEW_RESPONSE_SYSTEM +#include "ai_speechconcept.h" +#endif #if defined( _WIN32 ) #pragma once #endif +#ifdef NEW_RESPONSE_SYSTEM +typedef CAI_Concept AIConcept_t; +#else typedef const char *AIConcept_t; +#endif // Speak concepts #define TLK_LEAD_START "TLK_LEAD_START" diff --git a/mp/src/game/server/ai_behavior_standoff.cpp b/mp/src/game/server/ai_behavior_standoff.cpp index 0792df23..00e1565b 100644 --- a/mp/src/game/server/ai_behavior_standoff.cpp +++ b/mp/src/game/server/ai_behavior_standoff.cpp @@ -240,6 +240,23 @@ void CAI_StandoffBehavior::SetActive( bool fActive ) { if ( fActive != m_fActive ) { +#ifdef MAPBASE + // These sentences are only spoken if the standoff behavior is active, so they have to be arranged separately + if ( fActive ) + { + m_fActive = fActive; + NotifyChangeBehaviorStatus(); + + GetOuter()->SpeakSentence( STANDOFF_SENTENCE_BEGIN_STANDOFF ); + } + else + { + GetOuter()->SpeakSentence( STANDOFF_SENTENCE_END_STANDOFF ); + + m_fActive = fActive; + NotifyChangeBehaviorStatus(); + } +#else if ( fActive ) { GetOuter()->SpeakSentence( STANDOFF_SENTENCE_BEGIN_STANDOFF ); @@ -251,6 +268,7 @@ void CAI_StandoffBehavior::SetActive( bool fActive ) m_fActive = fActive; NotifyChangeBehaviorStatus(); +#endif } } @@ -278,7 +296,12 @@ bool CAI_StandoffBehavior::CanSelectSchedule() if ( !m_fActive ) return false; +#ifdef MAPBASE + // Allow NPCs with innate range attacks to use standoffs + return ( GetNpcState() == NPC_STATE_COMBAT && (GetOuter()->GetActiveWeapon() != NULL || GetOuter()->CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1) ); +#else return ( GetNpcState() == NPC_STATE_COMBAT && GetOuter()->GetActiveWeapon() != NULL ); +#endif } //------------------------------------- @@ -517,10 +540,11 @@ int CAI_StandoffBehavior::SelectScheduleCheckCover( void ) { 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 ) + if ( GetHintType() == HINT_TACTICAL_COVER_MED || GetCoverActivity() == ACT_COVER_MED ) + SetPosture( AIP_CROUCHING_MED ); + else #endif + if ( GetHintType() == HINT_TACTICAL_COVER_LOW ) SetPosture( AIP_CROUCHING ); else SetPosture( AIP_STANDING ); @@ -539,7 +563,11 @@ int CAI_StandoffBehavior::SelectScheduleEstablishAim( void ) { if ( HasCondition( COND_ENEMY_OCCLUDED ) ) { +#if EXPANDED_HL2_COVER_ACTIVITIES + if ( GetPosture() == AIP_CROUCHING || GetPosture() == AIP_CROUCHING_MED ) +#else if ( GetPosture() == AIP_CROUCHING ) +#endif { // force a stand up, just in case GetOuter()->SpeakSentence( STANDOFF_SENTENCE_STAND_CHECK_TARGET ); @@ -577,7 +605,11 @@ int CAI_StandoffBehavior::SelectScheduleAttack( void ) !HasCondition( COND_CAN_MELEE_ATTACK1 ) && HasCondition( COND_TOO_FAR_TO_ATTACK ) ) { +#ifdef MAPBASE + if ( (GetOuter()->GetActiveWeapon() && ( GetOuter()->GetActiveWeapon()->CapabilitiesGet() & bits_CAP_WEAPON_RANGE_ATTACK1 )) || GetOuter()->CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1 ) +#else if ( GetOuter()->GetActiveWeapon() && ( GetOuter()->GetActiveWeapon()->CapabilitiesGet() & bits_CAP_WEAPON_RANGE_ATTACK1 ) ) +#endif { if ( !HasCondition( COND_ENEMY_OCCLUDED ) || random->RandomInt(0,99) < 50 ) // Don't advance, just fire anyway @@ -650,6 +682,15 @@ Activity CAI_MappedActivityBehavior_Temporary::GetMappedActivity( AI_Posture_t p { if ( posture != AIP_STANDING ) { +#if EXPANDED_HL2_COVER_ACTIVITIES + // See UpdateTranslateActivityMap() for more information on what this is for + if ( posture == AIP_CROUCHING_MED ) + { + if (activity != ACT_RANGE_ATTACK1) + posture = AIP_CROUCHING; + } +#endif + unsigned short iActivityTranslation = m_ActivityMap.Find( MAKE_ACTMAP_KEY( posture, activity ) ); if ( iActivityTranslation != m_ActivityMap.InvalidIndex() ) { @@ -667,10 +708,28 @@ Activity CAI_StandoffBehavior::NPC_TranslateActivity( Activity activity ) Activity coverActivity = GetCoverActivity(); if ( coverActivity != ACT_INVALID ) { +#ifdef MAPBASE + if ( GetPosture() == AIP_STANDING ) + { + if ( coverActivity == ACT_COVER_LOW ) + SetPosture( AIP_CROUCHING ); + else if ( coverActivity == ACT_COVER_MED ) + { + SetPosture( AIP_CROUCHING_MED ); + coverActivity = ACT_COVER_LOW; + } + } + else if (coverActivity == ACT_COVER_MED) + coverActivity = ACT_COVER_LOW; + if ( activity == ACT_IDLE ) activity = coverActivity; +#else + if (activity == ACT_IDLE) + activity = coverActivity; if ( GetPosture() == AIP_STANDING && coverActivity == ACT_COVER_LOW ) SetPosture( AIP_CROUCHING ); +#endif } Activity result = GetMappedActivity( GetPosture(), activity ); @@ -1071,12 +1130,25 @@ 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; +#if EXPANDED_HL2_COVER_ACTIVITIES + // GetCoverActivity() already checks everything we checked here. + Activity coverActivity = GetOuter()->GetCoverActivity( GetHintNode() ); + + if (coverActivity == ACT_COVER_LOW) + { + // Check if this node will block our line of sight if aiming low. + Vector vHintPos, vHintForward, vHintRight; + GetHintNode()->GetPosition( GetHullType(), &vHintPos ); + vHintForward = GetHintNode()->GetDirection(); + + GetHintNode()->GetVectors( NULL, &vHintRight, NULL ); + if (GetOuter()->CouldShootIfCrouchingAt( vHintPos, vHintForward, vHintRight )) + { + coverActivity = ACT_COVER_MED; + } + } + + return coverActivity == ACT_IDLE ? ACT_INVALID : coverActivity; #else CAI_Hint *pHintNode = GetHintNode(); if ( pHintNode && pHintNode->HintType() == HINT_TACTICAL_COVER_LOW ) @@ -1094,6 +1166,14 @@ struct AI_ActivityMapping_t Activity activity; const char * pszWeapon; Activity translation; +#ifdef MAPBASE + Activity backup; + + AI_ActivityMapping_t( AI_Posture_t _p, Activity _a, const char *_w, Activity _t, Activity _b = ACT_INVALID ) + { + posture = _p; activity = _a; pszWeapon = _w; translation = _t; backup = _b; + } +#endif }; void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() @@ -1107,15 +1187,60 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() { AIP_CROUCHING, ACT_WALK_AIM, NULL, ACT_WALK_CROUCH_AIM, }, { AIP_CROUCHING, ACT_RUN_AIM, NULL, ACT_RUN_CROUCH_AIM, }, { AIP_CROUCHING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, }, +#ifdef MAPBASE + { AIP_CROUCHING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, }, + { AIP_CROUCHING, ACT_COVER_MED, NULL, ACT_COVER_LOW, }, +#else { AIP_CROUCHING, ACT_RANGE_ATTACK_SMG1, NULL, ACT_RANGE_ATTACK_SMG1_LOW, }, { AIP_CROUCHING, ACT_RANGE_ATTACK_AR2, NULL, ACT_RANGE_ATTACK_AR2_LOW, }, +#endif +#if EXPANDED_HL2_COVER_ACTIVITIES + // + // ============ Really long explanation that should be in a wiki/documentation article somewhere ~ Blixibon, 10/27/2021 ============ + // + // Standoff behavior assumes low attack animations allow NPCs to see over barricades, with ACT_COVER_LOW being their "safely in cover" animation. + // This is why AIP_CROUCHING translates ACT_RANGE_ATTACK1 to its low animation, but translates ACT_IDLE_ANGRY to ACT_COVER_LOW instead of ACT_RANGE_AIM_LOW, + // as this would ideally allow NPCs to pop in and out of cover to shoot. + // This is also why AIP_PEEKING translates ACT_COVER_LOW to ACT_RANGE_AIM_LOW, as it's supposed to force the NPC to peek over their cover. + // + // However, this assumption mainly just applies to metrocops. Citizens' low attacking animations crouch low to the ground (which isn't effective for + // shooting over most barricades) and, while they do have a distinct ACT_COVER_LOW animation with transitions, they are close enough together that popping + // in and out of cover is redundant in most cases. Meanwhile, Combine soldiers have identical ACT_COVER_LOW and ACT_RANGE_AIM_LOW animations, which means + // they do not pop in and out of cover and AIP_PEEKING does nothing. This may be the reason why Combine soldiers occasionally get stuck in cover after a fight. + // + // ------------------------------------------------------------- + // + // As part of Mapbase v7.0's NPC activity overhaul, a new "medium cover" activity set has been added. Metrocops' previous "low cover" animation set (which, as + // mentioned before, is different from that of other NPCs) has been retroactively changed to use "medium cover". This was done for a few reasons unrelated to + // standoff behavior, but the important point is that these activities indicate a new cover height. This means we can use them to give standoff behavior more leeway + // for judging which animations to use in various levels of cover. + // + // Standoff behavior can use "medium cover" animations in cover which is too high for the "low" animations, and when the medium cover animations are not available, + // it simply falls back to the "standing" animations, thus resolving the issue with other NPCs not peeking in and out of cover without requiring new medium cover + // animations. + // + // In Mapbase, this is done by changing AIP_PEEKING to use the medium cover animations and adding a new alternate crouching posture posture called "AIP_CROUCHING_MED", + // which only uses the medium cover attack activity and otherwise automatically falls back to AIP_CROUCHING. AIP_CROUCHING_MED is automatically set if the NPC cannot + // get LOS from a regular crouching position. + // + { AIP_CROUCHING_MED, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, }, + + //---- + { AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_MED, }, + { AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_MED, }, + { AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY }, + { AIP_PEEKING, ACT_COVER_MED, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY }, + { AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, }, + { AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, }, +#else //---- { AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_LOW, }, { AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_LOW, }, { AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_LOW, }, { AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, }, { AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, }, +#endif }; m_ActivityMap.RemoveAll(); @@ -1127,7 +1252,7 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() if ( !mappings[i].pszWeapon || stricmp( mappings[i].pszWeapon, pszWeaponClass ) == 0 ) { #ifdef MAPBASE - // Check backup activity + // Check NPC 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 ) ) ) @@ -1136,6 +1261,14 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() 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 ); } +#ifdef MAPBASE + // Check activity map backup activity + else if ( mappings[i].backup != ACT_INVALID && HaveSequenceForActivity( mappings[i].backup ) ) + { + 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].backup ); + } +#endif } } } diff --git a/mp/src/game/server/ai_behavior_standoff.h b/mp/src/game/server/ai_behavior_standoff.h index c08059e8..9141e874 100644 --- a/mp/src/game/server/ai_behavior_standoff.h +++ b/mp/src/game/server/ai_behavior_standoff.h @@ -51,6 +51,9 @@ enum AI_Posture_t AIP_INDIFFERENT, AIP_STANDING, AIP_CROUCHING, +#if EXPANDED_HL2_COVER_ACTIVITIES + AIP_CROUCHING_MED, // See UpdateTranslateActivityMap() for more information on what this is for +#endif AIP_PEEKING, }; @@ -149,6 +152,14 @@ protected: // Standoff overrides base AI crouch handling bool IsCrouching( void ) { return false; } + +#ifdef MAPBASE + // Standoff overrides base cover activity translation + bool CanTranslateCrouchActivity( void ) { return false; } + + // Don't do death poses while crouching + bool ShouldPickADeathPose( void ) { return (GetPosture() != AIP_CROUCHING && GetPosture() != AIP_PEEKING) && BaseClass::ShouldPickADeathPose(); } +#endif private: diff --git a/mp/src/game/server/ai_blended_movement.cpp b/mp/src/game/server/ai_blended_movement.cpp index 7c4ba8af..5262b644 100644 --- a/mp/src/game/server/ai_blended_movement.cpp +++ b/mp/src/game/server/ai_blended_movement.cpp @@ -1640,10 +1640,17 @@ void CAI_BlendedMotor::MaintainTurnActivity( void ) ConVar scene_flatturn( "scene_flatturn", "1" ); +#ifdef MAPBASE +ConVar ai_turning_enabled( "ai_turning_enabled", "1", FCVAR_NONE, "Enables NPC turning, which was previously disabled by Valve at some point after 2004 due to a now-unknown major issue." ); +#endif + bool CAI_BlendedMotor::AddTurnGesture( float flYD ) { // some funky bug with human turn gestures, disable for now +#ifdef MAPBASE + if (!ai_turning_enabled.GetBool()) +#endif return false; // try using a turn gesture diff --git a/mp/src/game/server/ai_expresserfollowup.cpp b/mp/src/game/server/ai_expresserfollowup.cpp new file mode 100644 index 00000000..65575709 --- /dev/null +++ b/mp/src/game/server/ai_expresserfollowup.cpp @@ -0,0 +1,509 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "ai_speech.h" + +#include "game.h" +#include "eventqueue.h" +#include "ai_basenpc.h" +#include "basemultiplayerplayer.h" +#include "ai_baseactor.h" +#include "sceneentity.h" +//#include "flex_expresser.h" +/* +#include "engine/ienginesound.h" +#include "keyvalues.h" +#include "ai_criteria.h" +#include "isaverestore.h" +#include "sceneentity.h" +*/ + + + +// memdbgon must be the last include file in a .cpp file!!! +#include + +static const char *GetResponseName( CBaseEntity *pEnt ) +{ + Assert( pEnt ); + if ( pEnt == NULL ) + return ""; + return STRING( pEnt->GetEntityName() ); +} + +// This is a tiny helper function for below -- what I'd use a lambda for, usually +static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *pSpeaker, CBaseEntity *pRespondent, AI_ResponseFollowup &followup ) +{ + AssertMsg(pSpeaker != NULL, "Response expressor somehow got called with a NULL Outer.\n"); + if ( !pRespondent ) + { + return; + } + + float delay = followup.followup_delay; + if (pSpeaker == pRespondent && delay < 0) + { + Warning("Response rule with a 'self' target specified negative delay, which isn't legal because that would make someone talk over himself."); + delay = 0; + } + + // Msg( "%s: Dispatch comeback about %s to %s\n", pSpeaker->GetBotString(), g_pConceptManager->GetTopicName( handle ), pRespondent->GetBotString() ); + + // build an input event that we will use to force the bot to talk through the IO system + variant_t value; + // Don't send along null contexts + if (followup.followup_contexts && followup.followup_contexts[0] != '\0') + { + value.SetString( MAKE_STRING( followup.followup_contexts ) ); + g_EventQueue.AddEvent( pRespondent, "AddContext", value, delay - 0.01, pSpeaker, pSpeaker ); + } + + /* + value.SetString(MAKE_STRING(followup.followup_concept)); + g_EventQueue.AddEvent( pRespondent, "SpeakResponseConcept", value, delay , pSpeaker, pSpeaker ); + */ + + AI_CriteriaSet criteria; + + // add in the FROM context so dispatchee knows was from me + const char * RESTRICT pszSpeakerName = GetResponseName( pSpeaker ); + criteria.AppendCriteria( "From", pszSpeakerName ); +#ifdef MAPBASE + // See DispatchFollowupThroughQueue() + criteria.AppendCriteria( "From_idx", CNumStr( pSpeaker->entindex() ) ); + criteria.AppendCriteria( "From_class", pSpeaker->GetClassname() ); +#endif + // if a SUBJECT criteria is missing, put it back in. + if ( criteria.FindCriterionIndex( "Subject" ) == -1 ) + { + criteria.AppendCriteria( "Subject", pszSpeakerName ); + } + + // add in any provided contexts from the parameters onto the ones stored in the followup + criteria.Merge( followup.followup_contexts ); + + // This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely + // kinds of targets and dispatch to them. + if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast(pRespondent)) + { + pPlayer->Speak( followup.followup_concept, &criteria ); + } + + else if (CAI_BaseActor *pActor = dynamic_cast(pRespondent)) + { + pActor->Speak( followup.followup_concept, &criteria ); + } +} + +#if 0 +//----------------------------------------------------------------------------- +// Purpose: Placeholder for rules based response system +// Input : concept - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_ExpresserWithFollowup::Speak( AIConcept_t &concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + AI_Response *result = SpeakFindResponse( concept, modifiers ); + if ( !result ) + { + return false; + } + + CNPC_CompanionBot *pBot = dynamic_cast(GetOuter()); + if ( pBot ) + { + pBot->SetConversationTopic( g_pConceptManager->GetTopic( handle ) ); + pBot->SetLastSpeaker( g_pConceptManager->GetSpeaker( handle ) ); + // Msg( "%s: Conversing about %s\n", pBot->GetBotString(), g_pConceptManager->GetTopicName( handle ) ); + } + + SpeechMsg( GetOuter(), "%s (%x) spoke %s (%f)\n", STRING(GetOuter()->GetEntityName()), GetOuter(), g_pConceptManager->GetConcept( handle ), gpGlobals->curtime ); + + bool spoke = SpeakDispatchResponse( handle, result, filter ); + if ( pszOutResponseChosen ) + { + result->GetResponse( pszOutResponseChosen, bufsize ); + } + + return spoke; +} +#endif + + +// Work out the character from the "subject" context. +// Right now, this is a simple find by entity name search. +// But you can define arbitrary subject names, like L4D does +// for "biker", "manager", etc. +static CBaseEntity *AscertainSpeechSubjectFromContext( AI_Response *response, AI_CriteriaSet &criteria, const char *pContextName ) +{ + const char *subject = criteria.GetValue( criteria.FindCriterionIndex( pContextName ) ); + if (subject) + { + CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, subject ); + +#ifdef MAPBASE + // Allow entity indices to be used (see DispatchFollowupThroughQueue() for one particular use case) + if (!pEnt && atoi(subject)) + { + pEnt = CBaseEntity::Instance( atoi( subject ) ); + } +#endif + + return pEnt; + + } + else + { + return NULL; + } +} + +// TODO: Currently uses awful stricmp. Use symbols! Once I know which ones we want, that is. +static CResponseQueue::CFollowupTargetSpec_t ResolveFollowupTargetToEntity( AIConcept_t &concept, AI_CriteriaSet &criteria, const char * RESTRICT szTarget, AI_Response * RESTRICT response = NULL ) +{ + + + + if ( Q_stricmp(szTarget, "self") == 0 ) + { + return CResponseQueue::CFollowupTargetSpec_t( kDRT_SPECIFIC, concept.GetSpeaker() ); + } + else if ( Q_stricmp(szTarget, "subject") == 0 ) + { + return CResponseQueue::CFollowupTargetSpec_t( AscertainSpeechSubjectFromContext( response, criteria, "Subject" ) ); + } + else if ( Q_stricmp(szTarget, "from") == 0 ) + { +#ifdef MAPBASE + // See DispatchFollowupThroughQueue() + return CResponseQueue::CFollowupTargetSpec_t( AscertainSpeechSubjectFromContext( response, criteria, "From_idx" ) ); +#else + return CResponseQueue::CFollowupTargetSpec_t( AscertainSpeechSubjectFromContext( response, criteria, "From" ) ); +#endif + } + else if ( Q_stricmp(szTarget, "any") == 0 ) + { + return CResponseQueue::CFollowupTargetSpec_t( kDRT_ANY, concept.GetSpeaker() ); + } + else if ( Q_stricmp(szTarget, "all") == 0 ) + { + return CResponseQueue::CFollowupTargetSpec_t( kDRT_ALL ); + } + + // last resort, try a named lookup +#ifdef MAPBASE + else if ( CBaseEntity *pSpecific = gEntList.FindEntityByName(NULL, szTarget, concept.GetSpeaker()) ) // it could be anything +#else + else if ( CBaseEntity *pSpecific = gEntList.FindEntityByName(NULL, szTarget) ) // it could be anything +#endif + { + return CResponseQueue::CFollowupTargetSpec_t( pSpecific ); + } + + Warning("Couldn't resolve response target %s\n", szTarget ); + return CResponseQueue::CFollowupTargetSpec_t(); // couldn't resolve. +} + + +// TODO: Currently uses awful stricmp. Use symbols! Once I know which ones we want, that is. +static CResponseQueue::CFollowupTargetSpec_t ResolveFollowupTargetToEntity( AIConcept_t &concept, AI_CriteriaSet &criteria, AI_Response * RESTRICT response, AI_ResponseFollowup * RESTRICT followup ) +{ + const char * RESTRICT szTarget = followup->followup_target; + const CResponseQueue::CFollowupTargetSpec_t INVALID; // default: invalid result + if ( szTarget == NULL ) + return INVALID; + else + return ResolveFollowupTargetToEntity( concept, criteria, szTarget, response ); +} + + +ConVar chet_debug_idle( "chet_debug_idle", "0", FCVAR_ARCHIVE, "If set one, many debug prints to help track down the TLK_IDLE issue. Set two for super verbose info" ); +// extern ConVar chet_debug_idle; +bool CAI_ExpresserWithFollowup::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + VPROF("CAI_Expresser::Speak"); + if ( IsSpeechGloballySuppressed() ) + { + return false; + } + + concept.SetSpeaker(GetOuter()); + AI_CriteriaSet criteria; + GatherCriteria(&criteria, concept, modifiers); + GetOuter()->ModifyOrAppendDerivedCriteria(criteria); + AI_Response result; + if ( !FindResponse( result, concept, &criteria ) ) + { + if (chet_debug_idle.GetBool()) + { + + const char *name = GetOuter()->GetDebugName(); + + Msg( "TLK_IDLE: %s did not FindResponse\n", name ); + } + return false; + } + else + { + if (chet_debug_idle.GetBool()) + { + + + const char *name = GetOuter()->GetDebugName(); + + Msg( "TLK_IDLE: %s SUCCESSFUL FindResponse\n", name ); + } + } + + SpeechMsg( GetOuter(), "%s (%p) spoke %s (%f)", STRING(GetOuter()->GetEntityName()), GetOuter(), (const char*)concept, gpGlobals->curtime ); + // Msg( "%s:%s to %s:%s\n", GetOuter()->GetDebugName(), concept.GetStringConcept(), criteria.GetValue(criteria.FindCriterionIndex("Subject")), pTarget ? pTarget->GetDebugName() : "none" ); + + bool spoke = SpeakDispatchResponse( concept, &result, &criteria, filter ); + if ( pszOutResponseChosen ) + { + result.GetResponse( pszOutResponseChosen, bufsize ); + } + + return spoke; +} + +extern ISoundEmitterSystemBase* soundemitterbase; + +static float GetSpeechDurationForResponse( const AI_Response * RESTRICT response, const char *szActorModel) +{ + switch (response->GetType()) + { + case ResponseRules::RESPONSE_SCENE: + { + char szScene[MAX_PATH]; + soundemitterbase->GenderExpandString(szActorModel, response->GetResponsePtr(), szScene, MAX_PATH); + return GetSceneSpeechDuration(szScene); + } + break; + default: + break; + } + + return 0.f; +} + +//----------------------------------------------------------------------------- +// Purpose: Dispatches the result +// Input : *response - +//----------------------------------------------------------------------------- +bool CAI_ExpresserWithFollowup::SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria, IRecipientFilter *filter ) +{ + // This gives the chance for the other bot to respond. + if ( !concept.GetSpeaker().IsValid() ) + { + concept.SetSpeaker(GetOuter()); + } + + bool bInterrupted = IsSpeaking(); + bool bSuc = CAI_Expresser::SpeakDispatchResponse( concept, response, criteria, filter ); + if (!bSuc) + { + return false; + } + + if ( bInterrupted ) + { + g_ResponseQueueManager.GetQueue()->RemoveSpeechQueuedFor( GetOuter() ); + } + + // Record my followup details so that I may defer its use til end of the speech + AI_ResponseFollowup * RESTRICT followup = response->GetParams()->m_pFollowup; + if ( followup ) + { + if ( followup->followup_entityiotarget && followup->followup_entityioinput ) + { +#ifdef MAPBASE + CBaseEntity * RESTRICT pTarget = ResolveFollowupTargetToEntity( concept, *criteria, followup->followup_entityiotarget, response ).m_hHandle; +#else + CBaseEntity * RESTRICT pTarget = gEntList.FindEntityByName( NULL, followup->followup_entityiotarget ); +#endif + if ( pTarget ) + { + g_EventQueue.AddEvent( pTarget, followup->followup_entityioinput, variant_t(), followup->followup_entityiodelay, GetOuter(), GetOuter() ); + } + } + if ( followup->IsValid() ) + { + // 11th hour change: rather than trigger followups from the end of a VCD, + // instead fire it from the end of the last speech event in the VCD, because + // there's a multisecond facial relax delay built into the scene. + // The speech length is stored in the cache, so we can post the followup now. + if ( response->GetType() == ResponseRules::RESPONSE_SCENE && + followup->followup_delay >= 0 ) + { + float fTimeToLastSpeech = GetSpeechDurationForResponse( response, STRING(GetOuter()->GetModelName()) ); + // failsafe + if ( fTimeToLastSpeech > 0 ) + { + DispatchFollowupThroughQueue( followup->followup_concept, followup->followup_contexts, + ResolveFollowupTargetToEntity( concept, *criteria, response, followup ), + fTimeToLastSpeech + followup->followup_delay, GetOuter() ); + } + else // error + { + // old way, copied from "else" below + m_pPostponedFollowup = followup; + if ( criteria ) + m_followupTarget = ResolveFollowupTargetToEntity( concept, *criteria, response, m_pPostponedFollowup ); + else + { + AI_CriteriaSet tmpCriteria; + m_followupTarget = ResolveFollowupTargetToEntity( concept, tmpCriteria, response, m_pPostponedFollowup ); + } + } + } + else if ( followup->followup_delay < 0 ) + { + // a negative delay has a special meaning. Usually the comeback dispatches after + // the currently said line is finished; the delay is added to that, to provide a + // pause between when character A finishes speaking and B begins. + // A negative delay (-n) actually means "dispatch the comeback n seconds + // after I start talking". + // In this case we do not need to postpone the followup; we just throw it directly + // into the queue. + DispatchFollowupThroughQueue( followup->followup_concept, followup->followup_contexts, + ResolveFollowupTargetToEntity( concept, *criteria, response, followup ), + -followup->followup_delay, GetOuter() ); + } +#ifndef MAPBASE // RESPONSE_PRINT now notes speaking time + else if ( response->GetType() == ResponseRules::RESPONSE_PRINT ) + { // zero-duration responses dispatch immediately via the queue (must be the queue bec. + // the m_pPostponedFollowup will never trigger) + DispatchFollowupThroughQueue( followup->followup_concept, followup->followup_contexts, + ResolveFollowupTargetToEntity( concept, *criteria, response, followup ), + followup->followup_delay, GetOuter() ); + } +#endif + else + { + // this is kind of a quick patch to immediately deal with the issue of null criteria + // (arose while branching to main) without replumbing a bunch of stuff -- to be fixed + // 5.13.08 egr + m_pPostponedFollowup = followup; + if ( criteria ) + m_followupTarget = ResolveFollowupTargetToEntity( concept, *criteria, response, m_pPostponedFollowup ); + else + { + AI_CriteriaSet tmpCriteria; + m_followupTarget = ResolveFollowupTargetToEntity( concept, tmpCriteria, response, m_pPostponedFollowup ); + } + } + } + } + + + return bSuc; +} + +// This is a gimmick used when a negative delay is specified in a followup, which is a shorthand +// for "this many seconds after the beginning of the line" rather than "this may seconds after the end +// of the line", eg to create a THEN rule when two characters talk over each other. +// It's static to avoid accidental use of the postponed followup/target members. +void CAI_ExpresserWithFollowup::DispatchFollowupThroughQueue( const AIConcept_t &concept, + const char * RESTRICT criteriaStr, + const CResponseQueue::CFollowupTargetSpec_t &target, + float delay, + CBaseEntity * RESTRICT pOuter + ) +{ + AI_CriteriaSet criteria; + // Don't add my own criteria! GatherCriteria( &criteria, followup.followup_concept, followup.followup_contexts ); + + criteria.AppendCriteria( "From", STRING( pOuter->GetEntityName() ) ); +#ifdef MAPBASE + // The index of the "From" entity. + // In HL2 mods, many followup users would be generic NPCs (e.g. citizens) who might not have any particular significance. + // Those generic NPCs are quite likely to have no name or have a name in common with other entities. As a result, Mapbase + // changes internal operations of the "From" context to search for an entity index. This won't be 100% reliable if the source + // talker dies and another entity is created immediately afterwards, but it's a lot more reliable than a simple entity name search. + criteria.AppendCriteria( "From_idx", CNumStr( pOuter->entindex() ) ); + + // Generic NPCs should also be attributable by classname + criteria.AppendCriteria( "From_class", pOuter->GetClassname() ); +#endif + + criteria.Merge( criteriaStr ); + g_ResponseQueueManager.GetQueue()->Add( concept, &criteria, gpGlobals->curtime + delay, target, pOuter ); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles the new concept objects +//----------------------------------------------------------------------------- +void CAI_ExpresserWithFollowup::SpeakDispatchFollowup( AI_ResponseFollowup &followup ) +{ + if ( !m_followupTarget.IsValid() ) + return; + + // If a specific entity target is given, use the old pathway for now + if ( m_followupTarget.m_iTargetType == kDRT_SPECIFIC && followup.followup_delay == 0 ) + { + CBaseEntity *pTarget = m_followupTarget.m_hHandle.Get(); + if (!pTarget) + { + return; + } + DispatchComeback( this, GetOuter(), pTarget, followup ); + } + else + { + DispatchFollowupThroughQueue( followup.followup_concept, followup.followup_contexts, m_followupTarget, followup.followup_delay, GetOuter() ); + } + // clear out the followup member just in case. + m_pPostponedFollowup = NULL; + m_followupTarget.m_iTargetType = kDRT_MAX; +} + +void CAI_ExpresserWithFollowup::OnSpeechFinished() +{ + if (m_pPostponedFollowup && m_pPostponedFollowup->IsValid()) + { +#ifdef MAPBASE + // HACKHACK: Non-scene speech (e.g. noscene speak/sentence) fire OnSpeechFinished() immediately, + // so add the actual speech time to the followup delay + if (GetTimeSpeechCompleteWithoutDelay() > gpGlobals->curtime) + m_pPostponedFollowup->followup_delay += GetTimeSpeechCompleteWithoutDelay() - gpGlobals->curtime; +#endif + return SpeakDispatchFollowup(*m_pPostponedFollowup); + } +} + + + + +void CC_RR_ForceConcept_f( const CCommand &args ) +{ + if ( args.ArgC() < 3 ) + { + Msg("USAGE: rr_forceconcept \"criteria1:value1,criteria2:value2,...\"\n"); + return; + } + + AI_CriteriaSet criteria; + if ( args.ArgC() >= 3 ) + { + const char *criteriastring = args[3]; + criteria.Merge( criteriastring ); + } + + AIConcept_t concept( args[2] ); + QueueSpeak( concept, ResolveFollowupTargetToEntity( concept, criteria, args[1] ), criteria ); +} + + +static ConCommand rr_forceconcept( "rr_forceconcept", CC_RR_ForceConcept_f, + "fire a response concept directly at a given character.\n" + "USAGE: rr_forceconcept \"criteria1:value1,criteria2:value2,...\"\n" + "criteria values are optional.\n" + + , FCVAR_CHEAT ); diff --git a/mp/src/game/server/ai_hint.cpp b/mp/src/game/server/ai_hint.cpp index 1f8ec4d1..2e8e90af 100644 --- a/mp/src/game/server/ai_hint.cpp +++ b/mp/src/game/server/ai_hint.cpp @@ -35,6 +35,10 @@ CHintCriteria::CHintCriteria( void ) m_strGroup = NULL_STRING; m_iFlags = 0; m_HintTypes.Purge(); +#ifdef MAPBASE // From Alien Swarm SDK + m_pfnFilter = NULL; + m_pFilterContext = NULL; +#endif } //----------------------------------------------------------------------------- @@ -1117,10 +1121,10 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ) HintIgnoreFacing_t facing = GetIgnoreFacing(); if (facing == HIF_DEFAULT) - facing = bDefaultFacing ? HIF_YES : HIF_NO; + facing = bDefaultFacing ? HIF_NO : HIF_YES; - if (facing == HIF_YES) - pNPC->GetNavigator()->SetArrivalDirection(GetDirection()); + if (facing == HIF_NO) + pNPC->GetNavigator()->SetArrivalDirection( GetDirection() ); if (HintActivityName() != NULL_STRING) { @@ -1139,6 +1143,51 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ) } } } + +//----------------------------------------------------------------------------- +// Purpose: Returns true if this hint should override a NPC's yaw even during regular AI. +//----------------------------------------------------------------------------- +bool CAI_Hint::OverridesNPCYaw( CAI_BaseNPC *pNPC ) +{ + switch (HintType()) + { + case HINT_TACTICAL_COVER_CUSTOM: + case HINT_TACTICAL_COVER_MED: + case HINT_TACTICAL_COVER_LOW: + { + if (pNPC->HasMemory( bits_MEMORY_INCOVER )) + { + // By default, don't override yaw on cover nodes unless they use custom activities. + HintIgnoreFacing_t facing = GetIgnoreFacing(); + if (facing == HIF_DEFAULT) + return ( HintActivityName() != NULL_STRING ); + + return facing == HIF_NO; + } + + break; + } + + case HINT_PLAYER_ALLY_MOVE_AWAY_DEST: + { + Vector vHintPos; + GetPosition( pNPC, &vHintPos ); + if (VectorsAreEqual( vHintPos, pNPC->GetAbsOrigin(), 0.1f )) + { + // By default, don't override yaw on move away destinations unless they use custom activities. + HintIgnoreFacing_t facing = GetIgnoreFacing(); + if (facing == HIF_DEFAULT) + return ( HintActivityName() != NULL_STRING ); + + return facing == HIF_NO; + } + + break; + } + } + + return false; +} #endif //----------------------------------------------------------------------------- @@ -1258,6 +1307,30 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint return false; } +#ifdef MAPBASE + // Test against generic filter + // (From Alien Swarm SDK) + if ( !hintCriteria.PassesFilter( this ) ) + { + REPORTFAILURE( "Failed filter test" ); + return false; + } + + // (From Alien Swarm SDK) + int nRadius = GetRadius(); + if ( nRadius != 0 ) + { + // Calculate our distance + float distance = (GetAbsOrigin() - position).LengthSqr(); + + if ( distance > nRadius * nRadius ) + { + REPORTFAILURE( "NPC is not within the node's radius." ); + return false; + } + } +#endif + if ( hintCriteria.HasFlag(bits_HINT_NPC_IN_NODE_FOV) ) { if ( pNPC == NULL ) @@ -1377,10 +1450,18 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint { trace_t tr; // Can my bounding box fit there? +#ifdef MAPBASE // From Alien Swarm SDK + Vector vStep( 0, 0, pNPC->StepHeight() ); + AI_TraceHull ( GetAbsOrigin() + vStep, GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs() - vStep, + MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr ); + + if ( tr.fraction < 0.95 ) +#else AI_TraceHull ( GetAbsOrigin(), GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(), MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction != 1.0 ) +#endif { REPORTFAILURE( "Node isn't clear." ); return false; @@ -1396,6 +1477,15 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint // Calculate our distance float distance = (GetAbsOrigin() - position).Length(); +#ifdef MAPBASE + // Divide by hint weight + float flWeight = GetHintWeight(); + if ( flWeight != 1.0f ) + { + distance *= GetHintWeightInverse(); + } +#endif + // Must be closer than the current best if ( distance > *flNearestDistance ) { @@ -1576,6 +1666,14 @@ void CAI_Hint::OnRestore() m_NodeData.nNodeID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( m_NodeData.nWCNodeID ); FixupTargetNode(); +#ifdef MAPBASE + if (m_NodeData.flWeight != 0.0f && m_NodeData.flWeight != 1.0f) + { + // Re-invert the weight + m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight; + } +#endif + CAI_Node *pNode = GetNode(); if ( !pNode ) @@ -1751,6 +1849,11 @@ void CC_ai_drop_hint( const CCommand &args ) nodeData.fIgnoreFacing = HIF_DEFAULT; nodeData.minState = NPC_STATE_IDLE; nodeData.maxState = NPC_STATE_COMBAT; +#ifdef MAPBASE + nodeData.nRadius = 0; // From Alien Swarm SDK + nodeData.flWeight = 1.0f; + nodeData.flWeightInverse = 1.0f; +#endif CAI_Hint *pHint = CAI_HintManager::CreateHint( &nodeData, NULL ); if ( pHint ) { diff --git a/mp/src/game/server/ai_hint.h b/mp/src/game/server/ai_hint.h index 25fd4fdb..570f85ef 100644 --- a/mp/src/game/server/ai_hint.h +++ b/mp/src/game/server/ai_hint.h @@ -112,6 +112,13 @@ enum Hint_e // CS port hints HINT_CSTRIKE_HOSTAGE_ESCAPE = 1100, + +#ifdef MAPBASE + // Mapbase hints + // (these start at a high number to avoid potential conflicts with mod hints) + + HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity) +#endif }; const char *GetHintTypeDescription( Hint_e iHintType ); const char *GetHintTypeDescription( CAI_Hint *pHint ); @@ -120,6 +127,10 @@ const char *GetHintTypeDescription( CAI_Hint *pHint ); // CHintCriteria //----------------------------------------------------------------------------- +#ifdef MAPBASE // From Alien Swarm SDK +typedef bool (*HintSearchFilterFunc_t)( void *pContext, CAI_Hint *pCandidate ); +#endif + class CHintCriteria { public: @@ -134,6 +145,11 @@ public: void SetGroup( string_t group ); string_t GetGroup( void ) const { return m_strGroup; } +#ifdef MAPBASE // From Alien Swarm SDK + void SetFilterFunc( HintSearchFilterFunc_t pfnFilter, void *pContext = NULL ) { m_pfnFilter = pfnFilter; m_pFilterContext = pContext; } + bool PassesFilter( CAI_Hint *pCandidate ) const { return (m_pfnFilter) ? (*m_pfnFilter)(m_pFilterContext, pCandidate) : true; } +#endif + int GetFirstHintType( void ) const { return m_iFirstHintType; } int GetLastHintType( void ) const { return m_iLastHintType; } bool MatchesHintType( int hintType ) const; @@ -176,6 +192,11 @@ private: zoneList_t m_zoneInclude; zoneList_t m_zoneExclude; + +#ifdef MAPBASE + HintSearchFilterFunc_t m_pfnFilter; + void * m_pFilterContext; +#endif }; class CAI_Node; @@ -289,6 +310,11 @@ public: void SetHintType( int hintType, bool force = false ); string_t HintActivityName( void ) const { return m_NodeData.iszActivityName; } int GetTargetNode( void ) const { return m_nTargetNodeID; } +#ifdef MAPBASE + // HACKHACK: This is for when target nodes need to be accessed before being sorted into engine IDs + int GetTargetWCNodeID( void ) const { return m_NodeData.nTargetWCNodeID; } + int GetWCNodeID( void ) const { return m_NodeData.nWCNodeID; } +#endif bool IsDisabled( void ) const { return (m_NodeData.iDisabled != 0); } void SetDisabled( bool bDisabled ) { m_NodeData.iDisabled = bDisabled; } void DisableForSeconds( float flSeconds ); @@ -305,11 +331,21 @@ public: int GetNodeId() { return m_NodeData.nNodeID; } int GetWCId() { return m_NodeData.nWCNodeID; } +#ifdef MAPBASE + int GetRadius() const { return m_NodeData.nRadius; } // From Alien Swarm SDK + + float GetHintWeight() const { return m_NodeData.flWeight; } + float GetHintWeightInverse() const { return m_NodeData.flWeightInverse; } // Used to multiply distances +#endif + 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 ); + + // Returns true if this hint should override a NPC's yaw even during regular AI. + bool OverridesNPCYaw( CAI_BaseNPC *pNPC ); #endif #ifdef MAPBASE_VSCRIPT @@ -319,7 +355,9 @@ public: const char* ScriptGetHintActivity() { return STRING( HintActivityName() ); } #endif +#ifndef MAPBASE private: +#endif void Spawn( void ); virtual void Activate(); virtual void UpdateOnRemove( void ); diff --git a/mp/src/game/server/ai_initutils.cpp b/mp/src/game/server/ai_initutils.cpp index 8c476719..cd792798 100644 --- a/mp/src/game/server/ai_initutils.cpp +++ b/mp/src/game/server/ai_initutils.cpp @@ -173,6 +173,10 @@ BEGIN_SIMPLE_DATADESC( HintNodeData ) DEFINE_KEYFIELD( fIgnoreFacing, FIELD_INTEGER, "IgnoreFacing" ), DEFINE_KEYFIELD( minState, FIELD_INTEGER, "MinimumState" ), DEFINE_KEYFIELD( maxState, FIELD_INTEGER, "MaximumState" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( nRadius, FIELD_INTEGER, "radius" ), // From Alien Swarm SDK + DEFINE_KEYFIELD( flWeight, FIELD_FLOAT, "hintweight" ), +#endif END_DATADESC() @@ -205,6 +209,17 @@ int CNodeEnt::Spawn( const char *pMapData ) m_NodeData.minState = NPC_STATE_IDLE; if ( m_NodeData.maxState == NPC_STATE_NONE ) m_NodeData.maxState = NPC_STATE_COMBAT; +#ifdef MAPBASE + if (m_NodeData.flWeight == 0.0f) + { + m_NodeData.flWeight = 1.0f; + } + else if (m_NodeData.flWeight != 1.0f) + { + // Invert the weight so that it could be used as a direct multiplier for distances, etc. + m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight; + } +#endif // --------------------------------------------------------------------------------- // If just a hint node (not used for navigation) just create a hint and bail // --------------------------------------------------------------------------------- @@ -227,7 +242,11 @@ int CNodeEnt::Spawn( const char *pMapData ) // --------------------------------------------------------------------------------- CAI_Hint *pHint = NULL; - if ( ClassMatches( "info_node_hint" ) || ClassMatches( "info_node_air_hint" ) ) + if ( ClassMatches( "info_node_hint" ) || ClassMatches( "info_node_air_hint" ) +#ifdef MAPBASE + || ClassMatches( "info_node_climb" ) // Climb nodes contain hint data in the FGD +#endif + ) { if ( m_NodeData.nHintType || m_NodeData.strGroup != NULL_STRING || m_NodeData.strEntityName != NULL_STRING ) { diff --git a/mp/src/game/server/ai_initutils.h b/mp/src/game/server/ai_initutils.h index 22aaf5f3..70bd019c 100644 --- a/mp/src/game/server/ai_initutils.h +++ b/mp/src/game/server/ai_initutils.h @@ -42,6 +42,11 @@ struct HintNodeData HintIgnoreFacing_t fIgnoreFacing; NPC_STATE minState; NPC_STATE maxState; +#ifdef MAPBASE + int nRadius; // From Alien Swarm SDK + float flWeight; + float flWeightInverse; // Not saved +#endif int nWCNodeID; // Node ID assigned by worldcraft (not same as engine!) diff --git a/mp/src/game/server/ai_memory.cpp b/mp/src/game/server/ai_memory.cpp index 0af73a89..7ac69311 100644 --- a/mp/src/game/server/ai_memory.cpp +++ b/mp/src/game/server/ai_memory.cpp @@ -146,29 +146,6 @@ 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 faa482a2..d348c53e 100644 --- a/mp/src/game/server/ai_memory.h +++ b/mp/src/game/server/ai_memory.h @@ -45,29 +45,6 @@ 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_motor.cpp b/mp/src/game/server/ai_motor.cpp index 49f3d5f4..43eb86a3 100644 --- a/mp/src/game/server/ai_motor.cpp +++ b/mp/src/game/server/ai_motor.cpp @@ -14,6 +14,9 @@ #include "ai_basenpc.h" #include "ai_localnavigator.h" #include "ai_moveprobe.h" +#ifdef MAPBASE +#include "ai_hint.h" +#endif #include "saverestore_utlvector.h" // memdbgon must be the last include file in a .cpp file!!! @@ -235,18 +238,47 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir // > code are not reciprocal for all state, and furthermore, stomp // > other state? + bool bGoingUp = (climbDir.z > 0.01); +#if EXPANDED_NAVIGATION_ACTIVITIES + if ( bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_BOTTOM ) ) + { + SetActivity( ACT_CLIMB_MOUNT_BOTTOM ); + + // Steal m_vecDismount for this + GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount ); + GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) ); + } + else if ( !bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_TOP ) ) + { + SetActivity( ACT_CLIMB_MOUNT_TOP ); + + // Steal m_vecDismount for this + GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount ); + GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) ); + } + else +#endif if ( fabsf( climbDir.z ) < .1 ) { SetActivity( GetNavigator()->GetMovementActivity() ); } else { - SetActivity( (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN ); + SetActivity( bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN ); } m_nDismountSequence = SelectWeightedSequence( ACT_CLIMB_DISMOUNT ); if (m_nDismountSequence != ACT_INVALID) { +#if EXPANDED_NAVIGATION_ACTIVITIES + if ( !bGoingUp ) + { + int nBottomDismount = SelectWeightedSequence( ACT_CLIMB_DISMOUNT_BOTTOM ); + if (nBottomDismount != ACTIVITY_NOT_AVAILABLE) + m_nDismountSequence = nBottomDismount; + } +#endif + GetOuter()->GetSequenceLinearMotion( m_nDismountSequence, &m_vecDismount ); } else @@ -262,6 +294,76 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft ) { +#if EXPANDED_NAVIGATION_ACTIVITIES + if ( (GetActivity() == ACT_CLIMB_MOUNT_TOP || GetActivity() == ACT_CLIMB_MOUNT_BOTTOM) ) + { + if (!GetOuter()->IsActivityFinished()) + { + // Wait for the mounting to finish + SetGroundEntity( NULL ); + } + else + { + // Fix up our position if we have to + Vector vecTeleportOrigin; + if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin )) + { + SetLocalOrigin( vecTeleportOrigin ); + } + + // Reset activity and start from the beginning + GetOuter()->ResetActivity(); + return MoveClimbExecute( climbDest, climbDir, climbDist, yaw, climbNodesLeft ); + } + } + else if ( fabsf( climbDir.z ) > .1 && (GetActivity() != ACT_CLIMB_DISMOUNT && GetActivity() != ACT_CLIMB_DISMOUNT_BOTTOM) ) + { + bool bGoingUp = (climbDir.z > -0.01); + if ( GetOuter()->HaveSequenceForActivity( ACT_CLIMB_ALL ) ) + { + SetActivity( ACT_CLIMB_ALL ); + + // TODO: Use UTIL_VecToPitch() instead if move_yaw becomes a true climb yaw and not just an up-down scalar + SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), climbDir.z < 0 ? 180.0 : -180.0 ); + } + else + { + Activity desiredActivity = bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN; + if ( GetActivity() != desiredActivity ) + { + SetActivity( desiredActivity ); + } + } + + if (m_nDismountSequence != ACT_INVALID) + { + if (climbNodesLeft <= 2 && climbDist < fabs( m_vecDismount.z )) + { + if (bGoingUp) + { + // fixme: No other way to force m_nIdealSequence? + GetOuter()->SetActivity( ACT_CLIMB_DISMOUNT ); + GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) ); + } + else + { + if (GetSequence() != m_nDismountSequence && GetOuter()->GetSequenceActivity( m_nDismountSequence ) == ACT_CLIMB_DISMOUNT_BOTTOM) + { + SetActivity( ACT_CLIMB_DISMOUNT_BOTTOM ); + } + } + } + } + } + else if ( climbDir.Length() == 0 && GetOuter()->GetInstantaneousVelocity() <= 0.01 ) + { + // The NPC is somehow stuck climbing with no direction or movement. + // This can be caused by NPCs getting stuck in each other and/or being moved away from the ladder. + // In these cases, the NPC has to be made unstuck, or else they may remain in an immobile climbing state forever. + Warning( "%s had to abort climbing due to no direction or movement\n", GetOuter()->GetDebugName() ); + return AIMR_ILLEGAL; + } +#else if ( fabsf( climbDir.z ) > .1 ) { if ( GetActivity() != ACT_CLIMB_DISMOUNT ) @@ -292,13 +394,34 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto } } } +#endif float climbSpeed = GetOuter()->GetInstantaneousVelocity(); if (m_nDismountSequence != ACT_INVALID) { // catch situations where the climb mount/dismount finished before reaching goal +#if EXPANDED_NAVIGATION_ACTIVITIES + if ((GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_DISMOUNT_BOTTOM)) + { + SetGroundEntity( NULL ); + + if (GetOuter()->IsActivityFinished()) + { + // Fix up our position if we have to + Vector vecTeleportOrigin; + if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin )) + { + // Just force it to complete + climbDist = 0.0f; + } + + climbSpeed = 200.0f; + } + } +#else climbSpeed = MAX( climbSpeed, 30.0 ); +#endif } else { @@ -314,7 +437,7 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto climbDist = 0; const float climbTime = climbDist / climbSpeed; - + SetMoveInterval( GetMoveInterval() - climbTime ); SetLocalOrigin( climbDest ); @@ -330,6 +453,20 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto // -------------------------------------------- SetIdealYawAndUpdate( yaw ); +#ifdef MAPBASE + // Lock the yaw if we're in position + if ( UTIL_AngleMod( yaw ) == UTIL_AngleMod( GetLocalAngles().y ) ) + { + SetYawLocked( true ); + } + else if ( IsYawLocked() ) + { + // We're in a different position now. Unlock the yaw and update it + SetYawLocked( false ); + UpdateYaw( -1 ); + } +#endif + return AIMR_OK; } @@ -340,11 +477,62 @@ void CAI_Motor::MoveClimbStop() else SetActivity( ACT_IDLE ); +#ifdef MAPBASE + // Unlock desired weapon state so NPCs can unholster their weapons again. + GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_IGNORE ); + + // Unlock yaw + SetYawLocked( false ); +#endif + GetOuter()->RemoveFlag( FL_FLY ); SetSmoothedVelocity( vec3_origin ); SetGravity( 1.0 ); } +#ifdef MAPBASE +void CAI_Motor::MoveClimbPause() +{ + if (GetActivity() != ACT_CLIMB_DISMOUNT +#if EXPANDED_NAVIGATION_ACTIVITIES + && GetActivity() != ACT_CLIMB_MOUNT_TOP && GetActivity() != ACT_CLIMB_MOUNT_BOTTOM +#endif + ) + { + if ( GetActivity() == ACT_CLIMB_ALL ) + { + SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), 0.0f ); + } + + SetSmoothedVelocity( vec3_origin ); + } + else + { + // If already dismounting, do nothing + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is part of a hack needed in cases where ladder mount/dismount animations collide with the world and don't move properly. +// It's based off of the same code as scripted_sequence's teleportation fixup, although this function only resolves the bone origin and +// returns whether or not teleportation is necessary, as the teleportation is achieved in different ways for different uses of this code. +//----------------------------------------------------------------------------- +bool CAI_Motor::MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin ) +{ + QAngle new_angle; + GetOuter()->GetBonePosition( 0, teleportOrigin, new_angle ); + + // Ensure that there is actually a distance needed to teleport there + if ((GetLocalOrigin() - teleportOrigin).Length2DSqr() > Square( 8.0 )) + { + teleportOrigin.z = GetLocalOrigin().z; + return true; + } + + return false; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Motion for jumping // Input : @@ -651,9 +839,21 @@ void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move ) { // FIXME: move this up to navigator so that path goals can ignore these overrides. Vector dir; - float flInfluence = GetFacingDirection( dir ); - dir = move.facing * (1 - flInfluence) + dir * flInfluence; - VectorNormalize( dir ); + +#ifdef MAPBASE + if (IsDeceleratingToGoal() && (GetOuter()->GetHintNode() /*|| GetOuter()->m_hOpeningDoor*/)) + { + // Don't let the facing queue interfere with arrival direction in important cases + dir = move.facing; + VectorNormalize( dir ); + } + else +#endif + { + float flInfluence = GetFacingDirection( dir ); + dir = move.facing * (1 - flInfluence) + dir * flInfluence; + VectorNormalize( dir ); + } // ideal facing direction float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) ); diff --git a/mp/src/game/server/ai_motor.h b/mp/src/game/server/ai_motor.h index d7f14293..29c05f84 100644 --- a/mp/src/game/server/ai_motor.h +++ b/mp/src/game/server/ai_motor.h @@ -62,6 +62,10 @@ public: virtual void MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw ); virtual AIMoveResult_t MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft ); virtual void MoveClimbStop(); +#ifdef MAPBASE + virtual void MoveClimbPause(); + virtual bool MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin ); +#endif //--------------------------------- @@ -83,6 +87,9 @@ public: const Vector & GetCurVel() const { return m_vecVelocity; } virtual float OverrideMaxYawSpeed( Activity activity ) { return -1; } +#ifdef MAPBASE + virtual +#endif bool IsDeceleratingToGoal() const { return false; } //--------------------------------- diff --git a/mp/src/game/server/ai_navigator.cpp b/mp/src/game/server/ai_navigator.cpp index d136c923..dacab2aa 100644 --- a/mp/src/game/server/ai_navigator.cpp +++ b/mp/src/game/server/ai_navigator.cpp @@ -999,12 +999,23 @@ int CAI_Navigator::GetArrivalSequence( int curSequence ) activity = ACT_IDLE; } - sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( activity ), curSequence ); + Activity translatedActivity = GetOuter()->TranslateActivity( activity ); + sequence = GetOuter()->SelectWeightedSequence( translatedActivity, curSequence ); if ( sequence == ACT_INVALID ) { - DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() ); - sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence ); +#ifdef MAPBASE + if ( translatedActivity == ACT_SCRIPT_CUSTOM_MOVE ) + { + // ACT_SCRIPT_CUSTOM_MOVE allows activity translation to resolve into specific sequences + sequence = GetOuter()->GetScriptCustomMoveSequence(); + } + else +#endif + { + DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() ); + sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence ); + } } Assert( sequence != ACT_INVALID ); GetPath()->SetArrivalSequence( sequence ); @@ -1642,6 +1653,15 @@ void CAI_Navigator::MoveCalcBaseGoal( AILocalMoveGoal_t *pMoveGoal ) AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint(); if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() != pCurWaypoint->NavType() ) pMoveGoal->flags |= AILMG_TARGET_IS_TRANSITION; + +#ifdef MAPBASE + // TODO: Better place for this code? + if (pMoveGoal->flags & AILMG_TARGET_IS_TRANSITION && pCurWaypoint->GetNext()->NavType() == NAV_CLIMB) + { + // NPCs need to holster their weapons before climbing. + GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED ); + } +#endif } const Task_t *pCurTask = GetOuter()->GetTask(); @@ -2164,11 +2184,26 @@ bool CAI_Navigator::OnMoveBlocked( AIMoveResult_t *pResult ) if (pDoor != NULL) { GetOuter()->OpenPropDoorBegin( pDoor ); +#ifdef MAPBASE + // Tell the navigation to stop running until we're done. + OnNewGoal(); +#endif *pResult = AIMR_OK; return true; } } +#ifdef MAPBASE + if ( GetOuter()->m_hOpeningDoor ) + { + // In the process of opening a door + // Because navigation is now supposed to terminate when a NPC begins opening a door, this code should not be reached. + DbgNavMsg( GetOuter(), "CAI_Navigator::OnMoveBlocked had to check for m_hOpeningDoor\n" ); + *pResult = AIMR_OK; + return true; + } +#endif + // Allow the NPC to override this behavior if ( GetOuter()->OnMoveBlocked( pResult )) @@ -2591,8 +2626,12 @@ bool CAI_Navigator::Move( float flInterval ) if ( GetNavType() == NAV_CLIMB ) { +#ifdef MAPBASE + GetMotor()->MoveClimbPause(); +#else GetMotor()->MoveClimbStop(); SetNavType( NAV_GROUND ); +#endif } GetMotor()->MoveStop(); AssertMsg( TaskIsRunning() || TaskIsComplete(), ("Schedule stalled!!\n") ); @@ -2695,6 +2734,11 @@ void CAI_Navigator::AdvancePath() if (pDoor != NULL) { GetOuter()->OpenPropDoorBegin(pDoor); + +#ifdef MAPBASE + // Tell the navigation to stop running until we're done. + OnNewGoal(); +#endif } else { @@ -3880,7 +3924,12 @@ bool CAI_Navigator::GetStoppingPath( CAI_WaypointList * pClippedWaypoints ) AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint(); if ( pCurWaypoint ) { +#if EXPANDED_NAVIGATION_ACTIVITIES + // Since regular climb nav can interrupt itself now, only do this when dismounting + bool bMustCompleteCurrent = ( (pCurWaypoint->NavType() == NAV_CLIMB && (GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_MOUNT_TOP)) || pCurWaypoint->NavType() == NAV_JUMP ); +#else bool bMustCompleteCurrent = ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP ); +#endif float distRemaining = GetMotor()->MinStoppingDist( 0 ); if ( bMustCompleteCurrent ) diff --git a/mp/src/game/server/ai_network.cpp b/mp/src/game/server/ai_network.cpp index 16d8a7fa..1323b7ce 100644 --- a/mp/src/game/server/ai_network.cpp +++ b/mp/src/game/server/ai_network.cpp @@ -144,6 +144,31 @@ public: int m_capabilities; // cache this }; +#ifdef MAPBASE +//------------------------------------- +// Purpose: A version of CNodeFilter which allows hints to influence the result. +//------------------------------------- +class CNodeHintFilter : public CNodeFilter +{ +public: + CNodeHintFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : CNodeFilter( pNPC, pos ) {} + CNodeHintFilter( const Vector &pos ) : CNodeFilter( pos ) {} + + virtual float NodeDistanceSqr( CAI_Node &node ) + { + // Heavier hints are considered closer + if ( node.GetHint() && node.GetHint()->GetHintWeight() != 1.0f && (node.GetHint()->GetGroup() == NULL_STRING || node.GetHint()->GetGroup() == m_pNPC->GetHintGroup()) ) + { + return CNodeFilter::NodeDistanceSqr( node ) * node.GetHint()->GetHintWeightInverse(); + } + else + { + return CNodeFilter::NodeDistanceSqr( node ); + } + } +}; +#endif + //----------------------------------------------------------------------------- // CAI_Network //----------------------------------------------------------------------------- @@ -351,7 +376,12 @@ int CAI_Network::NearestNodeToPoint( CAI_BaseNPC *pNPC, const Vector &vecOrigin, // First get nodes distances and eliminate those that are beyond // the maximum allowed distance for local movements // --------------------------------------------------------------- +#ifdef MAPBASE + // Allow hint weight to influence supposed distance + CNodeHintFilter filter( pNPC, vecOrigin ); +#else CNodeFilter filter( pNPC, vecOrigin ); +#endif #ifdef AI_PERF_MON m_nPerfStatNN++; @@ -605,8 +635,13 @@ CAI_Node *CAI_Network::AddNode( const Vector &origin, float yaw ) CAI_Link *CAI_Network::CreateLink( int srcID, int destID, CAI_DynamicLink *pDynamicLink ) { +#ifdef MAPBASE // From Alien Swarm SDK + CAI_Node *pSrcNode = GetNode( srcID ); + CAI_Node *pDestNode = GetNode( destID ); +#else CAI_Node *pSrcNode = g_pBigAINet->GetNode( srcID ); CAI_Node *pDestNode = g_pBigAINet->GetNode( destID ); +#endif Assert( pSrcNode && pDestNode && pSrcNode != pDestNode ); diff --git a/mp/src/game/server/ai_networkmanager.cpp b/mp/src/game/server/ai_networkmanager.cpp index 8f470a47..10fb51ff 100644 --- a/mp/src/game/server/ai_networkmanager.cpp +++ b/mp/src/game/server/ai_networkmanager.cpp @@ -25,6 +25,9 @@ #include "ndebugoverlay.h" #include "ai_hint.h" #include "tier0/icommandline.h" +#ifdef MAPBASE +#include "gameinterface.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -71,6 +74,11 @@ CON_COMMAND( ai_debug_node_connect, "Debug the attempted connection between two 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" ); + +ConVar g_ai_norebuildgraph_if_in_chapters( "ai_norebuildgraph_if_in_chapters", "0", FCVAR_NONE, "Ignores rebuilding nodegraph if it's in chapters.txt. This allows for bypassing problems with Steam rebuilding nodegraphs in a mod's main maps without affecting custom maps." ); + +extern CUtlVector *Mapbase_GetChapterMaps(); +extern CUtlVector *Mapbase_GetChapterList(); #endif @@ -986,6 +994,24 @@ bool CAI_NetworkManager::IsAIFileCurrent ( const char *szMapName ) // dvd build process validates and guarantees correctness, timestamps are allowed to be wrong return true; } + +#ifdef MAPBASE + if (g_ai_norebuildgraph_if_in_chapters.GetBool()) + { + // Look in the mod's chapter list. If this map is part of one of the chapters, consider it to have a good node graph + CUtlVector *ModChapterComments = Mapbase_GetChapterMaps(); + if (ModChapterComments->Count() > 0) + { + for ( int i = 0; i < ModChapterComments->Count(); i++ ) + { + if ( !Q_strnicmp( STRING(gpGlobals->mapname), ModChapterComments->Element(i).pBSPName, strlen(ModChapterComments->Element(i).pBSPName) ) ) + { + return true; + } + } + } + } +#endif { const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" ); @@ -3059,6 +3085,16 @@ int CAI_NetworkBuilder::ComputeConnection( CAI_Node *pSrcNode, CAI_Node *pDestNo } else { +#ifdef MAPBASE + // This is kind of a hack since target node IDs are designed to be used *after* the nodegraph is generated. + // However, for the purposes of forcing a climb connection outside of regular lineup bounds, it seems to be a reasonable solution. + if (pSrcNode->GetHint() && pDestNode->GetHint() && + (pSrcNode->GetHint()->GetTargetWCNodeID() == pDestNode->GetHint()->GetWCId() || pDestNode->GetHint()->GetTargetWCNodeID() == pSrcNode->GetHint()->GetWCId())) + { + DebugConnectMsg( srcId, destId, " Ignoring climbing lineup due to manual target ID linkage\n" ); + } + else +#endif if ( !IsInLineForClimb(srcPos, UTIL_YawToVector( pSrcNode->m_flYaw ), destPos, UTIL_YawToVector( pDestNode->m_flYaw ) ) ) { Assert( !IsInLineForClimb(destPos, UTIL_YawToVector( pDestNode->m_flYaw ), srcPos, UTIL_YawToVector( pSrcNode->m_flYaw ) ) ); diff --git a/mp/src/game/server/ai_playerally.cpp b/mp/src/game/server/ai_playerally.cpp index fabbbee1..9f5a3524 100644 --- a/mp/src/game/server/ai_playerally.cpp +++ b/mp/src/game/server/ai_playerally.cpp @@ -137,12 +137,23 @@ bool ConceptStringLessFunc( const string_t &lhs, const string_t &rhs ) return CaselessStringLessThan( STRING(lhs), STRING(rhs) ); } +#ifdef NEW_RESPONSE_SYSTEM +bool ConceptInfoStringLessFunc( const AIConcept_t& lhs, const AIConcept_t& rhs ) +{ + return CaselessStringLessThan( lhs.GetStringConcept(), rhs.GetStringConcept() ); +} +#endif + //----------------------------------------------------------------------------- class CConceptInfoMap : public CUtlMap { public: CConceptInfoMap() : +#ifdef NEW_RESPONSE_SYSTEM + CUtlMap( ConceptInfoStringLessFunc ) +#else CUtlMap( CaselessStringLessThan ) +#endif { for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ ) { @@ -558,7 +569,11 @@ void CAI_PlayerAlly::PrescheduleThink( void ) if ( SelectNonCombatSpeech( &selection ) ) { SetSpeechTarget( selection.hSpeechTarget ); +#ifdef NEW_RESPONSE_SYSTEM + SpeakDispatchResponse( selection.concept.c_str(), &selection.Response ); +#else SpeakDispatchResponse( selection.concept.c_str(), selection.Response ); +#endif m_flNextIdleSpeechTime = gpGlobals->curtime + RandomFloat( 20,30 ); } else @@ -698,7 +713,11 @@ bool CAI_PlayerAlly::SelectInterjection() if ( SelectIdleSpeech( &selection ) ) { SetSpeechTarget( selection.hSpeechTarget ); +#ifdef NEW_RESPONSE_SYSTEM + SpeakDispatchResponse( selection.concept.c_str(), &selection.Response ); +#else SpeakDispatchResponse( selection.concept.c_str(), selection.Response ); +#endif return true; } } @@ -763,11 +782,11 @@ void CAI_PlayerAlly::PostSpeakDispatchResponse( AIConcept_t concept, AI_Response { if ( bSaidHelloToNPC ) { - Warning("Q&A: '%s' said Hello to '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), concept ); + Warning("Q&A: '%s' said Hello to '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), (const char*)concept ); } else { - Warning("Q&A: '%s' questioned '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), concept ); + Warning("Q&A: '%s' questioned '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), (const char*)concept ); } NDebugOverlay::HorzArrow( GetAbsOrigin(), GetSpeechTarget()->GetAbsOrigin(), 8, 0, 255, 0, 64, true, duration ); } @@ -883,6 +902,18 @@ bool CAI_PlayerAlly::AskQuestionNow( CBaseEntity *pSpeechTarget, int iQARandomNu m_iQARandomNumber = RandomInt(0, 100); AISpeechSelection_t selection; +#ifdef NEW_RESPONSE_SYSTEM + if (SelectSpeechResponse( concept, NULL, m_hPotentialSpeechTarget.Get(), &selection )) + { + SetSpeechTarget( selection.hSpeechTarget ); + ClearPendingSpeech(); + + // Speak immediately + return SpeakDispatchResponse( selection.concept.c_str(), &selection.Response ); + } + + return false; +#else SelectSpeechResponse( concept, NULL, m_hPotentialSpeechTarget.Get(), &selection ); SetSpeechTarget( selection.hSpeechTarget ); @@ -890,6 +921,7 @@ bool CAI_PlayerAlly::AskQuestionNow( CBaseEntity *pSpeechTarget, int iQARandomNu // Speak immediately return SpeakDispatchResponse( selection.concept.c_str(), selection.Response ); +#endif } //----------------------------------------------------------------------------- @@ -978,7 +1010,11 @@ void CAI_PlayerAlly::AnswerQuestion( CAI_PlayerAlly *pQuestioner, int iQARandomN } SetSpeechTarget( selection.hSpeechTarget ); +#ifdef NEW_RESPONSE_SYSTEM + SpeakDispatchResponse( selection.concept.c_str(), &selection.Response ); +#else SpeakDispatchResponse( selection.concept.c_str(), selection.Response ); +#endif // Prevent idle speech for a while DeferAllIdleSpeech( random->RandomFloat( TALKER_DEFER_IDLE_SPEAK_MIN, TALKER_DEFER_IDLE_SPEAK_MAX ), GetSpeechTarget()->MyNPCPointer() ); @@ -1105,7 +1141,11 @@ void CAI_PlayerAlly::StartTask( const Task_t *pTask ) case TASK_TALKER_SPEAK_PENDING: if ( !m_PendingConcept.empty() ) { +#ifdef NEW_RESPONSE_SYSTEM + SpeakDispatchResponse( m_PendingConcept.c_str(), &m_PendingResponse ); +#else SpeakDispatchResponse( m_PendingConcept.c_str(), m_PendingResponse ); +#endif m_PendingConcept.erase(); TaskComplete(); } @@ -1842,7 +1882,7 @@ bool CAI_PlayerAlly::RespondedTo( const char *ResponseConcept, bool bForce, bool { // We're being forced to respond to the event, probably because it's the // player dying or something equally important. - AI_Response response; + AI_Response response; bool result = SpeakFindResponse( response, ResponseConcept, NULL ); if ( result ) { @@ -1850,7 +1890,11 @@ bool CAI_PlayerAlly::RespondedTo( const char *ResponseConcept, bool bForce, bool if ( bCancelScene ) RemoveActorFromScriptedScenes( this, false ); +#ifdef NEW_RESPONSE_SYSTEM + return SpeakDispatchResponse( ResponseConcept, &response ); +#else return SpeakDispatchResponse( ResponseConcept, response ); +#endif } return false; diff --git a/mp/src/game/server/ai_speech.cpp b/mp/src/game/server/ai_speech.cpp index 370f6f17..821e6da1 100644 --- a/mp/src/game/server/ai_speech.cpp +++ b/mp/src/game/server/ai_speech.cpp @@ -1069,6 +1069,32 @@ void CAI_Expresser::ClearSpokeConcept( AIConcept_t concept ) m_ConceptHistories.Remove( concept ); } +#ifdef MAPBASE +//------------------------------------- + +AIConcept_t CAI_Expresser::GetLastSpokeConcept( AIConcept_t excludeConcept /* = NULL */ ) +{ + int iLastSpokenIndex = m_ConceptHistories.InvalidIndex(); + float flLast = 0.0f; + for ( int i = m_ConceptHistories.First(); i != m_ConceptHistories.InvalidIndex(); i = m_ConceptHistories.Next(i ) ) + { + ConceptHistory_t *h = &m_ConceptHistories[ i ]; + + // If an 'exclude concept' was provided, skip over this entry in the history if it matches the exclude concept + if ( excludeConcept != NULL && FStrEq( m_ConceptHistories.GetElementName( i ), excludeConcept ) ) + continue; + + if ( h->timeSpoken >= flLast ) + { + iLastSpokenIndex = i; + flLast = h->timeSpoken; + } + } + + return iLastSpokenIndex != m_ConceptHistories.InvalidIndex() ? m_ConceptHistories.GetElementName( iLastSpokenIndex ) : NULL; +} +#endif + //------------------------------------- void CAI_Expresser::DumpHistories() @@ -1126,7 +1152,7 @@ void CAI_Expresser::SpeechMsg( CBaseEntity *pFlex, const char *pszFormat, ... ) } else { - CGMsg( 1, CON_GROUP_CHOREO "%s", string ); + CGMsg( 1, CON_GROUP_CHOREO, "%s", string ); } UTIL_LogPrintf( "%s", string ); } @@ -1156,7 +1182,7 @@ const char *CAI_Expresser::ParseApplyContext( const char *szContext ) { // 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); + return szContext; } // This is the existing value; will be operated upon and become the final assignment diff --git a/mp/src/game/server/ai_speech.h b/mp/src/game/server/ai_speech.h index 87b74e07..dd605fc8 100644 --- a/mp/src/game/server/ai_speech.h +++ b/mp/src/game/server/ai_speech.h @@ -5,6 +5,9 @@ // $NoKeywords: $ //=============================================================================// +#ifdef NEW_RESPONSE_SYSTEM +#include "ai_speech_new.h" +#else #ifndef AI_SPEECH_H #define AI_SPEECH_H @@ -184,6 +187,10 @@ public: bool CanSpeakAfterMyself(); float GetTimeSpeechComplete() const { return m_flStopTalkTime; } void BlockSpeechUntil( float time ); + +#ifdef MAPBASE + float GetRealTimeSpeechComplete() const { return m_flStopTalkTimeWithoutDelay; } +#endif // -------------------------------- @@ -192,6 +199,10 @@ public: float GetTimeSpokeConcept( AIConcept_t concept ); // returns -1 if never void SetSpokeConcept( AIConcept_t concept, AI_Response *response, bool bCallback = true ); void ClearSpokeConcept( AIConcept_t concept ); + +#ifdef MAPBASE + AIConcept_t GetLastSpokeConcept( AIConcept_t excludeConcept = NULL ); +#endif // -------------------------------- @@ -452,3 +463,4 @@ inline void CAI_ExpresserHost::DispatchResponse( const char *conceptNa //----------------------------------------------------------------------------- #endif // AI_SPEECH_H +#endif diff --git a/mp/src/game/server/ai_speech_new.cpp b/mp/src/game/server/ai_speech_new.cpp new file mode 100644 index 00000000..d29b6b39 --- /dev/null +++ b/mp/src/game/server/ai_speech_new.cpp @@ -0,0 +1,1777 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "ai_speech.h" + +#include "game.h" +#include "engine/IEngineSound.h" +#include "KeyValues.h" +#include "ai_basenpc.h" +#include "AI_Criteria.h" +#include "isaverestore.h" +#include "sceneentity.h" +#include "ai_speechqueue.h" +#ifdef MAPBASE +#include "ai_squad.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#define DEBUG_AISPEECH 1 +#ifdef DEBUG_AISPEECH +ConVar ai_debug_speech( "ai_debug_speech", "0" ); +#define DebuggingSpeech() ai_debug_speech.GetBool() +#else +inline void SpeechMsg( ... ) {} +#define DebuggingSpeech() (false) +#endif + +extern ConVar rr_debugresponses; + +#ifdef MAPBASE +ConVar ai_speech_print_mode( "ai_speech_print_mode", "1", FCVAR_NONE, "Set this value to 1 to print responses as game_text instead of debug point_message-like text." ); +#endif + +//----------------------------------------------------------------------------- + +CAI_TimedSemaphore g_AIFriendliesTalkSemaphore; +CAI_TimedSemaphore g_AIFoesTalkSemaphore; + +ConceptHistory_t::~ConceptHistory_t() +{ +} + +ConceptHistory_t::ConceptHistory_t( const ConceptHistory_t& src ) +{ + timeSpoken = src.timeSpoken; + m_response = src.m_response ; +} + +ConceptHistory_t& ConceptHistory_t::operator =( const ConceptHistory_t& src ) +{ + if ( this == &src ) + return *this; + + timeSpoken = src.timeSpoken; + m_response = src.m_response ; + + return *this; +} + +BEGIN_SIMPLE_DATADESC( ConceptHistory_t ) + DEFINE_FIELD( timeSpoken, FIELD_TIME ), // Relative to server time + // DEFINE_EMBEDDED( response, FIELD_??? ), // This is manually saved/restored by the ConceptHistory saverestore ops below +END_DATADESC() + +class CConceptHistoriesDataOps : public CDefSaveRestoreOps +{ +public: + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) + { + CUtlDict< ConceptHistory_t, int > *ch = ((CUtlDict< ConceptHistory_t, int > *)fieldInfo.pField); + int count = ch->Count(); + pSave->WriteInt( &count ); + for ( int i = 0 ; i < count; i++ ) + { + ConceptHistory_t *pHistory = &(*ch)[ i ]; + + pSave->StartBlock(); + { + + // Write element name + pSave->WriteString( ch->GetElementName( i ) ); + + // Write data + pSave->WriteAll( pHistory ); + // Write response blob + bool hasresponse = !pHistory->m_response.IsEmpty() ; + pSave->WriteBool( &hasresponse ); + if ( hasresponse ) + { + pSave->WriteAll( &pHistory->m_response ); + } + // TODO: Could blat out pHistory->criteria pointer here, if it's needed + } + pSave->EndBlock(); + } + } + + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) + { + CUtlDict< ConceptHistory_t, int > *ch = ((CUtlDict< ConceptHistory_t, int > *)fieldInfo.pField); + + int count = pRestore->ReadInt(); + Assert( count >= 0 ); + for ( int i = 0 ; i < count; i++ ) + { + char conceptname[ 512 ]; + conceptname[ 0 ] = 0; + ConceptHistory_t history; + + pRestore->StartBlock(); + { + pRestore->ReadString( conceptname, sizeof( conceptname ), 0 ); + + pRestore->ReadAll( &history ); + + bool hasresponse = false; + + pRestore->ReadBool( &hasresponse ); + if ( hasresponse ) + { + history.m_response; + pRestore->ReadAll( &history.m_response ); + } + else + { + history.m_response.Invalidate(); + } + } + + pRestore->EndBlock(); + + // TODO: Could restore pHistory->criteria pointer here, if it's needed + + // Add to utldict + if ( conceptname[0] != 0 ) + { + ch->Insert( conceptname, history ); + } + else + { + Assert( !"Error restoring ConceptHistory_t, discarding!" ); + } + } + } + + virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + } + + virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + CUtlDict< ConceptHistory_t, int > *ch = ((CUtlDict< ConceptHistory_t, int > *)fieldInfo.pField); + return ch->Count() == 0 ? true : false; + } +}; + +CConceptHistoriesDataOps g_ConceptHistoriesSaveDataOps; + +///////////////////////////////////////////////// +// context operators +RR::CApplyContextOperator RR::sm_OpCopy(0); // " +RR::CIncrementOperator RR::sm_OpIncrement(2); // "++" +RR::CDecrementOperator RR::sm_OpDecrement(2); // "--" +#ifdef MAPBASE +RR::CMultiplyOperator RR::sm_OpMultiply(2); // "**" +RR::CDivideOperator RR::sm_OpDivide(2); // "/=" +#endif +RR::CToggleOperator RR::sm_OpToggle(1); // "!" + +#ifdef MAPBASE +// LEGACY - See below +RR::CIncrementOperator RR::sm_OpLegacyIncrement(1); // "+" +RR::CDecrementOperator RR::sm_OpLegacyDecrement(1); // "-" +RR::CMultiplyOperator RR::sm_OpLegacyMultiply(1); // "*" +RR::CDivideOperator RR::sm_OpLegacyDivide(1); // "/" +#endif + +RR::CApplyContextOperator *RR::CApplyContextOperator::FindOperator( const char *pContextString ) +{ + if ( !pContextString || pContextString[0] == 0 ) + { + return &sm_OpCopy; + } + +#ifdef MAPBASE + // This is one of those freak coincidences where Mapbase implemented its own context operators with no knowledge of context operators from later versions of the engine. + // Mapbase's context operators only required *one* operator character (e.g. '+' as opposed to '++') and supported multiplication and division. + // Although Valve's system now replaces Mapbase's system, multiplication and division have been added and maintaining the old syntax is needed for legacy support. + // That being said, it's believed that almost nobody knew that Mapbase supported context operators in the first place, so there might not be much legacy to support anyway. + switch (pContextString[0]) + { + case '+': + { + if (pContextString[1] != '+') + { + Warning( "\"%s\" needs another '+' to qualify as a proper operator. This code is regarding it as an operator anyway for legacy support, which might be going away soon!!!\n", pContextString ); + return &sm_OpLegacyIncrement; + } + else if (pContextString[2] == '\0') + break; + return &sm_OpIncrement; + } + case '-': + { + if (pContextString[1] != '-') + { + Warning( "\"%s\" needs another '-' to qualify as a proper operator. This code is regarding it as an operator anyway for legacy support, which might be going away soon!!!\n", pContextString ); + return &sm_OpLegacyDecrement; + } + else if (pContextString[2] == '\0') + break; + return &sm_OpIncrement; + } + case '*': + { + if (pContextString[1] != '*') + { + Warning( "\"%s\" needs another '*' to qualify as a proper operator. This code is regarding it as an operator anyway for legacy support, which might be going away soon!!!\n", pContextString ); + return &sm_OpLegacyMultiply; + } + else if (pContextString[2] == '\0') + break; + return &sm_OpMultiply; + } + case '/': + { + if (pContextString[1] != '=') + { + Warning( "\"%s\" needs a '=' after the '/' to qualify as a proper operator. This code is regarding it as an operator anyway for legacy support, which might be going away soon!!!\n", pContextString ); + return &sm_OpLegacyDivide; + } + else if (pContextString[2] == '\0') + break; + return &sm_OpDivide; + } break; + case '!': + { + return &sm_OpToggle; + } + } + + return &sm_OpCopy; +#else + if ( pContextString[0] == '+' && pContextString [1] == '+' && pContextString[2] != '\0' ) + { + return &sm_OpIncrement; + } + else if ( pContextString[0] == '-' && pContextString [1] == '-' && pContextString[2] != '\0' ) + { + return &sm_OpDecrement; + } +#ifdef MAPBASE + else if ( pContextString[0] == '*' && pContextString [1] == '*' && pContextString[2] != '\0' ) + { + return &sm_OpMultiply; + } + else if ( pContextString[0] == '/' && pContextString [1] == '=' && pContextString[2] != '\0' ) + { + return &sm_OpDivide; + } +#endif + else if ( pContextString[0] == '!' ) + { + return &sm_OpToggle; + } + else + { + return &sm_OpCopy; + } +#endif +} + +// default is just copy +bool RR::CApplyContextOperator::Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ) +{ + Assert( pOperator && pNewValue && pNewValBufSize > 0 ); + Assert( m_nSkipChars == 0 ); + if ( pOperator ) + { + V_strncpy( pNewValue, pOperator, pNewValBufSize ); + } + else + { + *pNewValue = 0; + } + return true; +} + +bool RR::CIncrementOperator::Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ) +{ + Assert( pOperator[0] == '+' && pOperator[1] == '+' ); + // parse out the old value as a numeric + int nOld = pOldValue ? V_atoi(pOldValue) : 0; + int nInc = V_atoi( pOperator+m_nSkipChars ); + V_snprintf( pNewValue, pNewValBufSize, "%d", nOld+nInc ); + return true; +} + +bool RR::CDecrementOperator::Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ) +{ + Assert( pOperator[0] == '-' && pOperator[1] == '-' ); + // parse out the old value as a numeric + int nOld = pOldValue ? V_atoi(pOldValue) : 0; + int nInc = V_atoi( pOperator+m_nSkipChars ); + V_snprintf( pNewValue, pNewValBufSize, "%d", nOld-nInc ); + return true; +} + +#ifdef MAPBASE +bool RR::CMultiplyOperator::Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ) +{ + Assert( pOperator[0] == '*' && pOperator[1] == '*' ); + // parse out the old value as a numeric + int nOld = pOldValue ? V_atoi(pOldValue) : 0; + int nInc = V_atoi( pOperator+m_nSkipChars ); + V_snprintf( pNewValue, pNewValBufSize, "%d", nOld*nInc ); + return true; +} + +bool RR::CDivideOperator::Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ) +{ + Assert( pOperator[0] == '/' && pOperator[1] == '=' ); + // parse out the old value as a numeric + int nOld = pOldValue ? V_atoi(pOldValue) : 0; + int nInc = V_atoi( pOperator+m_nSkipChars ); + if (nInc == 0) + V_strncpy( pNewValue, "0", pNewValBufSize ); + else + V_snprintf( pNewValue, pNewValBufSize, "%d", nOld/nInc ); + return true; +} +#endif + +bool RR::CToggleOperator::Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ) +{ + Assert( pOperator[0] == '!' ); + // parse out the old value as a numeric + int nOld = pOldValue ? V_atoi(pOldValue) : 0; + V_snprintf( pNewValue, pNewValBufSize, "%d", nOld ? 0 : 1 ); + return true; +} + + +//----------------------------------------------------------------------------- +// +// CLASS: CAI_Expresser +// + +BEGIN_SIMPLE_DATADESC( CAI_Expresser ) + // m_pSink (reconnected on load) +// DEFINE_FIELD( m_pOuter, CHandle < CBaseFlex > ), + DEFINE_CUSTOM_FIELD( m_ConceptHistories, &g_ConceptHistoriesSaveDataOps ), + DEFINE_FIELD( m_flStopTalkTime, FIELD_TIME ), + DEFINE_FIELD( m_flStopTalkTimeWithoutDelay, FIELD_TIME ), + DEFINE_FIELD( m_flBlockedTalkTime, FIELD_TIME ), + DEFINE_FIELD( m_voicePitch, FIELD_INTEGER ), + 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 ) +{ + if ( !GetSink()->UseSemaphore() ) + return true; + + CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( pTalker->MyNPCPointer() ); + return (pSemaphore ? pSemaphore->IsAvailable( pTalker ) : true); +} + +//------------------------------------- + +float CAI_Expresser::GetSemaphoreAvailableTime( CBaseEntity *pTalker ) +{ + CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( pTalker->MyNPCPointer() ); + return (pSemaphore ? pSemaphore->GetReleaseTime() : 0); +} + +//------------------------------------- + +int CAI_Expresser::GetVoicePitch() const +{ + return m_voicePitch + random->RandomInt(0,3); +} + +#ifdef DEBUG +static int g_nExpressers; +#endif + +/* +inline bool ShouldBeInExpresserQueue( CBaseFlex *pOuter ) +{ + return true; // return IsTerrorPlayer( pOuter, TEAM_SURVIVOR ); +} +*/ + +CAI_Expresser::CAI_Expresser( CBaseFlex *pOuter ) + : m_pOuter( pOuter ), + m_pSink( NULL ), + m_flStopTalkTime( 0 ), + m_flBlockedTalkTime( 0 ), + m_flStopTalkTimeWithoutDelay( 0 ), + m_voicePitch( 100 ), + m_flLastTimeAcceptedSpeak( 0 ) +{ +#ifdef DEBUG + g_nExpressers++; +#endif + if (m_pOuter) + { + // register me with the global expresser queue. + + // L4D: something a little ass backwards is happening here. We only want + // survivors to be in the queue. However, the team number isn't + // specified yet. So, we actually need to do this in the player's ChangeTeam. + g_ResponseQueueManager.GetQueue()->AddExpresserHost(m_pOuter); + + } +} + +CAI_Expresser::~CAI_Expresser() +{ + m_ConceptHistories.Purge(); + + CBaseFlex *RESTRICT outer = GetOuter(); + if ( outer ) + { + CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( outer ); + if ( pSemaphore ) + { + if ( pSemaphore->GetOwner() == outer ) + pSemaphore->Release(); + +#ifdef DEBUG + g_nExpressers--; + if ( g_nExpressers == 0 && pSemaphore->GetOwner() ) + CGMsg( 2, CON_GROUP_SPEECH_AI, "Speech semaphore being held by non-talker entity\n" ); +#endif + } + + g_ResponseQueueManager.GetQueue()->RemoveExpresserHost(outer); + } +} + +//----------------------------------------------------------------------------- +void CAI_Expresser::TestAllResponses() +{ + IResponseSystem *pResponseSystem = GetOuter()->GetResponseSystem(); + if ( pResponseSystem ) + { + CUtlVector responses; + pResponseSystem->GetAllResponses( &responses ); + for ( int i = 0; i < responses.Count(); i++ ) + { + char response[ 256 ]; + responses[i].GetResponse( response, sizeof( response ) ); + + Msg( "Response: %s\n", response ); + AIConcept_t concept; + SpeakDispatchResponse( concept, &responses[i], NULL ); + } + } +} + +//----------------------------------------------------------------------------- +void CAI_Expresser::SetOuter( CBaseFlex *pOuter ) +{ + // if we're changing outers (which is a strange thing to do) + // unregister the old one from the queue. + if ( m_pOuter && ( m_pOuter != pOuter ) ) + { + AssertMsg2( false, "Expresser is switching its Outer from %s to %s. Why?", m_pOuter->GetDebugName(), pOuter->GetDebugName() ); + // unregister me with the global expresser queue + g_ResponseQueueManager.GetQueue()->RemoveExpresserHost(m_pOuter); + } + + m_pOuter = pOuter; +} + +//----------------------------------------------------------------------------- + +static const int LEN_SPECIFIC_SCENE_MODIFIER = strlen( AI_SPECIFIC_SCENE_MODIFIER ); + + +// This function appends "Global world" criteria that are always added to +// any character doing any match. This represents global concepts like weather, who's +// alive, etc. +static void ModifyOrAppendGlobalCriteria( AI_CriteriaSet * RESTRICT outputSet ) +{ + return; +} + + +void CAI_Expresser::GatherCriteria( AI_CriteriaSet * RESTRICT outputSet, const AIConcept_t &concept, const char * RESTRICT modifiers ) +{ + // Always include the concept name + outputSet->AppendCriteria( "concept", concept, CONCEPT_WEIGHT ); + +#if 1 + outputSet->Merge( modifiers ); +#else + // Always include any optional modifiers + if ( modifiers != NULL ) + { + 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, modifiers ); + + if( *key && *value ) + { + outputSet->AppendCriteria( key, value, CONCEPT_WEIGHT ); + } + } + } +#endif + + // include any global criteria + ModifyOrAppendGlobalCriteria( outputSet ); + + // Let our outer fill in most match criteria + GetOuter()->ModifyOrAppendCriteria( *outputSet ); + + // 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( *outputSet ); + } + +#ifdef MAPBASE + GetOuter()->ReAppendContextCriteria( *outputSet ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Searches for a possible response +// Input : concept - +// NULL - +// Output : AI_Response +//----------------------------------------------------------------------------- +// AI_Response *CAI_Expresser::SpeakFindResponse( AIConcept_t concept, const char *modifiers /*= NULL*/ ) +bool CAI_Expresser::FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *criteria ) +{ + VPROF("CAI_Expresser::FindResponse"); + IResponseSystem *rs = GetOuter()->GetResponseSystem(); + if ( !rs ) + { + Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" ); + return NULL; + } + + // if I'm dead, I can't possibly match dialog. +#ifndef MAPBASE // Except for...you know...death sounds. + if ( !GetOuter()->IsAlive() ) + { + return false; + } +#endif + +#if 0 // this is the old technique, where we always gathered criteria in this function + AI_CriteriaSet set; + // Always include the concept name + set.AppendCriteria( "concept", concept, CONCEPT_WEIGHT ); + + // Always include any optional modifiers + if ( modifiers != NULL ) + { + 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, modifiers ); + + if( *key && *value ) + { + set.AppendCriteria( key, value, CONCEPT_WEIGHT ); + } + } + } + + // 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 ); + } +#else + AI_CriteriaSet localCriteriaSet; // put it on the stack so we don't deal with new/delete + if (criteria == NULL) + { + GatherCriteria( &localCriteriaSet, concept, NULL ); + criteria = &localCriteriaSet; + } +#endif + + /// intercept any deferred criteria that are being sent to world + AI_CriteriaSet worldWritebackCriteria; + AI_CriteriaSet::InterceptWorldSetContexts( criteria, &worldWritebackCriteria ); + + // Now that we have a criteria set, ask for a suitable response + bool found = rs->FindBestResponse( *criteria, outResponse, this ); + + if ( rr_debugresponses.GetInt() == 4 ) + { + if ( ( GetOuter()->MyNPCPointer() && GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) || GetOuter()->IsPlayer() ) + { + const char *pszName; + if ( GetOuter()->IsPlayer() ) + { + pszName = ((CBasePlayer*)GetOuter())->GetPlayerName(); + } + else + { + pszName = GetOuter()->GetDebugName(); + } + + if ( found ) + { + char response[ 256 ]; + outResponse.GetResponse( response, sizeof( response ) ); + + Warning( "RESPONSERULES: %s spoke '%s'. Found response '%s'.\n", pszName, (const char*)concept, response ); + } + else + { + Warning( "RESPONSERULES: %s spoke '%s'. Found no matching response.\n", pszName, (const char*)concept ); + } + } + } + + if ( !found ) + { + return false; + } + else if ( worldWritebackCriteria.GetCount() > 0 ) + { + Assert( CBaseEntity::Instance( INDEXENT( 0 ) )->IsWorld( ) ); + worldWritebackCriteria.WriteToEntity( CBaseEntity::Instance( INDEXENT( 0 ) ) ); + } + + if ( outResponse.IsEmpty() ) + { + // AssertMsg2( false, "RR: %s got empty but valid response for %s", GetOuter()->GetDebugName(), concept.GetStringConcept() ); + return false; + } + else + { + return true; + } +} + +#if 0 +//----------------------------------------------------------------------------- +// Purpose: Searches for a possible response; writes it into a response passed as +// parameter rather than new'ing one up. +// Input : concept - +// NULL - +// Output : bool : true on success, false on fail +//----------------------------------------------------------------------------- +AI_Response *CAI_Expresser::SpeakFindResponse( AI_Response *result, const AIConcept_t &concept, AI_CriteriaSet *criteria ) +{ + Assert(response); + + IResponseSystem *rs = GetOuter()->GetResponseSystem(); + if ( !rs ) + { + Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" ); + return NULL; + } + +#if 0 + AI_CriteriaSet set; + // Always include the concept name + set.AppendCriteria( "concept", concept, CONCEPT_WEIGHT ); + + // Always include any optional modifiers + if ( modifiers != NULL ) + { + 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, modifiers ); + + if( *key && *value ) + { + set.AppendCriteria( key, value, CONCEPT_WEIGHT ); + } + } + } + + // 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 ); + } +#else + AI_CriteriaSet &set = *criteria; +#endif + + // Now that we have a criteria set, ask for a suitable response + bool found = rs->FindBestResponse( set, *result, this ); + + if ( rr_debugresponses.GetInt() == 4 ) + { + if ( ( GetOuter()->MyNPCPointer() && GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) || GetOuter()->IsPlayer() ) + { + const char *pszName; + if ( GetOuter()->IsPlayer() ) + { + pszName = ((CBasePlayer*)GetOuter())->GetPlayerName(); + } + else + { + pszName = GetOuter()->GetDebugName(); + } + + if ( found ) + { + char response[ 256 ]; + result->GetResponse( response, sizeof( response ) ); + + Warning( "RESPONSERULES: %s spoke '%s'. Found response '%s'.\n", pszName, concept, response ); + } + else + { + Warning( "RESPONSERULES: %s spoke '%s'. Found no matching response.\n", pszName, concept ); + } + } + } + + if ( !found ) + { + //Assert( !"rs->FindBestResponse: Returned a NULL AI_Response!" ); + return false; + } + + char response[ 256 ]; + result->GetResponse( response, sizeof( response ) ); + + if ( !response[0] ) + { + return false; + } + + return true; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Dispatches the result +// Input : *response - +//----------------------------------------------------------------------------- +bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response *result, AI_CriteriaSet *criteria, IRecipientFilter *filter /* = NULL */ ) +{ + char response[ 256 ]; + result->GetResponse( response, sizeof( response ) ); + + float delay = result->GetDelay(); + + bool spoke = false; + + soundlevel_t soundlevel = result->GetSoundLevel(); + + if ( IsSpeaking() && concept[0] != 0 && result->GetType() != ResponseRules::RESPONSE_PRINT ) + { + const char *entityName = STRING( GetOuter()->GetEntityName() ); + if ( GetOuter()->IsPlayer() ) + { + entityName = ToBasePlayer( GetOuter() )->GetPlayerName(); + } + CGMsg( 2, CON_GROUP_SPEECH_AI, "SpeakDispatchResponse: Entity ( %i/%s ) already speaking, forcing '%s'\n", GetOuter()->entindex(), entityName ? entityName : "UNKNOWN", (const char*)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 + RemoveActorFromScriptedScenes( GetOuter(), true /*instanced scenes only*/ ); + GetOuter()->SentenceStop(); + + if ( IsRunningScriptedScene( GetOuter() ) ) + { + CGMsg( 1, CON_GROUP_SPEECH_AI, "SpeakDispatchResponse: Entity ( %i/%s ) refusing to speak due to scene entity, tossing '%s'\n", GetOuter()->entindex(), entityName ? entityName : "UNKNOWN", (const char*)concept ); + return false; + } + } + + switch ( result->GetType() ) + { + default: + case ResponseRules::RESPONSE_NONE: + break; + + case ResponseRules::RESPONSE_SPEAK: + { + if ( !result->ShouldntUseScene() ) + { + // This generates a fake CChoreoScene wrapping the sound.txt name +#ifdef MAPBASE + spoke = SpeakAutoGeneratedScene( response, delay, result, filter ); +#else + spoke = SpeakAutoGeneratedScene( response, delay ); +#endif + } + else + { + float speakTime = GetResponseDuration( result ); + GetOuter()->EmitSound( response ); + + CGMsg( 2, CON_GROUP_SPEECH_AI, "SpeakDispatchResponse: Entity ( %i/%s ) playing sound '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), response ); + NoteSpeaking( speakTime, delay ); + spoke = true; +#ifdef MAPBASE + // Not really any other way of doing this + OnSpeechFinished(); +#endif + } + } + break; + + case ResponseRules::RESPONSE_SENTENCE: + { + spoke = ( -1 != SpeakRawSentence( response, delay, VOL_NORM, soundlevel ) ) ? true : false; +#ifdef MAPBASE + // Not really any other way of doing this + OnSpeechFinished(); +#endif + } + break; + + case ResponseRules::RESPONSE_SCENE: + { + spoke = SpeakRawScene( response, delay, result, filter ); + } + break; + + case ResponseRules::RESPONSE_RESPONSE: + { + // This should have been recursively resolved already + Assert( 0 ); + } + break; + case ResponseRules::RESPONSE_PRINT: + { +#ifdef MAPBASE + // Note speaking for print responses + int responseLen = Q_strlen( response ); + float responseDuration = ((float)responseLen) * 0.1f; + NoteSpeaking( responseDuration, delay ); + + // game_text print responses + hudtextparms_t textParams; + textParams.holdTime = 4.0f + responseDuration; // Give extra padding for the text itself + textParams.fadeinTime = 0.5f; + textParams.fadeoutTime = 0.5f; + + textParams.channel = 3; + textParams.x = -1; + textParams.y = 0.6; + textParams.effect = 0; + + textParams.r1 = 255; + textParams.g1 = 255; + textParams.b1 = 255; + + if (ai_speech_print_mode.GetBool() && GetOuter()->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( response ); + WRITE_STRING( "" ); // No custom font + WRITE_BYTE ( responseLen ); + MessageEnd(); + + spoke = true; + + OnSpeechFinished(); + } + else +#endif + if ( g_pDeveloper->GetInt() > 0 ) + { + Vector vPrintPos; + GetOuter()->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,1.0f), &vPrintPos ); + NDebugOverlay::Text( vPrintPos, response, true, 1.5 ); + } + spoke = true; +#ifdef MAPBASE + OnSpeechFinished(); +#endif + } + break; + case ResponseRules::RESPONSE_ENTITYIO: + { + spoke = FireEntIOFromResponse( response, GetOuter() ); +#ifdef MAPBASE + OnSpeechFinished(); +#endif + } + break; +#ifdef MAPBASE_VSCRIPT + case ResponseRules::RESPONSE_VSCRIPT: + { + spoke = RunScriptResponse( GetOuter(), response, criteria, false ); + OnSpeechFinished(); + } + break; + case ResponseRules::RESPONSE_VSCRIPT_FILE: + { + spoke = RunScriptResponse( GetOuter(), response, criteria, true ); + OnSpeechFinished(); + } + break; +#endif + } + + if ( spoke ) + { + m_flLastTimeAcceptedSpeak = gpGlobals->curtime; + if ( DebuggingSpeech() && g_pDeveloper->GetInt() > 0 && response && result->GetType() != ResponseRules::RESPONSE_PRINT ) + { + Vector vPrintPos; + GetOuter()->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,1.0f), &vPrintPos ); + NDebugOverlay::Text( vPrintPos, CFmtStr( "%s: %s", (const char*)concept, response ), true, 1.5 ); + } + +#ifdef MAPBASE + if (result->GetContext()) + { + const char *pszContext = result->GetContext(); + + int iContextFlags = result->GetContextFlags(); + if ( iContextFlags & ResponseRules::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 & ResponseRules::APPLYCONTEXT_ENEMY ) + { + CBaseEntity *pEnemy = GetOuter()->GetEnemy(); + if ( pEnemy ) + { + pEnemy->AddContext( pszContext ); + } + } + if ( iContextFlags & ResponseRules::APPLYCONTEXT_WORLD ) + { + CBaseEntity *pEntity = CBaseEntity::Instance( INDEXENT( 0 ) ); + if ( pEntity ) + { + pEntity->AddContext( pszContext ); + } + } + if ( iContextFlags == 0 || iContextFlags & ResponseRules::APPLYCONTEXT_SELF ) + { + GetOuter()->AddContext( pszContext ); + } + } +#else + if ( result->IsApplyContextToWorld() ) + { + CBaseEntity *pEntity = CBaseEntity::Instance( INDEXENT( 0 ) ); + if ( pEntity ) + { + pEntity->AddContext( result->GetContext() ); + } + } + else + { + GetOuter()->AddContext( result->GetContext() ); + } +#endif + SetSpokeConcept( concept, result ); + } + else + { + } + + return spoke; +} + +bool CAI_Expresser::FireEntIOFromResponse( char *response, CBaseEntity *pInitiator ) +{ + // find the space-separator in the response name, then split into entityname, input, and parameter + // may barf in linux; there, should make some StringTokenizer() class that wraps the strtok_s behavior, etc. + char *pszEntname; + char *pszInput; + char *pszParam; + char *strtokContext; + + pszEntname = V_strtok_s( response, " ", &strtokContext ); + if ( !pszEntname ) + { + Warning( "Response was entityio but had bad value %s\n", response ); + return false; + } + + pszInput = V_strtok_s( NULL, " ", &strtokContext ); + if ( !pszInput ) + { + Warning( "Response was entityio but had bad value %s\n", response ); + return false; + } + + pszParam = V_strtok_s( NULL, " ", &strtokContext ); + + // poke entity io + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, pszEntname, pInitiator ); + if ( !pTarget ) + { + CGMsg( 0, CON_GROUP_SPEECH_AI, "Response rule targeted %s with entityio, but that doesn't exist.\n", pszEntname ); + // but this is actually a legit use case, so return true (below). + } + else + { + // pump the action into the target + variant_t variant; + if ( pszParam ) + { + variant.SetString( MAKE_STRING(pszParam) ); + } + pTarget->AcceptInput( pszInput, pInitiator, pInitiator, variant, 0 ); + + } + return true; +} + +#ifdef MAPBASE_VSCRIPT +bool CAI_Expresser::RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file ) +{ + if (!pTarget->ValidateScriptScope()) + return false; + + ScriptVariant_t varCriteriaTable; + g_pScriptVM->CreateTable( varCriteriaTable ); + + if (criteria) + { + // Sort all of the criteria into a table. + // Letting VScript have access to this is important because not all criteria is appended in ModifyOrAppendCriteria() and + // not all contexts are actually appended as contexts. This is specifically important for followup responses. + int count = criteria->GetCount(); + for ( int i = 0 ; i < count ; ++i ) + { + // TODO: Weight? + g_pScriptVM->SetValue( varCriteriaTable, criteria->GetName(i), criteria->GetValue(i) ); + } + + g_pScriptVM->SetValue( "criteria", varCriteriaTable ); + } + + bool bSuccess = false; + if (file) + { + bSuccess = pTarget->RunScriptFile( response ); + } + else + { + bSuccess = pTarget->RunScript( response, "ResponseScript" ); + } + + g_pScriptVM->ClearValue( "criteria" ); + g_pScriptVM->ReleaseScript( varCriteriaTable ); + + return bSuccess; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *response - +// Output : float +//----------------------------------------------------------------------------- +float CAI_Expresser::GetResponseDuration( AI_Response *result ) +{ + Assert( result ); + char response[ 256 ]; + result->GetResponse( response, sizeof( response ) ); + + switch ( result->GetType() ) + { + case ResponseRules::RESPONSE_SPEAK: + { + return GetOuter()->GetSoundDuration( response, STRING( GetOuter()->GetModelName() ) ); + } + break; + case ResponseRules::RESPONSE_SENTENCE: + { + Assert( 0 ); + return 999.0f; + } + break; + case ResponseRules::RESPONSE_SCENE: + { + return GetSceneDuration( response ); + } + break; + case ResponseRules::RESPONSE_RESPONSE: + { + // This should have been recursively resolved already + Assert( 0 ); + } + break; + case ResponseRules::RESPONSE_PRINT: + { + return 1.0; + } + break; + default: + case ResponseRules::RESPONSE_NONE: + case ResponseRules::RESPONSE_ENTITYIO: + return 0.0f; + } + + return 0.0f; +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Expresser::SetUsingProspectiveResponses( bool bToggle ) +{ + VPROF("CAI_Expresser::SetUsingProspectiveResponses"); + IResponseSystem *rs = GetOuter()->GetResponseSystem(); + if ( !rs ) + { + Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" ); + return; + } + + rs->SetProspective( bToggle ); +} + +void CAI_Expresser::MarkResponseAsUsed( AI_Response *response ) +{ + VPROF("CAI_Expresser::MarkResponseAsUsed"); + IResponseSystem *rs = GetOuter()->GetResponseSystem(); + if ( !rs ) + { + Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" ); + return; + } + + rs->MarkResponseAsUsed( response->GetInternalIndices()[0], response->GetInternalIndices()[1] ); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Placeholder for rules based response system +// Input : concept - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_Expresser::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + concept.SetSpeaker(GetOuter()); + AI_CriteriaSet criteria; + GatherCriteria(&criteria, concept, modifiers); + + return Speak( concept, &criteria, pszOutResponseChosen, bufsize, filter ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_Expresser::Speak( const AIConcept_t &concept, AI_CriteriaSet * RESTRICT criteria, char *pszOutResponseChosen , size_t bufsize , IRecipientFilter *filter ) +{ + VPROF("CAI_Expresser::Speak"); + if ( IsSpeechGloballySuppressed() ) + { + return false; + } + + GetOuter()->ModifyOrAppendDerivedCriteria(*criteria); + AI_Response result; + if ( !FindResponse( result, concept, criteria ) ) + { + return false; + } + + SpeechMsg( GetOuter(), "%s (%p) spoke %s (%f)", STRING(GetOuter()->GetEntityName()), GetOuter(), (const char*)concept, gpGlobals->curtime ); + // Msg( "%s:%s to %s:%s\n", GetOuter()->GetDebugName(), concept.GetStringConcept(), criteria.GetValue(criteria.FindCriterionIndex("Subject")), pTarget ? pTarget->GetDebugName() : "none" ); + + bool spoke = SpeakDispatchResponse( concept, &result, criteria, filter ); + if ( pszOutResponseChosen ) + { + result.GetResponse( pszOutResponseChosen, bufsize ); + } + + return spoke; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_Expresser::SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter /* = NULL */ ) +{ + float sceneLength = GetOuter()->PlayScene( pszScene, delay, response, filter ); + if ( sceneLength > 0 ) + { + SpeechMsg( GetOuter(), "SpeakRawScene( %s, %f) %f\n", pszScene, delay, sceneLength ); + +#if defined( HL2_EPISODIC ) + char szInstanceFilename[256]; + GetOuter()->GenderExpandString( pszScene, szInstanceFilename, sizeof( szInstanceFilename ) ); + // Only mark ourselves as speaking if the scene has speech + if ( GetSceneSpeechCount(szInstanceFilename) > 0 ) + { + NoteSpeaking( sceneLength, delay ); + } +#else + NoteSpeaking( sceneLength, delay ); +#endif + + return true; + } + return false; +} + +// 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 ); + NoteSpeaking( speakTime, delay ); + return true; + } + return false; +} + +//------------------------------------- + +int CAI_Expresser::SpeakRawSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener ) +{ + int sentenceIndex = -1; + + if ( !pszSentence ) + return sentenceIndex; + + if ( pszSentence[0] == AI_SP_SPECIFIC_SENTENCE ) + { + sentenceIndex = SENTENCEG_Lookup( pszSentence ); + + if( sentenceIndex == -1 ) + { + // sentence not found + return -1; + } + + CPASAttenuationFilter filter( GetOuter(), soundlevel ); + CBaseEntity::EmitSentenceByIndex( filter, GetOuter()->entindex(), CHAN_VOICE, sentenceIndex, volume, soundlevel, 0, GetVoicePitch()); + } + else + { + sentenceIndex = SENTENCEG_PlayRndSz( GetOuter()->NetworkProp()->edict(), pszSentence, volume, soundlevel, 0, GetVoicePitch() ); + } + + SpeechMsg( GetOuter(), "SpeakRawSentence( %s, %f) %f\n", pszSentence, delay, engine->SentenceLength( sentenceIndex ) ); + NoteSpeaking( engine->SentenceLength( sentenceIndex ), delay ); + + return sentenceIndex; +} + +//------------------------------------- + +void CAI_Expresser::BlockSpeechUntil( float time ) +{ + SpeechMsg( GetOuter(), "BlockSpeechUntil(%f) %f\n", time, time - gpGlobals->curtime ); + m_flBlockedTalkTime = time; +} + + +//------------------------------------- + +void CAI_Expresser::NoteSpeaking( float duration, float delay ) +{ + duration += delay; + + GetSink()->OnStartSpeaking(); + + if ( duration <= 0 ) + { + // no duration :( + m_flStopTalkTime = gpGlobals->curtime + 3; + duration = 0; + } + else + { + m_flStopTalkTime = gpGlobals->curtime + duration; + } + + m_flStopTalkTimeWithoutDelay = m_flStopTalkTime - delay; + + SpeechMsg( GetOuter(), "NoteSpeaking( %f, %f ) (stop at %f)\n", duration, delay, m_flStopTalkTime ); + + if ( GetSink()->UseSemaphore() ) + { + CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( GetOuter() ); + if ( pSemaphore ) + { + pSemaphore->Acquire( duration, GetOuter() ); + } + } +} + +//------------------------------------- + +void CAI_Expresser::ForceNotSpeaking( void ) +{ + if ( IsSpeaking() ) + { + m_flStopTalkTime = gpGlobals->curtime; + m_flStopTalkTimeWithoutDelay = gpGlobals->curtime; + + CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( GetOuter() ); + if ( pSemaphore ) + { + if ( pSemaphore->GetOwner() == GetOuter() ) + { + pSemaphore->Release(); + } + } + } +} + +//------------------------------------- + +bool CAI_Expresser::IsSpeaking( void ) +{ + if ( m_flStopTalkTime > gpGlobals->curtime ) + SpeechMsg( GetOuter(), "IsSpeaking() %f\n", m_flStopTalkTime - gpGlobals->curtime ); + + if ( m_flLastTimeAcceptedSpeak == gpGlobals->curtime ) // only one speak accepted per think + return true; + + return ( m_flStopTalkTime > gpGlobals->curtime ); +} + +//------------------------------------- + +bool CAI_Expresser::CanSpeak() +{ + if ( m_flLastTimeAcceptedSpeak == gpGlobals->curtime ) // only one speak accepted per think + return false; + + float timeOk = MAX( m_flStopTalkTime, m_flBlockedTalkTime ); + return ( timeOk <= gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if it's ok for this entity to speak after himself. +// The base CanSpeak() includes the default speech delay, and won't +// return true until that delay time has passed after finishing the +// speech. This returns true as soon as the speech finishes. +//----------------------------------------------------------------------------- +bool CAI_Expresser::CanSpeakAfterMyself() +{ + if ( m_flLastTimeAcceptedSpeak == gpGlobals->curtime ) // only one speak accepted per think + return false; + + float timeOk = MAX( m_flStopTalkTimeWithoutDelay, m_flBlockedTalkTime ); + return ( timeOk <= gpGlobals->curtime ); +} + +//------------------------------------- +bool CAI_Expresser::CanSpeakConcept( const AIConcept_t &concept ) +{ + // Not in history? + int iter = m_ConceptHistories.Find( concept ); + if ( iter == m_ConceptHistories.InvalidIndex() ) + { + return true; + } + + ConceptHistory_t *history = &m_ConceptHistories[iter]; + Assert( history ); + + const AI_Response &response = history->m_response; + if ( response.IsEmpty() ) + return true; + + if ( response.GetSpeakOnce() ) + return false; + + float respeakDelay = response.GetRespeakDelay(); + + if ( respeakDelay != 0.0f ) + { + if ( history->timeSpoken != -1 && ( gpGlobals->curtime < history->timeSpoken + respeakDelay ) ) + return false; + } + + return true; +} + +//------------------------------------- + +bool CAI_Expresser::SpokeConcept( const AIConcept_t &concept ) +{ + return GetTimeSpokeConcept( concept ) != -1.f; +} + +//------------------------------------- + +float CAI_Expresser::GetTimeSpokeConcept( const AIConcept_t &concept ) +{ + int iter = m_ConceptHistories.Find( concept ); + if ( iter == m_ConceptHistories.InvalidIndex() ) + return -1; + + ConceptHistory_t *h = &m_ConceptHistories[iter]; + return h->timeSpoken; +} + +//------------------------------------- + +void CAI_Expresser::SetSpokeConcept( const AIConcept_t &concept, AI_Response *response, bool bCallback ) +{ + int idx = m_ConceptHistories.Find( concept ); + if ( idx == m_ConceptHistories.InvalidIndex() ) + { + ConceptHistory_t h; + h.timeSpoken = gpGlobals->curtime; + idx = m_ConceptHistories.Insert( concept, h ); + } + + ConceptHistory_t *slot = &m_ConceptHistories[ idx ]; + + slot->timeSpoken = gpGlobals->curtime; + // Update response info + if ( response ) + { + slot->m_response = *response; + } + + if ( bCallback ) + GetSink()->OnSpokeConcept( concept, response ); +} + +//------------------------------------- + +void CAI_Expresser::ClearSpokeConcept( const AIConcept_t &concept ) +{ + m_ConceptHistories.Remove( concept ); +} + +#ifdef MAPBASE +//------------------------------------- + +AIConcept_t CAI_Expresser::GetLastSpokeConcept( AIConcept_t excludeConcept /* = NULL */ ) +{ + int iLastSpokenIndex = m_ConceptHistories.InvalidIndex(); + float flLast = 0.0f; + for ( int i = m_ConceptHistories.First(); i != m_ConceptHistories.InvalidIndex(); i = m_ConceptHistories.Next(i ) ) + { + ConceptHistory_t *h = &m_ConceptHistories[ i ]; + + // If an 'exclude concept' was provided, skip over this entry in the history if it matches the exclude concept + if ( excludeConcept != NULL && FStrEq( m_ConceptHistories.GetElementName( i ), excludeConcept ) ) + continue; + + if ( h->timeSpoken >= flLast ) + { + iLastSpokenIndex = i; + flLast = h->timeSpoken; + } + } + + return iLastSpokenIndex != m_ConceptHistories.InvalidIndex() ? m_ConceptHistories.GetElementName( iLastSpokenIndex ) : NULL; +} +#endif + +//------------------------------------- + +void CAI_Expresser::DumpHistories() +{ + int c = 1; + for ( int i = m_ConceptHistories.First(); i != m_ConceptHistories.InvalidIndex(); i = m_ConceptHistories.Next(i ) ) + { + ConceptHistory_t *h = &m_ConceptHistories[ i ]; + + CGMsg( 1, CON_GROUP_SPEECH_AI, "%i: %s at %f\n", c++, m_ConceptHistories.GetElementName( i ), h->timeSpoken ); + } +} + +//------------------------------------- + +bool CAI_Expresser::IsValidResponse( ResponseType_t type, const char *pszValue ) +{ + if ( type == ResponseRules::RESPONSE_SCENE ) + { + char szInstanceFilename[256]; + GetOuter()->GenderExpandString( pszValue, szInstanceFilename, sizeof( szInstanceFilename ) ); + return ( GetSceneDuration( szInstanceFilename ) > 0 ); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAI_TimedSemaphore *CAI_Expresser::GetMySpeechSemaphore( CBaseEntity *pNpc ) +{ + if ( !pNpc->MyNPCPointer() ) + return NULL; + + return (pNpc->MyNPCPointer()->IsPlayerAlly() ? &g_AIFriendliesTalkSemaphore : &g_AIFoesTalkSemaphore ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Expresser::SpeechMsg( CBaseEntity *pFlex, const char *pszFormat, ... ) +{ + if ( !DebuggingSpeech() ) + return; + + va_list arg_ptr; + + va_start(arg_ptr, pszFormat); + CFmtStr formatted; + formatted.sprintf_argv(pszFormat, arg_ptr); + va_end(arg_ptr); + + if ( pFlex->MyNPCPointer() ) + { + + DevMsg( pFlex->MyNPCPointer(), "%s", formatted.Get() ); + } + else + { + CGMsg( 1, CON_GROUP_SPEECH_AI, "%s", formatted.Get() ); + } + UTIL_LogPrintf( "%s", formatted.Get() ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true when l4d is in credits screen or some other +// speech-forbidden state +//----------------------------------------------------------------------------- +bool CAI_Expresser::IsSpeechGloballySuppressed() +{ + return false; +} + +//----------------------------------------------------------------------------- + +void CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_CriteriaSet& set ) +{ + // Append current activity name + const char *pActivityName = pSpeaker->GetActivityName( pSpeaker->GetActivity() ); + if ( pActivityName ) + { + set.AppendCriteria( "activity", pActivityName ); + } + + static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" }; + if ( (int)pSpeaker->m_NPCState < ARRAYSIZE(pStateNames) ) + { + set.AppendCriteria( "npcstate", UTIL_VarArgs( "[NPCState::%s]", pStateNames[pSpeaker->m_NPCState] ) ); + } + + if ( pSpeaker->GetEnemy() ) + { + set.AppendCriteria( "enemy", pSpeaker->GetEnemy()->GetClassname() ); + set.AppendCriteria( "timesincecombat", "-1" ); + } + else + { + if ( pSpeaker->GetLastEnemyTime() == 0.0 ) + set.AppendCriteria( "timesincecombat", "999999.0" ); + else + set.AppendCriteria( "timesincecombat", UTIL_VarArgs( "%f", gpGlobals->curtime - pSpeaker->GetLastEnemyTime() ) ); + } + + set.AppendCriteria( "speed", UTIL_VarArgs( "%.3f", pSpeaker->GetSmoothedVelocity().Length() ) ); + + CBaseCombatWeapon *weapon = pSpeaker->GetActiveWeapon(); + if ( weapon ) + { + set.AppendCriteria( "weapon", weapon->GetClassname() ); + } + else + { + set.AppendCriteria( "weapon", "none" ); + } + + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + if ( pPlayer ) + { + Vector distance = pPlayer->GetAbsOrigin() - pSpeaker->GetAbsOrigin(); + + set.AppendCriteria( "distancetoplayer", UTIL_VarArgs( "%f", distance.Length() ) ); + + } + else + { + set.AppendCriteria( "distancetoplayer", UTIL_VarArgs( "%i", MAX_COORD_RANGE ) ); + } + + if ( pSpeaker->HasCondition( COND_SEE_PLAYER ) ) + { + set.AppendCriteria( "seeplayer", "1" ); + } + else + { + set.AppendCriteria( "seeplayer", "0" ); + } + + if ( pPlayer && pPlayer->FInViewCone( pSpeaker ) && pPlayer->FVisible( pSpeaker ) ) + { + set.AppendCriteria( "seenbyplayer", "1" ); + } + else + { + set.AppendCriteria( "seenbyplayer", "0" ); + } +} + +//----------------------------------------------------------------------------- + +extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer ); + +CON_COMMAND( npc_speakall, "Force the npc to try and speak all their responses" ) +{ + CBaseEntity *pEntity; + + if ( args[1] && *args[1] ) + { + pEntity = gEntList.FindEntityByName( NULL, args[1], NULL ); + if ( !pEntity ) + { + pEntity = gEntList.FindEntityByClassname( NULL, args[1] ); + } + } + else + { + pEntity = UTIL_GetCommandClient() ? FindPickerEntity( UTIL_GetCommandClient() ) : NULL; + } + + if ( pEntity ) + { + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if (pNPC) + { + if ( pNPC->GetExpresser() ) + { + bool save = engine->LockNetworkStringTables( false ); + pNPC->GetExpresser()->TestAllResponses(); + engine->LockNetworkStringTables( save ); + } + } + } +} +//----------------------------------------------------------------------------- + +CMultiplayer_Expresser::CMultiplayer_Expresser( CBaseFlex *pOuter ) : CAI_ExpresserWithFollowup( pOuter ) +{ + m_bAllowMultipleScenes = false; +} + +bool CMultiplayer_Expresser::IsSpeaking( void ) +{ + if ( m_bAllowMultipleScenes ) + { + return false; + } + + return CAI_Expresser::IsSpeaking(); +} + + +void CMultiplayer_Expresser::AllowMultipleScenes() +{ + m_bAllowMultipleScenes = true; +} + +void CMultiplayer_Expresser::DisallowMultipleScenes() +{ + m_bAllowMultipleScenes = false; +} diff --git a/mp/src/game/server/ai_speech_new.h b/mp/src/game/server/ai_speech_new.h new file mode 100644 index 00000000..c61f6f03 --- /dev/null +++ b/mp/src/game/server/ai_speech_new.h @@ -0,0 +1,713 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_SPEECH_H +#define AI_SPEECH_H + +#include "utlmap.h" + +#include "soundflags.h" +#include "AI_Criteria.h" +#include "AI_ResponseSystem.h" +#include "utldict.h" +#include "ai_speechconcept.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +class KeyValues; + +using ResponseRules::ResponseType_t; +using ResponseRules::AI_ResponseFollowup; + + +//----------------------------------------------------------------------------- +// Purpose: Used to share a global resource or prevent a system stepping on +// own toes. +//----------------------------------------------------------------------------- + +class CAI_TimedSemaphore +{ +public: + CAI_TimedSemaphore() + : m_ReleaseTime( 0 ) + { + m_hCurrentTalker = NULL; + } + + void Acquire( float time, CBaseEntity *pTalker ) { m_ReleaseTime = gpGlobals->curtime + time; m_hCurrentTalker = pTalker; } + void Release() { m_ReleaseTime = 0; m_hCurrentTalker = NULL; } + + // Current owner of the semaphore is always allowed to talk + bool IsAvailable( CBaseEntity *pTalker ) const { return ((gpGlobals->curtime > m_ReleaseTime) || (m_hCurrentTalker == pTalker)); } + float GetReleaseTime() const { return m_ReleaseTime; } + + CBaseEntity *GetOwner() { return m_hCurrentTalker; } + +private: + float m_ReleaseTime; + EHANDLE m_hCurrentTalker; +}; + +//----------------------------------------------------------------------------- + +extern CAI_TimedSemaphore g_AIFriendliesTalkSemaphore; +extern CAI_TimedSemaphore g_AIFoesTalkSemaphore; + +#define GetSpeechSemaphore( pNpc ) (((pNpc)->IsPlayerAlly()) ? &g_AIFriendliesTalkSemaphore : &g_AIFoesTalkSemaphore ) +//----------------------------------------------------------------------------- +// Basic speech system types +//----------------------------------------------------------------------------- + +//------------------------------------- +// Constants + + +const float AIS_NO_DELAY = 0; +const soundlevel_t AIS_DEF_SNDLVL = SNDLVL_TALKING; +#define AI_NULL_CONCEPT NULL + +#define AI_NULL_SENTENCE NULL + +// Sentence prefix constants +#define AI_SP_SPECIFIC_SENTENCE '!' +#define AI_SP_WAVFILE '^' +#define AI_SP_SCENE_GROUP '=' +#define AI_SP_SPECIFIC_SCENE '?' + +#define AI_SPECIFIC_SENTENCE(str_constant) "!" str_constant +#define AI_WAVFILE(str_constant) "^" str_constant +// @Note (toml 09-12-02): as scene groups are not currently implemented, the string is a semi-colon delimited list +#define AI_SCENE_GROUP(str_constant) "=" str_constant +#define AI_SPECIFIC_SCENE(str_constant) "?" str_constant + +// Designer overriding modifiers +#define AI_SPECIFIC_SCENE_MODIFIER "scene:" + +//------------------------------------- + +//------------------------------------- +// An id that represents the core meaning of a spoken phrase, +// eventually to be mapped to a sentence group or scene + +#if AI_CONCEPTS_ARE_STRINGS +typedef const char *AIConcept_t; +inline bool CompareConcepts( AIConcept_t c1, AIConcept_t c2 ) +{ + return ( (void *)c1 == (void *)c2 || ( c1 && c2 && Q_stricmp( c1, c2 ) == 0 ) ); +} +#else +typedef CAI_Concept AIConcept_t; +inline bool CompareConcepts( AIConcept_t c1, AIConcept_t c2 ) +{ + return c1.m_iConcept == c2.m_iConcept; +} +#endif + + +//----------------------------------------------------------------------------- +// CAI_Expresser +// +// Purpose: Provides the functionality of going from abstract concept ("hello") +// to specific sentence/scene/wave +// + +//------------------------------------- +// Sink supports behavior control and receives notifications of internal events + +class CAI_ExpresserSink +{ +public: + virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {}; + virtual void OnStartSpeaking() {} + virtual bool UseSemaphore() { return true; } +}; + +struct ConceptHistory_t +{ + DECLARE_SIMPLE_DATADESC(); + + ConceptHistory_t(float timeSpoken = -1 ) + : timeSpoken( timeSpoken ), m_response( ) + { + } + + ConceptHistory_t( const ConceptHistory_t& src ); + ConceptHistory_t& operator = ( const ConceptHistory_t& src ); + + ~ConceptHistory_t(); + + float timeSpoken; + AI_Response m_response; +}; +//------------------------------------- + +class CAI_Expresser : public ResponseRules::IResponseFilter +{ +public: + CAI_Expresser( CBaseFlex *pOuter = NULL ); + ~CAI_Expresser(); + + // -------------------------------- + + bool Connect( CAI_ExpresserSink *pSink ) { m_pSink = pSink; return true; } + bool Disconnect( CAI_ExpresserSink *pSink ) { m_pSink = NULL; return true;} + + void TestAllResponses(); + + // -------------------------------- + + bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); + bool Speak( const AIConcept_t &concept, AI_CriteriaSet *criteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); + + // Given modifiers (which are colon-delimited strings), fill out a criteria set including this + // character's contexts and the ones in the modifier. This lets us hang on to them after a call + // to SpeakFindResponse. + void GatherCriteria( AI_CriteriaSet *outputCritera, const AIConcept_t &concept, const char *modifiers ); + // These two methods allow looking up a response and dispatching it to be two different steps + // AI_Response *SpeakFindResponse( AIConcept_t concept, const char *modifiers = NULL ); + // AI_Response *SpeakFindResponse( AIConcept_t &concept, AI_CriteriaSet *criteria ); + // Find the appropriate response for the given concept. Return false if none found. + // Fills out the response object that you provide. + bool FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *modifiers = NULL ); + virtual bool SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria, IRecipientFilter *filter = NULL ); + float GetResponseDuration( AI_Response *response ); + +#ifdef MAPBASE + void SetUsingProspectiveResponses( bool bToggle ); + void MarkResponseAsUsed( AI_Response *response ); +#endif + + virtual int SpeakRawSentence( const char *pszSentence, float delay, float volume = VOL_NORM, soundlevel_t soundlevel = SNDLVL_TALKING, CBaseEntity *pListener = NULL ); + + bool SemaphoreIsAvailable( CBaseEntity *pTalker ); + float GetSemaphoreAvailableTime( CBaseEntity *pTalker ); + + virtual void OnSpeechFinished() {}; + + // This function can be overriden by games to suppress speech altogether during glue screens, etc + static bool IsSpeechGloballySuppressed(); + + // -------------------------------- + + virtual bool IsSpeaking(); + bool CanSpeak(); + bool CanSpeakAfterMyself(); + float GetTimeSpeechComplete() const { return m_flStopTalkTime; } +#ifdef MAPBASE + float GetTimeSpeechCompleteWithoutDelay() const { return m_flStopTalkTimeWithoutDelay; } +#endif + void BlockSpeechUntil( float time ); + + // -------------------------------- + + bool CanSpeakConcept( const AIConcept_t &concept ); + bool SpokeConcept( const AIConcept_t &concept ); + float GetTimeSpokeConcept( const AIConcept_t &concept ); // returns -1 if never + void SetSpokeConcept( const AIConcept_t &concept, AI_Response *response, bool bCallback = true ); + void ClearSpokeConcept( const AIConcept_t &concept ); + +#ifdef MAPBASE + AIConcept_t GetLastSpokeConcept( AIConcept_t excludeConcept = NULL ); +#endif + + // -------------------------------- + + void SetVoicePitch( int voicePitch ) { m_voicePitch = voicePitch; } + int GetVoicePitch() const; + + void NoteSpeaking( float duration, float delay = 0 ); + + // 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 + + // helper used in dealing with RESPONSE_ENTITYIO + // response is the output of AI_Response::GetName + // note: the response string will get stomped on (by strtok) + // returns false on failure (eg, couldn't match parse contents) + static bool FireEntIOFromResponse( char *response, CBaseEntity *pInitiator ); + +#ifdef MAPBASE_VSCRIPT + // Used for RESPONSE_VSCRIPT(_FILE) + static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file ); +#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, ... ) FMTFUNCTION(3, 4); + + // -------------------------------- + + CAI_ExpresserSink *GetSink() { return m_pSink; } + +private: + // -------------------------------- + + virtual bool IsValidResponse( ResponseType_t type, const char *pszValue ); + + // -------------------------------- + + CAI_ExpresserSink *m_pSink; + + // -------------------------------- + // + // Speech concept data structures + // + + CUtlDict< ConceptHistory_t, int > m_ConceptHistories; + + // -------------------------------- + // + // Speaking states + // + + float m_flStopTalkTime; // when in the future that I'll be done saying this sentence. + float m_flStopTalkTimeWithoutDelay; // same as the above, but minus the delay before other people can speak + float m_flBlockedTalkTime; + int m_voicePitch; // pitch of voice for this head + float m_flLastTimeAcceptedSpeak; // because speech may not be blocked until NoteSpeaking called by scene ent, this handles in-think blocking + + DECLARE_SIMPLE_DATADESC(); + + // -------------------------------- + // +public: + void SetOuter( CBaseFlex *pOuter ); + + CBaseFlex * GetOuter() { return m_pOuter; } + const CBaseFlex * GetOuter() const { return m_pOuter; } + +private: + CHandle m_pOuter; +}; + +//----------------------------------------------------------------------------- +// +// An NPC base class to assist a branch of the inheritance graph +// in utilizing CAI_Expresser +// + +template +class CAI_ExpresserHost : public BASE_NPC, protected CAI_ExpresserSink +{ + DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC ); + +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 ); + virtual bool Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); +#ifdef MAPBASE + virtual bool Speak( AIConcept_t concept, AI_CriteriaSet& modifiers, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ) { return Speak( concept, &modifiers, pszOutResponseChosen, bufsize, filter ); } +#endif + + + void GatherCriteria( AI_CriteriaSet *outputCritera, const AIConcept_t &concept, const char *modifiers ); + // These two methods allow looking up a response and dispatching it to be two different steps +#ifdef MAPBASE + //AI_Response *SpeakFindResponse( AIConcept_t concept, const AI_CriteriaSet& modifiers ); + inline bool SpeakDispatchResponse( const AIConcept_t &concept, AI_Response &response, AI_CriteriaSet *criteria = NULL ) { return SpeakDispatchResponse( concept, &response, criteria ); } +#endif + bool SpeakFindResponse( AI_Response& outResponse, const AIConcept_t &concept, const char *modifiers = NULL ); + // AI_Response * SpeakFindResponse( AIConcept_t concept, const char *modifiers = NULL ); + // AI_Response *SpeakFindResponse( AIConcept_t concept, AI_CriteriaSet *criteria ); + // AI_Response *SpeakFindResponse( AIConcept_t concept ); + // Find the appropriate response for the given concept. Return false if none found. + // Fills out the response object that you provide. + bool FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *criteria = NULL ); + + bool SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria = NULL ); + virtual void PostSpeakDispatchResponse( AIConcept_t concept, AI_Response *response ) { return; } + float GetResponseDuration( AI_Response *response ); + + float GetTimeSpeechComplete() const { return this->GetExpresser()->GetTimeSpeechComplete(); } + + bool IsSpeaking() { return this->GetExpresser()->IsSpeaking(); } + bool CanSpeak() { return this->GetExpresser()->CanSpeak(); } + bool CanSpeakAfterMyself() { return this->GetExpresser()->CanSpeakAfterMyself(); } + + void SetSpokeConcept( AIConcept_t concept, AI_Response *response, bool bCallback = true ) { this->GetExpresser()->SetSpokeConcept( concept, response, bCallback ); } + float GetTimeSpokeConcept( AIConcept_t concept ) { return this->GetExpresser()->GetTimeSpokeConcept( concept ); } + bool SpokeConcept( AIConcept_t concept ) { return this->GetExpresser()->SpokeConcept( concept ); } + +protected: + int PlaySentence( const char *pszSentence, float delay, float volume = VOL_NORM, soundlevel_t soundlevel = SNDLVL_TALKING, CBaseEntity *pListener = NULL ); + virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set ); + + virtual IResponseSystem *GetResponseSystem(); + // Override of base entity response input handler + virtual void DispatchResponse( const char *conceptName ); +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CAI_ExpresserHost::NoteSpeaking( float duration, float delay ) +{ + this->GetExpresser()->NoteSpeaking( duration, delay ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /*=NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + AssertOnce( this->GetExpresser()->GetOuter() == this ); + return this->GetExpresser()->Speak( concept, modifiers, pszOutResponseChosen, bufsize, filter ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen /*=NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ ) +{ + AssertOnce( this->GetExpresser()->GetOuter() == this ); + CAI_Expresser * const RESTRICT pExpresser = this->GetExpresser(); + concept.SetSpeaker(this); + // add in any local criteria to the one passed on the command line. + pExpresser->GatherCriteria( pCriteria, concept, NULL ); + // call the "I have aleady gathered criteria" version of Expresser::Speak + return pExpresser->Speak( concept, pCriteria, pszOutResponseChosen, bufsize, filter ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CAI_ExpresserHost::PlaySentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener ) +{ + return this->GetExpresser()->SpeakRawSentence( pszSentence, delay, volume, soundlevel, pListener ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +extern void CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_CriteriaSet& criteriaSet ); + +template +inline void CAI_ExpresserHost::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ) +{ + BaseClass::ModifyOrAppendCriteria( criteriaSet ); + + + if ( this->MyNPCPointer() ) + { + CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( this->MyNPCPointer(), criteriaSet ); + } + +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline IResponseSystem *CAI_ExpresserHost::GetResponseSystem() +{ + extern IResponseSystem *g_pResponseSystem; + // Expressive NPC's use the general response system + return g_pResponseSystem; +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CAI_ExpresserHost::GatherCriteria( AI_CriteriaSet *outputCriteria, const AIConcept_t &concept, const char *modifiers ) +{ + return this->GetExpresser()->GatherCriteria( outputCriteria, concept, modifiers ); +} + + +#if 1 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::SpeakFindResponse(AI_Response& outResponse, const AIConcept_t &concept, const char *modifiers /*= NULL*/ ) +{ + AI_CriteriaSet criteria; + GatherCriteria(&criteria, concept, modifiers); + return FindResponse( outResponse, concept, &criteria ); +} +#else +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline AI_Response *CAI_ExpresserHost::SpeakFindResponse( const AIConcept_t &concept, const char *modifiers /*= NULL*/ ) +{ + return this->GetExpresser()->SpeakFindResponse( concept, modifiers ); +} +#endif + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline AI_Response *CAI_ExpresserHost::SpeakFindResponse( const AIConcept_t &concept, AI_CriteriaSet *criteria /*= NULL*/ ) +{ + return this->GetExpresser()->SpeakFindResponse( concept, criteria ); +} + + +//----------------------------------------------------------------------------- +// In this case we clearly don't care to hang on to the criteria, so make a convenience +// class that generates a one off. +//----------------------------------------------------------------------------- +template +inline AI_Response * CAI_ExpresserHost::SpeakFindResponse( const AIConcept_t &concept ) +{ + AI_CriteriaSet criteria; + GatherCriteria( &criteria, concept, NULL ); + return this->GetExpresser()->SpeakFindResponse( concept, &criteria ); +} +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *criteria ) +{ + return this->GetExpresser()->FindResponse( outResponse, concept, criteria ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CAI_ExpresserHost::SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria ) +{ + if ( this->GetExpresser()->SpeakDispatchResponse( concept, response, criteria ) ) + { + PostSpeakDispatchResponse( concept, response ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline float CAI_ExpresserHost::GetResponseDuration( AI_Response *response ) +{ + return this->GetExpresser()->GetResponseDuration( response ); +} + +//----------------------------------------------------------------------------- +// Override of base entity response input handler +//----------------------------------------------------------------------------- +template +inline void CAI_ExpresserHost::DispatchResponse( const char *conceptName ) +{ + Speak( AIConcept_t( conceptName ) ); +} + +//----------------------------------------------------------------------------- + +/// A shim under CAI_ExpresserHost you can use when deriving a new expresser +/// host type under CAI_BaseNPC. This does the extra step of declaring an m_pExpresser +/// member and initializing it from CreateComponents(). If your BASE_NPC class isn't +/// actually an NPC, then CreateComponents() never gets called and you won't have +/// an expresser created. +/// Note: you still need to add m_pExpresser to the Datadesc for your derived type. +/// This is because I couldn't figure out how to make a templatized datadesc declaration +/// that works generically on the template type. +template +class CAI_ExpresserHostWithData : public CAI_ExpresserHost +{ + DECLARE_CLASS_NOFRIEND( CAI_ExpresserHostWithData, CAI_ExpresserHost ); + +public: + CAI_ExpresserHostWithData( ) : m_pExpresser(NULL) {}; + + virtual CAI_Expresser *GetExpresser() { return m_pExpresser; } + const CAI_Expresser *GetExpresser() const { return m_pExpresser; } + + virtual bool CreateComponents() + { + return BaseClass::CreateComponents() && ( CreateExpresser() != NULL ); + } + +protected: + EXPRESSER_TYPE *CreateExpresser( void ) + { + AssertMsg1( m_pExpresser == NULL, "Tried to double-initialize expresser in %s\n", this->GetDebugName() ); + m_pExpresser = new EXPRESSER_TYPE(this); + if ( !m_pExpresser) + { + AssertMsg1( false, "Creating an expresser failed in %s\n", this->GetDebugName() ); + return NULL; + } + + m_pExpresser->Connect(this); + return m_pExpresser; + } + + virtual ~CAI_ExpresserHostWithData( void ) + { + delete m_pExpresser; + m_pExpresser = NULL; + } + + EXPRESSER_TYPE *m_pExpresser; +}; + +/// response rules +namespace RR +{ + /// some applycontext clauses have operators preceding them, + /// like ++1 which means "take the current value and increment it + /// by one". These classes detect these cases and do the appropriate + /// thing. + class CApplyContextOperator + { + public: + inline CApplyContextOperator( int nSkipChars ) : m_nSkipChars(nSkipChars) {}; + + /// perform whatever this operator does upon the given context value. + /// Default op is simply to copy old to new. + /// pOldValue should be the currently set value of the context. May be NULL meaning no prior value. + /// pOperator the value that applycontext says to set + /// pNewValue a pointer to a buffer where the real new value will be writ. + /// returns true on success; false on failure (eg, tried to increment a + /// non-numeric value). + virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ); + + /// This is the function that should be called from outside, + /// fed the input string, it'll select the right operator + /// to apply. + static CApplyContextOperator *FindOperator( const char *pContextString ); + + protected: + int m_nSkipChars; // how many chars to "skip" in the value string to get past the op specifier to the actual value + // eg, "++3" has a m_nSkipChars of 2, because the op string "++" is two characters. + }; + + class CIncrementOperator : public CApplyContextOperator + { + public: + inline CIncrementOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {}; + virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ); + }; + + class CDecrementOperator : public CApplyContextOperator + { + public: + inline CDecrementOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {}; + virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ); + }; + +#ifdef MAPBASE + class CMultiplyOperator : public CApplyContextOperator + { + public: + inline CMultiplyOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {}; + virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ); + }; + + class CDivideOperator : public CApplyContextOperator + { + public: + inline CDivideOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {}; + virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ); + }; +#endif + + class CToggleOperator : public CApplyContextOperator + { + public: + inline CToggleOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {}; + virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize ); + }; + + // the singleton operators + extern CApplyContextOperator sm_OpCopy; + extern CIncrementOperator sm_OpIncrement; + extern CDecrementOperator sm_OpDecrement; +#ifdef MAPBASE + extern CMultiplyOperator sm_OpMultiply; + extern CDivideOperator sm_OpDivide; +#endif + extern CToggleOperator sm_OpToggle; + +#ifdef MAPBASE + // LEGACY - See CApplyContextOperator::FindOperator() + extern CIncrementOperator sm_OpLegacyIncrement; + extern CDecrementOperator sm_OpLegacyDecrement; + extern CMultiplyOperator sm_OpLegacyMultiply; + extern CDivideOperator sm_OpLegacyDivide; +#endif +}; + + +//----------------------------------------------------------------------------- +#include "ai_speechqueue.h" + +//----------------------------------------------------------------------------- +// A kind of AI Expresser that can dispatch a follow-up speech event when it +// finishes speaking. +//----------------------------------------------------------------------------- +class CAI_ExpresserWithFollowup : public CAI_Expresser +{ +public: + CAI_ExpresserWithFollowup( CBaseFlex *pOuter = NULL ) : CAI_Expresser(pOuter), + m_pPostponedFollowup(NULL) + {}; + virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); + virtual bool SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria, IRecipientFilter *filter = NULL ); + virtual void SpeakDispatchFollowup( AI_ResponseFollowup &followup ); + + virtual void OnSpeechFinished(); + + typedef CAI_Expresser BaseClass; +protected: + static void DispatchFollowupThroughQueue( const AIConcept_t &concept, + const char *criteriaStr, + const CResponseQueue::CFollowupTargetSpec_t &target, + float delay, + CBaseEntity * RESTRICT pOuter ); + + AI_ResponseFollowup *m_pPostponedFollowup; // TODO: save/restore + CResponseQueue::CFollowupTargetSpec_t m_followupTarget; +}; + +class CMultiplayer_Expresser : public CAI_ExpresserWithFollowup +{ +public: + CMultiplayer_Expresser( CBaseFlex *pOuter = NULL ); + //~CMultiplayer_Expresser(); + + virtual bool IsSpeaking(); + + void AllowMultipleScenes(); + void DisallowMultipleScenes(); + +private: + bool m_bAllowMultipleScenes; + +}; + + +#endif // AI_SPEECH_H diff --git a/mp/src/game/server/ai_speechqueue.cpp b/mp/src/game/server/ai_speechqueue.cpp new file mode 100644 index 00000000..7e8bf055 --- /dev/null +++ b/mp/src/game/server/ai_speechqueue.cpp @@ -0,0 +1,495 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "basemultiplayerplayer.h" +#include "ai_baseactor.h" +#include "ai_speech.h" +//#include "flex_expresser.h" +// memdbgon must be the last include file in a .cpp file!!! +#include + +extern ConVar ai_debug_speech; +#define DebuggingSpeech() ai_debug_speech.GetBool() +extern ConVar rr_debugresponses; + +ConVar rr_followup_maxdist( "rr_followup_maxdist", "1800", FCVAR_CHEAT, "'then ANY' or 'then ALL' response followups will be dispatched only to characters within this distance." ); + +/////////////////////////////////////////////////////////////////////////////// +// RESPONSE QUEUE DATA STRUCTURE +/////////////////////////////////////////////////////////////////////////////// + +CResponseQueue::CResponseQueue( int queueSize ) : m_Queue(queueSize), m_ExpresserTargets(8,8) +{}; + +/// Add a deferred response. +void CResponseQueue::Add( const AIConcept_t &concept, ///< concept to dispatch + const AI_CriteriaSet * RESTRICT contexts, + float time, ///< when to dispatch it. You can specify a time of zero to mean "immediately." + const CFollowupTargetSpec_t &targetspec, + CBaseEntity *pIssuer + ) +{ + // Add a response. + AssertMsg( m_Queue.Count() < AI_RESPONSE_QUEUE_SIZE, "AI Response queue overfilled." ); + QueueType_t::IndexLocalType_t idx = m_Queue.AddToTail(); + m_Queue[idx].Init( concept, contexts, time, targetspec, pIssuer ); +} + + +/// Remove a deferred response matching the concept and issuer. +void CResponseQueue::Remove( const AIConcept_t &concept, ///< concept to dispatch + CBaseEntity * const RESTRICT pIssuer ///< the entity issuing the response, if one exists. + ) RESTRICT +{ + // walk through the queue until we find a response matching the concept and issuer, then strike it. + QueueType_t::IndexLocalType_t idx = m_Queue.Head(); + while (idx != m_Queue.InvalidIndex()) + { + CDeferredResponse &response = m_Queue[idx]; + QueueType_t::IndexLocalType_t previdx = idx; // advance the index immediately because we may be deleting the "current" element + idx = m_Queue.Next(idx); // is now the next index + if ( CompareConcepts( response.m_concept, concept ) && // if concepts match and + ( !pIssuer || ( response.m_hIssuer.Get() == pIssuer ) ) // issuer is null, or matches the one in the response + ) + { + m_Queue.Remove(previdx); + } + } +} + + +void CResponseQueue::RemoveSpeechQueuedFor( const CBaseEntity *pSpeaker ) +{ + // walk through the queue until we find a response matching the speaker, then strike it. + // because responses are dispatched from inside a loop that is already walking through the + // queue, it's not safe to actually remove the elements. Instead, quash it by replacing it + // with a null event. + + for ( QueueType_t::IndexLocalType_t idx = m_Queue.Head() ; + idx != m_Queue.InvalidIndex() ; + idx = m_Queue.Next(idx) ) // is now the next index + { + CDeferredResponse &response = m_Queue[idx]; + if ( response.m_Target.m_hHandle.Get() == pSpeaker ) + { + response.Quash(); + } + } +} + +// TODO: use a more compact representation. +void CResponseQueue::DeferContextsFromCriteriaSet( DeferredContexts_t &contextsOut, const AI_CriteriaSet * RESTRICT criteriaIn ) +{ + contextsOut.Reset(); + if (criteriaIn) + { + contextsOut.Merge(criteriaIn); + } +} + +void CResponseQueue::PerFrameDispatch() +{ +failsafe: + // Walk through the list, find any messages whose time has come, and dispatch them. Then remove them. + QueueType_t::IndexLocalType_t idx = m_Queue.Head(); + while (idx != m_Queue.InvalidIndex()) + { + // do we need to dispatch this concept? + CDeferredResponse &response = m_Queue[idx]; + QueueType_t::IndexLocalType_t previdx = idx; // advance the index immediately because we may be deleting the "current" element + idx = m_Queue.Next(idx); // is now the next index + + if ( response.IsQuashed() ) + { + // we can delete this entry now + m_Queue.Remove(previdx); + } + else if ( response.m_fDispatchTime <= gpGlobals->curtime ) + { + // dispatch. we've had bugs where dispatches removed things from inside the queue; + // so, as a failsafe, if the queue length changes as a result, start over. + int oldLength = m_Queue.Count(); + DispatchOneResponse(response); + if ( m_Queue.Count() < oldLength ) + { + AssertMsg( false, "Response queue length changed in non-reentrant way! FAILSAFE TRIGGERED" ); + goto failsafe; // ick + } + + // we can delete this entry now + m_Queue.Remove(previdx); + } + } +} + + +/// Add an expressor owner to this queue. +void CResponseQueue::AddExpresserHost(CBaseEntity *host) +{ + EHANDLE ehost(host); + // see if it's in there already + if (m_ExpresserTargets.HasElement(ehost)) + { + AssertMsg1(false, "Tried to add %s to response queue when it was already in there.", host->GetDebugName()); + } + else + { + // zip through the queue front to back, first see if there's any invalid handles to replace + int count = m_ExpresserTargets.Count(); + for (int i = 0 ; i < count ; ++i ) + { + if ( !m_ExpresserTargets[i].Get() ) + { + m_ExpresserTargets[i] = ehost; + return; + } + } + + // if we're down here we didn't find one to replace, so append the host to the end. + m_ExpresserTargets.AddToTail(ehost); + } +} + +/// Remove an expresser host from this queue. +void CResponseQueue::RemoveExpresserHost(CBaseEntity *host) +{ + int idx = m_ExpresserTargets.Find(host); + if (idx == -1) + { + // AssertMsg1(false, "Tried to remove %s from response queue, but it's not in there to begin with!", host->GetDebugName() ); + } + else + { + m_ExpresserTargets.FastRemove(idx); + } +} + +/// Get the expresser for a base entity. +/// TODO: Kind of an ugly hack until I get the class hierarchy straightened out. +static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt) +{ +#ifdef MAPBASE + if ( CBasePlayer *pPlayer = ToBasePlayer(pEnt) ) +#else + if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast(pEnt) ) +#endif + { + return pPlayer->GetExpresser(); + } + else if ( CAI_BaseActor *pActor = dynamic_cast(pEnt) ) + { + return pActor->GetExpresser(); + } + /* + else if ( CFlexExpresser *pFlex = dynamic_cast(pEnt) ) + { + return pFlex->GetExpresser(); + } + */ + else + { + return NULL; + } +} + + +void CResponseQueue::CDeferredResponse::Quash() +{ + m_Target = CFollowupTargetSpec_t(); + m_fDispatchTime = 0; +} + +bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response) +{ + // find the target. + CBaseEntity * RESTRICT pTarget = NULL; + AI_CriteriaSet &deferredCriteria = response.m_contexts; + CAI_Expresser * RESTRICT pEx = NULL; + CBaseEntity * RESTRICT pIssuer = response.m_hIssuer.Get(); // MAY BE NULL + float followupMaxDistSq; + { + /* + CFlexExpresser * RESTRICT pOrator = CFlexExpresser::AsFlexExpresser( pIssuer ); + if ( pOrator ) + { + // max dist is overridden. "0" means infinite distance (for orators only), + // anything else is a finite distance. + if ( pOrator->m_flThenAnyMaxDist > 0 ) + { + followupMaxDistSq = pOrator->m_flThenAnyMaxDist * pOrator->m_flThenAnyMaxDist; + } + else + { + followupMaxDistSq = FLT_MAX; + } + + } + else + */ + { + followupMaxDistSq = rr_followup_maxdist.GetFloat(); // square of max audibility distance + followupMaxDistSq *= followupMaxDistSq; + } + } + + switch (response.m_Target.m_iTargetType) + { + case kDRT_SPECIFIC: + { + pTarget = response.m_Target.m_hHandle.Get(); + } + break; + case kDRT_ANY: + { + return DispatchOneResponse_ThenANY( response, &deferredCriteria, pIssuer, followupMaxDistSq ); + } + break; + case kDRT_ALL: + { + bool bSaidAnything = false; + Vector issuerLocation; + if ( pIssuer ) + { + issuerLocation = pIssuer->GetAbsOrigin(); + } + + // find all characters + int numExprs = GetNumExpresserTargets(); + for ( int i = 0 ; i < numExprs; ++i ) + { + pTarget = GetExpresserHost(i); + float distIssuerToTargetSq = 0.0f; + if ( pIssuer ) + { + distIssuerToTargetSq = (pTarget->GetAbsOrigin() - issuerLocation).LengthSqr(); + if ( distIssuerToTargetSq > followupMaxDistSq ) + continue; // too far + } + + pEx = InferExpresserFromBaseEntity(pTarget); + if ( !pEx || pTarget == pIssuer ) + continue; + AI_CriteriaSet characterCriteria; + pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL); + characterCriteria.Merge(&deferredCriteria); + if ( pIssuer ) + { + characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) ); + } + AI_Response prospectiveResponse; + if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) ) + { + // dispatch it + bSaidAnything = pEx->SpeakDispatchResponse(response.m_concept, &prospectiveResponse, &deferredCriteria) || bSaidAnything ; + } + } + + return bSaidAnything; + + } + break; + default: + // WTF? + AssertMsg1( false, "Unknown deferred response type %d\n", response.m_Target.m_iTargetType ); + return false; + } + + if (!pTarget) + return false; // we're done right here. + + // Get the expresser for the target. + pEx = InferExpresserFromBaseEntity(pTarget); + if (!pEx) + return false; + + + AI_CriteriaSet characterCriteria; + pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL); + characterCriteria.Merge(&deferredCriteria); + pEx->Speak( response.m_concept, &characterCriteria ); + + return true; +} + +// +ConVar rr_thenany_score_slop( "rr_thenany_score_slop", "0.0", FCVAR_CHEAT, "When computing respondents for a 'THEN ANY' rule, all rule-matching scores within this much of the best score will be considered." ); +#define EXARRAYMAX 32 // maximum number of prospective expressers in the array (hardcoded for simplicity) +bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, AI_CriteriaSet * RESTRICT pDeferredCriteria, CBaseEntity * const RESTRICT pIssuer, float followupMaxDistSq ) +{ + CBaseEntity * RESTRICT pTarget = NULL; + CAI_Expresser * RESTRICT pEx = NULL; + float bestScore = 0; + float slop = rr_thenany_score_slop.GetFloat(); + Vector issuerLocation; + if ( pIssuer ) + { + issuerLocation = pIssuer->GetAbsOrigin(); + } + + // this is an array of prospective respondents. + CAI_Expresser * RESTRICT pBestEx[EXARRAYMAX]; + AI_Response responseToSay[EXARRAYMAX]; + int numExFound = 0; // and this is the high water mark for the array. + + // Here's the algorithm: we're going to walk through all the characters, finding the + // highest scoring ones for this rule. Let the highest score be called k. + // Because there may be (n) many characters all scoring k, we store an array of + // all characters with score k, then choose randomly from that array at return. + // We also define an allowable error for k in the global cvar + // rr_thenany_score_slop , which may be zero. + + // find all characters (except the issuer) + int numExprs = GetNumExpresserTargets(); + AssertMsg1( numExprs <= EXARRAYMAX, "Response queue has %d possible expresser targets, please increase EXARRAYMAX ", numExprs ); + for ( int i = 0 ; i < numExprs; ++i ) + { + pTarget = GetExpresserHost(i); + if ( pTarget == pIssuer ) + continue; // don't dispatch to myself + + if ( !pTarget->IsAlive() ) + continue; // dead men tell no tales + + float distIssuerToTargetSq = 0.0f; + if ( pIssuer ) + { + distIssuerToTargetSq = (pTarget->GetAbsOrigin() - issuerLocation).LengthSqr(); + if ( distIssuerToTargetSq > followupMaxDistSq ) + continue; // too far + } + + pEx = InferExpresserFromBaseEntity(pTarget); + if ( !pEx ) + continue; + + AI_CriteriaSet characterCriteria; + pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL); + characterCriteria.Merge( pDeferredCriteria ); + pTarget->ModifyOrAppendDerivedCriteria( characterCriteria ); + if ( pIssuer ) + { + characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) ); + } + AI_Response prospectiveResponse; + +#ifdef MAPBASE + pEx->SetUsingProspectiveResponses( true ); +#endif + + if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) ) + { + float score = prospectiveResponse.GetMatchScore(); + if ( score > 0 && !prospectiveResponse.IsEmpty() ) // ignore scores that are zero, regardless of slop + { + // if this score is better than all we've seen (outside the slop), then replace the array with + // an entry just to this expresser + if ( score > bestScore + slop ) + { + responseToSay[0] = prospectiveResponse; + pBestEx[0] = pEx; + bestScore = score; + numExFound = 1; + } + else if ( score >= bestScore - slop ) // if this score is at least as good as the best we've seen, but not better than all + { + if ( numExFound >= EXARRAYMAX ) + { +#ifdef MAPBASE + pEx->SetUsingProspectiveResponses( false ); +#endif + continue; // SAFETY: don't overflow the array + } + + responseToSay[numExFound] = prospectiveResponse; + pBestEx[numExFound] = pEx; + bestScore = fpmax( score, bestScore ); + numExFound += 1; + } + } + } + +#ifdef MAPBASE + pEx->SetUsingProspectiveResponses( false ); +#endif + } + + // if I have a response, dispatch it. + if ( numExFound > 0 ) + { + // get a random number between 0 and the responses found + int iSelect = numExFound > 1 ? RandomInt( 0, numExFound - 1 ) : 0; + + if ( pBestEx[iSelect] != NULL ) + { +#ifdef MAPBASE + pBestEx[iSelect]->MarkResponseAsUsed( responseToSay + iSelect ); +#endif + return pBestEx[iSelect]->SpeakDispatchResponse( response.m_concept, responseToSay + iSelect, pDeferredCriteria ); + } + else + { + AssertMsg( false, "Response queue somehow found a response, but no expresser for it.\n" ); + return false; + } + } + else + { // I did not find a response. + return false; + } + + return false; // just in case +} + +void CResponseQueue::Evacuate() +{ + m_Queue.RemoveAll(); +} + +#undef EXARRAYMAX + + +/////////////////////////////////////////////////////////////////////////////// +// RESPONSE QUEUE MANAGER +/////////////////////////////////////////////////////////////////////////////// + + +void CResponseQueueManager::LevelInitPreEntity( void ) +{ + if (m_pQueue == NULL) + { + m_pQueue = new CResponseQueue(AI_RESPONSE_QUEUE_SIZE); + } +} + +CResponseQueueManager::~CResponseQueueManager() +{ + if (m_pQueue != NULL) + { + delete m_pQueue; + m_pQueue = NULL; + } +} + +void CResponseQueueManager::Shutdown() +{ + if (m_pQueue != NULL) + { + delete m_pQueue; + m_pQueue = NULL; + } +} + +void CResponseQueueManager::FrameUpdatePostEntityThink() +{ + Assert(m_pQueue); + m_pQueue->PerFrameDispatch(); +} + +CResponseQueueManager g_ResponseQueueManager( "CResponseQueueManager" ); + diff --git a/mp/src/game/server/ai_speechqueue.h b/mp/src/game/server/ai_speechqueue.h new file mode 100644 index 00000000..15101b70 --- /dev/null +++ b/mp/src/game/server/ai_speechqueue.h @@ -0,0 +1,239 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: An event queue of AI concepts that dispatches them to appropriate characters. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_SPEECHQUEUE_H +#define AI_SPEECHQUEUE_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "ai_speech.h" + +#define AI_RESPONSE_QUEUE_SIZE 64 + +enum DeferredResponseTarget_t // possible targets for a deferred response +{ + kDRT_ANY, // best matching respondent within range -- except for the one in the m_hTarget handle + kDRT_ALL, // send to everyone in range -- except for the one in the m_hTarget handle + kDRT_SPECIFIC, // a specific entity is targeted + + kDRT_MAX, // high water mark +}; + +// Allows you to postpone AI speech concepts to a later time, or to direct them to +// a specific character, or all of them. +class CResponseQueue +{ + //////////////////// Local types //////////////////// +public: + + // We pack up contexts to send along with the concept. + // For now I'll just copy criteria sets, but it will be better to do something + // more efficient in the future. + typedef AI_CriteriaSet DeferredContexts_t; + + struct CFollowupTargetSpec_t ///< to whom a followup is directed. Can be a specific entity or something more exotic. + { + DeferredResponseTarget_t m_iTargetType; ///< ANY, ALL, or SPECIFIC. If specific, pass through a handle to: + EHANDLE m_hHandle; ///< a specific target for the message, or a specific character to OMIT. + inline bool IsValid( void ) const; + + // constructors/destructors + explicit CFollowupTargetSpec_t(const DeferredResponseTarget_t &targetType, const EHANDLE &handle) + : m_iTargetType(targetType), m_hHandle(handle) + {}; + explicit CFollowupTargetSpec_t(const EHANDLE &handle) + : m_iTargetType(kDRT_SPECIFIC), m_hHandle(handle) + {}; + CFollowupTargetSpec_t(DeferredResponseTarget_t target) // eg, ANY, ALL, etc. + : m_iTargetType(target) + { + AssertMsg(m_iTargetType != kDRT_SPECIFIC, "Response rule followup tried to specify an entity target, but didn't provide the target.\n" ); + } + CFollowupTargetSpec_t(void) // default: invalid + : m_iTargetType(kDRT_MAX) + {}; + }; + + /// A single deferred response. + struct CDeferredResponse + { + AIConcept_t m_concept; + DeferredContexts_t m_contexts; ///< contexts to send along with the concept + float m_fDispatchTime; + EHANDLE m_hIssuer; ///< an entity, if issued by an entity + /* + DeferredResponseTarget_t m_iTargetType; + EHANDLE m_hTarget; // May be invalid. + */ + CFollowupTargetSpec_t m_Target; + + inline void Init( const AIConcept_t &concept, const AI_CriteriaSet * RESTRICT contexts, float dtime, const CFollowupTargetSpec_t &target, CBaseEntity *pIssuer ); + inline bool IsQuashed() { return !m_Target.IsValid(); } + void Quash(); ///< make this response invalid. + }; + /// write + static void DeferContextsFromCriteriaSet( DeferredContexts_t &contextsOut, const AI_CriteriaSet *criteriaIn ); + + //////////////////// Methods //////////////////// +public: + CResponseQueue( int queueSize ); + + /// Add a deferred response. + void Add( const AIConcept_t &concept, ///< concept to dispatch + const AI_CriteriaSet * RESTRICT contexts, ///< the contexts that come with it (may be NULL) + float time, ///< when to dispatch it. You can specify a time of zero to mean "immediately." + const CFollowupTargetSpec_t &targetspec, /// All information necessary to target this response + CBaseEntity *pIssuer = NULL ///< the entity who should not respond if this is a ANY or ALL rule. (eg, don't let people talk to themselves.) + ); + + /// Remove all deferred responses matching the concept and issuer. + void Remove( const AIConcept_t &concept, ///< concept to dispatch + CBaseEntity * const pIssuer = NULL ///< the entity issuing the response, if one exists. + ); + + /// Remove all deferred responses queued to be spoken by given character + void RemoveSpeechQueuedFor( const CBaseEntity *pSpeaker ); + + /// Empty out all pending events + void Evacuate(); + + /// Go through and dispatch any deferred responses. + void PerFrameDispatch(); + + /// Add an expressor owner to this queue. + void AddExpresserHost(CBaseEntity *host); + + /// Remove an expresser host from this queue. + void RemoveExpresserHost(CBaseEntity *host); + + /// Iterate over potential expressers for this queue + inline int GetNumExpresserTargets() const; + inline CBaseEntity *GetExpresserHost(int which) const; + +protected: + /// Actually send off one response to a consumer + /// Return true if dispatch succeeded + bool DispatchOneResponse( CDeferredResponse &response ); + +private: + /// Helper function for one case in DispatchOneResponse + /// (for better organization) + bool DispatchOneResponse_ThenANY( CDeferredResponse &response, AI_CriteriaSet * RESTRICT pDeferredCriteria, CBaseEntity * const RESTRICT pIssuer, float followupMaxDistSq ); + + //////////////////// Data //////////////////// +protected: + typedef CUtlFixedLinkedList< CDeferredResponse > QueueType_t; + QueueType_t m_Queue; // the queue of deferred responses, will eventually be sorted + /// Note about the queue type: if you move to replace it with a sorted priority queue, + /// make sure it is a type such that an iterator is not invalidated by inserts and deletes. + /// CResponseQueue::PerFrameDispatch() iterates over the queue calling DispatchOneResponse + /// on each in turn, and those responses may very easily add new events to the queue. + /// A crash will result if the iterator used in CResponseQueue::PerFrameDispatch()'s loop + /// becomes invalid. + + CUtlVector m_ExpresserTargets; // a list of legitimate expresser targets +}; + +inline void CResponseQueue::CDeferredResponse::Init(const AIConcept_t &concept, const AI_CriteriaSet * RESTRICT contexts, float dtime, const CFollowupTargetSpec_t &target, CBaseEntity *pIssuer ) +{ + m_concept = concept; + m_fDispatchTime = dtime; + /* + m_iTargetType = targetType; + m_hTarget = handle ; + */ + m_Target = target; + m_hIssuer = pIssuer; + DeferContextsFromCriteriaSet(m_contexts, contexts); +} + +int CResponseQueue::GetNumExpresserTargets() const +{ + return m_ExpresserTargets.Count(); +} + +CBaseEntity *CResponseQueue::GetExpresserHost(int which) const +{ + return m_ExpresserTargets[which]; +} + + +// The wrapper game system that contains a response queue, and ticks it each frame. + +class CResponseQueueManager : public CAutoGameSystemPerFrame +{ +public: + CResponseQueueManager(char const *name) : CAutoGameSystemPerFrame( name ) + { + m_pQueue = NULL; + } + virtual ~CResponseQueueManager(void); + virtual void Shutdown(); + virtual void FrameUpdatePostEntityThink( void ); + virtual void LevelInitPreEntity( void ); + + inline CResponseQueue *GetQueue(void) { Assert(m_pQueue); return m_pQueue; } + +protected: + CResponseQueue *m_pQueue; +}; + + +// Valid if the target type enum is within bounds. Furthermore if it +// specifies a specific entity, that handle must be valid. +bool CResponseQueue::CFollowupTargetSpec_t::IsValid( void ) const +{ + if (m_iTargetType >= kDRT_MAX) + return false; + if (m_iTargetType < 0) + return false; + if (m_iTargetType == kDRT_SPECIFIC && !m_hHandle.IsValid()) + return false; + + return true; +} + +extern CResponseQueueManager g_ResponseQueueManager; + + +// Handy global helper funcs + +/// Automatically queue up speech to happen immediately -- calls straight through to response rules add +inline void QueueSpeak( const AIConcept_t &concept, ///< concept name to say + const CResponseQueue::CFollowupTargetSpec_t& targetspec, ///< kDRT_ANY, kDRT_ALL, etc + CBaseEntity *pIssuer = NULL ///< if specifying ANY or ALL, use this to specify the one you *don't* want to speak + ) +{ + return g_ResponseQueueManager.GetQueue()->Add( concept, NULL, 0.0f, targetspec, pIssuer ); +} + +/// Automatically queue up speech to happen immediately -- calls straight through to response rules add +inline void QueueSpeak( const AIConcept_t &concept, ///< concept name to say + const CResponseQueue::CFollowupTargetSpec_t& targetspec, ///< kDRT_ANY, kDRT_ALL, etc + const AI_CriteriaSet &criteria, ///< criteria to pass in + CBaseEntity *pIssuer = NULL ///< if specifying ANY or ALL, use this to specify the one you *don't* want to speak + ) +{ + return g_ResponseQueueManager.GetQueue()->Add( concept, &criteria, 0.0f, targetspec, pIssuer ); +} + +/// Automatically queue up speech to happen immediately -- calls straight through to response rules add +inline void QueueSpeak( const AIConcept_t &concept, ///< concept name to say + const EHANDLE &target, ///< which entity shall speak + float delay, ///< how far in the future to speak + const AI_CriteriaSet &criteria, ///< criteria to pass in + CBaseEntity *pIssuer = NULL ) +{ + return g_ResponseQueueManager.GetQueue()->Add( concept, &criteria, gpGlobals->curtime + delay, + CResponseQueue::CFollowupTargetSpec_t(target), pIssuer ); +} + + + +#endif // AI_SPEECHQUEUE_H diff --git a/mp/src/game/server/ai_tacticalservices.cpp b/mp/src/game/server/ai_tacticalservices.cpp index 87399c4f..8bcdcc08 100644 --- a/mp/src/game/server/ai_tacticalservices.cpp +++ b/mp/src/game/server/ai_tacticalservices.cpp @@ -411,7 +411,12 @@ int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vT // -------------------------------------------------------- pNode->Lock( 1.0 ); +#ifdef MAPBASE + if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW + || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_CUSTOM ) ) +#else if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) ) +#endif { if ( GetOuter()->GetHintNode() ) { diff --git a/mp/src/game/server/baseanimating.cpp b/mp/src/game/server/baseanimating.cpp index 446af96d..14510f89 100644 --- a/mp/src/game/server/baseanimating.cpp +++ b/mp/src/game/server/baseanimating.cpp @@ -32,6 +32,9 @@ #include "gib.h" #include "CRagdollMagnet.h" #endif +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_funcs_shared.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -282,6 +285,11 @@ IMPLEMENT_SERVERCLASS_ST(CBaseAnimating, DT_BaseAnimating) END_SEND_TABLE() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t CBaseAnimating::g_Hook_OnServerRagdoll; +ScriptHook_t CBaseAnimating::g_Hook_HandleAnimEvent; +#endif + BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) DEFINE_SCRIPTFUNC( LookupAttachment, "Get the named attachement id" ) @@ -297,7 +305,7 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) 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( SequenceLoops, "Does the current sequence loop?" ) 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" ) @@ -310,6 +318,8 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) 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( ResetSequenceInfo, "" ) + DEFINE_SCRIPTFUNC( StudioFrameAdvance, "" ) DEFINE_SCRIPTFUNC( GetPlaybackRate, "" ) DEFINE_SCRIPTFUNC( SetPlaybackRate, "" ) DEFINE_SCRIPTFUNC( GetCycle, "" ) @@ -333,6 +343,15 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) DEFINE_SCRIPTFUNC( BecomeRagdollOnClient, "" ) DEFINE_SCRIPTFUNC( IsRagdoll, "" ) DEFINE_SCRIPTFUNC( CanBecomeRagdoll, "" ) + + BEGIN_SCRIPTHOOK( CBaseAnimating::g_Hook_OnServerRagdoll, "OnServerRagdoll", FIELD_VOID, "Called when this entity creates/turns into a server-side ragdoll." ) + DEFINE_SCRIPTHOOK_PARAM( "ragdoll", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "submodel", FIELD_BOOLEAN ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseAnimating::g_Hook_HandleAnimEvent, "HandleAnimEvent", FIELD_BOOLEAN, "Called when handling animation events. Return false to cancel base handling." ) + DEFINE_SCRIPTHOOK_PARAM( "event", FIELD_HSCRIPT ) + END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -492,6 +511,11 @@ void CBaseAnimating::StudioFrameAdvanceInternal( CStudioHdr *pStudioHdr, float f float flNewCycle = GetCycle() + flCycleDelta; if (flNewCycle < 0.0 || flNewCycle >= 1.0) { + if (flNewCycle >= 1.0f) + { + ReachedEndOfSequence(); + } + if (m_bSequenceLoops) { flNewCycle -= (int)(flNewCycle); @@ -1238,6 +1262,11 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler ) event.eventtime = m_flAnimTime + (flCycle - GetCycle()) / flCycleRate + GetAnimTimeInterval(); } +#ifdef MAPBASE_VSCRIPT + if (eventHandler->ScriptHookHandleAnimEvent( &event ) == false) + continue; +#endif + /* if (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { @@ -1268,6 +1297,29 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler ) } } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseAnimating::ScriptHookHandleAnimEvent( animevent_t *pEvent ) +{ + if (m_ScriptScope.IsInitialized() && g_Hook_HandleAnimEvent.CanRunInScope(m_ScriptScope)) + { + HSCRIPT hEvent = g_pScriptVM->RegisterInstance( reinterpret_cast(pEvent) ); + + // event + ScriptVariant_t args[] = { hEvent }; + ScriptVariant_t returnValue = true; + g_Hook_HandleAnimEvent.Call( m_ScriptScope, &returnValue, args ); + + g_pScriptVM->RemoveInstance( hEvent ); + return returnValue.m_bool; + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1902,7 +1954,7 @@ ConVar ai_setupbones_debug( "ai_setupbones_debug", "0", 0, "Shows that bones tha -inline bool CBaseAnimating::CanSkipAnimation( void ) +bool CBaseAnimating::CanSkipAnimation( void ) { if ( !sv_pvsskipanimation.GetBool() ) return false; @@ -2882,9 +2934,9 @@ void CBaseAnimating::InvalidateBoneCache( void ) bool CBaseAnimating::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) { // Return a special case for scaled physics objects - if ( GetModelScale() != 1.0f ) + IPhysicsObject *pPhysObject = VPhysicsGetObject(); + if ( GetModelScale() != 1.0f && pPhysObject ) { - IPhysicsObject *pPhysObject = VPhysicsGetObject(); Vector vecPosition; QAngle vecAngles; pPhysObject->GetPosition( &vecPosition, &vecAngles ); diff --git a/mp/src/game/server/baseanimating.h b/mp/src/game/server/baseanimating.h index a5d0dc0d..b74a9360 100644 --- a/mp/src/game/server/baseanimating.h +++ b/mp/src/game/server/baseanimating.h @@ -84,6 +84,7 @@ public: virtual void StudioFrameAdvance(); // advance animation frame to some time in the future void StudioFrameAdvanceManual( float flInterval ); bool IsValidSequence( int iSequence ); + virtual void ReachedEndOfSequence() { return; } inline float GetPlaybackRate(); inline void SetPlaybackRate( float rate ); @@ -145,6 +146,9 @@ public: bool HasAnimEvent( int nSequence, int nEvent ); virtual void DispatchAnimEvents ( CBaseAnimating *eventHandler ); // Handle events that have happend since last time called up until X seconds into the future virtual void HandleAnimEvent( animevent_t *pEvent ); +#ifdef MAPBASE_VSCRIPT + bool ScriptHookHandleAnimEvent( animevent_t *pEvent ); +#endif int LookupPoseParameter( CStudioHdr *pStudioHdr, const char *szName ); inline int LookupPoseParameter( const char *szName ) { return LookupPoseParameter(GetModelPtr(), szName); } @@ -210,6 +214,9 @@ public: // For VScript int GetSkin() { return m_nSkin; } void SetSkin( int iSkin ) { m_nSkin = iSkin; } + + static ScriptHook_t g_Hook_OnServerRagdoll; + static ScriptHook_t g_Hook_HandleAnimEvent; #endif // These return the attachment in the space of the entity @@ -382,6 +389,8 @@ private: void InputSetCycle( inputdata_t &inputdata ); void InputSetPlaybackRate( inputdata_t &inputdata ); + +public: // From Alien Swarm SDK #endif bool CanSkipAnimation( void ); diff --git a/mp/src/game/server/basebludgeonweapon.cpp b/mp/src/game/server/basebludgeonweapon.cpp index cb9c1fc8..57683a19 100644 --- a/mp/src/game/server/basebludgeonweapon.cpp +++ b/mp/src/game/server/basebludgeonweapon.cpp @@ -406,4 +406,8 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary ) //Play swing sound WeaponSound( SINGLE ); #endif + +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_ATTACK1 ); +#endif } diff --git a/mp/src/game/server/basecombatcharacter.cpp b/mp/src/game/server/basecombatcharacter.cpp index 3a82ce65..01932587 100644 --- a/mp/src/game/server/basecombatcharacter.cpp +++ b/mp/src/game/server/basecombatcharacter.cpp @@ -61,11 +61,6 @@ 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" ); @@ -151,13 +146,16 @@ BEGIN_DATADESC( CBaseCombatCharacter ) END_DATADESC() #ifdef MAPBASE_VSCRIPT +ScriptHook_t CBaseCombatCharacter::g_Hook_RelationshipType; +ScriptHook_t CBaseCombatCharacter::g_Hook_RelationshipPriority; + 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_NAMED( ScriptGetActiveWeapon, "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( ScriptGetWeapon, "GetWeapon", "Get a specific weapon in the character's inventory." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeaponByType, "FindWeapon", "Find a specific weapon in the character's inventory by its classname." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAllWeapons, "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." ) @@ -165,6 +163,8 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by 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( ScriptGiveAmmo, "GiveAmmo", "Gives the specified amount of the specified ammo type. The third parameter is whether or not to suppress the ammo pickup sound. Returns the amount of ammo actually given, which is 0 if the player's ammo for this type is already full." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveAmmo, "RemoveAmmo", "Removes the specified amount of the specified ammo type." ) 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." ) @@ -176,7 +176,7 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by 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( ScriptGetVehicleEntity, "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." ) @@ -192,6 +192,19 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by DEFINE_SCRIPTFUNC( EyeDirection2D, "Get the eyes' 2D direction." ) DEFINE_SCRIPTFUNC( EyeDirection3D, "Get the eyes' 3D direction." ) + // + // Hooks + // + BEGIN_SCRIPTHOOK( CBaseCombatCharacter::g_Hook_RelationshipType, "RelationshipType", FIELD_INTEGER, "Called when a character's relationship to another entity is requested. Returning a disposition will make the game use that disposition instead of the default relationship. (note: 'default' in this case includes overrides from ai_relationship/SetRelationship)" ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "def", FIELD_INTEGER ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseCombatCharacter::g_Hook_RelationshipPriority, "RelationshipPriority", FIELD_INTEGER, "Called when a character's relationship priority for another entity is requested. Returning a number will make the game use that priority instead of the default priority. (note: 'default' in this case includes overrides from ai_relationship/SetRelationship)" ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "def", FIELD_INTEGER ) + END_SCRIPTHOOK() + END_SCRIPTDESC(); #endif @@ -1725,25 +1738,6 @@ 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 @@ -1791,7 +1785,14 @@ void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info ) // if flagged to drop a health kit if (HasSpawnFlags(SF_NPC_DROP_HEALTHKIT)) { - CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles() ); + CBaseEntity *pItem = CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles() ); + if (pItem) + { +#ifdef MAPBASE + if (MyNPCPointer()) + MyNPCPointer()->m_OnItemDrop.Set( pItem, pItem, this ); +#endif + } } // clear the deceased's sound channels.(may have been firing or reloading when killed) EmitSound( "BaseCombatCharacter.StopWeaponSounds" ); @@ -2726,10 +2727,38 @@ bool CBaseCombatCharacter::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) } #ifdef MAPBASE + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static Activity Weapon_BackupActivityFromList( CBaseCombatCharacter *pBCC, acttable_t *pTable, int actCount, Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pWeapon ) +{ + int i = 0; + for ( ; i < actCount; i++, pTable++ ) + { + if ( activity == pTable->baseAct ) + { + // Don't pick backup activities we don't actually have an animation for. + if (!pBCC->GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct)) + break; + + return (Activity)pTable->weaponAct; + } + } + + // We didn't succeed in finding an activity. See if we can recurse + acttable_t *pBackupTable = CBaseCombatWeapon::GetDefaultBackupActivityList( pTable - i, actCount ); + if (pBackupTable) + { + return Weapon_BackupActivityFromList( pBCC, pBackupTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); + } + + return activity; +} + //----------------------------------------------------------------------------- // 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. +// This gives NPCs and players the ability to use weapons they are otherwise unable to use. //----------------------------------------------------------------------------- Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon ) { @@ -2741,26 +2770,26 @@ Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool we 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)) + // UNDONE: Sometimes, a 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) && !IsPlayer()) { return activity; } + */ - acttable_t *pTable = GetSMG1Acttable(); - int actCount = GetSMG1ActtableCount(); - for ( int i = 0; i < actCount; i++, pTable++ ) + acttable_t *pTable = pWeapon->GetBackupActivityList(); + int actCount = pWeapon->GetBackupActivityListCount(); + if (!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; - } + // Look for a default list + acttable_t *pTable = pWeapon->ActivityList( actCount ); + pTable = CBaseCombatWeapon::GetDefaultBackupActivityList( pTable, actCount ); + } - return (Activity)pTable->weaponAct; - } + if (pTable && GetModelPtr()) + { + return Weapon_BackupActivityFromList( this, pTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); } return activity; @@ -2893,6 +2922,12 @@ int CBaseCombatCharacter::OnTakeDamage( const CTakeDamageInfo &info ) #endif if ( m_iHealth <= 0 ) { +#ifdef MAPBASE_VSCRIPT + // False = Cheat death + if (ScriptDeathHook( const_cast(&info) ) == false) + return retVal; +#endif + IPhysicsObject *pPhysics = VPhysicsGetObject(); if ( pPhysics ) { @@ -3296,7 +3331,24 @@ Relationship_t *CBaseCombatCharacter::FindEntityRelationship( CBaseEntity *pTarg Disposition_t CBaseCombatCharacter::IRelationType ( CBaseEntity *pTarget ) { if ( pTarget ) + { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_RelationshipType.CanRunInScope( m_ScriptScope )) + { + // entity, default + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( pTarget->GetScriptInstance() ), FindEntityRelationship( pTarget )->disposition }; + if (g_Hook_RelationshipType.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_INTEGER && functionReturn.m_int != D_ER)) + { + // Use the disposition returned by the script + return (Disposition_t)functionReturn.m_int; + } + } +#endif + return FindEntityRelationship( pTarget )->disposition; + } + return D_NU; } @@ -3308,7 +3360,24 @@ Disposition_t CBaseCombatCharacter::IRelationType ( CBaseEntity *pTarget ) int CBaseCombatCharacter::IRelationPriority( CBaseEntity *pTarget ) { if ( pTarget ) + { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_RelationshipPriority.CanRunInScope( m_ScriptScope )) + { + // entity, default + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( pTarget->GetScriptInstance() ), FindEntityRelationship( pTarget )->priority }; + if (g_Hook_RelationshipPriority.Call( m_ScriptScope, &functionReturn, args ) && functionReturn.m_type == FIELD_INTEGER) + { + // Use the priority returned by the script + return functionReturn.m_int; + } + } +#endif + return FindEntityRelationship( pTarget )->priority; + } + return 0; } @@ -4068,14 +4137,13 @@ void CBaseCombatCharacter::InputKilledNPC( inputdata_t &inputdata ) #ifdef MAPBASE //----------------------------------------------------------------------------- -// Purpose: Handle enemy kills. This actually measures players too. +// Purpose: Handle enemy kills. (this technically 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 + // Fire an additional output if this was a player if (pKilled && pKilled->IsPlayer()) m_OnKilledPlayer.Set(pKilled, pKilled, this); } @@ -4160,7 +4228,7 @@ void CBaseCombatCharacter::InputPickupWeaponInstant( inputdata_t &inputdata ) } else { - Warning("%s received PickupWeaponInstant with invalid entity %s\n", GetDebugName(), inputdata.value.Entity() ? "null" : inputdata.value.Entity()->GetDebugName()); + Warning("%s received PickupWeaponInstant with invalid entity %s\n", GetDebugName(), inputdata.value.Entity() ? inputdata.value.Entity()->GetDebugName() : "<>"); } } @@ -4335,9 +4403,8 @@ CBaseEntity *CBaseCombatCharacter::FindNamedEntity( const char *szName, IEntityF 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. + // HACKHACK: FindEntityProcedural can go through this now, so running this code could cause an infinite loop. + // As a result, FindEntityProcedural currently identifies itself with this entity filter. else if (!pFilter || !dynamic_cast(pFilter)) { // search for up to 32 entities with the same name and choose one randomly @@ -4389,28 +4456,33 @@ void CBaseCombatCharacter::DoMuzzleFlash() #ifdef MAPBASE_VSCRIPT //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -HSCRIPT CBaseCombatCharacter::GetScriptActiveWeapon() +HSCRIPT CBaseCombatCharacter::ScriptGetActiveWeapon() { return ToHScript( GetActiveWeapon() ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -HSCRIPT CBaseCombatCharacter::GetScriptWeaponIndex( int i ) +HSCRIPT CBaseCombatCharacter::ScriptGetWeapon( int i ) { + Assert( i >= 0 && i < MAX_WEAPONS ); + + if ( i < 0 || i >= MAX_WEAPONS ) + return NULL; + return ToHScript( GetWeapon( i ) ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -HSCRIPT CBaseCombatCharacter::GetScriptWeaponByType( const char *pszWeapon, int iSubType ) +HSCRIPT CBaseCombatCharacter::ScriptGetWeaponByType( const char *pszWeapon, int iSubType ) { return ToHScript( Weapon_OwnsThisType( pszWeapon, iSubType ) ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void CBaseCombatCharacter::GetScriptAllWeapons( HSCRIPT hTable ) +void CBaseCombatCharacter::ScriptGetAllWeapons( HSCRIPT hTable ) { for (int i=0;i= MAX_AMMO_SLOTS ) + return 0; + return GetAmmoCount( iType ); } @@ -4486,11 +4583,10 @@ int CBaseCombatCharacter::ScriptGetAmmoCount( int iType ) const //----------------------------------------------------------------------------- void CBaseCombatCharacter::ScriptSetAmmoCount( int iType, int iCount ) { - if (iType == -1) - { - Warning("%i is not a valid ammo type\n", iType); + Assert( iType == -1 || iType < MAX_AMMO_SLOTS ); + + if ( iType < 0 || iType >= MAX_AMMO_SLOTS ) return; - } return SetAmmoCount( iCount, iType ); } @@ -4549,7 +4645,7 @@ void CBaseCombatCharacter::ScriptSetRelationship( HSCRIPT pTarget, int dispositi //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -HSCRIPT CBaseCombatCharacter::GetScriptVehicleEntity() +HSCRIPT CBaseCombatCharacter::ScriptGetVehicleEntity() { return ToHScript( GetVehicleEntity() ); } diff --git a/mp/src/game/server/basecombatcharacter.h b/mp/src/game/server/basecombatcharacter.h index 6d971ca3..c7eb9960 100644 --- a/mp/src/game/server/basecombatcharacter.h +++ b/mp/src/game/server/basecombatcharacter.h @@ -412,20 +412,25 @@ public: } void SetCurrentWeaponProficiency( WeaponProficiency_t iProficiency ) { m_CurrentWeaponProficiency = iProficiency; } virtual WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ); +#ifdef MAPBASE + inline bool OverridingWeaponProficiency() { return (m_ProficiencyOverride > WEAPON_PROFICIENCY_INVALID); } +#endif 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 ); + HSCRIPT ScriptGetActiveWeapon(); + HSCRIPT ScriptGetWeapon( int i ); + HSCRIPT ScriptGetWeaponByType( const char *pszWeapon, int iSubType = 0 ); + void ScriptGetAllWeapons( HSCRIPT hTable ); int ScriptGetCurrentWeaponProficiency() { return GetCurrentWeaponProficiency(); } void ScriptDropWeapon( HSCRIPT hWeapon ); void ScriptEquipWeapon( HSCRIPT hWeapon ); - + + int ScriptGiveAmmo( int iCount, int iAmmoIndex, bool bSuppressSound = false ); + void ScriptRemoveAmmo( int iCount, int iAmmoIndex ); int ScriptGetAmmoCount( int iType ) const; void ScriptSetAmmoCount( int iType, int iCount ); @@ -436,7 +441,7 @@ public: int ScriptRelationPriority( HSCRIPT pTarget ); void ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority ); - HSCRIPT GetScriptVehicleEntity(); + HSCRIPT ScriptGetVehicleEntity(); bool ScriptInViewCone( const Vector &vecSpot ) { return FInViewCone( vecSpot ); } bool ScriptEntInViewCone( HSCRIPT pEntity ) { return FInViewCone( ToEnt( pEntity ) ); } @@ -445,6 +450,9 @@ public: 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; } + + static ScriptHook_t g_Hook_RelationshipType; + static ScriptHook_t g_Hook_RelationshipPriority; #endif // Interactions diff --git a/mp/src/game/server/basecombatweapon.cpp b/mp/src/game/server/basecombatweapon.cpp index 1135aa82..2863b747 100644 --- a/mp/src/game/server/basecombatweapon.cpp +++ b/mp/src/game/server/basecombatweapon.cpp @@ -373,7 +373,12 @@ bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector //----------------------------------------------------------------------------- int CBaseCombatWeapon::WeaponRangeAttack1Condition( float flDot, float flDist ) { +#ifdef MAPBASE + // HACKHACK: HasPrimaryAmmo() checks the NPC's reserve ammo counts, which should not be evaluated here if we use clips + if ( UsesPrimaryAmmo() && (UsesClipsForAmmo1() ? !m_iClip1 : !HasPrimaryAmmo()) ) +#else if ( UsesPrimaryAmmo() && !HasPrimaryAmmo() ) +#endif { return COND_NO_PRIMARY_AMMO; } @@ -482,7 +487,11 @@ void CBaseCombatWeapon::Kill( void ) //----------------------------------------------------------------------------- void CBaseCombatWeapon::FallInit( void ) { +#ifdef MAPBASE + SetModel( (GetDroppedModel() && GetDroppedModel()[0]) ? GetDroppedModel() : GetWorldModel() ); +#else SetModel( GetWorldModel() ); +#endif VPhysicsDestroyObject(); if ( !VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ) ) diff --git a/mp/src/game/server/baseentity.cpp b/mp/src/game/server/baseentity.cpp index 27900eca..ce6f6f6f 100644 --- a/mp/src/game/server/baseentity.cpp +++ b/mp/src/game/server/baseentity.cpp @@ -66,6 +66,9 @@ #include "mapbase/matchers.h" #include "mapbase/datadesc_mod.h" #endif +#ifdef NEW_RESPONSE_SYSTEM +#include "ai_speech.h" +#endif #if defined( TF_DLL ) #include "tf_gamerules.h" @@ -104,6 +107,10 @@ ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTO ConVar sv_script_think_interval("sv_script_think_interval", "0.1"); +#ifdef MAPBASE_VSCRIPT +ConVar ent_text_allow_script( "ent_text_allow_script", "1" ); +#endif + // This table encodes edict data. void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) @@ -285,6 +292,7 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) #ifdef MAPBASE // Keep consistent with VIEW_ID_COUNT in viewrender.h SendPropInt (SENDINFO(m_iViewHideFlags), 9, SPROP_UNSIGNED ), + SendPropBool (SENDINFO(m_bDisableFlashlight) ), #endif SendPropInt (SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0), SendPropInt (SENDINFO(m_CollisionGroup), 5, SPROP_UNSIGNED), @@ -453,6 +461,17 @@ extern bool g_bDisableEhandleAccess; //----------------------------------------------------------------------------- CBaseEntity::~CBaseEntity( ) { +#ifdef MAPBASE_VSCRIPT + // HACKHACK: This is needed to fix a crash when an entity removes itself with Destroy() during its own think function. + // (see https://github.com/mapbase-source/source-sdk-2013/issues/138) + FOR_EACH_VEC( m_ScriptThinkFuncs, i ) + { + HSCRIPT h = m_ScriptThinkFuncs[i]->m_hfnThink; + if ( h ) g_pScriptVM->ReleaseScript( h ); + } + m_ScriptThinkFuncs.PurgeAndDeleteElements(); +#endif // MAPBASE_VSCRIPT + // FIXME: This can't be called from UpdateOnRemove! There's at least one // case where friction sounds are added between the call to UpdateOnRemove + ~CBaseEntity PhysCleanupFrictionSounds( this ); @@ -626,18 +645,6 @@ 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 ); @@ -1071,6 +1078,28 @@ int CBaseEntity::DrawDebugTextOverlays(void) offset++; } #endif + +#ifdef MAPBASE_VSCRIPT + // 'OnEntText' hook inspired by later source games + if (m_ScriptScope.IsInitialized() && g_Hook_OnEntText.CanRunInScope( m_ScriptScope )) + { + if (ent_text_allow_script.GetBool()) + { + ScriptVariant_t functionReturn; + if ( g_Hook_OnEntText.Call( m_ScriptScope, &functionReturn, NULL ) && functionReturn.m_type == FIELD_CSTRING ) + { + CUtlStringList outStrings; + V_SplitString( functionReturn.m_pszString, "\n", outStrings ); + + FOR_EACH_VEC( outStrings, i ) + { + EntityText( offset, outStrings[i], 0, 224, 240, 255 ); + offset++; + } + } + } + } +#endif } if (m_debugOverlays & OVERLAY_VIEWOFFSET) @@ -1360,10 +1389,10 @@ float CBaseEntity::GetMaxOutputDelay( const char *pszOutput ) return 0; } -void CBaseEntity::CancelEventsByInput( const char *szInput ) -{ - g_EventQueue.CancelEventsByInput( this, szInput ); -} +//void CBaseEntity::CancelEventsByInput( const char *szInput ) +//{ +// g_EventQueue.CancelEventsByInput( this, szInput ); +//} #endif // MAPBASE_VSCRIPT CBaseEntityOutput *CBaseEntity::FindNamedOutput( const char *pszOutput ) @@ -1731,22 +1760,9 @@ int CBaseEntity::VPhysicsTakeDamage( const CTakeDamageInfo &info ) 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 ); - } + // False = Cheat death + if (ScriptDeathHook( const_cast(&info) ) == false) + return; #endif if( info.GetAttacker() ) @@ -1783,6 +1799,22 @@ void CBaseEntity::SendOnKilledGameEvent( const CTakeDamageInfo &info ) } } +void CBaseEntity::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_OnKilledOther.CanRunInScope( m_ScriptScope )) + { + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + // victim, info + ScriptVariant_t args[] = { ScriptVariant_t( pVictim->GetScriptInstance() ), ScriptVariant_t( hInfo ) }; + g_Hook_OnKilledOther.Call( m_ScriptScope, NULL, args ); + + g_pScriptVM->RemoveInstance( hInfo ); + } +#endif +} + bool CBaseEntity::HasTarget( string_t targetname ) { @@ -1931,6 +1963,7 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_GLOBAL_KEYFIELD( m_nModelIndex, FIELD_SHORT, "modelindex" ), #ifdef MAPBASE DEFINE_KEYFIELD( m_iViewHideFlags, FIELD_INTEGER, "viewhideflags" ), + DEFINE_KEYFIELD( m_bDisableFlashlight, FIELD_BOOLEAN, "disableflashlight" ), #endif #if !defined( NO_ENTITY_PREDICTION ) // DEFINE_FIELD( m_PredictableID, CPredictableId ), @@ -2164,6 +2197,8 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveEffects", InputRemoveEffects ), DEFINE_INPUTFUNC( FIELD_VOID, "EnableDraw", InputDrawEntity ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableDraw", InputUndrawEntity ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableReceivingFlashlight", InputEnableReceivingFlashlight ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableReceivingFlashlight", InputDisableReceivingFlashlight ), DEFINE_INPUTFUNC( FIELD_INTEGER, "AddEFlags", InputAddEFlags ), DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveEFlags", InputRemoveEFlags ), DEFINE_INPUTFUNC( FIELD_INTEGER, "AddSolidFlags", InputAddSolidFlags ), @@ -2194,7 +2229,7 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_THINKFUNC( ShadowCastDistThink ), DEFINE_THINKFUNC( ScriptThink ), #ifdef MAPBASE_VSCRIPT - DEFINE_THINKFUNC( ScriptThinkH ), + DEFINE_THINKFUNC( ScriptContextThink ), #endif #ifdef MAPBASE @@ -2215,10 +2250,15 @@ END_DATADESC() #ifdef MAPBASE_VSCRIPT ScriptHook_t CBaseEntity::g_Hook_UpdateOnRemove; +ScriptHook_t CBaseEntity::g_Hook_OnEntText; + ScriptHook_t CBaseEntity::g_Hook_VPhysicsCollision; ScriptHook_t CBaseEntity::g_Hook_FireBullets; ScriptHook_t CBaseEntity::g_Hook_OnDeath; +ScriptHook_t CBaseEntity::g_Hook_OnKilledOther; ScriptHook_t CBaseEntity::g_Hook_HandleInteraction; +ScriptHook_t CBaseEntity::g_Hook_ModifyEmitSoundParams; +ScriptHook_t CBaseEntity::g_Hook_ModifySentenceParams; #endif BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" ) @@ -2234,6 +2274,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( SetModel, "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopSound, "StopSound", "Stops a sound from this entity." ) 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.") @@ -2278,6 +2319,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( ApplyAbsVelocityImpulse, "" ) DEFINE_SCRIPTFUNC( ApplyLocalAngularVelocityImpulse, "" ) + + DEFINE_SCRIPTFUNC( BodyTarget, "" ) + DEFINE_SCRIPTFUNC( HeadTarget, "" ) #endif DEFINE_SCRIPTFUNC_NAMED( GetAbsVelocity, "GetVelocity", "" ) @@ -2294,11 +2338,11 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" 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( 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( Remove, "Destroy", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetOwner, "SetOwner", "" ) DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "" ) DEFINE_SCRIPTFUNC_NAMED( ChangeTeam, "SetTeam", "" ) @@ -2319,6 +2363,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelKeyValues, "GetModelKeyValues", "Get a KeyValue class instance on this entity's model") #ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( Activate, "" ) + 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." ) @@ -2347,7 +2393,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" 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( CancelEventsByInput, "Cancel all I/O events for this entity, match input" ) // Commented out due to unpredictability and unknown risks DEFINE_SCRIPTFUNC_NAMED( ScriptAddOutput, "AddOutput", "Add an output" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValue, "GetKeyValue", "Get a keyvalue" ) @@ -2364,6 +2410,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetRenderColorB", "Set the render color's B value" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetRenderAlpha", "Set the render color's alpha value" ) + // LEGACY DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetColorVector", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetColorR", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetColorG", SCRIPT_HIDE ) @@ -2374,6 +2421,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorG, "SetColorG", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetColorB", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetAlpha", SCRIPT_HIDE ) + // END LEGACY DEFINE_SCRIPTFUNC_NAMED( ScriptGetRenderMode, "GetRenderMode", "Get render mode" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetRenderMode, "SetRenderMode", "Set render mode" ) @@ -2414,7 +2462,10 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( SetFriction, "" ) DEFINE_SCRIPTFUNC( GetMass, "" ) DEFINE_SCRIPTFUNC( SetMass, "" ) - + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSolid, "GetSolid", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetSolid, "SetSolid", "" ) + DEFINE_SCRIPTFUNC( GetSolidFlags, "Get solid flags" ) DEFINE_SCRIPTFUNC( AddSolidFlags, "Add solid flags" ) DEFINE_SCRIPTFUNC( RemoveSolidFlags, "Remove solid flags" ) @@ -2446,6 +2497,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" #ifdef MAPBASE_VSCRIPT DEFINE_SCRIPTFUNC_NAMED( ScriptSetThinkFunction, "SetThinkFunction", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptStopThinkFunction, "StopThinkFunction", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetContextThink, "SetContextThink", "Set a think function on this entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetThink, "SetThink", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptStopThink, "StopThink", "" ) @@ -2453,6 +2505,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" // Hooks // DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." ) + DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_OnEntText, "OnEntText", FIELD_CSTRING, "Called every frame when ent_text is enabled on the entity. Return a string to be added to the ent_text printout." ) 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 ) @@ -2472,11 +2525,24 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT ) END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_OnKilledOther, "OnKilledOther", FIELD_VOID, "Called when the entity kills another entity." ) + DEFINE_SCRIPTHOOK_PARAM( "victim", FIELD_HSCRIPT ) + 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() + + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_ModifyEmitSoundParams, "ModifyEmitSoundParams", FIELD_VOID, "Called every time a sound is emitted on this entity, allowing for its parameters to be modified." ) + DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_ModifySentenceParams, "ModifySentenceParams", FIELD_VOID, "Called every time a sentence is emitted on this entity, allowing for its parameters to be modified." ) + DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT ) + END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -2584,7 +2650,7 @@ void CBaseEntity::UpdateOnRemove( void ) if ( m_hScriptInstance ) { #ifdef MAPBASE_VSCRIPT - if (m_ScriptScope.IsInitialized()) + if ( m_ScriptScope.IsInitialized() && g_Hook_UpdateOnRemove.CanRunInScope( m_ScriptScope ) ) { g_Hook_UpdateOnRemove.Call( m_ScriptScope, NULL, NULL ); } @@ -2592,14 +2658,6 @@ void CBaseEntity::UpdateOnRemove( void ) 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 } } @@ -4620,6 +4678,11 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, { (this->*pfnInput)( data ); } + + if ( m_ScriptScope.IsInitialized() ) + { + ScriptInputHookClearParams(); + } } else if ( dmap->dataDesc[i].flags & FTYPEDESC_KEY ) { @@ -4653,6 +4716,8 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, if (functionReturn.m_bool) return true; } + + ScriptInputHookClearParams(); } #endif @@ -4664,6 +4729,16 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, return false; } +#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: //----------------------------------------------------------------------------- @@ -4686,22 +4761,42 @@ bool CBaseEntity::ScriptInputHook( const char *szInputName, CBaseEntity *pActiva bHandled = true; } + return bHandled; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptInputHookClearParams() +{ 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 ) +bool CBaseEntity::ScriptDeathHook( CTakeDamageInfo *info ) { - variant_t value; - value.SetString( MAKE_STRING(szValue) ); + if (m_ScriptScope.IsInitialized() && g_Hook_OnDeath.CanRunInScope( m_ScriptScope )) + { + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( info ); - return AcceptInput( szInputName, ToEnt(hActivator), ToEnt(hCaller), value, 0 ); + // 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 false; + } + + g_pScriptVM->RemoveInstance( hInfo ); + } + + return true; } #endif @@ -7607,7 +7702,7 @@ bool CBaseEntity::HasContext( const char *name, const char *value ) const if (value == NULL) return true; else - return Matcher_Match(STRING(m_ResponseContexts[i].m_iszValue), value); + return Matcher_Match( value, STRING(m_ResponseContexts[i].m_iszValue) ); } } @@ -7652,7 +7747,11 @@ bool CBaseEntity::HasContext( const char *nameandvalue ) const const char *p = nameandvalue; while ( p ) { +#ifdef NEW_RESPONSE_SYSTEM + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, nameandvalue ); +#else p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL ); +#endif return HasContext( key, value ); } @@ -7699,7 +7798,11 @@ void CBaseEntity::RemoveContext( const char *contextName ) while ( p ) { duration = 0.0f; +#ifdef NEW_RESPONSE_SYSTEM + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration, contextName ); +#else p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration ); +#endif if ( duration ) { duration += gpGlobals->curtime; @@ -8267,6 +8370,22 @@ void CBaseEntity::InputUndrawEntity( inputdata_t& inputdata ) AddEffects(EF_NODRAW); } +//----------------------------------------------------------------------------- +// Purpose: Inspired by the Portal 2 input of the same name. +//----------------------------------------------------------------------------- +void CBaseEntity::InputEnableReceivingFlashlight( inputdata_t& inputdata ) +{ + m_bDisableFlashlight = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Inspired by the Portal 2 input of the same name. +//----------------------------------------------------------------------------- +void CBaseEntity::InputDisableReceivingFlashlight( inputdata_t& inputdata ) +{ + m_bDisableFlashlight = true; +} + //----------------------------------------------------------------------------- // Purpose: Adds eflags. //----------------------------------------------------------------------------- @@ -8647,61 +8766,6 @@ 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 //----------------------------------------------------------------------------- @@ -8730,51 +8794,6 @@ 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) @@ -8854,56 +8873,76 @@ void CBaseEntity::AddContext( const char *contextName ) { char key[ 128 ]; char value[ 128 ]; - float duration; + float duration = 0.0f; const char *p = contextName; while ( p ) { duration = 0.0f; +#ifdef NEW_RESPONSE_SYSTEM + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration, contextName ); +#else p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration ); +#endif if ( duration ) { duration += gpGlobals->curtime; } - int iIndex = FindContextByName( key ); - if ( iIndex != -1 ) - { - // Set the existing context to the new value - m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value ); - m_ResponseContexts[iIndex].m_fExpirationTime = duration; - continue; - } - - ResponseContext_t newContext; - newContext.m_iszName = AllocPooledString( key ); - newContext.m_iszValue = AllocPooledString( value ); - newContext.m_fExpirationTime = duration; - - m_ResponseContexts.AddToTail( newContext ); + AddContext( key, value, duration ); } } -#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 + +#ifdef NEW_RESPONSE_SYSTEM + char buf[64]; + if ( RR::CApplyContextOperator::FindOperator( value )->Apply( + m_ResponseContexts[iIndex].m_iszValue.ToCStr(), value, buf, sizeof(buf) ) ) + { + m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( buf ); + } + else + { + Warning( "RR: could not apply operator %s to prior value %s\n", + value, m_ResponseContexts[iIndex].m_iszValue.ToCStr() ); + m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value ); + } +#else 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 + m_ResponseContexts[iIndex].m_fExpirationTime = duration; + } + else + { + ResponseContext_t newContext; + newContext.m_iszName = AllocPooledString( name ); + +#ifdef NEW_RESPONSE_SYSTEM + char buf[64]; + if ( RR::CApplyContextOperator::FindOperator( value )->Apply( + NULL, value, buf, sizeof(buf) ) ) + { + newContext.m_iszValue = AllocPooledString( buf ); + } + else + { + newContext.m_iszValue = AllocPooledString( value ); + } +#else + newContext.m_iszValue = AllocPooledString( value ); +#endif + + newContext.m_fExpirationTime = duration; + + m_ResponseContexts.AddToTail( newContext ); + } +} //----------------------------------------------------------------------------- // Purpose: @@ -9055,6 +9094,11 @@ void CBaseEntity::InputChangeVariable( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CBaseEntity::DispatchResponse( const char *conceptName ) { +#ifdef NEW_RESPONSE_SYSTEM + #undef IResponseSystem + using namespace ResponseRules; +#endif + IResponseSystem *rs = GetResponseSystem(); if ( !rs ) return; @@ -9082,6 +9126,61 @@ void CBaseEntity::DispatchResponse( const char *conceptName ) // Handle the response here... const char *szResponse = result.GetResponsePtr(); +#ifdef NEW_RESPONSE_SYSTEM + switch (result.GetType()) + { + case ResponseRules::RESPONSE_SPEAK: + { + EmitSound(szResponse); + } + break; + case ResponseRules::RESPONSE_SENTENCE: + { + int sentenceIndex = SENTENCEG_Lookup(szResponse); + if (sentenceIndex == -1) + { + // sentence not found + break; + } + + // FIXME: Get pitch from npc? + CPASAttenuationFilter filter(this); + CBaseEntity::EmitSentenceByIndex(filter, entindex(), CHAN_VOICE, sentenceIndex, 1, result.GetSoundLevel(), 0, PITCH_NORM); + } + break; + case ResponseRules::RESPONSE_SCENE: + { + // Try to fire scene w/o an actor + InstancedScriptedScene(NULL, szResponse); + } + break; + case ResponseRules::RESPONSE_PRINT: + { + + } + break; + case ResponseRules::RESPONSE_ENTITYIO: + { + CAI_Expresser::FireEntIOFromResponse(const_cast(szResponse), this); + break; + } +#ifdef MAPBASE_VSCRIPT + case ResponseRules::RESPONSE_VSCRIPT: + { + CAI_Expresser::RunScriptResponse( this, szResponse, &set, false ); + break; + } + case ResponseRules::RESPONSE_VSCRIPT_FILE: + { + CAI_Expresser::RunScriptResponse( this, szResponse, &set, true ); + break; + } +#endif + default: + // Don't know how to handle .vcds!!! + break; + } +#else #ifdef MAPBASE if (szResponse[0] == '$') { @@ -9172,6 +9271,7 @@ void CBaseEntity::DispatchResponse( const char *conceptName ) // Don't know how to handle .vcds!!! break; } +#endif } //----------------------------------------------------------------------------- @@ -9498,6 +9598,7 @@ void CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( CRecipientFilter& filter } } +#ifndef MAPBASE // Moved to SoundEmitterSystem.cpp //----------------------------------------------------------------------------- // Purpose: Wrapper to emit a sentence and also a close caption token for the sentence as appropriate. // Input : filter - @@ -9520,6 +9621,7 @@ void CBaseEntity::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, enginesound->EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex, flVolume, iSoundlevel, iFlags, iPitch, 0, pOrigin, pDirection, &dummy, bUpdatePositions, soundtime ); } +#endif void CBaseEntity::SetRefEHandle( const CBaseHandle &handle ) @@ -10017,6 +10119,7 @@ void CBaseEntity::RunOnPostSpawnScripts(void) } } +#ifndef MAPBASE_VSCRIPT // This is shared now HSCRIPT CBaseEntity::GetScriptOwnerEntity() { return ToHScript(GetOwnerEntity()); @@ -10026,6 +10129,7 @@ void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) { SetOwnerEntity(ToEnt(pOwner)); } +#endif //----------------------------------------------------------------------------- // VScript access to model's key values @@ -10110,13 +10214,10 @@ const Vector& CBaseEntity::ScriptGetBoundingMaxs(void) //----------------------------------------------------------------------------- int CBaseEntity::ScriptTakeDamage( HSCRIPT pInfo ) { - if (pInfo) + CTakeDamageInfo *info = HScriptToClass< CTakeDamageInfo >( pInfo ); + if ( info ) { - CTakeDamageInfo *info = HScriptToClass( pInfo ); //ToDamageInfo( pInfo ); - if (info) - { - return OnTakeDamage( *info ); - } + return OnTakeDamage( *info ); } return 0; @@ -10126,14 +10227,10 @@ int CBaseEntity::ScriptTakeDamage( HSCRIPT pInfo ) //----------------------------------------------------------------------------- void CBaseEntity::ScriptFireBullets( HSCRIPT pInfo ) { - if (pInfo) + FireBulletsInfo_t *info = HScriptToClass< FireBulletsInfo_t >( pInfo ); + if ( info ) { - extern FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ); - FireBulletsInfo_t *info = GetFireBulletsInfoFromInfo( pInfo ); - if (info) - { - FireBullets( *info ); - } + FireBullets( *info ); } } @@ -10192,46 +10289,6 @@ const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName ) 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 //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/baseentity.h b/mp/src/game/server/baseentity.h index 1e1c1002..7e171ad0 100644 --- a/mp/src/game/server/baseentity.h +++ b/mp/src/game/server/baseentity.h @@ -20,6 +20,10 @@ #include "ServerNetworkProperty.h" #include "shareddefs.h" #include "engine/ivmodelinfo.h" +#ifdef NEW_RESPONSE_SYSTEM +#include "AI_Criteria.h" +#include "AI_ResponseSystem.h" +#endif #include "vscript/ivscript.h" #include "vscript_server.h" @@ -29,8 +33,10 @@ class CDmgAccumulator; struct CSoundParameters; +#ifndef NEW_RESPONSE_SYSTEM class AI_CriteriaSet; class IResponseSystem; +#endif class IEntitySaveUtils; class CRecipientFilter; class CStudioHdr; @@ -39,6 +45,11 @@ class CStudioHdr; // FIXME: Could do this in the script file by making it required and bumping up weighting there instead... #define CONCEPT_WEIGHT 5.0f +#ifdef NEW_RESPONSE_SYSTEM +// Relax the namespace standard a bit so that less code has to be changed +#define IResponseSystem ResponseRules::IResponseSystem +#endif + typedef CHandle EHANDLE; #define MANUALMODE_GETSET_PROP(type, accessorName, varName) \ @@ -338,6 +349,16 @@ struct thinkfunc_t DECLARE_SIMPLE_DATADESC(); }; +#ifdef MAPBASE_VSCRIPT +struct scriptthinkfunc_t +{ + float m_flNextThink; + HSCRIPT m_hfnThink; + unsigned m_iContextHash; + bool m_bNoParam; +}; +#endif + struct EmitSound_t; struct rotatingpushmove_t; @@ -589,7 +610,7 @@ public: #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 ); + //void CancelEventsByInput( const char *szInput ); #endif @@ -672,6 +693,10 @@ public: #endif bool ScriptInputHook( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, ScriptVariant_t &functionReturn ); + void ScriptInputHookClearParams(); +#ifdef MAPBASE_VSCRIPT + bool ScriptDeathHook( CTakeDamageInfo *info ); +#endif // // Input handlers. @@ -752,6 +777,8 @@ public: void InputRemoveEffects( inputdata_t &inputdata ); void InputDrawEntity( inputdata_t &inputdata ); void InputUndrawEntity( inputdata_t &inputdata ); + void InputEnableReceivingFlashlight( inputdata_t &inputdata ); + void InputDisableReceivingFlashlight( inputdata_t &inputdata ); void InputAddEFlags( inputdata_t &inputdata ); void InputRemoveEFlags( inputdata_t &inputdata ); void InputAddSolidFlags( inputdata_t &inputdata ); @@ -918,6 +945,9 @@ public: // // This was partly inspired by Underhell's keyvalue that allows entities to only render in mirrors and cameras. CNetworkVar( int, m_iViewHideFlags ); + + // Disables receiving projected textures. Based on a keyvalue from later Source games. + CNetworkVar( bool, m_bDisableFlashlight ); #endif // was pev->rendercolor @@ -986,10 +1016,10 @@ public: 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 ); + void AddContext( const char *name, const char *value, float duration = 0.0f ); protected: CUtlVector< ResponseContext_t > m_ResponseContexts; @@ -1064,7 +1094,7 @@ public: void SendOnKilledGameEvent( const CTakeDamageInfo &info ); // Notifier that I've killed some other entity. (called from Victim's Event_Killed). - virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) { return; } + virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); // UNDONE: Make this data? virtual int BloodColor( void ); @@ -1251,21 +1281,21 @@ public: #endif void FunctionCheck( void *pFunction, const char *name ); - ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) + ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, const char *name ) { COMPILE_TIME_ASSERT( sizeof(func) == ENTITYFUNCPTR_SIZE ); m_pfnTouch = func; FunctionCheck( *(reinterpret_cast(&m_pfnTouch)), name ); return func; } - USEPTR UseSet( USEPTR func, char *name ) + USEPTR UseSet( USEPTR func, const char *name ) { COMPILE_TIME_ASSERT( sizeof(func) == ENTITYFUNCPTR_SIZE ); m_pfnUse = func; FunctionCheck( *(reinterpret_cast(&m_pfnUse)), name ); return func; } - ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name ) + ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, const char *name ) { COMPILE_TIME_ASSERT( sizeof(func) == ENTITYFUNCPTR_SIZE ); m_pfnBlocked = func; @@ -1276,6 +1306,12 @@ public: #endif // _DEBUG virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set ); +#ifdef NEW_RESPONSE_SYSTEM + // this computes criteria that depend on the other criteria having been set. + // needs to be done in a second pass because we may have multiple overrids for + // a context before it all settles out. + virtual void ModifyOrAppendDerivedCriteria( AI_CriteriaSet& set ) {}; +#endif void AppendContextToCriteria( AI_CriteriaSet& set, const char *prefix = "" ); #ifdef MAPBASE void ReAppendContextCriteria( AI_CriteriaSet& set ); @@ -1510,6 +1546,12 @@ public: void GenderExpandString( char const *in, char *out, int maxlen ); virtual void ModifyEmitSoundParams( EmitSound_t ¶ms ); +#ifdef MAPBASE + // Same as above, but for sentences + // (which don't actually have EmitSound_t params) + virtual void ModifySentenceParams( int &iSentenceIndex, int &iChannel, float &flVolume, soundlevel_t &iSoundlevel, int &iFlags, int &iPitch, + const Vector **pOrigin, const Vector **pDirection, bool &bUpdatePositions, float &soundtime, int &iSpecialDSP, int &iSpeakerIndex ); +#endif static float GetSoundDuration( const char *soundname, char const *actormodel ); @@ -1539,7 +1581,11 @@ public: static void EmitCloseCaption( IRecipientFilter& filter, int entindex, char const *token, CUtlVector< Vector >& soundorigins, float duration, bool warnifmissing = false ); static void EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex, float flVolume, soundlevel_t iSoundlevel, int iFlags = 0, int iPitch = PITCH_NORM, - const Vector *pOrigin = NULL, const Vector *pDirection = NULL, bool bUpdatePositions = true, float soundtime = 0.0f ); + const Vector *pOrigin = NULL, const Vector *pDirection = NULL, bool bUpdatePositions = true, float soundtime = 0.0f +#ifdef MAPBASE + , int iSpecialDSP = 0, int iSpeakerIndex = 0 // Needed for env_microphone +#endif + ); static bool IsPrecacheAllowed(); static void SetAllowPrecache( bool allow ); @@ -1998,11 +2044,12 @@ public: #ifdef MAPBASE_VSCRIPT void ScriptSetThinkFunction(const char *szFunc, float time); void ScriptStopThinkFunction(); - void ScriptSetThink(HSCRIPT hFunc, float time); + void ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float time ); + void ScriptSetThink( HSCRIPT hFunc, float time ); void ScriptStopThink(); - void ScriptThinkH(); + void ScriptContextThink(); private: - HSCRIPT m_hfnThink; + CUtlVector< scriptthinkfunc_t* > m_ScriptThinkFuncs; public: #endif const char* GetScriptId(); @@ -2033,8 +2080,10 @@ public: 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 +#ifndef MAPBASE_VSCRIPT void ScriptSetSize(const Vector& mins, const Vector& maxs) { UTIL_SetSize(this, mins, maxs); } void ScriptUtilRemove(void) { UTIL_Remove(this); } +#endif 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); } @@ -2060,6 +2109,7 @@ public: const char* ScriptGetModelName(void) const; HSCRIPT ScriptGetModelKeyValues(void); + void ScriptStopSound(const char* soundname); void ScriptEmitSound(const char* soundname); float ScriptSoundDuration(const char* soundname, const char* actormodel); @@ -2106,16 +2156,24 @@ public: int ScriptGetMoveType() { return GetMoveType(); } void ScriptSetMoveType( int iMoveType ) { SetMoveType( (MoveType_t)iMoveType ); } + int ScriptGetSolid() { return GetSolid(); } + void ScriptSetSolid( int i ) { SetSolid( (SolidType_t)i ); } + 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_OnEntText; + static ScriptHook_t g_Hook_VPhysicsCollision; static ScriptHook_t g_Hook_FireBullets; static ScriptHook_t g_Hook_OnDeath; + static ScriptHook_t g_Hook_OnKilledOther; static ScriptHook_t g_Hook_HandleInteraction; + static ScriptHook_t g_Hook_ModifyEmitSoundParams; + static ScriptHook_t g_Hook_ModifySentenceParams; #endif string_t m_iszVScripts; diff --git a/mp/src/game/server/baseflex.cpp b/mp/src/game/server/baseflex.cpp index 156d6d7e..bb73a7f3 100644 --- a/mp/src/game/server/baseflex.cpp +++ b/mp/src/game/server/baseflex.cpp @@ -95,9 +95,17 @@ BEGIN_DATADESC( CBaseFlex ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimatingOverlay, "Animated characters who have vertex flex capability." ) +#else BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimating, "Animated characters who have vertex flex capability." ) +#endif 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." ) + +#ifdef MAPBASE + DEFINE_SCRIPTFUNC( SetViewtarget, "Sets the entity's eye target." ) +#endif END_SCRIPTDESC(); @@ -799,6 +807,15 @@ bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CCh textParams.fadeinTime = 0.5f; textParams.fadeoutTime = 0.5f; + textParams.channel = 3; + textParams.x = -1; + textParams.y = 0.6; + textParams.effect = 0; + + textParams.r1 = 255; + textParams.g1 = 255; + textParams.b1 = 255; + if ( GetGameTextSpeechParams( textParams ) ) { CRecipientFilter filter; @@ -2091,15 +2108,6 @@ float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname ) //----------------------------------------------------------------------------- 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) { @@ -2328,8 +2336,13 @@ void CBaseFlex::DoBodyLean( void ) { m_vecPrevVelocity = vecDelta; float decay = ExponentialDecay( 0.5, 0.1, dt ); +#ifdef MAPBASE // From Alien Swarm SDK + m_vecShift = m_vecShift * decay; + m_vecLean = m_vecLean * decay; +#else m_vecShift = m_vecLean * decay; m_vecLean = m_vecShift * decay; +#endif } m_vecPrevOrigin = vecOrigin; diff --git a/mp/src/game/server/baseflex.h b/mp/src/game/server/baseflex.h index 215b13f5..ec809534 100644 --- a/mp/src/game/server/baseflex.h +++ b/mp/src/game/server/baseflex.h @@ -19,7 +19,9 @@ struct flexsettinghdr_t; struct flexsetting_t; +#ifndef NEW_RESPONSE_SYSTEM class AI_Response; +#endif //----------------------------------------------------------------------------- // Purpose: A .vfe referenced by a scene during .vcd playback diff --git a/mp/src/game/server/basemultiplayerplayer.cpp b/mp/src/game/server/basemultiplayerplayer.cpp index fee7515d..9c3375ae 100644 --- a/mp/src/game/server/basemultiplayerplayer.cpp +++ b/mp/src/game/server/basemultiplayerplayer.cpp @@ -28,6 +28,7 @@ CBaseMultiplayerPlayer::CBaseMultiplayerPlayer() CBaseMultiplayerPlayer::~CBaseMultiplayerPlayer() { m_pAchievementKV->deleteThis(); + delete m_pExpresser; } //----------------------------------------------------------------------------- @@ -91,7 +92,13 @@ bool CBaseMultiplayerPlayer::SpeakConcept( AI_Response &response, int iConcept ) { // Save the current concept. m_iCurrentConcept = iConcept; +#ifdef NEW_RESPONSE_SYSTEM + CAI_Concept concept(g_pszMPConcepts[iConcept]); + concept.SetSpeaker(this); + return FindResponse( response, concept ); +#else return SpeakFindResponse( response, g_pszMPConcepts[iConcept] ); +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/bmodels.cpp b/mp/src/game/server/bmodels.cpp index 8c5ca480..e8291f8f 100644 --- a/mp/src/game/server/bmodels.cpp +++ b/mp/src/game/server/bmodels.cpp @@ -450,6 +450,11 @@ protected: bool m_bSolidBsp; // Brush is SOLID_BSP +#ifdef MAPBASE + int m_iMinPitch = 30; // FANPITCHMIN + int m_iMaxPitch = 100; // FANPITCHMAX +#endif + public: Vector m_vecClientOrigin; QAngle m_vecClientAngles; @@ -472,6 +477,10 @@ BEGIN_DATADESC( CFuncRotating ) DEFINE_FIELD( m_angStart, FIELD_VECTOR ), DEFINE_FIELD( m_bStopAtStartPos, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iMinPitch, FIELD_INTEGER, "minpitch" ), + DEFINE_KEYFIELD( m_iMaxPitch, FIELD_INTEGER, "maxpitch" ), +#endif // Function Pointers DEFINE_FUNCTION( SpinUpMove ), @@ -823,8 +832,14 @@ void CFuncRotating::HurtTouch ( CBaseEntity *pOther ) } +#ifdef MAPBASE +// In Mapbase, use the keyvalues instead +#define FANPITCHMIN m_iMinPitch +#define FANPITCHMAX m_iMaxPitch +#else #define FANPITCHMIN 30 #define FANPITCHMAX 100 +#endif //----------------------------------------------------------------------------- @@ -960,18 +975,6 @@ 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 ); } @@ -1102,6 +1105,18 @@ void CFuncRotating::RotateMove( void ) { SetMoveDoneTime( 10 ); +#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 + if ( m_bStopAtStartPos ) { SetMoveDoneTime( GetNextMoveInterval() ); diff --git a/mp/src/game/server/cbase.cpp b/mp/src/game/server/cbase.cpp index ec125a8f..f9975d52 100644 --- a/mp/src/game/server/cbase.cpp +++ b/mp/src/game/server/cbase.cpp @@ -850,7 +850,7 @@ void CEventQueue::Dump( void ) // Purpose: adds the action into the correct spot in the priority queue, targeting entity via string name //----------------------------------------------------------------------------- #ifdef MAPBASE_VSCRIPT -intptr_t +int #else void #endif @@ -874,6 +874,7 @@ CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Va AddEvent( newEvent ); #ifdef MAPBASE_VSCRIPT + Assert( sizeof(EventQueuePrioritizedEvent_t*) == sizeof(int) ); return reinterpret_cast(newEvent); // POINTER_TO_INT #endif } @@ -882,7 +883,7 @@ CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Va // Purpose: adds the action into the correct spot in the priority queue, targeting entity via pointer //----------------------------------------------------------------------------- #ifdef MAPBASE_VSCRIPT -intptr_t +int #else void #endif @@ -906,7 +907,8 @@ CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t V AddEvent( newEvent ); #ifdef MAPBASE_VSCRIPT - return reinterpret_cast(newEvent); + Assert( sizeof(EventQueuePrioritizedEvent_t*) == sizeof(int) ); + return reinterpret_cast(newEvent); // POINTER_TO_INT #endif } @@ -985,83 +987,31 @@ 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] == '!' ) + // This is a hack to access the entity from a FIELD_EHANDLE input + if ( FStrEq( STRING( pe->m_iTarget ), "!output" ) ) { - CBaseEntity *target = gEntList.FindEntityProcedural( szName, pSearchingEntity, pe->m_pActivator, pe->m_pCaller ); + pe->m_VariantValue.Convert( FIELD_EHANDLE ); + CBaseEntity *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 +#endif + { + CBaseEntity *target = NULL; + while ( 1 ) { - // 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(); - } - } + target = gEntList.FindEntityByName( target, pe->m_iTarget, pSearchingEntity, pe->m_pActivator, pe->m_pCaller ); + if ( !target ) + break; - 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 ) - { - target = gEntList.FindEntityByName( target, pe->m_iTarget, pSearchingEntity, pe->m_pActivator, pe->m_pCaller ); - if ( !target ) - break; - - // 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; - } -#endif } // direct pointer @@ -1257,7 +1207,10 @@ void ServiceEventQueue( void ) #ifdef MAPBASE_VSCRIPT //----------------------------------------------------------------------------- -// Remove events on entity by input. +// Remove pending events on entity by input. +// +// Also removes events that were targeted with their debug name (classname when unnamed). +// E.g. CancelEventsByInput( pRelay, "Trigger" ) removes all pending logic_relay "Trigger" events. //----------------------------------------------------------------------------- void CEventQueue::CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ) { @@ -1290,11 +1243,8 @@ void CEventQueue::CancelEventsByInput( CBaseEntity *pTarget, const char *szInput } } -bool CEventQueue::RemoveEvent( intptr_t event ) +bool CEventQueue::RemoveEvent( int 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 ) @@ -1310,11 +1260,8 @@ bool CEventQueue::RemoveEvent( intptr_t event ) return false; } -float CEventQueue::GetTimeLeft( intptr_t event ) +float CEventQueue::GetTimeLeft( int 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 ) diff --git a/mp/src/game/server/cbase.h b/mp/src/game/server/cbase.h index 290e3b25..2b00af39 100644 --- a/mp/src/game/server/cbase.h +++ b/mp/src/game/server/cbase.h @@ -104,6 +104,13 @@ extern void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseE #define MAX_OLD_ENEMIES 4 // how many old enemies to remember +#ifdef MAPBASE +// Use the model keyvalue if it is defined +#define DefaultOrCustomModel(defaultModel) GetModelName() != NULL_STRING ? STRING(GetModelName()) : defaultModel +#else +#define DefaultOrCustomModel() defaultModel +#endif + // used by suit voice to indicate damage sustained and repaired type to player enum diff --git a/mp/src/game/server/colorcorrection.cpp b/mp/src/game/server/colorcorrection.cpp index 16d01222..69e6db85 100644 --- a/mp/src/game/server/colorcorrection.cpp +++ b/mp/src/game/server/colorcorrection.cpp @@ -5,9 +5,8 @@ // $NoKeywords: $ //===========================================================================// -#include - #include "cbase.h" +#include "colorcorrection.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -16,64 +15,6 @@ static const char *s_pFadeInContextThink = "ColorCorrectionFadeInThink"; static const char *s_pFadeOutContextThink = "ColorCorrectionFadeOutThink"; - -//------------------------------------------------------------------------------ -// FIXME: This really should inherit from something more lightweight -//------------------------------------------------------------------------------ - - -//------------------------------------------------------------------------------ -// Purpose : Shadow control entity -//------------------------------------------------------------------------------ -class CColorCorrection : public CBaseEntity -{ - DECLARE_CLASS( CColorCorrection, CBaseEntity ); -public: - DECLARE_SERVERCLASS(); - DECLARE_DATADESC(); - - CColorCorrection(); - - void Spawn( void ); - int UpdateTransmitState(); - void Activate( void ); - - virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - // Inputs - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - void InputSetFadeInDuration ( inputdata_t &inputdata ); - void InputSetFadeOutDuration ( inputdata_t &inputdata ); - -private: - void FadeIn ( void ); - void FadeOut ( void ); - - void FadeInThink( void ); // Fades lookup weight from Cur->MaxWeight - void FadeOutThink( void ); // Fades lookup weight from CurWeight->0.0 - - - - float m_flFadeInDuration; // Duration for a full 0->MaxWeight transition - float m_flFadeOutDuration; // Duration for a full Max->0 transition - float m_flStartFadeInWeight; - float m_flStartFadeOutWeight; - float m_flTimeStartFadeIn; - float m_flTimeStartFadeOut; - - float m_flMaxWeight; - - bool m_bStartDisabled; - CNetworkVar( bool, m_bEnabled ); - - CNetworkVar( float, m_MinFalloff ); - CNetworkVar( float, m_MaxFalloff ); - CNetworkVar( float, m_flCurWeight ); - CNetworkString( m_netlookupFilename, MAX_PATH ); - - string_t m_lookupFilename; -}; LINK_ENTITY_TO_CLASS(color_correction, CColorCorrection); @@ -97,12 +38,19 @@ BEGIN_DATADESC( CColorCorrection ) DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ), DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ), +#ifdef MAPBASE // From Alien Swarm SDK + DEFINE_KEYFIELD( m_bExclusive, FIELD_BOOLEAN, "exclusive" ), +#endif // DEFINE_ARRAY( m_netlookupFilename, FIELD_CHARACTER, MAX_PATH ), DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFadeInDuration", InputSetFadeInDuration ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFadeOutDuration", InputSetFadeOutDuration ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMinFalloff", InputSetMinFalloff ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxFalloff", InputSetMaxFalloff ), +#endif END_DATADESC() @@ -112,8 +60,18 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE(CColorCorrection, DT_ColorCorrection) SendPropFloat( SENDINFO(m_MinFalloff) ), SendPropFloat( SENDINFO(m_MaxFalloff) ), SendPropFloat( SENDINFO(m_flCurWeight) ), +#ifdef MAPBASE // From Alien Swarm SDK + SendPropFloat( SENDINFO(m_flMaxWeight) ), + SendPropFloat( SENDINFO(m_flFadeInDuration) ), + SendPropFloat( SENDINFO(m_flFadeOutDuration) ), +#endif SendPropString( SENDINFO(m_netlookupFilename) ), SendPropBool( SENDINFO(m_bEnabled) ), +#ifdef MAPBASE // From Alien Swarm SDK + SendPropBool( SENDINFO(m_bMaster) ), + SendPropBool( SENDINFO(m_bClientSide) ), + SendPropBool( SENDINFO(m_bExclusive) ), +#endif END_SEND_TABLE() @@ -132,6 +90,11 @@ CColorCorrection::CColorCorrection() : BaseClass() m_flTimeStartFadeOut = 0.0f; m_netlookupFilename.GetForModify()[0] = 0; m_lookupFilename = NULL_STRING; +#ifdef MAPBASE // From Alien Swarm SDK + m_bMaster = false; + m_bClientSide = false; + m_bExclusive = false; +#endif } @@ -175,6 +138,11 @@ void CColorCorrection::Activate( void ) { BaseClass::Activate(); +#ifdef MAPBASE // From Alien Swarm SDK (moved to Activate() for save/restore support) + m_bMaster = IsMaster(); + m_bClientSide = IsClientSide(); +#endif + Q_strncpy( m_netlookupFilename.GetForModify(), STRING( m_lookupFilename ), MAX_PATH ); } @@ -183,6 +151,11 @@ void CColorCorrection::Activate( void ) //----------------------------------------------------------------------------- void CColorCorrection::FadeIn ( void ) { +#ifdef MAPBASE // From Alien Swarm SDK + if ( m_bClientSide || ( m_bEnabled && m_flCurWeight >= m_flMaxWeight ) ) + return; +#endif + m_bEnabled = true; m_flTimeStartFadeIn = gpGlobals->curtime; m_flStartFadeInWeight = m_flCurWeight; @@ -194,6 +167,11 @@ void CColorCorrection::FadeIn ( void ) //----------------------------------------------------------------------------- void CColorCorrection::FadeOut ( void ) { +#ifdef MAPBASE // From Alien Swarm SDK + if ( m_bClientSide || ( !m_bEnabled && m_flCurWeight <= 0.0f ) ) + return; +#endif + m_bEnabled = false; m_flTimeStartFadeOut = gpGlobals->curtime; m_flStartFadeOutWeight = m_flCurWeight; @@ -230,7 +208,11 @@ void CColorCorrection::FadeInThink( void ) flFadeRatio = clamp ( flFadeRatio, 0.0f, 1.0f ); m_flStartFadeInWeight = clamp ( m_flStartFadeInWeight, 0.0f, 1.0f ); +#ifdef MAPBASE + m_flCurWeight = Lerp( flFadeRatio, m_flStartFadeInWeight, m_flMaxWeight.Get() ); +#else m_flCurWeight = Lerp( flFadeRatio, m_flStartFadeInWeight, m_flMaxWeight ); +#endif SetNextThink( gpGlobals->curtime + COLOR_CORRECTION_ENT_THINK_RATE, s_pFadeInContextThink ); } @@ -312,3 +294,94 @@ void CColorCorrection::InputSetFadeOutDuration( inputdata_t& inputdata ) { m_flFadeOutDuration = inputdata.value.Float(); } + +#ifdef MAPBASE +void CColorCorrection::InputSetMinFalloff( inputdata_t& inputdata ) +{ + m_MinFalloff = inputdata.value.Float(); +} + +void CColorCorrection::InputSetMaxFalloff( inputdata_t& inputdata ) +{ + m_MaxFalloff = inputdata.value.Float(); +} +#endif + +#ifdef MAPBASE // From Alien Swarm SDK +CColorCorrectionSystem s_ColorCorrectionSystem( "ColorCorrectionSystem" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CColorCorrectionSystem *ColorCorrectionSystem( void ) +{ + return &s_ColorCorrectionSystem; +} + + +//----------------------------------------------------------------------------- +// Purpose: Clear out the fog controller. +//----------------------------------------------------------------------------- +void CColorCorrectionSystem::LevelInitPreEntity( void ) +{ + m_hMasterController = NULL; + ListenForGameEvent( "round_start" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Find the master controller. If no controller is +// set as Master, use the first controller found. +//----------------------------------------------------------------------------- +void CColorCorrectionSystem::InitMasterController( void ) +{ + CColorCorrection *pColorCorrection = NULL; + do + { + pColorCorrection = static_cast( gEntList.FindEntityByClassname( pColorCorrection, "color_correction" ) ); + if ( pColorCorrection ) + { + if ( m_hMasterController.Get() == NULL ) + { + m_hMasterController = pColorCorrection; + } + else + { + if ( pColorCorrection->IsMaster() ) + { + m_hMasterController = pColorCorrection; + } + } + } + } while ( pColorCorrection ); +} + +//----------------------------------------------------------------------------- +// Purpose: On a multiplayer map restart, re-find the master controller. +//----------------------------------------------------------------------------- +void CColorCorrectionSystem::FireGameEvent( IGameEvent *pEvent ) +{ + InitMasterController(); +} + +//----------------------------------------------------------------------------- +// Purpose: On level load find the master fog controller. If no controller is +// set as Master, use the first fog controller found. +//----------------------------------------------------------------------------- +void CColorCorrectionSystem::LevelInitPostEntity( void ) +{ + 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 fog 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_hColorCorrectionCtrl.Get() == NULL ) ) + { + pPlayer->InitColorCorrectionController(); + } + } +} +#endif diff --git a/mp/src/game/server/colorcorrection.h b/mp/src/game/server/colorcorrection.h new file mode 100644 index 00000000..2e96eb7c --- /dev/null +++ b/mp/src/game/server/colorcorrection.h @@ -0,0 +1,147 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013. +// Although technically a new Mapbase file, it only serves to move otherwise identical code, +// so most code and repo conventions will pretend it was always there. +// +// -------------------------------------------------------------------- +// +// Purpose: Color correction entity. +// +//=============================================================================// + +#ifndef COLOR_CORRECTION_H +#define COLOR_CORRECTION_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "cbase.h" +#ifdef MAPBASE // From Alien Swarm SDK +#include "GameEventListener.h" + +// Spawn Flags +#define SF_COLORCORRECTION_MASTER 0x0001 +#define SF_COLORCORRECTION_CLIENTSIDE 0x0002 +#endif + +//------------------------------------------------------------------------------ +// FIXME: This really should inherit from something more lightweight +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Purpose : Shadow control entity +//------------------------------------------------------------------------------ +class CColorCorrection : public CBaseEntity +{ + DECLARE_CLASS( CColorCorrection, CBaseEntity ); +public: + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + CColorCorrection(); + + void Spawn( void ); + int UpdateTransmitState(); + void Activate( void ); + + virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + +#ifdef MAPBASE // From Alien Swarm SDK + bool IsMaster( void ) const { return HasSpawnFlags( SF_COLORCORRECTION_MASTER ); } + + bool IsClientSide( void ) const { return HasSpawnFlags( SF_COLORCORRECTION_CLIENTSIDE ); } + + bool IsExclusive( void ) const { return m_bExclusive; } +#endif + + // Inputs + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputSetFadeInDuration ( inputdata_t &inputdata ); + void InputSetFadeOutDuration ( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetMinFalloff( inputdata_t &inputdata ); + void InputSetMaxFalloff( inputdata_t &inputdata ); +#endif + +private: + void FadeIn ( void ); + void FadeOut ( void ); + + void FadeInThink( void ); // Fades lookup weight from Cur->MaxWeight + void FadeOutThink( void ); // Fades lookup weight from CurWeight->0.0 + + + +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkVar( float, m_flFadeInDuration ); // Duration for a full 0->MaxWeight transition + CNetworkVar( float, m_flFadeOutDuration ); // Duration for a full Max->0 transition +#else + float m_flFadeInDuration; // Duration for a full 0->MaxWeight transition + float m_flFadeOutDuration; // Duration for a full Max->0 transition +#endif + float m_flStartFadeInWeight; + float m_flStartFadeOutWeight; + float m_flTimeStartFadeIn; + float m_flTimeStartFadeOut; + +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkVar( float, m_flMaxWeight ); +#else + float m_flMaxWeight; +#endif + + bool m_bStartDisabled; + CNetworkVar( bool, m_bEnabled ); +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkVar( bool, m_bMaster ); + CNetworkVar( bool, m_bClientSide ); + CNetworkVar( bool, m_bExclusive ); +#endif + + CNetworkVar( float, m_MinFalloff ); + CNetworkVar( float, m_MaxFalloff ); + CNetworkVar( float, m_flCurWeight ); + CNetworkString( m_netlookupFilename, MAX_PATH ); + + string_t m_lookupFilename; +}; + +#ifdef MAPBASE // From Alien Swarm SDK +//============================================================================= +// +// ColorCorrection Controller System. Just a place to store a master controller +// +class CColorCorrectionSystem : public CAutoGameSystem, public CGameEventListener +{ +public: + + // Creation/Init. + CColorCorrectionSystem( char const *name ) : CAutoGameSystem( name ) + { + m_hMasterController = NULL; + } + + ~CColorCorrectionSystem() + { + m_hMasterController = NULL; + } + + virtual void LevelInitPreEntity(); + virtual void LevelInitPostEntity(); + virtual void FireGameEvent( IGameEvent *pEvent ); + CColorCorrection *GetMasterColorCorrection( void ) { return m_hMasterController; } + +private: + + void InitMasterController( void ); + CHandle< CColorCorrection > m_hMasterController; +}; + +CColorCorrectionSystem *ColorCorrectionSystem( void ); +#endif + +#endif // COLOR_CORRECTION_H diff --git a/mp/src/game/server/colorcorrectionvolume.cpp b/mp/src/game/server/colorcorrectionvolume.cpp index a56cd533..abc55d75 100644 --- a/mp/src/game/server/colorcorrectionvolume.cpp +++ b/mp/src/game/server/colorcorrectionvolume.cpp @@ -48,7 +48,11 @@ public: private: +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkVar( bool, m_bEnabled ); +#else bool m_bEnabled; +#endif bool m_bStartDisabled; CNetworkVar( float, m_Weight ); @@ -61,7 +65,11 @@ private: float m_LastExitWeight; float m_LastExitTime; +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkVar( float, m_FadeDuration ); +#else float m_FadeDuration; +#endif }; LINK_ENTITY_TO_CLASS(color_correction_volume, CColorCorrectionVolume); @@ -90,6 +98,11 @@ END_DATADESC() IMPLEMENT_SERVERCLASS_ST_NOBASE(CColorCorrectionVolume, DT_ColorCorrectionVolume) +#ifdef MAPBASE // From Alien Swarm SDK + SendPropBool( SENDINFO(m_bEnabled) ), + SendPropFloat( SENDINFO(m_MaxWeight) ), + SendPropFloat( SENDINFO(m_FadeDuration) ), +#endif SendPropFloat( SENDINFO(m_Weight) ), SendPropString( SENDINFO(m_lookupFilename) ), END_SEND_TABLE() diff --git a/mp/src/game/server/effects.cpp b/mp/src/game/server/effects.cpp index 7b07e157..cbe05190 100644 --- a/mp/src/game/server/effects.cpp +++ b/mp/src/game/server/effects.cpp @@ -2511,7 +2511,7 @@ class CBreakableGibShooter : public CBaseEntity DECLARE_DATADESC(); public: - const char *GetRandomTemplateModel( CPointTemplate *pTemplate ); + int GetRandomTemplateModelIndex( CPointTemplate *pTemplate ); void Precache( void ); @@ -2560,19 +2560,16 @@ END_DATADESC() LINK_ENTITY_TO_CLASS( env_break_shooter, CBreakableGibShooter ); -const char *CBreakableGibShooter::GetRandomTemplateModel( CPointTemplate *pTemplate ) +int CBreakableGibShooter::GetRandomTemplateModelIndex( CPointTemplate *pTemplate ) { int iIndex = RandomInt( 0, pTemplate->GetNumTemplates() ); - char *iszTemplate = (char*)(STRING(Templates_FindByIndex(pTemplate->GetTemplateIndexForTemplate(iIndex)))); - - CEntityMapData entData( iszTemplate ); + const char *szTemplate = STRING(Templates_FindByIndex(pTemplate->GetTemplateIndexForTemplate(iIndex))); // 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; + bool modelExtracted = MapEntity_ExtractValue(szTemplate, "model", szModel); - return strdup(szModel); + return modelinfo->GetModelIndex( modelExtracted ? szModel : NULL ); } void CBreakableGibShooter::Precache( void ) @@ -2604,7 +2601,7 @@ void CBreakableGibShooter::Shoot( void ) if (m_iModelType == MODELTYPE_BREAKABLECHUNKS) iModelIndex = modelinfo->GetModelIndex( g_PropDataSystem.GetRandomChunkModel( STRING( GetModelName() ) ) ); else if (m_iModelType == MODELTYPE_TEMPLATE) - iModelIndex = modelinfo->GetModelIndex( GetRandomTemplateModel(pTemplate) ); + iModelIndex = GetRandomTemplateModelIndex( pTemplate ); // All objects except the first one in this run are marked as slaves... int slaveFlag = 0; diff --git a/mp/src/game/server/entity_tools_server.cpp b/mp/src/game/server/entity_tools_server.cpp index b08d0754..d3c59959 100644 --- a/mp/src/game/server/entity_tools_server.cpp +++ b/mp/src/game/server/entity_tools_server.cpp @@ -14,6 +14,13 @@ #include "sceneentity.h" #include "particles/particles.h" +#if _MSC_VER >= 1900 +#include "icommandline.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + //----------------------------------------------------------------------------- // Interface from engine to tools for manipulating entities diff --git a/mp/src/game/server/entitylist.cpp b/mp/src/game/server/entitylist.cpp index 00b83b66..caf3a34a 100644 --- a/mp/src/game/server/entitylist.cpp +++ b/mp/src/game/server/entitylist.cpp @@ -22,6 +22,7 @@ #include "npc_playercompanion.h" #ifdef MAPBASE #include "hl2_player.h" +#include "mapbase_matchers_base.h" #endif #endif // HL2_DLL @@ -78,6 +79,15 @@ public: // IEntityListener virtual void OnEntityCreated( CBaseEntity *pEntity ) {} + virtual void OnEntitySpawned( CBaseEntity *pEntity ) + { + // From Alien Swarm SDK + if ( ShouldAddEntity( pEntity ) ) + { + RemoveEntity( pEntity ); + AddEntity( pEntity ); + } + } virtual void OnEntityDeleted( CBaseEntity *pEntity ) { if ( !(pEntity->GetFlags() & FL_AIMTARGET) ) @@ -105,6 +115,10 @@ public: memcpy( pList, m_targetList.Base(), sizeof(CBaseEntity *) * count ); return count; } + CBaseEntity *ListElement( int iIndex ) // From Alien Swarm SDK + { + return m_targetList[iIndex]; + } private: CUtlVector m_targetList; @@ -120,6 +134,10 @@ int AimTarget_ListCopy( CBaseEntity *pList[], int listMax ) { return g_AimManager.ListCopy( pList, listMax ); } +CBaseEntity *AimTarget_ListElement( int iIndex ) +{ + return g_AimManager.ListElement( iIndex ); +} void AimTarget_ForceRepopulateList() { g_AimManager.ForceRepopulateList(); @@ -293,6 +311,10 @@ CBaseEntityClassList::~CBaseEntityClassList() { } +#ifdef MAPBASE_VSCRIPT +static CUtlVector g_CustomProcedurals; +#endif + CGlobalEntityList::CGlobalEntityList() { m_iHighestEnt = m_iNumEnts = m_iNumEdicts = 0; @@ -380,6 +402,10 @@ void CGlobalEntityList::Clear( void ) // free the memory g_DeleteList.Purge(); +#ifdef MAPBASE_VSCRIPT + g_CustomProcedurals.Purge(); +#endif + CBaseEntity::m_nDebugPlayer = -1; CBaseEntity::m_bInDebugSelect = false; m_iHighestEnt = 0; @@ -516,6 +542,43 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassname( CBaseEntity *pStartEntity return NULL; } +// From Alien Swarm SDK +CBaseEntity *CGlobalEntityList::FindEntityByClassnameFast( CBaseEntity *pStartEntity, string_t iszClassname ) +{ + /* + if ( pStartEntity ) + { + return pStartEntity->m_pNextByClass; + } + + EntsByStringList_t key = { iszClassname }; + UtlHashHandle_t hEntry = g_EntsByClassname.Find( key ); + if ( hEntry != g_EntsByClassname.InvalidHandle() ) + { + return g_EntsByClassname[hEntry].pHead; + } + */ + + const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : FirstEntInfo(); + + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + CBaseEntity *pEntity = (CBaseEntity *)pInfo->m_pEntity; + if ( !pEntity ) + { + DevWarning( "NULL entity in global entity list!\n" ); + continue; + } + + if ( pEntity->m_iClassname == iszClassname) + { + return pEntity; + } + } + + return NULL; +} + //----------------------------------------------------------------------------- // Purpose: Finds an entity given a procedural name. @@ -633,6 +696,93 @@ CBaseEntity *CGlobalEntityList::FindEntityProcedural( const char *szName, CBaseE } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: Finds an entity given a custom procedural name. +// Input : szName - The procedural name to search for, should start with '!'. +// pSearchingEntity - +// pActivator - The activator entity if this was called from an input +// or Use handler. +//----------------------------------------------------------------------------- +CBaseEntity *CGlobalEntityList::FindEntityCustomProcedural( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + const char *pName = szName; + + // + // Check for the name escape character. + // + if ( szName[0] == '!' ) + pName = szName + 1; + + // + // Look through the custom procedural list. + // + for (int i = 0; i < g_CustomProcedurals.Count(); i++) + { + if (Matcher_NamesMatch( g_CustomProcedurals[i].szName, pName )) + { + if (pStartEntity && g_CustomProcedurals[i].bCanReturnMultiple == false) + return NULL; + + // name, startEntity, searchingEntity, activator, caller + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { + ScriptVariant_t( pName ), + ScriptVariant_t( ToHScript( pStartEntity ) ), + ScriptVariant_t( ToHScript( pSearchingEntity ) ), + ScriptVariant_t( ToHScript( pActivator ) ), + ScriptVariant_t( ToHScript( pCaller ) ), + }; + + g_pScriptVM->ExecuteFunction( g_CustomProcedurals[i].hFunc, args, 5, &functionReturn, NULL, true ); + + return ToEnt( functionReturn.m_hScript ); + } + } + + return NULL; +} + +void CGlobalEntityList::AddCustomProcedural( const char *pszName, HSCRIPT hFunc, bool bCanReturnMultiple ) +{ + // + // Check for any existing custom procedurals to replace. + // + for (int i = 0; i < g_CustomProcedurals.Count(); i++) + { + if (FStrEq( pszName, g_CustomProcedurals[i].szName )) + { + g_CustomProcedurals.FastRemove( i ); + break; + } + } + + // + // Add a new custom procedural. + // + int i = g_CustomProcedurals.AddToTail(); + g_CustomProcedurals[i].szName = pszName; + g_CustomProcedurals[i].hFunc = hFunc; + g_CustomProcedurals[i].bCanReturnMultiple = bCanReturnMultiple; +} + +void CGlobalEntityList::RemoveCustomProcedural( const char *pszName ) +{ + // + // Remove the specified custom procedural. + // + for (int i = 0; i < g_CustomProcedurals.Count(); i++) + { + if (FStrEq( pszName, g_CustomProcedurals[i].szName )) + { + g_CustomProcedurals.FastRemove( i ); + break; + } + } +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Iterates the entities with a given name. // Input : pStartEntity - Last entity found, NULL to start a new iteration. @@ -647,6 +797,16 @@ CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, con if ( szName[0] == '!' ) { +#ifdef MAPBASE + if (g_CustomProcedurals.Count() > 0) + { + // Search for a custom procedural + CBaseEntity *ent = FindEntityCustomProcedural( pStartEntity, szName, pSearchingEntity, pActivator, pCaller ); + if (ent != NULL) + return ent; + } +#endif + // // Avoid an infinite loop, only find one match per procedural search! // @@ -682,6 +842,35 @@ CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, con return NULL; } +// From Alien Swarm SDK +CBaseEntity *CGlobalEntityList::FindEntityByNameFast( CBaseEntity *pStartEntity, string_t iszName ) +{ + if ( iszName == NULL_STRING || STRING(iszName)[0] == 0 ) + return NULL; + + const CEntInfo *pInfo = pStartEntity ? GetEntInfoPtr( pStartEntity->GetRefEHandle() )->m_pNext : 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->m_iName.Get() ) + continue; + + if ( ent->m_iName.Get() == iszName ) + { + return ent; + } + } + + return NULL; +} + //----------------------------------------------------------------------------- // Purpose: // Input : pStartEntity - @@ -899,6 +1088,80 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearest( const char *szName } +// From Alien Swarm SDK +CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearestFast( string_t iszName, const Vector &vecSrc, float flRadius ) +{ + CBaseEntity *pEntity = NULL; + + // + // Check for matching class names within the search radius. + // + float flMaxDist2 = flRadius * flRadius; + if (flMaxDist2 == 0) + { + flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH; + } + + CBaseEntity *pSearch = NULL; + while ((pSearch = gEntList.FindEntityByClassnameFast( pSearch, iszName )) != NULL) + { + if ( !pSearch->edict() ) + continue; + + float flDist2 = (pSearch->GetAbsOrigin() - vecSrc).LengthSqr(); + + if (flMaxDist2 > flDist2) + { + pEntity = pSearch; + flMaxDist2 = flDist2; + } + } + + return pEntity; +} + + +//----------------------------------------------------------------------------- +// Purpose: Finds the nearest entity by class name withing given search radius. +// From Alien Swarm SDK +// Input : szName - Entity name to search for. Treated as a target name first, +// then as an entity class name, ie "info_target". +// vecSrc - Center of search radius. +// flRadius - Search radius for classname search, 0 to search everywhere. +// Output : Returns a pointer to the found entity, NULL if none. +//----------------------------------------------------------------------------- +CBaseEntity *CGlobalEntityList::FindEntityByClassnameNearest2D( const char *szName, const Vector &vecSrc, float flRadius ) +{ + CBaseEntity *pEntity = NULL; + + // + // Check for matching class names within the search radius. + // + float flMaxDist2 = flRadius * flRadius; + if (flMaxDist2 == 0) + { + flMaxDist2 = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH; + } + + CBaseEntity *pSearch = NULL; + while ((pSearch = gEntList.FindEntityByClassname( pSearch, szName )) != NULL) + { + if ( !pSearch->edict() ) + continue; + + float flDist2 = (pSearch->GetAbsOrigin().AsVector2D() - vecSrc.AsVector2D()).LengthSqr(); + + if (flMaxDist2 > flDist2) + { + pEntity = pSearch; + flMaxDist2 = flDist2; + } + } + + return pEntity; +} + + //----------------------------------------------------------------------------- // Purpose: Finds the first entity within radius distance by class name. diff --git a/mp/src/game/server/entitylist.h b/mp/src/game/server/entitylist.h index 27074b3f..d8c02a13 100644 --- a/mp/src/game/server/entitylist.h +++ b/mp/src/game/server/entitylist.h @@ -74,6 +74,26 @@ public: virtual bool ShouldFindEntity( CBaseEntity *pEntity ) { return false; } virtual CBaseEntity *GetFilterResult( void ) { return NULL; } }; + +//----------------------------------------------------------------------------- +// Custom procedural names via VScript. This allows users to reference new '!' targets +// or override existing ones. It is capable of returning multiple entities when needed. +// +// This is useful if you want I/O and beyond to reference an entity/a number of entities +// which may possess differing or ambiguous targetnames, or if you want to reference an +// entity specific to the context of the operation (e.g. getting the searching entity's +// current weapon). +// +// For example, you could add a new procedural called "!first_child" which uses VScript to +// return the searching entity's first child entity. You could also add a procedural called +// "!children" which returns all of the searching entity's child entities. +//----------------------------------------------------------------------------- +struct CustomProcedural_t +{ + HSCRIPT hFunc; + const char *szName; + bool bCanReturnMultiple; +}; #endif //----------------------------------------------------------------------------- @@ -160,6 +180,7 @@ public: CBaseEntity *FindEntityByNameNearest( const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); CBaseEntity *FindEntityByNameWithin( CBaseEntity *pStartEntity, const char *szName, const Vector &vecSrc, float flRadius, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); CBaseEntity *FindEntityByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius ); + CBaseEntity *FindEntityByClassnameNearest2D( const char *szName, const Vector &vecSrc, float flRadius ); // From Alien Swarm SDK 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 ); @@ -175,7 +196,19 @@ public: CBaseEntity *FindEntityClassNearestFacing( const Vector &origin, const Vector &facing, float threshold, char *classname); CBaseEntity *FindEntityProcedural( const char *szName, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); - +#ifdef MAPBASE_VSCRIPT + CBaseEntity *FindEntityCustomProcedural( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pSearchingEntity = NULL, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL ); + + void AddCustomProcedural( const char *pszName, HSCRIPT hFunc, bool bCanReturnMultiple ); + void RemoveCustomProcedural( const char *pszName ); +#endif + + // Fast versions that require a (real) string_t, and won't do wildcarding + // From Alien Swarm SDK + CBaseEntity *FindEntityByClassnameFast( CBaseEntity *pStartEntity, string_t iszClassname ); + CBaseEntity *FindEntityByClassnameNearestFast( string_t iszClassname, const Vector &vecSrc, float flRadius ); + CBaseEntity *FindEntityByNameFast( CBaseEntity *pStartEntity, string_t iszName ); + CGlobalEntityList(); // CBaseEntityList overrides. @@ -372,6 +405,7 @@ extern INotify *g_pNotify; void EntityTouch_Add( CBaseEntity *pEntity ); int AimTarget_ListCount(); int AimTarget_ListCopy( CBaseEntity *pList[], int listMax ); +CBaseEntity *AimTarget_ListElement( int iIndex ); void AimTarget_ForceRepopulateList(); void SimThink_EntityChanged( CBaseEntity *pEntity ); diff --git a/mp/src/game/server/env_instructor_hint.cpp b/mp/src/game/server/env_instructor_hint.cpp index 32b75041..3ee20fb2 100644 --- a/mp/src/game/server/env_instructor_hint.cpp +++ b/mp/src/game/server/env_instructor_hint.cpp @@ -8,6 +8,9 @@ #include "cbase.h" #include "baseentity.h" #include "world.h" +#ifdef MAPBASE +#include "eventqueue.h" +#endif #ifdef INFESTED_DLL #include "asw_marine.h" @@ -26,6 +29,15 @@ public: DECLARE_CLASS( CEnvInstructorHint, CPointEntity ); DECLARE_DATADESC(); +#ifdef MAPBASE + CEnvInstructorHint( void ); +#endif + virtual ~CEnvInstructorHint( void ) {} + +#ifdef MAPBASE + virtual void OnRestore( void ); +#endif + private: void InputShowHint( inputdata_t &inputdata ); void InputEndHint( inputdata_t &inputdata ); @@ -56,6 +68,10 @@ private: #ifdef MAPBASE string_t m_iszStartSound; int m_iHintTargetPos; + float m_flActiveUntil; + CHandle m_hActivator; + EHANDLE m_hTarget; + bool m_bFilterByActivator; #endif }; @@ -85,8 +101,13 @@ BEGIN_DATADESC( CEnvInstructorHint ) #ifdef MAPBASE DEFINE_KEYFIELD( m_iszStartSound, FIELD_STRING, "hint_start_sound" ), DEFINE_KEYFIELD( m_iHintTargetPos, FIELD_INTEGER, "hint_target_pos" ), + + DEFINE_FIELD( m_flActiveUntil, FIELD_TIME ), + DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ), + DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ), + DEFINE_FIELD( m_bFilterByActivator, FIELD_BOOLEAN ), #endif - + DEFINE_INPUTFUNC( FIELD_STRING, "ShowHint", InputShowHint ), DEFINE_INPUTFUNC( FIELD_VOID, "EndHint", InputEndHint ), @@ -102,6 +123,44 @@ END_DATADESC() #define LOCATOR_ICON_FX_SHAKE_NARROW 0x00000040 #define LOCATOR_ICON_FX_STATIC 0x00000100 // This icon draws at a fixed location on the HUD. +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CEnvInstructorHint::CEnvInstructorHint( void ) +{ + m_hActivator = NULL; + m_hTarget = NULL; + m_bFilterByActivator = false; + m_flActiveUntil = -1.0f; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvInstructorHint::OnRestore( void ) +{ + BaseClass::OnRestore(); + + int iTimeLeft = 0; + if ( m_flActiveUntil < 0.0f ) + { + return; + } + if ( m_iTimeout != 0 ) + { + iTimeLeft = static_cast( m_flActiveUntil - gpGlobals->curtime ); + if ( iTimeLeft <= 0 ) + { + return; + } + } + + int iOriginalTimeout = m_iTimeout; + m_iTimeout = iTimeLeft; + g_EventQueue.AddEvent( this, "ShowHint", 0.01f, NULL, this ); + m_iTimeout = iOriginalTimeout; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Input handler for showing the message and/or playing the sound. //----------------------------------------------------------------------------- @@ -110,7 +169,15 @@ void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) IGameEvent * event = gameeventmanager->CreateEvent( "instructor_server_hint_create", false ); if ( event ) { - CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, m_iszHintTargetEntity ); + CBaseEntity *pTargetEntity = NULL; + +#ifdef MAPBASE + pTargetEntity = m_hTarget; + + if ( pTargetEntity == NULL ) +#endif + pTargetEntity = gEntList.FindEntityByName( NULL, m_iszHintTargetEntity ); + if( pTargetEntity == NULL && !m_bStatic ) pTargetEntity = inputdata.pActivator; @@ -137,6 +204,15 @@ void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) pActivator = pMarine->GetCommander(); } #else +#ifdef MAPBASE + if ( m_hActivator ) + { + pActivator = m_hActivator; + bFilterByActivator = m_bFilterByActivator; + } + else +#endif + if ( inputdata.value.StringID() != NULL_STRING ) { CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String() ); @@ -150,7 +226,7 @@ void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) { if ( GameRules()->IsMultiplayer() == false ) { - pActivator = UTIL_GetLocalPlayer(); + pActivator = UTIL_GetLocalPlayer(); } else { @@ -190,6 +266,13 @@ void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) #endif gameeventmanager->FireEvent( event ); + +#ifdef MAPBASE + m_flActiveUntil = gpGlobals->curtime + m_iTimeout; + m_hTarget = pTargetEntity; + m_hActivator = pActivator; + m_bFilterByActivator = bFilterByActivator; +#endif } } @@ -203,6 +286,13 @@ void CEnvInstructorHint::InputEndHint( inputdata_t &inputdata ) event->SetString( "hint_name", GetEntityName().ToCStr() ); gameeventmanager->FireEvent( event ); + +#ifdef MAPBASE + m_flActiveUntil = -1.0f; + m_hActivator = NULL; + m_hTarget = NULL; + m_bFilterByActivator = false; +#endif } } diff --git a/mp/src/game/server/env_projectedtexture.cpp b/mp/src/game/server/env_projectedtexture.cpp index 0299fbd0..15fb1367 100644 --- a/mp/src/game/server/env_projectedtexture.cpp +++ b/mp/src/game/server/env_projectedtexture.cpp @@ -50,6 +50,7 @@ BEGIN_DATADESC( CEnvProjectedTexture ) DEFINE_FIELD( m_flLinearAtten, FIELD_FLOAT ), DEFINE_FIELD( m_flQuadraticAtten, FIELD_FLOAT ), DEFINE_KEYFIELD( m_flShadowAtten, FIELD_FLOAT, "shadowatten" ), + DEFINE_KEYFIELD( m_flShadowFilter, FIELD_FLOAT, "shadowfilter" ), #endif DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), @@ -77,6 +78,7 @@ BEGIN_DATADESC( CEnvProjectedTexture ) DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLinear", InputSetLinear ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetConstant", InputSetConstant ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetShadowAtten", InputSetShadowAtten ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFilter", InputSetFilter ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetNearZ", InputSetNearZ ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFarZ", InputSetFarZ ), DEFINE_INPUTFUNC( FIELD_VOID, "AlwaysDrawOn", InputAlwaysDrawOn ), @@ -116,6 +118,7 @@ IMPLEMENT_SERVERCLASS_ST( CEnvProjectedTexture, DT_EnvProjectedTexture ) SendPropFloat( SENDINFO( m_flLinearAtten ) ), SendPropFloat( SENDINFO( m_flQuadraticAtten ) ), SendPropFloat( SENDINFO( m_flShadowAtten ) ), + SendPropFloat( SENDINFO( m_flShadowFilter ) ), SendPropBool( SENDINFO( m_bAlwaysDraw ) ), // Not needed on the client right now, change when it actually is needed @@ -158,6 +161,7 @@ CEnvProjectedTexture::CEnvProjectedTexture( void ) m_flLinearAtten = 100.0f; m_flConstantAtten = 0.0f; m_flShadowAtten = 0.0f; + m_flShadowFilter = 0.5f; #endif } @@ -316,6 +320,11 @@ void CEnvProjectedTexture::InputSetHorFOV( inputdata_t &inputdata ) { m_flLightHorFOV = inputdata.value.Float(); } + +void CEnvProjectedTexture::InputSetFilter( inputdata_t &inputdata ) +{ + m_flShadowFilter = inputdata.value.Float(); +} #endif void CEnvProjectedTexture::InputSetTarget( inputdata_t &inputdata ) diff --git a/mp/src/game/server/env_projectedtexture.h b/mp/src/game/server/env_projectedtexture.h index 06ff04ee..6cb248ba 100644 --- a/mp/src/game/server/env_projectedtexture.h +++ b/mp/src/game/server/env_projectedtexture.h @@ -64,6 +64,7 @@ public: void InputAlwaysDrawOff( inputdata_t &inputdata ) { m_bAlwaysDraw = false; } void InputStopFollowingTarget( inputdata_t &inputdata ) { m_bDontFollowTarget = true; } void InputStartFollowingTarget( inputdata_t &inputdata ) { m_bDontFollowTarget = false; } + void InputSetFilter( inputdata_t &inputdata ); // Corrects keyvalue/input attenuation for internal FlashlightEffect_t attenuation. float CorrectConstantAtten( float fl ) { return fl * 0.5f; } @@ -105,6 +106,8 @@ private: CNetworkVar( float, m_flQuadraticAtten ); CNetworkVar( float, m_flShadowAtten ); + CNetworkVar( float, m_flShadowFilter ); + CNetworkVar( bool, m_bAlwaysDraw ); // 1 = New projected texture diff --git a/mp/src/game/server/env_screenoverlay.cpp b/mp/src/game/server/env_screenoverlay.cpp index 07bbf08d..259a6fdc 100644 --- a/mp/src/game/server/env_screenoverlay.cpp +++ b/mp/src/game/server/env_screenoverlay.cpp @@ -39,6 +39,9 @@ protected: CNetworkVar( float, m_flStartTime ); CNetworkVar( int, m_iDesiredOverlay ); CNetworkVar( bool, m_bIsActive ); +#ifdef MAPBASE + CNetworkVar( int, m_iOverlayIndex ); +#endif }; LINK_ENTITY_TO_CLASS( env_screenoverlay, CEnvScreenOverlay ); @@ -74,6 +77,9 @@ BEGIN_DATADESC( CEnvScreenOverlay ) DEFINE_FIELD( m_iDesiredOverlay, FIELD_INTEGER ), DEFINE_FIELD( m_flStartTime, FIELD_TIME ), DEFINE_FIELD( m_bIsActive, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iOverlayIndex, FIELD_INTEGER, "OverlayIndex" ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "StartOverlays", InputStartOverlay ), DEFINE_INPUTFUNC( FIELD_VOID, "StopOverlays", InputStopOverlay ), @@ -93,6 +99,9 @@ IMPLEMENT_SERVERCLASS_ST( CEnvScreenOverlay, DT_EnvScreenOverlay ) SendPropFloat( SENDINFO( m_flStartTime ), 32, SPROP_NOSCALE ), SendPropInt( SENDINFO( m_iDesiredOverlay ), 5 ), SendPropBool( SENDINFO( m_bIsActive ) ), +#ifdef MAPBASE + SendPropInt( SENDINFO( m_iOverlayIndex ), 5 ), +#endif END_SEND_TABLE() //----------------------------------------------------------------------------- @@ -103,6 +112,9 @@ CEnvScreenOverlay::CEnvScreenOverlay( void ) m_flStartTime = 0; m_iDesiredOverlay = 0; m_bIsActive = false; +#ifdef MAPBASE + m_iOverlayIndex = -1; +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/env_tonemap_controller.cpp b/mp/src/game/server/env_tonemap_controller.cpp index 39998e4b..95cf6135 100644 --- a/mp/src/game/server/env_tonemap_controller.cpp +++ b/mp/src/game/server/env_tonemap_controller.cpp @@ -5,6 +5,7 @@ //============================================================================= #include "cbase.h" +#include "env_tonemap_controller.h" #include "baseentity.h" #include "entityoutput.h" #include "convar.h" @@ -16,50 +17,6 @@ ConVar mat_hdr_tonemapscale( "mat_hdr_tonemapscale", "1.0", FCVAR_CHEAT, "The HDR tonemap scale. 1 = Use autoexposure, 0 = eyes fully closed, 16 = eyes wide open." ); -// 0 - eyes fully closed / fully black -// 1 - nominal -// 16 - eyes wide open / fully white - -//----------------------------------------------------------------------------- -// Purpose: Entity that controls player's tonemap -//----------------------------------------------------------------------------- -class CEnvTonemapController : public CPointEntity -{ - DECLARE_CLASS( CEnvTonemapController, CPointEntity ); -public: - DECLARE_DATADESC(); - DECLARE_SERVERCLASS(); - - void Spawn( void ); - int UpdateTransmitState( void ); - void UpdateTonemapScaleBlend( void ); - - // Inputs - void InputSetTonemapScale( inputdata_t &inputdata ); - void InputBlendTonemapScale( inputdata_t &inputdata ); - void InputSetTonemapRate( inputdata_t &inputdata ); - void InputSetAutoExposureMin( inputdata_t &inputdata ); - void InputSetAutoExposureMax( inputdata_t &inputdata ); - void InputUseDefaultAutoExposure( inputdata_t &inputdata ); - void InputSetBloomScale( inputdata_t &inputdata ); - void InputUseDefaultBloomScale( inputdata_t &inputdata ); - void InputSetBloomScaleRange( inputdata_t &inputdata ); - -private: - float m_flBlendTonemapStart; // HDR Tonemap at the start of the blend - float m_flBlendTonemapEnd; // Target HDR Tonemap at the end of the blend - float m_flBlendEndTime; // Time at which the blend ends - float m_flBlendStartTime; // Time at which the blend started - - CNetworkVar( bool, m_bUseCustomAutoExposureMin ); - CNetworkVar( bool, m_bUseCustomAutoExposureMax ); - CNetworkVar( bool, m_bUseCustomBloomScale ); - CNetworkVar( float, m_flCustomAutoExposureMin ); - CNetworkVar( float, m_flCustomAutoExposureMax ); - CNetworkVar( float, m_flCustomBloomScale); - CNetworkVar( float, m_flCustomBloomScaleMinimum); -}; - LINK_ENTITY_TO_CLASS( env_tonemap_controller, CEnvTonemapController ); BEGIN_DATADESC( CEnvTonemapController ) @@ -120,27 +77,72 @@ int CEnvTonemapController::UpdateTransmitState() return SetTransmitState( FL_EDICT_ALWAYS ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CEnvTonemapController::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "TonemapScale" ) ) + { + float flTonemapScale = atof( szValue ); + if (flTonemapScale != -1.0f) + { + mat_hdr_tonemapscale.SetValue( flTonemapScale ); + } + } + else if (FStrEq( szKeyName, "TonemapRate" )) + { + float flTonemapRate = atof( szValue ); + if (flTonemapRate != -1.0f) + { + ConVarRef mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate" ); + if ( mat_hdr_manual_tonemap_rate.IsValid() ) + { + mat_hdr_manual_tonemap_rate.SetValue( flTonemapRate ); + } + } + } + else if (FStrEq( szKeyName, "AutoExposureMin" )) + { + float flAutoExposureMin = atof( szValue ); + if (flAutoExposureMin != -1.0f) + { + m_flCustomAutoExposureMin = flAutoExposureMin; + m_bUseCustomAutoExposureMin = true; + } + } + else if (FStrEq( szKeyName, "AutoExposureMax" )) + { + float flAutoExposureMax = atof( szValue ); + if (flAutoExposureMax != -1.0f) + { + m_flCustomAutoExposureMax = flAutoExposureMax; + m_bUseCustomAutoExposureMax = true; + } + } + else if (FStrEq( szKeyName, "BloomScale" )) + { + float flBloomScale = atof( szValue ); + if (flBloomScale != -1.0f) + { + m_flCustomBloomScale = flBloomScale; + m_flCustomBloomScaleMinimum = flBloomScale; + m_bUseCustomBloomScale = true; + } + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Set the tonemap scale to the specified value //----------------------------------------------------------------------------- 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 ); } @@ -150,10 +152,6 @@ 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)); @@ -189,7 +187,7 @@ void CEnvTonemapController::InputBlendTonemapScale( inputdata_t &inputdata ) void CEnvTonemapController::InputSetBloomScaleRange( inputdata_t &inputdata ) { float bloom_max=1, bloom_min=1; - int nargs=sscanf("%f %f",inputdata.value.String(), bloom_max, bloom_min ); + int nargs=sscanf( inputdata.value.String(), "%f %f", &bloom_max, &bloom_min ); if (nargs != 2) { Warning("%s (%s) received SetBloomScaleRange input without 2 arguments. Syntax: \n", GetClassname(), GetDebugName() ); @@ -197,6 +195,9 @@ void CEnvTonemapController::InputSetBloomScaleRange( inputdata_t &inputdata ) } m_flCustomBloomScale=bloom_max; m_flCustomBloomScaleMinimum=bloom_min; +#ifdef MAPBASE + m_bUseCustomBloomScale = true; +#endif } //----------------------------------------------------------------------------- @@ -204,22 +205,6 @@ 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() ) @@ -251,22 +236,6 @@ 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; } @@ -276,22 +245,6 @@ 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; } @@ -301,22 +254,6 @@ 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; } @@ -326,22 +263,6 @@ 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; @@ -352,21 +273,111 @@ 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; } + +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +LINK_ENTITY_TO_CLASS( trigger_tonemap, CTonemapTrigger ); + +BEGIN_DATADESC( CTonemapTrigger ) + DEFINE_KEYFIELD( m_tonemapControllerName, FIELD_STRING, "TonemapName" ), +END_DATADESC() + + +//-------------------------------------------------------------------------------------------------------- +void CTonemapTrigger::Spawn( void ) +{ + AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS ); + + BaseClass::Spawn(); + InitTrigger(); + + m_hTonemapController = gEntList.FindEntityByName( NULL, m_tonemapControllerName ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CTonemapTrigger::StartTouch( CBaseEntity *other ) +{ + if ( !PassesTriggerFilters( other ) ) + return; + + BaseClass::StartTouch( other ); + + CBasePlayer *player = ToBasePlayer( other ); + if ( !player ) + return; + + player->OnTonemapTriggerStartTouch( this ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CTonemapTrigger::EndTouch( CBaseEntity *other ) +{ + if ( !PassesTriggerFilters( other ) ) + return; + + BaseClass::EndTouch( other ); + + CBasePlayer *player = ToBasePlayer( other ); + if ( !player ) + return; + + player->OnTonemapTriggerEndTouch( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clear out the tonemap controller. +//----------------------------------------------------------------------------- +void CTonemapSystem::LevelInitPreEntity( void ) +{ + m_hMasterController = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: On level load find the master fog controller. If no controller is +// set as Master, use the first fog controller found. +//----------------------------------------------------------------------------- +void CTonemapSystem::LevelInitPostEntity( void ) +{ + // Overall master controller + CEnvTonemapController *pTonemapController = NULL; + do + { + pTonemapController = static_cast( gEntList.FindEntityByClassname( pTonemapController, "env_tonemap_controller" ) ); + if ( pTonemapController ) + { + if ( m_hMasterController == NULL ) + { + m_hMasterController = pTonemapController; + } + else + { + if ( pTonemapController->IsMaster() ) + { + m_hMasterController = pTonemapController; + } + } + } + } while ( pTonemapController ); + + +} + + +//-------------------------------------------------------------------------------------------------------- +CTonemapSystem s_TonemapSystem( "TonemapSystem" ); + + +//-------------------------------------------------------------------------------------------------------- +CTonemapSystem *TheTonemapSystem( void ) +{ + return &s_TonemapSystem; +} + + +//-------------------------------------------------------------------------------------------------------- +#endif diff --git a/mp/src/game/server/env_tonemap_controller.h b/mp/src/game/server/env_tonemap_controller.h new file mode 100644 index 00000000..94e3e0f4 --- /dev/null +++ b/mp/src/game/server/env_tonemap_controller.h @@ -0,0 +1,140 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013. +// Although technically a new Mapbase file, it only serves to move otherwise identical code, +// so most code and repo conventions will pretend it was always there. +// +// -------------------------------------------------------------------- +// +// Purpose: +// +//=============================================================================// + +#ifndef ENV_TONEMAP_CONTROLLER_H +#define ENV_TONEMAP_CONTROLLER_H + +#include "triggers.h" + +// 0 - eyes fully closed / fully black +// 1 - nominal +// 16 - eyes wide open / fully white + +#ifdef MAPBASE // From Alien Swarm SDK +// Spawn Flags +#define SF_TONEMAP_MASTER 0x0001 +#endif + +//----------------------------------------------------------------------------- +// Purpose: Entity that controls player's tonemap +//----------------------------------------------------------------------------- +class CEnvTonemapController : public CPointEntity +{ + DECLARE_CLASS( CEnvTonemapController, CPointEntity ); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + void Spawn( void ); + int UpdateTransmitState( void ); + void UpdateTonemapScaleBlend( void ); + +#ifdef MAPBASE + bool IsMaster( void ) const { return HasSpawnFlags( SF_TONEMAP_MASTER ); } // From Alien Swarm SDK + + bool KeyValue( const char *szKeyName, const char *szValue ); +#endif + + // Inputs + void InputSetTonemapScale( inputdata_t &inputdata ); + void InputBlendTonemapScale( inputdata_t &inputdata ); + void InputSetTonemapRate( inputdata_t &inputdata ); + void InputSetAutoExposureMin( inputdata_t &inputdata ); + void InputSetAutoExposureMax( inputdata_t &inputdata ); + void InputUseDefaultAutoExposure( inputdata_t &inputdata ); + void InputSetBloomScale( inputdata_t &inputdata ); + void InputUseDefaultBloomScale( inputdata_t &inputdata ); + void InputSetBloomScaleRange( inputdata_t &inputdata ); + +private: + float m_flBlendTonemapStart; // HDR Tonemap at the start of the blend + float m_flBlendTonemapEnd; // Target HDR Tonemap at the end of the blend + float m_flBlendEndTime; // Time at which the blend ends + float m_flBlendStartTime; // Time at which the blend started + +#ifdef MAPBASE // From Alien Swarm SDK +public: +#endif + CNetworkVar( bool, m_bUseCustomAutoExposureMin ); + CNetworkVar( bool, m_bUseCustomAutoExposureMax ); + CNetworkVar( bool, m_bUseCustomBloomScale ); + CNetworkVar( float, m_flCustomAutoExposureMin ); + CNetworkVar( float, m_flCustomAutoExposureMax ); + CNetworkVar( float, m_flCustomBloomScale); + CNetworkVar( float, m_flCustomBloomScaleMinimum); +}; + +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +class CTonemapTrigger : public CBaseTrigger +{ +public: + DECLARE_CLASS( CTonemapTrigger, CBaseTrigger ); + DECLARE_DATADESC(); + + virtual void Spawn( void ); + virtual void StartTouch( CBaseEntity *other ); + virtual void EndTouch( CBaseEntity *other ); + + CBaseEntity *GetTonemapController( void ) const; + +private: + string_t m_tonemapControllerName; + EHANDLE m_hTonemapController; +}; + + +//-------------------------------------------------------------------------------------------------------- +inline CBaseEntity *CTonemapTrigger::GetTonemapController( void ) const +{ + return m_hTonemapController.Get(); +} + + +//-------------------------------------------------------------------------------------------------------- +// Tonemap Controller System. +class CTonemapSystem : public CAutoGameSystem +{ +public: + + // Creation/Init. + CTonemapSystem( char const *name ) : CAutoGameSystem( name ) + { + m_hMasterController = NULL; + } + + ~CTonemapSystem() + { + m_hMasterController = NULL; + } + + virtual void LevelInitPreEntity(); + virtual void LevelInitPostEntity(); + CBaseEntity *GetMasterTonemapController( void ) const; + +private: + + EHANDLE m_hMasterController; +}; + + +//-------------------------------------------------------------------------------------------------------- +inline CBaseEntity *CTonemapSystem::GetMasterTonemapController( void ) const +{ + return m_hMasterController.Get(); +} + +//-------------------------------------------------------------------------------------------------------- +CTonemapSystem *TheTonemapSystem( void ); +#endif + +#endif //ENV_TONEMAP_CONTROLLER_H \ No newline at end of file diff --git a/mp/src/game/server/envmicrophone.cpp b/mp/src/game/server/envmicrophone.cpp index ee78f87f..23b1758c 100644 --- a/mp/src/game/server/envmicrophone.cpp +++ b/mp/src/game/server/envmicrophone.cpp @@ -19,6 +19,9 @@ #include "soundflags.h" #include "engine/IEngineSound.h" #include "filters.h" +#ifdef MAPBASE +#include "fmtstr.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -27,6 +30,10 @@ const float MICROPHONE_SETTLE_EPSILON = 0.005; +#ifdef MAPBASE +static ConVar sv_microphones_always_pickup_sentences( "sv_microphones_always_pickup_sentences", "0", FCVAR_NONE, "Allows env_microphones to always detect and play back sentences, regardless of their keyvalues." ); +#endif + // List of env_microphones who want to be told whenever a sound is started static CUtlVector< CHandle > s_Microphones; @@ -48,6 +55,7 @@ BEGIN_DATADESC( CEnvMicrophone ) DEFINE_KEYFIELD(m_iszLandmarkName, FIELD_STRING, "landmark"), DEFINE_FIELD(m_hLandmark, FIELD_EHANDLE), DEFINE_KEYFIELD(m_flPitchScale, FIELD_FLOAT, "PitchScale"), + DEFINE_KEYFIELD(m_flVolumeScale, FIELD_FLOAT, "VolumeScale"), DEFINE_KEYFIELD(m_nChannel, FIELD_INTEGER, "channel"), #endif // DEFINE_FIELD(m_bAvoidFeedback, FIELD_BOOLEAN), // DONT SAVE @@ -61,6 +69,7 @@ BEGIN_DATADESC( CEnvMicrophone ) #ifdef MAPBASE DEFINE_INPUTFUNC(FIELD_INTEGER, "SetDSPPreset", InputSetDSPPreset), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPitchScale", InputSetPitchScale ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetVolumeScale", InputSetVolumeScale ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetChannel", InputSetChannel ), #endif @@ -234,7 +243,11 @@ void CEnvMicrophone::InputDisable( inputdata_t &inputdata ) m_bDisabled = true; if ( m_hSpeaker ) { +#ifdef MAPBASE + CBaseEntity::StopSound( m_hSpeaker->entindex(), m_nChannel, m_szLastSound ); +#else CBaseEntity::StopSound( m_hSpeaker->entindex(), CHAN_STATIC, m_szLastSound ); +#endif m_szLastSound[0] = 0; // Remove ourselves from the list of active mics @@ -272,6 +285,15 @@ void CEnvMicrophone::InputSetPitchScale( inputdata_t &inputdata ) m_flPitchScale = inputdata.value.Float(); } +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvMicrophone::InputSetVolumeScale( inputdata_t &inputdata ) +{ + m_flVolumeScale = inputdata.value.Float(); +} + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -543,29 +565,41 @@ 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; - CBaseEntity::EmitSound( filter, m_hSpeaker->entindex(), ep ); +#ifdef MAPBASE + if (m_bHearingSentence) + { + CBaseEntity::EmitSentenceByIndex( filter, m_hSpeaker->entindex(), m_nChannel, atoi(soundname), flVolume, soundlevel, 0, iPitch, &vecOrigin, NULL, true, soundtime, + m_iSpeakerDSPPreset, entindex ); + } + else +#endif + { +#ifdef MAPBASE + ep.m_nChannel = m_nChannel; + if (m_flVolumeScale != 1.0f) + ep.m_flVolume = (flVolume * m_flVolumeScale); + else + ep.m_flVolume = flVolume; + 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_nChannel = CHAN_STATIC; + ep.m_flVolume = flVolume; + ep.m_nPitch = iPitch; + ep.m_pOrigin = &m_hSpeaker->GetAbsOrigin(); +#endif + ep.m_pSoundName = soundname; + ep.m_SoundLevel = soundlevel; + ep.m_nFlags = iFlags; + ep.m_flSoundTime = soundtime; + ep.m_nSpeakerEntity = entindex; + + CBaseEntity::EmitSound( filter, m_hSpeaker->entindex(), ep ); + } Q_strncpy( m_szLastSound, soundname, sizeof(m_szLastSound) ); m_OnRoutedSound.FireOutput( this, this, 0 ); @@ -633,3 +667,56 @@ bool CEnvMicrophone::OnSoundPlayed( int entindex, const char *soundname, soundle return bSwallowed; } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Called by the sound system whenever a sentence is played so that +// active microphones can have a chance to pick up the sound. +// Output : Returns whether or not the sentence was swallowed by the microphone. +// Swallowed sentences should not be played by the sound system. +//----------------------------------------------------------------------------- +bool CEnvMicrophone::OnSentencePlayed( int entindex, int sentenceIndex, soundlevel_t soundlevel, float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins ) +{ + bool bSwallowed = false; + + // Loop through all registered microphones and tell them the sound was just played + int iCount = s_Microphones.Count(); + if ( iCount > 0 ) + { + CNumStr szSentenceStr( sentenceIndex ); + + // Iterate backwards because we might be deleting microphones. + for ( int i = iCount - 1; i >= 0; i-- ) + { + if ( s_Microphones[i] && (s_Microphones[i]->ShouldHearSentences() || sv_microphones_always_pickup_sentences.GetBool()) ) + { + // HACKHACK: Don't want to duplicate all of the code, so just use the same function with a new member variable + s_Microphones[i]->ToggleHearingSentence( true ); + MicrophoneResult_t eResult = s_Microphones[i]->SoundPlayed( + entindex, + szSentenceStr, + soundlevel, + flVolume, + iFlags, + iPitch, + pOrigin, + soundtime, + soundorigins ); + s_Microphones[i]->ToggleHearingSentence( false ); + + if ( eResult == MicrophoneResult_Swallow ) + { + // Microphone told us to swallow it + bSwallowed = true; + } + else if ( eResult == MicrophoneResult_Remove ) + { + s_Microphones.FastRemove( i ); + } + } + } + } + + return bSwallowed; +} +#endif diff --git a/mp/src/game/server/envmicrophone.h b/mp/src/game/server/envmicrophone.h index cf8e729e..e330099c 100644 --- a/mp/src/game/server/envmicrophone.h +++ b/mp/src/game/server/envmicrophone.h @@ -20,6 +20,9 @@ const int SF_MICROPHONE_SOUND_BULLET_IMPACT = 0x08; const int SF_MICROPHONE_SWALLOW_ROUTED_SOUNDS = 0x10; const int SF_MICROPHONE_SOUND_EXPLOSION = 0x20; const int SF_MICROPHONE_IGNORE_NONATTENUATED = 0x40; +#ifdef MAPBASE +const int SF_MICROPHONE_SOUND_SENTENCE = 0x80; +#endif // Return codes from SoundPlayed @@ -50,6 +53,10 @@ public: void SetSensitivity( float flSensitivity ); void SetSpeakerName( string_t iszSpeakerName ); +#ifdef MAPBASE + bool ShouldHearSentences() const { return HasSpawnFlags( SF_MICROPHONE_SOUND_SENTENCE ); } + inline void ToggleHearingSentence( bool bToggle ) { m_bHearingSentence = bToggle; } +#endif void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); @@ -57,6 +64,7 @@ public: #ifdef MAPBASE void InputSetDSPPreset( inputdata_t &inputdata ); void InputSetPitchScale( inputdata_t &inputdata ); + void InputSetVolumeScale( inputdata_t &inputdata ); void InputSetChannel( inputdata_t &inputdata ); #endif @@ -66,6 +74,12 @@ public: static bool OnSoundPlayed( int entindex, const char *soundname, soundlevel_t soundlevel, float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins ); +#ifdef MAPBASE + // Same as above, except for sentences. + static bool OnSentencePlayed( int entindex, int sentenceIndex, soundlevel_t soundlevel, + float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins ); +#endif + private: // Per-microphone notification that a sound has played. @@ -88,7 +102,10 @@ private: string_t m_iszLandmarkName; EHANDLE m_hLandmark; float m_flPitchScale = 1.0f; + float m_flVolumeScale = 1.0f; int m_nChannel = CHAN_STATIC; + + bool m_bHearingSentence; // HACKHACK: Allows SoundPlayed() to know when to play a sentence instead #endif COutputFloat m_SoundLevel; // Fired when the sampled volume level changes. diff --git a/mp/src/game/server/episodic/npc_hunter.cpp b/mp/src/game/server/episodic/npc_hunter.cpp index ac904b81..0699d890 100644 --- a/mp/src/game/server/episodic/npc_hunter.cpp +++ b/mp/src/game/server/episodic/npc_hunter.cpp @@ -1447,6 +1447,10 @@ private: string_t m_iszFollowTarget; // Name of the strider we should follow. CSimpleStopwatch m_BeginFollowDelay; +#ifdef MAPBASE + bool m_bNoIdlePatrol; +#endif + int m_nKillingDamageType; HunterEyeStates_t m_eEyeState; @@ -1549,6 +1553,10 @@ BEGIN_DATADESC( CNPC_Hunter ) DEFINE_KEYFIELD( m_iszFollowTarget, FIELD_STRING, "FollowTarget" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bNoIdlePatrol, FIELD_BOOLEAN, "NoIdlePatrol" ), +#endif + DEFINE_FIELD( m_aimYaw, FIELD_FLOAT ), DEFINE_FIELD( m_aimPitch, FIELD_FLOAT ), @@ -1686,8 +1694,16 @@ CNPC_Hunter::~CNPC_Hunter() //----------------------------------------------------------------------------- void CNPC_Hunter::Precache() { +#ifdef MAPBASE + if (GetModelName() == NULL_STRING) + SetModelName( AllocPooledString( "models/hunter.mdl" ) ); + + PrecacheModel( STRING( GetModelName() ) ); + PropBreakablePrecacheAll( GetModelName() ); +#else PrecacheModel( "models/hunter.mdl" ); PropBreakablePrecacheAll( MAKE_STRING("models/hunter.mdl") ); +#endif PrecacheScriptSound( "NPC_Hunter.Idle" ); PrecacheScriptSound( "NPC_Hunter.Scan" ); @@ -1748,7 +1764,11 @@ void CNPC_Hunter::Spawn() { Precache(); +#ifdef MAPBASE + SetModel( STRING( GetModelName() ) ); +#else SetModel( "models/hunter.mdl" ); +#endif BaseClass::Spawn(); //m_debugOverlays |= OVERLAY_NPC_ROUTE_BIT | OVERLAY_BBOX_BIT | OVERLAY_PIVOT_BIT; @@ -3123,6 +3143,9 @@ int CNPC_Hunter::SelectSchedule() { case NPC_STATE_IDLE: { +#ifdef MAPBASE + if (!m_bNoIdlePatrol) +#endif return SCHED_HUNTER_PATROL; } diff --git a/mp/src/game/server/episodic/npc_magnusson.cpp b/mp/src/game/server/episodic/npc_magnusson.cpp index 31008353..30e1f697 100644 --- a/mp/src/game/server/episodic/npc_magnusson.cpp +++ b/mp/src/game/server/episodic/npc_magnusson.cpp @@ -37,6 +37,11 @@ public: Class_T Classify ( void ); void HandleAnimEvent( animevent_t *pEvent ); int GetSoundInterests ( void ); + +#ifdef MAPBASE + // Use Magnusson's default subtitle color (209,178,178) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 209; params.g1 = 178; params.b1 = 178; return BaseClass::GetGameTextSpeechParams( params ); } +#endif }; LINK_ENTITY_TO_CLASS( npc_magnusson, CNPC_Magnusson ); diff --git a/mp/src/game/server/eventqueue.h b/mp/src/game/server/eventqueue.h index 61b0d252..d5cc2d5f 100644 --- a/mp/src/game/server/eventqueue.h +++ b/mp/src/game/server/eventqueue.h @@ -41,8 +41,8 @@ class CEventQueue public: // pushes an event into the queue, targeting a string name (m_iName), or directly by a pointer #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 ); + int AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); + int 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 ); @@ -73,8 +73,8 @@ public: #ifdef MAPBASE_VSCRIPT void CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ); - bool RemoveEvent( intptr_t event ); - float GetTimeLeft( intptr_t event ); + bool RemoveEvent( int event ); + float GetTimeLeft( int event ); #endif // MAPBASE_VSCRIPT private: diff --git a/mp/src/game/server/explode.cpp b/mp/src/game/server/explode.cpp index d0321356..d9031dd4 100644 --- a/mp/src/game/server/explode.cpp +++ b/mp/src/game/server/explode.cpp @@ -412,7 +412,7 @@ void ExplosionCreate( const Vector ¢er, const QAngle &angles, CEnvExplosion *pExplosion = (CEnvExplosion*)CBaseEntity::Create( "env_explosion", center, angles, pOwner ); Q_snprintf( buf,sizeof(buf), "%3d", magnitude ); - char *szKeyName = "iMagnitude"; + const char *szKeyName = "iMagnitude"; char *szValue = buf; pExplosion->KeyValue( szKeyName, szValue ); diff --git a/mp/src/game/server/filters.cpp b/mp/src/game/server/filters.cpp index 8b7dcfc9..c15f8eee 100644 --- a/mp/src/game/server/filters.cpp +++ b/mp/src/game/server/filters.cpp @@ -134,6 +134,12 @@ void CBaseFilter::InputTestActivator( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CBaseFilter::InputTestEntity( inputdata_t &inputdata ) { + if ( !inputdata.value.Entity() ) + { + // HACKHACK: Not firing OnFail in this case is intentional for the time being (activator shouldn't be null) + return; + } + if ( PassesFilter( inputdata.pCaller, inputdata.value.Entity() ) ) { m_OnPass.FireOutput( inputdata.value.Entity(), m_bPassCallerWhenTested ? inputdata.pCaller : this ); @@ -1522,6 +1528,7 @@ public: void Activate() { + BaseClass::Activate(); ParseSurfaceIndex(); } @@ -2156,118 +2163,125 @@ class CFilterScript : public CBaseFilter public: bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) { - if (m_ScriptScope.IsInitialized()) + if ( !m_ScriptScope.IsInitialized() ) + { + Warning( "%s: No script scope, cannot filter\n", GetDebugName() ); + return false; + } + + if ( g_Hook_PassesFilter.CanRunInScope( m_ScriptScope ) ) { // 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() ); - } + g_Hook_PassesFilter.Call( m_ScriptScope, &functionReturn, args ); return functionReturn.m_bool; } - Warning("%s: No script scope, cannot filter\n", GetDebugName()); + Warning( "%s: No PassesFilter function\n", GetDebugName() ); return false; } bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) { - if (m_ScriptScope.IsInitialized()) + if ( !m_ScriptScope.IsInitialized() ) + { + Warning( "%s: No script scope, cannot filter\n", GetDebugName() ); + return false; + } + + if ( g_Hook_PassesDamageFilter.CanRunInScope( m_ScriptScope ) ) { 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_Hook_PassesDamageFilter.Call( m_ScriptScope, &functionReturn, args ); g_pScriptVM->RemoveInstance( pInfo ); return functionReturn.m_bool; } - Warning("%s: No script scope, cannot filter\n", GetDebugName()); - return false; + // Fall back to main filter function + return PassesFilterImpl( pCaller, info.GetAttacker() ); } bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) { - if (m_ScriptScope.IsInitialized()) + if ( !m_ScriptScope.IsInitialized() ) + { + Warning( "%s: No script scope, cannot filter\n", GetDebugName() ); + return false; + } + + if ( g_Hook_PassesFinalDamageFilter.CanRunInScope( m_ScriptScope ) ) { 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_Hook_PassesFinalDamageFilter.Call( m_ScriptScope, &functionReturn, args ); g_pScriptVM->RemoveInstance( pInfo ); return functionReturn.m_bool; } - Warning("%s: No script scope, cannot filter\n", GetDebugName()); - return false; + return BaseClass::PassesFinalDamageFilter( pCaller, info ); } bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) { - if (m_ScriptScope.IsInitialized()) + if ( !m_ScriptScope.IsInitialized() ) + { + Warning( "%s: No script scope, cannot filter\n", GetDebugName() ); + return false; + } + + if ( g_Hook_BloodAllowed.CanRunInScope( m_ScriptScope ) ) { 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_Hook_BloodAllowed.Call( m_ScriptScope, &functionReturn, args ); g_pScriptVM->RemoveInstance( pInfo ); return functionReturn.m_bool; } - Warning("%s: No script scope, cannot filter\n", GetDebugName()); - return false; + return BaseClass::BloodAllowed( pCaller, info ); } bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) { - if (m_ScriptScope.IsInitialized()) + if ( !m_ScriptScope.IsInitialized() ) + { + Warning( "%s: No script scope, cannot filter\n", GetDebugName() ); + return false; + } + + if ( g_Hook_DamageMod.CanRunInScope( m_ScriptScope ) ) { 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_Hook_DamageMod.Call( m_ScriptScope, &functionReturn, args ); g_pScriptVM->RemoveInstance( pInfo ); return functionReturn.m_bool; } - Warning("%s: No script scope, cannot filter\n", GetDebugName()); - return false; + return BaseClass::DamageMod( pCaller, info ); } }; diff --git a/mp/src/game/server/fogvolume.cpp b/mp/src/game/server/fogvolume.cpp new file mode 100644 index 00000000..f2f9a718 --- /dev/null +++ b/mp/src/game/server/fogvolume.cpp @@ -0,0 +1,153 @@ +//-------------------------------------------------------------------------------------------------------- +// Copyright (c) 2007 Turtle Rock Studios, Inc. - All Rights Reserved + +#include "cbase.h" +#include "fogvolume.h" +#include "collisionutils.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +CUtlVector< CFogVolume * > TheFogVolumes; + +ConVar fog_volume_debug( "fog_volume_debug", "0", 0, "If enabled, prints diagnostic information about the current fog volume" ); + +//-------------------------------------------------------------------------------------------------------- +LINK_ENTITY_TO_CLASS(fog_volume, CFogVolume); + +BEGIN_DATADESC( CFogVolume ) + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_KEYFIELD( m_fogName, FIELD_STRING, "FogName" ), + DEFINE_KEYFIELD( m_postProcessName, FIELD_STRING, "PostProcessName" ), + DEFINE_KEYFIELD( m_colorCorrectionName, FIELD_STRING, "ColorCorrectionName" ), + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + DEFINE_FIELD( m_hFogController, FIELD_EHANDLE ), + DEFINE_FIELD( m_hPostProcessController, FIELD_EHANDLE ), + DEFINE_FIELD( m_hColorCorrectionController, FIELD_EHANDLE ), + +END_DATADESC() + + +//-------------------------------------------------------------------------------------------------------- +CFogVolume *CFogVolume::FindFogVolumeForPosition( const Vector &position ) +{ + CFogVolume *fogVolume = NULL; + for ( int i=0; iCollisionProp()->WorldToCollisionSpace( position, &vecRelativeCenter ); + if ( IsBoxIntersectingSphere( fogVolume->CollisionProp()->OBBMins(), fogVolume->CollisionProp()->OBBMaxs(), vecRelativeCenter, 1.0f ) ) + { + break; + } + fogVolume = NULL; + } + + // This doesn't work well if there are multiple players or multiple fog volume queries per frame; might want to relocate this if that's the case + if ( fog_volume_debug.GetBool() ) + { + if ( fogVolume ) + { + char fogVolumeName[256]; + fogVolume->GetKeyValue( "targetname", fogVolumeName, 256 ); + engine->Con_NPrintf( 0, "Fog Volume ""%s"" found at position (%f %f %f)", fogVolumeName, position.x, position.y, position.z ); + engine->Con_NPrintf( 1, "Fog: %s, post process: %s, color correct: %s", fogVolume->m_fogName, fogVolume->m_postProcessName, fogVolume->m_colorCorrectionName ); + } + else + { + engine->Con_NPrintf( 0, "No Fog Volume found at given position (%f %f %f)", position.x, position.y, position.z ); + } + } + + return fogVolume; +} + + +//-------------------------------------------------------------------------------------------------------- +CFogVolume::CFogVolume() : + BaseClass(), + m_bDisabled( false ), + m_bInFogVolumesList( false ) +{ +} + + +//-------------------------------------------------------------------------------------------------------- +CFogVolume::~CFogVolume() +{ + RemoveFromGlobalList(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFogVolume::Spawn( void ) +{ + BaseClass::Spawn(); + + SetSolid( SOLID_BSP ); + SetSolidFlags( FSOLID_NOT_SOLID ); + SetModel( STRING( GetModelName() ) ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFogVolume::AddToGlobalList() +{ + if ( !m_bInFogVolumesList ) + { + TheFogVolumes.AddToTail( this ); + m_bInFogVolumesList = true; + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CFogVolume::RemoveFromGlobalList() +{ + if ( m_bInFogVolumesList ) + { + TheFogVolumes.FindAndRemove( this ); + m_bInFogVolumesList = false; + } +} + + +//---------------------------------------------------------------------------- +void CFogVolume::InputEnable( inputdata_t &data ) +{ + m_bDisabled = false; + AddToGlobalList(); +} + + +//---------------------------------------------------------------------------- +void CFogVolume::InputDisable( inputdata_t &data ) +{ + m_bDisabled = true; + RemoveFromGlobalList(); +} + + +//---------------------------------------------------------------------------- +// Called when the level loads or is restored +//---------------------------------------------------------------------------- +void CFogVolume::Activate() +{ + BaseClass::Activate(); + + m_hFogController = dynamic_cast< CFogController* >( gEntList.FindEntityByName( NULL, m_fogName ) ); + m_hPostProcessController = dynamic_cast< CPostProcessController* >( gEntList.FindEntityByName( NULL, m_postProcessName ) ); + m_hColorCorrectionController = dynamic_cast< CColorCorrection* >( gEntList.FindEntityByName( NULL, m_colorCorrectionName ) ); + + if ( !m_bDisabled ) + { + AddToGlobalList(); + } +} diff --git a/mp/src/game/server/fogvolume.h b/mp/src/game/server/fogvolume.h new file mode 100644 index 00000000..6bd5880a --- /dev/null +++ b/mp/src/game/server/fogvolume.h @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------------------------------------------- +// Copyright (c) 2007 Turtle Rock Studios, Inc. - All Rights Reserved + +#ifndef FOG_VOLUME_H +#define FOG_VOLUME_H + +#ifdef _WIN32 +#pragma once +#endif + + +class CFogController; +class CPostProcessController; +class CColorCorrection; + + +//-------------------------------------------------------------------------------------------------------- +// Fog volume entity +class CFogVolume : public CServerOnlyEntity +{ + DECLARE_CLASS( CFogVolume, CServerOnlyEntity ); + DECLARE_DATADESC(); + +public: + CFogVolume(); + virtual ~CFogVolume(); + virtual void Spawn( void ); + virtual void Activate(); + + static CFogVolume *FindFogVolumeForPosition( const Vector &position ); + + const char *GetFogControllerName() const + { + return STRING( m_fogName ); + } + + CFogController* GetFogController( ) const + { + return m_hFogController.Get(); + } + + CPostProcessController* GetPostProcessController( ) const + { + return m_hPostProcessController.Get(); + } + + CColorCorrection* GetColorCorrectionController( ) const + { + return m_hColorCorrectionController.Get(); + } + + void InputEnable( inputdata_t &data ); + void InputDisable( inputdata_t &data ); + +private: + string_t m_fogName; + string_t m_postProcessName; + string_t m_colorCorrectionName; + + CHandle< CFogController > m_hFogController; + CHandle< CPostProcessController > m_hPostProcessController; + CHandle< CColorCorrection > m_hColorCorrectionController; + + bool m_bDisabled; + bool m_bInFogVolumesList; + + void AddToGlobalList(); + void RemoveFromGlobalList(); +}; + +extern CUtlVector< CFogVolume * > TheFogVolumes; + + +#endif // FOG_VOLUME_H \ No newline at end of file diff --git a/mp/src/game/server/func_break.cpp b/mp/src/game/server/func_break.cpp index 7fd23009..35aacee3 100644 --- a/mp/src/game/server/func_break.cpp +++ b/mp/src/game/server/func_break.cpp @@ -221,6 +221,10 @@ bool CBreakable::KeyValue( const char *szKeyName, const char *szValue ) if ( object > 0 && object < ARRAYSIZE(pSpawnObjects) ) m_iszSpawnObject = MAKE_STRING( pSpawnObjects[object] ); #ifdef MAPBASE + // "0" is the default value of a "choices" field in Hammer, representing nothing selected + // atoi() returning 0 may also indicate a failed conversion, so check szValue directly + else if ( FStrEq( szValue, "0" ) ) + m_iszSpawnObject = NULL_STRING; else m_iszSpawnObject = AllocPooledString(szValue); #endif diff --git a/mp/src/game/server/func_lod.cpp b/mp/src/game/server/func_lod.cpp index e1c41c12..7a71e270 100644 --- a/mp/src/game/server/func_lod.cpp +++ b/mp/src/game/server/func_lod.cpp @@ -112,6 +112,10 @@ bool CFunc_LOD::KeyValue( const char *szKeyName, const char *szValue ) { m_fDisappearMaxDist = (float)atof(szValue); } + else if (FStrEq(szKeyName, "DisappearMinDist")) // Forwards compatibility + { + m_fDisappearDist = (float)atof(szValue); + } #endif else if (FStrEq(szKeyName, "Solid")) { diff --git a/mp/src/game/server/gameinterface.cpp b/mp/src/game/server/gameinterface.cpp index 32a61fc9..fc658c55 100644 --- a/mp/src/game/server/gameinterface.cpp +++ b/mp/src/game/server/gameinterface.cpp @@ -3260,7 +3260,11 @@ float CServerGameClients::ProcessUsercmds( edict_t *player, bf_read *buf, int nu for ( i = totalcmds - 1; i >= 0; i-- ) { to = &cmds[ i ]; +#if defined( MAPBASE_VSCRIPT ) + ReadUsercmd( buf, to, from, pPlayer ); // Tell whose UserCmd it is +#else ReadUsercmd( buf, to, from ); +#endif from = to; } diff --git a/mp/src/game/server/genericactor.cpp b/mp/src/game/server/genericactor.cpp index a20cc7a7..4b7113cc 100644 --- a/mp/src/game/server/genericactor.cpp +++ b/mp/src/game/server/genericactor.cpp @@ -277,7 +277,8 @@ bool CGenericActorCustom::KeyValue( const char *szKeyName, const char *szValue ) //----------------------------------------------------------------------------- void CGenericActorCustom::SpeakIfAllowed( const char *concept, AI_CriteriaSet *modifiers ) { - Speak( concept, modifiers ? *modifiers : AI_CriteriaSet() ); + AI_CriteriaSet empty; + Speak( concept, modifiers ? *modifiers : empty ); } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/ai_behavior_actbusy.cpp b/mp/src/game/server/hl2/ai_behavior_actbusy.cpp index 9a4f09e6..c5ad733d 100644 --- a/mp/src/game/server/hl2/ai_behavior_actbusy.cpp +++ b/mp/src/game/server/hl2/ai_behavior_actbusy.cpp @@ -1755,7 +1755,11 @@ void CAI_ActBusyBehavior::PlaySoundForActBusy( busyanimparts_t AnimPart ) CAI_Expresser *pExpresser = GetOuter()->GetExpresser(); if ( pExpresser ) { +#ifdef NEW_RESPONSE_SYSTEM + CAI_Concept concept = STRING(pBusyAnim->iszSounds[AnimPart]); +#else const char *concept = STRING(pBusyAnim->iszSounds[AnimPart]); +#endif // Must be able to speak the concept if ( !pExpresser->IsSpeaking() && pExpresser->CanSpeakConcept( concept ) ) @@ -2738,8 +2742,15 @@ void CAI_ActBusyGoal::InputForceThisNPCToStopBusy( inputdata_t &inputdata ) if ( !pBehavior ) return; - // Just stop busying - pBehavior->StopBusying(); + if (!IsActive() && pBehavior->GetActBusyGoal() == this) + { + pBehavior->Disable(); + } + else + { + // Just stop busying + pBehavior->StopBusying(); + } } #endif diff --git a/mp/src/game/server/hl2/ai_behavior_actbusy.h b/mp/src/game/server/hl2/ai_behavior_actbusy.h index a33dd438..264fdb3d 100644 --- a/mp/src/game/server/hl2/ai_behavior_actbusy.h +++ b/mp/src/game/server/hl2/ai_behavior_actbusy.h @@ -153,6 +153,10 @@ public: bool IsInSafeZone( CBaseEntity *pEntity ); int CountEnemiesInSafeZone(); +#ifdef MAPBASE + CAI_ActBusyGoal *GetActBusyGoal() const { return m_hActBusyGoal; } +#endif + private: virtual int SelectSchedule( void ); int SelectScheduleForLeaving( void ); diff --git a/mp/src/game/server/hl2/ai_behavior_functank.cpp b/mp/src/game/server/hl2/ai_behavior_functank.cpp index f0a32601..89f79228 100644 --- a/mp/src/game/server/hl2/ai_behavior_functank.cpp +++ b/mp/src/game/server/hl2/ai_behavior_functank.cpp @@ -110,6 +110,28 @@ bool CAI_FuncTankBehavior::IsInterruptable( void ) return BaseClass::IsInterruptable(); } + +ConVar ai_tank_allow_expanded_npcs( "ai_tank_allow_expanded_npcs", "1", FCVAR_NONE, "Allows Father Grigori, Barney, and vortigaunts to automatically man func_tanks." ); + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_FuncTankBehavior::CanManTank( CFuncTank *pTank, bool bForced ) +{ + if (!bForced) + { + // In order to prevent potential problems in existing maps, Father Grigori, Barney, and vortigaunts can be set to not automatically man func_tanks by default. + if (ai_tank_allow_expanded_npcs.GetBool() == false) + { + const char *pszClass = GetOuter()->GetClassname(); + if ( FStrEq( pszClass, "npc_monk" ) || FStrEq( pszClass, "npc_barney" ) || FStrEq( pszClass, "npc_vortigaunt" ) ) + return false; + } + } + + return true; +} #endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/ai_behavior_functank.h b/mp/src/game/server/hl2/ai_behavior_functank.h index a9f19496..75d5b8df 100644 --- a/mp/src/game/server/hl2/ai_behavior_functank.h +++ b/mp/src/game/server/hl2/ai_behavior_functank.h @@ -51,6 +51,8 @@ public: void PrescheduleThink(); #ifdef MAPBASE bool IsInterruptable( void ); + + bool CanManTank( CFuncTank *pTank, bool bForced ); #endif Activity NPC_TranslateActivity( Activity activity ); diff --git a/mp/src/game/server/hl2/ai_behavior_police.cpp b/mp/src/game/server/hl2/ai_behavior_police.cpp index ffb8dd96..8dc5ec40 100644 --- a/mp/src/game/server/hl2/ai_behavior_police.cpp +++ b/mp/src/game/server/hl2/ai_behavior_police.cpp @@ -143,7 +143,12 @@ void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, SentencePri } else if ( GetOuter()->GetExpresser() ) { +#ifdef NEW_RESPONSE_SYSTEM + CAI_Concept concept = pSentence; + GetOuter()->GetExpresser()->Speak( concept ); +#else GetOuter()->GetExpresser()->Speak( pSentence ); +#endif } #endif } @@ -168,7 +173,12 @@ void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, const char } else if ( GetOuter()->GetExpresser() ) { +#ifdef NEW_RESPONSE_SYSTEM + CAI_Concept concept( pSentence ); + GetOuter()->GetExpresser()->Speak( concept, modifiers ); +#else GetOuter()->GetExpresser()->Speak( pSentence, modifiers ); +#endif } #endif } diff --git a/mp/src/game/server/hl2/basehlcombatweapon.cpp b/mp/src/game/server/hl2/basehlcombatweapon.cpp index 33a700fc..894c290b 100644 --- a/mp/src/game/server/hl2/basehlcombatweapon.cpp +++ b/mp/src/game/server/hl2/basehlcombatweapon.cpp @@ -394,6 +394,10 @@ void CHLSelectFireMachineGun::SecondaryAttack( void ) { m_iSecondaryAttacks++; gamestats->Event_WeaponFired( pOwner, false, GetClassname() ); + +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_ATTACK2 ); +#endif } } diff --git a/mp/src/game/server/hl2/cbasehelicopter.h b/mp/src/game/server/hl2/cbasehelicopter.h index fcf74291..714f62f4 100644 --- a/mp/src/game/server/hl2/cbasehelicopter.h +++ b/mp/src/game/server/hl2/cbasehelicopter.h @@ -267,6 +267,10 @@ private: typedef CHandle AvoidSphereHandle_t; float m_flRadius; +#ifdef MAPBASE + string_t m_iszAvoidFilter; + EHANDLE m_hAvoidFilter; +#endif static CUtlVector< AvoidSphereHandle_t > s_AvoidSpheres; }; diff --git a/mp/src/game/server/hl2/combine_mine.cpp b/mp/src/game/server/hl2/combine_mine.cpp index f813abc9..4d1c876a 100644 --- a/mp/src/game/server/hl2/combine_mine.cpp +++ b/mp/src/game/server/hl2/combine_mine.cpp @@ -58,6 +58,10 @@ char *pszMineStateNames[] = // Approximate radius of the bomb's model #define BOUNCEBOMB_RADIUS 24 +#ifdef MAPBASE +ConVar combine_mine_trace_dist( "combine_mine_trace_dist", "1024" ); +#endif + BEGIN_DATADESC( CBounceBomb ) DEFINE_THINKFUNC( ExplodeThink ), DEFINE_ENTITYFUNC( ExplodeTouch ), @@ -91,6 +95,8 @@ BEGIN_DATADESC( CBounceBomb ) DEFINE_KEYFIELD( m_iInitialState, FIELD_INTEGER, "InitialState" ), DEFINE_KEYFIELD( m_bCheapWarnSound, FIELD_BOOLEAN, "CheapWarnSound" ), DEFINE_KEYFIELD( m_iLOSMask, FIELD_INTEGER, "LOSMask" ), + DEFINE_INPUT( m_bUnavoidable, FIELD_BOOLEAN, "SetUnavoidable" ), + DEFINE_KEYFIELD( m_vecPlantOrientation, FIELD_VECTOR, "PlantOrientation" ), #endif DEFINE_KEYFIELD( m_iModification, FIELD_INTEGER, "Modification" ), @@ -128,6 +134,8 @@ BEGIN_DATADESC( CBounceBomb ) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "Bounce", InputBounce ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "BounceAtTarget", InputBounceAtTarget ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetPlantOrientation", InputSetPlantOrientation ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetPlantOrientationRaw", InputSetPlantOrientationRaw ), DEFINE_OUTPUT( m_OnTriggered, "OnTriggered" ), DEFINE_OUTPUT( m_OnExplode, "OnExplode" ), @@ -265,6 +273,14 @@ void CBounceBomb::Spawn() // pretend like the player set me down. m_bPlacedByPlayer = true; } + +#ifdef MAPBASE + if (m_vecPlantOrientation != vec3_invalid) + { + // Turn angles into direction + AngleVectors( QAngle( m_vecPlantOrientation.x, m_vecPlantOrientation.y, m_vecPlantOrientation.z ), &m_vecPlantOrientation ); + } +#endif } //--------------------------------------------------------- @@ -693,7 +709,20 @@ void CBounceBomb::SettleThink() { // If i'm not resting on the world, jump randomly. trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_SHOT|CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &tr ); +#ifdef MAPBASE + Vector vecTraceDir; + if (m_vecPlantOrientation != vec3_invalid) + { + vecTraceDir = m_vecPlantOrientation * combine_mine_trace_dist.GetFloat(); + } + else + { + vecTraceDir = Vector( 0, 0, combine_mine_trace_dist.GetFloat() ); + } +#else + Vector vecTraceDir = Vector( 0, 0, 1024 ); +#endif + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - vecTraceDir, MASK_SHOT|CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &tr ); bool bHop = false; if( tr.m_pEnt ) @@ -727,6 +756,20 @@ void CBounceBomb::SettleThink() // Check for upside-down Vector vecUp; GetVectors( NULL, NULL, &vecUp ); +#ifdef MAPBASE + if (m_vecPlantOrientation != vec3_invalid) + { + float flDiff = abs(m_vecPlantOrientation.z - vecUp.z); + if ( flDiff >= 0.2f ) + { + // Landed upside down. Right self + Vector vecForce( 0, 0, 2500 ); + Flip( vecForce, AngularImpulse( 60, 0, 0 ) ); + return; + } + } + else +#endif if( vecUp.z <= 0.8 ) { // Landed upside down. Right self @@ -1454,6 +1497,22 @@ void CBounceBomb::InputBounceAtTarget( inputdata_t &inputdata ) m_hNearestNPC = inputdata.value.Entity(); SetMineState(MINE_STATE_TRIGGERED); } + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CBounceBomb::InputSetPlantOrientation( inputdata_t &inputdata ) +{ + Vector vecInput; + inputdata.value.Vector3D( vecInput ); + AngleVectors( QAngle(vecInput.x, vecInput.y, vecInput.z), &m_vecPlantOrientation ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CBounceBomb::InputSetPlantOrientationRaw( inputdata_t &inputdata ) +{ + inputdata.value.Vector3D( m_vecPlantOrientation ); +} #endif //--------------------------------------------------------- @@ -1498,6 +1557,18 @@ CBasePlayer *CBounceBomb::HasPhysicsAttacker( float dt ) return NULL; } +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CBounceBomb::ShouldBeAvoidedByCompanions() +{ +#ifdef MAPBASE + if (m_bUnavoidable) + return false; +#endif + + return !IsPlayerPlaced() && IsAwake(); +} + //--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) diff --git a/mp/src/game/server/hl2/combine_mine.h b/mp/src/game/server/hl2/combine_mine.h index fdcd68da..097968db 100644 --- a/mp/src/game/server/hl2/combine_mine.h +++ b/mp/src/game/server/hl2/combine_mine.h @@ -34,7 +34,7 @@ class CBounceBomb : public CBaseAnimating, public CDefaultPlayerPickupVPhysics public: #ifdef MAPBASE - CBounceBomb() { m_pWarnSound = NULL; m_bPlacedByPlayer = false; m_flExplosionDelay = 0.5f; m_iLOSMask = MASK_SOLID_BRUSHONLY; } + CBounceBomb() { m_pWarnSound = NULL; m_bPlacedByPlayer = false; m_flExplosionDelay = 0.5f; m_iLOSMask = MASK_SOLID_BRUSHONLY; m_vecPlantOrientation = vec3_invalid; } #else CBounceBomb() { m_pWarnSound = NULL; m_bPlacedByPlayer = false; } #endif @@ -72,6 +72,9 @@ public: bool IsPlayerPlaced() { return m_bPlacedByPlayer; } + // Determines whether companions should treat the mine as a navigation obstacle and avoid it + bool ShouldBeAvoidedByCompanions(); + bool CreateVPhysics() { VPhysicsInitNormal( SOLID_VPHYSICS, 0, false ); @@ -125,6 +128,12 @@ private: // Allows control over the mask used in LOS int m_iLOSMask; + + bool m_bUnavoidable; + + // What direction the mine should be facing when planting itself (i.e. facing up, facing left, etc.) + // vec3_invalid = use default (0 0 1 or -90 0 0) + Vector m_vecPlantOrientation; #endif bool m_bPlacedByPlayer; @@ -159,6 +168,8 @@ private: #ifdef MAPBASE void InputBounce( inputdata_t &inputdata ); void InputBounceAtTarget( inputdata_t &inputdata ); + void InputSetPlantOrientation( inputdata_t &inputdata ); + void InputSetPlantOrientationRaw( inputdata_t &inputdata ); COutputEvent m_OnTriggered; COutputEvent m_OnExplode; #endif diff --git a/mp/src/game/server/hl2/env_speaker.cpp b/mp/src/game/server/hl2/env_speaker.cpp index 872dbcbc..5cdf7606 100644 --- a/mp/src/game/server/hl2/env_speaker.cpp +++ b/mp/src/game/server/hl2/env_speaker.cpp @@ -240,6 +240,65 @@ void CSpeaker::DispatchResponse( const char *conceptName ) PrecacheScriptSound( response ); } +#ifdef NEW_RESPONSE_SYSTEM + switch (result.GetType()) + { + case ResponseRules::RESPONSE_SPEAK: + { + pTarget->EmitSound( response ); + } + break; + case ResponseRules::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 ResponseRules::RESPONSE_SCENE: + { + CBaseFlex *pFlex = NULL; + if (pTarget != this) + { + // Attempt to get flex on the target + pFlex = dynamic_cast(pTarget); + } + InstancedScriptedScene(pFlex, response); + } + break; + case ResponseRules::RESPONSE_PRINT: + { + + } + break; + case ResponseRules::RESPONSE_ENTITYIO: + { + CAI_Expresser::FireEntIOFromResponse( const_cast(response), pTarget ); + break; + } +#ifdef MAPBASE_VSCRIPT + case ResponseRules::RESPONSE_VSCRIPT: + { + CAI_Expresser::RunScriptResponse( pTarget, response, &set, false ); + break; + } + case ResponseRules::RESPONSE_VSCRIPT_FILE: + { + CAI_Expresser::RunScriptResponse( pTarget, response, &set, true ); + break; + } +#endif + default: + break; + } +#else switch ( result.GetType() ) { case RESPONSE_SPEAK: @@ -280,6 +339,7 @@ void CSpeaker::DispatchResponse( const char *conceptName ) default: break; } +#endif // AllocPooledString? m_OnSpeak.Set(MAKE_STRING(response), pTarget, this); diff --git a/mp/src/game/server/hl2/func_tank.cpp b/mp/src/game/server/hl2/func_tank.cpp index 963a0e54..c4462575 100644 --- a/mp/src/game/server/hl2/func_tank.cpp +++ b/mp/src/game/server/hl2/func_tank.cpp @@ -402,7 +402,11 @@ void CFuncTank::InputFindNPCToManTank( inputdata_t &inputdata ) { // Verify the npc has the func_tank controller behavior. CAI_FuncTankBehavior *pBehavior; +#ifdef MAPBASE + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) ) +#else if ( pNPC->GetBehavior( &pBehavior ) ) +#endif { m_hController = pNPC; pBehavior->SetFuncTank( this ); @@ -439,7 +443,7 @@ void CFuncTank::InputTeleportNPCToManTank( inputdata_t &inputdata ) { // Verify the npc has the func_tank controller behavior. CAI_FuncTankBehavior *pBehavior; - if ( pNPC->GetBehavior( &pBehavior ) ) + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) ) { Vector vecVec; QAngle angAng; @@ -512,7 +516,7 @@ void CFuncTank::InputForceNPCToManTank( inputdata_t &inputdata ) { // Verify the npc has the func_tank controller behavior. CAI_FuncTankBehavior *pBehavior; - if ( pNPC->GetBehavior( &pBehavior ) ) + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) ) { // Set the forced condition pBehavior->SetCondition( CAI_FuncTankBehavior::COND_FUNCTANK_FORCED ); @@ -627,7 +631,11 @@ void CFuncTank::NPC_FindController( void ) continue; CAI_FuncTankBehavior *pBehavior; +#ifdef MAPBASE + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, false ) ) +#else if ( pNPC->GetBehavior( &pBehavior ) ) +#endif { // Don't mount the func_tank if your "enemy" is within X feet or it or the npc. CBaseEntity *pEnemy = pNPC->GetEnemy(); diff --git a/mp/src/game/server/hl2/hl2_client.cpp b/mp/src/game/server/hl2/hl2_client.cpp index 4cd9dd7b..12dc248f 100644 --- a/mp/src/game/server/hl2/hl2_client.cpp +++ b/mp/src/game/server/hl2/hl2_client.cpp @@ -142,7 +142,7 @@ void respawn( CBaseEntity *pEdict, bool fCopyCorpse ) { // In SP respawns, only create corpse if drawing externally CBasePlayer *pPlayer = (CBasePlayer*)pEdict; - if ( fCopyCorpse && pPlayer->m_bDrawPlayerModelExternally ) + if ( fCopyCorpse && pPlayer->GetDrawPlayerModelExternally() ) { // make a copy of the dead body for appearances sake pPlayer->CreateCorpse(); diff --git a/mp/src/game/server/hl2/hl2_player.cpp b/mp/src/game/server/hl2/hl2_player.cpp index f249c3e6..1cd610bb 100644 --- a/mp/src/game/server/hl2/hl2_player.cpp +++ b/mp/src/game/server/hl2/hl2_player.cpp @@ -118,6 +118,11 @@ ConVar sv_stickysprint("sv_stickysprint", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBO #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." ); + +#ifdef SP_ANIM_STATE +ConVar hl2_use_sp_animstate( "hl2_use_sp_animstate", "1", FCVAR_NONE, "Allows SP HL2 players to use HL2:DM animations for custom player models. (changes may not apply until model is reloaded)" ); +#endif + #endif #define FLASH_DRAIN_TIME 1.1111 // 100 units / 90 secs @@ -259,6 +264,7 @@ public: void InputSetHandModel( inputdata_t &inputdata ); void InputSetHandModelSkin( inputdata_t &inputdata ); + void InputSetHandModelBodyGroup( inputdata_t &inputdata ); void InputSetPlayerModel( inputdata_t &inputdata ); void InputSetPlayerDrawExternally( inputdata_t &inputdata ); @@ -278,6 +284,7 @@ public: CUtlDict m_QueuedKV; int m_MaxArmor = 100; + int m_SuitZoomFOV = 25; #endif bool PassesDamageFilter( const CTakeDamageInfo &info ); @@ -324,39 +331,41 @@ public: } // Will the command point change? - // True = Judged guilty and changed. - // False = Judged not guilty and unchanged. - bool GetVerdict(Vector *defendant, CHL2_Player *pPlayer) + // True = Command point changes + // False = Comand point doesn't change + bool TestRedirect(Vector *vecNewCommandPoint, CHL2_Player *pPlayer) { - // Deliver goal to relevant destinations before sentencing. - m_OnCommandGoal.Set(*defendant, pPlayer, this); + // Output the goal before doing anything else. + m_OnCommandGoal.Set(*vecNewCommandPoint, pPlayer, this); if (m_target == NULL_STRING) { - // Abort sentencing. + // Not targeting anything. Don't redirect and just leave it at the output return false; } else if (FStrEq(STRING(m_target), "-1")) { - // Deliver verdict immediately. - *defendant = Vector(0, 0, 0); - return false; + // Completely cancel the squad command. + *vecNewCommandPoint = vec3_origin; + return true; } 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(); + // Use the entity's absolute origin as the new command point. + *vecNewCommandPoint = pEntOfInterest->GetAbsOrigin(); return true; } + else + { + Warning("%s couldn't find target entity \"%s\"\n", GetDebugName(), STRING(m_target)); + } } - // No sentence. return false; } @@ -614,6 +623,10 @@ BEGIN_ENT_SCRIPTDESC( CHL2_Player, CBasePlayer, "The HL2 player entity." ) DEFINE_SCRIPTFUNC( RemoveCustomSuitDevice, "Removes a custom suit device ID. (1-3)" ) DEFINE_SCRIPTFUNC( IsCustomSuitDeviceActive, "Checks if a custom suit device is active." ) +#ifdef SP_ANIM_STATE + DEFINE_SCRIPTFUNC( AddAnimStateLayer, "Adds a custom sequence index as a misc. layer for the singleplayer anim state, wtih parameters for blending in/out, setting the playback rate, holding the animation at the end, and only playing when the player is still." ) +#endif + END_SCRIPTDESC(); #endif @@ -659,6 +672,9 @@ CSuitPowerDevice SuitDeviceCustom[] = IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player) SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable), SendPropBool( SENDINFO(m_fIsSprinting) ), +#ifdef SP_ANIM_STATE + SendPropFloat( SENDINFO(m_flAnimRenderYaw), 0, SPROP_NOSCALE ), +#endif END_SEND_TABLE() @@ -1142,6 +1158,16 @@ void CHL2_Player::PostThink( void ) { HandleAdmireGlovesAnimation(); } + +#ifdef SP_ANIM_STATE + if (m_pPlayerAnimState) + { + QAngle angEyeAngles = EyeAngles(); + m_pPlayerAnimState->Update( angEyeAngles.y, angEyeAngles.x ); + + m_flAnimRenderYaw.Set( m_pPlayerAnimState->GetRenderAngles().y ); + } +#endif } void CHL2_Player::StartAdmireGlovesAnimation( void ) @@ -1237,6 +1263,10 @@ Class_T CHL2_Player::Classify ( void ) if(IsInAVehicle()) { IServerVehicle *pVehicle = GetVehicle(); +#ifdef MAPBASE + if (!pVehicle) + return CLASS_PLAYER; +#endif return pVehicle->ClassifyPassenger( this, CLASS_PLAYER ); } else @@ -1351,193 +1381,109 @@ void CHL2_Player::SpawnedAtPoint( CBaseEntity *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)" ); +ConVar player_use_anim_enabled( "player_use_anim_enabled", "1" ); +ConVar player_use_anim_heavy_mass( "player_use_anim_heavy_mass", "20.0" ); -void CHL2_Player::ResetAnimation( void ) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CHL2_Player::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) { - if (!hl2_use_hl2dm_anims.GetBool()) - return; - - if (IsAlive()) + Activity weaponTranslation = BaseClass::Weapon_TranslateActivity( baseAct, pRequired ); + +#if EXPANDED_HL2DM_ACTIVITIES + // +USE activities + // HACKHACK: Make sure m_hUseEntity is a pickup controller first + if ( m_hUseEntity && m_hUseEntity->ClassMatches("player_pickup") && player_use_anim_enabled.GetBool()) { - 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 ); + CBaseEntity* pHeldEnt = GetPlayerHeldEntity( this ); + float flMass = pHeldEnt ? + (pHeldEnt->VPhysicsGetObject() ? PlayerPickupGetHeldObjectMass( m_hUseEntity, pHeldEnt->VPhysicsGetObject() ) : player_use_anim_heavy_mass.GetFloat()) : + (m_hUseEntity->VPhysicsGetObject() ? m_hUseEntity->GetMass() : player_use_anim_heavy_mass.GetFloat()); + if ( flMass >= player_use_anim_heavy_mass.GetFloat() ) + { + // Heavy versions + switch (baseAct) + { + case ACT_HL2MP_IDLE: weaponTranslation = ACT_HL2MP_IDLE_USE_HEAVY; break; + case ACT_HL2MP_RUN: weaponTranslation = ACT_HL2MP_RUN_USE_HEAVY; break; + case ACT_HL2MP_WALK: weaponTranslation = ACT_HL2MP_WALK_USE_HEAVY; break; + case ACT_HL2MP_IDLE_CROUCH: weaponTranslation = ACT_HL2MP_IDLE_CROUCH_USE_HEAVY; break; + case ACT_HL2MP_WALK_CROUCH: weaponTranslation = ACT_HL2MP_WALK_CROUCH_USE_HEAVY; break; + case ACT_HL2MP_JUMP: weaponTranslation = ACT_HL2MP_JUMP_USE_HEAVY; break; + } + } + else + { + switch (baseAct) + { + case ACT_HL2MP_IDLE: weaponTranslation = ACT_HL2MP_IDLE_USE; break; + case ACT_HL2MP_RUN: weaponTranslation = ACT_HL2MP_RUN_USE; break; + case ACT_HL2MP_WALK: weaponTranslation = ACT_HL2MP_WALK_USE; break; + case ACT_HL2MP_IDLE_CROUCH: weaponTranslation = ACT_HL2MP_IDLE_CROUCH_USE; break; + case ACT_HL2MP_WALK_CROUCH: weaponTranslation = ACT_HL2MP_WALK_CROUCH_USE; break; + case ACT_HL2MP_JUMP: weaponTranslation = ACT_HL2MP_JUMP_USE; break; + } + } } +#endif + + return weaponTranslation; } +#ifdef SP_ANIM_STATE // Set the activity based on an event or current state void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim ) { - if (!hl2_use_hl2dm_anims.GetBool()) + if (!m_pPlayerAnimState) { 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 ); + m_pPlayerAnimState->SetPlayerAnimation( playerAnim ); +} +void CHL2_Player::AddAnimStateLayer( int iSequence, float flBlendIn, float flBlendOut, float flPlaybackRate, bool bHoldAtEnd, bool bOnlyWhenStill ) +{ + if (!m_pPlayerAnimState) return; - } - else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD ) + + m_pPlayerAnimState->AddMiscSequence( iSequence, flBlendIn, flBlendOut, flPlaybackRate, bHoldAtEnd, bOnlyWhenStill ); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: model-change notification. Fires on dynamic load completion as well +//----------------------------------------------------------------------------- +CStudioHdr *CHL2_Player::OnNewModel() +{ + CStudioHdr *hdr = BaseClass::OnNewModel(); + +#ifdef SP_ANIM_STATE + // Clears the animation state if we already have one. + if ( m_pPlayerAnimState != NULL ) { - RestartGesture( Weapon_TranslateActivity( idealActivity ) ); - return; + m_pPlayerAnimState->Release(); + m_pPlayerAnimState = NULL; + } + + if ( hdr && hdr->HaveSequenceForActivity(ACT_HL2MP_IDLE) && hl2_use_sp_animstate.GetBool() ) + { + // Here we create and init the player animation state. + m_pPlayerAnimState = CreatePlayerAnimationState(this); } 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; + m_flAnimRenderYaw = FLT_MAX; } +#endif - // 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 ); + return hdr; } + +extern char g_szDefaultPlayerModel[MAX_PATH]; +extern bool g_bDefaultPlayerDrawExternally; #endif //----------------------------------------------------------------------------- @@ -1548,8 +1494,13 @@ void CHL2_Player::Spawn(void) #ifndef HL2MP #ifndef PORTAL +#ifdef MAPBASE + if ( GetModelName() == NULL_STRING ) + SetModel( g_szDefaultPlayerModel ); +#else SetModel( "models/player.mdl" ); #endif +#endif #endif BaseClass::Spawn(); @@ -1563,6 +1514,8 @@ void CHL2_Player::Spawn(void) RemoveEffects( EF_NODRAW ); } + + SetDrawPlayerModelExternally( g_bDefaultPlayerDrawExternally ); #endif // @@ -1760,7 +1713,11 @@ void CHL2_Player::ToggleZoom(void) //----------------------------------------------------------------------------- void CHL2_Player::StartZooming( void ) { +#ifdef MAPBASE + int iFOV = GetPlayerProxy() ? GetPlayerProxy()->m_SuitZoomFOV : 25; +#else int iFOV = 25; +#endif if ( SetFOV( this, iFOV, 0.4f ) ) { m_HL2Local.m_bZooming = true; @@ -1829,6 +1786,14 @@ void CHL2_Player::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecA CHL2_Player::~CHL2_Player( void ) { +#ifdef SP_ANIM_STATE + // Clears the animation state. + if ( m_pPlayerAnimState != NULL ) + { + m_pPlayerAnimState->Release(); + m_pPlayerAnimState = NULL; + } +#endif } //----------------------------------------------------------------------------- @@ -1868,25 +1833,22 @@ bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal ) if (!pCommandRedirect || pCommandRedirect->IsDisabled() || !pCommandRedirect->PointIsWithin(tr.endpos)) continue; - // First, GIVE IT OUR ALLIES so it could fire outputs + // First, give it our allies so it could fire outputs pCommandRedirect->HandleAllies(m_pPlayerAISquad, this); Vector vec = tr.endpos; - if (pCommandRedirect->GetVerdict(&vec, this)) + if (pCommandRedirect->TestRedirect(&vec, this)) { - // It doesn't want us moving, so just don't find a goal at all + // If it returned a 0 vector, cancel the command if (vec.IsZero()) { return false; } - // Just set our goal to this, the mapper didn't sign up for these checks + // Just set our goal to this and skip the code below which checks the target position's validity pGoal->m_vecGoalLocation = vec; return true; } - - // Only one should be necessary - break; } } //else @@ -4031,7 +3993,10 @@ Vector CHL2_Player::EyeDirection2D( void ) Vector CHL2_Player::EyeDirection3D( void ) { Vector vecForward; - +#ifdef MAPBASE + EyeVectors( &vecForward ); + return vecForward; +#else // Return the vehicle angles if we request them if ( GetVehicle() != NULL ) { @@ -4042,6 +4007,7 @@ Vector CHL2_Player::EyeDirection3D( void ) AngleVectors( EyeAngles(), &vecForward ); return vecForward; +#endif } @@ -4612,9 +4578,11 @@ BEGIN_DATADESC( CLogicPlayerProxy ) DEFINE_INPUTFUNC( FIELD_STRING, "GetAmmoOnWeapon", InputGetAmmoOnWeapon ), DEFINE_INPUTFUNC( FIELD_STRING, "SetHandModel", InputSetHandModel ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelSkin", InputSetHandModelSkin ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelBodyGroup", InputSetHandModelBodyGroup ), DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerModel", InputSetPlayerModel ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetPlayerDrawExternally", InputSetPlayerDrawExternally ), DEFINE_INPUT( m_MaxArmor, FIELD_INTEGER, "SetMaxInputArmor" ), + DEFINE_INPUT( m_SuitZoomFOV, FIELD_INTEGER, "SetSuitZoomFOV" ), #endif DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), END_DATADESC() @@ -4653,6 +4621,8 @@ bool CLogicPlayerProxy::KeyValue( const char *szKeyName, const char *szValue ) vm->SetModel(szValue); else if (FStrEq(szKeyName, "Skin")) // HandsVMSkin vm->m_nSkin = atoi(szValue); + else if (FStrEq(szKeyName, "Body")) // HandsVMBody + vm->m_nBody = atoi(szValue); } return true; } @@ -4728,7 +4698,8 @@ bool CLogicPlayerProxy::AcceptInput( const char *szInputName, CBaseEntity *pActi { DevMsg("logic_playerproxy: Player not found!\n"); - g_EventQueue.AddEvent("!player", szInputName, Value, 0.01f, pActivator, pCaller); + // Need to allocate the string here in case szInputName is freed before the input fires + g_EventQueue.AddEvent("!player", STRING( AllocPooledString(szInputName) ), Value, 0.01f, pActivator, pCaller); } } @@ -5055,6 +5026,17 @@ void CLogicPlayerProxy::InputSetHandModelSkin( inputdata_t &inputdata ) vm->m_nSkin = inputdata.value.Int(); } +void CLogicPlayerProxy::InputSetHandModelBodyGroup( inputdata_t &inputdata ) +{ + if (!m_hPlayer) + return; + + CBasePlayer *pPlayer = static_cast( m_hPlayer.Get() ); + CBaseViewModel *vm = pPlayer->GetViewModel(1); + if (vm) + vm->m_nBody = inputdata.value.Int(); +} + void CLogicPlayerProxy::InputSetPlayerModel( inputdata_t &inputdata ) { if (!m_hPlayer) @@ -5082,6 +5064,6 @@ void CLogicPlayerProxy::InputSetPlayerDrawExternally( inputdata_t &inputdata ) return; CBasePlayer *pPlayer = static_cast(m_hPlayer.Get()); - pPlayer->m_bDrawPlayerModelExternally = inputdata.value.Bool(); + pPlayer->SetDrawPlayerModelExternally( 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 84ae23b9..12c0386c 100644 --- a/mp/src/game/server/hl2/hl2_player.h +++ b/mp/src/game/server/hl2/hl2_player.h @@ -18,6 +18,8 @@ // In HL2MP we need to inherit from BaseMultiplayerPlayer! #if defined ( HL2MP ) #include "basemultiplayerplayer.h" +#elif defined ( MAPBASE ) +#include "mapbase/singleplayer_animstate.h" #endif class CAI_Squad; @@ -130,9 +132,16 @@ public: // For the logic_playerproxy output void SpawnedAtPoint( CBaseEntity *pSpawnPoint ); - void ResetAnimation( void ); + Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); + +#ifdef SP_ANIM_STATE void SetAnimation( PLAYER_ANIM playerAnim ); + void AddAnimStateLayer( int iSequence, float flBlendIn = 0.0f, float flBlendOut = 0.0f, float flPlaybackRate = 1.0f, bool bHoldAtEnd = false, bool bOnlyWhenStill = false ); +#endif + + virtual CStudioHdr* OnNewModel(); + virtual const char *GetOverrideStepSound( const char *pszBaseStepSoundName ); #endif @@ -303,7 +312,7 @@ public: virtual bool IsHoldingEntity( CBaseEntity *pEnt ); virtual void ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldindThis ); virtual float GetHeldObjectMass( IPhysicsObject *pHeldObject ); - virtual CBaseEntity *CHL2_Player::GetHeldObject( void ); + virtual CBaseEntity *GetHeldObject( void ); virtual bool IsFollowingPhysics( void ) { return (m_afPhysicsFlags & PFLAG_ONBARNACLE) > 0; } void InputForceDropPhysObjects( inputdata_t &data ); @@ -432,6 +441,14 @@ private: float m_flTimeNextLadderHint; // Next time we're eligible to display a HUD hint about a ladder. friend class CHL2GameMovement; + +#ifdef SP_ANIM_STATE + CSinglePlayerAnimState* m_pPlayerAnimState; + + // At the moment, we network the render angles since almost none of the player anim stuff is done on the client in SP. + // If any of this is ever adapted for MP, this method should be replaced with replicating/moving the anim state to the client. + CNetworkVar( float, m_flAnimRenderYaw ); +#endif }; diff --git a/mp/src/game/server/hl2/item_battery.cpp b/mp/src/game/server/hl2/item_battery.cpp index 7e299fc5..d5c8b416 100644 --- a/mp/src/game/server/hl2/item_battery.cpp +++ b/mp/src/game/server/hl2/item_battery.cpp @@ -23,12 +23,12 @@ public: void Spawn( void ) { Precache( ); - SetModel( "models/items/battery.mdl" ); + SetModel( DefaultOrCustomModel( "models/items/battery.mdl" ) ); BaseClass::Spawn( ); } void Precache( void ) { - PrecacheModel ("models/items/battery.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/items/battery.mdl" ) ); PrecacheScriptSound( "ItemBattery.Touch" ); @@ -36,10 +36,30 @@ public: bool MyTouch( CBasePlayer *pPlayer ) { CHL2_Player *pHL2Player = dynamic_cast( pPlayer ); +#ifdef MAPBASE + return ( pHL2Player && pHL2Player->ApplyBattery( m_flPowerMultiplier ) ); +#else return ( pHL2Player && pHL2Player->ApplyBattery() ); +#endif } + +#ifdef MAPBASE + void InputSetPowerMultiplier( inputdata_t &inputdata ) { m_flPowerMultiplier = inputdata.value.Float(); } + float m_flPowerMultiplier = 1.0f; + + DECLARE_DATADESC(); +#endif }; LINK_ENTITY_TO_CLASS(item_battery, CItemBattery); PRECACHE_REGISTER(item_battery); +#ifdef MAPBASE +BEGIN_DATADESC( CItemBattery ) + + DEFINE_KEYFIELD( m_flPowerMultiplier, FIELD_FLOAT, "PowerMultiplier" ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPowerMultiplier", InputSetPowerMultiplier ), + +END_DATADESC() +#endif + diff --git a/mp/src/game/server/hl2/item_healthkit.cpp b/mp/src/game/server/hl2/item_healthkit.cpp index 628f873e..54f961c7 100644 --- a/mp/src/game/server/hl2/item_healthkit.cpp +++ b/mp/src/game/server/hl2/item_healthkit.cpp @@ -30,11 +30,29 @@ public: void Spawn( void ); void Precache( void ); bool MyTouch( CBasePlayer *pPlayer ); + +#ifdef MAPBASE + float GetItemAmount() { return sk_healthkit.GetFloat() * m_flHealthMultiplier; } + + void InputSetHealthMultiplier( inputdata_t &inputdata ) { m_flHealthMultiplier = inputdata.value.Float(); } + float m_flHealthMultiplier = 1.0f; + + DECLARE_DATADESC(); +#endif }; LINK_ENTITY_TO_CLASS( item_healthkit, CHealthKit ); PRECACHE_REGISTER(item_healthkit); +#ifdef MAPBASE +BEGIN_DATADESC( CHealthKit ) + + DEFINE_KEYFIELD( m_flHealthMultiplier, FIELD_FLOAT, "HealthMultiplier" ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthMultiplier", InputSetHealthMultiplier ), + +END_DATADESC() +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -66,7 +84,11 @@ void CHealthKit::Precache( void ) //----------------------------------------------------------------------------- bool CHealthKit::MyTouch( CBasePlayer *pPlayer ) { +#ifdef MAPBASE + if ( pPlayer->TakeHealth( GetItemAmount(), DMG_GENERIC ) ) +#else if ( pPlayer->TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) ) +#endif { CSingleUserRecipientFilter user( pPlayer ); user.MakeReliable(); @@ -119,7 +141,11 @@ public: bool MyTouch( CBasePlayer *pPlayer ) { +#ifdef MAPBASE + if ( pPlayer->TakeHealth( GetItemAmount(), DMG_GENERIC ) ) +#else if ( pPlayer->TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) ) +#endif { CSingleUserRecipientFilter user( pPlayer ); user.MakeReliable(); @@ -145,11 +171,132 @@ public: return false; } + +#ifdef MAPBASE + float GetItemAmount() { return sk_healthvial.GetFloat() * m_flHealthMultiplier; } + + void InputSetHealthMultiplier( inputdata_t &inputdata ) { m_flHealthMultiplier = inputdata.value.Float(); } + float m_flHealthMultiplier = 1.0f; + + DECLARE_DATADESC(); +#endif }; LINK_ENTITY_TO_CLASS( item_healthvial, CHealthVial ); PRECACHE_REGISTER( item_healthvial ); +#ifdef MAPBASE +BEGIN_DATADESC( CHealthVial ) + + DEFINE_KEYFIELD( m_flHealthMultiplier, FIELD_FLOAT, "HealthMultiplier" ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthMultiplier", InputSetHealthMultiplier ), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Small health kit. Heals the player when picked up. +//----------------------------------------------------------------------------- +class CHealthKitCustom : public CItem +{ +public: + DECLARE_CLASS( CHealthKitCustom, CItem ); + CHealthKitCustom(); + + void Spawn( void ); + void Precache( void ); + bool MyTouch( CBasePlayer *pPlayer ); + + float GetItemAmount() { return m_flHealthAmount; } + + void InputSetHealthAmount( inputdata_t &inputdata ) { m_flHealthAmount = inputdata.value.Float(); } + + float m_flHealthAmount; + string_t m_iszTouchSound; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( item_healthkit_custom, CHealthKitCustom ); +//PRECACHE_REGISTER(item_healthkit_custom); + +#ifdef MAPBASE +BEGIN_DATADESC( CHealthKitCustom ) + + DEFINE_KEYFIELD( m_flHealthAmount, FIELD_FLOAT, "HealthAmount" ), + DEFINE_KEYFIELD( m_iszTouchSound, FIELD_STRING, "TouchSound" ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthAmount", InputSetHealthAmount ), + +END_DATADESC() +#endif + + +CHealthKitCustom::CHealthKitCustom() +{ + SetModelName( AllocPooledString( "models/items/healthkit.mdl" ) ); + m_flHealthAmount = sk_healthkit.GetFloat(); + m_iszTouchSound = AllocPooledString( "HealthKit.Touch" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHealthKitCustom::Spawn( void ) +{ + Precache(); + SetModel( STRING( GetModelName() ) ); + + BaseClass::Spawn(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHealthKitCustom::Precache( void ) +{ + PrecacheModel( STRING( GetModelName() ) ); + + PrecacheScriptSound( STRING( m_iszTouchSound ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// Output : +//----------------------------------------------------------------------------- +bool CHealthKitCustom::MyTouch( CBasePlayer *pPlayer ) +{ + if ( pPlayer->TakeHealth( GetItemAmount(), DMG_GENERIC ) ) + { + CSingleUserRecipientFilter user( pPlayer ); + user.MakeReliable(); + + UserMessageBegin( user, "ItemPickup" ); + WRITE_STRING( GetClassname() ); + MessageEnd(); + + CPASAttenuationFilter filter( pPlayer, STRING( m_iszTouchSound ) ); + EmitSound( filter, pPlayer->entindex(), STRING( m_iszTouchSound ) ); + + if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES ) + { + Respawn(); + } + else + { + UTIL_Remove(this); + } + + return true; + } + + return false; +} +#endif + //----------------------------------------------------------------------------- // Wall mounted health kit. Heals the player when used. //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/npc_BaseZombie.cpp b/mp/src/game/server/hl2/npc_BaseZombie.cpp index bbe1f9d0..fee51dd7 100644 --- a/mp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/mp/src/game/server/hl2/npc_BaseZombie.cpp @@ -1210,6 +1210,10 @@ void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info ) if( pAnimating ) { pAnimating->SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless ); +#ifdef MAPBASE + // Inherit some animating properties + pAnimating->m_nSkin = m_nSkin; +#endif } #ifdef MAPBASE @@ -2350,11 +2354,14 @@ void CNPC_BaseZombie::BecomeTorso( const Vector &vecTorsoForce, const Vector &ve 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) + if (pGib && pGib->VPhysicsGetObject()) { - pGib->SUB_StartFadeOut( flFadeTime, false ); + pGib->VPhysicsGetObject()->AddVelocity( &vecLegsForce, NULL ); + + if (flFadeTime > 0.0) + { + pGib->SUB_StartFadeOut( flFadeTime, false ); + } } } else @@ -2502,11 +2509,14 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve 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 ); + if (pGib && pGib->VPhysicsGetObject()) + { + pGib->VPhysicsGetObject()->AddVelocity(&vecVelocity, NULL); + if (ShouldIgniteZombieGib()) + static_cast(pGib)->Ignite( random->RandomFloat( 8.0, 12.0 ), false ); - pGib->SUB_StartFadeOut( 15, false ); + pGib->SUB_StartFadeOut( 15, false ); + } } else pGib = CreateRagGib( GetHeadcrabModel(), vecOrigin, GetLocalAngles(), vecVelocity, 15, ShouldIgniteZombieGib() ); diff --git a/mp/src/game/server/hl2/npc_alyx.h b/mp/src/game/server/hl2/npc_alyx.h index 551460b2..266dca6d 100644 --- a/mp/src/game/server/hl2/npc_alyx.h +++ b/mp/src/game/server/hl2/npc_alyx.h @@ -36,6 +36,9 @@ public: // 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; } + + // Use Alyx's default subtitle color (255,212,255) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 212; params.b1 = 255; return BaseClass::GetGameTextSpeechParams( params ); } #endif EHANDLE m_hEmpTool; diff --git a/mp/src/game/server/hl2/npc_alyx_episodic.cpp b/mp/src/game/server/hl2/npc_alyx_episodic.cpp index 19616e10..ee8b197c 100644 --- a/mp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/mp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -156,6 +156,8 @@ 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" ); + +ConVar npc_alyx_allow_fly( "npc_alyx_allow_fly", "0", FCVAR_NONE, "Allows Alyx to use FL_FLY outside of scripted sequences, actbusy, or navigation." ); #endif // global pointer to Alyx for fast lookups @@ -319,7 +321,9 @@ CNPC_Alyx *CNPC_Alyx::GetAlyx( void ) //========================================================= bool CNPC_Alyx::CreateBehaviors() { +#ifndef MAPBASE // Moved to CNPC_PlayerCompanion AddBehavior( &m_FuncTankBehavior ); +#endif bool result = BaseClass::CreateBehaviors(); return result; @@ -885,7 +889,11 @@ void CNPC_Alyx::GatherConditions() // ROBIN: This was here to solve a problem in a playtest. We've since found what we think was the cause. // It's a useful piece of debug to have lying there, so I've left it in. - if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled() ) + if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled() +#ifdef MAPBASE + && GetNavType() != NAV_CLIMB && !npc_alyx_allow_fly.GetBool() +#endif + ) { Warning( "Removed FL_FLY from Alyx, who wasn't running a script or actbusy. Time %.2f, map %s.\n", gpGlobals->curtime, STRING(gpGlobals->mapname) ); RemoveFlag( FL_FLY ); @@ -1069,6 +1077,13 @@ void CNPC_Alyx::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo & pMemory->timeFirstSeen = gpGlobals->curtime - 10.0f; } } + +#ifdef MAPBASE + // This call has a side effect of causing Alyx to speak a regular companion TLK_ENEMY_DEAD, which may conflict with the TLK_ALYX_ENEMY_DEAD + // further up, but this is fine because concepts are protected against interrupting each other and Alyx may even be overridden + // to use TLK_ENEMY_DEAD instead, which is used by other NPCs and appends more modifiers. + BaseClass::Event_KilledOther( pVictim, info ); +#endif } //----------------------------------------------------------------------------- @@ -1680,9 +1695,88 @@ Activity CNPC_Alyx::NPC_TranslateActivity( Activity activity ) case ACT_DROP_WEAPON: if ( HasShotgun() ) return (Activity)ACT_DROP_WEAPON_SHOTGUN; } +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Alyx has her own pistol readiness animations which use the default activities + switch (activity) + { + case ACT_IDLE_PISTOL_RELAXED: + return ACT_IDLE_RELAXED; + case ACT_IDLE_PISTOL_STIMULATED: + return ACT_IDLE_STIMULATED; + case ACT_WALK_PISTOL_RELAXED: + return ACT_WALK; + case ACT_WALK_PISTOL_STIMULATED: + return ACT_WALK_PISTOL; + case ACT_RUN_PISTOL_RELAXED: + return ACT_RUN; + case ACT_RUN_PISTOL_STIMULATED: + return ACT_RUN_PISTOL; + } +#endif + return activity; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CNPC_Alyx::Weapon_TranslateActivity( Activity activity, bool *pRequired ) +{ + activity = BaseClass::Weapon_TranslateActivity( activity, pRequired ); + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Alyx has her own pistol readiness animations which use the default activities + switch (activity) + { + case ACT_IDLE_PISTOL_RELAXED: + return ACT_IDLE_RELAXED; + case ACT_IDLE_PISTOL_STIMULATED: + return ACT_IDLE_STIMULATED; + case ACT_WALK_PISTOL_RELAXED: + return ACT_WALK; + case ACT_WALK_PISTOL_STIMULATED: + return ACT_WALK_PISTOL; + case ACT_RUN_PISTOL_RELAXED: + return ACT_RUN; + case ACT_RUN_PISTOL_STIMULATED: + return ACT_RUN_PISTOL; + } +#endif + + return activity; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CNPC_Alyx::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon ) +{ + activity = BaseClass::Weapon_BackupActivity( activity, weaponTranslationWasRequired, pSpecificWeapon ); + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Alyx has her own pistol readiness animations which use the default activities + switch (activity) + { + case ACT_IDLE_PISTOL_RELAXED: + return ACT_IDLE_RELAXED; + case ACT_IDLE_PISTOL_STIMULATED: + return ACT_IDLE_STIMULATED; + case ACT_WALK_PISTOL_RELAXED: + return ACT_WALK; + case ACT_WALK_PISTOL_STIMULATED: + return ACT_WALK_PISTOL; + case ACT_RUN_PISTOL_RELAXED: + return ACT_RUN; + case ACT_RUN_PISTOL_STIMULATED: + return ACT_RUN_PISTOL; + } +#endif + + return activity; +} +#endif + bool CNPC_Alyx::ShouldDeferToFollowBehavior() { return BaseClass::ShouldDeferToFollowBehavior(); diff --git a/mp/src/game/server/hl2/npc_alyx_episodic.h b/mp/src/game/server/hl2/npc_alyx_episodic.h index 9e72fd8d..f24ddf67 100644 --- a/mp/src/game/server/hl2/npc_alyx_episodic.h +++ b/mp/src/game/server/hl2/npc_alyx_episodic.h @@ -57,6 +57,9 @@ public: #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(); } + + // Use Alyx's default subtitle color (255,212,255) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 212; params.b1 = 255; return BaseClass::GetGameTextSpeechParams( params ); } #endif virtual float GetJumpGravity() const { return 1.8f; } @@ -88,6 +91,10 @@ public: bool CanSeeEntityInDarkness( CBaseEntity *pEntity ); bool IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition ); Activity NPC_TranslateActivity ( Activity activity ); +#ifdef MAPBASE + Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); + Activity Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired = false, CBaseCombatWeapon *pSpecificWeapon = NULL ); +#endif bool ShouldDeferToFollowBehavior(); void BuildScheduleTestBits(); bool ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior ); @@ -228,7 +235,9 @@ private: bool m_bShouldHaveEMP; +#ifndef MAPBASE // Moved to CNPC_PlayerCompanion CAI_FuncTankBehavior m_FuncTankBehavior; +#endif COutputEvent m_OnFinishInteractWithObject; COutputEvent m_OnPlayerUse; diff --git a/mp/src/game/server/hl2/npc_antlion.cpp b/mp/src/game/server/hl2/npc_antlion.cpp index d94c658d..2d59c9a0 100644 --- a/mp/src/game/server/hl2/npc_antlion.cpp +++ b/mp/src/game/server/hl2/npc_antlion.cpp @@ -65,6 +65,9 @@ ConVar sk_antlion_worker_burst_radius( "sk_antlion_worker_burst_radius", "160", ConVar g_test_new_antlion_jump( "g_test_new_antlion_jump", "1", FCVAR_ARCHIVE ); ConVar antlion_easycrush( "antlion_easycrush", "1" ); +#ifdef MAPBASE +ConVar antlion_no_ignite_die( "antlion_no_ignite_die", "0" ); +#endif ConVar g_antlion_cascade_push( "g_antlion_cascade_push", "1", FCVAR_ARCHIVE ); ConVar g_debug_antlion_worker( "g_debug_antlion_worker", "0" ); @@ -277,17 +280,17 @@ void CNPC_Antlion::Spawn( void ) #ifdef HL2_EPISODIC if ( IsWorker() ) { - SetModel( ANTLION_WORKER_MODEL ); + SetModel( DefaultOrCustomModel(ANTLION_WORKER_MODEL) ); AddSpawnFlags( SF_NPC_LONG_RANGE ); SetBloodColor( BLOOD_COLOR_ANTLION_WORKER ); } else { - SetModel( ANTLION_MODEL ); + SetModel( DefaultOrCustomModel(ANTLION_MODEL) ); SetBloodColor( BLOOD_COLOR_ANTLION ); } #else - SetModel( ANTLION_MODEL ); + SetModel( DefaultOrCustomModel(ANTLION_MODEL) ); SetBloodColor( BLOOD_COLOR_YELLOW ); #endif // HL2_EPISODIC @@ -2623,6 +2626,15 @@ int CNPC_Antlion::SelectSchedule( void ) void CNPC_Antlion::Ignite ( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner ) { #ifdef HL2_EPISODIC + +#ifdef MAPBASE + if (antlion_no_ignite_die.GetBool()) + { + BaseClass::Ignite(flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner); + return; + } +#endif + float flDamage = m_iHealth + 1; CTakeDamageInfo dmgInfo( this, this, flDamage, DMG_GENERIC ); diff --git a/mp/src/game/server/hl2/npc_antlionguard.cpp b/mp/src/game/server/hl2/npc_antlionguard.cpp index efc5883d..57592617 100644 --- a/mp/src/game/server/hl2/npc_antlionguard.cpp +++ b/mp/src/game/server/hl2/npc_antlionguard.cpp @@ -678,7 +678,7 @@ void CNPC_AntlionGuard::UpdateOnRemove( void ) //----------------------------------------------------------------------------- void CNPC_AntlionGuard::Precache( void ) { - PrecacheModel( ANTLIONGUARD_MODEL ); + PrecacheModel( DefaultOrCustomModel( ANTLIONGUARD_MODEL ) ); PrecacheScriptSound( "NPC_AntlionGuard.Shove" ); PrecacheScriptSound( "NPC_AntlionGuard.HitHard" ); @@ -779,7 +779,7 @@ void CNPC_AntlionGuard::Spawn( void ) { Precache(); - SetModel( ANTLIONGUARD_MODEL ); + SetModel( DefaultOrCustomModel( ANTLIONGUARD_MODEL ) ); // Switch our skin (for now), if we're the cavern guard if ( m_bCavernBreed ) diff --git a/mp/src/game/server/hl2/npc_attackchopper.cpp b/mp/src/game/server/hl2/npc_attackchopper.cpp index ddb9752a..ac6efb1c 100644 --- a/mp/src/game/server/hl2/npc_attackchopper.cpp +++ b/mp/src/game/server/hl2/npc_attackchopper.cpp @@ -42,6 +42,10 @@ #include "physics_bone_follower.h" #endif // HL2_EPISODIC +#ifdef MAPBASE +#include "filters.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -5680,6 +5684,9 @@ LINK_ENTITY_TO_CLASS( npc_heli_avoidsphere, CAvoidSphere ); BEGIN_DATADESC( CAvoidSphere ) DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszAvoidFilter, FIELD_STRING, "AvoidFilter" ), +#endif END_DATADESC() @@ -5720,6 +5727,18 @@ void CAvoidSphere::Activate( ) { BaseClass::Activate(); s_AvoidSpheres.AddToTail( this ); + +#ifdef MAPBASE + m_hAvoidFilter = gEntList.FindEntityByName( NULL, m_iszAvoidFilter, this ); + if (m_hAvoidFilter) + { + if (dynamic_cast(m_hAvoidFilter.Get()) == NULL) + { + Warning( "%s: \"%s\" is not a valid filter", GetDebugName(), m_hAvoidFilter->GetDebugName() ); + m_hAvoidFilter = NULL; + } + } +#endif } void CAvoidSphere::UpdateOnRemove( ) @@ -5746,6 +5765,12 @@ void CAvoidSphere::ComputeAvoidanceForces( CBaseEntity *pEntity, float flEntityR CAvoidSphere *pSphere = s_AvoidSpheres[i].Get(); const Vector &vecAvoidCenter = pSphere->WorldSpaceCenter(); +#ifdef MAPBASE + // Continue if not passing the avoid sphere filter + if ( pSphere->m_hAvoidFilter && !(static_cast( pSphere->m_hAvoidFilter.Get())->PassesFilter(pSphere, pEntity )) ) + continue; +#endif + // NOTE: This test can be thought of sweeping a sphere through space // and seeing if it intersects the avoidance sphere float flTotalRadius = flEntityRadius + pSphere->m_flRadius; diff --git a/mp/src/game/server/hl2/npc_barnacle.cpp b/mp/src/game/server/hl2/npc_barnacle.cpp index 68b7064c..d26f702a 100644 --- a/mp/src/game/server/hl2/npc_barnacle.cpp +++ b/mp/src/game/server/hl2/npc_barnacle.cpp @@ -266,7 +266,7 @@ void CNPC_Barnacle::Spawn() { Precache( ); - SetModel( "models/barnacle.mdl" ); + SetModel( DefaultOrCustomModel( "models/barnacle.mdl" ) ); UTIL_SetSize( this, Vector(-16, -16, -40), Vector(16, 16, 0) ); SetSolid( SOLID_BBOX ); @@ -689,6 +689,12 @@ bool CNPC_Barnacle::CanPickup( CBaseCombatCharacter *pBCC ) if( FClassnameIs( pBCC, "npc_turret_floor" ) ) return false; +#ifdef MAPBASE + // Don't pickup rollermines + if( FClassnameIs( pBCC, "npc_rollermine" ) ) + return false; +#endif + // Don't pick up a dead player or NPC if( !pBCC->IsAlive() ) return false; @@ -2375,7 +2381,7 @@ const impactdamagetable_t &CNPC_Barnacle::GetPhysicsImpactDamageTable( void ) //========================================================= void CNPC_Barnacle::Precache() { - PrecacheModel("models/barnacle.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/barnacle.mdl" ) ); // Precache all gibs for ( int i=0; i < ARRAYSIZE(m_szGibNames); i++ ) diff --git a/mp/src/game/server/hl2/npc_barney.cpp b/mp/src/game/server/hl2/npc_barney.cpp index 02afce18..cfb80afc 100644 --- a/mp/src/game/server/hl2/npc_barney.cpp +++ b/mp/src/game/server/hl2/npc_barney.cpp @@ -51,8 +51,10 @@ public: virtual void Precache() { +#ifndef MAPBASE // This is now done in CNPC_PlayerCompanion::Precache() // Prevents a warning SelectModel( ); +#endif BaseClass::Precache(); PrecacheScriptSound( "NPC_Barney.FootstepLeft" ); @@ -81,6 +83,11 @@ public: void GatherConditions(); void UseFunc( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +#ifdef MAPBASE + // Use Barney's default subtitle color (215,255,255) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 215; params.g1 = 255; params.b1 = 255; return BaseClass::GetGameTextSpeechParams( params ); } +#endif + CAI_FuncTankBehavior m_FuncTankBehavior; COutputEvent m_OnPlayerUse; diff --git a/mp/src/game/server/hl2/npc_basescanner.cpp b/mp/src/game/server/hl2/npc_basescanner.cpp index a94c77b6..fdae50da 100644 --- a/mp/src/game/server/hl2/npc_basescanner.cpp +++ b/mp/src/game/server/hl2/npc_basescanner.cpp @@ -1278,6 +1278,14 @@ void CNPC_BaseScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarg myZAccel = flDist / flInterval; } +#ifdef MAPBASE + if (m_flSpeedModifier != 1.0f) + { + myAccel *= m_flSpeedModifier; + //myZAccel *= m_flSpeedModifier; + } +#endif + MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay ); // calc relative banking targets diff --git a/mp/src/game/server/hl2/npc_breen.cpp b/mp/src/game/server/hl2/npc_breen.cpp index e12bab0c..e1409c80 100644 --- a/mp/src/game/server/hl2/npc_breen.cpp +++ b/mp/src/game/server/hl2/npc_breen.cpp @@ -34,6 +34,11 @@ public: void HandleAnimEvent( animevent_t *pEvent ); int GetSoundInterests ( void ); bool UseSemaphore( void ); + +#ifdef MAPBASE + // Use Breen's default subtitle color (188,188,188) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 188; params.g1 = 188; params.b1 = 188; return BaseClass::GetGameTextSpeechParams( params ); } +#endif }; LINK_ENTITY_TO_CLASS( npc_breen, CNPC_Breen ); diff --git a/mp/src/game/server/hl2/npc_citizen17.cpp b/mp/src/game/server/hl2/npc_citizen17.cpp index 7b6d6ba8..7455be5a 100644 --- a/mp/src/game/server/hl2/npc_citizen17.cpp +++ b/mp/src/game/server/hl2/npc_citizen17.cpp @@ -407,6 +407,27 @@ BEGIN_DATADESC( CNPC_Citizen ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t CNPC_Citizen::g_Hook_SelectModel; + +BEGIN_ENT_SCRIPTDESC( CNPC_Citizen, CAI_BaseActor, "npc_citizen from Half-Life 2" ) + + DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this citizen is a medic." ) + DEFINE_SCRIPTFUNC( IsAmmoResupplier, "Returns true if this citizen is an ammo resupplier." ) + DEFINE_SCRIPTFUNC( CanHeal, "Returns true if this citizen is a medic or ammo resupplier currently able to heal/give ammo." ) + + DEFINE_SCRIPTFUNC( GetCitizenType, "Gets the citizen's type. 1 = Downtrodden, 2 = Refugee, 3 = Rebel, 4 = Unique" ) + DEFINE_SCRIPTFUNC( SetCitizenType, "Sets the citizen's type. 1 = Downtrodden, 2 = Refugee, 3 = Rebel, 4 = Unique" ) + + BEGIN_SCRIPTHOOK( CNPC_Citizen::g_Hook_SelectModel, "SelectModel", FIELD_CSTRING, "Called when a citizen is selecting a random model. 'model_path' is the directory of the selected model and 'model_head' is the name. The 'gender' parameter uses the 'GENDER_' constants and is based only on the citizen's random head spawnflags. If a full model path string is returned, it will be used as the model instead." ) + DEFINE_SCRIPTHOOK_PARAM( "model_path", FIELD_CSTRING ) + DEFINE_SCRIPTHOOK_PARAM( "model_head", FIELD_CSTRING ) + DEFINE_SCRIPTHOOK_PARAM( "gender", FIELD_INTEGER ) + END_SCRIPTHOOK() + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -418,10 +439,11 @@ CSimpleSimTimer CNPC_Citizen::gm_PlayerSquadEvaluateTimer; bool CNPC_Citizen::CreateBehaviors() { BaseClass::CreateBehaviors(); - AddBehavior( &m_FuncTankBehavior ); #ifdef MAPBASE AddBehavior( &m_RappelBehavior ); AddBehavior( &m_PolicingBehavior ); +#else // Moved to CNPC_PlayerCompanion + AddBehavior( &m_FuncTankBehavior ); #endif return true; @@ -431,7 +453,12 @@ bool CNPC_Citizen::CreateBehaviors() //----------------------------------------------------------------------------- void CNPC_Citizen::Precache() { +#ifdef MAPBASE + // CNPC_PlayerCompanion::Precache() is responsible for calling this now + BaseClass::Precache(); +#else SelectModel(); +#endif SelectExpressionType(); if ( !npc_citizen_dont_precache_all.GetBool() ) @@ -468,7 +495,9 @@ void CNPC_Citizen::Precache() } } +#ifndef MAPBASE // See above BaseClass::Precache(); +#endif } //----------------------------------------------------------------------------- @@ -769,6 +798,54 @@ void CNPC_Citizen::SelectModel() pszModelName = g_ppszRandomHeads[m_iHead]; SetModelName(NULL_STRING); } + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_SelectModel.CanRunInScope( m_ScriptScope )) + { + gender_t scriptGender; + switch (gender) + { + case 'm': + scriptGender = GENDER_MALE; + break; + case 'f': + scriptGender = GENDER_FEMALE; + break; + default: + scriptGender = GENDER_NONE; + break; + } + + const char *pszModelPath = CFmtStr( "models/Humans/%s/", (const char *)(CFmtStr( g_ppszModelLocs[m_Type], (IsMedic()) ? "m" : "" )) ); + + // model_path, model_head, gender + ScriptVariant_t args[] = { pszModelPath, pszModelName, (int)scriptGender }; + ScriptVariant_t returnValue = NULL; + g_Hook_SelectModel.Call( m_ScriptScope, &returnValue, args ); + + if (returnValue.m_type == FIELD_CSTRING && returnValue.m_pszString[0] != '\0') + { + // Refresh the head if it's different + const char *pszNewHead = strrchr( returnValue.m_pszString, '/' ); + if ( pszNewHead && Q_stricmp(pszNewHead+1, pszModelName) != 0 ) + { + pszNewHead++; + for ( int i = 0; i < ARRAYSIZE(g_ppszRandomHeads); i++ ) + { + if ( Q_stricmp( g_ppszRandomHeads[i], pszModelName ) == 0 ) + { + m_iHead = i; + break; + } + } + } + + // Just set the model right here + SetModelName( AllocPooledString( returnValue.m_pszString ) ); + return; + } + } +#endif } Assert( pszModelName || GetModelName() != NULL_STRING ); @@ -1466,7 +1543,11 @@ int CNPC_Citizen::SelectScheduleRetrieveItem() // Been kicked out of the player squad since the time I located the health. ClearCondition( COND_HEALTH_ITEM_AVAILABLE ); } +#ifdef MAPBASE + else if ( m_FollowBehavior.GetFollowTarget() ) +#else else +#endif { CBaseEntity *pBase = FindHealthItem(m_FollowBehavior.GetFollowTarget()->GetAbsOrigin(), Vector( 120, 120, 120 ) ); CItem *pItem = dynamic_cast(pBase); @@ -1719,10 +1800,16 @@ void CNPC_Citizen::StartTask( const Task_t *pTask ) break; } +#ifdef MAPBASE + SetSpeechTarget( GetTarget() ); +#endif Speak( TLK_HEAL ); } else if ( IsAmmoResupplier() ) { +#ifdef MAPBASE + SetSpeechTarget( GetTarget() ); +#endif Speak( TLK_GIVEAMMO ); } SetIdealActivity( (Activity)ACT_CIT_HEAL ); @@ -1932,12 +2019,7 @@ 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; @@ -2014,6 +2096,13 @@ Activity CNPC_Citizen::NPC_TranslateActivity( Activity activity ) return ACT_RUN_AIM_AR2_STIMULATED; if (activity == ACT_WALK_AIM_AR2) return ACT_WALK_AIM_AR2_STIMULATED; + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + if (activity == ACT_RUN_AIM_PISTOL) + return ACT_RUN_AIM_PISTOL_STIMULATED; + if (activity == ACT_WALK_AIM_PISTOL) + return ACT_WALK_AIM_PISTOL_STIMULATED; +#endif } #endif @@ -2278,25 +2367,21 @@ 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. + // The gunship RPG code does not appear to be funcitonal, so only set the laser position. if ( GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_RPG) && GetEnemy() ) { CWeaponRPG *pRPG = static_cast(GetActiveWeapon()); - if ( EntIsClass( GetEnemy(), gm_isz_class_Gunship ) ) + pRPG->SetNPCLaserPosition( vecTarget ); + } #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 ); @@ -2331,11 +2416,10 @@ Vector CNPC_Citizen::GetActualShootPosition( const Vector &shootOrigin ) { pRPG->SetNPCLaserPosition( vecTarget ); } - } +#endif return vecTarget; -#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/npc_citizen17.h b/mp/src/game/server/hl2/npc_citizen17.h index 35f39a05..5d23429d 100644 --- a/mp/src/game/server/hl2/npc_citizen17.h +++ b/mp/src/game/server/hl2/npc_citizen17.h @@ -277,6 +277,11 @@ public: virtual void OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior ); +#ifdef MAPBASE + int GetCitizenType() { return (int)m_Type; } + void SetCitizenType( int iType ) { m_Type = (CitizenType_t)iType; } +#endif + private: //----------------------------------------------------- // Conditions, Schedules, Tasks @@ -365,7 +370,6 @@ private: #endif //----------------------------------------------------- - CAI_FuncTankBehavior m_FuncTankBehavior; #ifdef MAPBASE CAI_RappelBehavior m_RappelBehavior; CAI_PolicingBehavior m_PolicingBehavior; @@ -373,6 +377,8 @@ private: // Rappel virtual bool IsWaitingToRappel( void ) { return m_RappelBehavior.IsWaitingToRappel(); } void BeginRappel() { m_RappelBehavior.BeginRappel(); } +#else // Moved to CNPC_PlayerCompanion + CAI_FuncTankBehavior m_FuncTankBehavior; #endif CHandle m_hSavedFollowGoalEnt; @@ -382,6 +388,10 @@ private: //----------------------------------------------------- +#ifdef MAPBASE_VSCRIPT + static ScriptHook_t g_Hook_SelectModel; + DECLARE_ENT_SCRIPTDESC(); +#endif DECLARE_DATADESC(); #ifdef _XBOX protected: diff --git a/mp/src/game/server/hl2/npc_combine.cpp b/mp/src/game/server/hl2/npc_combine.cpp index 7599a6ac..73bac242 100644 --- a/mp/src/game/server/hl2/npc_combine.cpp +++ b/mp/src/game/server/hl2/npc_combine.cpp @@ -43,6 +43,7 @@ int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared wh #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_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use \"protected run\" animations." ); 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." ); @@ -116,19 +117,20 @@ int COMBINE_AE_ALTFIRE; //Activity ACT_COMBINE_WALKING_AR2; //Activity ACT_COMBINE_STANDING_SHOTGUN; //Activity ACT_COMBINE_CROUCHING_SHOTGUN; -#ifndef SHARED_COMBINE_ACTIVITIES +#if !SHARED_COMBINE_ACTIVITIES Activity ACT_COMBINE_THROW_GRENADE; #endif Activity ACT_COMBINE_LAUNCH_GRENADE; Activity ACT_COMBINE_BUGBAIT; -#ifndef SHARED_COMBINE_ACTIVITIES +#if !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; +Activity ACT_TURRET_CARRY_IDLE; +Activity ACT_TURRET_CARRY_WALK; +Activity ACT_TURRET_CARRY_RUN; #endif // ----------------------------------------------- @@ -230,7 +232,9 @@ DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ), DEFINE_AIGRENADE_DATADESC() #endif +#ifndef MAPBASE DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), +#endif DEFINE_FIELD( m_fIsElite, FIELD_BOOLEAN ), #ifndef MAPBASE DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ), @@ -542,7 +546,12 @@ void CNPC_Combine::GatherConditions() if( GetState() == NPC_STATE_COMBAT ) { +#ifdef MAPBASE + // Don't override the standoff + if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) && !m_StandoffBehavior.IsActive() ) +#else if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) ) +#endif { // Soldiers that are standing around doing nothing poll for attack slots so // that they can respond quickly when one comes available. If they can @@ -746,7 +755,8 @@ Class_T CNPC_Combine::Classify ( void ) //----------------------------------------------------------------------------- bool CNPC_Combine::IsAltFireCapable( void ) { - return IsElite() || m_bAlternateCapable; + // The base class tells us if we're carrying an alt-fire-able weapon. + return (IsElite() || m_bAlternateCapable) && BaseClass::IsAltFireCapable(); } //----------------------------------------------------------------------------- @@ -1538,11 +1548,6 @@ void CNPC_Combine::BuildScheduleTestBits( void ) //----------------------------------------------------------------------------- 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); } @@ -1551,11 +1556,12 @@ Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pR //----------------------------------------------------------------------------- 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; + // Some models might not contain ACT_COMBINE_BUGBAIT, which the soldier model uses instead of ACT_IDLE_ON_FIRE. + // Contrariwise, soldiers may be called to use ACT_IDLE_ON_FIRE in other parts of the AI and need to translate to ACT_COMBINE_BUGBAIT. + if (eNewActivity == ACT_COMBINE_BUGBAIT) + return ACT_IDLE_ON_FIRE; + else if (eNewActivity == ACT_IDLE_ON_FIRE) + return ACT_COMBINE_BUGBAIT; return BaseClass::NPC_BackupActivity( eNewActivity ); } @@ -1588,7 +1594,7 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) else #endif { -#ifdef SHARED_COMBINE_ACTIVITIES +#if SHARED_COMBINE_ACTIVITIES return ACT_COMBINE_THROW_GRENADE; #else return ( Activity )ACT_COMBINE_THROW_GRENADE; @@ -1621,16 +1627,32 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) } } #ifdef MAPBASE - else if (!GetActiveWeapon() && npc_combine_unarmed_anims.GetBool() && HaveSequenceForActivity(ACT_IDLE_UNARMED)) + else if (!GetActiveWeapon() && !npc_combine_unarmed_anims.GetBool()) { if (eNewActivity == ACT_IDLE || eNewActivity == ACT_IDLE_ANGRY) - eNewActivity = ACT_IDLE_UNARMED; + eNewActivity = ACT_IDLE_SMG1; else if (eNewActivity == ACT_WALK) - eNewActivity = ACT_WALK_UNARMED; + eNewActivity = ACT_WALK_RIFLE; + else if (eNewActivity == ACT_RUN) + eNewActivity = ACT_RUN_RIFLE; } - else if (eNewActivity == ACT_WALK && m_NPCState == NPC_STATE_IDLE && npc_combine_idle_walk_easy.GetBool() && HaveSequenceForActivity(ACT_WALK_EASY)) + else if (m_NPCState == NPC_STATE_IDLE && eNewActivity == ACT_WALK) { - eNewActivity = ACT_WALK_EASY; + if (npc_combine_idle_walk_easy.GetBool()) + { + // ACT_WALK_EASY has been replaced with ACT_WALK_RELAXED for weapon translation purposes + eNewActivity = ACT_WALK_RELAXED; + } + else if (GetActiveWeapon()) + { + eNewActivity = ACT_WALK_RIFLE; + } + } + + if ( eNewActivity == ACT_RUN && ( IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND ) || IsCurSchedule( SCHED_FLEE_FROM_BEST_SOUND ) ) ) + { + if ( random->RandomInt( 0, 1 ) && npc_combine_protected_run.GetBool() && HaveSequenceForActivity( ACT_RUN_PROTECTED ) ) + eNewActivity = ACT_RUN_PROTECTED; } #endif @@ -2015,7 +2037,7 @@ 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. + // This was switched to IsAltFireCapable() before, but m_bAlternateCapable makes it necessary to use IsElite() again. #endif if ( IsElite() ) { @@ -2597,7 +2619,6 @@ int CNPC_Combine::TranslateSchedule( int scheduleType ) #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 @@ -2698,7 +2719,7 @@ void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent ) else if ( pEvent->event == COMBINE_AE_ALTFIRE ) { #ifdef MAPBASE - if ( IsAltFireCapable() ) + if ( IsAltFireCapable() && GetActiveWeapon() ) #else if ( IsElite() ) #endif @@ -3004,7 +3025,11 @@ bool CNPC_Combine::SpeakIfAllowed( const char *concept, const char *modifiers, S AI_CriteriaSet set; if (modifiers) { +#ifdef NEW_RESPONSE_SYSTEM + GatherCriteria( &set, concept, modifiers ); +#else GetExpresser()->MergeModifiers(set, modifiers); +#endif } return SpeakIfAllowed( concept, set, sentencepriority, sentencecriteria ); } @@ -3676,7 +3701,9 @@ void CNPC_Combine::SetActivity( Activity NewActivity ) { BaseClass::SetActivity( NewActivity ); +#ifndef MAPBASE // CAI_GrenadeUser m_iLastAnimEventHandled = -1; +#endif } //----------------------------------------------------------------------------- @@ -3776,6 +3803,13 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea { return WEAPON_PROFICIENCY_GOOD; } +#ifdef MAPBASE + else if ( pWeapon->ClassMatches( gm_isz_class_Pistol ) ) + { + // Mods which need a lower soldier pistol accuracy can either change this value or use proficiency override in Hammer. + return WEAPON_PROFICIENCY_VERY_GOOD; + } +#endif return BaseClass::CalcWeaponProficiency( pWeapon ); } @@ -3891,7 +3925,12 @@ bool CNPC_Combine::IsRunningApproachEnemySchedule() bool CNPC_Combine::ShouldPickADeathPose( void ) { +#ifdef MAPBASE + // Check base class as well + return !IsCrouching() && BaseClass::ShouldPickADeathPose(); +#else return !IsCrouching(); +#endif } //----------------------------------------------------------------------------- @@ -3914,19 +3953,20 @@ DECLARE_TASK( TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS ) DECLARE_TASK( TASK_COMBINE_SET_STANDING ) //Activities -#ifndef SHARED_COMBINE_ACTIVITIES +#if !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 +#if !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 ) +DECLARE_ACTIVITY( ACT_TURRET_CARRY_IDLE ) +DECLARE_ACTIVITY( ACT_TURRET_CARRY_WALK ) +DECLARE_ACTIVITY( ACT_TURRET_CARRY_RUN ) #endif DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE ) diff --git a/mp/src/game/server/hl2/npc_combine.h b/mp/src/game/server/hl2/npc_combine.h index ec541cca..c9af8010 100644 --- a/mp/src/game/server/hl2/npc_combine.h +++ b/mp/src/game/server/hl2/npc_combine.h @@ -59,7 +59,7 @@ public: // Create components virtual bool CreateComponents(); -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser bool CanThrowGrenade( const Vector &vecTarget ); bool CheckCanThrowGrenade( const Vector &vecTarget ); #endif @@ -118,7 +118,7 @@ public: const char* GetGrenadeAttachment() { return "lefthand"; } #else #endif -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser void DelayAltFireAttack( float flDelay ); void DelaySquadAltFireAttack( float flDelay ); #endif @@ -131,7 +131,7 @@ public: Vector EyeOffset( Activity nActivity ); Vector EyePosition( void ); Vector BodyTarget( const Vector &posSrc, bool bNoisy = true ); -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser Vector GetAltFireTarget(); #endif @@ -320,7 +320,7 @@ private: private: int m_nKickDamage; -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser Vector m_vecTossVelocity; EHANDLE m_hForcedGrenadeTarget; #else @@ -334,12 +334,12 @@ private: // Time Variables float m_flNextPainSoundTime; float m_flNextAlertSoundTime; -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser float m_flNextGrenadeCheck; #endif float m_flNextLostSoundTime; float m_flAlertPatrolTime; // When to stop doing alert patrol -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser float m_flNextAltFireTime; // Elites only. Next time to begin considering alt-fire attack. #endif @@ -351,7 +351,7 @@ private: CAI_Sentence< CNPC_Combine > m_Sentences; #endif -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser int m_iNumGrenades; #endif CAI_AssaultBehavior m_AssaultBehavior; @@ -365,9 +365,11 @@ private: #endif public: +#ifndef MAPBASE // CAI_GrenadeUser int m_iLastAnimEventHandled; +#endif bool m_fIsElite; -#ifndef MAPBASE +#ifndef MAPBASE // CAI_GrenadeUser Vector m_vecAltFireTarget; #endif diff --git a/mp/src/game/server/hl2/npc_combinedropship.cpp b/mp/src/game/server/hl2/npc_combinedropship.cpp index fa40fd76..bc6067f2 100644 --- a/mp/src/game/server/hl2/npc_combinedropship.cpp +++ b/mp/src/game/server/hl2/npc_combinedropship.cpp @@ -689,9 +689,15 @@ int CCombineDropshipContainer::OnTakeDamage( const CTakeDamageInfo &info ) if ( m_iHealth <= 0 ) { - m_iHealth = 0; - Event_Killed( dmgInfo ); - return 0; +#ifdef MAPBASE_VSCRIPT + // False = Cheat death + if (ScriptDeathHook( const_cast(&info) ) != false) +#endif + { + m_iHealth = 0; + Event_Killed( dmgInfo ); + return 0; + } } // Spawn damage effects @@ -993,7 +999,11 @@ void CNPC_CombineDropship::Spawn( void ) IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject(); if ( pPhysicsObject ) { +#ifdef MAPBASE + pPhysicsObject->SetShadow( 1e4, 1e4, true, true ); // (allowing physics movement and rotation) +#else pPhysicsObject->SetShadow( 1e4, 1e4, false, false ); +#endif } m_hContainer->SetParent(this, 0); diff --git a/mp/src/game/server/hl2/npc_eli.cpp b/mp/src/game/server/hl2/npc_eli.cpp index 4b42c6c0..c66e9273 100644 --- a/mp/src/game/server/hl2/npc_eli.cpp +++ b/mp/src/game/server/hl2/npc_eli.cpp @@ -37,6 +37,11 @@ public: int GetSoundInterests( void ); void SetupWithoutParent( void ); void PrescheduleThink( void ); + +#ifdef MAPBASE + // Use Eli's default subtitle color (255,208,172) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 208; params.b1 = 172; return BaseClass::GetGameTextSpeechParams( params ); } +#endif }; LINK_ENTITY_TO_CLASS( npc_eli, CNPC_Eli ); diff --git a/mp/src/game/server/hl2/npc_enemyfinder.cpp b/mp/src/game/server/hl2/npc_enemyfinder.cpp index f8fc21bd..d9855815 100644 --- a/mp/src/game/server/hl2/npc_enemyfinder.cpp +++ b/mp/src/game/server/hl2/npc_enemyfinder.cpp @@ -59,6 +59,10 @@ public: void InputTurnOff( inputdata_t &inputdata ); virtual void Wake( bool bFireOutput = true ); +#ifdef MAPBASE + // A version of Wake() that takes an activator + virtual void Wake(CBaseEntity* pActivator); +#endif private: int m_nStartOn; @@ -234,6 +238,16 @@ void CNPC_EnemyFinder::Wake( bool bFireOutput ) AddEffects( EF_NODRAW ); } +#ifdef MAPBASE +void CNPC_EnemyFinder::Wake(CBaseEntity* pActivator) +{ + BaseClass::Wake(pActivator); + + //Enemy finder is not allowed to become visible. + AddEffects(EF_NODRAW); +} +#endif // MAPBASE + //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ diff --git a/mp/src/game/server/hl2/npc_fastzombie.cpp b/mp/src/game/server/hl2/npc_fastzombie.cpp index 0e15e2a8..6e5ec136 100644 --- a/mp/src/game/server/hl2/npc_fastzombie.cpp +++ b/mp/src/game/server/hl2/npc_fastzombie.cpp @@ -1296,7 +1296,11 @@ void CFastZombie::StartTask( const Task_t *pTask ) CBaseEntity *pEnemy = GetEnemy(); Vector vecJumpDir; - if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN ) + if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN +#if EXPANDED_NAVIGATION_ACTIVITIES + || GetActivity() == ACT_CLIMB_ALL +#endif + ) { // Jump off the pipe backwards! Vector forward; @@ -1449,7 +1453,11 @@ int CFastZombie::TranslateSchedule( int scheduleType ) break; case SCHED_FASTZOMBIE_UNSTICK_JUMP: - if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT ) + if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT +#if EXPANDED_NAVIGATION_ACTIVITIES + || (GetActivity() >= ACT_CLIMB_ALL && GetActivity() <= ACT_CLIMB_DISMOUNT_BOTTOM) +#endif + ) { return SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP; } @@ -1477,8 +1485,10 @@ int CFastZombie::TranslateSchedule( int scheduleType ) //--------------------------------------------------------- Activity CFastZombie::NPC_TranslateActivity( Activity baseAct ) { +#ifndef MAPBASE // Now covered by CAI_BaseNPC::NPC_BackupActivity if ( baseAct == ACT_CLIMB_DOWN ) return ACT_CLIMB_UP; +#endif return BaseClass::NPC_TranslateActivity( baseAct ); } diff --git a/mp/src/game/server/hl2/npc_fisherman.cpp b/mp/src/game/server/hl2/npc_fisherman.cpp index 4530f261..967c5795 100644 --- a/mp/src/game/server/hl2/npc_fisherman.cpp +++ b/mp/src/game/server/hl2/npc_fisherman.cpp @@ -64,8 +64,10 @@ public: virtual void Precache() { +#ifndef MAPBASE // This is now done in CNPC_PlayerCompanion::Precache() // Prevents a warning - SelectModel( ); + SelectModel(); +#endif BaseClass::Precache(); PrecacheScriptSound( "NPC_Fisherman.FootstepLeft" ); diff --git a/mp/src/game/server/hl2/npc_headcrab.cpp b/mp/src/game/server/hl2/npc_headcrab.cpp index 22f44d3d..b1b8db81 100644 --- a/mp/src/game/server/hl2/npc_headcrab.cpp +++ b/mp/src/game/server/hl2/npc_headcrab.cpp @@ -2433,7 +2433,7 @@ void CBaseHeadcrab::CreateDust( bool placeDecal ) //----------------------------------------------------------------------------- void CHeadcrab::Precache( void ) { - PrecacheModel( "models/headcrabclassic.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/headcrabclassic.mdl" ) ); PrecacheScriptSound( "NPC_HeadCrab.Gib" ); PrecacheScriptSound( "NPC_HeadCrab.Idle" ); @@ -2455,7 +2455,7 @@ void CHeadcrab::Precache( void ) void CHeadcrab::Spawn( void ) { Precache(); - SetModel( "models/headcrabclassic.mdl" ); + SetModel( DefaultOrCustomModel( "models/headcrabclassic.mdl" ) ); BaseClass::Spawn(); @@ -2570,7 +2570,7 @@ END_DATADESC() //----------------------------------------------------------------------------- void CFastHeadcrab::Precache( void ) { - PrecacheModel( "models/headcrab.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/headcrab.mdl" ) ); PrecacheScriptSound( "NPC_FastHeadcrab.Idle" ); PrecacheScriptSound( "NPC_FastHeadcrab.Alert" ); @@ -2589,7 +2589,7 @@ void CFastHeadcrab::Precache( void ) void CFastHeadcrab::Spawn( void ) { Precache(); - SetModel( "models/headcrab.mdl" ); + SetModel( DefaultOrCustomModel( "models/headcrab.mdl" ) ); BaseClass::Spawn(); @@ -3089,7 +3089,7 @@ void CBlackHeadcrab::TelegraphSound( void ) void CBlackHeadcrab::Spawn( void ) { Precache(); - SetModel( "models/headcrabblack.mdl" ); + SetModel( DefaultOrCustomModel( "models/headcrabblack.mdl" ) ); BaseClass::Spawn(); @@ -3106,7 +3106,7 @@ void CBlackHeadcrab::Spawn( void ) //----------------------------------------------------------------------------- void CBlackHeadcrab::Precache( void ) { - PrecacheModel( "models/headcrabblack.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/headcrabblack.mdl" ) ); PrecacheScriptSound( "NPC_BlackHeadcrab.Telegraph" ); PrecacheScriptSound( "NPC_BlackHeadcrab.Attack" ); diff --git a/mp/src/game/server/hl2/npc_kleiner.cpp b/mp/src/game/server/hl2/npc_kleiner.cpp index e8081762..4e5e5cf8 100644 --- a/mp/src/game/server/hl2/npc_kleiner.cpp +++ b/mp/src/game/server/hl2/npc_kleiner.cpp @@ -35,6 +35,11 @@ public: Class_T Classify ( void ); void HandleAnimEvent( animevent_t *pEvent ); int GetSoundInterests ( void ); + +#ifdef MAPBASE + // Use Kleiner's default subtitle color (255,255,200) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 255; params.b1 = 200; return BaseClass::GetGameTextSpeechParams( params ); } +#endif }; LINK_ENTITY_TO_CLASS( npc_kleiner, CNPC_Kleiner ); diff --git a/mp/src/game/server/hl2/npc_manhack.cpp b/mp/src/game/server/hl2/npc_manhack.cpp index 27dcde85..e221e938 100644 --- a/mp/src/game/server/hl2/npc_manhack.cpp +++ b/mp/src/game/server/hl2/npc_manhack.cpp @@ -2193,9 +2193,9 @@ void CNPC_Manhack::Precache(void) // // Model. // - PrecacheModel("models/manhack.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/manhack.mdl" ) ); PrecacheModel( MANHACK_GLOW_SPRITE ); - PropBreakablePrecacheAll( MAKE_STRING("models/manhack.mdl") ); + PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( "models/manhack.mdl" ) ) ); PrecacheScriptSound( "NPC_Manhack.Die" ); PrecacheScriptSound( "NPC_Manhack.Bat" ); @@ -2389,7 +2389,7 @@ void CNPC_Manhack::Spawn(void) AddSpawnFlags( SF_NPC_FADE_CORPSE ); #endif // _XBOX - SetModel( "models/manhack.mdl" ); + SetModel( DefaultOrCustomModel( "models/manhack.mdl" ) ); SetHullType(HULL_TINY_CENTERED); SetHullSizeNormal(); diff --git a/mp/src/game/server/hl2/npc_metropolice.cpp b/mp/src/game/server/hl2/npc_metropolice.cpp index 174a832d..02cc658a 100644 --- a/mp/src/game/server/hl2/npc_metropolice.cpp +++ b/mp/src/game/server/hl2/npc_metropolice.cpp @@ -261,6 +261,7 @@ BEGIN_DATADESC( CNPC_MetroPolice ) #ifdef MAPBASE DEFINE_AIGRENADE_DATADESC() DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ), + DEFINE_INPUT( m_iGrenadeDropCapabilities, FIELD_INTEGER, "SetGrenadeDropCapabilities" ), #endif END_DATADESC() @@ -517,6 +518,11 @@ CNPC_MetroPolice::CNPC_MetroPolice() { #ifdef MAPBASE m_iGrenadeCapabilities = GRENCAP_GRENADE; + + if (ai_grenade_always_drop.GetBool()) + { + m_iGrenadeDropCapabilities = (eGrenadeDropCapabilities)(GRENDROPCAP_GRENADE | GRENDROPCAP_ALTFIRE | GRENDROPCAP_INTERRUPTED); + } #endif } @@ -882,7 +888,7 @@ void CNPC_MetroPolice::SpeakStandoffSentence( int nSentenceType ) break; case STANDOFF_SENTENCE_FORCED_TAKE_COVER: - SpeakIfAllowed( TLK_COP_SO_END ); + SpeakIfAllowed( TLK_COP_SO_FORCE_COVER ); break; case STANDOFF_SENTENCE_STAND_CHECK_TARGET: @@ -1008,7 +1014,12 @@ void CNPC_MetroPolice::SpeakSentence( int nSentenceType ) return; } +#ifdef MAPBASE + // Fixed issues with standoff sentences not playing when they should + if ( m_StandoffBehavior.IsActive() ) +#else if ( GetRunningBehavior() == &m_StandoffBehavior ) +#endif { SpeakStandoffSentence( nSentenceType ); return; @@ -1156,7 +1167,11 @@ bool CNPC_MetroPolice::SpeakIfAllowed( const char *concept, const char *modifier AI_CriteriaSet set; if (modifiers) { +#ifdef NEW_RESPONSE_SYSTEM + GatherCriteria( &set, concept, modifiers ); +#else GetExpresser()->MergeModifiers(set, modifiers); +#endif } return SpeakIfAllowed( concept, set, sentencepriority, sentencecriteria ); } @@ -1522,7 +1537,12 @@ void CNPC_MetroPolice::OnUpdateShotRegulator( ) BaseClass::OnUpdateShotRegulator(); // FIXME: This code (except the burst interval) could be used for all weapon types +#ifdef MAPBASE + // Only if we actually have the pistol out + if ( GetActiveWeapon() && EntIsClass( GetActiveWeapon(), gm_isz_class_Pistol ) ) +#else if( Weapon_OwnsThisType( "weapon_pistol" ) ) +#endif { if ( m_nBurstMode == BURST_NOT_ACTIVE ) { @@ -1622,6 +1642,11 @@ bool CNPC_MetroPolice::ShouldAttemptToStitch() //----------------------------------------------------------------------------- Vector CNPC_MetroPolice::StitchAimTarget( const Vector &posSrc, bool bNoisy ) { +#ifdef MAPBASE + if ( !GetEnemy() ) + return vec3_origin; +#endif + // This will make us aim a stitch at the feet of the player so we can see it if ( !GetEnemy()->IsPlayer() ) return GetShootTarget()->BodyTarget( posSrc, bNoisy ); @@ -3523,7 +3548,11 @@ Activity CNPC_MetroPolice::NPC_TranslateActivity( Activity newActivity ) // If we're shoving, see if we should be more forceful in doing so if ( newActivity == ACT_PUSH_PLAYER ) { +#ifdef MAPBASE + if ( m_nNumWarnings >= METROPOLICE_MAX_WARNINGS && Weapon_TranslateActivity( ACT_MELEE_ATTACK1, NULL ) == ACT_MELEE_ATTACK_SWING ) +#else if ( m_nNumWarnings >= METROPOLICE_MAX_WARNINGS ) +#endif return ACT_MELEE_ATTACK1; } @@ -3667,6 +3696,11 @@ void CNPC_MetroPolice::Event_Killed( const CTakeDamageInfo &info ) DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); pHL2GameRules->NPC_DroppedHealth(); } + +#ifdef MAPBASE + // Drop grenades if we should + DropGrenadeItemsOnDeath( info, pPlayer ); +#endif } BaseClass::Event_Killed( info ); @@ -3874,6 +3908,15 @@ int CNPC_MetroPolice::SelectScheduleNoDirectEnemy() return SCHED_METROPOLICE_SMASH_PROP; } +#ifdef MAPBASE + // If you see your enemy and you're still arming yourself, wait and don't just charge in + // (if your weapon is holstered, you're probably about to arm yourself) + if ( HasCondition( COND_SEE_ENEMY ) && GetWeapon(0) && (IsWeaponHolstered() || FindGestureLayer( TranslateActivity( ACT_ARM ) ) != -1) ) + { + return SCHED_COMBAT_FACE; + } +#endif + return SCHED_METROPOLICE_CHASE_ENEMY; } @@ -4497,34 +4540,7 @@ int CNPC_MetroPolice::SelectBehaviorOverrideSchedule() //----------------------------------------------------------------------------- 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; + return BaseClass::IsCrouchedActivity( activity ); } //----------------------------------------------------------------------------- @@ -4920,7 +4936,12 @@ int CNPC_MetroPolice::SelectSchedule( void ) // This will cause the cops to run backwards + shoot at the same time if ( !bHighHealth && !HasBaton() ) { +#ifdef MAPBASE + // Don't do this with low-capacity weapons or weapons which don't use clips + if ( GetActiveWeapon() && GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->GetMaxClip1() > 10 && (GetActiveWeapon()->m_iClip1 <= 5) ) +#else if ( GetActiveWeapon() && (GetActiveWeapon()->m_iClip1 <= 5) ) +#endif { #ifdef METROPOLICE_USES_RESPONSE_SYSTEM SpeakIfAllowed( TLK_COP_LOWAMMO ); diff --git a/mp/src/game/server/hl2/npc_metropolice.h b/mp/src/game/server/hl2/npc_metropolice.h index 1f3f9517..742de7e3 100644 --- a/mp/src/game/server/hl2/npc_metropolice.h +++ b/mp/src/game/server/hl2/npc_metropolice.h @@ -67,8 +67,12 @@ public: const char* GetGrenadeAttachment() { return "LHand"; } - virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; } + virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0 && BaseClass::IsAltFireCapable(); } virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; } + + virtual bool ShouldDropGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_GRENADE) != 0 && BaseClass::ShouldDropGrenades(); } + virtual bool ShouldDropInterruptedGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_INTERRUPTED) != 0 && BaseClass::ShouldDropInterruptedGrenades(); } + virtual bool ShouldDropAltFire() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_ALTFIRE) != 0 && BaseClass::ShouldDropAltFire(); } #endif Vector EyeDirection3D( void ) { return CAI_BaseHumanoid::EyeDirection3D(); } // cops don't have eyes @@ -520,6 +524,7 @@ private: // Determines whether this NPC is allowed to use grenades or alt-fire stuff. eGrenadeCapabilities m_iGrenadeCapabilities; + eGrenadeDropCapabilities m_iGrenadeDropCapabilities; #endif AIHANDLE m_hManhack; diff --git a/mp/src/game/server/hl2/npc_monk.cpp b/mp/src/game/server/hl2/npc_monk.cpp index 63a4ee6d..e517a6fe 100644 --- a/mp/src/game/server/hl2/npc_monk.cpp +++ b/mp/src/game/server/hl2/npc_monk.cpp @@ -196,6 +196,9 @@ Class_T CNPC_Monk::Classify( void ) return CLASS_PLAYER_ALLY_VITAL; } +#ifdef MAPBASE +ConVar npc_monk_use_old_acts( "npc_monk_use_old_acts", "1" ); +#endif //----------------------------------------------------------------------------- // Purpose: @@ -229,6 +232,45 @@ Activity CNPC_Monk::NPC_TranslateActivity( Activity eNewActivity ) } } +#if defined(EXPANDED_HL2_WEAPON_ACTIVITIES) && AR2_ACTIVITY_FIX == 1 + if (npc_monk_use_old_acts.GetBool()) + { + // HACKHACK: Don't break the balcony scene + if ( FStrEq( STRING(gpGlobals->mapname), "d1_town_02" ) && eNewActivity == ACT_IDLE ) + { + eNewActivity = ACT_IDLE_SMG1; + } + else + { + switch (eNewActivity) + { + case ACT_IDLE_AR2: + eNewActivity = ACT_IDLE_SMG1; + break; + + case ACT_IDLE_ANGRY_SHOTGUN: + case ACT_IDLE_ANGRY_AR2: + eNewActivity = ACT_IDLE_ANGRY_SMG1; + break; + + case ACT_WALK_AIM_SHOTGUN: + case ACT_WALK_AIM_AR2: + eNewActivity = ACT_WALK_AIM_RIFLE; + break; + + case ACT_RUN_AIM_SHOTGUN: + case ACT_RUN_AIM_AR2: + eNewActivity = ACT_RUN_AIM_RIFLE; + break; + + case ACT_RANGE_ATTACK_SHOTGUN_LOW: + case ACT_RANGE_ATTACK_AR2_LOW: + eNewActivity = ACT_RANGE_ATTACK_SMG1_LOW; + break; + } + } + } +#else // We need these so that we can pick up the shotgun to throw it in the balcony scene if ( eNewActivity == ACT_IDLE_ANGRY_SHOTGUN ) { @@ -246,6 +288,7 @@ Activity CNPC_Monk::NPC_TranslateActivity( Activity eNewActivity ) { return ACT_RANGE_ATTACK_SMG1_LOW; } +#endif return eNewActivity; } diff --git a/mp/src/game/server/hl2/npc_mossman.cpp b/mp/src/game/server/hl2/npc_mossman.cpp index ff924cd2..f92a8f51 100644 --- a/mp/src/game/server/hl2/npc_mossman.cpp +++ b/mp/src/game/server/hl2/npc_mossman.cpp @@ -41,6 +41,11 @@ public: bool CreateBehaviors( void ); int SelectSchedule( void ); +#ifdef MAPBASE + // Use Mossman's default subtitle color (220,255,198) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 220; params.g1 = 255; params.b1 = 198; return BaseClass::GetGameTextSpeechParams( params ); } +#endif + private: CAI_FollowBehavior m_FollowBehavior; }; diff --git a/mp/src/game/server/hl2/npc_playercompanion.cpp b/mp/src/game/server/hl2/npc_playercompanion.cpp index 7cfc3a24..68568a72 100644 --- a/mp/src/game/server/hl2/npc_playercompanion.cpp +++ b/mp/src/game/server/hl2/npc_playercompanion.cpp @@ -34,6 +34,7 @@ #ifdef MAPBASE #include "mapbase/GlobalStrings.h" #include "world.h" +#include "vehicle_base.h" #endif ConVar ai_debug_readiness("ai_debug_readiness", "0" ); @@ -60,6 +61,10 @@ int AE_COMPANION_RELEASE_FLARE; #define COMPANION_MELEE_DIST 64.0 #endif +#ifdef MAPBASE +ConVar ai_allow_new_weapons( "ai_allow_new_weapons", "1", FCVAR_NONE, "Allows companion NPCs to automatically pick up and use weapons they were unable pick up before, i.e. 357s or crossbows." ); +#endif + #define MAX_TIME_BETWEEN_BARRELS_EXPLODING 5.0f #define MAX_TIME_BETWEEN_CONSECUTIVE_PLAYER_KILLS 3.0f @@ -148,6 +153,11 @@ BEGIN_DATADESC( CNPC_PlayerCompanion ) #ifdef MAPBASE DEFINE_AIGRENADE_DATADESC() DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ), + DEFINE_INPUT( m_iGrenadeDropCapabilities, FIELD_INTEGER, "SetGrenadeDropCapabilities" ), +#endif + +#ifdef COMPANION_MELEE_ATTACK + DEFINE_FIELD( m_nMeleeDamage, FIELD_INTEGER ), #endif END_DATADESC() @@ -175,6 +185,19 @@ string_t CNPC_PlayerCompanion::gm_iszAR2Classname; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +CNPC_PlayerCompanion::CNPC_PlayerCompanion() +{ +#ifdef MAPBASE + if (ai_grenade_always_drop.GetBool()) + { + m_iGrenadeDropCapabilities = (eGrenadeDropCapabilities)(GRENDROPCAP_GRENADE | GRENDROPCAP_ALTFIRE | GRENDROPCAP_INTERRUPTED); + } +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + bool CNPC_PlayerCompanion::CreateBehaviors() { #ifdef HL2_EPISODIC @@ -196,6 +219,10 @@ bool CNPC_PlayerCompanion::CreateBehaviors() AddBehavior( &m_FollowBehavior ); AddBehavior( &m_LeadBehavior ); #endif//HL2_EPISODIC + +#ifdef MAPBASE + AddBehavior( &m_FuncTankBehavior ); +#endif return BaseClass::CreateBehaviors(); } @@ -219,6 +246,11 @@ void CNPC_PlayerCompanion::Precache() #endif #endif +#ifdef MAPBASE + // Moved from Spawn() + SelectModel(); +#endif + PrecacheModel( STRING( GetModelName() ) ); #ifdef HL2_EPISODIC @@ -237,7 +269,9 @@ void CNPC_PlayerCompanion::Precache() //----------------------------------------------------------------------------- void CNPC_PlayerCompanion::Spawn() { +#ifndef MAPBASE // Moved to Precache() SelectModel(); +#endif Precache(); @@ -1620,6 +1654,19 @@ Activity CNPC_PlayerCompanion::TranslateActivityReadiness( Activity activity ) continue; } +#ifdef MAPBASE + // If we don't have the readiness activity we selected and there's no backup activity available, break the loop and return the base act. + bool bRequired; + if ( !HaveSequenceForActivity( actremap.mappedActivity ) && !HaveSequenceForActivity( Weapon_TranslateActivity( actremap.mappedActivity, &bRequired ) ) ) + { + Activity backupAct = Weapon_BackupActivity( actremap.mappedActivity, bRequired ); + if ( backupAct != actremap.mappedActivity ) + return backupAct; + else + break; + } +#endif + // We've successfully passed all criteria for remapping this return actremap.mappedActivity; } @@ -1643,21 +1690,6 @@ 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 ) @@ -1928,10 +1960,9 @@ bool CNPC_PlayerCompanion::IsReadinessCapable() { // 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 ) + if ( TranslateActivity( ACT_IDLE_RELAXED ) == ACT_IDLE_RELAXED && + TranslateActivity( ACT_IDLE_STIMULATED ) == ACT_IDLE_STIMULATED && + TranslateActivity( ACT_IDLE_AGITATED ) == ACT_IDLE_AGITATED ) return false; if (LookupActivity( "ACT_IDLE_AIM_RIFLE_STIMULATED" ) == ACT_INVALID) @@ -2726,6 +2757,13 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); } +#ifdef MAPBASE + else if (EntIsClass( pWeapon, gm_isz_class_Pistol ) || EntIsClass( pWeapon, gm_isz_class_357 ) || EntIsClass( pWeapon, gm_isz_class_Crossbow )) + { + // The AI automatically detects these weapons as usable now that there's animations for them, so ensure this behavior can be toggled in situations where that's not desirable + return ai_allow_new_weapons.GetBool(); + } +#endif else { return true; @@ -2762,6 +2800,21 @@ void CNPC_PlayerCompanion::Weapon_Equip( CBaseCombatWeapon *pWeapon ) m_bReadinessCapable = IsReadinessCapable(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CNPC_PlayerCompanion::DoUnholster() +{ + if ( BaseClass::DoUnholster() ) + { + m_bReadinessCapable = IsReadinessCapable(); + return true; + } + + return false; +} +#endif + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void CNPC_PlayerCompanion::PickupWeapon( CBaseCombatWeapon *pWeapon ) @@ -3423,7 +3476,7 @@ bool CNPC_PlayerCompanion::OverrideMove( float flInterval ) else if ( pEntity->m_iClassname == iszBounceBomb ) { CBounceBomb *pBomb = static_cast(pEntity); - if ( pBomb && !pBomb->IsPlayerPlaced() && pBomb->IsAwake() ) + if ( pBomb && pBomb->ShouldBeAvoidedByCompanions() ) { UTIL_TraceLine( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), MASK_BLOCKLOS, pEntity, COLLISION_GROUP_NONE, &tr ); if (tr.fraction == 1.0 && !tr.startsolid) @@ -4179,10 +4232,44 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak } #ifdef MAPBASE +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNPC_PlayerCompanion::Event_Killed( const CTakeDamageInfo &info ) +{ + // For now, allied player companions are set to always drop grenades and other items + // even if the player did not kill them + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (!IsPlayerAlly( pPlayer )) + { + pPlayer = ToBasePlayer( info.GetAttacker() ); + + // See if there's a player in a vehicle instead (from CNPC_CombineS) + if ( !pPlayer ) + { + CPropVehicleDriveable *pVehicle = dynamic_cast( info.GetAttacker() ) ; + if ( pVehicle && pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer() ) + { + pPlayer = assert_cast( pVehicle->GetDriver() ); + } + } + } + + if ( pPlayer != NULL ) + { + // Drop grenades if we should + DropGrenadeItemsOnDeath( info, pPlayer ); + } + + BaseClass::Event_Killed( info ); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) { + BaseClass::Event_KilledOther( pVictim, info ); + if ( pVictim ) { if (pVictim->IsPlayer() || (pVictim->IsNPC() && @@ -4223,17 +4310,10 @@ void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeD } //----------------------------------------------------------------------------- -// 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. +// Purpose: Handles custom combat speech stuff ported from Alyx. //----------------------------------------------------------------------------- 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 @@ -4296,7 +4376,6 @@ void CNPC_PlayerCompanion::DoCustomCombatAI( void ) { SpeakIfAllowed( TLK_MANY_ENEMIES ); } - */ } #endif diff --git a/mp/src/game/server/hl2/npc_playercompanion.h b/mp/src/game/server/hl2/npc_playercompanion.h index 81ba080c..8dcf1aa1 100644 --- a/mp/src/game/server/hl2/npc_playercompanion.h +++ b/mp/src/game/server/hl2/npc_playercompanion.h @@ -23,6 +23,7 @@ #endif #ifdef MAPBASE +#include "ai_behavior_functank.h" #include "mapbase/ai_grenade.h" #endif @@ -108,8 +109,10 @@ class CNPC_PlayerCompanion : public CAI_PlayerAlly { DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly ); #endif - public: + + CNPC_PlayerCompanion(); + //--------------------------------- bool CreateBehaviors(); void Precache(); @@ -237,6 +240,7 @@ public: // This is just here to overwrite ai_playerally's TLK_ENEMY_DEAD virtual void OnKilledNPC(CBaseCombatCharacter *pKilled) {} + virtual void Event_Killed( const CTakeDamageInfo &info ); virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); virtual void DoCustomCombatAI( void ); #endif @@ -278,6 +282,9 @@ public: bool ShouldLookForBetterWeapon(); bool Weapon_CanUse( CBaseCombatWeapon *pWeapon ); void Weapon_Equip( CBaseCombatWeapon *pWeapon ); +#ifdef MAPBASE + bool DoUnholster( void ); +#endif void PickupWeapon( CBaseCombatWeapon *pWeapon ); #if COMPANION_MELEE_ATTACK @@ -337,13 +344,18 @@ public: bool AllowReadinessValueChange( void ); #ifdef MAPBASE - virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; } + virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0 && BaseClass::IsAltFireCapable(); } virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; } + virtual bool ShouldDropGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_GRENADE) != 0 && BaseClass::ShouldDropGrenades(); } + virtual bool ShouldDropInterruptedGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_INTERRUPTED) != 0 && BaseClass::ShouldDropInterruptedGrenades(); } + virtual bool ShouldDropAltFire() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_ALTFIRE) != 0 && BaseClass::ShouldDropAltFire(); } + private: // Determines whether this NPC is allowed to use grenades or alt-fire stuff. eGrenadeCapabilities m_iGrenadeCapabilities; + eGrenadeDropCapabilities m_iGrenadeDropCapabilities; #endif protected: @@ -424,6 +436,9 @@ protected: CAI_OperatorBehavior m_OperatorBehavior; CAI_PassengerBehaviorCompanion m_PassengerBehavior; CAI_FearBehavior m_FearBehavior; +#endif +#ifdef MAPBASE + CAI_FuncTankBehavior m_FuncTankBehavior; #endif //----------------------------------------------------- diff --git a/mp/src/game/server/hl2/npc_rollermine.cpp b/mp/src/game/server/hl2/npc_rollermine.cpp index ec9be919..fc803f1f 100644 --- a/mp/src/game/server/hl2/npc_rollermine.cpp +++ b/mp/src/game/server/hl2/npc_rollermine.cpp @@ -310,6 +310,16 @@ protected: bool IsActive() { return m_flActiveTime > gpGlobals->curtime ? false : true; } + inline float GetForwardSpeed() const + { +#ifdef MAPBASE + if (m_flSpeedModifier != 1.0f) + return m_flForwardSpeed * m_flSpeedModifier; + else +#endif + return m_flForwardSpeed; + } + // INPCInteractive Functions virtual bool CanInteractWith( CAI_BaseNPC *pUser ) { return true; } virtual bool HasBeenInteractedWith() { return m_bHackedByAlyx; } @@ -1313,7 +1323,7 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) Vector vecRight; AngleVectors( QAngle( 0, yaw, 0 ), NULL, &vecRight, NULL ); - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, -m_flForwardSpeed * 5 ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, -GetForwardSpeed() * 5 ); TaskComplete(); return; @@ -1323,6 +1333,8 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) } { + float flForwardSpeed = GetForwardSpeed(); + float yaw = UTIL_VecToYaw( GetNavigator()->GetCurWaypointPos() - GetLocalOrigin() ); Vector vecRight; @@ -1362,17 +1374,17 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) vecCompensate.y = -vecVelocity.x; vecCompensate.z = 0; - m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 ); + m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, flForwardSpeed * -0.75 ); } if( m_bHackedByAlyx ) { // Move faster. - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * 2.0f ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed * 2.0f ); } else { - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed ); } } break; @@ -1496,8 +1508,10 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) vecCompensate.z = 0; VectorNormalize( vecCompensate ); - m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 ); - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * flTorqueFactor ); + float flForwardSpeed = GetForwardSpeed(); + + m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, flForwardSpeed * -0.75 ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed * flTorqueFactor ); // Taunt when I get closer if( !(m_iSoundEventFlags & ROLLERMINE_SE_TAUNT) && UTIL_DistApprox( GetLocalOrigin(), vecTargetPosition ) <= 400 ) @@ -1615,8 +1629,10 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) vecCompensate.z = 0; VectorNormalize( vecCompensate ); - m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 ); - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * flTorqueFactor ); + float flForwardSpeed = GetForwardSpeed(); + + m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, flForwardSpeed * -0.75 ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed * flTorqueFactor ); // Once we're near the player, slow & stop if ( GetAbsOrigin().DistToSqr( vecTargetPosition ) < (ROLLERMINE_RETURN_TO_PLAYER_DIST*2.0) ) @@ -2572,6 +2588,12 @@ float CNPC_RollerMine::RollingSpeed() float rollingSpeed = angVel.Length() - 90; rollingSpeed = clamp( rollingSpeed, 1, MAX_ROLLING_SPEED ); rollingSpeed *= (1/MAX_ROLLING_SPEED); +#ifdef MAPBASE + if (m_flSpeedModifier != 1.0f) + { + rollingSpeed *= m_flSpeedModifier; + } +#endif return rollingSpeed; } return 0; diff --git a/mp/src/game/server/hl2/npc_scanner.cpp b/mp/src/game/server/hl2/npc_scanner.cpp index fb49ff95..7df3e1ce 100644 --- a/mp/src/game/server/hl2/npc_scanner.cpp +++ b/mp/src/game/server/hl2/npc_scanner.cpp @@ -256,11 +256,11 @@ void CNPC_CScanner::Spawn(void) if( m_bIsClawScanner ) { - SetModel( "models/shield_scanner.mdl"); + SetModel( DefaultOrCustomModel( "models/shield_scanner.mdl" ) ); } else { - SetModel( "models/combine_scanner.mdl"); + SetModel( DefaultOrCustomModel( "models/combine_scanner.mdl" ) ); } m_iHealth = sk_scanner_health.GetFloat(); @@ -565,7 +565,7 @@ void CNPC_CScanner::Precache(void) // Model if( m_bIsClawScanner ) { - PrecacheModel("models/shield_scanner.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/shield_scanner.mdl" ) ); PrecacheModel("models/gibs/Shield_Scanner_Gib1.mdl"); PrecacheModel("models/gibs/Shield_Scanner_Gib2.mdl"); @@ -591,7 +591,7 @@ void CNPC_CScanner::Precache(void) } else { - PrecacheModel("models/combine_scanner.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/combine_scanner.mdl" ) ); PrecacheModel("models/gibs/scanner_gib01.mdl" ); PrecacheModel("models/gibs/scanner_gib02.mdl" ); @@ -2567,6 +2567,14 @@ void CNPC_CScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarget myZAccel = flDist / flInterval; } +#ifdef MAPBASE + if (m_flSpeedModifier != 1.0f) + { + myAccel *= m_flSpeedModifier; + //myZAccel *= m_flSpeedModifier; + } +#endif + MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay ); // calc relative banking targets diff --git a/mp/src/game/server/hl2/npc_stalker.cpp b/mp/src/game/server/hl2/npc_stalker.cpp index 0165c29d..67ed567a 100644 --- a/mp/src/game/server/hl2/npc_stalker.cpp +++ b/mp/src/game/server/hl2/npc_stalker.cpp @@ -278,7 +278,7 @@ void CNPC_Stalker::Spawn( void ) { Precache( ); - SetModel( "models/stalker.mdl" ); + SetModel( DefaultOrCustomModel( "models/stalker.mdl" ) ); SetHullType(HULL_HUMAN); SetHullSizeNormal(); @@ -321,7 +321,7 @@ void CNPC_Stalker::Spawn( void ) //----------------------------------------------------------------------------- void CNPC_Stalker::Precache( void ) { - PrecacheModel("models/stalker.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/stalker.mdl" ) ); PrecacheModel("sprites/laser.vmt"); PrecacheModel("sprites/redglow1.vmt"); diff --git a/mp/src/game/server/hl2/npc_strider.cpp b/mp/src/game/server/hl2/npc_strider.cpp index 5be578f3..a4289fdb 100644 --- a/mp/src/game/server/hl2/npc_strider.cpp +++ b/mp/src/game/server/hl2/npc_strider.cpp @@ -395,6 +395,9 @@ BEGIN_DATADESC( CNPC_Strider ) DEFINE_INPUTFUNC( FIELD_VOID, "EnableMinigun", InputEnableMinigun ), DEFINE_INPUTFUNC( FIELD_FLOAT, "StopShootingMinigunForSeconds", InputStopShootingMinigunForSeconds ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableCrouch", InputDisableCrouch ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnableCrouch", InputEnableCrouch ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "DisableMoveToLOS", InputDisableMoveToLOS ), DEFINE_INPUTFUNC( FIELD_STRING, "DisableCollisionWith", InputDisableCollisionWith ), DEFINE_INPUTFUNC( FIELD_STRING, "EnableCollisionWith", InputEnableCollisionWith ), @@ -2362,6 +2365,15 @@ void CNPC_Strider::InputDisableCrouch( inputdata_t &inputdata ) m_bDontCrouch = true; } +#ifdef MAPBASE +//--------------------------------------------------------- +//--------------------------------------------------------- +void CNPC_Strider::InputEnableCrouch( inputdata_t &inputdata ) +{ + m_bDontCrouch = false; +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CNPC_Strider::InputDisableMoveToLOS( inputdata_t &inputdata ) diff --git a/mp/src/game/server/hl2/npc_strider.h b/mp/src/game/server/hl2/npc_strider.h index a9074599..d5bdd209 100644 --- a/mp/src/game/server/hl2/npc_strider.h +++ b/mp/src/game/server/hl2/npc_strider.h @@ -171,6 +171,9 @@ public: void InputDisableAggressiveBehavior( inputdata_t &inputdata ); void InputStopShootingMinigunForSeconds( inputdata_t &inputdata ); void InputDisableCrouch( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputEnableCrouch( inputdata_t &inputdata ); +#endif void InputDisableMoveToLOS( inputdata_t &inputdata ); void InputExplode( inputdata_t &inputdata ); void InputScaleGroundSpeed( inputdata_t &inputdata ); diff --git a/mp/src/game/server/hl2/npc_turret_ceiling.cpp b/mp/src/game/server/hl2/npc_turret_ceiling.cpp index bc4f6821..0637525a 100644 --- a/mp/src/game/server/hl2/npc_turret_ceiling.cpp +++ b/mp/src/game/server/hl2/npc_turret_ceiling.cpp @@ -304,7 +304,7 @@ CNPC_CeilingTurret::~CNPC_CeilingTurret( void ) //----------------------------------------------------------------------------- void CNPC_CeilingTurret::Precache( void ) { - PrecacheModel( CEILING_TURRET_MODEL ); + PrecacheModel( DefaultOrCustomModel( CEILING_TURRET_MODEL ) ); PrecacheModel( CEILING_TURRET_GLOW_SPRITE ); // Activities @@ -347,7 +347,7 @@ void CNPC_CeilingTurret::Spawn( void ) { Precache(); - SetModel( CEILING_TURRET_MODEL ); + SetModel( DefaultOrCustomModel( CEILING_TURRET_MODEL ) ); BaseClass::Spawn(); diff --git a/mp/src/game/server/hl2/npc_turret_ground.cpp b/mp/src/game/server/hl2/npc_turret_ground.cpp index 8ffd37df..c7dceeae 100644 --- a/mp/src/game/server/hl2/npc_turret_ground.cpp +++ b/mp/src/game/server/hl2/npc_turret_ground.cpp @@ -69,7 +69,7 @@ END_DATADESC() void CNPC_GroundTurret::Precache( void ) { PrecacheModel( GROUNDTURRET_BEAM_SPRITE ); - PrecacheModel( "models/combine_turrets/ground_turret.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/combine_turrets/ground_turret.mdl" ) ); PrecacheScriptSound( "NPC_CeilingTurret.Deploy" ); m_ShotSounds = PrecacheScriptSound( "NPC_FloorTurret.ShotSounds" ); @@ -88,7 +88,7 @@ void CNPC_GroundTurret::Spawn( void ) { Precache(); - UTIL_SetModel( this, "models/combine_turrets/ground_turret.mdl" ); + UTIL_SetModel( this, DefaultOrCustomModel( "models/combine_turrets/ground_turret.mdl" ) ); SetNavType( NAV_FLY ); SetSolid( SOLID_VPHYSICS ); diff --git a/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index 8dffd6db..218da1a5 100644 --- a/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/mp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -582,8 +582,11 @@ bool CNPC_Vortigaunt::InnateWeaponLOSCondition( const Vector &ownerPos, const Ve UTIL_PredictedPosition( GetEnemy(), flTimeDelta, &vecNewTargetPos ); #ifdef MAPBASE - // This fix was created by DKY. - // His original comment is below. + // There's apparently a null pointer crash here + if (!GetEnemy()) + return false; + + // The fix below and its accompanying comment were created by DKY. /* @@ -1067,9 +1070,17 @@ Activity CNPC_Vortigaunt::NPC_TranslateActivity( Activity eNewActivity ) if ( GetReadinessLevel() >= AIRL_STIMULATED ) return ACT_IDLE_STIMULATED; } - + if ( eNewActivity == ACT_RANGE_ATTACK2 ) + { +#ifdef MAPBASE + // If we're capable of using grenades, use ACT_COMBINE_THROW_GRENADE + if (IsGrenadeCapable()) + return ACT_COMBINE_THROW_GRENADE; + else +#endif return (Activity) ACT_VORTIGAUNT_DISPEL; + } return BaseClass::NPC_TranslateActivity( eNewActivity ); } @@ -2715,6 +2726,15 @@ void CNPC_Vortigaunt::OnSquishedGrub( const CBaseEntity *pGrub ) //----------------------------------------------------------------------------- void CNPC_Vortigaunt::AimGun( void ) { +#ifdef MAPBASE + // Use base for func_tank + if (m_FuncTankBehavior.IsRunning()) + { + BaseClass::AimGun(); + return; + } +#endif + // If our aim lock is on, don't bother if ( m_flAimDelay >= gpGlobals->curtime ) return; diff --git a/mp/src/game/server/hl2/npc_vortigaunt_episodic.h b/mp/src/game/server/hl2/npc_vortigaunt_episodic.h index e0da23d3..91e6a3c6 100644 --- a/mp/src/game/server/hl2/npc_vortigaunt_episodic.h +++ b/mp/src/game/server/hl2/npc_vortigaunt_episodic.h @@ -138,6 +138,13 @@ public: // used so a grub can notify me that I stepped on it. Says a line. void OnSquishedGrub( const CBaseEntity *pGrub ); +#ifdef MAPBASE + // Use the vortigaunts' default subtitle color (188,241,174) + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 188; params.g1 = 241; params.b1 = 174; return BaseClass::GetGameTextSpeechParams( params ); } + + const char* GetGrenadeAttachment() { return "rightclaw"; } +#endif + private: int NumAntlionsInRadius( float flRadius ); diff --git a/mp/src/game/server/hl2/npc_zombie.cpp b/mp/src/game/server/hl2/npc_zombie.cpp index 47e8923b..d7aab3dd 100644 --- a/mp/src/game/server/hl2/npc_zombie.cpp +++ b/mp/src/game/server/hl2/npc_zombie.cpp @@ -908,7 +908,7 @@ bool CZombie::IsSquashed( const CTakeDamageInfo &info ) return false; } - if( info.GetDamageType() & DMG_CRUSH ) + if( info.GetDamageType() & DMG_CRUSH && info.GetInflictor() ) // Mapbase - Fixes a crash with inflictor-less crush damage { IPhysicsObject *pCrusher = info.GetInflictor()->VPhysicsGetObject(); if( pCrusher && pCrusher->GetMass() >= ZOMBIE_SQUASH_MASS && info.GetInflictor()->WorldSpaceCenter().z > EyePosition().z ) @@ -1210,13 +1210,25 @@ const char *CZombieCustom::GetMoanSound( int nSound ) // We could probably do this through the response system alone now, but whatever. modifiers.AppendCriteria( "moansound", UTIL_VarArgs("%i", nSound & 4) ); +#ifdef NEW_RESPONSE_SYSTEM + AI_Response response; + CAI_Concept concept = "TLK_ZOMBIE_MOAN"; + concept.SetSpeaker( this ); + if (!FindResponse( response, concept, &modifiers )) + return "NPC_BaseZombie.Moan1"; +#else AI_Response response; if ( !SpeakFindResponse(response, TLK_ZOMBIE_MOAN, modifiers) ) return "NPC_BaseZombie.Moan1"; +#endif // Must be static so it could be returned static char szSound[128]; +#ifdef NEW_RESPONSE_SYSTEM + response.GetName(szSound, sizeof(szSound)); +#else Q_strncpy( szSound, response.GetResponsePtr(), AI_Response::MAX_RESPONSE_NAME ); +#endif return szSound; } @@ -1249,7 +1261,8 @@ void CZombieCustom::AttackSound( void ) //----------------------------------------------------------------------------- void CZombieCustom::SpeakIfAllowed(const char *concept, AI_CriteriaSet *modifiers) { - Speak( concept, modifiers ? *modifiers : AI_CriteriaSet() ); + AI_CriteriaSet empty; + Speak( concept, modifiers ? *modifiers : empty ); } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/proto_sniper.cpp b/mp/src/game/server/hl2/proto_sniper.cpp index 5427850a..4315e35c 100644 --- a/mp/src/game/server/hl2/proto_sniper.cpp +++ b/mp/src/game/server/hl2/proto_sniper.cpp @@ -250,6 +250,10 @@ public: virtual int SelectSchedule( void ); virtual int TranslateSchedule( int scheduleType ); +#ifdef MAPBASE + Activity NPC_TranslateActivity( Activity eNewActivity ); +#endif + bool KeyValue( const char *szKeyName, const char *szValue ); void PrescheduleThink( void ); @@ -334,6 +338,10 @@ private: bool IsPlayerAllySniper(); +#ifdef MAPBASE + const Vector &GetPaintCursor() { return m_vecPaintCursor; } +#endif + private: /// This is the variable from which m_flPaintTime gets set. @@ -403,6 +411,9 @@ private: DEFINE_CUSTOM_AI; DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; @@ -500,6 +511,26 @@ BEGIN_DATADESC( CProtoSniper ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CProtoSniper, CAI_BaseNPC, "Combine sniper NPC." ) + + DEFINE_SCRIPTFUNC( GetBulletSpeed, "" ) + DEFINE_SCRIPTFUNC( GetBulletOrigin, "" ) + DEFINE_SCRIPTFUNC( ScopeGlint, "" ) + + DEFINE_SCRIPTFUNC( GetPositionParameter, "" ) + DEFINE_SCRIPTFUNC( IsSweepingRandomly, "" ) + DEFINE_SCRIPTFUNC( FindFrustratedShot, "" ) + + DEFINE_SCRIPTFUNC( IsLaserOn, "" ) + DEFINE_SCRIPTFUNC( LaserOn, "" ) + DEFINE_SCRIPTFUNC( LaserOff, "" ) + + DEFINE_SCRIPTFUNC( GetPaintCursor, "Get the point the sniper is currently aiming at." ) + +END_SCRIPTDESC() +#endif + //========================================================= @@ -909,6 +940,14 @@ void CProtoSniper::OnScheduleChange( void ) { LaserOff(); +#ifdef MAPBASE + if ( m_bKilledPlayer && HasCondition( COND_SEE_PLAYER ) ) + { + // IMPOSSIBLE! (possible when SP respawn is enabled) + m_bKilledPlayer = false; + } +#endif + BaseClass::OnScheduleChange(); } @@ -1492,6 +1531,8 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info ) void CProtoSniper::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) { #ifdef MAPBASE + BaseClass::Event_KilledOther( pVictim, info ); + if (pVictim == GetEnemy()) SetCondition(COND_SNIPER_KILLED_ENEMY); #endif @@ -2007,6 +2048,23 @@ int CProtoSniper::TranslateSchedule( int scheduleType ) return BaseClass::TranslateSchedule( scheduleType ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CProtoSniper::NPC_TranslateActivity( Activity eNewActivity ) +{ + // ACT_IDLE is now just the soldier's unarmed idle animation. + // Use a gun-holding animation like what unhidden snipers were using before. + if (!HasSpawnFlags( SF_SNIPER_HIDDEN ) && eNewActivity == ACT_IDLE) + { + eNewActivity = ACT_IDLE_SMG1; + } + + return BaseClass::NPC_TranslateActivity( eNewActivity ); +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CProtoSniper::ScopeGlint() @@ -2588,6 +2646,19 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget ) // By default, aim for the center Vector vecTarget = pTarget->WorldSpaceCenter(); +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope)) + { + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { GetBulletOrigin(), ToHScript( pTarget ) }; + if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f) + return *functionReturn.m_pVector; + } + } +#endif + float flTimeSinceLastMiss = gpGlobals->curtime - m_flTimeLastShotMissed; if( pTarget->GetFlags() & FL_CLIENT ) @@ -3376,6 +3447,18 @@ AI_BEGIN_CUSTOM_NPC( proto_sniper, CProtoSniper ) //========================================================= //========================================================= +#ifdef MAPBASE + DEFINE_SCHEDULE + ( + SCHED_PSNIPER_PLAYER_DEAD, + + " Tasks" + " TASK_SNIPER_PLAYER_DEAD 0" + " " + " Interrupts" + " COND_SEE_PLAYER" + ) +#else DEFINE_SCHEDULE ( SCHED_PSNIPER_PLAYER_DEAD, @@ -3385,6 +3468,7 @@ AI_BEGIN_CUSTOM_NPC( proto_sniper, CProtoSniper ) " " " Interrupts" ) +#endif AI_END_CUSTOM_NPC() diff --git a/mp/src/game/server/hl2/vehicle_apc.cpp b/mp/src/game/server/hl2/vehicle_apc.cpp index 80c4291f..63d5e3ed 100644 --- a/mp/src/game/server/hl2/vehicle_apc.cpp +++ b/mp/src/game/server/hl2/vehicle_apc.cpp @@ -561,9 +561,15 @@ int CPropAPC::OnTakeDamage( const CTakeDamageInfo &info ) m_iHealth -= dmgInfo.GetDamage(); if ( m_iHealth <= 0 ) { - m_iHealth = 0; - Event_Killed( dmgInfo ); - return 0; +#ifdef MAPBASE_VSCRIPT + // False = Cheat death + if (ScriptDeathHook( const_cast(&info) ) != false) +#endif + { + m_iHealth = 0; + Event_Killed( dmgInfo ); + return 0; + } } // Chain diff --git a/mp/src/game/server/hl2/weapon_357.cpp b/mp/src/game/server/hl2/weapon_357.cpp index 3fa3fab8..a3c05371 100644 --- a/mp/src/game/server/hl2/weapon_357.cpp +++ b/mp/src/game/server/hl2/weapon_357.cpp @@ -27,6 +27,11 @@ // CWeapon357 //----------------------------------------------------------------------------- +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeapon357 : public CBaseHLCombatWeapon { DECLARE_CLASS( CWeapon357, CBaseHLCombatWeapon ); @@ -69,6 +74,9 @@ public: void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + + virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); } + virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); } #endif DECLARE_SERVERCLASS(); @@ -91,6 +99,22 @@ END_DATADESC() #ifdef MAPBASE acttable_t CWeapon357::m_acttable[] = { +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_REVOLVER, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_REVOLVER, true }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_REVOLVER, true }, + { ACT_RELOAD, ACT_RELOAD_REVOLVER, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_REVOLVER, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_REVOLVER, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_REVOLVER, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_REVOLVER_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_REVOLVER_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_REVOLVER_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_REVOLVER_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_REVOLVER, false }, + { ACT_WALK, ACT_WALK_REVOLVER, true }, + { ACT_RUN, ACT_RUN_REVOLVER, true }, +#else { ACT_IDLE, ACT_IDLE_PISTOL, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true }, { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true }, @@ -105,10 +129,113 @@ acttable_t CWeapon357::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, { ACT_WALK, ACT_WALK_PISTOL, false }, { ACT_RUN, ACT_RUN_PISTOL, false }, +#endif + + // + // Activities ported from weapon_alyxgun below + // + + // Readiness activities (not aiming) +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, +#else + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, +#endif + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, +#else + { ACT_WALK_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, +#endif + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, +#else + { ACT_RUN_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, +#endif + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities + + // Crouch activities + { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, + { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + + // Readiness translations + { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false }, + { ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false }, + { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false }, + { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false }, + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_REVOLVER_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_REVOLVER_MED, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_REVOLVER, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_REVOLVER, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_REVOLVER, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_REVOLVER, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_REVOLVER, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_REVOLVER, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_REVOLVER, false }, +#else + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE( CWeapon357 ); + +// Allows Weapon_BackupActivity() to access the 357's activity table. +acttable_t *Get357Acttable() +{ + return CWeapon357::m_acttable; +} + +int Get357ActtableCount() +{ + return ARRAYSIZE(CWeapon357::m_acttable); +} #endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_alyxgun.cpp b/mp/src/game/server/hl2/weapon_alyxgun.cpp index 270a503c..5bf20eb6 100644 --- a/mp/src/game/server/hl2/weapon_alyxgun.cpp +++ b/mp/src/game/server/hl2/weapon_alyxgun.cpp @@ -37,20 +37,40 @@ acttable_t CWeaponAlyxGun::m_acttable[] = { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, true }, { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, true }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, true }, +#ifdef MAPBASE + // For non-Alyx NPCs + { ACT_WALK, ACT_WALK_PISTOL, false }, + { ACT_RUN, ACT_RUN_PISTOL, false }, +#endif // Readiness activities (not aiming) +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, +#else { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, +#endif { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, +#else { ACT_WALK_RELAXED, ACT_WALK, false },//never aims { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, +#endif { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, - + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, +#else { ACT_RUN_RELAXED, ACT_RUN, false },//never aims { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, +#endif { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, @@ -85,6 +105,21 @@ acttable_t CWeaponAlyxGun::m_acttable[] = // { ACT_ARM, ACT_ARM_PISTOL, true }, // { ACT_DISARM, ACT_DISARM_PISTOL, true }, + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponAlyxGun); diff --git a/mp/src/game/server/hl2/weapon_alyxgun.h b/mp/src/game/server/hl2/weapon_alyxgun.h index cc3e862c..4dd95532 100644 --- a/mp/src/game/server/hl2/weapon_alyxgun.h +++ b/mp/src/game/server/hl2/weapon_alyxgun.h @@ -13,6 +13,11 @@ #pragma once #endif +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeaponAlyxGun : public CHLSelectFireMachineGun { DECLARE_DATADESC(); @@ -51,6 +56,11 @@ public: SetTouch(NULL); } +#ifdef MAPBASE + virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); } + virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); } +#endif + float m_flTooCloseTimer; DECLARE_ACTTABLE(); diff --git a/mp/src/game/server/hl2/weapon_annabelle.cpp b/mp/src/game/server/hl2/weapon_annabelle.cpp index 47ee9e39..6b7ecdd9 100644 --- a/mp/src/game/server/hl2/weapon_annabelle.cpp +++ b/mp/src/game/server/hl2/weapon_annabelle.cpp @@ -20,6 +20,11 @@ extern ConVar sk_auto_reload_time; +#ifdef MAPBASE +extern acttable_t *GetShotgunActtable(); +extern int GetShotgunActtableCount(); +#endif + class CWeaponAnnabelle : public CBaseHLCombatWeapon { DECLARE_DATADESC(); @@ -41,6 +46,16 @@ public: virtual const Vector& GetBulletSpread( void ) { static Vector cone = vec3_origin; + +#ifdef MAPBASE + if (GetOwner() && GetOwner()->OverridingWeaponProficiency()) + { + // If the owner's weapon proficiency is being overridden, return a more realistic spread + static Vector cone2 = VECTOR_CONE_6DEGREES; + return cone2; + } +#endif + return cone; } @@ -61,6 +76,11 @@ public: void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + virtual acttable_t *GetBackupActivityList() { return GetShotgunActtable(); } + virtual int GetBackupActivityListCount() { return GetShotgunActtableCount(); } +#endif + DECLARE_ACTTABLE(); CWeaponAnnabelle(void); @@ -82,6 +102,28 @@ END_DATADESC() acttable_t CWeaponAnnabelle::m_acttable[] = { +#if defined(EXPANDED_HL2_WEAPON_ACTIVITIES) && AR2_ACTIVITY_FIX == 1 + { ACT_IDLE, ACT_IDLE_AR2, false }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_AR2, true }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR2_LOW, false }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_ANNABELLE, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_ANNABELLE_LOW, true }, + { ACT_RELOAD, ACT_RELOAD_ANNABELLE, true }, + { ACT_WALK, ACT_WALK_AR2, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_AR2, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, false }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, false }, + { ACT_RUN, ACT_RUN_AR2, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_AR2, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, false }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, false }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_ANNABELLE, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_ANNABELLE_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_ANNABELLE, false }, + + { ACT_ARM, ACT_ARM_RIFLE, true }, + { ACT_DISARM, ACT_DISARM_RIFLE, true }, +#else #ifdef MAPBASE { ACT_IDLE, ACT_IDLE_SMG1, false }, #endif @@ -99,6 +141,22 @@ acttable_t CWeaponAnnabelle::m_acttable[] = { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true }, { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR2, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR2, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR2, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR2, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_AR2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponAnnabelle); @@ -175,6 +233,13 @@ bool CWeaponAnnabelle::StartReload( void ) pOwner->m_flNextAttack = gpGlobals->curtime; m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#ifdef MAPBASE + if ( pOwner->IsPlayer() ) + { + static_cast(pOwner)->SetAnimation( PLAYER_RELOAD ); + } +#endif + m_bInReload = true; return true; } diff --git a/mp/src/game/server/hl2/weapon_ar1.cpp b/mp/src/game/server/hl2/weapon_ar1.cpp index 994cdabb..0d9c0240 100644 --- a/mp/src/game/server/hl2/weapon_ar1.cpp +++ b/mp/src/game/server/hl2/weapon_ar1.cpp @@ -37,6 +37,10 @@ float Damage[ MAX_SETTINGS ] = 20, }; +#ifdef MAPBASE +extern acttable_t *GetAR2Acttable(); +extern int GetAR2ActtableCount(); +#endif //========================================================= //========================================================= @@ -93,6 +97,12 @@ public: break; } } + +#ifdef MAPBASE + virtual acttable_t *GetBackupActivityList() { return GetAR2Acttable(); } + virtual int GetBackupActivityListCount() { return GetAR2ActtableCount(); } +#endif + DECLARE_ACTTABLE(); }; @@ -105,6 +115,77 @@ PRECACHE_WEAPON_REGISTER(weapon_ar1); acttable_t CWeaponAR1::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR1, true }, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Optional new NPC activities + // (these should fall back to AR2 animations when they don't exist on an NPC) + { ACT_RELOAD, ACT_RELOAD_AR1, true }, + { ACT_IDLE, ACT_IDLE_AR1, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_AR1, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_AR1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_AR1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_AR1_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_AR1_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_AR1, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_AR1_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_AR1_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_AR1, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR1_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_AR1_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR1_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR1, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_AR1_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR1_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_AR1, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_AR1, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_AR1, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_AR1, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_AR1, 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_AR1, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR1_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_AR1_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR1_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_AR1_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR1, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR1_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR1_MED, false }, +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR1, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR1, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR1, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR1, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR1, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_AR1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponAR1); diff --git a/mp/src/game/server/hl2/weapon_ar2.cpp b/mp/src/game/server/hl2/weapon_ar2.cpp index 89a16700..6e7d4cac 100644 --- a/mp/src/game/server/hl2/weapon_ar2.cpp +++ b/mp/src/game/server/hl2/weapon_ar2.cpp @@ -38,10 +38,6 @@ 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 - //========================================================= //========================================================= @@ -100,14 +96,14 @@ acttable_t CWeaponAR2::m_acttable[] = { 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_AIM, ACT_RUN_AIM_AR2, 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_COVER_LOW, ACT_COVER_AR2_LOW, true }, { 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_RELOAD_LOW, ACT_RELOAD_AR2_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR2, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, #else @@ -160,10 +156,53 @@ acttable_t CWeaponAR2::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, #endif + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR2_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR2_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR2, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR2, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR2, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR2, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_AR2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponAR2); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the AR2's activity table. +acttable_t *GetAR2Acttable() +{ + return CWeaponAR2::m_acttable; +} + +int GetAR2ActtableCount() +{ + return ARRAYSIZE(CWeaponAR2::m_acttable); +} +#endif + CWeaponAR2::CWeaponAR2( ) { m_fMinRange1 = 65; @@ -343,6 +382,9 @@ void CWeaponAR2::SecondaryAttack( void ) if( pPlayer ) { pPlayer->RumbleEffect(RUMBLE_AR2_ALT_FIRE, 0, RUMBLE_FLAG_RESTART ); +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#endif } SendWeaponAnim( ACT_VM_FIDGET ); diff --git a/mp/src/game/server/hl2/weapon_ar2.h b/mp/src/game/server/hl2/weapon_ar2.h index d5376a83..5a855f2c 100644 --- a/mp/src/game/server/hl2/weapon_ar2.h +++ b/mp/src/game/server/hl2/weapon_ar2.h @@ -71,6 +71,9 @@ protected: bool m_bShotDelayed; int m_nVentPose; +#ifdef MAPBASE // Make act table accessible outside class +public: +#endif DECLARE_ACTTABLE(); DECLARE_DATADESC(); }; diff --git a/mp/src/game/server/hl2/weapon_bugbait.cpp b/mp/src/game/server/hl2/weapon_bugbait.cpp index 612fe142..d9e6f94c 100644 --- a/mp/src/game/server/hl2/weapon_bugbait.cpp +++ b/mp/src/game/server/hl2/weapon_bugbait.cpp @@ -58,6 +58,9 @@ public: bool ShouldDisplayHUDHint() { return true; } DECLARE_DATADESC(); +#ifdef MAPBASE + DECLARE_ACTTABLE(); +#endif protected: @@ -86,6 +89,26 @@ BEGIN_DATADESC( CWeaponBugBait ) END_DATADESC() +#ifdef MAPBASE +acttable_t CWeaponBugBait::m_acttable[] = +{ + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_GRENADE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE, false }, +#endif +}; + +IMPLEMENT_ACTTABLE( CWeaponBugBait ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -292,6 +315,10 @@ void CWeaponBugBait::ThrowGrenade( CBasePlayer *pPlayer ) } m_bRedraw = true; + +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif } //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_citizenpackage.cpp b/mp/src/game/server/hl2/weapon_citizenpackage.cpp index cdb9378a..95a02296 100644 --- a/mp/src/game/server/hl2/weapon_citizenpackage.cpp +++ b/mp/src/game/server/hl2/weapon_citizenpackage.cpp @@ -23,6 +23,13 @@ acttable_t CWeaponCitizenPackage::m_acttable[] = { { ACT_IDLE, ACT_IDLE_PACKAGE, false }, { ACT_WALK, ACT_WALK_PACKAGE, false }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_PACKAGE, false }, + + { ACT_IDLE_ANGRY, ACT_IDLE_PACKAGE, false }, + { ACT_WALK_AIM, ACT_WALK_PACKAGE, false }, + { ACT_RUN_AIM, ACT_RUN_PACKAGE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCitizenPackage); @@ -70,5 +77,12 @@ acttable_t CWeaponCitizenSuitcase::m_acttable[] = { { ACT_IDLE, ACT_IDLE_SUITCASE, false }, { ACT_WALK, ACT_WALK_SUITCASE, false }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_SUITCASE, false }, + + { ACT_IDLE_ANGRY, ACT_IDLE_SUITCASE, false }, + { ACT_WALK_AIM, ACT_WALK_SUITCASE, false }, + { ACT_RUN_AIM, ACT_RUN_SUITCASE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCitizenSuitcase); diff --git a/mp/src/game/server/hl2/weapon_crossbow.cpp b/mp/src/game/server/hl2/weapon_crossbow.cpp index b86b4c05..b55fa441 100644 --- a/mp/src/game/server/hl2/weapon_crossbow.cpp +++ b/mp/src/game/server/hl2/weapon_crossbow.cpp @@ -542,7 +542,7 @@ public: virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); virtual bool Reload( void ); #ifdef MAPBASE - virtual void Reload_NPC( void ); + virtual void Reload_NPC( bool bPlaySound = true ); #endif virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); @@ -591,6 +591,7 @@ private: void CheckZoomToggle( void ); void FireBolt( void ); #ifdef MAPBASE + void SetBolt( int iSetting ); void FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, Vector &vecShootDir ); #endif void ToggleZoom( void ); @@ -638,6 +639,57 @@ END_DATADESC() #ifdef MAPBASE acttable_t CWeaponCrossbow::m_acttable[] = { +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_CROSSBOW, true }, + { ACT_RELOAD, ACT_RELOAD_CROSSBOW, true }, + { ACT_IDLE, ACT_IDLE_CROSSBOW, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_CROSSBOW, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_CROSSBOW_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_CROSSBOW_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_CROSSBOW, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_CROSSBOW_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_CROSSBOW_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_CROSSBOW, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_CROSSBOW_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_CROSSBOW_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_CROSSBOW, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_CROSSBOW_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_CROSSBOW_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_CROSSBOW, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_CROSSBOW_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_CROSSBOW_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_CROSSBOW, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_CROSSBOW_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_CROSSBOW_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_CROSSBOW, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_CROSSBOW, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_CROSSBOW, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_CROSSBOW, 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_CROSSBOW, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_CROSSBOW_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_CROSSBOW_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_CROSSBOW_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_CROSSBOW_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_CROSSBOW, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#else { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true }, { ACT_RELOAD, ACT_RELOAD_SMG1, true }, { ACT_IDLE, ACT_IDLE_SMG1, true }, @@ -686,6 +738,27 @@ acttable_t CWeaponCrossbow::m_acttable[] = { 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 }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_CROSSBOW_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_CROSSBOW_MED, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_CROSSBOW, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrossbow); @@ -755,6 +828,10 @@ void CWeaponCrossbow::PrimaryAttack( void ) { m_iPrimaryAttacks++; gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); + +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif } } @@ -785,11 +862,11 @@ bool CWeaponCrossbow::Reload( void ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CWeaponCrossbow::Reload_NPC( void ) +void CWeaponCrossbow::Reload_NPC( bool bPlaySound ) { - BaseClass::Reload_NPC(); + BaseClass::Reload_NPC( bPlaySound ); - m_nSkin = 0; + SetBolt( 0 ); } #endif @@ -894,6 +971,10 @@ void CWeaponCrossbow::FireBolt( void ) m_iClip1--; +#ifdef MAPBASE + SetBolt( 1 ); +#endif + pOwner->ViewPunch( QAngle( -2, 0, 0 ) ); WeaponSound( SINGLE ); @@ -916,6 +997,18 @@ void CWeaponCrossbow::FireBolt( void ) } #ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets whether or not the bolt is visible +//----------------------------------------------------------------------------- +inline void CWeaponCrossbow::SetBolt( int iSetting ) +{ + int iBody = FindBodygroupByName( "bolt" ); + if (iBody != -1 /*|| (GetOwner() && GetOwner()->IsPlayer())*/) // TODO: Player models check the viewmodel instead of the worldmodel, but setting the bodygroup regardless can cause a crash, so we need a better solution + SetBodygroup( iBody, iSetting ); + else + m_nSkin = iSetting; +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -939,7 +1032,7 @@ void CWeaponCrossbow::FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, m_iClip1--; - m_nSkin = 1; + SetBolt( 1 ); WeaponSound( SINGLE_NPC ); WeaponSound( SPECIAL2 ); @@ -961,11 +1054,18 @@ bool CWeaponCrossbow::Deploy( void ) { if ( m_iClip1 <= 0 ) { +#ifdef MAPBASE + SetBolt( 1 ); +#endif return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() ); } SetSkin( BOLT_SKIN_GLOW ); +#ifdef MAPBASE + SetBolt( 0 ); +#endif + return BaseClass::Deploy(); } @@ -1113,6 +1213,10 @@ void CWeaponCrossbow::SetChargerState( ChargerState_t state ) // Shoot some sparks and draw a beam between the two outer points DoLoadEffect(); +#ifdef MAPBASE + SetBolt( 0 ); +#endif + break; case CHARGER_STATE_START_CHARGE: diff --git a/mp/src/game/server/hl2/weapon_crowbar.cpp b/mp/src/game/server/hl2/weapon_crowbar.cpp index fdbc7943..7cc34b9c 100644 --- a/mp/src/game/server/hl2/weapon_crowbar.cpp +++ b/mp/src/game/server/hl2/weapon_crowbar.cpp @@ -42,6 +42,29 @@ acttable_t CWeaponCrowbar::m_acttable[] = { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, + + { ACT_ARM, ACT_ARM_MELEE, false }, + { ACT_DISARM, ACT_DISARM_MELEE, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, + { 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 }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_MELEE, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrowbar); diff --git a/mp/src/game/server/hl2/weapon_crowbar.h b/mp/src/game/server/hl2/weapon_crowbar.h index 1890ef6e..26bb961f 100644 --- a/mp/src/game/server/hl2/weapon_crowbar.h +++ b/mp/src/game/server/hl2/weapon_crowbar.h @@ -46,6 +46,12 @@ public: // Animation event virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + // Don't use backup activities + acttable_t *GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } +#endif + private: // Animation event handlers void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); diff --git a/mp/src/game/server/hl2/weapon_frag.cpp b/mp/src/game/server/hl2/weapon_frag.cpp index b0e188aa..91eddcd3 100644 --- a/mp/src/game/server/hl2/weapon_frag.cpp +++ b/mp/src/game/server/hl2/weapon_frag.cpp @@ -82,6 +82,21 @@ END_DATADESC() acttable_t CWeaponFrag::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_GRENADE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponFrag); @@ -404,6 +419,10 @@ void CWeaponFrag::ThrowGrenade( CBasePlayer *pPlayer ) WeaponSound( SINGLE ); +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif + m_iPrimaryAttacks++; gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); } @@ -428,6 +447,10 @@ void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer ) WeaponSound( WPN_DOUBLE ); +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#endif + m_bRedraw = true; m_iPrimaryAttacks++; @@ -472,6 +495,10 @@ void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer ) WeaponSound( SPECIAL1 ); +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#endif + m_bRedraw = true; m_iPrimaryAttacks++; diff --git a/mp/src/game/server/hl2/weapon_physcannon.cpp b/mp/src/game/server/hl2/weapon_physcannon.cpp index 87fddf4e..2e985460 100644 --- a/mp/src/game/server/hl2/weapon_physcannon.cpp +++ b/mp/src/game/server/hl2/weapon_physcannon.cpp @@ -1238,6 +1238,9 @@ public: DECLARE_SERVERCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE + DECLARE_ACTTABLE(); +#endif CWeaponPhysCannon( void ); @@ -1455,6 +1458,30 @@ BEGIN_DATADESC( CWeaponPhysCannon ) END_DATADESC() +#ifdef MAPBASE +acttable_t CWeaponPhysCannon::m_acttable[] = +{ + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_PHYSGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_PHYSGUN, false }, +#endif + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponPhysCannon ); +#endif + enum { @@ -1738,6 +1765,9 @@ void CWeaponPhysCannon::DryFire( void ) if ( pOwner ) { pOwner->RumbleEffect( RUMBLE_PISTOL, 0, RUMBLE_FLAG_RESTART ); +#ifdef MAPBASE // TODO: Is this animation too dramatic? + pOwner->SetAnimation( PLAYER_ATTACK1 ); +#endif } } @@ -1794,6 +1824,11 @@ void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &for PrimaryFireEffect(); SendWeaponAnim( ACT_VM_SECONDARYATTACK ); +#ifdef MAPBASE + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if (pPlayer) + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif m_nChangeState = ELEMENT_STATE_CLOSED; m_flElementDebounce = gpGlobals->curtime + 0.5f; @@ -1944,6 +1979,10 @@ void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecFor PrimaryFireEffect(); SendWeaponAnim( ACT_VM_SECONDARYATTACK ); +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_ATTACK1 ); +#endif + m_nChangeState = ELEMENT_STATE_CLOSED; m_flElementDebounce = gpGlobals->curtime + 0.5f; m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; @@ -2062,6 +2101,10 @@ void CWeaponPhysCannon::PuntRagdoll( CBaseEntity *pEntity, const Vector &vecForw PrimaryFireEffect(); SendWeaponAnim( ACT_VM_SECONDARYATTACK ); +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_ATTACK1 ); +#endif + m_nChangeState = ELEMENT_STATE_CLOSED; m_flElementDebounce = gpGlobals->curtime + 0.5f; m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; @@ -2167,6 +2210,9 @@ void CWeaponPhysCannon::PrimaryAttack( void ) PrimaryFireEffect(); SendWeaponAnim( ACT_VM_SECONDARYATTACK ); +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_ATTACK1 ); +#endif return; } diff --git a/mp/src/game/server/hl2/weapon_pistol.cpp b/mp/src/game/server/hl2/weapon_pistol.cpp index 80bb5090..9819f748 100644 --- a/mp/src/game/server/hl2/weapon_pistol.cpp +++ b/mp/src/game/server/hl2/weapon_pistol.cpp @@ -108,6 +108,12 @@ public: return 0.5f; } +#ifdef MAPBASE + // Pistols are their own backup activities + virtual acttable_t *GetBackupActivityList() { return NULL; } + virtual int GetBackupActivityListCount() { return 0; } +#endif + DECLARE_ACTTABLE(); private: @@ -149,11 +155,141 @@ acttable_t CWeaponPistol::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, { ACT_WALK, ACT_WALK_PISTOL, false }, { ACT_RUN, ACT_RUN_PISTOL, false }, + +#ifdef MAPBASE + // + // Activities ported from weapon_alyxgun below + // + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_PISTOL_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities +#else + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + + { ACT_RUN_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities +#endif + + // Crouch activities + { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, + { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + + // Readiness translations + { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false }, + { ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false }, + { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false }, + { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false }, +#endif + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_PISTOL, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_PISTOL, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_PISTOL, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_PISTOL, true }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_PISTOL_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_PISTOL_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_PISTOL, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_PISTOL, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_PISTOL, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_PISTOL, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE( CWeaponPistol ); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the pistol's activity table. +acttable_t *GetPistolActtable() +{ + return CWeaponPistol::m_acttable; +} + +int GetPistolActtableCount() +{ + return ARRAYSIZE(CWeaponPistol::m_acttable); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/hl2/weapon_rpg.cpp b/mp/src/game/server/hl2/weapon_rpg.cpp index 3ae41e28..1ffc0326 100644 --- a/mp/src/game/server/hl2/weapon_rpg.cpp +++ b/mp/src/game/server/hl2/weapon_rpg.cpp @@ -1399,10 +1399,22 @@ PRECACHE_WEAPON_REGISTER(weapon_rpg); acttable_t CWeaponRPG::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_RPG_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_RPG_LOW, false }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_RPG, false }, +#endif +#ifdef MAPBASE + // Readiness activities should not be required + { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, false }, + { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, false }, +#else { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true }, { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true }, { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, true }, +#endif { ACT_IDLE, ACT_IDLE_RPG, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_RPG, true }, @@ -1411,6 +1423,31 @@ acttable_t CWeaponRPG::m_acttable[] = { ACT_RUN, ACT_RUN_RPG, true }, { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true }, { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true }, + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RPG, false }, + { ACT_DISARM, ACT_DISARM_RPG, false }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_RPG_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_RPG_MED, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_RPG, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_RPG, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_RPG, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_RPG, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_RPG, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_RPG, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_RPG, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponRPG); @@ -1559,6 +1596,54 @@ void CWeaponRPG::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatChara } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponRPG::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + if ( m_hMissile != NULL ) + return; + + Vector muzzlePoint, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), muzzlePoint, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + + // look for a better launch location + Vector altLaunchPoint; + if (GetAttachment( "missile", altLaunchPoint )) + { + // check to see if it's relativly free + trace_t tr; + AI_TraceHull( altLaunchPoint, altLaunchPoint + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); + + if( tr.fraction == 1.0) + { + muzzlePoint = altLaunchPoint; + } + } + + m_hMissile = CMissile::Create( muzzlePoint, angShootDir, pOperator->edict() ); + m_hMissile->m_hOwner = this; + + // NPCs always get a grace period + m_hMissile->SetGracePeriod( 0.5 ); + + pOperator->DoMuzzleFlash(); + + WeaponSound( SINGLE_NPC ); + + // Make sure our laserdot is off + m_bGuiding = false; + + if ( m_hLaserDot ) + { + m_hLaserDot->TurnOff(); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1634,6 +1719,10 @@ void CWeaponRPG::PrimaryAttack( void ) SendWeaponAnim( ACT_VM_PRIMARYATTACK ); WeaponSound( SINGLE ); + +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_ATTACK1 ); +#endif pOwner->RumbleEffect( RUMBLE_SHOTGUN_SINGLE, 0, RUMBLE_FLAG_RESTART ); diff --git a/mp/src/game/server/hl2/weapon_rpg.h b/mp/src/game/server/hl2/weapon_rpg.h index 3c9dae5f..a536d1c2 100644 --- a/mp/src/game/server/hl2/weapon_rpg.h +++ b/mp/src/game/server/hl2/weapon_rpg.h @@ -216,6 +216,9 @@ public: int WeaponRangeAttack1Condition( float flDot, float flDist ); void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); +#endif void StartGuiding( void ); void StopGuiding( void ); void ToggleGuiding( void ); diff --git a/mp/src/game/server/hl2/weapon_shotgun.cpp b/mp/src/game/server/hl2/weapon_shotgun.cpp index fb9ec215..5348e08a 100644 --- a/mp/src/game/server/hl2/weapon_shotgun.cpp +++ b/mp/src/game/server/hl2/weapon_shotgun.cpp @@ -108,6 +108,57 @@ END_DATADESC() acttable_t CWeaponShotgun::m_acttable[] = { +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Note that ACT_IDLE_SHOTGUN_AGITATED seems to be a stand-in for ACT_IDLE_SHOTGUN on citizens, + // but that isn't acceptable for NPCs which don't use readiness activities. + { ACT_IDLE, ACT_IDLE_SHOTGUN, true }, + + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RELOAD, ACT_RELOAD_SHOTGUN, false }, + { ACT_WALK, ACT_WALK_SHOTGUN, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_SHOTGUN_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_SHOTGUN_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_SHOTGUN_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_SHOTGUN_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_SHOTGUN, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, 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_SHOTGUN, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, + { ACT_COVER_LOW, ACT_COVER_SHOTGUN_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SHOTGUN_LOW, false }, +#else { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to shotgun unique { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, @@ -153,10 +204,49 @@ acttable_t CWeaponShotgun::m_acttable[] = { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, +#endif + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_SHOTGUN, true }, + { ACT_DISARM, ACT_DISARM_SHOTGUN, true }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SHOTGUN_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SHOTGUN_MED, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SHOTGUN, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SHOTGUN, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SHOTGUN, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SHOTGUN, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SHOTGUN, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponShotgun); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the shotgun's activity table. +acttable_t *GetShotgunActtable() +{ + return CWeaponShotgun::m_acttable; +} + +int GetShotgunActtableCount() +{ + return ARRAYSIZE(CWeaponShotgun::m_acttable); +} +#endif + void CWeaponShotgun::Precache( void ) { CBaseCombatWeapon::Precache(); @@ -316,6 +406,13 @@ bool CWeaponShotgun::StartReload( void ) pOwner->m_flNextAttack = gpGlobals->curtime; m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#ifdef MAPBASE + if ( pOwner->IsPlayer() ) + { + static_cast(pOwner)->SetAnimation( PLAYER_RELOAD ); + } +#endif + m_bInReload = true; return true; } @@ -469,7 +566,11 @@ void CWeaponShotgun::PrimaryAttack( void ) pPlayer->SetAnimation( PLAYER_ATTACK1 ); // Don't fire again until fire animation has completed +#ifdef MAPBASE + m_flNextPrimaryAttack = gpGlobals->curtime + GetViewModelSequenceDuration(); +#else m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#endif m_iClip1 -= 1; Vector vecSrc = pPlayer->Weapon_ShootPosition( ); @@ -524,10 +625,18 @@ void CWeaponShotgun::SecondaryAttack( void ) SendWeaponAnim( ACT_VM_SECONDARYATTACK ); // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif // Don't fire again until fire animation has completed +#ifdef MAPBASE + m_flNextPrimaryAttack = gpGlobals->curtime + GetViewModelSequenceDuration(); +#else m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#endif m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks Vector vecSrc = pPlayer->Weapon_ShootPosition(); diff --git a/mp/src/game/server/hl2/weapon_smg1.cpp b/mp/src/game/server/hl2/weapon_smg1.cpp index 97b0e89e..b11fbedc 100644 --- a/mp/src/game/server/hl2/weapon_smg1.cpp +++ b/mp/src/game/server/hl2/weapon_smg1.cpp @@ -134,6 +134,36 @@ acttable_t CWeaponSMG1::m_acttable[] = { 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 }, + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SMG1_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SMG1_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SMG1, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SMG1, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SMG1, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SMG1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SMG1, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SMG1, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SMG1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponSMG1); @@ -439,7 +469,11 @@ void CWeaponSMG1::SecondaryAttack( void ) CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON ); // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif // Decrease ammo pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType ); diff --git a/mp/src/game/server/hl2/weapon_smg2.cpp b/mp/src/game/server/hl2/weapon_smg2.cpp index 498faddd..9c5037b9 100644 --- a/mp/src/game/server/hl2/weapon_smg2.cpp +++ b/mp/src/game/server/hl2/weapon_smg2.cpp @@ -46,6 +46,77 @@ PRECACHE_WEAPON_REGISTER(weapon_smg2); acttable_t CWeaponSMG2::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG2, true }, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Optional new NPC activities + // (these should fall back to SMG animations when they don't exist on an NPC) + { ACT_RELOAD, ACT_RELOAD_SMG2, true }, + { ACT_IDLE, ACT_IDLE_SMG2, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG2, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SMG2_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SMG2_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG2, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_SMG2_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_SMG2_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_SMG2, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_SMG2_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_SMG2_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_SMG2, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG2_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_SMG2_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG2, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_SMG2_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_SMG2_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_SMG2, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_SMG2_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_SMG2_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_SMG2, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_SMG2, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_SMG2, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_SMG2, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_SMG2, 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_SMG2, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG2_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_SMG2_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG2_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_SMG2_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG2, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SMG2_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SMG2_MED, false }, +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SMG2, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SMG2, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SMG2, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SMG2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SMG2, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SMG2, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SMG2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponSMG2); diff --git a/mp/src/game/server/hl2/weapon_sniperrifle.cpp b/mp/src/game/server/hl2/weapon_sniperrifle.cpp index 901a73ae..89887507 100644 --- a/mp/src/game/server/hl2/weapon_sniperrifle.cpp +++ b/mp/src/game/server/hl2/weapon_sniperrifle.cpp @@ -46,6 +46,10 @@ static int g_nZoomFOV[] = 5 }; +#ifdef MAPBASE +extern acttable_t *GetAR2Acttable(); +extern int GetAR2ActtableCount(); +#endif class CWeaponSniperRifle : public CBaseHLCombatWeapon { @@ -72,6 +76,11 @@ public: void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + virtual acttable_t *GetBackupActivityList() { return GetAR2Acttable(); } + virtual int GetBackupActivityListCount() { return GetAR2ActtableCount(); } +#endif + DECLARE_ACTTABLE(); protected: @@ -98,7 +107,78 @@ END_DATADESC() //----------------------------------------------------------------------------- acttable_t CWeaponSniperRifle::m_acttable[] = { - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SNIPER_RIFLE, true } + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SNIPER_RIFLE, true }, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Optional new NPC activities + // (these should fall back to AR2 animations when they don't exist on an NPC) + { ACT_RELOAD, ACT_RELOAD_SNIPER_RIFLE, true }, + { ACT_IDLE, ACT_IDLE_SNIPER_RIFLE, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SNIPER_RIFLE, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SNIPER_RIFLE_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SNIPER_RIFLE_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SNIPER_RIFLE, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_SNIPER_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_SNIPER_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_SNIPER_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_SNIPER_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_SNIPER_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_SNIPER_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SNIPER_RIFLE_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_SNIPER_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SNIPER_RIFLE, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_SNIPER_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_SNIPER_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_SNIPER_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_SNIPER_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_SNIPER_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_SNIPER_RIFLE, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_SNIPER_RIFLE, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_SNIPER_RIFLE, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_SNIPER_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_SNIPER_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_SNIPER_RIFLE, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_SNIPER_RIFLE_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SNIPER_RIFLE_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_SNIPER_RIFLE_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SNIPER_RIFLE, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SNIPER_RIFLE_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SNIPER_RIFLE_MED, false }, +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SNIPER_RIFLE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SNIPER_RIFLE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SNIPER_RIFLE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SNIPER_RIFLE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SNIPER_RIFLE, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SNIPER_RIFLE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponSniperRifle); diff --git a/mp/src/game/server/hltvdirector.h b/mp/src/game/server/hltvdirector.h index 653bf1dd..1b390e83 100644 --- a/mp/src/game/server/hltvdirector.h +++ b/mp/src/game/server/hltvdirector.h @@ -67,7 +67,7 @@ public: // CBaseGameSystem overrides virtual void Shutdown(); virtual void FrameUpdatePostEntityThink(); virtual void LevelInitPostEntity(); - virtual char *GetFixedCameraEntityName( void ) { return "point_viewcontrol"; } + virtual const char *GetFixedCameraEntityName( void ) { return "point_viewcontrol"; } bool SetCameraMan( int iPlayerIndex ); int GetCameraMan() { return m_iCameraManIndex; } diff --git a/mp/src/game/server/items.h b/mp/src/game/server/items.h index ed40fa59..8321e5cc 100644 --- a/mp/src/game/server/items.h +++ b/mp/src/game/server/items.h @@ -89,9 +89,12 @@ public: #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; } + // This appeared to have no prior use in Source SDK 2013. + // It may have been originally intended for TF2 or some other game-specific item class. + virtual bool IsCombatItem() const { return true; } + + // Used to access item_healthkit values, etc. from outside of the class + virtual float GetItemAmount() { return 1.0f; } void InputEnablePlayerPickup( inputdata_t &inputdata ); void InputDisablePlayerPickup( inputdata_t &inputdata ); diff --git a/mp/src/game/server/logic_playmovie.cpp b/mp/src/game/server/logic_playmovie.cpp new file mode 100644 index 00000000..4e62f738 --- /dev/null +++ b/mp/src/game/server/logic_playmovie.cpp @@ -0,0 +1,136 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: Plays a movie and reports on finish +// +//===========================================================================// + +#include "cbase.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLogicPlayMovie : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicPlayMovie, CLogicalEntity ); + DECLARE_DATADESC(); + + CLogicPlayMovie( void ) { } + ~CLogicPlayMovie( void ) { } + + virtual void Precache( void ); + virtual void Spawn( void ); + +private: + + void InputPlayMovie( inputdata_t &data ); +#ifdef MAPBASE + void InputStopMovie( inputdata_t &data ); +#endif + void InputMovieFinished( inputdata_t &data ); + + string_t m_strMovieFilename; + bool m_bAllowUserSkip; +#ifdef MAPBASE + bool m_bLooping; + bool m_bMuted; + + bool m_bPlayingVideo; +#endif + + COutputEvent m_OnPlaybackFinished; +}; + +LINK_ENTITY_TO_CLASS( logic_playmovie, CLogicPlayMovie ); + +BEGIN_DATADESC( CLogicPlayMovie ) + + DEFINE_KEYFIELD( m_strMovieFilename, FIELD_STRING, "MovieFilename" ), + DEFINE_KEYFIELD( m_bAllowUserSkip, FIELD_BOOLEAN, "allowskip" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "loopvideo" ), + DEFINE_KEYFIELD( m_bMuted, FIELD_BOOLEAN, "mute" ), + + DEFINE_FIELD( m_bPlayingVideo, FIELD_BOOLEAN ), +#endif + + DEFINE_INPUTFUNC( FIELD_VOID, "PlayMovie", InputPlayMovie ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "StopMovie", InputStopMovie ), +#endif + DEFINE_INPUTFUNC( FIELD_VOID, "__MovieFinished", InputMovieFinished ), + + DEFINE_OUTPUT( m_OnPlaybackFinished, "OnPlaybackFinished" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::Precache( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::Spawn( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputPlayMovie( inputdata_t &data ) +{ + // Build the hacked string + + char szClientCmd[256]; + Q_snprintf( szClientCmd, sizeof(szClientCmd), + "playvideo_complex %s \"ent_fire %s __MovieFinished\" %d %d %d\n", + STRING(m_strMovieFilename), + GetEntityNameAsCStr(), + m_bAllowUserSkip, +#ifdef MAPBASE + m_bLooping, + m_bMuted +#else + 0, + 0 +#endif + ); + + // Send it on + engine->ServerCommand( szClientCmd ); + +#ifdef MAPBASE + m_bPlayingVideo = true; +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputStopMovie( inputdata_t &data ) +{ + if (m_bPlayingVideo) + { + // Send it on + engine->ServerCommand( "stopvideos\n" ); + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputMovieFinished( inputdata_t &data ) +{ + // Simply fire our output + m_OnPlaybackFinished.FireOutput( this, this ); + +#ifdef MAPBASE + m_bPlayingVideo = false; +#endif +} diff --git a/mp/src/game/server/logicentities.cpp b/mp/src/game/server/logicentities.cpp index 2197baff..aa838a35 100644 --- a/mp/src/game/server/logicentities.cpp +++ b/mp/src/game/server/logicentities.cpp @@ -4385,7 +4385,7 @@ void CMathClamp::ClampValue(variant_t var, inputdata_t *inputdata) { // Don't convert up here in case of invalid type - int nBounds; + int nBounds = 0; switch (var.FieldType()) { diff --git a/mp/src/game/server/mapbase/GlobalStrings.h b/mp/src/game/server/mapbase/GlobalStrings.h index 336ff622..13c4775a 100644 --- a/mp/src/game/server/mapbase/GlobalStrings.h +++ b/mp/src/game/server/mapbase/GlobalStrings.h @@ -18,13 +18,8 @@ // 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. +// This system was developed early in Mapbase's development as an attempt to make this technique more widely used. +// For the most part, this mainly just serves to apply micro-optimize parts of the code. // // ------------------------------------------------------------- @@ -79,7 +74,6 @@ inline bool EntIsClass( CBaseEntity *ent, string_t 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; } diff --git a/mp/src/game/server/mapbase/ai_grenade.cpp b/mp/src/game/server/mapbase/ai_grenade.cpp index e14b47a3..a9ff5302 100644 --- a/mp/src/game/server/mapbase/ai_grenade.cpp +++ b/mp/src/game/server/mapbase/ai_grenade.cpp @@ -11,3 +11,5 @@ int COMBINE_AE_BEGIN_ALTFIRE; int COMBINE_AE_ALTFIRE; + +ConVar ai_grenade_always_drop( "ai_grenade_always_drop", "0", FCVAR_NONE, "Causes non-Combine grenade user NPCs to be allowed to drop grenades, alt-fire items, etc. *IF* the keyvalue has not already been set in Hammer. This is useful for debugging purposes or if a mod which was developed before this feature was introduced wants its NPCs to drop grenades and beyond without recompiling all of the maps." ); diff --git a/mp/src/game/server/mapbase/ai_grenade.h b/mp/src/game/server/mapbase/ai_grenade.h index 7f1ea8c1..543969c2 100644 --- a/mp/src/game/server/mapbase/ai_grenade.h +++ b/mp/src/game/server/mapbase/ai_grenade.h @@ -19,6 +19,10 @@ #include "basegrenade_shared.h" #include "ai_squad.h" #include "GlobalStrings.h" +#include "gameweaponmanager.h" +#include "hl2_gamerules.h" +#include "weapon_physcannon.h" +#include "globalstate.h" #define COMBINE_AE_GREN_TOSS ( 7 ) @@ -36,7 +40,9 @@ DEFINE_FIELD( m_flNextAltFireTime, FIELD_TIME ), \ DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ), \ DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), \ + DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), \ DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), \ + DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeGestureAtTarget", InputThrowGrenadeGestureAtTarget ), \ DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGrenades", InputSetGrenades ), \ DEFINE_INPUTFUNC( FIELD_INTEGER, "AddGrenades", InputAddGrenades ), \ DEFINE_OUTPUT(m_OnThrowGrenade, "OnThrowGrenade"), \ @@ -61,12 +67,22 @@ extern int COMBINE_AE_BEGIN_ALTFIRE; extern int COMBINE_AE_ALTFIRE; +extern ConVar ai_grenade_always_drop; + enum eGrenadeCapabilities { GRENCAP_GRENADE = (1 << 0), GRENCAP_ALTFIRE = (1 << 1), }; +// What grenade/item types NPCs are capable of dropping +enum eGrenadeDropCapabilities +{ + GRENDROPCAP_GRENADE = (1 << 0), + GRENDROPCAP_ALTFIRE = (1 << 1), + GRENDROPCAP_INTERRUPTED = (1 << 2), // Drops grenades when interrupted mid-animation +}; + //----------------------------------------------------------------------------- // Other classes can use this and access some CAI_GrenadeUser functions. //----------------------------------------------------------------------------- @@ -102,17 +118,20 @@ public: m_OnOutOfGrenades.Set( pLastGrenade, pLastGrenade, this ); } - virtual bool IsAltFireCapable() { return false; } + // Use secondary ammo as a way of checking if this is a weapon which can be alt-fired (e.g. AR2 or SMG) + virtual bool IsAltFireCapable() { return (this->GetActiveWeapon() && this->GetActiveWeapon()->UsesSecondaryAmmo()); } 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 ); + void InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata ); virtual void DelayGrenadeCheck( float delay ) { m_flNextGrenadeCheck = gpGlobals->curtime + delay; } void HandleAnimEvent( animevent_t *pEvent ); + void SetActivity( Activity NewActivity ); // Soldiers use "lefthand", cops use "LHand", and citizens use "anim_attachment_LH" virtual const char* GetGrenadeAttachment() { return "anim_attachment_LH"; } @@ -131,6 +150,12 @@ public: // For OnThrowGrenade + point_entity_replace, see grenade_frag.cpp bool UsingOnThrowGrenade() { return m_OnThrowGrenade.NumberOfElements() > 0; } + // For dropping grenades and beyond + void DropGrenadeItemsOnDeath( const CTakeDamageInfo &info, CBasePlayer *pPlayer ); + virtual bool ShouldDropGrenades() { return HasGrenades(); } + virtual bool ShouldDropInterruptedGrenades() { return true; } + virtual bool ShouldDropAltFire() { return HasGrenades(); } + protected: void StartTask_FaceAltFireTarget( const Task_t *pTask ); @@ -151,6 +176,9 @@ protected: // We can't have any private saved variables because only derived cla Vector m_vecAltFireTarget; Vector m_vecTossVelocity; + // CNPC_Combine port for determining if we tossed a grenade + int m_iLastAnimEventHandled; + COutputEHANDLE m_OnThrowGrenade; COutputEHANDLE m_OnOutOfGrenades; }; @@ -166,6 +194,8 @@ void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) if (this->GetActiveWeapon()) this->GetActiveWeapon()->WeaponSound( SPECIAL1 ); + m_iLastAnimEventHandled = pEvent->event; + //SpeakIfAllowed( TLK_CMB_THROWGRENADE, "altfire:1" ); return; } @@ -185,6 +215,8 @@ void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) AddGrenades(-1); + m_iLastAnimEventHandled = pEvent->event; + return; } @@ -221,12 +253,25 @@ void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) // wait six seconds before even looking again to see if a grenade can be thrown. m_flNextGrenadeCheck = gpGlobals->curtime + 6; + + m_iLastAnimEventHandled = pEvent->event; + return; } BaseClass::HandleAnimEvent( pEvent ); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::SetActivity( Activity NewActivity ) +{ + BaseClass::SetActivity( NewActivity ); + + m_iLastAnimEventHandled = -1; +} + //----------------------------------------------------------------------------- // 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. @@ -252,6 +297,63 @@ void CAI_GrenadeUser::InputThrowGrenadeAtTarget( inputdata_t &inputdat this->ClearSchedule( "Told to throw grenade via input" ); } +//----------------------------------------------------------------------------- +// Purpose: Force the combine soldier to throw a grenade at the target using the gesture animation. +// If I'm a combine elite, fire my combine ball at the target instead. +// Input : &inputdata - +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::InputThrowGrenadeGestureAtTarget( 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 ThrowGrenadeGestureAtTarget input, but couldn't find target entity '%s'\n", this->GetClassname(), this->GetDebugName(), inputdata.value.String() ); + return; + } + + m_hForcedGrenadeTarget = pEntity; + m_flNextGrenadeCheck = 0; + + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + +#if SHARED_COMBINE_ACTIVITIES + if (IsAltFireCapable()) + { + if (this->FVisible( m_hForcedGrenadeTarget )) + { + m_vecAltFireTarget = vecTarget; + m_hForcedGrenadeTarget = NULL; + + int iLayer = this->AddGesture( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + if (iLayer != -1) + { + this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) ); + } + } + } + else + { + // If we can, throw a grenade at the target. + // Ignore grenade count / distance / etc + if (CheckCanThrowGrenade( vecTarget )) + { + int iLayer = this->AddGesture( ACT_GESTURE_COMBINE_THROW_GRENADE ); + if (iLayer != -1) + { + this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) ); + } + } + } +#else + Warning("Gesture grenades/alt-fire not supported\n"); +#endif +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template @@ -534,6 +636,105 @@ void CAI_GrenadeUser::ClearAttackConditions() } } +//----------------------------------------------------------------------------- +// Purpose: Drops grenades and alt-fire items on death. Based on code from npc_combines.cpp and npc_combine.cpp +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::DropGrenadeItemsOnDeath( const CTakeDamageInfo &info, CBasePlayer *pPlayer ) +{ + // Elites drop alt-fire ammo, so long as they weren't killed by dissolving. + if( IsAltFireCapable() && ShouldDropAltFire() ) + { + CBaseEntity *pItem; + if (this->GetActiveWeapon() && FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" )) + pItem = this->DropItem( "item_ammo_smg1_grenade", this->WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + else + pItem = this->DropItem( "item_ammo_ar2_altfire", this->WorldSpaceCenter() + RandomVector( -4, 4 ), RandomAngle( 0, 360 ) ); + + if ( pItem ) + { + IPhysicsObject *pObj = pItem->VPhysicsGetObject(); + + if ( pObj ) + { + Vector vel = RandomVector( -64.0f, 64.0f ); + AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); + + vel[2] = 0.0f; + pObj->AddVelocity( &vel, &angImp ); + } + + if( info.GetDamageType() & DMG_DISSOLVE ) + { + CBaseAnimating *pAnimating = dynamic_cast(pItem); + + if( pAnimating ) + { + pAnimating->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + else + { + WeaponManager_AddManaged( pItem ); + } + } + } + + if ( IsGrenadeCapable() ) + { + if ( ShouldDropGrenades() ) + { + CHalfLife2 *pHL2GameRules = static_cast(g_pGameRules); + + // Attempt to drop a grenade + if ( pHL2GameRules->NPC_ShouldDropGrenade( pPlayer ) ) + { + this->DropItem( "weapon_frag", this->WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + pHL2GameRules->NPC_DroppedGrenade(); + } + } + + // if I was killed before I could finish throwing my grenade, drop + // a grenade item that the player can retrieve. + if (this->GetActivity() == ACT_RANGE_ATTACK2 && ShouldDropInterruptedGrenades()) + { + if( m_iLastAnimEventHandled != COMBINE_AE_GREN_TOSS ) + { + // Drop the grenade as an item. + Vector vecStart; + this->GetAttachment( GetGrenadeAttachment(), vecStart ); + + CBaseEntity *pItem = this->DropItem( "weapon_frag", vecStart, RandomAngle(0,360) ); + + if ( pItem ) + { + IPhysicsObject *pObj = pItem->VPhysicsGetObject(); + + if ( pObj ) + { + Vector vel; + vel.x = random->RandomFloat( -100.0f, 100.0f ); + vel.y = random->RandomFloat( -100.0f, 100.0f ); + vel.z = random->RandomFloat( 800.0f, 1200.0f ); + AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); + + vel[2] = 0.0f; + pObj->AddVelocity( &vel, &angImp ); + } + + // In the Citadel we need to dissolve this + if ( PlayerHasMegaPhysCannon() && GlobalEntity_GetCounter("super_phys_gun") != 1 ) + { + CBaseCombatWeapon *pWeapon = static_cast(pItem); + + pWeapon->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + } + } + } +} + //----------------------------------------------------------------------------- // Purpose: Task helpers //----------------------------------------------------------------------------- diff --git a/mp/src/game/server/mapbase/ai_monitor.cpp b/mp/src/game/server/mapbase/ai_monitor.cpp index 754ff56d..fa11a203 100644 --- a/mp/src/game/server/mapbase/ai_monitor.cpp +++ b/mp/src/game/server/mapbase/ai_monitor.cpp @@ -672,6 +672,33 @@ int CAI_Monitor::TranslateScheduleString(const char *schedName) return 0; } +template +static void SetForEachDelimited( CAI_Monitor &monitor, const char *szValue, const char *delimiters, void (CAI_Monitor::*setter)(int), Translator translator) +{ + char *value = strdup(szValue); + char *token = strtok(value, ":"); + while (token) + { + (monitor.*setter)(translator(token)); + + token = strtok(NULL, ":"); + } + free(value); +} + +template +struct CAI_MonitorTranslator +{ + CAI_Monitor &monitor; + + CAI_MonitorTranslator(CAI_Monitor &monitor) : monitor(monitor) {} + + int operator()(const char *value) + { + return (monitor.*translator)(value); + } +}; + //----------------------------------------------------------------------------- // Purpose: Cache user entity field values until spawn is called. // Input : szKeyName - Key to handle. @@ -688,13 +715,7 @@ bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue ) } else if (FStrEq(szKeyName, "Conditions")) { - char *token = strtok(strdup(szValue), ":"); - while (token) - { - SetCondition(TranslateConditionString(token)); - - token = strtok(NULL, ":"); - } + SetForEachDelimited(*this, szValue, ":", &CAI_Monitor::SetCondition, CAI_MonitorTranslator<&CAI_Monitor::TranslateConditionString>(*this)); } else if (FStrEq(szKeyName, "SchedulesSimple")) { @@ -703,13 +724,7 @@ bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue ) } else if (FStrEq(szKeyName, "Schedules")) { - char *token = strtok(strdup(szValue), ":"); - while (token) - { - SetSchedule(TranslateScheduleString(token)); - - token = strtok(NULL, ":"); - } + SetForEachDelimited(*this, szValue, ":", &CAI_Monitor::SetSchedule, CAI_MonitorTranslator<&CAI_Monitor::TranslateScheduleString>(*this)); } else if (FStrEq(szKeyName, "HintsSimple")) { @@ -718,13 +733,7 @@ bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue ) } else if (FStrEq(szKeyName, "Hints")) { - char *token = strtok(strdup(szValue), ":"); - while (token) - { - SetHint(atoi(szValue)); - - token = strtok(NULL, ":"); - } + SetForEachDelimited(*this, szValue, ":", &CAI_Monitor::SetHint, atoi); } else return CBaseEntity::KeyValue( szKeyName, szValue ); diff --git a/mp/src/game/server/mapbase/func_fake_worldportal.cpp b/mp/src/game/server/mapbase/func_fake_worldportal.cpp index 3184d2fa..6430d5df 100644 --- a/mp/src/game/server/mapbase/func_fake_worldportal.cpp +++ b/mp/src/game/server/mapbase/func_fake_worldportal.cpp @@ -1,6 +1,6 @@ //========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // -// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// Purpose: Recreates Portal 2 linked_portal_door visual functionality using SDK code only. // (basically a combination of point_camera and func_reflective_glass) // //===========================================================================// @@ -54,6 +54,7 @@ public: 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(); } } + void InputSetScale( inputdata_t &inputdata ) { m_flScale = inputdata.value.Float(); } private: @@ -83,6 +84,7 @@ BEGIN_DATADESC( CFuncFakeWorldPortal ) DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSkyMode", InputSetSkyMode ), DEFINE_INPUTFUNC( FIELD_STRING, "SetRenderTarget", InputSetRenderTarget ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetFogController", InputSetFogController ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetScale", InputSetScale ), END_DATADESC() diff --git a/mp/src/game/server/mapbase/logic_externaldata.cpp b/mp/src/game/server/mapbase/logic_externaldata.cpp index b28189f7..588bb6c7 100644 --- a/mp/src/game/server/mapbase/logic_externaldata.cpp +++ b/mp/src/game/server/mapbase/logic_externaldata.cpp @@ -96,7 +96,7 @@ BEGIN_ENT_SCRIPTDESC( CLogicExternalData, CBaseEntity, "An entity which loads ke 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_NAMED( ScriptSetKeyValueBlock, "SetKeyValueBlock", "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." ) @@ -199,7 +199,7 @@ void CLogicExternalData::InputWriteKeyValue( inputdata_t &inputdata ) // Separate key from value char *delimiter = Q_strstr(szValue, " "); - if (delimiter && (delimiter + 1) != '\0') + if (delimiter && delimiter[1] != '\0') { Q_strncpy(key, szValue, MIN((delimiter - szValue) + 1, sizeof(key))); Q_strncpy(value, delimiter + 1, sizeof(value)); diff --git a/mp/src/game/server/mapbase/logic_substring.cpp b/mp/src/game/server/mapbase/logic_substring.cpp new file mode 100644 index 00000000..7c7d153b --- /dev/null +++ b/mp/src/game/server/mapbase/logic_substring.cpp @@ -0,0 +1,110 @@ +//====================== By Holly Liberatore / MoofEMP ======================// +// +// Purpose: Takes a string parameter and returns a substring defined by keyvalues +// +//===========================================================================// + +#include "cbase.h" + +#define SF_SUBSTRING_START_DISABLED (1 << 0) + +class CLogicSubstring : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicSubstring, CLogicalEntity ); + DECLARE_DATADESC(); + + CLogicSubstring( void ) { } + + void InputDisable( inputdata_t &inputData ); + void InputEnable( inputdata_t &inputData ); + void InputInValue( inputdata_t &inputData ); + void InputSetLength( inputdata_t &inputData ); + void InputSetStartPos( inputdata_t &inputData ); + + void Spawn(void); + +private: + int m_nLength; + int m_nStartPos; + + bool m_bEnabled; + + COutputString m_OutValue; +}; + +LINK_ENTITY_TO_CLASS( logic_substring, CLogicSubstring ); + +BEGIN_DATADESC( CLogicSubstring ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_KEYFIELD(m_nLength, FIELD_INTEGER, "length" ), + DEFINE_KEYFIELD(m_nStartPos, FIELD_INTEGER, "startPos" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_STRING, "InValue", InputInValue ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetLength", InputSetLength ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetStartPos", InputSetStartPos ), + + DEFINE_OUTPUT( m_OutValue, "OutValue" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Disable or enable the entity (disabling prevents any input functions from running) +//----------------------------------------------------------------------------- +void CLogicSubstring::InputDisable( inputdata_t &inputData ) { m_bEnabled = false; } +void CLogicSubstring::InputEnable ( inputdata_t &inputData ) { m_bEnabled = true ; } + +//----------------------------------------------------------------------------- +// Purpose: Trim substring from input +// Output: Substring +//----------------------------------------------------------------------------- +void CLogicSubstring::InputInValue( inputdata_t &inputData ) +{ + if( !m_bEnabled ) return; + + int inputLength = Q_strlen(inputData.value.String()); + int startPosCheck = m_nStartPos < 0 ? inputLength + m_nStartPos : m_nStartPos; + if( startPosCheck < 0 ) + { + startPosCheck = 0; + } + int lengthCheck = (m_nLength < 0 || m_nLength > inputLength - startPosCheck ? inputLength - startPosCheck : m_nLength) + 1; + if( lengthCheck < 1 || startPosCheck > inputLength ) + { + m_OutValue.Set( MAKE_STRING(""), inputData.pActivator, this ); + return; + } + char* strOutValue = (char*)malloc( lengthCheck ); + Q_strncpy( strOutValue, inputData.value.String() + startPosCheck, lengthCheck ); + m_OutValue.Set( AllocPooledString(strOutValue), inputData.pActivator, this ); + free(strOutValue); +} + +//----------------------------------------------------------------------------- +// Purpose: Setter methods for keyvalues +//----------------------------------------------------------------------------- +void CLogicSubstring::InputSetLength( inputdata_t &inputData ) +{ + if( !m_bEnabled ) return; + + m_nLength = inputData.value.Int(); +} + +void CLogicSubstring::InputSetStartPos( inputdata_t &inputData ) +{ + if( !m_bEnabled ) return; + + m_nStartPos = inputData.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to spawnflags when entity spawns +//----------------------------------------------------------------------------- +void CLogicSubstring::Spawn( void ) +{ + m_bEnabled = !HasSpawnFlags( SF_SUBSTRING_START_DISABLED ); +} diff --git a/mp/src/game/server/mapbase/vgui_text_display.cpp b/mp/src/game/server/mapbase/vgui_text_display.cpp new file mode 100644 index 00000000..84e0ac63 --- /dev/null +++ b/mp/src/game/server/mapbase/vgui_text_display.cpp @@ -0,0 +1,437 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Displays easy, flexible VGui text. Mapbase equivalent of point_worldtext. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "vguiscreen.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define SF_TESTDISPLAY_START_DISABLED (1 << 0) + +//----------------------------------------------------------------------------- +// vgui_text_display +//----------------------------------------------------------------------------- +class CVGuiTextDisplay : public CBaseEntity +{ +public: + + DECLARE_CLASS( CVGuiTextDisplay, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CVGuiTextDisplay(); + virtual ~CVGuiTextDisplay(); + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + virtual int UpdateTransmitState(); + virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); + + virtual void Spawn( void ); + virtual void Precache( void ); + virtual void OnRestore( void ); + + void ScreenVisible( bool bVisible ); + + void Disable( void ); + void Enable( void ); + + void InputDisable( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + void InputSetMessage( inputdata_t &inputdata ); + void InputSetTextAlignment( inputdata_t &inputdata ); + void InputSetFont( inputdata_t &inputdata ); + void InputSetResolution( inputdata_t &inputdata ); + void InputSetTextSize( inputdata_t &inputdata ); + +private: + + // Control panel + void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ); + void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ); + void SpawnControlPanels( void ); + void RestoreControlPanels( void ); + +private: + CNetworkVar( bool, m_bEnabled ); + + CNetworkString( m_szDisplayText, 256 ); + CNetworkVar( int, m_iContentAlignment ); + CNetworkString( m_szFont, 64 ); + CNetworkVar( int, m_iResolution ); + float m_flTextSize; + + //CNetworkColor32( m_DisplayColor ); // Use render color + + bool m_bDoFullTransmit; + + CHandle m_hScreen; +}; + +LINK_ENTITY_TO_CLASS( vgui_text_display, CVGuiTextDisplay ); + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- +BEGIN_DATADESC( CVGuiTextDisplay ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_AUTO_ARRAY_KEYFIELD( m_szDisplayText, FIELD_CHARACTER, "message" ), + DEFINE_KEYFIELD( m_iContentAlignment, FIELD_INTEGER, "alignment" ), + DEFINE_AUTO_ARRAY_KEYFIELD( m_szFont, FIELD_CHARACTER, "font" ), + DEFINE_KEYFIELD( m_iResolution, FIELD_INTEGER, "resolution" ), + DEFINE_KEYFIELD( m_flTextSize, FIELD_FLOAT, "textsize" ), + + DEFINE_FIELD( m_bDoFullTransmit, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_hScreen, FIELD_EHANDLE ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetMessage", InputSetMessage ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetTextAlignment", InputSetTextAlignment ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFont", InputSetFont ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetResolution", InputSetResolution ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPanelSize", InputSetTextSize ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CVGuiTextDisplay, DT_VGuiTextDisplay ) + SendPropBool( SENDINFO( m_bEnabled ) ), + SendPropString( SENDINFO( m_szDisplayText ) ), + SendPropInt( SENDINFO( m_iContentAlignment ) ), + SendPropString( SENDINFO( m_szFont ) ), + SendPropInt( SENDINFO( m_iResolution ) ), +END_SEND_TABLE() + +CVGuiTextDisplay::CVGuiTextDisplay() +{ + m_flTextSize = 100.0f; + m_iResolution = 200; + m_iContentAlignment = 7; // a_south +} + +CVGuiTextDisplay::~CVGuiTextDisplay() +{ + DestroyVGuiScreen( m_hScreen.Get() ); +} + +//----------------------------------------------------------------------------- +// Read in Hammer data +//----------------------------------------------------------------------------- +bool CVGuiTextDisplay::KeyValue( const char *szKeyName, const char *szValue ) +{ + // NOTE: Have to do these separate because they set two values instead of one + if( FStrEq( szKeyName, "angles" ) ) + { + Assert( GetMoveParent() == NULL ); + QAngle angles; + UTIL_StringToVector( angles.Base(), szValue ); + + // Because the vgui screen basis is strange (z is front, y is up, x is right) + // we need to rotate the typical basis before applying it + VMatrix mat, rotation, tmp; + MatrixFromAngles( angles, mat ); + MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 ); + MatrixMultiply( mat, rotation, tmp ); + MatrixBuildRotateZ( rotation, 90 ); + MatrixMultiply( tmp, rotation, mat ); + MatrixToAngles( mat, angles ); + SetAbsAngles( angles ); + } + else if( FStrEq( szKeyName, "message" ) ) + { + Q_strcpy( m_szDisplayText.GetForModify(), szValue ); + } + else if( FStrEq( szKeyName, "font" ) ) + { + Q_strcpy( m_szFont.GetForModify(), szValue ); + } + else if( FStrEq( szKeyName, "color" ) ) + { + // Use render color + return BaseClass::KeyValue( "rendercolor", szValue ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CVGuiTextDisplay::UpdateTransmitState() +{ + if ( m_bDoFullTransmit ) + { + m_bDoFullTransmit = false; + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + return SetTransmitState( FL_EDICT_FULLCHECK ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) +{ + // Are we already marked for transmission? + if ( pInfo->m_pTransmitEdict->Get( entindex() ) ) + return; + + BaseClass::SetTransmit( pInfo, bAlways ); + + // Force our screen to be sent too. + m_hScreen->SetTransmit( pInfo, bAlways ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Spawn( void ) +{ + Precache(); + + BaseClass::Spawn(); + + m_bEnabled = !HasSpawnFlags( SF_TESTDISPLAY_START_DISABLED ); + + SpawnControlPanels(); + + ScreenVisible( m_bEnabled ); + + m_bDoFullTransmit = true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheVGuiScreen( "text_display_panel" ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::OnRestore( void ) +{ + BaseClass::OnRestore(); + + RestoreControlPanels(); + + ScreenVisible( m_bEnabled ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::ScreenVisible( bool bVisible ) +{ + // Set its active state + m_hScreen->SetActive( bVisible ); + + if ( bVisible ) + { + m_hScreen->RemoveEffects( EF_NODRAW ); + } + else + { + m_hScreen->AddEffects( EF_NODRAW ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Disable( void ) +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + + ScreenVisible( false ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Enable( void ) +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + + ScreenVisible( true ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputToggle( inputdata_t &inputdata ) +{ + m_bEnabled ? Disable() : Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetMessage( inputdata_t &inputdata ) +{ + Q_strcpy( m_szDisplayText.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetTextAlignment( inputdata_t &inputdata ) +{ + m_iContentAlignment = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetFont( inputdata_t &inputdata ) +{ + Q_strcpy( m_szFont.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetResolution( inputdata_t &inputdata ) +{ + m_iResolution = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetTextSize( inputdata_t &inputdata ) +{ + m_flTextSize = inputdata.value.Float(); + + if (m_hScreen) + { + m_hScreen->SetActualSize( m_flTextSize, m_flTextSize ); + m_hScreen->SetLocalOrigin( m_hScreen->CollisionProp()->OBBCenter() * -1.0f ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "text_display_panel"; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "vgui_screen"; +} + +//----------------------------------------------------------------------------- +// This is called by the base object when it's time to spawn the control panels +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::SpawnControlPanels() +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + float flWidth = m_flTextSize; + float flHeight = m_flTextSize; + + CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, this, this, 0 ); + pScreen->ChangeTeam( GetTeamNumber() ); + pScreen->SetActualSize( flWidth, flHeight ); + pScreen->SetLocalOrigin( pScreen->CollisionProp()->OBBCenter() * -1.0f ); + pScreen->SetActive( true ); + pScreen->MakeVisibleOnlyToTeammates( false ); + pScreen->SetTransparency( true ); + m_hScreen = pScreen; + + return; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::RestoreControlPanels( void ) +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname ); + + while ( ( pScreen && pScreen->GetOwnerEntity() != this ) || Q_strcmp( pScreen->GetPanelName(), pScreenName ) != 0 ) + { + pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname ); + } + + if ( pScreen ) + { + m_hScreen = pScreen; + m_hScreen->SetActive( true ); + } + + return; + } +} diff --git a/mp/src/game/server/movie_display.cpp b/mp/src/game/server/movie_display.cpp new file mode 100644 index 00000000..b5a4476c --- /dev/null +++ b/mp/src/game/server/movie_display.cpp @@ -0,0 +1,378 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: Allows movies to be played as a VGUI screen in the world +// +//=====================================================================================// + +#include "cbase.h" +#include "EnvMessage.h" +#include "fmtstr.h" +#include "vguiscreen.h" +#include "filesystem.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +class CMovieDisplay : public CBaseEntity +{ +public: + + DECLARE_CLASS( CMovieDisplay, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CMovieDisplay() { m_bMuted = true; } + + virtual ~CMovieDisplay(); + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + virtual int UpdateTransmitState(); + virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); + + virtual void Spawn( void ); + virtual void Precache( void ); + virtual void OnRestore( void ); + + void ScreenVisible( bool bVisible ); + + void Disable( void ); + void Enable( void ); + + void InputDisable( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + + void InputSetDisplayText( inputdata_t &inputdata ); + +private: + + // Control panel + void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ); + void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ); + void SpawnControlPanels( void ); + void RestoreControlPanels( void ); + +private: + CNetworkVar( bool, m_bEnabled ); + CNetworkVar( bool, m_bLooping ); + CNetworkVar( bool, m_bMuted); + + CNetworkString( m_szDisplayText, 128 ); + + // Filename of the movie to play + CNetworkString( m_szMovieFilename, 128 ); + string_t m_strMovieFilename; + + // "Group" name. Screens of the same group name will play the same movie at the same time + // Effectively this lets multiple screens tune to the same "channel" in the world + CNetworkString( m_szGroupName, 128 ); + string_t m_strGroupName; + + int m_iScreenWidth; + int m_iScreenHeight; + + bool m_bDoFullTransmit; + + CHandle m_hScreen; +}; + +LINK_ENTITY_TO_CLASS( vgui_movie_display, CMovieDisplay ); + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- +BEGIN_DATADESC( CMovieDisplay ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_AUTO_ARRAY_KEYFIELD( m_szDisplayText, FIELD_CHARACTER, "displaytext" ), + + DEFINE_AUTO_ARRAY( m_szMovieFilename, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_strMovieFilename, FIELD_STRING, "moviefilename" ), + + DEFINE_AUTO_ARRAY( m_szGroupName, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_strGroupName, FIELD_STRING, "groupname" ), + + DEFINE_KEYFIELD( m_iScreenWidth, FIELD_INTEGER, "width" ), + DEFINE_KEYFIELD( m_iScreenHeight, FIELD_INTEGER, "height" ), + DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "looping" ), + DEFINE_KEYFIELD( m_bMuted, FIELD_BOOLEAN, "muted"), + + DEFINE_FIELD( m_bDoFullTransmit, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_hScreen, FIELD_EHANDLE ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetDisplayText", InputSetDisplayText ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CMovieDisplay, DT_MovieDisplay ) + SendPropBool( SENDINFO( m_bEnabled ) ), + SendPropBool( SENDINFO( m_bLooping ) ), + SendPropBool( SENDINFO( m_bMuted ) ), + SendPropString( SENDINFO( m_szMovieFilename ) ), + SendPropString( SENDINFO( m_szGroupName ) ), +END_SEND_TABLE() + +CMovieDisplay::~CMovieDisplay() +{ + DestroyVGuiScreen( m_hScreen.Get() ); +} + +//----------------------------------------------------------------------------- +// Read in Hammer data +//----------------------------------------------------------------------------- + +bool CMovieDisplay::KeyValue( const char *szKeyName, const char *szValue ) +{ + // NOTE: Have to do these separate because they set two values instead of one + if( FStrEq( szKeyName, "angles" ) ) + { + Assert( GetMoveParent() == NULL ); + QAngle angles; + UTIL_StringToVector( angles.Base(), szValue ); + + // Because the vgui screen basis is strange (z is front, y is up, x is right) + // we need to rotate the typical basis before applying it + VMatrix mat, rotation, tmp; + MatrixFromAngles( angles, mat ); + MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 ); + MatrixMultiply( mat, rotation, tmp ); + MatrixBuildRotateZ( rotation, 90 ); + MatrixMultiply( tmp, rotation, mat ); + MatrixToAngles( mat, angles ); + SetAbsAngles( angles ); + + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMovieDisplay::UpdateTransmitState() +{ + if ( m_bDoFullTransmit ) + { + m_bDoFullTransmit = false; + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + return SetTransmitState( FL_EDICT_FULLCHECK ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) +{ + // Are we already marked for transmission? + if ( pInfo->m_pTransmitEdict->Get( entindex() ) ) + return; + + BaseClass::SetTransmit( pInfo, bAlways ); + + // Force our screen to be sent too. + m_hScreen->SetTransmit( pInfo, bAlways ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Spawn( void ) +{ + // Move the strings into a networkable form + Q_strcpy( m_szMovieFilename.GetForModify(), m_strMovieFilename.ToCStr() ); + Q_strcpy( m_szGroupName.GetForModify(), m_strGroupName.ToCStr() ); + + Precache(); + + BaseClass::Spawn(); + + m_bEnabled = false; + + SpawnControlPanels(); + + ScreenVisible( m_bEnabled ); + + m_bDoFullTransmit = true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheVGuiScreen( "video_display_screen" ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::OnRestore( void ) +{ + BaseClass::OnRestore(); + + RestoreControlPanels(); + + ScreenVisible( m_bEnabled ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::ScreenVisible( bool bVisible ) +{ + // Set its active state + m_hScreen->SetActive( bVisible ); + + if ( bVisible ) + { + m_hScreen->RemoveEffects( EF_NODRAW ); + } + else + { + m_hScreen->AddEffects( EF_NODRAW ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Disable( void ) +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + + ScreenVisible( false ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Enable( void ) +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + + ScreenVisible( true ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputSetDisplayText( inputdata_t &inputdata ) +{ + Q_strcpy( m_szDisplayText.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "movie_display_screen"; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "vgui_screen"; +} + +//----------------------------------------------------------------------------- +// This is called by the base object when it's time to spawn the control panels +//----------------------------------------------------------------------------- +void CMovieDisplay::SpawnControlPanels() +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + float flWidth = m_iScreenWidth; + float flHeight = m_iScreenHeight; + + CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, this, this, 0 ); + pScreen->ChangeTeam( GetTeamNumber() ); + pScreen->SetActualSize( flWidth, flHeight ); + pScreen->SetActive( true ); + pScreen->MakeVisibleOnlyToTeammates( false ); + pScreen->SetTransparency( true ); + m_hScreen = pScreen; + + return; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::RestoreControlPanels( void ) +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname ); + + while ( ( pScreen && pScreen->GetOwnerEntity() != this ) || Q_strcmp( pScreen->GetPanelName(), pScreenName ) != 0 ) + { + pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname ); + } + + if ( pScreen ) + { + m_hScreen = pScreen; + m_hScreen->SetActive( true ); + } + + return; + } +} diff --git a/mp/src/game/server/nav_merge.cpp b/mp/src/game/server/nav_merge.cpp index d62764ab..1d5330ca 100644 --- a/mp/src/game/server/nav_merge.cpp +++ b/mp/src/game/server/nav_merge.cpp @@ -303,7 +303,7 @@ void CNavMesh::CommandNavMergeMesh( const CCommand &args ) //-------------------------------------------------------------------------------------------------------- int NavMeshMergeAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ) { - char *commandName = "nav_merge_mesh"; + const char *commandName = "nav_merge_mesh"; int numMatches = 0; partial += Q_strlen( commandName ) + 1; int partialLength = Q_strlen( partial ); diff --git a/mp/src/game/server/particle_system.cpp b/mp/src/game/server/particle_system.cpp index 6ec0cb96..23e98eaf 100644 --- a/mp/src/game/server/particle_system.cpp +++ b/mp/src/game/server/particle_system.cpp @@ -31,6 +31,7 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE(CParticleSystem, DT_ParticleSystem) SendPropFloat( SENDINFO(m_flStartTime) ), SendPropArray3( SENDINFO_ARRAY3(m_hControlPointEnts), SendPropEHandle( SENDINFO_ARRAY(m_hControlPointEnts) ) ), + SendPropArray3( SENDINFO_ARRAY3(m_vControlPointVecs), SendPropVector( SENDINFO_ARRAY(m_vControlPointVecs) ) ), SendPropArray3( SENDINFO_ARRAY3(m_iControlPointParents), SendPropInt( SENDINFO_ARRAY(m_iControlPointParents), 3, SPROP_UNSIGNED ) ), SendPropBool( SENDINFO(m_bWeatherEffect) ), END_SEND_TABLE() @@ -131,6 +132,7 @@ BEGIN_DATADESC( CParticleSystem ) END_DATADESC() LINK_ENTITY_TO_CLASS( info_particle_system, CParticleSystem ); +LINK_ENTITY_TO_CLASS( info_particle_system_coordinate, CParticleSystemCoordinate ); //----------------------------------------------------------------------------- // Purpose: @@ -260,17 +262,28 @@ void CParticleSystem::ReadControlPointEnts( void ) { for ( int i = 0 ; i < kMAXCONTROLPOINTS; ++i ) { - if ( m_iszControlPointNames[i] == NULL_STRING ) - continue; - - CBaseEntity *pPointEnt = gEntList.FindEntityGeneric( NULL, STRING( m_iszControlPointNames[i] ), this ); - Assert( pPointEnt != NULL ); - if ( pPointEnt == NULL ) + if (UsesCoordinates()) { - Warning("Particle system %s could not find control point entity (%s)\n", GetEntityName().ToCStr(), m_iszControlPointNames[i].ToCStr() ); - continue; + Vector vecCoords; + // cast str to vector, add vector to array + const char* pszVector = STRING(m_iszControlPointNames[i]); + UTIL_StringToVector(vecCoords.Base(), pszVector); + m_vControlPointVecs.Set(i, vecCoords); } + else + { + if ( m_iszControlPointNames[i] == NULL_STRING ) + continue; - m_hControlPointEnts.Set( i, pPointEnt ); + CBaseEntity *pPointEnt = gEntList.FindEntityGeneric( NULL, STRING( m_iszControlPointNames[i] ), this ); + Assert( pPointEnt != NULL ); + if ( pPointEnt == NULL ) + { + Warning("Particle system %s could not find control point entity (%s)\n", GetEntityName().ToCStr(), m_iszControlPointNames[i].ToCStr() ); + continue; + } + + m_hControlPointEnts.Set( i, pPointEnt ); + } } } diff --git a/mp/src/game/server/particle_system.h b/mp/src/game/server/particle_system.h index 6ce9a9c9..42e213f5 100644 --- a/mp/src/game/server/particle_system.h +++ b/mp/src/game/server/particle_system.h @@ -41,6 +41,8 @@ public: enum { kMAXCONTROLPOINTS = 63 }; ///< actually one less than the total number of cpoints since 0 is assumed to be me + virtual bool UsesCoordinates( void ) { return false; } + protected: /// Load up and resolve the entities that are supposed to be the control points @@ -58,8 +60,19 @@ protected: string_t m_iszControlPointNames[kMAXCONTROLPOINTS]; CNetworkArray( EHANDLE, m_hControlPointEnts, kMAXCONTROLPOINTS ); + CNetworkArray( Vector, m_vControlPointVecs, kMAXCONTROLPOINTS ); CNetworkArray( unsigned char, m_iControlPointParents, kMAXCONTROLPOINTS ); CNetworkVar( bool, m_bWeatherEffect ); }; +//----------------------------------------------------------------------------- +// Purpose: An entity that spawns and controls a particle system using coordinates. +//----------------------------------------------------------------------------- +class CParticleSystemCoordinate : public CParticleSystem +{ + DECLARE_CLASS( CParticleSystemCoordinate, CParticleSystem ); +public: + virtual bool UsesCoordinates( void ) { return true; } +}; + #endif // PARTICLE_SYSTEM_H diff --git a/mp/src/game/server/physics_main.cpp b/mp/src/game/server/physics_main.cpp index a27a669a..5cf2b5c5 100644 --- a/mp/src/game/server/physics_main.cpp +++ b/mp/src/game/server/physics_main.cpp @@ -7,12 +7,12 @@ #include "cbase.h" -#ifdef _WIN32 +#if POSIX || _MSC_VER >= 1900 +#include +#elif defined(_WIN32) #include "typeinfo.h" // BUGBUG: typeinfo stomps some of the warning settings (in yvals.h) #pragma warning(disable:4244) -#elif POSIX -#include #else #error "need typeinfo defined" #endif @@ -944,8 +944,8 @@ void CBaseEntity::PhysicsDispatchThink( BASEPTR thinkFunc ) if ( thinkLimit ) { // calculate running time of the AI in milliseconds - float time = ( engine->Time() - startTime ) * 1000.0f; - if ( time > thinkLimit ) + float flTime = ( engine->Time() - startTime ) * 1000.0f; + if ( flTime > thinkLimit ) { #if defined( _XBOX ) && !defined( _RETAIL ) if ( vprof_think_limit.GetBool() ) @@ -958,14 +958,14 @@ void CBaseEntity::PhysicsDispatchThink( BASEPTR thinkFunc ) CAI_BaseNPC *pNPC = MyNPCPointer(); if (pNPC && pNPC->GetCurSchedule()) { - pNPC->ReportOverThinkLimit( time ); + pNPC->ReportOverThinkLimit( flTime ); } else { #ifdef _WIN32 - Msg( "%s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).raw_name(), time ); + Msg( "%s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).raw_name(), flTime ); #elif POSIX - Msg( "%s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).name(), time ); + Msg( "%s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).name(), flTime ); #else #error "typeinfo" #endif diff --git a/mp/src/game/server/physics_prop_ragdoll.cpp b/mp/src/game/server/physics_prop_ragdoll.cpp index 0844f7b7..93efddc7 100644 --- a/mp/src/game/server/physics_prop_ragdoll.cpp +++ b/mp/src/game/server/physics_prop_ragdoll.cpp @@ -56,6 +56,9 @@ const float ATTACHED_DAMPING_SCALE = 50.0f; #define SF_RAGDOLLPROP_MOTIONDISABLED 0x4000 #define SF_RAGDOLLPROP_ALLOW_STRETCH 0x8000 #define SF_RAGDOLLPROP_STARTASLEEP 0x10000 +#ifdef MAPBASE +#define SF_RAGDOLLPROP_FIXED_CONSTRAINTS 0x20000 +#endif //----------------------------------------------------------------------------- // Networking @@ -156,6 +159,16 @@ BEGIN_DATADESC(CRagdollProp) DEFINE_RAGDOLL_ELEMENT( 21 ), DEFINE_RAGDOLL_ELEMENT( 22 ), DEFINE_RAGDOLL_ELEMENT( 23 ), +#ifdef MAPBASE + DEFINE_RAGDOLL_ELEMENT( 24 ), + DEFINE_RAGDOLL_ELEMENT( 25 ), + DEFINE_RAGDOLL_ELEMENT( 26 ), + DEFINE_RAGDOLL_ELEMENT( 27 ), + DEFINE_RAGDOLL_ELEMENT( 28 ), + DEFINE_RAGDOLL_ELEMENT( 29 ), + DEFINE_RAGDOLL_ELEMENT( 30 ), + DEFINE_RAGDOLL_ELEMENT( 31 ), +#endif END_DATADESC() @@ -166,6 +179,7 @@ BEGIN_ENT_SCRIPTDESC( CRagdollProp, CBaseAnimating, "Ragdoll physics prop." ) DEFINE_SCRIPTFUNC( SetSourceClassName, "Sets the ragdoll's source classname." ) DEFINE_SCRIPTFUNC( HasPhysgunInteraction, "Checks if the ragdoll has the specified interaction." ) + // TODO: Proper shared ragdoll funcs? 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." ) @@ -187,8 +201,10 @@ void CRagdollProp::Spawn( void ) // Starts out as the default fade scale value m_flDefaultFadeScale = m_flFadeScale; +#ifndef MAPBASE // NOTE: If this fires, then the assert or the datadesc is wrong! (see DEFINE_RAGDOLL_ELEMENT above) Assert( RAGDOLL_MAX_ELEMENTS == 24 ); +#endif Precache(); SetModel( STRING( GetModelName() ) ); @@ -758,7 +774,11 @@ void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const params.pCurrentBones = pBoneToWorld; params.jointFrictionScale = 1.0; params.allowStretch = HasSpawnFlags(SF_RAGDOLLPROP_ALLOW_STRETCH); +#ifdef MAPBASE + params.fixedConstraints = HasSpawnFlags(SF_RAGDOLLPROP_FIXED_CONSTRAINTS); +#else params.fixedConstraints = false; +#endif RagdollCreate( m_ragdoll, params, physenv ); RagdollApplyAnimationAsVelocity( m_ragdoll, pPrevBones, pBoneToWorld, dt ); if ( m_anglesOverrideString != NULL_STRING && Q_strlen(m_anglesOverrideString.ToCStr()) > 0 ) @@ -1353,6 +1373,16 @@ CBaseAnimating *CreateServerRagdollSubmodel( CBaseAnimating *pOwner, const char matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES]; pRagdoll->ResetSequence( 0 ); +#ifdef MAPBASE_VSCRIPT + // Hook for pre-spawn ragdolling + if (pOwner && pOwner->m_ScriptScope.IsInitialized() && CBaseAnimating::g_Hook_OnServerRagdoll.CanRunInScope( pOwner->m_ScriptScope )) + { + // ragdoll, submodel + ScriptVariant_t args[] = { ScriptVariant_t( pRagdoll->GetScriptInstance() ), true }; + CBaseAnimating::g_Hook_OnServerRagdoll.Call( pOwner->m_ScriptScope, NULL, args ); + } +#endif + // let bone merging do the work of copying everything over for us pRagdoll->SetParent( pOwner ); pRagdoll->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); @@ -1377,6 +1407,16 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con pRagdoll->CopyAnimationDataFrom( pAnimating ); pRagdoll->SetOwnerEntity( pAnimating ); +#ifdef MAPBASE_VSCRIPT + // Hook for pre-spawn ragdolling + if (pAnimating->m_ScriptScope.IsInitialized() && CBaseAnimating::g_Hook_OnServerRagdoll.CanRunInScope( pAnimating->m_ScriptScope )) + { + // ragdoll, submodel + ScriptVariant_t args[] = { ScriptVariant_t( pRagdoll->GetScriptInstance() ), false }; + CBaseAnimating::g_Hook_OnServerRagdoll.Call( pAnimating->m_ScriptScope, NULL, args ); + } +#endif + pRagdoll->InitRagdollAnimation(); matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES]; diff --git a/mp/src/game/server/player.cpp b/mp/src/game/server/player.cpp index 59511f4a..641db194 100644 --- a/mp/src/game/server/player.cpp +++ b/mp/src/game/server/player.cpp @@ -59,6 +59,10 @@ #include "env_zoom.h" #include "rumble_shared.h" #include "gamestats.h" +#ifdef MAPBASE // From Alien Swarm SDK +#include "env_tonemap_controller.h" +#include "fogvolume.h" +#endif #include "npcevent.h" #include "datacache/imdlcache.h" #include "hintsystem.h" @@ -459,8 +463,13 @@ BEGIN_DATADESC( CBasePlayer ) // Inputs DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetHUDVisibility", InputSetHUDVisibility ), - DEFINE_INPUTFUNC( FIELD_STRING, "SetFogController", InputSetFogController ), +#ifdef MAPBASE // From Alien Swarm SDK (kind of) + DEFINE_INPUTFUNC( FIELD_INPUT, "SetFogController", InputSetFogController ), DEFINE_INPUTFUNC( FIELD_INPUT, "SetPostProcessController", InputSetPostProcessController ), + DEFINE_INPUTFUNC( FIELD_INPUT, "SetColorCorrectionController", InputSetColorCorrectionController ), +#else + DEFINE_INPUTFUNC( FIELD_STRING, "SetFogController", InputSetFogController ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "HandleMapEvent", InputHandleMapEvent ), #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetSuppressAttacks", InputSetSuppressAttacks ), @@ -474,9 +483,10 @@ BEGIN_DATADESC( CBasePlayer ) DEFINE_FIELD( m_nNumCrateHudHints, FIELD_INTEGER ), +#ifdef MAPBASE // From Alien Swarm SDK DEFINE_FIELD( m_hPostProcessCtrl, FIELD_EHANDLE ), - - + DEFINE_FIELD( m_hColorCorrectionCtrl, FIELD_EHANDLE ), +#endif // DEFINE_FIELD( m_nBodyPitchPoseParam, FIELD_INTEGER ), // DEFINE_ARRAY( m_StepSoundCache, StepSoundCache_t, 2 ), @@ -526,8 +536,8 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) 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_NAMED( ScriptGetFOVOwner, "GetFOVOwner", "Gets current view owner." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFOV, "SetFOV", "Sets player FOV regardless of view owner." ) DEFINE_SCRIPTFUNC( ViewPunch, "Punches the player's view with the specified vector." ) DEFINE_SCRIPTFUNC( SetMuzzleFlashTime, "Sets the player's muzzle flash time for AI." ) @@ -541,6 +551,8 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeRight, "GetEyeRight", "Gets the player's right eye vector." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeUp, "GetEyeUp", "Gets the player's up eye vector." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetViewModel, "GetViewModel", "Returns the viewmodel of the specified index." ) + // // Hooks // @@ -622,6 +634,10 @@ void CBasePlayer::DestroyViewModels( void ) } #ifdef MAPBASE +extern char g_szDefaultHandsModel[MAX_PATH]; +extern int g_iDefaultHandsSkin; +extern int g_iDefaultHandsBody; + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -629,8 +645,14 @@ void CBasePlayer::CreateHandModel(int index, int iOtherVm) { Assert(index >= 0 && index < MAX_VIEWMODELS && iOtherVm >= 0 && iOtherVm < MAX_VIEWMODELS ); - if (GetViewModel(index)) + if (GetViewModel( index )) + { + // This can happen if the player respawns + // Don't draw unless we're already using a hands weapon + if ( !GetActiveWeapon() || !GetActiveWeapon()->UsesHands() ) + GetViewModel( index )->AddEffects( EF_NODRAW ); return; + } CBaseViewModel *vm = (CBaseViewModel *)CreateEntityByName("hand_viewmodel"); if (vm) @@ -638,9 +660,15 @@ void CBasePlayer::CreateHandModel(int index, int iOtherVm) vm->SetAbsOrigin(GetAbsOrigin()); vm->SetOwner(this); vm->SetIndex(index); + + vm->SetModel( g_szDefaultHandsModel ); + vm->m_nSkin = g_iDefaultHandsSkin; + vm->m_nBody = g_iDefaultHandsBody; + DispatchSpawn(vm); vm->FollowEntity(GetViewModel(iOtherVm), true); m_hViewModel.Set(index, vm); + vm->AddEffects( EF_NODRAW ); } } #endif @@ -1700,6 +1728,15 @@ void CBasePlayer::RemoveAllItems( bool removeSuit ) RemoveAllWeapons(); RemoveAllAmmo(); +#ifdef MAPBASE + // Hide hand viewmodel + CBaseViewModel *vm = GetViewModel( 1 ); + if ( vm ) + { + vm->AddEffects( EF_NODRAW ); + } +#endif + if ( removeSuit ) { RemoveSuit(); @@ -3851,7 +3888,7 @@ void CBasePlayer::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) // Movement hook for VScript if (m_ScriptScope.IsInitialized() && g_Hook_PlayerRunCommand.CanRunInScope(m_ScriptScope)) { - HSCRIPT hCmd = g_pScriptVM->RegisterInstance( ucmd ); + HSCRIPT hCmd = g_pScriptVM->RegisterInstance( reinterpret_cast(ucmd) ); // command ScriptVariant_t args[] = { hCmd }; @@ -4688,6 +4725,55 @@ void CBasePlayer::ForceOrigin( const Vector &vecOrigin ) m_vForcedOrigin = vecOrigin; } +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::OnTonemapTriggerStartTouch( CTonemapTrigger *pTonemapTrigger ) +{ + m_hTriggerTonemapList.FindAndRemove( pTonemapTrigger ); + m_hTriggerTonemapList.AddToTail( pTonemapTrigger ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::OnTonemapTriggerEndTouch( CTonemapTrigger *pTonemapTrigger ) +{ + m_hTriggerTonemapList.FindAndRemove( pTonemapTrigger ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::UpdateTonemapController( void ) +{ + // For now, Mapbase uses Tony Sergi's Source 2007 tonemap fixes. + // Alien Swarm SDK tonemap controller code copies the parameters instead. + + CEnvTonemapController *pController = NULL; + + if (m_hTriggerTonemapList.Count() > 0) + { + pController = static_cast(m_hTriggerTonemapList.Tail()->GetTonemapController()); + } + else if (TheTonemapSystem()->GetMasterTonemapController()) + { + pController = static_cast(TheTonemapSystem()->GetMasterTonemapController()); + } + + if (pController) + { + //m_hTonemapController = TheTonemapSystem()->GetMasterTonemapController(); + + if (pController->m_bUseCustomAutoExposureMax) + m_Local.m_TonemapParams.m_flAutoExposureMax = pController->m_flCustomAutoExposureMax; + + if (pController->m_bUseCustomAutoExposureMin) + m_Local.m_TonemapParams.m_flAutoExposureMin = pController->m_flCustomAutoExposureMin; + + if (pController->m_bUseCustomBloomScale) + m_Local.m_TonemapParams.m_flBloomScale = pController->m_flCustomBloomScale; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -4695,6 +4781,11 @@ void CBasePlayer::PostThink() { m_vecSmoothedVelocity = m_vecSmoothedVelocity * SMOOTHING_FACTOR + GetAbsVelocity() * ( 1 - SMOOTHING_FACTOR ); +#ifdef MAPBASE // From Alien Swarm SDK + UpdateTonemapController(); + UpdateFXVolume(); +#endif + if ( !g_fGameOver && !m_iPlayerLocked ) { if ( IsAlive() ) @@ -5171,7 +5262,9 @@ void CBasePlayer::Spawn( void ) // Initialize the fog and postprocess controllers. InitFogController(); +#ifdef MAPBASE // From Alien Swarm SDK InitPostProcessController(); +#endif m_DmgTake = 0; m_DmgSave = 0; @@ -5271,6 +5364,11 @@ void CBasePlayer::Spawn( void ) m_vecSmoothedVelocity = vec3_origin; InitVCollision( GetAbsOrigin(), GetAbsVelocity() ); + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } + #if !defined( TF_DLL ) IGameEvent *event = gameeventmanager->CreateEvent( "player_spawn" ); @@ -5295,11 +5393,6 @@ void CBasePlayer::Spawn( void ) UpdateLastKnownArea(); m_weaponFiredTimer.Invalidate(); - - if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) - { - g_pScriptVM->SetValue( "player", GetScriptInstance() ); - } } void CBasePlayer::Activate( void ) @@ -5372,6 +5465,10 @@ void CBasePlayer::Precache( void ) m_iTrain = TRAIN_NEW; #endif +#ifdef MAPBASE + PrecacheModel( g_szDefaultHandsModel ); +#endif + m_iClientBattery = -1; m_iUpdateTime = 5; // won't update for 1/2 a second @@ -7089,6 +7186,19 @@ HSCRIPT CBasePlayer::VScriptGetExpresser() return hScript; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBasePlayer::ScriptGetViewModel( int viewmodelindex ) +{ + if (viewmodelindex < 0 || viewmodelindex >= MAX_VIEWMODELS) + { + Warning( "GetViewModel: Invalid index '%i'\n", viewmodelindex ); + return NULL; + } + + return ToHScript( GetViewModel( viewmodelindex ) ); +} #endif //----------------------------------------------------------------------------- @@ -7718,6 +7828,10 @@ void CBasePlayer::Weapon_DropSlot( int weaponSlot ) } } +#ifdef MAPBASE +ConVar player_autoswitch_on_first_pickup("player_autoswitch_on_pickup", "1", FCVAR_NONE, "Determines how the player should autoswitch when picking up a new weapon. 0 = no autoswitch, 1 = always (default), 2 = use unused weighting system"); +#endif + //----------------------------------------------------------------------------- // Purpose: Override to add weapon to the hud //----------------------------------------------------------------------------- @@ -7726,24 +7840,25 @@ 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() ) + // BumpWeapon's code appeared to be deprecated; The same operation is already handled here, but with much more code involved. + // There's also an unused weighting system which was overridden by that deprecated code. The unused weighting code can be enabled + // via player_autoswitch_on_first_pickup. + bool bShouldSwitch = false; + switch (player_autoswitch_on_first_pickup.GetInt()) { - Weapon_Switch( pWeapon ); + // Unused Weighting + case 2: + bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon ); + break; + + // Always (old behavior) + case 1: + bShouldSwitch = true; + break; } #else bool bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon ); +#endif #ifdef HL2_DLL if ( bShouldSwitch == false && PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon && @@ -7758,7 +7873,6 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) { Weapon_Switch( pWeapon ); } -#endif } #ifdef MAPBASE @@ -7767,39 +7881,24 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) //----------------------------------------------------------------------------- Activity CBasePlayer::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) { -#ifdef HL2_DLL - // HAAAAAAAAAAAAAACKS! - if (GetActiveWeapon()) + Activity weaponTranslation = BaseClass::Weapon_TranslateActivity( baseAct, pRequired ); + + if ( GetActiveWeapon() && GetActiveWeapon()->IsEffectActive(EF_NODRAW) && baseAct != ACT_ARM ) { - 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; + // Our weapon is holstered. Use the base activity. + return baseAct; } -#endif + if ( GetModelPtr() && (!GetModelPtr()->HaveSequenceForActivity(weaponTranslation) || baseAct == weaponTranslation) ) + { + // This is used so players can fall back to backup activities in the same way NPCs in Mapbase can + Activity backupActivity = Weapon_BackupActivity(baseAct, pRequired ? *pRequired : false); + if ( baseAct != backupActivity && GetModelPtr()->HaveSequenceForActivity(backupActivity) ) + return backupActivity; - return BaseClass::Weapon_TranslateActivity( baseAct, pRequired ); + return baseAct; + } + + return weaponTranslation; } #endif @@ -8692,6 +8791,7 @@ void SendProxy_ShiftPlayerSpawnflags( const SendProp *pProp, const void *pStruct SendPropInt ( SENDINFO( m_spawnflags ), 3, SPROP_UNSIGNED, SendProxy_ShiftPlayerSpawnflags ), SendPropBool ( SENDINFO( m_bDrawPlayerModelExternally ) ), + SendPropBool ( SENDINFO( m_bInTriggerFall ) ), #endif END_SEND_TABLE() @@ -8731,8 +8831,11 @@ void SendProxy_ShiftPlayerSpawnflags( const SendProp *pProp, const void *pStruct SendPropArray ( SendPropEHandle( SENDINFO_ARRAY( m_hViewModel ) ), m_hViewModel ), SendPropString (SENDINFO(m_szLastPlaceName) ), +#ifdef MAPBASE // From Alien Swarm SDK // Postprocess data - SendPropEHandle( SENDINFO( m_hPostProcessCtrl ) ), + SendPropEHandle ( SENDINFO(m_hPostProcessCtrl) ), + SendPropEHandle ( SENDINFO(m_hColorCorrectionCtrl) ), +#endif #if defined USES_ECON_ITEMS SendPropUtlVector( SENDINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, SendPropEHandle( NULL, 0 ) ), @@ -9448,7 +9551,19 @@ void CBasePlayer::InputSetSuppressAttacks( inputdata_t &inputdata ) void CBasePlayer::InputSetFogController( inputdata_t &inputdata ) { // Find the fog controller with the given name. +#ifdef MAPBASE // From Alien Swarm SDK + CFogController *pFogController = NULL; + if ( inputdata.value.FieldType() == FIELD_EHANDLE ) + { + pFogController = dynamic_cast( inputdata.value.Entity().Get() ); + } + else + { + pFogController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); + } +#else CFogController *pFogController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); +#endif if ( pFogController ) { m_Local.m_PlayerFog.m_hCtrl.Set( pFogController ); @@ -9464,6 +9579,7 @@ void CBasePlayer::InitFogController( void ) m_Local.m_PlayerFog.m_hCtrl = FogSystem()->GetMasterFogController(); } +#ifdef MAPBASE // From Alien Swarm SDK //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -9476,25 +9592,57 @@ void CBasePlayer::InitPostProcessController( void ) //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -void CBasePlayer::InputSetPostProcessController( inputdata_t& inputdata ) +void CBasePlayer::InitColorCorrectionController( void ) { - // Find the postprocess controller with the given name. - CPostProcessController* pController = NULL; - if (inputdata.value.FieldType() == FIELD_EHANDLE) + m_hColorCorrectionCtrl = ColorCorrectionSystem()->GetMasterColorCorrection(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetPostProcessController( inputdata_t &inputdata ) +{ + // Find the fog controller with the given name. + CPostProcessController *pController = NULL; + if ( inputdata.value.FieldType() == FIELD_EHANDLE ) { - pController = dynamic_cast(inputdata.value.Entity().Get()); + pController = dynamic_cast( inputdata.value.Entity().Get() ); } else { - pController = dynamic_cast(gEntList.FindEntityByName( NULL, inputdata.value.String() )); + pController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); } - if (pController) + if ( pController ) { m_hPostProcessCtrl.Set( pController ); } } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetColorCorrectionController( inputdata_t &inputdata ) +{ + // Find the fog controller with the given name. + CColorCorrection *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_hColorCorrectionCtrl.Set( pController ); + } + +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pEntity - @@ -10185,3 +10333,70 @@ uint64 CBasePlayer::GetSteamIDAsUInt64( void ) return 0; } #endif // NO_STEAM + +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::UpdateFXVolume( void ) +{ + CFogController *pFogController = NULL; + CPostProcessController *pPostProcessController = NULL; + CColorCorrection* pColorCorrectionEnt = NULL; + + Vector eyePos; + CBaseEntity *pViewEntity = GetViewEntity(); + if ( pViewEntity ) + { + eyePos = pViewEntity->GetAbsOrigin(); + } + else + { + eyePos = EyePosition(); + } + + CFogVolume *pFogVolume = CFogVolume::FindFogVolumeForPosition( eyePos ); + if ( pFogVolume ) + { + pFogController = pFogVolume->GetFogController(); + pPostProcessController = pFogVolume->GetPostProcessController(); + pColorCorrectionEnt = pFogVolume->GetColorCorrectionController(); + + if ( !pFogController ) + { + pFogController = FogSystem()->GetMasterFogController(); + } + + if ( !pPostProcessController ) + { + pPostProcessController = PostProcessSystem()->GetMasterPostProcessController(); + } + + if ( !pColorCorrectionEnt ) + { + pColorCorrectionEnt = ColorCorrectionSystem()->GetMasterColorCorrection(); + } + } + else if ( TheFogVolumes.Count() > 0 ) + { + // If we're not in a fog volume, clear our fog volume, if the map has any. + // This will get us back to using the master fog controller. + pFogController = FogSystem()->GetMasterFogController(); + pPostProcessController = PostProcessSystem()->GetMasterPostProcessController(); + pColorCorrectionEnt = ColorCorrectionSystem()->GetMasterColorCorrection(); + } + + if ( pFogController && m_Local.m_PlayerFog.m_hCtrl.Get() != pFogController ) + { + m_Local.m_PlayerFog.m_hCtrl.Set( pFogController ); + } + + if ( pPostProcessController ) + { + m_hPostProcessCtrl.Set( pPostProcessController ); + } + + if ( pColorCorrectionEnt ) + { + m_hColorCorrectionCtrl.Set( pColorCorrectionEnt ); + } +} +#endif diff --git a/mp/src/game/server/player.h b/mp/src/game/server/player.h index b425f388..d25e380b 100644 --- a/mp/src/game/server/player.h +++ b/mp/src/game/server/player.h @@ -88,6 +88,10 @@ class CNavArea; class CHintSystem; class CAI_Expresser; +#ifdef MAPBASE // From Alien Swarm SDK +class CTonemapTrigger; +#endif + #if defined USES_ECON_ITEMS class CEconWearable; #endif // USES_ECON_ITEMS @@ -408,6 +412,8 @@ public: 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; } + + HSCRIPT ScriptGetViewModel( int viewmodelindex ); #endif // View model prediction setup @@ -658,6 +664,8 @@ public: #ifdef MAPBASE bool ShouldUseVisibilityCache( CBaseEntity *pEntity ); + + void UpdateFXVolume( void ); // From Alien Swarm SDK #endif public: @@ -888,9 +896,18 @@ public: void InitFogController( void ); void InputSetFogController( inputdata_t &inputdata ); +#ifdef MAPBASE // From Alien Swarm SDK + void OnTonemapTriggerStartTouch( CTonemapTrigger *pTonemapTrigger ); + void OnTonemapTriggerEndTouch( CTonemapTrigger *pTonemapTrigger ); + CUtlVector< CHandle< CTonemapTrigger > > m_hTriggerTonemapList; + CNetworkHandle( CPostProcessController, m_hPostProcessCtrl ); // active postprocessing controller + CNetworkHandle( CColorCorrection, m_hColorCorrectionCtrl ); // active FXVolume color correction void InitPostProcessController( void ); - void InputSetPostProcessController( inputdata_t& inputdata ); + void InputSetPostProcessController( inputdata_t &inputdata ); + void InitColorCorrectionController( void ); + void InputSetColorCorrectionController( inputdata_t &inputdata ); +#endif // Used by env_soundscape_triggerable to manage when the player is touching multiple // soundscape triggers simultaneously. @@ -948,7 +965,7 @@ public: #endif #ifdef MAPBASE - bool m_bInTriggerFall; + CNetworkVar( bool, m_bInTriggerFall ); #endif private: @@ -1037,6 +1054,13 @@ protected: float m_fReplayEnd; // time to stop replay mode int m_iReplayEntity; // follow this entity in replay +#ifdef MAPBASE // From Alien Swarm SDK + // For now, Mapbase uses Tony Sergi's Source 2007 tonemap fixes. + // Alien Swarm SDK tonemap controller code copies the parameters instead. + virtual void UpdateTonemapController( void ); + //CNetworkHandle( CBaseEntity, m_hTonemapController ); +#endif + private: void HandleFuncTrain(); @@ -1155,7 +1179,8 @@ public: int m_nNumCrateHudHints; #ifdef MAPBASE - CNetworkVar( bool, m_bDrawPlayerModelExternally ); + bool GetDrawPlayerModelExternally( void ) { return m_bDrawPlayerModelExternally; } + void SetDrawPlayerModelExternally( bool bToggle ) { m_bDrawPlayerModelExternally.Set( bToggle ); } #endif private: @@ -1196,6 +1221,10 @@ private: // Player name char m_szNetname[MAX_PLAYER_NAME_LENGTH]; +#ifdef MAPBASE + CNetworkVar( bool, m_bDrawPlayerModelExternally ); +#endif + protected: // HACK FOR TF2 Prediction friend class CTFGameMovementRecon; diff --git a/mp/src/game/server/playerlocaldata.h b/mp/src/game/server/playerlocaldata.h index 587dbd1a..f9ceef70 100644 --- a/mp/src/game/server/playerlocaldata.h +++ b/mp/src/game/server/playerlocaldata.h @@ -15,7 +15,10 @@ #include "playernet_vars.h" #include "networkvar.h" #include "fogcontroller.h" +#ifdef MAPBASE // From Alien Swarm SDK #include "postprocesscontroller.h" +#include "colorcorrection.h" +#endif //----------------------------------------------------------------------------- // Purpose: Player specific data ( sent only to local player, too ) diff --git a/mp/src/game/server/point_camera.cpp b/mp/src/game/server/point_camera.cpp index 626bc61b..72a40b26 100644 --- a/mp/src/game/server/point_camera.cpp +++ b/mp/src/game/server/point_camera.cpp @@ -28,6 +28,58 @@ CPointCamera* GetPointCameraList() return g_PointCameraList.m_pClassList; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Returns true if a camera is in the PVS of the specified entity +//----------------------------------------------------------------------------- +edict_t *UTIL_FindRTCameraInEntityPVS( edict_t *pEdict ) +{ + CBaseEntity *pe = GetContainingEntity( pEdict ); + if ( !pe ) + return NULL; + + bool bGotPVS = false; + Vector org; + static byte pvs[ MAX_MAP_CLUSTERS/8 ]; + static int pvssize = sizeof( pvs ); + + for ( CPointCamera *pCameraEnt = GetPointCameraList(); pCameraEnt != NULL; pCameraEnt = pCameraEnt->m_pNext ) + { + if (!pCameraEnt->IsActive()) + continue; + + if (!bGotPVS) + { + // Getting the PVS during the loop like this makes sure we only get the PVS if there's actually an active camera in the level + org = pe->EyePosition(); + int clusterIndex = engine->GetClusterForOrigin( org ); + Assert( clusterIndex >= 0 ); + engine->GetPVSForCluster( clusterIndex, pvssize, pvs ); + bGotPVS = true; + } + + Vector vecCameraEye = pCameraEnt->EyePosition(); + + Vector vecCameraDirection; + pCameraEnt->GetVectors( &vecCameraDirection, NULL, NULL ); + + Vector los = (org - vecCameraEye); + float flDot = DotProduct( los, vecCameraDirection ); + + // Make sure we're in the camera's FOV before checking PVS + if ( flDot <= cos( DEG2RAD( pCameraEnt->GetFOV() / 2 ) ) ) + continue; + + if ( engine->CheckOriginInPVS( vecCameraEye, pvs, pvssize ) ) + { + return pCameraEnt->edict(); + } + } + + return NULL; +} +#endif + // These are already built into CBaseEntity // DEFINE_KEYFIELD( m_iName, FIELD_STRING, "targetname" ), // DEFINE_KEYFIELD( m_iParent, FIELD_STRING, "parentname" ), diff --git a/mp/src/game/server/point_camera.h b/mp/src/game/server/point_camera.h index c669ab82..9cb7d3c4 100644 --- a/mp/src/game/server/point_camera.h +++ b/mp/src/game/server/point_camera.h @@ -42,6 +42,7 @@ public: void InputSetRenderTarget( inputdata_t &inputdata ) { m_iszRenderTarget = inputdata.value.StringID(); } float GetFOV() const { return m_FOV; } + bool IsActive() const { return m_bIsOn; } #endif private: @@ -117,4 +118,8 @@ private: #endif CPointCamera *GetPointCameraList(); + +#ifdef MAPBASE +edict_t *UTIL_FindRTCameraInEntityPVS( edict_t *pEdict ); +#endif #endif // CAMERA_H diff --git a/mp/src/game/server/point_spotlight.cpp b/mp/src/game/server/point_spotlight.cpp index f4bb60d6..71aad0b1 100644 --- a/mp/src/game/server/point_spotlight.cpp +++ b/mp/src/game/server/point_spotlight.cpp @@ -76,6 +76,12 @@ private: float m_flHDRColorScale; int m_nMinDXLevel; +#ifdef MAPBASE + float m_flHaloScale; + string_t m_iszHaloMaterial; + string_t m_iszSpotlightMaterial; +#endif + public: COutputEvent m_OnOn, m_OnOff; ///< output fires when turned on, off }; @@ -100,6 +106,11 @@ BEGIN_DATADESC( CPointSpotlight ) DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), DEFINE_KEYFIELD( m_nMinDXLevel, FIELD_INTEGER, "mindxlevel" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flHaloScale, FIELD_FLOAT, "HaloScale" ), + DEFINE_KEYFIELD( m_iszHaloMaterial, FIELD_STRING, "HaloMaterial" ), + DEFINE_KEYFIELD( m_iszSpotlightMaterial, FIELD_STRING, "SpotlightMaterial" ), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "LightOn", InputLightOn ), @@ -130,6 +141,9 @@ CPointSpotlight::CPointSpotlight() m_flHDRColorScale = 1.0f; m_nMinDXLevel = 0; m_bIgnoreSolid = false; +#ifdef MAPBASE + m_flHaloScale = 60.0f; +#endif } #ifdef MAPBASE @@ -151,8 +165,23 @@ void CPointSpotlight::Precache(void) BaseClass::Precache(); // Sprites. +#ifdef MAPBASE + if (m_iszHaloMaterial == NULL_STRING) + { + m_iszHaloMaterial = AllocPooledString( "sprites/light_glow03.vmt" ); + } + + if (m_iszSpotlightMaterial == NULL_STRING) + { + m_iszSpotlightMaterial = AllocPooledString( "sprites/glow_test02.vmt" ); + } + + m_nHaloSprite = PrecacheModel( STRING( m_iszHaloMaterial ) ); + PrecacheModel( STRING( m_iszSpotlightMaterial ) ); +#else m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt"); PrecacheModel( "sprites/glow_test02.vmt" ); +#endif } @@ -379,13 +408,21 @@ void CPointSpotlight::SpotlightCreate(void) } //m_hSpotlight = CBeam::BeamCreate( "sprites/spotlight.vmt", m_flSpotlightGoalWidth ); +#ifdef MAPBASE + m_hSpotlight = CBeam::BeamCreate( STRING(m_iszSpotlightMaterial), m_flSpotlightGoalWidth ); +#else m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", m_flSpotlightGoalWidth ); +#endif // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore) m_hSpotlight->SetHDRColorScale( m_flHDRColorScale ); m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY ); m_hSpotlight->SetColor( m_clrRender->r, m_clrRender->g, m_clrRender->b ); m_hSpotlight->SetHaloTexture(m_nHaloSprite); +#ifdef MAPBASE + m_hSpotlight->SetHaloScale(m_flHaloScale); +#else m_hSpotlight->SetHaloScale(60); +#endif m_hSpotlight->SetEndWidth(m_flSpotlightGoalWidth); m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) ); m_hSpotlight->SetBrightness( 64 ); diff --git a/mp/src/game/server/props.cpp b/mp/src/game/server/props.cpp index 06de0eae..24b8d4bd 100644 --- a/mp/src/game/server/props.cpp +++ b/mp/src/game/server/props.cpp @@ -44,6 +44,7 @@ #ifdef MAPBASE #include "mapbase/GlobalStrings.h" #include "collisionutils.h" +#include "vstdlib/IKeyValuesSystem.h" // From Alien Swarm SDK #endif // memdbgon must be the last include file in a .cpp file!!! @@ -387,8 +388,15 @@ int CBaseProp::ParsePropData( void ) return PARSE_FAILED_NO_DATA; } +#ifdef MAPBASE // From Alien Swarm SDK + static int keyPropData = KeyValuesSystem()->GetSymbolForString( "prop_data" ); + + // Do we have a props section? + KeyValues *pkvPropData = modelKeyValues->FindKey( keyPropData ); +#else // Do we have a props section? KeyValues *pkvPropData = modelKeyValues->FindKey("prop_data"); +#endif if ( !pkvPropData ) { modelKeyValues->deleteThis(); @@ -1200,6 +1208,17 @@ int CBreakableProp::OnTakeDamage( const CTakeDamageInfo &inputInfo ) { m_hLastAttacker.Set( info.GetAttacker() ); } +#ifdef MAPBASE // From Alien Swarm SDK + else if ( info.GetAttacker() ) + { + CBaseEntity *attacker = info.GetAttacker(); + CBaseEntity *attackerOwner = attacker->GetOwnerEntity(); + if ( attackerOwner && attackerOwner->MyCombatCharacterPointer() ) + { + m_hLastAttacker.Set( attackerOwner ); + } + } +#endif float flPropDamage = GetBreakableDamage( info, assert_cast(this) ); info.SetDamage( flPropDamage ); @@ -2001,9 +2020,16 @@ BEGIN_DATADESC( CDynamicProp ) DEFINE_KEYFIELD( m_bDisableBoneFollowers, FIELD_BOOLEAN, "DisableBoneFollowers" ), DEFINE_FIELD( m_bUseHitboxesForRenderBox, FIELD_BOOLEAN ), DEFINE_FIELD( m_nPendingSequence, FIELD_SHORT ), +#ifdef MAPBASE // From Alien Swarm SDK + DEFINE_KEYFIELD( m_bUpdateAttachedChildren, FIELD_BOOLEAN, "updatechildren" ), + DEFINE_KEYFIELD( m_bHoldAnimation, FIELD_BOOLEAN, "HoldAnimation" ), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_STRING, "SetAnimation", InputSetAnimation ), +#ifdef MAPBASE // From Alien Swarm SDK + DEFINE_INPUTFUNC( FIELD_STRING, "SetAnimationNoReset", InputSetAnimationNoReset ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetDefaultAnimation", InputSetDefaultAnimation ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), @@ -2228,9 +2254,8 @@ void CDynamicProp::CreateBoneFollowers() pBone = pBone->GetNextKey(); } } - - modelKeyValues->deleteThis(); } + modelKeyValues->deleteThis(); // if we got here, we don't have a bone follower section, but if we have a ragdoll // go ahead and create default bone followers for it @@ -2368,10 +2393,23 @@ void CDynamicProp::AnimThink( void ) } else { +#ifdef MAPBASE // From Alien Swarm SDK + if ( m_iszDefaultAnim != NULL_STRING && m_bHoldAnimation == false ) + { + PropSetAnim( STRING( m_iszDefaultAnim ) ); + } + + // We need to wait for an animation change to come in + if ( m_bHoldAnimation ) + { + SetNextThink( gpGlobals->curtime + 0.1f ); + } +#else if (m_iszDefaultAnim != NULL_STRING) { PropSetAnim( STRING( m_iszDefaultAnim ) ); } +#endif } } } @@ -2383,6 +2421,17 @@ void CDynamicProp::AnimThink( void ) StudioFrameAdvance(); DispatchAnimEvents(this); m_BoneFollowerManager.UpdateBoneFollowers(this); + +#ifdef MAPBASE // From Alien Swarm SDK + // Update any SetParentAttached children + if ( m_bUpdateAttachedChildren ) + { + for ( CBaseEntity *pChild = FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() ) + { + pChild->PhysicsTouchTriggers(); + } + } +#endif } @@ -2421,6 +2470,19 @@ void CDynamicProp::InputSetAnimation( inputdata_t &inputdata ) PropSetAnim( inputdata.value.String() ); } +#ifdef MAPBASE // From Alien Swarm SDK +//------------------------------------------------------------------------------ +// Purpose: Set the animation unless the prop is already set to this particular animation +//------------------------------------------------------------------------------ +void CDynamicProp::InputSetAnimationNoReset( inputdata_t &inputdata ) +{ + if ( GetSequence() != LookupSequence( inputdata.value.String() ) ) + { + PropSetAnim( inputdata.value.String() ); + } +} +#endif + //------------------------------------------------------------------------------ // Purpose: //------------------------------------------------------------------------------ @@ -2736,7 +2798,7 @@ void CInteractableProp::Precache( void ) // useType - // value - //----------------------------------------------------------------------------- -void CInteractableProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +void CInteractableProp::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) { if (m_flCooldownTime > gpGlobals->curtime) return; @@ -2744,18 +2806,18 @@ void CInteractableProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ // 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 ); + CBasePlayer *pPlayer = ToBasePlayer(pActivator); if (pPlayer) { Vector forward; - pPlayer->EyeVectors( &forward, NULL, NULL ); + 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 )) + 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"); @@ -2768,28 +2830,36 @@ void CInteractableProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ if (m_bLocked) { - m_OnLockedUse.FireOutput( pActivator, this ); + m_OnLockedUse.FireOutput(pActivator, this); EmitSound(STRING(m_iszLockedSound)); - nSequence = LookupSequence( STRING( m_iszLockedSequence ) ); + nSequence = LookupSequence(STRING(m_iszLockedSequence)); m_iCurSequence = INTERACTSEQ_LOCKED; } else { - m_OnPressed.FireOutput( pActivator, this ); + m_OnPressed.FireOutput(pActivator, this); EmitSound(STRING(m_iszPressedSound)); - nSequence = LookupSequence( STRING( m_iszInSequence ) ); + nSequence = LookupSequence(STRING(m_iszInSequence)); m_iCurSequence = INTERACTSEQ_IN; } - if ( nSequence > ACTIVITY_NOT_AVAILABLE ) + if (nSequence > ACTIVITY_NOT_AVAILABLE) { SetPushSequence(nSequence); // We still fire our inherited animation outputs - m_pOutputAnimBegun.FireOutput( pActivator, this ); + m_pOutputAnimBegun.FireOutput(pActivator, this); } - m_flCooldownTime = gpGlobals->curtime + m_flCooldown; + if (m_flCooldown == -1 && !m_bLocked){ + m_flCooldownTime = FLT_MAX; // yeah we're not going to hit this any time soon + } + else if (m_flCooldown == -1){ + m_flCooldownTime = gpGlobals->curtime + 1.0f; // 1s cooldown if locked + } + else{ + m_flCooldownTime = gpGlobals->curtime + m_flCooldown; + } } //----------------------------------------------------------------------------- @@ -4075,6 +4145,11 @@ enum void PlayLockSounds(CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton); +#ifdef MAPBASE +ConVar ai_door_enable_acts( "ai_door_enable_acts", "0", FCVAR_NONE, "Enables the new door-opening activities by default. Override keyvalues will override this cvar." ); +ConVar ai_door_open_dist_override( "ai_door_open_dist_override", "-1", FCVAR_NONE, "Overrides the distance from a door a NPC has to navigate to in order to open a door." ); +#endif + BEGIN_DATADESC_NO_BASE(locksound_t) DEFINE_FIELD( sLockedSound, FIELD_STRING), @@ -4101,6 +4176,11 @@ BEGIN_DATADESC(CBasePropDoor) DEFINE_KEYFIELD(m_SoundClose, FIELD_SOUNDNAME, "soundcloseoverride"), DEFINE_KEYFIELD(m_ls.sLockedSound, FIELD_SOUNDNAME, "soundlockedoverride"), DEFINE_KEYFIELD(m_ls.sUnlockedSound, FIELD_SOUNDNAME, "soundunlockedoverride"), +#ifdef MAPBASE + DEFINE_KEYFIELD(m_flNPCOpenDistance, FIELD_FLOAT, "opendistoverride"), + DEFINE_KEYFIELD(m_eNPCOpenFrontActivity, FIELD_INTEGER, "openfrontactivityoverride"), + DEFINE_KEYFIELD(m_eNPCOpenBackActivity, FIELD_INTEGER, "openbackactivityoverride"), +#endif DEFINE_KEYFIELD(m_SlaveName, FIELD_STRING, "slavename" ), DEFINE_FIELD(m_bLocked, FIELD_BOOLEAN), //DEFINE_KEYFIELD(m_flBlockDamage, FIELD_FLOAT, "dmg"), @@ -4145,6 +4225,11 @@ END_SEND_TABLE() CBasePropDoor::CBasePropDoor( void ) { m_hMaster = NULL; +#ifdef MAPBASE + m_flNPCOpenDistance = -1; + m_eNPCOpenFrontActivity = ACT_INVALID; + m_eNPCOpenBackActivity = ACT_INVALID; +#endif } //----------------------------------------------------------------------------- @@ -4216,6 +4301,32 @@ void CBasePropDoor::Precache(void) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Handles keyvalues from the BSP. Called before spawning. +//----------------------------------------------------------------------------- +bool CBasePropDoor::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq(szKeyName, "openfrontactivityoverride") ) + { + m_eNPCOpenFrontActivity = (Activity)CAI_BaseNPC::GetActivityID( szValue ); + if (m_eNPCOpenFrontActivity == ACT_INVALID) + m_eNPCOpenFrontActivity = ActivityList_RegisterPrivateActivity( szValue ); + } + else if ( FStrEq(szKeyName, "openbackactivityoverride") ) + { + m_eNPCOpenBackActivity = (Activity)CAI_BaseNPC::GetActivityID( szValue ); + if (m_eNPCOpenBackActivity == ACT_INVALID) + m_eNPCOpenBackActivity = ActivityList_RegisterPrivateActivity( szValue ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -4342,8 +4453,44 @@ void CBasePropDoor::CalcDoorSounds() { strSoundLocked = AllocPooledString( pkvHardwareData->GetString( "locked" ) ); strSoundUnlocked = AllocPooledString( pkvHardwareData->GetString( "unlocked" ) ); + +#ifdef MAPBASE + if (ai_door_enable_acts.GetBool()) + { + if (m_eNPCOpenFrontActivity == ACT_INVALID) + { + const char *pszActivity = pkvHardwareData->GetString( "activity_front" ); + if (pszActivity[0] != '\0') + { + m_eNPCOpenFrontActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity ); + if (m_eNPCOpenFrontActivity == ACT_INVALID) + m_eNPCOpenFrontActivity = ActivityList_RegisterPrivateActivity( pszActivity ); + } + } + if (m_eNPCOpenBackActivity == ACT_INVALID) + { + const char *pszActivity = pkvHardwareData->GetString( "activity_back" ); + if (pszActivity[0] != '\0') + { + m_eNPCOpenBackActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity ); + if (m_eNPCOpenBackActivity == ACT_INVALID) + m_eNPCOpenBackActivity = ActivityList_RegisterPrivateActivity( pszActivity ); + } + } + } + + if (m_flNPCOpenDistance == -1) + m_flNPCOpenDistance = pkvHardwareData->GetFloat( "npc_distance", ai_door_enable_acts.GetBool() ? 32.0 : 64.0 ); +#endif } +#ifdef MAPBASE + // This would still be -1 if the hardware wasn't valid + if (m_flNPCOpenDistance == -1) + m_flNPCOpenDistance = ai_door_enable_acts.GetBool() ? 32.0 : 64.0; +#endif + + // If any sounds were missing, try the "defaults" block. if ( ( strSoundOpen == NULL_STRING ) || ( strSoundClose == NULL_STRING ) || ( strSoundMoving == NULL_STRING ) || ( strSoundLocked == NULL_STRING ) || ( strSoundUnlocked == NULL_STRING ) ) @@ -5963,20 +6110,37 @@ void CPropDoorRotating::GetNPCOpenData(CAI_BaseNPC *pNPC, opendata_t &opendata) Vector vecNPCOrigin = pNPC->GetAbsOrigin(); +#ifdef MAPBASE + float flPosOffset = ai_door_open_dist_override.GetFloat() >= 0.0f ? ai_door_open_dist_override.GetFloat() : GetNPCOpenDistance(); +#else + float flPosOffset = 64; +#endif + if (pNPC->GetAbsOrigin().Dot(vecForward) > GetAbsOrigin().Dot(vecForward)) { // In front of the door relative to the door's forward vector. - opendata.vecStandPos += vecForward * 64; + opendata.vecStandPos += vecForward * flPosOffset; opendata.vecFaceDir = -vecForward; +#ifdef MAPBASE + opendata.eActivity = GetNPCOpenFrontActivity(); +#endif } else { // Behind the door relative to the door's forward vector. - opendata.vecStandPos -= vecForward * 64; + opendata.vecStandPos -= vecForward * flPosOffset; opendata.vecFaceDir = vecForward; +#ifdef MAPBASE + opendata.eActivity = GetNPCOpenBackActivity(); +#endif } +#ifdef MAPBASE + if (opendata.eActivity == ACT_INVALID) + opendata.eActivity = ACT_OPEN_DOOR; +#else opendata.eActivity = ACT_OPEN_DOOR; +#endif } @@ -6008,6 +6172,14 @@ int CPropDoorRotating::DrawDebugTextOverlays(void) EntityText( text_offset, tempstr, 0); text_offset++; +#ifdef MAPBASE // From Alien Swarm SDK + if ( IsDoorLocked() ) + { + EntityText( text_offset, "LOCKED", 0); + text_offset++; + } +#endif + if ( IsDoorOpen() ) { Q_strncpy(tempstr, "DOOR STATE: OPEN", sizeof(tempstr)); diff --git a/mp/src/game/server/props.h b/mp/src/game/server/props.h index 66da8131..e830713a 100644 --- a/mp/src/game/server/props.h +++ b/mp/src/game/server/props.h @@ -329,6 +329,9 @@ public: // Input handlers void InputSetAnimation( inputdata_t &inputdata ); +#ifdef MAPBASE // From Alien Swarm SDK + void InputSetAnimationNoReset( inputdata_t &inputdata ); +#endif void InputSetDefaultAnimation( inputdata_t &inputdata ); void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); @@ -345,6 +348,9 @@ public: int m_iTransitionDirection; // Random animations +#ifdef MAPBASE // From Alien Swarm SDK + bool m_bHoldAnimation; +#endif bool m_bRandomAnimator; float m_flNextRandAnim; float m_flMinRandAnimTime; @@ -353,6 +359,9 @@ public: bool m_bStartDisabled; bool m_bDisableBoneFollowers; +#ifdef MAPBASE // From Alien Swarm SDK + bool m_bUpdateAttachedChildren; // For props with children on attachment points, update their child touches as we animate +#endif CNetworkVar( bool, m_bUseHitboxesForRenderBox ); diff --git a/mp/src/game/server/rope.cpp b/mp/src/game/server/rope.cpp index 516015fe..23f44c39 100644 --- a/mp/src/game/server/rope.cpp +++ b/mp/src/game/server/rope.cpp @@ -90,6 +90,10 @@ BEGIN_DATADESC( CRopeKeyframe ) DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ), #ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSlack", InputSetSlack ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetWidth", InputSetWidth ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSubdivision", InputSetSubdivision ), + // Outputs DEFINE_OUTPUT( m_OnBreak, "OnBreak" ), #endif @@ -613,6 +617,51 @@ void CRopeKeyframe::InputBreak( inputdata_t &inputdata ) #endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets the slack +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CRopeKeyframe::InputSetSlack( inputdata_t &inputdata ) +{ + m_Slack = inputdata.value.Int(); + + // Must resize in order for changes to occur + m_RopeFlags |= ROPE_RESIZE; + + if (!(m_RopeFlags & ROPE_USE_WIND)) + { + Warning( "WARNING: SetSlack on %s may need wind enabled in order to function\n", GetDebugName() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the width +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CRopeKeyframe::InputSetWidth( inputdata_t &inputdata ) +{ + m_Width = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the subdivision +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CRopeKeyframe::InputSetSubdivision( inputdata_t &inputdata ) +{ + m_Subdiv = inputdata.value.Int(); + + // Must resize in order for changes to occur + m_RopeFlags |= ROPE_RESIZE; + + if (!(m_RopeFlags & ROPE_USE_WIND)) + { + Warning( "WARNING: SetSubdivision on %s may need wind enabled in order to function\n", GetDebugName() ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: Breaks the rope // Output : Returns true on success, false on failure. diff --git a/mp/src/game/server/rope.h b/mp/src/game/server/rope.h index 74037800..44fc7102 100644 --- a/mp/src/game/server/rope.h +++ b/mp/src/game/server/rope.h @@ -102,6 +102,11 @@ public: void InputSetScrollSpeed( inputdata_t &inputdata ); void InputSetForce( inputdata_t &inputdata ); void InputBreak( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputSetSlack( inputdata_t &inputdata ); + void InputSetWidth( inputdata_t &inputdata ); + void InputSetSubdivision( inputdata_t &inputdata ); +#endif public: diff --git a/mp/src/game/server/sceneentity.cpp b/mp/src/game/server/sceneentity.cpp index 78ec120e..74bcdb24 100644 --- a/mp/src/game/server/sceneentity.cpp +++ b/mp/src/game/server/sceneentity.cpp @@ -32,6 +32,7 @@ #include "scenefilecache/ISceneFileCache.h" #include "SceneCache.h" #include "scripted.h" +#include "basemultiplayerplayer.h" #include "env_debughistory.h" #include "team.h" #include "triggers.h" @@ -159,6 +160,7 @@ public: #ifdef MAPBASE bool IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes, const char *pszNotThisScene = NULL ); + bool IsTalkingInAScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); CUtlVector< CHandle< CSceneEntity > > *GetActiveSceneList(); #endif @@ -485,6 +487,9 @@ public: bool HasUnplayedSpeech( void ); bool HasFlexAnimation( void ); +#ifdef MAPBASE + bool IsPlayingSpeech( void ); +#endif void SetCurrentTime( float t, bool forceClientSync ); @@ -1157,6 +1162,31 @@ bool CSceneEntity::HasFlexAnimation( void ) return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSceneEntity::IsPlayingSpeech( void ) +{ + if ( m_pScene ) + { + float flTime = m_pScene->GetTime(); + for ( int i = 0; i < m_pScene->GetNumEvents(); i++ ) + { + CChoreoEvent *e = m_pScene->GetEvent( i ); + if ( e->GetType() == CChoreoEvent::SPEAK ) + { + if ( /*flTime >= e->GetStartTime() &&*/ flTime <= e->GetEndTime() ) + return true; + } + } + } + + return false; +} +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -2337,7 +2367,37 @@ void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata ) { CUtlString modifiers("scene:"); modifiers += STRING( GetEntityName() ); +#ifdef NEW_RESPONSE_SYSTEM + while (candidates.Count() > 0) + { + // Pick a random slot in the candidates array. + int slot = RandomInt( 0, candidates.Count() - 1 ); + CAI_BaseActor *npc = candidates[ slot ]; + + // Try to find the response for this slot. + AI_Response response; + CAI_Concept concept(inputdata.value.String()); + concept.SetSpeaker(npc); + AI_CriteriaSet set; + npc->GatherCriteria(&set, concept, modifiers.Get()); + bool result = npc->FindResponse( response, concept, &set); + if ( result ) + { + float duration = npc->GetResponseDuration( &response ); + + if ( ( duration > 0.0f ) && npc->PermitResponse( duration ) ) + { + // If we could look it up, dispatch it and bail. + npc->SpeakDispatchResponse( concept, &response, &set); + return; + } + } + + // Remove this entry and look for another one. + candidates.FastRemove(slot); + } +#else while (candidates.Count() > 0) { // Pick a random slot in the candidates array. @@ -2363,6 +2423,7 @@ void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata ) // Remove this entry and look for another one. candidates.FastRemove(slot); } +#endif } } @@ -2540,6 +2601,30 @@ bool CSceneEntity::CheckActors() return false; } } +#ifdef MAPBASE + else if ( pTestActor->IsPlayer() ) + { + // Blixibon - Player speech handling + CBasePlayer *pPlayer = static_cast(pTestActor); + bool bShouldWait = false; + if ( pPlayer->GetExpresser() && pPlayer->GetExpresser()->IsSpeaking() ) + { + bShouldWait = true; + } + else if ( IsTalkingInAScriptedScene( pPlayer ) ) + { + bShouldWait = true; + } + + if ( bShouldWait ) + { + // One of the actors for this scene is talking already. + // Try again next think. + m_bWaitingForActor = true; + return false; + } + } +#endif } else if ( m_BusyActor == SCENE_BUSYACTOR_INTERRUPT || m_BusyActor == SCENE_BUSYACTOR_INTERRUPT_CANCEL ) { @@ -2984,7 +3069,12 @@ void CSceneEntity::QueueResumePlayback( void ) if ( pBaseActor ) { AI_Response response; +#ifdef NEW_RESPONSE_SYSTEM + CAI_Concept concept(STRING(m_iszResumeSceneFile)); + bool result = pBaseActor->FindResponse( response, concept, NULL ); +#else bool result = pBaseActor->SpeakFindResponse( response, STRING(m_iszResumeSceneFile), NULL ); +#endif if ( result ) { const char *szResponse = response.GetResponsePtr(); @@ -3425,7 +3515,10 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv if ( IsMultiplayer() ) break; - DispatchStartPermitResponses( scene, pActor, event ); + if ( pActor ) + { + DispatchStartPermitResponses( scene, pActor, event ); + } } break; default: @@ -3583,7 +3676,10 @@ void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEven if ( IsMultiplayer() ) break; - DispatchEndPermitResponses( scene, pActor, event ); + if ( pActor ) + { + DispatchEndPermitResponses( scene, pActor, event ); + } } break; default: @@ -3646,7 +3742,7 @@ CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallbac Q_FixSlashes( loadfile ); // binary compiled vcd - void *pBuffer; + void *pBuffer = NULL; #ifdef MAPBASE // // Raw scene file support @@ -3671,12 +3767,13 @@ CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallbac { g_TokenProcessor.SetBuffer((char*)pBuffer); pScene = ChoreoLoadScene( loadfile, NULL, &g_TokenProcessor, LocalScene_Printf ); + g_TokenProcessor.SetBuffer(NULL); } // Okay, it's definitely missing. else { MissingSceneWarning( loadfile ); - return NULL; + pScene = NULL; } if (pScene) @@ -4194,6 +4291,7 @@ CBaseEntity *CSceneEntity::FindNamedEntity( const char *name, CBaseEntity *pActo #ifdef MAPBASE const char *GetFirstSoundInScene(const char *pszScene) { + const char *soundName = NULL; SceneCachedData_t sceneData; if ( scenefilecache->GetSceneCachedData( pszScene, &sceneData ) ) { @@ -4203,16 +4301,17 @@ const char *GetFirstSoundInScene(const char *pszScene) short stringId = scenefilecache->GetSceneCachedSound( sceneData.sceneId, 0 ); // Trust that it's been precached - return scenefilecache->GetSceneString( stringId ); + soundName = scenefilecache->GetSceneString( stringId ); } } else { void *pBuffer = NULL; - if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, false, true )) + if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, true )) { g_TokenProcessor.SetBuffer((char*)pBuffer); CChoreoScene *pScene = ChoreoLoadScene( pszScene, NULL, &g_TokenProcessor, LocalScene_Printf ); + g_TokenProcessor.SetBuffer(NULL); if (pScene) { for (int i = 0; i < pScene->GetNumEvents(); i++) @@ -4220,13 +4319,17 @@ const char *GetFirstSoundInScene(const char *pszScene) CChoreoEvent *pEvent = pScene->GetEvent(i); if (pEvent->GetType() == CChoreoEvent::SPEAK) - return pEvent->GetParameters(); + { + soundName = pEvent->GetParameters(); + break; + } } } } + FreeSceneFileMemory( pBuffer ); } - return NULL; + return soundName; } const char *GetFirstSoundInScene(CChoreoScene *scene) @@ -4394,6 +4497,8 @@ bool CSceneEntity::ScriptLoadSceneFromString(const char* pszFilename, const char PrecacheScene(pScene); } + g_TokenProcessor.SetBuffer(NULL); + if (pScene != NULL) { // release prior scene if present @@ -4747,6 +4852,33 @@ void CSceneEntity::OnSceneFinished( bool canceled, bool fireoutput ) m_OnCompletion.FireOutput( this, this, 0 ); } +#ifdef NEW_RESPONSE_SYSTEM + { + CBaseFlex *pFlex = FindNamedActor( 0 ) ; + if ( pFlex ) + { +#ifdef MAPBASE + CBasePlayer *pAsPlayer = ToBasePlayer(pFlex); +#else + CBaseMultiplayerPlayer *pAsPlayer = dynamic_cast(pFlex); +#endif + if (pAsPlayer) + { + CAI_Expresser *pExpresser = pAsPlayer->GetExpresser(); +#ifdef MAPBASE + if (pExpresser) +#endif + pExpresser->OnSpeechFinished(); + } + else if ( CAI_BaseActor *pActor = dynamic_cast( pFlex ) ) + { + CAI_Expresser *pExpresser = pActor->GetExpresser(); + pExpresser->OnSpeechFinished(); + } + } + } +#endif + // Put face back in neutral pose ClearSceneEvents( m_pScene, canceled ); @@ -5124,6 +5256,34 @@ float GetSceneDuration( char const *pszScene ) return (float)msecs * 0.001f; } +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pszScene - +// Output : float +//----------------------------------------------------------------------------- +float GetSceneSpeechDuration( char const* pszScene ) +{ + float flSecs = 0.0f; + + CChoreoScene* pScene = CSceneEntity::LoadScene( pszScene, nullptr ); + if (pScene) + { + for (int i = pScene->GetNumEvents() - 1; i >= 0; i--) + { + CChoreoEvent* pEvent = pScene->GetEvent( i ); + + if (pEvent->GetType() == CChoreoEvent::SPEAK) + { + flSecs = pEvent->GetStartTime() + pEvent->GetDuration(); + break; + } + } + delete pScene; + } + + return flSecs; +} + //----------------------------------------------------------------------------- // Purpose: // Input : *pszScene - @@ -5140,12 +5300,12 @@ int GetSceneSpeechCount( char const *pszScene ) else { void *pBuffer = NULL; - if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, false, true )) + int iNumSounds = 0; + if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, true )) { - int iNumSounds = 0; - g_TokenProcessor.SetBuffer((char*)pBuffer); CChoreoScene *pScene = ChoreoLoadScene( pszScene, NULL, &g_TokenProcessor, LocalScene_Printf ); + g_TokenProcessor.SetBuffer(NULL); if (pScene) { for (int i = 0; i < pScene->GetNumEvents(); i++) @@ -5156,9 +5316,11 @@ int GetSceneSpeechCount( char const *pszScene ) iNumSounds++; } } - - return iNumSounds; } + + FreeSceneFileMemory( pBuffer ); + + return iNumSounds; } #endif return 0; @@ -5215,7 +5377,7 @@ void PrecacheInstancedScene( char const *pszScene ) // Attempt to precache manually void *pBuffer = NULL; - if (filesystem->ReadFileEx( loadfile, "MOD", &pBuffer, false, true )) + if (filesystem->ReadFileEx( loadfile, "MOD", &pBuffer, true )) { g_TokenProcessor.SetBuffer((char*)pBuffer); CChoreoScene *pScene = ChoreoLoadScene( loadfile, NULL, &g_TokenProcessor, LocalScene_Printf ); @@ -5223,7 +5385,9 @@ void PrecacheInstancedScene( char const *pszScene ) { PrecacheChoreoScene(pScene); } + g_TokenProcessor.SetBuffer(NULL); } + FreeSceneFileMemory( pBuffer ); #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. @@ -5896,6 +6060,31 @@ bool CSceneManager::IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActo } +bool CSceneManager::IsTalkingInAScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes ) +{ + 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 ) + ) + { + continue; + } + + if ( pScene->InvolvesActor( pActor ) ) + { + if ( pScene->IsPlayingSpeech() ) + return true; + } + } + return false; +} + + CUtlVector< CHandle< CSceneEntity > > *CSceneManager::GetActiveSceneList() { return &m_ActiveScenes; @@ -5994,6 +6183,11 @@ bool IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnore return GetSceneManager()->IsRunningScriptedSceneWithFlexAndNotPaused( pActor, bIgnoreInstancedScenes, pszNotThisScene ); } +bool IsTalkingInAScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes ) +{ + return GetSceneManager()->IsTalkingInAScriptedScene( pActor, bIgnoreInstancedScenes ); +} + CUtlVector< CHandle< CSceneEntity > > *GetActiveSceneList() { return GetSceneManager()->GetActiveSceneList(); diff --git a/mp/src/game/server/sceneentity.h b/mp/src/game/server/sceneentity.h index 6e005a60..ea286fe9 100644 --- a/mp/src/game/server/sceneentity.h +++ b/mp/src/game/server/sceneentity.h @@ -13,7 +13,9 @@ // List of the last 5 lines of speech from NPCs for bug reports #define SPEECH_LIST_MAX_SOUNDS 5 +#ifndef NEW_RESPONSE_SYSTEM class AI_Response; +#endif struct recentNPCSpeech_t { @@ -41,9 +43,11 @@ bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedS bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); #ifdef MAPBASE bool IsRunningScriptedSceneWithFlexAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false, const char *pszNotThisScene = NULL ); +bool IsTalkingInAScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); CUtlVector< CHandle< CSceneEntity > > *GetActiveSceneList(); #endif float GetSceneDuration( char const *pszScene ); +float GetSceneSpeechDuration( char const* pszScene ); int GetSceneSpeechCount( char const *pszScene ); bool IsInInterruptableScenes( CBaseFlex *pActor ); diff --git a/mp/src/game/server/scripted.cpp b/mp/src/game/server/scripted.cpp index 19fc6784..37b45844 100644 --- a/mp/src/game/server/scripted.cpp +++ b/mp/src/game/server/scripted.cpp @@ -136,6 +136,8 @@ BEGIN_DATADESC( CAI_ScriptedSequence ) DEFINE_OUTPUT(m_OnScriptEvent[6], "OnScriptEvent07"), DEFINE_OUTPUT(m_OnScriptEvent[7], "OnScriptEvent08"), #ifdef MAPBASE + DEFINE_OUTPUT(m_OnEntrySequence, "OnEntrySequence"), + DEFINE_OUTPUT(m_OnActionSequence, "OnActionSequence"), DEFINE_OUTPUT(m_OnPreIdleSequence, "OnPreIdleSequence"), DEFINE_OUTPUT(m_OnFoundNPC, "OnFoundNPC"), #endif @@ -864,6 +866,16 @@ void CAI_ScriptedSequence::OnBeginSequence( CBaseEntity *pActor ) m_OnBeginSequence.FireOutput( pActor, this ); } +void CAI_ScriptedSequence::OnEntrySequence( CBaseEntity *pActor ) +{ + m_OnEntrySequence.FireOutput( pActor, this ); +} + +void CAI_ScriptedSequence::OnActionSequence( CBaseEntity *pActor ) +{ + m_OnActionSequence.FireOutput( pActor, this ); +} + void CAI_ScriptedSequence::OnPreIdleSequence( CBaseEntity *pActor ) { m_OnPreIdleSequence.FireOutput( pActor, this ); diff --git a/mp/src/game/server/scripted.h b/mp/src/game/server/scripted.h index 1b7398a4..7b1d8254 100644 --- a/mp/src/game/server/scripted.h +++ b/mp/src/game/server/scripted.h @@ -97,6 +97,8 @@ public: void FireScriptEvent( int nEvent ); #ifdef MAPBASE void OnBeginSequence( CBaseEntity *pActor ); + void OnEntrySequence( CBaseEntity *pActor ); + void OnActionSequence( CBaseEntity *pActor ); void OnPreIdleSequence( CBaseEntity *pActor ); #else void OnBeginSequence( void ); @@ -220,6 +222,8 @@ private: COutputEvent m_OnCancelFailedSequence; // Fired when a scene is cancelled before it's ever run COutputEvent m_OnScriptEvent[MAX_SCRIPT_EVENTS]; #ifdef MAPBASE + COutputEvent m_OnEntrySequence; + COutputEvent m_OnActionSequence; COutputEvent m_OnPreIdleSequence; COutputEvent m_OnFoundNPC; #endif diff --git a/mp/src/game/server/server_base.vpc b/mp/src/game/server/server_base.vpc index df35da1e..64558734 100644 --- a/mp/src/game/server/server_base.vpc +++ b/mp/src/game/server/server_base.vpc @@ -126,8 +126,10 @@ $Project $File "ai_concommands.cpp" $File "ai_condition.cpp" $File "ai_condition.h" - $File "AI_Criteria.cpp" + $File "AI_Criteria.cpp" [!$NEW_RESPONSE_SYSTEM] $File "AI_Criteria.h" + $File "$SRCDIR\game\shared\ai_criteria_new.cpp" [$NEW_RESPONSE_SYSTEM] + $File "$SRCDIR\game\shared\ai_criteria_new.h" [$NEW_RESPONSE_SYSTEM] $File "ai_debug.h" $File "$SRCDIR\game\shared\ai_debug_shared.h" $File "ai_default.cpp" @@ -182,8 +184,10 @@ $Project $File "ai_planesolver.h" $File "ai_playerally.cpp" $File "ai_playerally.h" - $File "AI_ResponseSystem.cpp" + $File "AI_ResponseSystem.cpp" [!$NEW_RESPONSE_SYSTEM] $File "AI_ResponseSystem.h" + $File "$SRCDIR\game\shared\ai_responsesystem_new.cpp" [$NEW_RESPONSE_SYSTEM] + $File "$SRCDIR\game\shared\ai_responsesystem_new.h" [$NEW_RESPONSE_SYSTEM] $File "ai_route.cpp" $File "ai_route.h" $File "ai_routedist.h" @@ -197,8 +201,10 @@ $Project $File "ai_senses.h" $File "ai_sentence.cpp" $File "ai_sentence.h" - $File "ai_speech.cpp" + $File "ai_speech.cpp" [!$NEW_RESPONSE_SYSTEM] $File "ai_speech.h" + $File "ai_speech_new.cpp" [$NEW_RESPONSE_SYSTEM] + $File "ai_speech_new.h" [$NEW_RESPONSE_SYSTEM] $File "ai_speechfilter.cpp" $File "ai_speechfilter.h" $File "ai_squad.cpp" @@ -428,7 +434,6 @@ $Project $File "init_factory.h" $File "intermission.cpp" $File "$SRCDIR\public\interpolatortypes.h" - $File "$SRCDIR\game\shared\interval.h" $File "$SRCDIR\public\iregistry.h" $File "$SRCDIR\game\shared\iscenetokenprocessor.h" $File "iservervehicle.h" @@ -686,7 +691,6 @@ $Project "h_export.cpp" \ "init_factory.cpp" \ "$SRCDIR\public\interpolatortypes.cpp" \ - "$SRCDIR\game\shared\interval.cpp" \ "$SRCDIR\public\keyframe\keyframe.cpp" \ "$SRCDIR\common\language.cpp" \ "$SRCDIR\public\map_utils.cpp" \ @@ -1000,6 +1004,7 @@ $Project $File "$SRCDIR\public\winlite.h" $File "$SRCDIR\public\worldsize.h" $File "$SRCDIR\public\zip_uncompressed.h" + $File "$SRCDIR\public\tier1\interval.h" $File "$SRCDIR\game\shared\mp_shareddefs.h" $File "$SRCDIR\game\shared\econ\ihasowner.h" //Haptics diff --git a/mp/src/game/server/server_mapbase.vpc b/mp/src/game/server/server_mapbase.vpc index 40581e0f..b04706d4 100644 --- a/mp/src/game/server/server_mapbase.vpc +++ b/mp/src/game/server/server_mapbase.vpc @@ -10,6 +10,7 @@ $Configuration { $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS;GLOWS_ENABLE" $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] + $PreprocessorDefinitions "$BASE;NEW_RESPONSE_SYSTEM" [$NEW_RESPONSE_SYSTEM] } } @@ -27,16 +28,26 @@ $Project $File "postprocesscontroller.h" $File "env_dof_controller.cpp" $File "env_dof_controller.h" + $File "logic_playmovie.cpp" + $File "movie_display.cpp" + $File "fogvolume.cpp" + $File "fogvolume.h" + $File "ai_expresserfollowup.cpp" [$NEW_RESPONSE_SYSTEM] + $File "ai_speechqueue.cpp" [$NEW_RESPONSE_SYSTEM] + $File "ai_speechqueue.h" [$NEW_RESPONSE_SYSTEM] $Folder "Mapbase" { $File "$SRCDIR\game\shared\mapbase\mapbase_shared.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_usermessages.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\singleplayer_animstate.cpp" + $File "$SRCDIR\game\shared\mapbase\singleplayer_animstate.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] @@ -46,6 +57,7 @@ $Project $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 "$SRCDIR\game\shared\mapbase\logic_script_client.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\ai_grenade.cpp" $File "mapbase\ai_grenade.h" @@ -61,6 +73,7 @@ $Project $File "mapbase\GlobalStrings.h" $File "mapbase\logic_externaldata.cpp" $File "mapbase\logic_skill.cpp" + $File "mapbase\logic_substring.cpp" $File "mapbase\point_advanced_finder.cpp" $File "mapbase\point_copy_size.cpp" $File "mapbase\point_damageinfo.cpp" @@ -72,6 +85,7 @@ $Project $File "mapbase\SystemConvarMod.cpp" $File "mapbase\SystemConvarMod.h" $File "mapbase\variant_tools.h" + $File "mapbase\vgui_text_display.cpp" $File "mapbase\logic_eventlistener.cpp" $File "mapbase\logic_register_activator.cpp" @@ -102,5 +116,6 @@ $Project $Folder "Link Libraries" { $Lib "vscript" [$MAPBASE_VSCRIPT] + $Lib "responserules" [$NEW_RESPONSE_SYSTEM] } } diff --git a/mp/src/game/server/triggers.cpp b/mp/src/game/server/triggers.cpp index 06246448..2226f4bc 100644 --- a/mp/src/game/server/triggers.cpp +++ b/mp/src/game/server/triggers.cpp @@ -1313,7 +1313,7 @@ void CTriggerLook::Touch(CBaseEntity *pOther) VectorNormalize(vTargetDir); float fDotPr = DotProduct(vLookDir,vTargetDir); - if (fDotPr > m_flFieldOfView && (!m_bUseLOS || pOther->FVisible(pOther))) + if (fDotPr > m_flFieldOfView && (!m_bUseLOS || pOther->FVisible(m_hLookTargets[i]))) { hLookingAtEntity = m_hLookTargets[i]; break; @@ -3131,6 +3131,7 @@ BEGIN_DATADESC( CTriggerCamera ) #ifdef MAPBASE DEFINE_KEYFIELD( m_fov, FIELD_FLOAT, "fov" ), DEFINE_KEYFIELD( m_fovSpeed, FIELD_FLOAT, "fov_rate" ), + DEFINE_KEYFIELD( m_flTrackSpeed, FIELD_FLOAT, "trackspeed" ), DEFINE_KEYFIELD( m_bDontSetPlayerView, FIELD_BOOLEAN, "DontSetPlayerView" ), #endif @@ -3141,6 +3142,10 @@ BEGIN_DATADESC( CTriggerCamera ) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOV", InputSetFOV ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOVRate", InputSetFOVRate ), + + //DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), // Defined by base class + DEFINE_INPUTFUNC( FIELD_STRING, "SetTargetAttachment", InputSetTargetAttachment ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTrackSpeed", InputSetTrackSpeed ), #endif // Function Pointers @@ -3169,6 +3174,7 @@ CTriggerCamera::CTriggerCamera() { m_fov = 90; m_fovSpeed = 1; + m_flTrackSpeed = 40.0f; } //------------------------------------------------------------------------------ @@ -3290,6 +3296,61 @@ void CTriggerCamera::InputSetFOVRate( inputdata_t &inputdata ) { m_fovSpeed = inputdata.value.Float(); } + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetTarget( inputdata_t &inputdata ) +{ + BaseClass::InputSetTarget( inputdata ); + + if ( FStrEq(STRING(m_target), "!player") ) + { + AddSpawnFlags( SF_CAMERA_PLAYER_TARGET ); + m_hTarget = m_hPlayer; + } + else + { + RemoveSpawnFlags( SF_CAMERA_PLAYER_TARGET ); + m_hTarget = GetNextTarget(); + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetTargetAttachment( inputdata_t &inputdata ) +{ + m_iszTargetAttachment = inputdata.value.StringID(); + m_iAttachmentIndex = 0; + + if (m_hTarget) + { + if ( m_iszTargetAttachment != NULL_STRING ) + { + if ( !m_hTarget->GetBaseAnimating() ) + { + Warning("%s tried to target an attachment (%s) on target %s, which has no model.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) ); + } + else + { + m_iAttachmentIndex = m_hTarget->GetBaseAnimating()->LookupAttachment( STRING(m_iszTargetAttachment) ); + if ( m_iAttachmentIndex <= 0 ) + { + Warning("%s could not find attachment %s on target %s.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) ); + } + } + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetTrackSpeed( inputdata_t &inputdata ) +{ + m_flTrackSpeed = inputdata.value.Float(); +} #endif //----------------------------------------------------------------------------- @@ -3467,12 +3528,14 @@ void CTriggerCamera::Enable( void ) #ifdef MAPBASE if (!m_bDontSetPlayerView) #endif - pPlayer->SetViewEntity( this ); - - // Hide the player's viewmodel - if ( pPlayer->GetActiveWeapon() ) { - pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW ); + pPlayer->SetViewEntity( this ); + + // Hide the player's viewmodel + if ( pPlayer->GetActiveWeapon() ) + { + pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW ); + } } // Only track if we have a target @@ -3518,11 +3581,16 @@ void CTriggerCamera::Disable( void ) pBasePlayer->RemoveSolidFlags( FSOLID_NOT_SOLID ); } - if (!m_bDontSetPlayerView) - pBasePlayer->SetViewEntity( NULL ); + if ( HasSpawnFlags( SF_CAMERA_PLAYER_TAKECONTROL ) ) + { + pBasePlayer->EnableControl( TRUE ); + } - pBasePlayer->EnableControl(TRUE); - pBasePlayer->m_Local.m_bDrawViewmodel = true; + if (!m_bDontSetPlayerView) + { + pBasePlayer->SetViewEntity( NULL ); + pBasePlayer->m_Local.m_bDrawViewmodel = true; + } } if ( HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) ) @@ -3657,7 +3725,11 @@ void CTriggerCamera::FollowTarget( ) dy = dy - 360; QAngle vecAngVel; +#ifdef MAPBASE + vecAngVel.Init( dx * m_flTrackSpeed * gpGlobals->frametime, dy * m_flTrackSpeed * gpGlobals->frametime, GetLocalAngularVelocity().z ); +#else vecAngVel.Init( dx * 40 * gpGlobals->frametime, dy * 40 * gpGlobals->frametime, GetLocalAngularVelocity().z ); +#endif SetLocalAngularVelocity(vecAngVel); } diff --git a/mp/src/game/server/triggers.h b/mp/src/game/server/triggers.h index fe0aefe1..bab07059 100644 --- a/mp/src/game/server/triggers.h +++ b/mp/src/game/server/triggers.h @@ -316,6 +316,10 @@ public: #ifdef MAPBASE void InputSetFOV( inputdata_t &inputdata ); void InputSetFOVRate( inputdata_t &inputdata ); + + void InputSetTarget( inputdata_t &inputdata ); + void InputSetTargetAttachment( inputdata_t &inputdata ); + void InputSetTrackSpeed( inputdata_t &inputdata ); #endif private: @@ -339,6 +343,7 @@ private: #ifdef MAPBASE float m_fov; float m_fovSpeed; + float m_flTrackSpeed; bool m_bDontSetPlayerView; #endif diff --git a/mp/src/game/server/vscript_server.cpp b/mp/src/game/server/vscript_server.cpp index 63f0ac0d..40e35f00 100644 --- a/mp/src/game/server/vscript_server.cpp +++ b/mp/src/game/server/vscript_server.cpp @@ -13,11 +13,11 @@ #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" +#include "mapbase/vscript_singletons.h" #endif extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); @@ -37,10 +37,16 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #endif // VMPROFILE +#ifdef MAPBASE_VSCRIPT +static ScriptHook_t g_Hook_OnEntityCreated; +static ScriptHook_t g_Hook_OnEntitySpawned; +static ScriptHook_t g_Hook_OnEntityDeleted; +#endif + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -class CScriptEntityIterator +class CScriptEntityIterator : public IEntityListener { public: #ifdef MAPBASE_VSCRIPT @@ -110,6 +116,71 @@ public: { return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecMins, vecMaxs ) ); } + + HSCRIPT FindByClassNearestFacing( const Vector &origin, const Vector &facing, float threshold, const char *classname ) + { + return ToHScript( gEntList.FindEntityClassNearestFacing( origin, facing, threshold, const_cast(classname) ) ); + } + + HSCRIPT FindByClassnameNearest2D( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameNearest2D( szName, vecSrc, flRadius ) ); + } + + // + // Custom Procedurals + // + void AddCustomProcedural( const char *pszName, HSCRIPT hFunc, bool bCanReturnMultiple ) + { + gEntList.AddCustomProcedural( pszName, hFunc, bCanReturnMultiple ); + } + + void RemoveCustomProcedural( const char *pszName ) + { + gEntList.RemoveCustomProcedural( pszName ); + } + + void EnableEntityListening() + { + // Start getting entity updates! + gEntList.AddListenerEntity( this ); + } + + void DisableEntityListening() + { + // Stop getting entity updates! + gEntList.RemoveListenerEntity( this ); + } + + void OnEntityCreated( CBaseEntity *pEntity ) + { + if ( g_pScriptVM && GetScriptHookManager().IsEventHooked( "OnEntityCreated" ) ) + { + // entity + ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) }; + g_Hook_OnEntityCreated.Call( NULL, NULL, args ); + } + }; + + void OnEntitySpawned( CBaseEntity *pEntity ) + { + if ( g_pScriptVM && GetScriptHookManager().IsEventHooked( "OnEntitySpawned" ) ) + { + // entity + ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) }; + g_Hook_OnEntitySpawned.Call( NULL, NULL, args ); + } + }; + + void OnEntityDeleted( CBaseEntity *pEntity ) + { + if ( g_pScriptVM && GetScriptHookManager().IsEventHooked( "OnEntityDeleted" ) ) + { + // entity + ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) }; + g_Hook_OnEntityDeleted.Call( NULL, NULL, args ); + } + }; #endif private: } g_ScriptEntityIterator; @@ -132,6 +203,26 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETO 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" ) + DEFINE_SCRIPTFUNC( FindByClassNearestFacing, "Find the nearest entity along the facing direction from the given origin within the angular threshold with the given classname." ) + DEFINE_SCRIPTFUNC( FindByClassnameNearest2D, "Find entities by class name nearest to a point in 2D space." ) + + DEFINE_SCRIPTFUNC( AddCustomProcedural, "Adds a custom '!' target name. The first parameter is the name of the procedural (which should NOT include the '!'), the second parameter is a function which should support 5 arguments (name, startEntity, searchingEntity, activator, caller), and the third parameter is whether or not this procedural can return multiple entities. Note that these are NOT saved and must be redeclared on restore!" ) + DEFINE_SCRIPTFUNC( RemoveCustomProcedural, "Removes a custom '!' target name previously defined with AddCustomProcedural." ) + + DEFINE_SCRIPTFUNC( EnableEntityListening, "Enables the 'OnEntity' hooks. This function must be called before using them." ) + DEFINE_SCRIPTFUNC( DisableEntityListening, "Disables the 'OnEntity' hooks." ) + + BEGIN_SCRIPTHOOK( g_Hook_OnEntityCreated, "OnEntityCreated", FIELD_VOID, "Called when an entity is created. Requires EnableEntityListening() to be fired beforehand." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_OnEntitySpawned, "OnEntitySpawned", FIELD_VOID, "Called when an entity spawns. Requires EnableEntityListening() to be fired beforehand." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_OnEntityDeleted, "OnEntityDeleted", FIELD_VOID, "Called when an entity is deleted. Requires EnableEntityListening() to be fired beforehand." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -246,451 +337,6 @@ CScriptKeyValues::~CScriptKeyValues( ) } #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 - - //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -710,11 +356,6 @@ static int MaxPlayers() return gpGlobals->maxClients; } -static float IntervalPerTick() -{ - return gpGlobals->interval_per_tick; -} - static int GetLoadType() { return gpGlobals->eLoadType; @@ -773,7 +414,11 @@ static void DoEntFire( const char *pszTarget, const char *pszAction, const char // ent_fire point_servercommand command "rcon_password mynewpassword" if ( gpGlobals->maxClients > 1 && V_stricmp( target, "point_servercommand" ) == 0 ) { +#ifdef MAPBASE_VSCRIPT return 0; +#else + return; +#endif } if ( *pszAction ) @@ -892,12 +537,6 @@ 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() @@ -961,6 +600,10 @@ bool VScriptServerInit() Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); #endif +#ifdef MAPBASE_VSCRIPT + GetScriptHookManager().OnInit(); +#endif + #ifdef MAPBASE_VSCRIPT // MULTIPLAYER // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByIndex, "GetPlayerByIndex", "PlayerInstanceFromIndex" ); @@ -982,7 +625,6 @@ bool VScriptServerInit() #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." ) ); @@ -990,7 +632,6 @@ bool VScriptServerInit() 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." ); @@ -1010,9 +651,7 @@ bool VScriptServerInit() } g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); -#ifdef MAPBASE_VSCRIPT - g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" ); -#endif // MAPBASE_VSCRIPT + #ifdef MAPBASE_VSCRIPT g_pScriptVM->RegisterAllClasses(); @@ -1029,9 +668,12 @@ bool VScriptServerInit() g_pScriptVM->Run( g_Script_vscript_server ); } + VScriptRunScript( "vscript_server", true ); VScriptRunScript( "mapspawn", false ); #ifdef MAPBASE_VSCRIPT + RunAddonScripts(); + // 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(); @@ -1046,6 +688,12 @@ bool VScriptServerInit() CGWarning( 1, CON_GROUP_VSCRIPT, "VM Did not start!\n" ); } } +#ifdef MAPBASE_VSCRIPT + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "VSCRIPT SERVER: Not starting because language is set to 'none'\n" ); + } +#endif } else { @@ -1192,6 +840,11 @@ public: virtual void LevelShutdownPostEntity( void ) { +#ifdef MAPBASE_VSCRIPT + g_ScriptNetMsg->LevelShutdownPreVM(); + + GetScriptHookManager().OnShutdown(); +#endif VScriptServerTerm(); } @@ -1219,172 +872,3 @@ bool IsEntityCreationAllowedInScripts( void ) 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 index 6a7d44d1..2808f3eb 100644 --- a/mp/src/game/server/vscript_server.h +++ b/mp/src/game/server/vscript_server.h @@ -15,19 +15,7 @@ #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 ); diff --git a/mp/src/game/server/vscript_server.nut b/mp/src/game/server/vscript_server.nut index 17ba64d3..ad48da19 100644 --- a/mp/src/game/server/vscript_server.nut +++ b/mp/src/game/server/vscript_server.nut @@ -5,14 +5,16 @@ static char g_Script_vscript_server[] = R"vscript( // //============================================================================= +local DoEntFire = DoEntFire +local DoEntFireByInstanceHandle = DoEntFireByInstanceHandle +local DoDispatchParticleEffect = DoDispatchParticleEffect +local DoUniqueString = DoUniqueString + function UniqueString( string = "" ) { - return ::DoUniqueString( string.tostring() ); + return DoUniqueString( "" + string ); } -local DoEntFire = ::DoEntFire -local DoEntFireByInstanceHandle = ::DoEntFireByInstanceHandle - function EntFire( target, action, value = null, delay = 0.0, activator = null, caller = null ) { if ( !value ) @@ -33,7 +35,7 @@ function EntFire( target, action, value = null, delay = 0.0, activator = null, c } } - return DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); + return DoEntFire( "" + target, "" + action, "" + value, delay, activator, caller ); } function EntFireByHandle( target, action, value = null, delay = 0.0, activator = null, caller = null ) @@ -56,21 +58,31 @@ function EntFireByHandle( target, action, value = null, delay = 0.0, activator = } } - return DoEntFireByInstanceHandle( target, action.tostring(), value.tostring(), delay, activator, caller ); + return DoEntFireByInstanceHandle( target, "" + action, "" + value, delay, activator, caller ); } function DispatchParticleEffect( particleName, origin, angles, entity = null ) { - DoDispatchParticleEffect( particleName, origin, angles, entity ); + return DoDispatchParticleEffect( particleName, origin, angles, entity ); } -// CConvars is declared within the library -function CConvars::GetClientConvarValue(cvar,idx) +function ImpulseScale( flTargetMass, flDesiredSpeed ) { - return ::ScriptGetClientConvarValue(cvar,idx); + return flTargetMass * flDesiredSpeed; +} +__Documentation.RegisterHelp( "ImpulseScale", "float ImpulseScale(float, float)", "Returns an impulse scale required to push an object." ); + +local PrecacheModel = PrecacheModel +function PrecacheModel( a, b = true ) +{ + return PrecacheModel( a, b ) } -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." ); +local PrecacheOther = PrecacheOther +function PrecacheOther( a, b = "" ) +{ + PrecacheOther( a, b ) +} function __ReplaceClosures( script, scope ) { @@ -81,7 +93,7 @@ function __ReplaceClosures( script, scope ) local tempParent = { getroottable = function() { return null; } }; local temp = { runscript = script }; - temp.set_delegate(tempParent); + temp.setdelegate(tempParent); temp.runscript() foreach( key,val in temp ) diff --git a/mp/src/game/server/world.cpp b/mp/src/game/server/world.cpp index 628a79bb..fc3d59ae 100644 --- a/mp/src/game/server/world.cpp +++ b/mp/src/game/server/world.cpp @@ -395,7 +395,7 @@ BEGIN_DATADESC( CWorld ) 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" ), + //DEFINE_KEYFIELD( m_iScriptLanguageClient, FIELD_INTEGER, "vscriptlanguage_client" ), #endif DEFINE_KEYFIELD( m_bColdWorld, FIELD_BOOLEAN, "coldworld" ), @@ -421,9 +421,6 @@ IMPLEMENT_SERVERCLASS_ST(CWorld, DT_WORLD) #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() // @@ -485,7 +482,7 @@ CWorld::CWorld( ) #ifdef MAPBASE_VSCRIPT m_iScriptLanguage = SL_NONE; - m_iScriptLanguageClient = -2; + //m_iScriptLanguageClient = -2; #endif m_bColdWorld = false; @@ -552,14 +549,6 @@ 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[] = diff --git a/mp/src/game/server/world.h b/mp/src/game/server/world.h index 7e949450..5d75a64b 100644 --- a/mp/src/game/server/world.h +++ b/mp/src/game/server/world.h @@ -90,7 +90,7 @@ private: #ifdef MAPBASE_VSCRIPT int m_iScriptLanguage; - CNetworkVar( int, m_iScriptLanguageClient ); + //CNetworkVar( int, m_iScriptLanguageClient ); // Now entirely on client #endif // start flags diff --git a/mp/src/game/shared/GameEventListener.h b/mp/src/game/shared/GameEventListener.h index 42d2ebaa..9378257d 100644 --- a/mp/src/game/shared/GameEventListener.h +++ b/mp/src/game/shared/GameEventListener.h @@ -25,7 +25,7 @@ public: { } - ~CGameEventListener() + virtual ~CGameEventListener() { StopListeningForAllEvents(); } diff --git a/mp/src/game/shared/SoundEmitterSystem.cpp b/mp/src/game/shared/SoundEmitterSystem.cpp index 14bb9b8c..221bb9ad 100644 --- a/mp/src/game/shared/SoundEmitterSystem.cpp +++ b/mp/src/game/shared/SoundEmitterSystem.cpp @@ -132,6 +132,21 @@ void Hack_FixEscapeChars( char *str ) #ifdef MAPBASE static const ConVar *pHostTimescale; +static ConVar host_pitchscale( "host_pitchscale", "-1", FCVAR_REPLICATED, "If greater than 0, controls the pitch scale of sounds instead of host_timescale." ); + +static float GetSoundPitchScale() +{ + static ConVarRef sv_cheats( "sv_cheats" ); + if (sv_cheats.GetBool()) + { + if (host_pitchscale.GetFloat() > 0.0f) + return host_pitchscale.GetFloat(); + else + return pHostTimescale->GetFloat(); + } + + return 1.0f; +} #endif //----------------------------------------------------------------------------- @@ -543,7 +558,7 @@ public: (soundlevel_t)params.soundlevel, ep.m_nFlags, #ifdef MAPBASE - pHostTimescale->GetFloat() != 0.0f ? params.pitch * pHostTimescale->GetFloat() : params.pitch, + params.pitch * GetSoundPitchScale(), #else params.pitch, #endif @@ -626,7 +641,7 @@ public: ep.m_SoundLevel, ep.m_nFlags, #ifdef MAPBASE - pHostTimescale->GetFloat() != 0.0f ? ep.m_nPitch * pHostTimescale->GetFloat() : ep.m_nPitch, + ep.m_nPitch * GetSoundPitchScale(), #else ep.m_nPitch, #endif @@ -852,10 +867,7 @@ public: } #ifdef MAPBASE - if ( pHostTimescale->GetFloat() != 0.0f ) - { - params.pitch *= pHostTimescale->GetFloat(); - } + params.pitch *= GetSoundPitchScale(); #endif #if defined( CLIENT_DLL ) @@ -987,10 +999,7 @@ public: if ( pSample && ( Q_stristr( pSample, ".wav" ) || Q_stristr( pSample, ".mp3" )) ) { #ifdef MAPBASE - if ( pHostTimescale->GetFloat() != 0.0f ) - { - pitch *= pHostTimescale->GetFloat(); - } + pitch *= GetSoundPitchScale(); #endif #if defined( CLIENT_DLL ) @@ -1229,6 +1238,11 @@ void CBaseEntity::ScriptEmitSound( const char *soundname ) EmitSound( soundname ); } +void CBaseEntity::ScriptStopSound( const char *soundname ) +{ + StopSound( soundname ); +} + float CBaseEntity::ScriptSoundDuration( const char *soundname, const char *actormodel ) { float duration = CBaseEntity::GetSoundDuration( soundname, actormodel ); @@ -1415,6 +1429,52 @@ int SENTENCEG_Lookup(const char *sample) } #endif +#if defined(MAPBASE) && defined(GAME_DLL) +//----------------------------------------------------------------------------- +// Purpose: Wrapper to emit a sentence and also a close caption token for the sentence as appropriate. +// Input : filter - +// iEntIndex - +// iChannel - +// iSentenceIndex - +// flVolume - +// iSoundlevel - +// iFlags - +// iPitch - +// bUpdatePositions - +// soundtime - +//----------------------------------------------------------------------------- +void CBaseEntity::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex, + float flVolume, soundlevel_t iSoundlevel, int iFlags /*= 0*/, int iPitch /*=PITCH_NORM*/, + const Vector *pOrigin /*=NULL*/, const Vector *pDirection /*=NULL*/, + bool bUpdatePositions /*=true*/, float soundtime /*=0.0f*/, int iSpecialDSP /*= 0*/, int iSpeakerIndex /*= 0*/ ) +{ + CUtlVector< Vector > soundOrigins; + + bool bSwallowed = CEnvMicrophone::OnSentencePlayed( + iEntIndex, + iSentenceIndex, + iSoundlevel, + flVolume, + iFlags, + iPitch, + pOrigin, + soundtime, + soundOrigins ); + if ( bSwallowed ) + return; + + CBaseEntity *pEntity = UTIL_EntityByIndex( iEntIndex ); + if ( pEntity ) + { + pEntity->ModifySentenceParams( iSentenceIndex, iChannel, flVolume, iSoundlevel, iFlags, iPitch, + &pOrigin, &pDirection, bUpdatePositions, soundtime, iSpecialDSP, iSpeakerIndex ); + } + + enginesound->EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex, + flVolume, iSoundlevel, iFlags, iPitch * GetSoundPitchScale(), iSpecialDSP, pOrigin, pDirection, &soundOrigins, bUpdatePositions, soundtime, iSpeakerIndex ); +} +#endif + void UTIL_EmitAmbientSound( int entindex, const Vector &vecOrigin, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ ) { #ifdef STAGING_ONLY diff --git a/mp/src/game/shared/activitylist.cpp b/mp/src/game/shared/activitylist.cpp index b22edfc1..eaa3b94b 100644 --- a/mp/src/game/shared/activitylist.cpp +++ b/mp/src/game/shared/activitylist.cpp @@ -2372,15 +2372,22 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2 ); REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2 ); - //REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR2_LOW ); #endif -#ifdef SHARED_COMBINE_ACTIVITIES +#if SHARED_COMBINE_ACTIVITIES REGISTER_SHARED_ACTIVITY( ACT_COMBINE_THROW_GRENADE ); REGISTER_SHARED_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_THROW_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK2 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_ADVANCE ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_FORWARD ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_GROUP ); @@ -2390,9 +2397,474 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_TAKECOVER ); #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#if EXPANDED_HL2_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_IDLE_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_REVOLVER ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_CROSSBOW ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_AIM_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_AIM_PISTOL ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_SHOTGUN_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_LOW ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_RPG ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_ANNABELLE ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_ANNABELLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_ANNABELLE ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_ANNABELLE ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_ANNABELLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_ANNABELLE ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_MELEE ); + + REGISTER_SHARED_ACTIVITY( ACT_RUN_PACKAGE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SUITCASE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_MELEE ); REGISTER_SHARED_ACTIVITY( ACT_DISARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_MELEE ); +#endif + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR1 ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR1_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR1 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR3 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG2 ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_SMG2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG2_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_SMG2 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_SMG3 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_HMG1 ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_HMG1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_HMG1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_HMG1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_HMG1_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_HMG1 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_HMG1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_HMG1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_HMG1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_HMG1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_HMG1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SNIPER_RIFLE ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SNIPER_RIFLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_SNIPER_RIFLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SNIPER_RIFLE_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_SNIPER_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SNIPER_RIFLE_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SNIPER_RIFLE_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SNIPER_RIFLE_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SNIPER_RIFLE_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SNIPER_RIFLE_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_DUAL_PISTOLS ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_DUAL_PISTOLS_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_DUAL_PISTOLS_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_DUAL_PISTOLS_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_DUAL_PISTOLS_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_DUAL_PISTOLS_STIMULATED ); +#endif + +#if EXPANDED_NAVIGATION_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_ALL ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_IDLE ); + + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_TOP ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_BOTTOM ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_DISMOUNT_BOTTOM ); +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_MED ); + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_HMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_HMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SNIPER_RIFLE_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SNIPER_RIFLE_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_DUAL_PISTOLS_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_DUAL_PISTOLS_MED ); +#endif + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_PISTOL ); +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_PHYSGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SLAM ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_PHYSGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_REVOLVER ); + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_AR1 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_AR3 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SMG2 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SMG3 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_HMG1 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SNIPER_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_DUAL_PISTOLS ); +#endif + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_USE ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_USE_HEAVY ); #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 a6dddd16..ad5b5c8e 100644 --- a/mp/src/game/shared/ai_activity.h +++ b/mp/src/game/shared/ai_activity.h @@ -13,42 +13,53 @@ #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. +// Mapbase adds many new shared activities, primarily for NPCs. +// +// These are at the bottom of the enum to prevent disruptions in the order of existing activities. +// // 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. +// Citizens and Combine soldiers have several activities for the SMG1 and AR2 which differ from each other. +// Across both NPCs, there are around 20-40 different AR2 animations. Most of these animations are similar to the +// SMG1 animations, except their hand positions are adjusted to use the AR2 instead. // -// 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. +// Unfortunately, the vast majority of the AR2 animations in those models are not declared as real activities in +// code. The AR2 instead falls back to its SMG1 animation counterparts. +// This is thought to be an oversight which dates back to late in Half-Life 2's development. // -// 1 = Add activities directly -// 2 = Add activities as custom activities (todo) -// -// 2 should only be preferable if adding them like this breaks something. +// This preprocessor declares those activities and implements them on the AR2. In-game, they work surprisingly well +// despite presumably never being tested in-game during HL2's development. #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. +// This turns ACT_COMBINE_AR2_ALTFIRE, ACT_COMBINE_THROW_GRENADE, and their new gesture counterparts into shared activities. +// This is necessary for other NPCs to use them without having to rely on private custom activities declared through the AI definition system. #define SHARED_COMBINE_ACTIVITIES 1 +// EXPANDED HL2 WEAPON ACTIVITIES +// This enables a bunch of new activities for Half-Life 2 weapons, including new 357 animations and readiness activities for pistols. +#define EXPANDED_HL2_WEAPON_ACTIVITIES 1 + +// EXPANDED HL2 UNUSED WEAPON ACTIVITIES +// This enables a bunch of new activities for unused Half-Life 2 weapons, particularly those which exist in the SDK, but are deactivated by default. +// This essentially just means mods which restore those weapons have the option of using custom activities for them. +// Mapbase's backup activity system would allow them to fall back to other weapons if the relevant activities do not exist. +// Also includes activity names for "AR3", "SMG3", and "DUAL_PISTOLS", which were never used in HL2, but may be useful when additional animation sets are needed. +#define EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES 0 + +// EXPANDED NAVIGATION ACTIVITIES +// This enables some new navigation-related activities. +#define EXPANDED_NAVIGATION_ACTIVITIES 1 + +// EXPANDED HL2 COVER ACTIVITIES +// This enables some new cover-related activities. +#define EXPANDED_HL2_COVER_ACTIVITIES 1 + +// EXPANDED HL2DM ACTIVITIES +// This enables some new activities for the HL2:DM set. +#define EXPANDED_HL2DM_ACTIVITIES 1 + #endif #define ACTIVITY_NOT_AVAILABLE -1 @@ -2250,16 +2261,23 @@ typedef enum ACT_RUN_AIM_AR2, ACT_RELOAD_AR2, - //ACT_RELOAD_AR2_LOW, + ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2, + + ACT_COVER_AR2_LOW, #endif -#ifdef SHARED_COMBINE_ACTIVITIES +#if SHARED_COMBINE_ACTIVITIES ACT_COMBINE_THROW_GRENADE, ACT_COMBINE_AR2_ALTFIRE, - // New gesture-based signals as activities for people who want to use them + // Gesture versions for existing Combine signal and grenade activities + ACT_GESTURE_COMBINE_THROW_GRENADE, + ACT_GESTURE_COMBINE_AR2_ALTFIRE, + ACT_GESTURE_SPECIAL_ATTACK1, + ACT_GESTURE_SPECIAL_ATTACK2, + ACT_GESTURE_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_GROUP, @@ -2269,9 +2287,494 @@ typedef enum ACT_GESTURE_SIGNAL_TAKECOVER, #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Revolver (357) + ACT_IDLE_REVOLVER, + ACT_IDLE_ANGRY_REVOLVER, + ACT_WALK_REVOLVER, + ACT_RUN_REVOLVER, + ACT_WALK_AIM_REVOLVER, + ACT_RUN_AIM_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER, + ACT_RELOAD_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER_LOW, + ACT_RELOAD_REVOLVER_LOW, + ACT_COVER_REVOLVER_LOW, + ACT_RANGE_AIM_REVOLVER_LOW, + ACT_GESTURE_RANGE_ATTACK_REVOLVER, + ACT_GESTURE_RELOAD_REVOLVER, + + // Crossbow + ACT_IDLE_CROSSBOW, + ACT_IDLE_ANGRY_CROSSBOW, + ACT_WALK_CROSSBOW, + ACT_RUN_CROSSBOW, + ACT_WALK_AIM_CROSSBOW, + ACT_RUN_AIM_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW, + ACT_RELOAD_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW_LOW, + ACT_RELOAD_CROSSBOW_LOW, + ACT_COVER_CROSSBOW_LOW, + ACT_RANGE_AIM_CROSSBOW_LOW, + ACT_GESTURE_RANGE_ATTACK_CROSSBOW, + ACT_GESTURE_RELOAD_CROSSBOW, + + ACT_IDLE_CROSSBOW_RELAXED, + ACT_IDLE_CROSSBOW_STIMULATED, + ACT_WALK_CROSSBOW_RELAXED, + ACT_RUN_CROSSBOW_RELAXED, + ACT_WALK_CROSSBOW_STIMULATED, + ACT_RUN_CROSSBOW_STIMULATED, + + ACT_IDLE_AIM_CROSSBOW_STIMULATED, + ACT_WALK_AIM_CROSSBOW_STIMULATED, + ACT_RUN_AIM_CROSSBOW_STIMULATED, + + // Pistol + ACT_IDLE_PISTOL_RELAXED, + ACT_IDLE_PISTOL_STIMULATED, + ACT_WALK_PISTOL_RELAXED, + ACT_WALK_PISTOL_STIMULATED, + ACT_RUN_PISTOL_RELAXED, + ACT_RUN_PISTOL_STIMULATED, + + ACT_IDLE_AIM_PISTOL_STIMULATED, + ACT_WALK_AIM_PISTOL_STIMULATED, + ACT_RUN_AIM_PISTOL_STIMULATED, + + ACT_WALK_CROUCH_PISTOL, + ACT_WALK_CROUCH_AIM_PISTOL, + ACT_RUN_CROUCH_PISTOL, + ACT_RUN_CROUCH_AIM_PISTOL, + + // Shotgun + ACT_IDLE_SHOTGUN, + ACT_WALK_SHOTGUN, + ACT_RUN_SHOTGUN, + + ACT_COVER_SHOTGUN_LOW, + ACT_RANGE_AIM_SHOTGUN_LOW, + + ACT_WALK_SHOTGUN_RELAXED, + ACT_WALK_SHOTGUN_STIMULATED, + ACT_RUN_SHOTGUN_RELAXED, + ACT_RUN_SHOTGUN_STIMULATED, + + ACT_IDLE_AIM_SHOTGUN_STIMULATED, + ACT_WALK_AIM_SHOTGUN_STIMULATED, + ACT_RUN_AIM_SHOTGUN_STIMULATED, + + // RPG + ACT_RANGE_AIM_RPG_LOW, + ACT_RANGE_ATTACK_RPG_LOW, + ACT_GESTURE_RANGE_ATTACK_RPG, + + // Annabelle + ACT_RANGE_ATTACK_ANNABELLE, + ACT_RANGE_ATTACK_ANNABELLE_LOW, + ACT_GESTURE_RANGE_ATTACK_ANNABELLE, + ACT_RELOAD_ANNABELLE, + ACT_RELOAD_ANNABELLE_LOW, + ACT_GESTURE_RELOAD_ANNABELLE, + + // Melee + ACT_WALK_MELEE, + ACT_RUN_MELEE, + + // Citizen accessories + ACT_RUN_PACKAGE, + ACT_RUN_SUITCASE, + + // Holster/Unholster ACT_ARM_RIFLE, + ACT_ARM_SHOTGUN, + ACT_ARM_RPG, + ACT_ARM_MELEE, ACT_DISARM_RIFLE, + ACT_DISARM_SHOTGUN, + ACT_DISARM_RPG, + ACT_DISARM_MELEE, +#endif + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // AR1 + ACT_IDLE_AR1, + ACT_IDLE_ANGRY_AR1, + ACT_WALK_AR1, + ACT_RUN_AR1, + ACT_WALK_AIM_AR1, + ACT_RUN_AIM_AR1, + //ACT_RANGE_ATTACK_AR1, + ACT_RELOAD_AR1, + ACT_RANGE_ATTACK_AR1_LOW, + ACT_RELOAD_AR1_LOW, + ACT_COVER_AR1_LOW, + ACT_RANGE_AIM_AR1_LOW, + //ACT_GESTURE_RANGE_ATTACK_AR1, + ACT_GESTURE_RELOAD_AR1, + + ACT_IDLE_AR1_RELAXED, + ACT_IDLE_AR1_STIMULATED, + ACT_WALK_AR1_RELAXED, + ACT_RUN_AR1_RELAXED, + ACT_WALK_AR1_STIMULATED, + ACT_RUN_AR1_STIMULATED, + + ACT_IDLE_AIM_AR1_STIMULATED, + ACT_WALK_AIM_AR1_STIMULATED, + ACT_RUN_AIM_AR1_STIMULATED, + + // AR3 (new) + ACT_IDLE_AR3, + ACT_IDLE_ANGRY_AR3, + ACT_WALK_AR3, + ACT_RUN_AR3, + ACT_WALK_AIM_AR3, + ACT_RUN_AIM_AR3, + ACT_RANGE_ATTACK_AR3, + ACT_RELOAD_AR3, + ACT_RANGE_ATTACK_AR3_LOW, + ACT_RELOAD_AR3_LOW, + ACT_COVER_AR3_LOW, + ACT_RANGE_AIM_AR3_LOW, + ACT_GESTURE_RANGE_ATTACK_AR3, + ACT_GESTURE_RELOAD_AR3, + + ACT_IDLE_AR3_RELAXED, + ACT_IDLE_AR3_STIMULATED, + ACT_WALK_AR3_RELAXED, + ACT_RUN_AR3_RELAXED, + ACT_WALK_AR3_STIMULATED, + ACT_RUN_AR3_STIMULATED, + + ACT_IDLE_AIM_AR3_STIMULATED, + ACT_WALK_AIM_AR3_STIMULATED, + ACT_RUN_AIM_AR3_STIMULATED, + + // SMG2 + ACT_IDLE_SMG2, + ACT_IDLE_ANGRY_SMG2, + ACT_WALK_SMG2, + ACT_RUN_SMG2, + ACT_WALK_AIM_SMG2, + ACT_RUN_AIM_SMG2, + //ACT_RANGE_ATTACK_SMG2, + ACT_RELOAD_SMG2, + ACT_RANGE_ATTACK_SMG2_LOW, + ACT_RELOAD_SMG2_LOW, + ACT_COVER_SMG2_LOW, + ACT_RANGE_AIM_SMG2_LOW, + //ACT_GESTURE_RANGE_ATTACK_SMG2, + ACT_GESTURE_RELOAD_SMG2, + + ACT_IDLE_SMG2_RELAXED, + ACT_IDLE_SMG2_STIMULATED, + ACT_WALK_SMG2_RELAXED, + ACT_RUN_SMG2_RELAXED, + ACT_WALK_SMG2_STIMULATED, + ACT_RUN_SMG2_STIMULATED, + + ACT_IDLE_AIM_SMG2_STIMULATED, + ACT_WALK_AIM_SMG2_STIMULATED, + ACT_RUN_AIM_SMG2_STIMULATED, + + // SMG3 (new) + ACT_IDLE_SMG3, + ACT_IDLE_ANGRY_SMG3, + ACT_WALK_SMG3, + ACT_RUN_SMG3, + ACT_WALK_AIM_SMG3, + ACT_RUN_AIM_SMG3, + ACT_RANGE_ATTACK_SMG3, + ACT_RELOAD_SMG3, + ACT_RANGE_ATTACK_SMG3_LOW, + ACT_RELOAD_SMG3_LOW, + ACT_COVER_SMG3_LOW, + ACT_RANGE_AIM_SMG3_LOW, + ACT_GESTURE_RANGE_ATTACK_SMG3, + ACT_GESTURE_RELOAD_SMG3, + + ACT_IDLE_SMG3_RELAXED, + ACT_IDLE_SMG3_STIMULATED, + ACT_WALK_SMG3_RELAXED, + ACT_RUN_SMG3_RELAXED, + ACT_WALK_SMG3_STIMULATED, + ACT_RUN_SMG3_STIMULATED, + + ACT_IDLE_AIM_SMG3_STIMULATED, + ACT_WALK_AIM_SMG3_STIMULATED, + ACT_RUN_AIM_SMG3_STIMULATED, + + // HMG1 + ACT_IDLE_HMG1, + ACT_IDLE_ANGRY_HMG1, + ACT_WALK_HMG1, + ACT_RUN_HMG1, + ACT_WALK_AIM_HMG1, + ACT_RUN_AIM_HMG1, + //ACT_RANGE_ATTACK_HMG1, + ACT_RELOAD_HMG1, + ACT_RANGE_ATTACK_HMG1_LOW, + ACT_RELOAD_HMG1_LOW, + ACT_COVER_HMG1_LOW, + ACT_RANGE_AIM_HMG1_LOW, + //ACT_GESTURE_RANGE_ATTACK_HMG1, + ACT_GESTURE_RELOAD_HMG1, + + ACT_IDLE_HMG1_RELAXED, + ACT_IDLE_HMG1_STIMULATED, + ACT_WALK_HMG1_RELAXED, + ACT_RUN_HMG1_RELAXED, + ACT_WALK_HMG1_STIMULATED, + ACT_RUN_HMG1_STIMULATED, + + ACT_IDLE_AIM_HMG1_STIMULATED, + ACT_WALK_AIM_HMG1_STIMULATED, + ACT_RUN_AIM_HMG1_STIMULATED, + + // Sniper Rifle + ACT_IDLE_SNIPER_RIFLE, + ACT_IDLE_ANGRY_SNIPER_RIFLE, + ACT_WALK_SNIPER_RIFLE, + ACT_RUN_SNIPER_RIFLE, + ACT_WALK_AIM_SNIPER_RIFLE, + ACT_RUN_AIM_SNIPER_RIFLE, + //ACT_RANGE_ATTACK_SNIPER_RIFLE, + ACT_RELOAD_SNIPER_RIFLE, + ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW, + ACT_RELOAD_SNIPER_RIFLE_LOW, + ACT_COVER_SNIPER_RIFLE_LOW, + ACT_RANGE_AIM_SNIPER_RIFLE_LOW, + //ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE, + ACT_GESTURE_RELOAD_SNIPER_RIFLE, + + ACT_IDLE_SNIPER_RIFLE_RELAXED, + ACT_IDLE_SNIPER_RIFLE_STIMULATED, + ACT_WALK_SNIPER_RIFLE_RELAXED, + ACT_RUN_SNIPER_RIFLE_RELAXED, + ACT_WALK_SNIPER_RIFLE_STIMULATED, + ACT_RUN_SNIPER_RIFLE_STIMULATED, + + ACT_IDLE_AIM_SNIPER_RIFLE_STIMULATED, + ACT_WALK_AIM_SNIPER_RIFLE_STIMULATED, + ACT_RUN_AIM_SNIPER_RIFLE_STIMULATED, + + // Dual Pistols + ACT_IDLE_DUAL_PISTOLS, + ACT_IDLE_ANGRY_DUAL_PISTOLS, + ACT_WALK_DUAL_PISTOLS, + ACT_RUN_DUAL_PISTOLS, + ACT_WALK_AIM_DUAL_PISTOLS, + ACT_RUN_AIM_DUAL_PISTOLS, + ACT_RANGE_ATTACK_DUAL_PISTOLS, + ACT_RELOAD_DUAL_PISTOLS, + ACT_RANGE_ATTACK_DUAL_PISTOLS_LOW, + ACT_RELOAD_DUAL_PISTOLS_LOW, + ACT_COVER_DUAL_PISTOLS_LOW, + ACT_RANGE_AIM_DUAL_PISTOLS_LOW, + ACT_GESTURE_RANGE_ATTACK_DUAL_PISTOLS, + ACT_GESTURE_RELOAD_DUAL_PISTOLS, + + ACT_IDLE_DUAL_PISTOLS_RELAXED, + ACT_IDLE_DUAL_PISTOLS_STIMULATED, + ACT_WALK_DUAL_PISTOLS_RELAXED, + ACT_RUN_DUAL_PISTOLS_RELAXED, + ACT_WALK_DUAL_PISTOLS_STIMULATED, + ACT_RUN_DUAL_PISTOLS_STIMULATED, + + ACT_IDLE_AIM_DUAL_PISTOLS_STIMULATED, + ACT_WALK_AIM_DUAL_PISTOLS_STIMULATED, + ACT_RUN_AIM_DUAL_PISTOLS_STIMULATED, +#endif + +#if EXPANDED_NAVIGATION_ACTIVITIES + ACT_CLIMB_ALL, // An actual blend animation which uses pose parameters for direction + ACT_CLIMB_IDLE, + + ACT_CLIMB_MOUNT_TOP, + ACT_CLIMB_MOUNT_BOTTOM, + ACT_CLIMB_DISMOUNT_BOTTOM, +#endif + +#if EXPANDED_HL2_COVER_ACTIVITIES + // Crouch Cover Medium + ACT_RANGE_ATTACK1_MED, + ACT_RANGE_ATTACK2_MED, + ACT_RANGE_AIM_MED, + + ACT_RANGE_ATTACK_AR2_MED, + ACT_RANGE_ATTACK_SMG1_MED, + ACT_RANGE_ATTACK_SHOTGUN_MED, + ACT_RANGE_ATTACK_PISTOL_MED, + ACT_RANGE_ATTACK_RPG_MED, + ACT_RANGE_ATTACK_REVOLVER_MED, + ACT_RANGE_ATTACK_CROSSBOW_MED, + + ACT_RANGE_AIM_AR2_MED, + ACT_RANGE_AIM_SMG1_MED, + ACT_RANGE_AIM_SHOTGUN_MED, + ACT_RANGE_AIM_PISTOL_MED, + ACT_RANGE_AIM_RPG_MED, + ACT_RANGE_AIM_REVOLVER_MED, + ACT_RANGE_AIM_CROSSBOW_MED, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // MED activities for unused weapons + ACT_RANGE_AIM_AR1_MED, + ACT_RANGE_ATTACK_AR1_MED, + ACT_RANGE_AIM_AR3_MED, + ACT_RANGE_ATTACK_AR3_MED, + ACT_RANGE_AIM_SMG2_MED, + ACT_RANGE_ATTACK_SMG2_MED, + ACT_RANGE_AIM_SMG3_MED, + ACT_RANGE_ATTACK_SMG3_MED, + ACT_RANGE_AIM_HMG1_MED, + ACT_RANGE_ATTACK_HMG1_MED, + ACT_RANGE_AIM_SNIPER_RIFLE_MED, + ACT_RANGE_ATTACK_SNIPER_RIFLE_MED, + ACT_RANGE_AIM_DUAL_PISTOLS_MED, + ACT_RANGE_ATTACK_DUAL_PISTOLS_MED, +#endif + + // Wall Cover (for use in custom cover hints) + ACT_COVER_WALL_R, + ACT_COVER_WALL_L, + ACT_COVER_WALL_LOW_R, + ACT_COVER_WALL_LOW_L, + + ACT_COVER_WALL_R_RIFLE, + ACT_COVER_WALL_L_RIFLE, + ACT_COVER_WALL_LOW_R_RIFLE, + ACT_COVER_WALL_LOW_L_RIFLE, + + ACT_COVER_WALL_R_PISTOL, + ACT_COVER_WALL_L_PISTOL, + ACT_COVER_WALL_LOW_R_PISTOL, + ACT_COVER_WALL_LOW_L_PISTOL, +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + ACT_HL2MP_WALK, + ACT_HL2MP_WALK_PISTOL, + ACT_HL2MP_WALK_SHOTGUN, + ACT_HL2MP_WALK_SMG1, + ACT_HL2MP_WALK_AR2, + ACT_HL2MP_WALK_PHYSGUN, + ACT_HL2MP_WALK_GRENADE, + ACT_HL2MP_WALK_RPG, + ACT_HL2MP_WALK_CROSSBOW, + ACT_HL2MP_WALK_MELEE, + ACT_HL2MP_WALK_SLAM, + + ACT_HL2MP_GESTURE_RANGE_ATTACK2, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_PHYSGUN, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM, + + ACT_HL2MP_IDLE_REVOLVER, + ACT_HL2MP_RUN_REVOLVER, + ACT_HL2MP_WALK_REVOLVER, + ACT_HL2MP_IDLE_CROUCH_REVOLVER, + ACT_HL2MP_WALK_CROUCH_REVOLVER, + ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER, + ACT_HL2MP_GESTURE_RELOAD_REVOLVER, + ACT_HL2MP_JUMP_REVOLVER, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Player activities for unused weapons + ACT_HL2MP_IDLE_AR1, + ACT_HL2MP_RUN_AR1, + ACT_HL2MP_WALK_AR1, + ACT_HL2MP_IDLE_CROUCH_AR1, + ACT_HL2MP_WALK_CROUCH_AR1, + ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1, + ACT_HL2MP_GESTURE_RELOAD_AR1, + ACT_HL2MP_JUMP_AR1, + + ACT_HL2MP_IDLE_AR3, + ACT_HL2MP_RUN_AR3, + ACT_HL2MP_WALK_AR3, + ACT_HL2MP_IDLE_CROUCH_AR3, + ACT_HL2MP_WALK_CROUCH_AR3, + ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3, + ACT_HL2MP_GESTURE_RELOAD_AR3, + ACT_HL2MP_JUMP_AR3, + + ACT_HL2MP_IDLE_SMG2, + ACT_HL2MP_RUN_SMG2, + ACT_HL2MP_WALK_SMG2, + ACT_HL2MP_IDLE_CROUCH_SMG2, + ACT_HL2MP_WALK_CROUCH_SMG2, + ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2, + ACT_HL2MP_GESTURE_RELOAD_SMG2, + ACT_HL2MP_JUMP_SMG2, + + ACT_HL2MP_IDLE_SMG3, + ACT_HL2MP_RUN_SMG3, + ACT_HL2MP_WALK_SMG3, + ACT_HL2MP_IDLE_CROUCH_SMG3, + ACT_HL2MP_WALK_CROUCH_SMG3, + ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3, + ACT_HL2MP_GESTURE_RELOAD_SMG3, + ACT_HL2MP_JUMP_SMG3, + + ACT_HL2MP_IDLE_HMG1, + ACT_HL2MP_RUN_HMG1, + ACT_HL2MP_WALK_HMG1, + ACT_HL2MP_IDLE_CROUCH_HMG1, + ACT_HL2MP_WALK_CROUCH_HMG1, + ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1, + ACT_HL2MP_GESTURE_RELOAD_HMG1, + ACT_HL2MP_JUMP_HMG1, + + ACT_HL2MP_IDLE_SNIPER_RIFLE, + ACT_HL2MP_RUN_SNIPER_RIFLE, + ACT_HL2MP_WALK_SNIPER_RIFLE, + ACT_HL2MP_IDLE_CROUCH_SNIPER_RIFLE, + ACT_HL2MP_WALK_CROUCH_SNIPER_RIFLE, + ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE, + ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE, + ACT_HL2MP_JUMP_SNIPER_RIFLE, + + ACT_HL2MP_IDLE_DUAL_PISTOLS, + ACT_HL2MP_RUN_DUAL_PISTOLS, + ACT_HL2MP_WALK_DUAL_PISTOLS, + ACT_HL2MP_IDLE_CROUCH_DUAL_PISTOLS, + ACT_HL2MP_WALK_CROUCH_DUAL_PISTOLS, + ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS, + ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS, + ACT_HL2MP_JUMP_DUAL_PISTOLS, +#endif + + ACT_HL2MP_IDLE_USE, + ACT_HL2MP_RUN_USE, + ACT_HL2MP_WALK_USE, + ACT_HL2MP_IDLE_CROUCH_USE, + ACT_HL2MP_WALK_CROUCH_USE, + ACT_HL2MP_JUMP_USE, + + ACT_HL2MP_IDLE_USE_HEAVY, + ACT_HL2MP_RUN_USE_HEAVY, + ACT_HL2MP_WALK_USE_HEAVY, + ACT_HL2MP_IDLE_CROUCH_USE_HEAVY, + ACT_HL2MP_WALK_CROUCH_USE_HEAVY, + ACT_HL2MP_JUMP_USE_HEAVY, #endif // this is the end of the global activities, private per-monster activities start here. diff --git a/mp/src/game/shared/ai_criteria_new.cpp b/mp/src/game/shared/ai_criteria_new.cpp new file mode 100644 index 00000000..837c61aa --- /dev/null +++ b/mp/src/game/shared/ai_criteria_new.cpp @@ -0,0 +1,38 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "cbase.h" +#include "AI_Criteria.h" + +#ifdef GAME_DLL +#include "ai_speech.h" +#endif + +#include +#include "engine/IEngineSound.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + + + +BEGIN_SIMPLE_DATADESC( AI_ResponseParams ) + DEFINE_FIELD( flags, FIELD_SHORT ), + DEFINE_FIELD( odds, FIELD_SHORT ), + DEFINE_FIELD( soundlevel, FIELD_CHARACTER ), + DEFINE_FIELD( delay, FIELD_INTEGER ), // These are compressed down to two float16s, so treat as an INT for saverestore + DEFINE_FIELD( respeakdelay, FIELD_INTEGER ), // +END_DATADESC() + +BEGIN_SIMPLE_DATADESC( AI_Response ) + DEFINE_FIELD( m_Type, FIELD_CHARACTER ), + DEFINE_ARRAY( m_szResponseName, FIELD_CHARACTER, AI_Response::MAX_RESPONSE_NAME ), + DEFINE_ARRAY( m_szMatchingRule, FIELD_CHARACTER, AI_Response::MAX_RULE_NAME ), + // DEFINE_FIELD( m_pCriteria, FIELD_??? ), // Don't need to save this probably + DEFINE_EMBEDDED( m_Params ), +END_DATADESC() + diff --git a/mp/src/game/shared/ai_criteria_new.h b/mp/src/game/shared/ai_criteria_new.h new file mode 100644 index 00000000..b5d2c4fd --- /dev/null +++ b/mp/src/game/shared/ai_criteria_new.h @@ -0,0 +1,41 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef AI_CRITERIA_H +#define AI_CRITERIA_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier1/interval.h" +#include "mathlib/compressed_vector.h" +#include "../../public/responserules/response_types.h" + + +using ResponseRules::ResponseType_t; + +extern const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration, const char *entireContext ); + +#ifndef AI_CriteriaSet +#define AI_CriteriaSet ResponseRules::CriteriaSet +#endif + +typedef ResponseRules::ResponseParams AI_ResponseParams ; +typedef ResponseRules::CRR_Response AI_Response; + + + +/* +// An AI response that is dynamically new'ed up and returned from SpeakFindResponse. +class AI_ResponseReturnValue : AI_Response +{ + +}; +*/ + +#endif // AI_CRITERIA_H diff --git a/mp/src/game/shared/ai_responsesystem_new.cpp b/mp/src/game/shared/ai_responsesystem_new.cpp new file mode 100644 index 00000000..b639f270 --- /dev/null +++ b/mp/src/game/shared/ai_responsesystem_new.cpp @@ -0,0 +1,1388 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "AI_ResponseSystem.h" +#include "igamesystem.h" +#include "AI_Criteria.h" +#include +#include "filesystem.h" +#include "utldict.h" +#ifdef GAME_DLL +#include "ai_speech.h" +#endif +#include "tier0/icommandline.h" +#include +#include "isaverestore.h" +#include "utlbuffer.h" +#include "stringpool.h" +#include "fmtstr.h" +#include "multiplay_gamerules.h" +#include "characterset.h" +#include "responserules/response_host_interface.h" +#include "../../responserules/runtime/response_types_internal.h" + +#include "scenefilecache/ISceneFileCache.h" + +#ifdef GAME_DLL +#include "sceneentity.h" +#endif + +#include "networkstringtabledefs.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#undef IResponseSystem +using namespace ResponseRules; + +extern ConVar rr_debugresponses; // ( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring, 3 for noisy). If set to 4, it will only show response success/failure for npc_selected NPCs." ); +extern 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."); +extern ConVar rr_dumpresponses; // ( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" ); +extern ConVar rr_debugresponseconcept; // ( "rr_debugresponseconcept", "", FCVAR_NONE, "If set, rr_debugresponses will print only responses testing for the specified concept" ); + +static void CC_RR_DumpHashInfo( const CCommand &args ); + +#ifdef MAPBASE +ConVar rr_enhanced_saverestore( "rr_enhanced_saverestore", "0", FCVAR_NONE, "Enables enhanced save/restore capabilities for the Response System." ); +#endif + +extern ISceneFileCache *scenefilecache; +extern INetworkStringTable *g_pStringTableClientSideChoreoScenes; + +static characterset_t g_BreakSetIncludingColons; + +// Simple class to initialize breakset +class CBreakInit +{ +public: + CBreakInit() + { + CharacterSetBuild( &g_BreakSetIncludingColons, "{}()':" ); + } +} g_BreakInit; + +inline char rr_tolower( char c ) +{ + if ( c >= 'A' && c <= 'Z' ) + return c - 'A' + 'a'; + return c; +} +// BUG BUG: Note that this function doesn't check for data overruns!!! +// Also, this function lowercases the token as it parses!!! +inline const char *RR_Parse(const char *data, char *token ) +{ + unsigned char c; + int len; + characterset_t *breaks = &g_BreakSetIncludingColons; + len = 0; + token[0] = 0; + + if (!data) + return NULL; + + // skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + return NULL; // end of file; + data++; + } + + // skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + + // handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = rr_tolower( *data++ ); + if (c=='\"' || !c) + { + token[len] = 0; + return data; + } + token[len] = c; + len++; + } + } + + // parse single characters + if ( IN_CHARACTERSET( *breaks, c ) ) + { + token[len] = c; + len++; + token[len] = 0; + return data+1; + } + + // parse a regular word + do + { + token[len] = rr_tolower( c ); + data++; + len++; + c = rr_tolower( *data ); + if ( IN_CHARACTERSET( *breaks, c ) ) + break; + } while (c>32); + + token[len] = 0; + return data; +} + +#ifdef MAPBASE +// A version of the above which preserves casing and supports escaped quotes +inline const char *RR_Parse_Preserve(const char *data, char *token ) +{ + unsigned char c; + int len; + characterset_t *breaks = &g_BreakSetIncludingColons; + len = 0; + token[0] = 0; + + if (!data) + return NULL; + + // skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + return NULL; // end of file; + data++; + } + + // skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + // handle quoted strings specially + if (c == '\"') + { + bool escaped = false; + data++; + while (1) + { + c = *data++; + if ((c=='\"' && !escaped) || !c) + { + token[len] = 0; + return data; + } + else if (c != '\"' && escaped) + { + // Not an escape character, just a back slash + token[len] = '\\'; + len++; + } + + escaped = (c == '\\'); + if (!escaped) + { + token[len] = c; + len++; + } + } + } + + // parse single characters + if ( IN_CHARACTERSET( *breaks, c ) ) + { + token[len] = c; + len++; + token[len] = 0; + return data+1; + } + + // parse a regular word + do + { + token[len] = c; + data++; + len++; + c = *data; + if ( IN_CHARACTERSET( *breaks, c ) ) + break; + } while (c>32); + + token[len] = 0; + return data; +} +#endif + +namespace ResponseRules +{ + extern const char *ResponseCopyString( const char *in ); +} + +// Host functions required by the ResponseRules::IEngineEmulator interface +class CResponseRulesToEngineInterface : public ResponseRules::IEngineEmulator +{ + /// Given an input text buffer data pointer, parses a single token into the variable token and returns the new + /// reading position + virtual const char *ParseFile( const char *data, char *token, int maxlen ) + { + NOTE_UNUSED( maxlen ); + return RR_Parse( data, token ); + } + +#ifdef MAPBASE + /// (Optional) Same as ParseFile, but with casing preserved and escaped quotes supported + virtual const char *ParseFilePreserve( const char *data, char *token, int maxlen ) + { + NOTE_UNUSED( maxlen ); + return RR_Parse_Preserve( data, token ); + } +#endif + + /// Return a pointer to an IFileSystem we can use to read and process scripts. + virtual IFileSystem *GetFilesystem() + { + return filesystem; + } + + /// Return a pointer to an instance of an IUniformRandomStream + virtual IUniformRandomStream *GetRandomStream() + { + return random; + } + + /// Return a pointer to a tier0 ICommandLine + virtual ICommandLine *GetCommandLine() + { + return CommandLine(); + } + + /// Emulates the server's UTIL_LoadFileForMe + virtual byte *LoadFileForMe( const char *filename, int *pLength ) + { + return UTIL_LoadFileForMe( filename, pLength ); + } + + /// Emulates the server's UTIL_FreeFile + virtual void FreeFile( byte *buffer ) + { + return UTIL_FreeFile( buffer ); + } + +}; + +CResponseRulesToEngineInterface g_ResponseRulesEngineWrapper; +IEngineEmulator *IEngineEmulator::s_pSingleton = &g_ResponseRulesEngineWrapper; + + +BEGIN_SIMPLE_DATADESC( ParserResponse ) + // DEFINE_FIELD( type, FIELD_INTEGER ), + // DEFINE_ARRAY( value, FIELD_CHARACTER ), + // DEFINE_FIELD( weight, FIELD_FLOAT ), + DEFINE_FIELD( depletioncount, FIELD_CHARACTER ), + // DEFINE_FIELD( first, FIELD_BOOLEAN ), + // DEFINE_FIELD( last, FIELD_BOOLEAN ), +END_DATADESC() + + +BEGIN_SIMPLE_DATADESC( ResponseGroup ) + // DEFINE_FIELD( group, FIELD_UTLVECTOR ), + // DEFINE_FIELD( rp, FIELD_EMBEDDED ), + // DEFINE_FIELD( m_bDepleteBeforeRepeat, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nDepletionCount, FIELD_CHARACTER ), + // DEFINE_FIELD( m_bHasFirst, FIELD_BOOLEAN ), + // DEFINE_FIELD( m_bHasLast, FIELD_BOOLEAN ), + // DEFINE_FIELD( m_bSequential, FIELD_BOOLEAN ), + // DEFINE_FIELD( m_bNoRepeat, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nCurrentIndex, FIELD_CHARACTER ), +END_DATADESC() + + +/// Add some game-specific code to the basic response system +/// (eg, the scene precacher, which requires the client and server +/// to work) + +class CGameResponseSystem : public CResponseSystem +{ +public: + CGameResponseSystem(); + + virtual void Precache(); + virtual void PrecacheResponses( bool bEnable ) + { + m_bPrecache = bEnable; + } + bool ShouldPrecache() { return m_bPrecache; } + +protected: + bool m_bPrecache; +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGameResponseSystem::CGameResponseSystem() : m_bPrecache(true) +{}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +#if 0 +class CScenePrecacheSystem : public CAutoGameSystem +{ +public: + CScenePrecacheSystem() : CAutoGameSystem( "CScenePrecacheSystem" ), m_RepeatCounts( 0, 0, DefLessFunc( int ) ) + { + } + + // Level init, shutdown + virtual void LevelShutdownPreEntity() + { + m_RepeatCounts.Purge(); + } + + bool ShouldPrecache( char const *pszScene ) + { + int hash = HashStringCaselessConventional( pszScene ); + + int slot = m_RepeatCounts.Find( hash ); + if ( slot != m_RepeatCounts.InvalidIndex() ) + { + m_RepeatCounts[ slot ]++; + return false; + } + + m_RepeatCounts.Insert( hash, 0 ); + return true; + } + +private: + + CUtlMap< int, int > m_RepeatCounts; +}; + +static CScenePrecacheSystem g_ScenePrecacheSystem; +//----------------------------------------------------------------------------- +// Purpose: Used for precaching instanced scenes +// Input : *pszScene - +//----------------------------------------------------------------------------- +void PrecacheInstancedScene( char const *pszScene ) +{ + static int nMakingReslists = -1; + + if ( !g_ScenePrecacheSystem.ShouldPrecache( pszScene ) ) + return; + + if ( nMakingReslists == -1 ) + { + nMakingReslists = CommandLine()->FindParm( "-makereslists" ) > 0 ? 1 : 0; + } + + if ( nMakingReslists == 1 ) + { + // Just stat the file to add to reslist + g_pFullFileSystem->Size( pszScene ); + } + + // verify existence, cache is pre-populated, should be there + SceneCachedData_t sceneData; + if ( !scenefilecache->GetSceneCachedData( pszScene, &sceneData ) ) + { + // 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 ( 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 ); + } + } + else + { + for ( int i = 0; i < sceneData.numSounds; ++i ) + { + short stringId = scenefilecache->GetSceneCachedSound( sceneData.sceneId, i ); + CBaseEntity::PrecacheScriptSound( scenefilecache->GetSceneString( stringId ) ); + } + } + + g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), pszScene ); +} +#endif + +static void TouchFile( char const *pchFileName ) +{ + IEngineEmulator::Get()->GetFilesystem()->Size( pchFileName ); +} + +void CGameResponseSystem::Precache() +{ + bool bTouchFiles = CommandLine()->FindParm( "-makereslists" ) != 0; + + // enumerate and mark all the scripts so we know they're referenced + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + ResponseGroup &group = m_Responses[i]; + + for ( int j = 0; j < group.group.Count(); j++) + { + ParserResponse &response = group.group[j]; + + switch ( response.type ) + { + default: + break; + case RESPONSE_SCENE: + { + // fixup $gender references + char file[_MAX_PATH]; + Q_strncpy( file, response.value, 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]; + // male + Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender); + + PrecacheInstancedScene( genderFile ); + if ( bTouchFiles ) + { + TouchFile( genderFile ); + } + + Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender); + + PrecacheInstancedScene( genderFile ); + if ( bTouchFiles ) + { + TouchFile( genderFile ); + } + } + else + { + PrecacheInstancedScene( file ); + if ( bTouchFiles ) + { + TouchFile( file ); + } + } + } + break; + case RESPONSE_SPEAK: + { + CBaseEntity::PrecacheScriptSound( response.value ); + } + break; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: A special purpose response system associated with a custom entity +//----------------------------------------------------------------------------- +class CInstancedResponseSystem : public CGameResponseSystem +{ + typedef CGameResponseSystem BaseClass; + +public: + CInstancedResponseSystem( const char *scriptfile ) : + m_pszScriptFile( 0 ) + { + Assert( scriptfile ); + + int len = Q_strlen( scriptfile ) + 1; + m_pszScriptFile = new char[ len ]; + Assert( m_pszScriptFile ); + Q_strncpy( m_pszScriptFile, scriptfile, len ); + } + + ~CInstancedResponseSystem() + { + delete[] m_pszScriptFile; + } + virtual const char *GetScriptFile( void ) + { + Assert( m_pszScriptFile ); + return m_pszScriptFile; + } + + // CAutoGameSystem + virtual bool Init() + { + const char *basescript = GetScriptFile(); + LoadRuleSet( basescript ); + return true; + } + + virtual void LevelInitPostEntity() + { +#ifdef MAPBASE + if (!rr_enhanced_saverestore.GetBool() || gpGlobals->eLoadType != MapLoad_Transition) +#endif + ResetResponseGroups(); + } + + virtual void Release() + { + Clear(); + delete this; + } +private: + + char *m_pszScriptFile; +}; + +//----------------------------------------------------------------------------- +// Purpose: The default response system for expressive AIs +//----------------------------------------------------------------------------- +class CDefaultResponseSystem : public CGameResponseSystem, public CAutoGameSystem +{ + typedef CAutoGameSystem BaseClass; + +public: + CDefaultResponseSystem() : CAutoGameSystem( "CDefaultResponseSystem" ) + { + } + + virtual const char *GetScriptFile( void ) + { + return "scripts/talker/response_rules.txt"; + } + + // CAutoServerSystem + virtual bool Init(); + virtual void Shutdown(); + + virtual void LevelInitPostEntity() + { +#ifdef MAPBASE + // CInstancedResponseSystem is not a CAutoGameSystem, so this needs to be called manually. + // The same could've been accomplished by making CInstancedResponseSystem derive from CAutoGameSystem, + // but their instanced nature would've complicated things a lot. + int c = m_InstancedSystems.Count(); + for ( int i = c - 1 ; i >= 0; i-- ) + { + m_InstancedSystems[i]->LevelInitPostEntity(); + } +#endif + } + + virtual void Release() + { + Assert( 0 ); + } + + void AddInstancedResponseSystem( const char *scriptfile, CInstancedResponseSystem *sys ) + { + m_InstancedSystems.Insert( scriptfile, sys ); + } + + CInstancedResponseSystem *FindResponseSystem( const char *scriptfile ) + { + int idx = m_InstancedSystems.Find( scriptfile ); + if ( idx == m_InstancedSystems.InvalidIndex() ) + return NULL; + return m_InstancedSystems[ idx ]; + } + + IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ) + { + COM_TimestampedLog( "PrecacheCustomResponseSystem %s - Start", scriptfile ); + CInstancedResponseSystem *sys = ( CInstancedResponseSystem * )FindResponseSystem( scriptfile ); + if ( !sys ) + { + sys = new CInstancedResponseSystem( scriptfile ); + if ( !sys ) + { + Error( "Failed to load response system data from %s", scriptfile ); + } + + if ( !sys->Init() ) + { + Error( "CInstancedResponseSystem: Failed to init response system from %s!", scriptfile ); + } + + AddInstancedResponseSystem( scriptfile, sys ); + } + + sys->Precache(); + + COM_TimestampedLog( "PrecacheCustomResponseSystem %s - Finish", scriptfile ); + + return ( IResponseSystem * )sys; + } + + IResponseSystem *BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore ); + void DestroyCustomResponseSystems(); + + virtual void LevelInitPreEntity() + { + // This will precache the default system + // All user installed systems are init'd by PrecacheCustomResponseSystem which will call sys->Precache() on the ones being used + + // FIXME: This is SLOW the first time you run the engine (can take 3 - 10 seconds!!!) + if ( ShouldPrecache() ) + { + Precache(); + } + +#ifdef MAPBASE + if (!rr_enhanced_saverestore.GetBool() || gpGlobals->eLoadType != MapLoad_Transition) +#endif + ResetResponseGroups(); + } + + void ReloadAllResponseSystems() + { + Clear(); + Init(); + + int c = m_InstancedSystems.Count(); + for ( int i = c - 1 ; i >= 0; i-- ) + { + CInstancedResponseSystem *sys = m_InstancedSystems[ i ]; + if ( !IsCustomManagable() ) + { + sys->Clear(); + sys->Init(); + } + else + { + // Custom reponse rules will manage/reload themselves - remove them. + m_InstancedSystems.RemoveAt( i ); + } + } + + // precache sounds in case we added new ones + Precache(); + + } + +private: + + void ClearInstanced() + { + int c = m_InstancedSystems.Count(); + for ( int i = c - 1 ; i >= 0; i-- ) + { + CInstancedResponseSystem *sys = m_InstancedSystems[ i ]; + sys->Release(); + } + m_InstancedSystems.RemoveAll(); + } + + CUtlDict< CInstancedResponseSystem *, int > m_InstancedSystems; + friend void CC_RR_DumpHashInfo( const CCommand &args ); +}; + +IResponseSystem *CDefaultResponseSystem::BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore ) +{ + // Create a instanced response system. + CInstancedResponseSystem *pCustomSystem = new CInstancedResponseSystem( pszCustomName ); + if ( !pCustomSystem ) + { + Error( "BuildCustomResponseSystemGivenCriterea: Failed to create custom response system %s!", pszCustomName ); + } + + pCustomSystem->Clear(); + + // Copy the relevant rules and data. + /* + int nRuleCount = m_Rules.Count(); + for ( int iRule = 0; iRule < nRuleCount; ++iRule ) + */ + for ( ResponseRulePartition::tIndex iIdx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(iIdx) ; + iIdx = m_RulePartitions.Next( iIdx ) ) + { + Rule *pRule = &m_RulePartitions[iIdx]; + if ( pRule ) + { + float flScore = 0.0f; + + int nCriteriaCount = pRule->m_Criteria.Count(); + for ( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria ) + { + int iRuleCriteria = pRule->m_Criteria[iCriteria]; + + flScore += LookForCriteria( criteriaSet, iRuleCriteria ); + if ( flScore >= flCriteriaScore ) + { + CopyRuleFrom( pRule, iIdx, pCustomSystem ); + break; + } + } + } + } + + // Set as a custom response system. + m_bCustomManagable = true; + AddInstancedResponseSystem( pszCustomName, pCustomSystem ); + + // pCustomSystem->DumpDictionary( pszCustomName ); + + return pCustomSystem; +} + +void CDefaultResponseSystem::DestroyCustomResponseSystems() +{ + ClearInstanced(); +} + + +static CDefaultResponseSystem defaultresponsesytem; +IResponseSystem *g_pResponseSystem = &defaultresponsesytem; + +CON_COMMAND( rr_reloadresponsesystems, "Reload all response system scripts." ) +{ +#ifdef GAME_DLL + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; +#endif + + defaultresponsesytem.ReloadAllResponseSystems(); +} + +#if RR_DUMPHASHINFO_ENABLED +static void CC_RR_DumpHashInfo( const CCommand &args ) +{ + defaultresponsesytem.m_RulePartitions.PrintBucketInfo( &defaultresponsesytem ); +} +static ConCommand rr_dumphashinfo( "rr_dumphashinfo", CC_RR_DumpHashInfo, "Statistics on primary hash bucketing of response rule partitions"); +#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 = criterion; + defaultresponsesytem.ComputeMatcher( &criteria, criteria.matcher ); + return defaultresponsesytem.CompareUsingMatcher( value, criteria.matcher, true ); + + return false; +} + +//----------------------------------------------------------------------------- +// 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; + */ + + // HACKHACK: This is not very efficient + /* + CInstancedResponseSystem *tempSys = new CInstancedResponseSystem( scriptfile ); + if ( tempSys && tempSys->Init() ) + { + tempSys->Precache(); + + for ( ResponseRulePartition::tIndex idx = tempSys->m_RulePartitions.First() ; + tempSys->m_RulePartitions.IsValid(idx) ; + idx = tempSys->m_RulePartitions.Next(idx) ) + { + Rule &rule = tempSys->m_RulePartitions[idx]; + tempSys->CopyRuleFrom( &rule, idx, &defaultresponsesytem ); + } + + tempSys->Release(); + } + */ + + // HACKHACK: This is even less efficient + defaultresponsesytem.LoadFromBuffer( scriptfile, (const char *)buf.PeekGet() ); + defaultresponsesytem.Precache(); + + 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 +// +class CDefaultResponseSystemSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + const char *GetBlockName() + { + return "ResponseSystem"; + } + + void WriteSaveHeaders( ISave *pSave ) + { + pSave->WriteShort( &RESPONSESYSTEM_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 == RESPONSESYSTEM_SAVE_RESTORE_VERSION ); + } + + void Save( ISave *pSave ) + { + CDefaultResponseSystem& rs = defaultresponsesytem; + + int count = rs.m_Responses.Count(); + pSave->WriteInt( &count ); + for ( int i = 0; i < count; ++i ) + { + pSave->StartBlock( "ResponseGroup" ); + + pSave->WriteString( rs.m_Responses.GetElementName( i ) ); + const ResponseGroup *group = &rs.m_Responses[ i ]; + pSave->WriteAll( group ); + + short groupCount = group->group.Count(); + pSave->WriteShort( &groupCount ); + for ( int j = 0; j < groupCount; ++j ) + { + const ParserResponse *response = &group->group[ j ]; + pSave->StartBlock( "Response" ); + pSave->WriteString( response->value ); + pSave->WriteAll( response ); + pSave->EndBlock(); + } + + pSave->EndBlock(); + } + +#ifdef MAPBASE + // Enhanced Response System save/restore + int count2 = 0; + if (rr_enhanced_saverestore.GetBool()) + { + // Rule state save/load + count2 = rs.m_RulePartitions.Count(); + pSave->WriteInt( &count2 ); + for ( ResponseRulePartition::tIndex idx = rs.m_RulePartitions.First() ; + rs.m_RulePartitions.IsValid(idx) ; + idx = rs.m_RulePartitions.Next(idx) ) + { + pSave->StartBlock( "Rule" ); + + pSave->WriteString( rs.m_RulePartitions.GetElementName( idx ) ); + const Rule &rule = rs.m_RulePartitions[ idx ]; + + 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 ) + { + if ( !m_fDoLoad ) + return; + + CDefaultResponseSystem& rs = defaultresponsesytem; + + int count = pRestore->ReadInt(); + for ( int i = 0; i < count; ++i ) + { + char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF]; + pRestore->StartBlock( szResponseGroupBlockName ); + if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) ) + { + + char groupname[ 256 ]; + pRestore->ReadString( groupname, sizeof( groupname ), 0 ); + + // Try and find it + int idx = rs.m_Responses.Find( groupname ); + if ( idx != rs.m_Responses.InvalidIndex() ) + { + ResponseGroup *group = &rs.m_Responses[ idx ]; + pRestore->ReadAll( group ); + + short groupCount = pRestore->ReadShort(); + for ( int j = 0; j < groupCount; ++j ) + { + char szResponseBlockName[SIZE_BLOCK_NAME_BUF]; + + char responsename[ 256 ]; + pRestore->StartBlock( szResponseBlockName ); + if ( !Q_stricmp( szResponseBlockName, "Response" ) ) + { + pRestore->ReadString( responsename, sizeof( responsename ), 0 ); + + // Find it by name + int ri; + for ( ri = 0; ri < group->group.Count(); ++ri ) + { + ParserResponse *response = &group->group[ ri ]; + if ( !Q_stricmp( response->value, responsename ) ) + { + break; + } + } + + if ( ri < group->group.Count() ) + { + ParserResponse *response = &group->group[ ri ]; + pRestore->ReadAll( response ); + } + } + + pRestore->EndBlock(); + } + } + } + + 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 + Rule *rule = rs.m_RulePartitions.FindByName( groupname ); + if ( rule ) + { + bool bEnabled; + pRestore->ReadBool( &bEnabled ); + rule->m_bEnabled = bEnabled; + } + else + { + Warning("Warning: Can't find rule %s\n", groupname); + } + } + + pRestore->EndBlock(); + } +#endif + } +private: + + bool m_fDoLoad; + +} g_DefaultResponseSystemSaveRestoreBlockHandler; + +ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler() +{ + return &g_DefaultResponseSystemSaveRestoreBlockHandler; +} + +//----------------------------------------------------------------------------- +// CResponseSystemSaveRestoreOps +// +// Purpose: Handles save and load for instanced response systems... +// +// BUGBUG: This will save the same response system to file multiple times for "shared" response systems and +// therefore it'll restore the same data onto the same pointer N times on reload (probably benign for now, but we could +// write code to save/restore the instanced ones by filename in the block handler above maybe? +//----------------------------------------------------------------------------- + +class CResponseSystemSaveRestoreOps : public CDefSaveRestoreOps +{ +public: + + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) + { + CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField; + if ( !pRS || pRS == &defaultresponsesytem ) + return; + + int count = pRS->m_Responses.Count(); + pSave->WriteInt( &count ); + for ( int i = 0; i < count; ++i ) + { + pSave->StartBlock( "ResponseGroup" ); + + pSave->WriteString( pRS->m_Responses.GetElementName( i ) ); + const ResponseGroup *group = &pRS->m_Responses[ i ]; + pSave->WriteAll( group ); + + short groupCount = group->group.Count(); + pSave->WriteShort( &groupCount ); + for ( int j = 0; j < groupCount; ++j ) + { + const ParserResponse *response = &group->group[ j ]; + pSave->StartBlock( "Response" ); + pSave->WriteString( response->value ); + pSave->WriteAll( response ); + pSave->EndBlock(); + } + + pSave->EndBlock(); + } + } + + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) + { + CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField; + if ( !pRS || pRS == &defaultresponsesytem ) + return; + + int count = pRestore->ReadInt(); + for ( int i = 0; i < count; ++i ) + { + char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF]; + pRestore->StartBlock( szResponseGroupBlockName ); + if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) ) + { + + char groupname[ 256 ]; + pRestore->ReadString( groupname, sizeof( groupname ), 0 ); + + // Try and find it + int idx = pRS->m_Responses.Find( groupname ); + if ( idx != pRS->m_Responses.InvalidIndex() ) + { + ResponseGroup *group = &pRS->m_Responses[ idx ]; + pRestore->ReadAll( group ); + + short groupCount = pRestore->ReadShort(); + for ( int j = 0; j < groupCount; ++j ) + { + char szResponseBlockName[SIZE_BLOCK_NAME_BUF]; + + char responsename[ 256 ]; + pRestore->StartBlock( szResponseBlockName ); + if ( !Q_stricmp( szResponseBlockName, "Response" ) ) + { + pRestore->ReadString( responsename, sizeof( responsename ), 0 ); + + // Find it by name + int ri; + for ( ri = 0; ri < group->group.Count(); ++ri ) + { + ParserResponse *response = &group->group[ ri ]; + if ( !Q_stricmp( response->value, responsename ) ) + { + break; + } + } + + if ( ri < group->group.Count() ) + { + ParserResponse *response = &group->group[ ri ]; + pRestore->ReadAll( response ); + } + } + + pRestore->EndBlock(); + } + } + } + + pRestore->EndBlock(); + } + } + +} g_ResponseSystemSaveRestoreOps; + +ISaveRestoreOps *responseSystemSaveRestoreOps = &g_ResponseSystemSaveRestoreOps; + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CDefaultResponseSystem::Init() +{ + /* + Warning( "sizeof( Response ) == %d\n", sizeof( Response ) ); + Warning( "sizeof( ResponseGroup ) == %d\n", sizeof( ResponseGroup ) ); + Warning( "sizeof( Criteria ) == %d\n", sizeof( Criteria ) ); + Warning( "sizeof( AI_ResponseParams ) == %d\n", sizeof( AI_ResponseParams ) ); + */ + const char *basescript = GetScriptFile(); + + LoadRuleSet( basescript ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDefaultResponseSystem::Shutdown() +{ + // Wipe instanced versions + ClearInstanced(); + + // Clear outselves + Clear(); + // IServerSystem chain + BaseClass::Shutdown(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Instance a custom response system +// Input : *scriptfile - +// Output : IResponseSystem +//----------------------------------------------------------------------------- +IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ) +{ + return defaultresponsesytem.PrecacheCustomResponseSystem( scriptfile ); +} + +//----------------------------------------------------------------------------- +// Purpose: Instance a custom response system +// Input : *scriptfile - +// set - +// Output : IResponseSystem +//----------------------------------------------------------------------------- +IResponseSystem *BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore ) +{ + return defaultresponsesytem.BuildCustomResponseSystemGivenCriteria( pszBaseFile, pszCustomName, criteriaSet, flCriteriaScore ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void DestroyCustomResponseSystems() +{ + defaultresponsesytem.DestroyCustomResponseSystems(); +} diff --git a/mp/src/game/shared/ai_responsesystem_new.h b/mp/src/game/shared/ai_responsesystem_new.h new file mode 100644 index 00000000..9d2fff6b --- /dev/null +++ b/mp/src/game/shared/ai_responsesystem_new.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef AI_RESPONSESYSTEM_H +#define AI_RESPONSESYSTEM_H + +#include "utlvector.h" + +#ifdef _WIN32 +#pragma once +#endif + +#include "AI_Criteria.h" +#include "../../public/responserules/response_types.h" + +// using ResponseRules::IResponseFilter; +// using ResponseRules::IResponseSystem; + +ResponseRules::IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ); +ResponseRules::IResponseSystem *BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore ); +void DestroyCustomResponseSystems(); + +class ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler(); +class ISaveRestoreOps *GetResponseSystemSaveRestoreOps(); + +#endif // AI_RESPONSESYSTEM_H diff --git a/mp/src/game/shared/ai_speechconcept.cpp b/mp/src/game/shared/ai_speechconcept.cpp new file mode 100644 index 00000000..c0ae8e36 --- /dev/null +++ b/mp/src/game/shared/ai_speechconcept.cpp @@ -0,0 +1,28 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "ai_speechconcept.h" + +#ifdef GAME_DLL +#include "game.h" +#include "ai_basenpc.h" +#include "sceneentity.h" +#endif + +#include "engine/ienginesound.h" +#include "keyvalues.h" +#include "ai_criteria.h" +#include "isaverestore.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include + + +// empty \ No newline at end of file diff --git a/mp/src/game/shared/ai_speechconcept.h b/mp/src/game/shared/ai_speechconcept.h new file mode 100644 index 00000000..3e375a0a --- /dev/null +++ b/mp/src/game/shared/ai_speechconcept.h @@ -0,0 +1,45 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Class data for an AI Concept, an atom of response-driven dialog. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_SPEECHCONCEPT_H +#define AI_SPEECHCONCEPT_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "responserules/response_types.h" + +class CAI_Concept : public ResponseRules::CRR_Concept +{ +public: + CAI_Concept() {}; + // construct concept from a string. + CAI_Concept(const char *fromString) : CRR_Concept(fromString) {} ; + + // get/set BS + inline EHANDLE GetSpeaker() const { return m_hSpeaker; } + inline void SetSpeaker(EHANDLE val) { m_hSpeaker = val; } + + /* + inline EHANDLE GetTarget() const { return m_hTarget; } + inline void SetTarget(EHANDLE val) { m_hTarget = val; } + inline EHANDLE GetTopic() const { return m_hTopic; } + inline void SetTopic(EHANDLE val) { m_hTopic = val; } + */ + +protected: + EHANDLE m_hSpeaker; + + /* + EHANDLE m_hTarget; + EHANDLE m_hTopic; + */ +}; + + +#endif diff --git a/mp/src/game/shared/base_playeranimstate.cpp b/mp/src/game/shared/base_playeranimstate.cpp index 768e4f17..09770f9f 100644 --- a/mp/src/game/shared/base_playeranimstate.cpp +++ b/mp/src/game/shared/base_playeranimstate.cpp @@ -544,7 +544,26 @@ bool CBasePlayerAnimState::CanThePlayerMove() void CBasePlayerAnimState::ComputePlaybackRate() { VPROF( "CBasePlayerAnimState::ComputePlaybackRate" ); +#ifdef MAPBASE + if ( m_AnimConfig.m_LegAnimType == LEGANIM_9WAY ) + { + // If the movement would be greater than the pose range, set playback rate anyway + if ( abs(m_vLastMovePose.x) > 1.0f || abs(m_vLastMovePose.y) > 1.0f ) + { + bool bIsMoving; + float flRate = CalcMovementPlaybackRate( &bIsMoving ); + if ( bIsMoving ) + GetOuter()->SetPlaybackRate( flRate ); + else + GetOuter()->SetPlaybackRate( 1 ); + } + else + GetOuter()->SetPlaybackRate( 1 ); + } + else // Allow LEGANIM_8WAY to change playback rate +#else if ( m_AnimConfig.m_LegAnimType != LEGANIM_9WAY && m_AnimConfig.m_LegAnimType != LEGANIM_8WAY ) +#endif { // When using a 9-way blend, playback rate is always 1 and we just scale the pose params // to speed up or slow down the animation. diff --git a/mp/src/game/shared/basecombatweapon_shared.cpp b/mp/src/game/shared/basecombatweapon_shared.cpp index 9785f926..3cb21657 100644 --- a/mp/src/game/shared/basecombatweapon_shared.cpp +++ b/mp/src/game/shared/basecombatweapon_shared.cpp @@ -200,7 +200,11 @@ void CBaseCombatWeapon::Spawn( void ) if ( GetWorldModel() ) { +#ifdef MAPBASE + SetModel( (GetDroppedModel() && GetDroppedModel()[0]) ? GetDroppedModel() : GetWorldModel() ); +#else SetModel( GetWorldModel() ); +#endif } #if !defined( CLIENT_DLL ) @@ -305,6 +309,18 @@ void CBaseCombatWeapon::Precache( void ) { m_iWorldModelIndex = CBaseEntity::PrecacheModel( GetWorldModel() ); } +#ifdef MAPBASE + m_iDroppedModelIndex = 0; + if ( GetDroppedModel() && GetDroppedModel()[0] ) + { + m_iDroppedModelIndex = CBaseEntity::PrecacheModel( GetDroppedModel() ); + } + else + { + // Use the world model index + m_iDroppedModelIndex = m_iWorldModelIndex; + } +#endif // Precache sounds, too for ( int i = 0; i < NUM_SHOOT_SOUND_TYPES; ++i ) @@ -475,6 +491,38 @@ bool CBaseCombatWeapon::IsMeleeWeapon() const return GetWpnData().m_bMeleeWeapon; } +#ifdef MAPBASE +float CBaseCombatWeapon::GetViewmodelFOVOverride() const +{ + return GetWpnData().m_flViewmodelFOV; +} + +float CBaseCombatWeapon::GetBobScale() const +{ + return GetWpnData().m_flBobScale; +} + +float CBaseCombatWeapon::GetSwayScale() const +{ + return GetWpnData().m_flSwayScale; +} + +float CBaseCombatWeapon::GetSwaySpeedScale() const +{ + return GetWpnData().m_flSwaySpeedScale; +} + +const char *CBaseCombatWeapon::GetDroppedModel() const +{ + return GetWpnData().szDroppedModel; +} + +bool CBaseCombatWeapon::UsesHands() const +{ + return GetWpnData().m_bUsesHands; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1060,7 +1108,18 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassify() Activity idleact = ActivityOverride(ACT_IDLE_ANGRY, NULL); switch (idleact) { +#if EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_REVOLVER: +#endif case ACT_IDLE_ANGRY_PISTOL: return WEPCLASS_HANDGUN; +#if EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_CROSSBOW: // For now, crossbows are rifles +#endif +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_AR1: + case ACT_IDLE_ANGRY_SMG2: + case ACT_IDLE_ANGRY_SNIPER_RIFLE: +#endif case ACT_IDLE_ANGRY_SMG1: case ACT_IDLE_ANGRY_AR2: return WEPCLASS_RIFLE; case ACT_IDLE_ANGRY_SHOTGUN: return WEPCLASS_SHOTGUN; @@ -1091,6 +1150,23 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str) return WEPCLASS_INVALID; } +#ifdef HL2_DLL +extern acttable_t *GetSMG1Acttable(); +extern int GetSMG1ActtableCount(); + +extern acttable_t *GetAR2Acttable(); +extern int GetAR2ActtableCount(); + +extern acttable_t *GetShotgunActtable(); +extern int GetShotgunActtableCount(); + +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); + +extern acttable_t *Get357Acttable(); +extern int Get357ActtableCount(); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1098,12 +1174,81 @@ 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()) +#ifdef HL2_DLL + // Melee users should not use SMG animations for missing activities. + if (IsMeleeWeapon() && GetBackupActivityList() == GetSMG1Acttable()) return false; +#endif return true; } + +acttable_t *CBaseCombatWeapon::GetBackupActivityList() +{ + return NULL; +} + +int CBaseCombatWeapon::GetBackupActivityListCount() +{ + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +acttable_t *CBaseCombatWeapon::GetDefaultBackupActivityList( acttable_t *pTable, int &actCount ) +{ +#ifdef HL2_DLL + // Ensure this isn't already a default backup activity list + if (pTable == GetSMG1Acttable() || pTable == GetPistolActtable()) + return NULL; + + // Use a backup table based on what ACT_IDLE_ANGRY is translated to + Activity actTranslated = ACT_INVALID; + for ( int i = 0; i < actCount; i++, pTable++ ) + { + if ( pTable->baseAct == ACT_IDLE_ANGRY ) + { + actTranslated = (Activity)pTable->weaponAct; + break; + } + } + + if (actTranslated == ACT_INVALID) + return NULL; + + switch (actTranslated) + { +#if EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_REVOLVER: +#endif + case ACT_IDLE_ANGRY_PISTOL: + { + actCount = GetPistolActtableCount(); + return GetPistolActtable(); + } +#if EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_CROSSBOW: // For now, crossbows are rifles +#endif +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_AR1: + case ACT_IDLE_ANGRY_SMG2: + case ACT_IDLE_ANGRY_SNIPER_RIFLE: +#endif + case ACT_IDLE_ANGRY_SMG1: + case ACT_IDLE_ANGRY_AR2: + case ACT_IDLE_ANGRY_SHOTGUN: + case ACT_IDLE_ANGRY_RPG: + { + actCount = GetSMG1ActtableCount(); + return GetSMG1Acttable(); + } + } +#endif + + actCount = 0; + return NULL; +} #endif @@ -1564,6 +1709,10 @@ bool CBaseCombatWeapon::DefaultDeploy( char *szViewModel, char *szWeaponModel, i SetViewModel(); SendWeaponAnim( iActivity ); + +#ifdef MAPBASE + pOwner->SetAnimation( PLAYER_UNHOLSTER ); +#endif pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() ); } @@ -1640,6 +1789,11 @@ bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) if (pOwner) { pOwner->SetNextAttack( gpGlobals->curtime + flSequenceDuration ); + +#ifdef MAPBASE + if (IsWeaponVisible() && pOwner->IsPlayer()) + static_cast(pOwner)->SetAnimation( PLAYER_HOLSTER ); +#endif } // If we don't have a holster anim, hide immediately to avoid timing issues @@ -2366,6 +2520,28 @@ bool CBaseCombatWeapon::Reload( void ) return DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::Reload_NPC( bool bPlaySound ) +{ + if (bPlaySound) + WeaponSound( RELOAD_NPC ); + + if (UsesClipsForAmmo1()) + { + m_iClip1 = GetMaxClip1(); + } + else + { + // For weapons which don't use clips, give the owner ammo. + if (GetOwner()) + GetOwner()->SetAmmoCount( GetDefaultClip1(), m_iPrimaryAmmoType ); + } +} +#endif + //========================================================= void CBaseCombatWeapon::WeaponIdle( void ) { @@ -2944,15 +3120,33 @@ END_PREDICTION_DATA() IMPLEMENT_NETWORKCLASS_ALIASED( BaseCombatWeapon, DT_BaseCombatWeapon ) #ifdef MAPBASE_VSCRIPT + +// Don't allow client to use Set functions. +// They will only cause visual discrepancies, +// and will be reverted on the next update from the server. +#ifdef GAME_DLL +#define DEFINE_SCRIPTFUNC_SV( p1, p2 ) DEFINE_SCRIPTFUNC( p1, p2 ) +#define DEFINE_SCRIPTFUNC_NAMED_SV( p1, p2, p3 ) DEFINE_SCRIPTFUNC_NAMED( p1, p2, p3 ) + +#define DEFINE_SCRIPTFUNC_CL( p1, p2 ) +#define DEFINE_SCRIPTFUNC_NAMED_CL( p1, p2, p3 ) +#else +#define DEFINE_SCRIPTFUNC_SV( p1, p2 ) +#define DEFINE_SCRIPTFUNC_NAMED_SV( p1, p2, p3 ) + +#define DEFINE_SCRIPTFUNC_CL( p1, p2 ) DEFINE_SCRIPTFUNC( p1, p2 ) +#define DEFINE_SCRIPTFUNC_NAMED_CL( p1, p2, p3 ) DEFINE_SCRIPTFUNC_NAMED( p1, p2, p3 ) +#endif + 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_NAMED_SV( 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_NAMED_SV( ScriptSetClip1, "SetClip1", "Set the weapon's current primary ammo." ) + DEFINE_SCRIPTFUNC_NAMED_SV( 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." ) @@ -2963,36 +3157,37 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, CBaseAnimating, "The base class for all 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_SV( 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_SV( 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( GetDroppedModel, "Get the weapon's unique dropped model if it has one." ) DEFINE_SCRIPTFUNC( GetWeight, "Get the weapon's weight." ) + DEFINE_SCRIPTFUNC( GetPrintName, "" ) + + DEFINE_SCRIPTFUNC_CL( GetSlot, "" ) + DEFINE_SCRIPTFUNC_CL( GetPosition, "" ) 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_SV( CapabilitiesGet, "Get the capabilities the weapon currently possesses." ) 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_SV( 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." ) @@ -3009,22 +3204,22 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, CBaseAnimating, "The base class for all 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_SV( 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_SV( 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_SV( 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_SV( 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_SV( 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_SV( 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_SV( 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." ) + DEFINE_SCRIPTFUNC_SV( SetNextSecondaryAttack, "Sets the next time SecondaryAttack() will run when the player is pressing +ATTACK2." ) END_SCRIPTDESC(); #endif @@ -3282,6 +3477,9 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) SendPropDataTable("LocalActiveWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalActiveWeaponData), SendProxy_SendActiveLocalWeaponDataTable ), SendPropModelIndex( SENDINFO(m_iViewModelIndex) ), SendPropModelIndex( SENDINFO(m_iWorldModelIndex) ), +#ifdef MAPBASE + SendPropModelIndex( SENDINFO(m_iDroppedModelIndex) ), +#endif SendPropInt( SENDINFO(m_iState ), 8, SPROP_UNSIGNED ), SendPropEHandle( SENDINFO(m_hOwner) ), @@ -3294,6 +3492,9 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) RecvPropDataTable("LocalActiveWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponData)), RecvPropInt( RECVINFO(m_iViewModelIndex)), RecvPropInt( RECVINFO(m_iWorldModelIndex)), +#ifdef MAPBASE + RecvPropInt( RECVINFO(m_iDroppedModelIndex) ), +#endif RecvPropInt( RECVINFO(m_iState), 0, &CBaseCombatWeapon::RecvProxy_WeaponState ), RecvPropEHandle( RECVINFO(m_hOwner ) ), diff --git a/mp/src/game/shared/basecombatweapon_shared.h b/mp/src/game/shared/basecombatweapon_shared.h index 762ca099..03b6bef2 100644 --- a/mp/src/game/shared/basecombatweapon_shared.h +++ b/mp/src/game/shared/basecombatweapon_shared.h @@ -251,6 +251,9 @@ public: static WeaponClass_t WeaponClassFromString(const char *str); virtual bool SupportsBackupActivity(Activity activity); + virtual acttable_t *GetBackupActivityList(); + virtual int GetBackupActivityListCount(); + static acttable_t *GetDefaultBackupActivityList( acttable_t *pTable, int &actCount ); #endif virtual void Equip( CBaseCombatCharacter *pOwner ); @@ -341,7 +344,7 @@ public: 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(); } + virtual void Reload_NPC( bool bPlaySound = true ); #endif virtual bool AutoFiresFullClip( void ) const { return false; } @@ -437,6 +440,14 @@ public: virtual bool UsesClipsForAmmo1( void ) const; virtual bool UsesClipsForAmmo2( void ) const; bool IsMeleeWeapon() const; +#ifdef MAPBASE + float GetViewmodelFOVOverride() const; + float GetBobScale() const; + float GetSwayScale() const; + float GetSwaySpeedScale() const; + virtual const char *GetDroppedModel( void ) const; + bool UsesHands( void ) const; +#endif // derive this function if you mod uses encrypted weapon info files virtual const unsigned char *GetEncryptionKey( void ); @@ -697,6 +708,9 @@ public: // Weapon art CNetworkVar( int, m_iViewModelIndex ); CNetworkVar( int, m_iWorldModelIndex ); +#ifdef MAPBASE + CNetworkVar( int, m_iDroppedModelIndex ); +#endif // Sounds float m_flNextEmptySoundTime; // delay on empty sound playing diff --git a/mp/src/game/shared/baseentity_shared.cpp b/mp/src/game/shared/baseentity_shared.cpp index cf123b10..24c18104 100644 --- a/mp/src/game/shared/baseentity_shared.cpp +++ b/mp/src/game/shared/baseentity_shared.cpp @@ -1620,7 +1620,7 @@ typedef CTraceFilterSimpleList CBulletsTraceFilter; void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) { #if defined(MAPBASE_VSCRIPT) && defined(GAME_DLL) - if (m_ScriptScope.IsInitialized()) + if ( m_ScriptScope.IsInitialized() && g_Hook_FireBullets.CanRunInScope( m_ScriptScope ) ) { HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); @@ -1938,7 +1938,11 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) { flActualDamage = g_pGameRules->GetAmmoDamage( pAttacker, tr.m_pEnt, info.m_iAmmoType ); } +#ifdef MAPBASE + else if ((info.m_nFlags & FIRE_BULLETS_NO_AUTO_GIB_TYPE) == 0) +#else else +#endif { nActualDamageType = nDamageType | ((flActualDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB ); } @@ -2409,8 +2413,67 @@ void CBaseEntity::ModifyEmitSoundParams( EmitSound_t ¶ms ) params.m_pSoundName = GameRules()->TranslateEffectForVisionFilter( "sounds", params.m_pSoundName ); } #endif + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_ModifyEmitSoundParams.CanRunInScope( m_ScriptScope )) + { + HSCRIPT hParams = g_pScriptVM->RegisterInstance( reinterpret_cast(¶ms) ); + + // params + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( hParams ) }; + g_Hook_ModifyEmitSoundParams.Call( m_ScriptScope, &functionReturn, args ); + + g_pScriptVM->RemoveInstance( hParams ); + } +#endif } +#if defined(MAPBASE) && defined(GAME_DLL) +void CBaseEntity::ModifySentenceParams( int &iSentenceIndex, int &iChannel, float &flVolume, soundlevel_t &iSoundlevel, int &iFlags, int &iPitch, + const Vector **pOrigin, const Vector **pDirection, bool &bUpdatePositions, float &soundtime, int &iSpecialDSP, int &iSpeakerIndex ) +{ +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_ModifySentenceParams.CanRunInScope( m_ScriptScope )) + { + // This is a bit of a hack, but for consistency with ModifyEmitSoundParams, put them into an EmitSound_t params + ScriptEmitSound_t params; + params.m_pSoundName = engine->SentenceNameFromIndex( iSentenceIndex ); + params.m_nChannel = iChannel; + params.m_flVolume = flVolume; + params.m_SoundLevel = iSoundlevel; + params.m_nFlags = iFlags; + params.m_nPitch = iPitch; + params.m_pOrigin = *pOrigin; + params.m_flSoundTime = soundtime; + params.m_nSpecialDSP = iSpecialDSP; + params.m_nSpeakerEntity = iSpeakerIndex; + + HSCRIPT hParams = g_pScriptVM->RegisterInstance( ¶ms ); + + // params + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( hParams ) }; + if (g_Hook_ModifySentenceParams.Call( m_ScriptScope, &functionReturn, args )) + { + iSentenceIndex = engine->SentenceIndexFromName( params.m_pSoundName ); + iChannel = params.m_nChannel; + flVolume = params.m_flVolume; + iSoundlevel = params.m_SoundLevel; + iFlags = params.m_nFlags; + iPitch = params.m_nPitch; + *pOrigin = params.m_pOrigin; + soundtime = params.m_flSoundTime; + iSpecialDSP = params.m_nSpecialDSP; + iSpeakerIndex = params.m_nSpeakerEntity; + } + + g_pScriptVM->RemoveInstance( hParams ); + } +#endif +} +#endif + //----------------------------------------------------------------------------- // These methods encapsulate MOVETYPE_FOLLOW, which became obsolete //----------------------------------------------------------------------------- @@ -2434,6 +2497,18 @@ void CBaseEntity::FollowEntity( CBaseEntity *pBaseEntity, bool bBoneMerge ) } } +#ifdef MAPBASE_VSCRIPT +void CBaseEntity::ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ) +{ + FollowEntity( ToEnt( hBaseEntity ), bBoneMerge ); +} + +HSCRIPT CBaseEntity::ScriptGetFollowedEntity() +{ + return ToHScript( GetFollowedEntity() ); +} +#endif + void CBaseEntity::SetEffectEntity( CBaseEntity *pEffectEnt ) { if ( m_hEffectEntity.Get() != pEffectEnt ) @@ -2623,3 +2698,357 @@ bool CBaseEntity::IsToolRecording() const #endif } #endif + +#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); +} + +HSCRIPT CBaseEntity::GetScriptOwnerEntity() +{ + return ToHScript(GetOwnerEntity()); +} + +void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) +{ + SetOwnerEntity(ToEnt(pOwner)); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +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; +} + + +#ifdef GAME_DLL +#define SCRIPT_NEVER_THINK TICK_NEVER_THINK +#else +#define SCRIPT_NEVER_THINK CLIENT_THINK_NEVER +#endif + +static inline void ScriptStopContextThink( scriptthinkfunc_t *context ) +{ + Assert( context->m_hfnThink ); + Assert( context->m_flNextThink == SCRIPT_NEVER_THINK ); + + g_pScriptVM->ReleaseScript( context->m_hfnThink ); + context->m_hfnThink = NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptContextThink() +{ + float flNextThink = FLT_MAX; + float flScheduled = 0.0f; + + ScriptVariant_t arg = m_hScriptInstance; + + for ( int i = 0; i < m_ScriptThinkFuncs.Count(); ++i ) + { + scriptthinkfunc_t *cur = m_ScriptThinkFuncs[i]; + + if ( cur->m_flNextThink == SCRIPT_NEVER_THINK ) + { + continue; + } + + if ( cur->m_flNextThink > gpGlobals->curtime ) + { + if ( ( flScheduled == 0.0f ) || ( flScheduled > cur->m_flNextThink ) ) + { + flScheduled = cur->m_flNextThink; + } + continue; + } + +#ifdef _DEBUG + // going to run the script func + cur->m_flNextThink = 0; +#endif + + ScriptVariant_t varReturn; + +#ifndef CLIENT_DLL + if ( !cur->m_bNoParam ) + { +#endif + g_pScriptVM->ExecuteFunction( cur->m_hfnThink, &arg, 1, &varReturn, NULL, true ); +#ifndef CLIENT_DLL + } + else + { + g_pScriptVM->ExecuteFunction( cur->m_hfnThink, NULL, 0, &varReturn, NULL, true ); + } +#endif + + if ( cur->m_flNextThink == SCRIPT_NEVER_THINK ) + { + // stopped from script while thinking + continue; + } + + float flReturn; + if ( !varReturn.AssignTo( &flReturn ) ) + { + varReturn.Free(); + cur->m_flNextThink = SCRIPT_NEVER_THINK; + continue; + } + + if ( flReturn < 0.0f ) + { + cur->m_flNextThink = SCRIPT_NEVER_THINK; + continue; + } + + if ( flReturn < flNextThink ) + { + flNextThink = flReturn; + } + + cur->m_flNextThink = gpGlobals->curtime + flReturn - 0.001f; + } + + // deferred safe removal + for ( int i = 0; i < m_ScriptThinkFuncs.Count(); ) + { + scriptthinkfunc_t *cur = m_ScriptThinkFuncs[i]; + if ( cur->m_flNextThink == SCRIPT_NEVER_THINK ) + { + ScriptStopContextThink( cur ); + delete cur; + m_ScriptThinkFuncs.Remove(i); + } + else ++i; + } + + if ( flNextThink < FLT_MAX ) + { + if ( flScheduled > 0.0f ) + { + flNextThink = min( gpGlobals->curtime + flNextThink, flScheduled ); + } + else + { + flNextThink = gpGlobals->curtime + flNextThink; + } + } + else + { + if ( flScheduled > 0.0f ) + { + flNextThink = flScheduled; + } + else + { + flNextThink = SCRIPT_NEVER_THINK; + } + } + +#ifdef _DEBUG +#ifdef GAME_DLL + int nNextThinkTick = GetNextThinkTick("ScriptContextThink"); + float flNextThinkTime = TICKS_TO_TIME(nNextThinkTick); + + // If internal next think tick is earlier than what we have here with flNextThink, + // whoever set that think may fail. In worst case scenario the entity may stop thinking. + if ( nNextThinkTick > gpGlobals->tickcount ) + { + if ( flNextThink == SCRIPT_NEVER_THINK ) + Assert(0); + if ( flNextThinkTime < flNextThink ) + Assert(0); + } +#endif +#endif + +#ifdef GAME_DLL + SetNextThink( flNextThink, "ScriptContextThink" ); +#else + SetNextClientThink( flNextThink ); +#endif +} + +#ifndef CLIENT_DLL +// see ScriptSetThink +static bool s_bScriptContextThinkNoParam = false; +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float flTime ) +{ +#ifdef CLIENT_DLL + // Context thinking is not yet supported on client, entities can only have 1 think function. + // C_World does not have one by default, so it is safe to set its. + if ( !IsWorld() ) + { + g_pScriptVM->RaiseException("SetContextThink is only supported on C_World"); + return; + } +#endif + + scriptthinkfunc_t *pf = NULL; + unsigned hash = szContext ? HashString( szContext ) : 0; + + FOR_EACH_VEC( m_ScriptThinkFuncs, i ) + { + scriptthinkfunc_t *f = m_ScriptThinkFuncs[i]; + if ( hash == f->m_iContextHash ) + { + pf = f; + break; + } + } + + if ( hFunc ) + { + // add new + if ( !pf ) + { + pf = new scriptthinkfunc_t; + + m_ScriptThinkFuncs.SetGrowSize(1); + m_ScriptThinkFuncs.AddToTail( pf ); + + pf->m_iContextHash = hash; +#ifndef CLIENT_DLL + pf->m_bNoParam = s_bScriptContextThinkNoParam; +#endif + } + // update existing + else + { +#ifdef _DEBUG + if ( pf->m_flNextThink == 0 ) + { + Warning("Script think ('%s') was changed while it was thinking!\n", szContext); + } +#endif + g_pScriptVM->ReleaseScript( pf->m_hfnThink ); + } + + float nextthink = gpGlobals->curtime + flTime; + + pf->m_hfnThink = hFunc; + pf->m_flNextThink = nextthink; + +#ifdef GAME_DLL + int nexttick = GetNextThinkTick( RegisterThinkContext( "ScriptContextThink" ) ); + if ( nexttick <= 0 || TICKS_TO_TIME(nexttick) > nextthink ) + { + SetContextThink( &CBaseEntity::ScriptContextThink, nextthink, "ScriptContextThink" ); + } +#else + { + // let it self adjust + SetNextClientThink( gpGlobals->curtime ); + } +#endif + } + // null func input, think exists + else if ( pf ) + { + pf->m_flNextThink = SCRIPT_NEVER_THINK; + } +} + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// m_bNoParam and s_bScriptContextThinkNoParam exist only to keep backwards compatibility +// and are an alternative to this script closure: +// +// function CBaseEntity::SetThink( func, time ) +// { +// SetContextThink( "", function(_){ return func() }, time ) +// } +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetThink( HSCRIPT hFunc, float time ) +{ + s_bScriptContextThinkNoParam = true; + ScriptSetContextThink( NULL, hFunc, time ); + s_bScriptContextThinkNoParam = false; +} + +void CBaseEntity::ScriptStopThink() +{ + ScriptSetContextThink( NULL, NULL, 0.0f ); +} +#endif +#endif diff --git a/mp/src/game/shared/baseviewmodel_shared.cpp b/mp/src/game/shared/baseviewmodel_shared.cpp index b7fef9b7..5f9c0f10 100644 --- a/mp/src/game/shared/baseviewmodel_shared.cpp +++ b/mp/src/game/shared/baseviewmodel_shared.cpp @@ -289,6 +289,19 @@ void CBaseViewModel::AddEffects( int nEffects ) SetControlPanelsActive( false ); } +#ifdef MAPBASE + if (GetOwningWeapon() && GetOwningWeapon()->UsesHands()) + { + // If using hands, apply effect changes to any viewmodel children as well + // (fixes hand models) + for (CBaseEntity *pChild = FirstMoveChild(); pChild != NULL; pChild = pChild->NextMovePeer()) + { + if (pChild->GetClassname()[0] == 'h') + pChild->AddEffects( nEffects ); + } + } +#endif + BaseClass::AddEffects( nEffects ); } @@ -302,6 +315,19 @@ void CBaseViewModel::RemoveEffects( int nEffects ) SetControlPanelsActive( true ); } +#ifdef MAPBASE + if (GetOwningWeapon() && GetOwningWeapon()->UsesHands()) + { + // If using hands, apply effect changes to any viewmodel children as well + // (fixes hand models) + for (CBaseEntity *pChild = FirstMoveChild(); pChild != NULL; pChild = pChild->NextMovePeer()) + { + if (pChild->GetClassname()[0] == 'h') + pChild->RemoveEffects( nEffects ); + } + } +#endif + BaseClass::RemoveEffects( nEffects ); } @@ -339,6 +365,18 @@ void CBaseViewModel::SetWeaponModel( const char *modelname, CBaseCombatWeapon *w SetControlPanelsActive( showControlPanels ); } #endif + +#ifdef MAPBASE + // If our owning weapon doesn't support hands, disable the hands viewmodel(s) + bool bSupportsHands = weapon != NULL ? weapon->UsesHands() : false; + for (CBaseEntity *pChild = FirstMoveChild(); pChild != NULL; pChild = pChild->NextMovePeer()) + { + if (pChild->GetClassname()[0] == 'h') + { + bSupportsHands ? pChild->RemoveEffects( EF_NODRAW ) : pChild->AddEffects( EF_NODRAW ); + } + } +#endif } //----------------------------------------------------------------------------- @@ -424,6 +462,21 @@ void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePos g_ClientVirtualReality.OverrideViewModelTransform( vmorigin, vmangles, pWeapon && pWeapon->ShouldUseLargeViewModelVROverride() ); } +#ifdef MAPBASE + // Flip the view if we should be flipping + if (ShouldFlipViewModel()) + { + Vector vecOriginDiff = (eyePosition - vmorigin); + QAngle angAnglesDiff = (eyeAngles - vmangles); + + vmorigin.x = (eyePosition.x + vecOriginDiff.x); + vmorigin.y = (eyePosition.y + vecOriginDiff.y); + + vmangles.y = (eyeAngles.y + angAnglesDiff.y); + vmangles.z = (eyeAngles.z + angAnglesDiff.z); + } +#endif + SetLocalOrigin( vmorigin ); SetLocalAngles( vmangles ); @@ -478,6 +531,23 @@ void CBaseViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& o float flSpeed = 5.0f; +#ifdef MAPBASE + CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); + if (pWeapon) + { + const FileWeaponInfo_t *pInfo = &pWeapon->GetWpnData(); + if (pInfo->m_flSwayScale != 1.0f) + { + vDifference *= pInfo->m_flSwayScale; + pInfo->m_flSwayScale != 0.0f ? flSpeed /= pInfo->m_flSwayScale : flSpeed = 0.0f; + } + if (pInfo->m_flSwaySpeedScale != 1.0f) + { + flSpeed *= pInfo->m_flSwaySpeedScale; + } + } +#endif + // If we start to lag too far behind, we'll increase the "catch up" speed. Solves the problem with fast cl_yawspeed, m_yaw or joysticks // rotating quickly. The old code would slam lastfacing with origin causing the viewmodel to pop to a new position float flDiff = vDifference.Length(); @@ -702,7 +772,13 @@ class CHandViewModel : public CBaseViewModel DECLARE_CLASS( CHandViewModel, CBaseViewModel ); public: DECLARE_NETWORKCLASS(); + + CBaseViewModel *GetVMOwner(); + + CBaseCombatWeapon *GetOwningWeapon( void ); + private: + CHandle m_hVMOwner; }; LINK_ENTITY_TO_CLASS(hand_viewmodel, CHandViewModel); @@ -718,4 +794,26 @@ BEGIN_NETWORK_TABLE(CHandViewModel, DT_HandViewModel) RecvPropInt(RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent), #endif END_NETWORK_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseViewModel *CHandViewModel::GetVMOwner() +{ + if (!m_hVMOwner) + m_hVMOwner = assert_cast(GetMoveParent()); + return m_hVMOwner; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseCombatWeapon *CHandViewModel::GetOwningWeapon() +{ + CBaseViewModel *pVM = GetVMOwner(); + if (pVM) + return pVM->GetOwningWeapon(); + else + return NULL; +} #endif diff --git a/mp/src/game/shared/beam_shared.cpp b/mp/src/game/shared/beam_shared.cpp index c3e39af8..fcefa3f9 100644 --- a/mp/src/game/shared/beam_shared.cpp +++ b/mp/src/game/shared/beam_shared.cpp @@ -42,6 +42,9 @@ public: DECLARE_CLASS( CInfoTarget, CPointEntity ); void Spawn( void ); +#ifdef MAPBASE + virtual int UpdateTransmitState(); +#endif }; //info targets are like point entities except you can force them to spawn on the client @@ -55,6 +58,19 @@ void CInfoTarget::Spawn( void ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Always transmitted to clients +//----------------------------------------------------------------------------- +int CInfoTarget::UpdateTransmitState() +{ + // Spawn flags 2 means we always transmit + if ( HasSpawnFlags(0x02) ) + return SetTransmitState( FL_EDICT_ALWAYS ); + return BaseClass::UpdateTransmitState(); +} +#endif + LINK_ENTITY_TO_CLASS( info_target, CInfoTarget ); #endif diff --git a/mp/src/game/shared/gamemovement.cpp b/mp/src/game/shared/gamemovement.cpp index e4c4fafe..2466091a 100644 --- a/mp/src/game/shared/gamemovement.cpp +++ b/mp/src/game/shared/gamemovement.cpp @@ -3907,13 +3907,11 @@ void CGameMovement::CheckFalling( void ) 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. + // This value lets the existing fall damage functions ensure a fatal fall. 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 ) @@ -3921,7 +3919,11 @@ void CGameMovement::CheckFalling( void ) bool bAlive = true; float fvol = 0.5; +#ifdef MAPBASE + if ( player->GetWaterLevel() > 0 && !player->m_bInTriggerFall ) +#else if ( player->GetWaterLevel() > 0 ) +#endif { // They landed in water. } diff --git a/mp/src/game/shared/hl2/basehlcombatweapon_shared.cpp b/mp/src/game/shared/hl2/basehlcombatweapon_shared.cpp index 8d64f4a5..06c69df7 100644 --- a/mp/src/game/shared/hl2/basehlcombatweapon_shared.cpp +++ b/mp/src/game/shared/hl2/basehlcombatweapon_shared.cpp @@ -317,6 +317,14 @@ float CBaseHLCombatWeapon::CalcViewmodelBob( void ) g_lateralBob = speed*0.005f; g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); + +#ifdef MAPBASE + if (GetBobScale() != 1.0f) + { + //g_verticalBob *= GetBobScale(); + g_lateralBob *= GetBobScale(); + } +#endif //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) return 0.0f; diff --git a/mp/src/game/shared/hl2/hl2_usermessages.cpp b/mp/src/game/shared/hl2/hl2_usermessages.cpp index 0ab97330..93ca871e 100644 --- a/mp/src/game/shared/hl2/hl2_usermessages.cpp +++ b/mp/src/game/shared/hl2/hl2_usermessages.cpp @@ -44,10 +44,11 @@ void RegisterUserMessages( void ) #ifdef MAPBASE // This sends the credits file now usermessages->Register( "CreditsMsg", -1 ); + usermessages->Register( "LogoTimeMsg", -1 ); #else usermessages->Register( "CreditsMsg", 1 ); -#endif usermessages->Register( "LogoTimeMsg", 4 ); +#endif usermessages->Register( "AchievementEvent", -1 ); usermessages->Register( "UpdateJalopyRadar", -1 ); diff --git a/mp/src/game/shared/hl2mp/weapon_357.cpp b/mp/src/game/shared/hl2mp/weapon_357.cpp index d943f278..2b7de4f2 100644 --- a/mp/src/game/shared/hl2mp/weapon_357.cpp +++ b/mp/src/game/shared/hl2mp/weapon_357.cpp @@ -74,6 +74,19 @@ acttable_t CWeapon357::m_acttable[] = IMPLEMENT_ACTTABLE( CWeapon357 ); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the 357's activity table. +acttable_t *Get357Acttable() +{ + return CWeapon357::m_acttable; +} + +int Get357ActtableCount() +{ + return ARRAYSIZE(CWeapon357::m_acttable); +} +#endif + #endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/hl2mp/weapon_ar2.cpp b/mp/src/game/shared/hl2mp/weapon_ar2.cpp index 5086db1b..8bb1ba23 100644 --- a/mp/src/game/shared/hl2mp/weapon_ar2.cpp +++ b/mp/src/game/shared/hl2mp/weapon_ar2.cpp @@ -62,6 +62,19 @@ acttable_t CWeaponAR2::m_acttable[] = IMPLEMENT_ACTTABLE(CWeaponAR2); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the AR2's activity table. +acttable_t *GetAR2Acttable() +{ + return CWeaponAR2::m_acttable; +} + +int GetAR2ActtableCount() +{ + return ARRAYSIZE(CWeaponAR2::m_acttable); +} +#endif + #endif CWeaponAR2::CWeaponAR2( ) diff --git a/mp/src/game/shared/hl2mp/weapon_ar2.h b/mp/src/game/shared/hl2mp/weapon_ar2.h index 8aa8dde8..923b9bfc 100644 --- a/mp/src/game/shared/hl2mp/weapon_ar2.h +++ b/mp/src/game/shared/hl2mp/weapon_ar2.h @@ -76,6 +76,9 @@ protected: int m_nVentPose; #ifndef CLIENT_DLL +#ifdef MAPBASE +public: +#endif DECLARE_ACTTABLE(); #endif }; diff --git a/mp/src/game/shared/hl2mp/weapon_pistol.cpp b/mp/src/game/shared/hl2mp/weapon_pistol.cpp index 5a9533c4..cbaadfff 100644 --- a/mp/src/game/shared/hl2mp/weapon_pistol.cpp +++ b/mp/src/game/shared/hl2mp/weapon_pistol.cpp @@ -143,6 +143,19 @@ acttable_t CWeaponPistol::m_acttable[] = IMPLEMENT_ACTTABLE( CWeaponPistol ); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the pistol's activity table. +acttable_t *GetPistolActtable() +{ + return CWeaponPistol::m_acttable; +} + +int GetPistolActtableCount() +{ + return ARRAYSIZE(CWeaponPistol::m_acttable); +} +#endif + #endif //----------------------------------------------------------------------------- diff --git a/mp/src/game/shared/hl2mp/weapon_shotgun.cpp b/mp/src/game/shared/hl2mp/weapon_shotgun.cpp index da370b53..b2f62fb0 100644 --- a/mp/src/game/shared/hl2mp/weapon_shotgun.cpp +++ b/mp/src/game/shared/hl2mp/weapon_shotgun.cpp @@ -114,6 +114,19 @@ acttable_t CWeaponShotgun::m_acttable[] = IMPLEMENT_ACTTABLE(CWeaponShotgun); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the shotgun's activity table. +acttable_t *GetShotgunActtable() +{ + return CWeaponShotgun::m_acttable; +} + +int GetShotgunActtableCount() +{ + return ARRAYSIZE(CWeaponShotgun::m_acttable); +} +#endif + #endif diff --git a/mp/src/game/shared/hl2mp/weapon_slam.cpp b/mp/src/game/shared/hl2mp/weapon_slam.cpp index 5bdea44f..c4eb7140 100644 --- a/mp/src/game/shared/hl2mp/weapon_slam.cpp +++ b/mp/src/game/shared/hl2mp/weapon_slam.cpp @@ -115,6 +115,10 @@ acttable_t CWeapon_SLAM::m_acttable[] = { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SLAM, false }, { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SLAM, false }, { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SLAM, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SLAM, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeapon_SLAM); diff --git a/mp/src/game/shared/hl2mp/weapon_stunstick.cpp b/mp/src/game/shared/hl2mp/weapon_stunstick.cpp index 649101dd..24fb78b1 100644 --- a/mp/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/mp/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -81,6 +81,27 @@ acttable_t CWeaponStunStick::m_acttable[] = #endif { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_MELEE, false }, + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, + { 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 }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_MELEE, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponStunStick); diff --git a/mp/src/game/shared/hl2mp/weapon_stunstick.h b/mp/src/game/shared/hl2mp/weapon_stunstick.h index 3cfd054a..679d3993 100644 --- a/mp/src/game/shared/hl2mp/weapon_stunstick.h +++ b/mp/src/game/shared/hl2mp/weapon_stunstick.h @@ -77,6 +77,12 @@ public: float GetDamageForActivity( Activity hitActivity ); +#ifdef MAPBASE + // Don't use backup activities + acttable_t *GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } +#endif + CWeaponStunStick( const CWeaponStunStick & ); private: diff --git a/mp/src/game/shared/mapbase/MapEdit.cpp b/mp/src/game/shared/mapbase/MapEdit.cpp index 32659964..0891c29e 100644 --- a/mp/src/game/shared/mapbase/MapEdit.cpp +++ b/mp/src/game/shared/mapbase/MapEdit.cpp @@ -409,7 +409,6 @@ public: pkvClassname = pkvClassname->GetNextKey(); } - pkvClassname->deleteThis(); } else if (FStrEq(pNodeName, "edit")) { @@ -432,7 +431,6 @@ public: pName = pName->GetNextKey(); } - pName->deleteThis(); } else if (FStrEq(pNodeName, "delete")) { @@ -455,7 +453,6 @@ public: pName = pName->GetNextKey(); } - pName->deleteThis(); } else if (FStrEq(pNodeName, "fire")) { @@ -464,7 +461,7 @@ public: { pNodeName = pName->GetName(); - const char *pInputName = NULL; + string_t pInputName = NULL_STRING; variant_t varInputParam; float flInputDelay = 0.0f; CBaseEntity *pActivator = NULL; @@ -480,7 +477,7 @@ public: { // Input name case 0: - pInputName = inputparams; break; + pInputName = AllocPooledString(inputparams); break; // Input parameter case 1: varInputParam.SetString(AllocPooledString(inputparams)); break; @@ -500,9 +497,10 @@ public: iter++; inputparams = strtok(NULL, ","); } + free(pszValue); DebugMsg("MapEdit Debug: Firing input %s on %s\n", pInputName, pNodeName); - g_EventQueue.AddEvent(pNodeName, pInputName, varInputParam, flInputDelay, pActivator, pCaller, iOutputID); + g_EventQueue.AddEvent(pNodeName, STRING(pInputName), varInputParam, flInputDelay, pActivator, pCaller, iOutputID); pName = pName->GetNextKey(); } @@ -524,12 +522,10 @@ public: pkvNodeData = pkvNodeData->GetNextKey(); } - pkvNodeData->deleteThis(); } pkvNode = pkvNode->GetNextKey(); } - pkvNode->deleteThis(); } void SpawnMapEdit(const char *pFile = NULL) @@ -888,8 +884,8 @@ void CC_MapEdit_Print( const CCommand& args ) pkvNode = pkvNode->GetNextKey(); } - pkvNode->deleteThis(); } + pkvFile->deleteThis(); } } static ConCommand mapedit_print("mapedit_print", CC_MapEdit_Print, "Prints a mapedit file in the console."); diff --git a/mp/src/game/shared/mapbase/logic_script_client.cpp b/mp/src/game/shared/mapbase/logic_script_client.cpp new file mode 100644 index 00000000..97edfe30 --- /dev/null +++ b/mp/src/game/shared/mapbase/logic_script_client.cpp @@ -0,0 +1,276 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom client-side equivalent of logic_script. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "vscript_shared.h" +#include "tier1/fmtstr.h" + +#ifdef CLIENT_DLL +ConVar cl_script_think_interval( "cl_script_think_interval", "0.1" ); +#endif + +//----------------------------------------------------------------------------- +// Purpose: An entity that acts as a container for client-side game scripts. +//----------------------------------------------------------------------------- + +#define MAX_SCRIPT_GROUP_CLIENT 8 + +class CLogicScriptClient : public CBaseEntity +{ +public: + DECLARE_CLASS( CLogicScriptClient, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_NETWORKCLASS(); + +#ifdef CLIENT_DLL + void OnDataChanged( DataUpdateType_t type ) + { + BaseClass::OnDataChanged( type ); + + if ( !m_ScriptScope.IsInitialized() ) + { + RunVScripts(); + } + } +#else + int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); } +#endif + + bool KeyValue( const char *szKeyName, const char *szValue ) + { + if ( FStrEq( szKeyName, "vscripts" ) ) + { + Q_strcpy( m_iszClientScripts.GetForModify(), szValue ); + } + + return BaseClass::KeyValue( szKeyName, szValue ); + } + + void RunVScripts() + { +#ifdef CLIENT_DLL + if (m_iszClientScripts == NULL_STRING) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s has no client scripts", GetDebugName() ); + return; + } + + if (g_pScriptVM == NULL) + { + return; + } + + 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, m_iszClientScripts.Get() ); + CUtlStringList szScripts; + + V_SplitString( szScriptsList, " ", szScripts ); + + for (int i = 0; i < szScripts.Count(); i++) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i] ); + + 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_bClientThink) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } +#else + // Avoids issues from having m_iszVScripts set without actually having a script scope + ValidateScriptScope(); + + if (m_bRunOnServer) + { + BaseClass::RunVScripts(); + } +#endif + } + +#ifdef CLIENT_DLL + void ClientThink() + { + ScriptVariant_t varThinkRetVal; + if (CallScriptFunction("ClientThink", &varThinkRetVal)) + { + float flThinkFrequency = 0.0f; + if (!varThinkRetVal.AssignTo(&flThinkFrequency)) + { + // use default think interval if script think function doesn't provide one + flThinkFrequency = cl_script_think_interval.GetFloat(); + } + + if (flThinkFrequency == CLIENT_THINK_ALWAYS) + SetNextClientThink( CLIENT_THINK_ALWAYS ); + else + SetNextClientThink( gpGlobals->curtime + flThinkFrequency ); + } + else + { + DevWarning("%s FAILED to call client script think function!\n", GetDebugName()); + } + + BaseClass::ClientThink(); + } + + void OnSave() + { + // HACKHACK: Save the next think in the VM since the VM is saved + if (m_bClientThink) + { + g_pScriptVM->SetValue( m_ScriptScope, "__c_think", GetNextThink() ); + } + + BaseClass::OnSave(); + } + + void OnRestore() + { + // HACKHACK: See OnSave() + if (m_bClientThink) + { + ScriptVariant_t flNextThink; + if (g_pScriptVM->GetValue( m_ScriptScope, "__c_think", &flNextThink )) + { + SetNextClientThink( flNextThink ); + } + } + + BaseClass::OnRestore(); + } + + void ReceiveMessage( int classID, bf_read &msg ) + { + if ( classID != GetClientClass()->m_ClassID ) + { + BaseClass::ReceiveMessage( classID, msg ); + return; + } + + char szFunction[64]; + msg.ReadString( szFunction, sizeof( szFunction ) ); + + if ( m_ScriptScope.IsInitialized() ) + { + CallScriptFunction( szFunction, NULL ); + } + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s script scope not initialized!\n", GetDebugName() ); + } + } +#endif + +#ifdef GAME_DLL + void InputCallScriptFunctionClient( inputdata_t &inputdata ) + { + const char *pszFunction = inputdata.value.String(); + if ( V_strlen( pszFunction ) >= 64 ) + { + Msg( "%s CallScriptFunctionClient: \"%s\" is too long at %i characters, must be 64 or less\n", GetDebugName(), pszFunction, V_strlen(pszFunction)+1 ); + return; + } + + EntityMessageBegin( this, true ); + WRITE_STRING( pszFunction ); + MessageEnd(); + } +#endif + + //CNetworkArray( string_t, m_iszGroupMembers, MAX_SCRIPT_GROUP_CLIENT ); + CNetworkString( m_iszClientScripts, 128 ); + CNetworkVar( bool, m_bClientThink ); + +#ifndef CLIENT_DLL + bool m_bRunOnServer; +#endif +}; + +LINK_ENTITY_TO_CLASS( logic_script_client, CLogicScriptClient ); + +BEGIN_DATADESC( CLogicScriptClient ) + + // TODO: Does this need to be saved? + //DEFINE_AUTO_ARRAY( m_iszClientScripts, FIELD_CHARACTER ), + + //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_bClientThink, FIELD_BOOLEAN, "ClientThink" ), + +#ifndef CLIENT_DLL + DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "CallScriptFunctionClient", InputCallScriptFunctionClient ), +#endif + +END_DATADESC() + +IMPLEMENT_NETWORKCLASS_DT( CLogicScriptClient, DT_LogicScriptClient ) + +#ifdef CLIENT_DLL + //RecvPropArray( RecvPropString( RECVINFO( m_iszGroupMembers[0] ) ), m_iszGroupMembers ), + RecvPropString( RECVINFO( m_iszClientScripts ) ), + RecvPropBool( RECVINFO( m_bClientThink ) ), +#else + //SendPropArray( SendPropStringT( SENDINFO_ARRAY( m_iszGroupMembers ) ), m_iszGroupMembers ), + SendPropString( SENDINFO( m_iszClientScripts ) ), + SendPropBool( SENDINFO( m_bClientThink ) ), +#endif + +END_NETWORK_TABLE() diff --git a/mp/src/game/shared/mapbase/mapbase_shared.cpp b/mp/src/game/shared/mapbase/mapbase_shared.cpp index 2763b457..60460b8b 100644 --- a/mp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/mp/src/game/shared/mapbase/mapbase_shared.cpp @@ -9,6 +9,7 @@ #include "cbase.h" #include "tier0/icommandline.h" +#include "tier1/mapbase_con_groups.h" #include "igamesystem.h" #include "filesystem.h" #include @@ -16,10 +17,13 @@ #include "saverestore_utlvector.h" #include "props_shared.h" #include "utlbuffer.h" +#include "usermessages.h" #ifdef CLIENT_DLL #include "hud_closecaption.h" #include "panelmetaclassmgr.h" #include "c_soundscape.h" +#include "hud_macros.h" +#include "clientmode_shared.h" #else #include "soundscape_system.h" #include "AI_ResponseSystem.h" @@ -44,32 +48,11 @@ 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." ); +// This constant should change with each Mapbase update +ConVar mapbase_version( "mapbase_version", MAPBASE_VERSION, FCVAR_NONE, "The version of Mapbase currently being used in this mod's server.dll" ); + +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."); extern void MapbaseGameLog_Init(); @@ -80,28 +63,56 @@ extern void ReloadResponseSystem(); // Reloads the response system when the map changes to avoid custom talker leaking static bool g_bMapContainsCustomTalker; +#else +// This constant should change with each Mapbase update +ConVar mapbase_version_client( "mapbase_version_client", MAPBASE_VERSION, FCVAR_NONE, "The version of Mapbase currently being used in this mod's client.dll" ); + +// This is from the vgui_controls library +extern vgui::HScheme g_iCustomClientSchemeOverride; + +bool g_bUsingCustomHudAnimations = false; +bool g_bUsingCustomHudLayout = false; #endif +extern void AddSurfacepropFile( const char *pFileName, IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem ); + // 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]; +#ifdef GAME_DLL +// Default player configuration +char g_szDefaultPlayerModel[MAX_PATH]; +bool g_bDefaultPlayerDrawExternally; + +char g_szDefaultHandsModel[MAX_PATH]; +int g_iDefaultHandsSkin; +int g_iDefaultHandsBody; +#endif + enum { MANIFEST_SOUNDSCRIPTS, //MANIFEST_PROPDATA, //MANIFEST_SOUNDSCAPES, MANIFEST_LOCALIZATION, + MANIFEST_SURFACEPROPS, #ifdef CLIENT_DLL - //MANIFEST_CLOSECAPTION, + MANIFEST_CLOSECAPTION, MANIFEST_VGUI, + MANIFEST_CLIENTSCHEME, + MANIFEST_HUDANIMATIONS, + MANIFEST_HUDLAYOUT, #else MANIFEST_TALKER, - MANIFEST_SENTENCES, + //MANIFEST_SENTENCES, MANIFEST_ACTBUSY, #endif +#ifdef MAPBASE_VSCRIPT + MANIFEST_VSCRIPT, +#endif // Must always be kept below MANIFEST_NUM_TYPES, @@ -109,24 +120,38 @@ enum struct ManifestType_t { + ManifestType_t( const char *_string, const char *cvarname, const char *cvardesc ) : cvar( cvarname, "1", FCVAR_ARCHIVE, cvardesc ) + { + string = _string; + } + //int type; const char *string; - ConVar *cvar; + ConVar cvar; }; +#define DECLARE_MANIFEST_TYPE(name, cvar, desc) { #name, ConVar(#cvar, "1", FCVAR_ARCHIVE, #desc) } + // 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 }, + { "soundscripts", "mapbase_load_soundscripts", "Should we load map-specific soundscripts? e.g. \"maps/_level_sounds.txt\"" }, + //{ "propdata", "mapbase_load_propdata", "Should we load map-specific soundscripts? e.g. \"maps/_level_sounds.txt\"" }, + //{ "soundscapes", "mapbase_load_soundscapes", "Should we load map-specific soundscapes? e.g. \"maps/_soundscapes.txt\"" }, + { "localization", "mapbase_load_localization", "Should we load map-specific localized text files? e.g. \"maps/_english.txt\"" }, + { "surfaceprops", "mapbase_load_surfaceprops", "Should we load map-specific surfaceproperties files? e.g. \"maps/_surfaceproperties.txt\"" }, #ifdef CLIENT_DLL - //{ "closecaption", &mapbase_load_cc }, - { "vgui", NULL }, + { "closecaption", "mapbase_load_closecaption", "Should we load map-specific closed captioning? e.g. \"maps/_closecaption_english.txt\" and \"maps/_closecaption_english.dat\"" }, + { "vgui", "mapbase_load_vgui", "Should we load map-specific VGUI screens? e.g. \"maps/_screens.txt\"" }, + { "clientscheme", "mapbase_load_clientscheme", "Should we load map-specific ClientScheme.res overrides? e.g. \"maps/_clientscheme.res\"" }, + { "hudanimations", "mapbase_load_hudanimations", "Should we load map-specific HUD animation overrides? e.g. \"maps/_hudanimations.txt\"" }, + { "hudlayout", "mapbase_load_hudlayout", "Should we load map-specific HUD layout overrides? e.g. \"maps/_hudlayout.res\"" }, #else - { "talker", &mapbase_load_talker }, - { "sentences", &mapbase_load_sentences }, - { "actbusy", &mapbase_load_actbusy }, + { "talker", "mapbase_load_talker", "Should we load map-specific talker files? e.g. \"maps/_talker.txt\"" }, + //{ "sentences", "mapbase_load_sentences", "Should we load map-specific sentences? e.g. \"maps/_sentences.txt\"" }, + { "actbusy", "mapbase_load_actbusy", "Should we load map-specific actbusy files? e.g. \"maps/_actbusy.txt\"" }, +#endif +#ifdef MAPBASE_VSCRIPT + { "vscript", "mapbase_load_vscript", "Should we load map-specific VScript map spawn files? e.g. \"maps/_mapspawn.nut\"" }, #endif }; @@ -149,6 +174,8 @@ public: virtual bool Init() { + InitConsoleGroups( g_pFullFileSystem ); + // Checks gameinfo.txt for additional command line options KeyValues *gameinfo = new KeyValues("GameInfo"); if (GetGameInfoKeyValues(gameinfo)) @@ -176,7 +203,7 @@ public: #ifdef GAME_DLL if (g_bMapContainsCustomTalker && mapbase_flush_talker.GetBool()) { - CGMsg( 1, "Mapbase Misc.", "Mapbase: Reloading response system to flush custom talker\n" ); + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Mapbase: Reloading response system to flush custom talker\n" ); ReloadResponseSystem(); g_bMapContainsCustomTalker = false; } @@ -186,7 +213,7 @@ public: virtual void LevelInitPreEntity() { #ifdef GAME_DLL - CGMsg( 0, "Mapbase Misc.", "Mapbase system loaded\n" ); + CGMsg( 0, CON_GROUP_MAPBASE_MISC, "Mapbase system loaded\n" ); #endif // Checks gameinfo.txt for Mapbase-specific options @@ -205,13 +232,25 @@ public: Q_strncpy(g_iszGameName, pszGameName, sizeof(g_iszGameName)); } + +#ifdef GAME_DLL + Q_strncpy( g_szDefaultPlayerModel, gameinfo->GetString( "player_default_model", "models/player.mdl" ), sizeof( g_szDefaultPlayerModel ) ); + g_bDefaultPlayerDrawExternally = gameinfo->GetBool( "player_default_draw_externally", false ); + + Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", "models/weapons/v_hands.mdl" ), sizeof( g_szDefaultHandsModel ) ); + g_iDefaultHandsSkin = gameinfo->GetInt( "player_default_hands_skin", 0 ); + g_iDefaultHandsBody = gameinfo->GetInt( "player_default_hands_body", 0 ); +#endif } gameinfo->deleteThis(); RefreshMapName(); - // Shared Mapbase localization file + // Shared Mapbase scripts to avoid overwriting mod files g_pVGuiLocalize->AddFile( "resource/mapbase_%language%.txt" ); +#ifdef CLIENT_DLL + PanelMetaClassMgr()->LoadMetaClassDefinitionFile( "scripts/vgui_screens_mapbase.txt" ); +#endif } virtual void OnRestore() @@ -250,6 +289,46 @@ public: g_MapName = NULL; RefreshCustomTalker(); + +#ifdef CLIENT_DLL + CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); + FOR_EACH_VEC( m_CloseCaptionFileNames, i ) + { + hudCloseCaption->RemoveCaptionDictionary( m_CloseCaptionFileNames[i] ); + } + m_CloseCaptionFileNames.RemoveAll(); + + if (g_iCustomClientSchemeOverride != 0 || g_bUsingCustomHudAnimations || g_bUsingCustomHudLayout) + { + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Mapbase: Reloading client mode and viewport scheme\n" ); + + // TODO: We currently have no way of actually cleaning up custom schemes upon level unload. + // That may or may not be sustainable if there's a ton of custom schemes loaded at once + g_iCustomClientSchemeOverride = 0; + + g_bUsingCustomHudAnimations = false; + g_bUsingCustomHudLayout = false; + + // Reload scheme + ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); + if ( mode ) + { + mode->ReloadScheme( true ); + + // We need to reload default values, so load a special "hudlayout_mapbase.res" file that only contains + // default Mapbase definitions identical to the defaults in the code + CBaseViewport *pViewport = dynamic_cast(g_pClientMode->GetViewport()); + if (pViewport) + { + KeyValuesAD pConditions( "conditions" ); + g_pClientMode->ComputeVguiResConditions( pConditions ); + + // reload the .res file from disk + pViewport->LoadControlSettings( "scripts/hudlayout_mapbase.res", NULL, NULL, pConditions ); + } + } + } +#endif } bool RefreshMapName() @@ -272,9 +351,6 @@ public: } #ifdef CLIENT_DLL - bool m_bInitializedRTs = false; - CUtlVector m_CameraTextures; - //----------------------------------------------------------------------------- // Initialize custom RT textures if necessary //----------------------------------------------------------------------------- @@ -335,7 +411,7 @@ public: KeyValues *pKV = new KeyValues("DefaultManifest"); pKV->LoadFromFile(filesystem, GENERIC_MANIFEST_FILE); - AddManifestFile(pKV/*, true*/); + AddManifestFile(pKV, g_MapName/*, true*/); pKV->deleteThis(); } @@ -350,18 +426,18 @@ public: return; } - CGMsg( 1, "Mapbase Misc.", "===== Mapbase Manifest: Loading manifest file %s =====\n", file ); + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "===== Mapbase Manifest: Loading manifest file %s =====\n", file ); - AddManifestFile(pKV, false); + AddManifestFile(pKV, g_MapName, false); - CGMsg( 1, "Mapbase Misc.", "==============================================================================\n" ); + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "==============================================================================\n" ); pKV->deleteThis(); } void LoadFromValue( const char *value, int type, bool bDontWarn ) { - if (!filesystem->FileExists(value, "MOD")) + if (type != MANIFEST_VSCRIPT && !filesystem->FileExists(value, "MOD")) { if (!bDontWarn) { @@ -375,9 +451,13 @@ public: 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; + case MANIFEST_SURFACEPROPS: { AddSurfacepropFile( value, physprops, filesystem ); } break; #ifdef CLIENT_DLL - //case MANIFEST_CLOSECAPTION: { todo } break; - case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break; + case MANIFEST_CLOSECAPTION: { ManifestLoadCustomCloseCaption( value ); } break; + case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break; + case MANIFEST_CLIENTSCHEME: { ManifestLoadCustomScheme( value ); } break; + case MANIFEST_HUDANIMATIONS: { ManifestLoadCustomHudAnimations( value ); } break; + case MANIFEST_HUDLAYOUT: { ManifestLoadCustomHudLayout( value ); } break; //case MANIFEST_SOUNDSCAPES: { Soundscape_AddFile(value); } break; #else case MANIFEST_TALKER: { @@ -385,14 +465,17 @@ public: LoadResponseSystemFile(value); //PrecacheCustomResponseSystem( value ); } break; //case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break; - case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break; + //case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break; case MANIFEST_ACTBUSY: { ParseCustomActbusyFile(value); } break; +#endif +#ifdef MAPBASE_VSCRIPT + case MANIFEST_VSCRIPT: { VScriptRunScript(value, false); } break; #endif } } // This doesn't call deleteThis()! - void AddManifestFile(KeyValues *pKV, bool bDontWarn = false) + void AddManifestFile(KeyValues *pKV, const char *pszMapName, bool bDontWarn = false) { char value[MAX_PATH]; const char *name; @@ -411,7 +494,7 @@ public: { if (FStrEq( outStrings[i], "mapname" )) { - Q_strncat( value, g_MapName, sizeof( value ) ); + Q_strncat( value, pszMapName, sizeof( value ) ); } else if (FStrEq( outStrings[i], "language" )) { @@ -444,7 +527,7 @@ public: { if (FStrEq(name, gm_szManifestFileStrings[i].string)) { - if (!gm_szManifestFileStrings[i].cvar || gm_szManifestFileStrings[i].cvar->GetBool()) + if (gm_szManifestFileStrings[i].cvar.GetBool()) { LoadFromValue(value, i, bDontWarn); } @@ -454,23 +537,101 @@ public: } } -#ifdef MAPBASE_VSCRIPT - void ScriptAddManifestFile( const char *szScript ) { AddManifestFile( szScript ); } +private: - 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 ); } +#ifdef CLIENT_DLL + void ManifestLoadCustomCloseCaption( const char *pszFile ) + { + if (GET_HUDELEMENT( CHudCloseCaption )) + (GET_HUDELEMENT( CHudCloseCaption ))->AddCustomCaptionFile( pszFile, m_CloseCaptionFileNames ); + } + + // Custom scheme loading + void ManifestLoadCustomScheme( const char *pszFile ) + { + g_iCustomClientSchemeOverride = vgui::scheme()->LoadSchemeFromFile( pszFile, "CustomClientScheme" ); + + // Reload scheme + ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); + if ( mode ) + { + mode->ReloadScheme( true ); + } + } + + void ManifestLoadCustomHudAnimations( const char *pszFile ) + { + CBaseViewport *pViewport = dynamic_cast(g_pClientMode->GetViewport()); + if (pViewport) + { + g_bUsingCustomHudAnimations = true; + if (!pViewport->LoadCustomHudAnimations( pszFile )) + { + g_bUsingCustomHudAnimations = false; + CGWarning( 0, CON_GROUP_MAPBASE_MISC, "Custom HUD animations file \"%s\" failed to load\n", pszFile ); + pViewport->ReloadHudAnimations(); + } + else + { + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Loaded custom HUD animations file \"%s\"\n", pszFile );; + } + } + } + + void ManifestLoadCustomHudLayout( const char *pszFile ) + { + CBaseViewport *pViewport = dynamic_cast(g_pClientMode->GetViewport()); + if (pViewport) + { + g_bUsingCustomHudLayout = true; + + KeyValuesAD pConditions( "conditions" ); + g_pClientMode->ComputeVguiResConditions( pConditions ); + + // reload the .res file from disk + pViewport->LoadControlSettings( pszFile, NULL, NULL, pConditions ); + + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Loaded custom HUD layout file \"%s\"\n", pszFile );; + } + } +#endif + +public: + + void LoadCustomSoundscriptFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_SOUNDSCRIPTS, false ); } + void LoadCustomLocalizationFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_LOCALIZATION, false ); } + void LoadCustomSurfacePropsFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_SURFACEPROPS, false ); } +#ifdef CLIENT_DLL + void LoadCustomCloseCaptionFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_CLOSECAPTION, false ); } + void LoadCustomVGUIFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_VGUI, false ); } + void LoadCustomClientSchemeFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_CLIENTSCHEME, false ); } + void LoadCustomHUDAnimationsFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_HUDANIMATIONS, false ); } + void LoadCustomHUDLayoutFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_HUDLAYOUT, false ); } +#else + void LoadCustomTalkerFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_TALKER, false ); } + void LoadCustomActbusyFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_ACTBUSY, false ); } #endif const char *GetModName() { return g_iszGameName; } bool IsCoreMapbase() { return g_bMapbaseCore; } +#ifdef MAPBASE_VSCRIPT + void ScriptAddManifestFile( const char *szScript ) { AddManifestFile( szScript ); } + virtual void RegisterVScript() { g_pScriptVM->RegisterInstance( this, "Mapbase" ); } #endif + +private: + +#ifdef CLIENT_DLL + bool m_bInitializedRTs = false; + CUtlVector m_CameraTextures; + + CUtlVector m_CloseCaptionFileNames; +#endif }; CMapbaseSystem g_MapbaseSystem; @@ -483,14 +644,32 @@ 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." ) + DEFINE_SCRIPTFUNC( LoadCustomSoundscriptFile, "Loads a custom soundscript file." ) + DEFINE_SCRIPTFUNC( LoadCustomLocalizationFile, "Loads a custom localization file." ) + DEFINE_SCRIPTFUNC( LoadCustomSurfacePropsFile, "Loads a custom surface properties file." ) +#ifdef CLIENT_DLL + DEFINE_SCRIPTFUNC( LoadCustomCloseCaptionFile, "Loads a custom closed captions file." ) + DEFINE_SCRIPTFUNC( LoadCustomVGUIFile, "Loads a custom VGUI definitions file." ) + DEFINE_SCRIPTFUNC( LoadCustomClientSchemeFile, "Loads a custom ClientScheme.res override file." ) + DEFINE_SCRIPTFUNC( LoadCustomHUDAnimationsFile, "Loads a custom HUD animations override file." ) + DEFINE_SCRIPTFUNC( LoadCustomHUDLayoutFile, "Loads a custom HUD layout override file." ) +#else + DEFINE_SCRIPTFUNC( LoadCustomTalkerFile, "Loads a custom talker file." ) + DEFINE_SCRIPTFUNC( LoadCustomActbusyFile, "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." ) + + // Legacy + DEFINE_SCRIPTFUNC_NAMED( LoadCustomSoundscriptFile, "LoadSoundscriptFile", SCRIPT_HIDE ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC_NAMED( LoadCustomTalkerFile, "LoadTalkerFile", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( LoadCustomActbusyFile, "LoadActbusyFile", SCRIPT_HIDE ) +#endif + END_SCRIPTDESC(); #endif @@ -551,6 +730,11 @@ CUtlVector *Mapbase_GetChapterList() return &g_MapbaseChapterList; } +int Mapbase_GetChapterCount() +{ + return g_MapbaseChapterList.Count(); +} + ThreeState_t Flashlight_GetLegacyVersionKey() { KeyValues *gameinfo = new KeyValues( "GameInfo" ); @@ -589,7 +773,7 @@ public: const char *scriptfile = STRING(m_target); if ( filesystem->FileExists( scriptfile, "MOD" ) ) { - CGMsg(0, "Mapbase Misc.", "Mapbase: Adding manifest file \"%s\"\n", scriptfile); + CGMsg(0, CON_GROUP_MAPBASE_MISC, "Mapbase: Adding manifest file \"%s\"\n", scriptfile); g_MapbaseSystem.AddManifestFile(scriptfile); } else @@ -616,3 +800,38 @@ BEGIN_DATADESC( CMapbaseManifestEntity ) END_DATADESC() #endif + +//----------------------------------------------------------------------------- + +void CV_IncludeNameChanged( IConVar *pConVar, const char *pOldString, float flOldValue ); + +#ifdef CLIENT_DLL +ConVar con_group_include_name_client( "con_group_include_name_client", "0", FCVAR_NONE, "Includes groups when printing on the client.", CV_IncludeNameChanged ); + +void CV_IncludeNameChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + SetConsoleGroupIncludeNames( con_group_include_name_client.GetBool() ); +} +#else +ConVar con_group_include_name( "con_group_include_name", "0", FCVAR_NONE, "Includes groups when printing.", CV_IncludeNameChanged ); + +void CV_IncludeNameChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + SetConsoleGroupIncludeNames( con_group_include_name.GetBool() ); +} +#endif + +CON_COMMAND_SHARED( con_group_reload, "Reloads all console groups." ) +{ + InitConsoleGroups( g_pFullFileSystem ); +} + +CON_COMMAND_SHARED( con_group_list, "Prints a list of all console groups." ) +{ + PrintAllConsoleGroups(); +} + +CON_COMMAND_SHARED( con_group_toggle, "Toggles a console group." ) +{ + ToggleConsoleGroups( args.Arg( 1 ) ); +} diff --git a/mp/src/game/shared/mapbase/mapbase_usermessages.cpp b/mp/src/game/shared/mapbase/mapbase_usermessages.cpp new file mode 100644 index 00000000..5af946e6 --- /dev/null +++ b/mp/src/game/shared/mapbase/mapbase_usermessages.cpp @@ -0,0 +1,35 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase-specific user messages. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "usermessages.h" +#ifdef CLIENT_DLL +#include "hud_macros.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef CLIENT_DLL +void HookMapbaseUserMessages( void ) +{ + // VScript + //HOOK_MESSAGE( ScriptMsg ); // Hooked in CNetMsgScriptHelper +} +#endif + +void RegisterMapbaseUserMessages( void ) +{ + // VScript + usermessages->Register( "ScriptMsg", -1 ); // CNetMsgScriptHelper + +#ifdef CLIENT_DLL + // TODO: Better placement? + HookMapbaseUserMessages(); +#endif +} diff --git a/mp/src/game/shared/mapbase/matchers.cpp b/mp/src/game/shared/mapbase/matchers.cpp index f1f1d26b..b8b915f4 100644 --- a/mp/src/game/shared/mapbase/matchers.cpp +++ b/mp/src/game/shared/mapbase/matchers.cpp @@ -10,16 +10,6 @@ #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); } @@ -47,181 +37,6 @@ bool Matcher_Match(const char *pszQuery, const char *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) diff --git a/mp/src/game/shared/mapbase/matchers.h b/mp/src/game/shared/mapbase/matchers.h index 52e8cc1a..7d86ad63 100644 --- a/mp/src/game/shared/mapbase/matchers.h +++ b/mp/src/game/shared/mapbase/matchers.h @@ -11,8 +11,7 @@ #pragma once #endif - -#define MAPBASE_MATCHERS 1 +#include "tier1/mapbase_matchers_base.h" // Compares with != and the like. Basically hijacks the response system matching. // This also loops back around to Matcher_NamesMatch. @@ -22,47 +21,7 @@ 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; -} +//static inline bool Matcher_Compare( const char *pszQuery, const char *szValue ) { return Matcher_Match( pszQuery, szValue ); } #endif diff --git a/mp/src/game/shared/mapbase/singleplayer_animstate.cpp b/mp/src/game/shared/mapbase/singleplayer_animstate.cpp new file mode 100644 index 00000000..7c8b92c9 --- /dev/null +++ b/mp/src/game/shared/mapbase/singleplayer_animstate.cpp @@ -0,0 +1,669 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Single Player animation state 'handler'. This utility is used +// to evaluate the pose parameter value based on the direction +// and speed of the player. +// +// ------------------------------------------------------------------------------ +// +// This was originally based on the following VDC article: +// https://developer.valvesoftware.com/wiki/Fixing_the_player_animation_state_(Single_Player) +// +// It has been modified by Blixibon to derive from CBasePlayerAnimState instead and support 9-way blends. +// Much of the work done to make this derive from CBasePlayerAnimState utilized code from the Alien Swarm SDK. +// +//=============================================================================// + +#include "cbase.h" +#include "singleplayer_animstate.h" +#include "tier0/vprof.h" +#include "animation.h" +#include "studio.h" +#include "apparent_velocity_helper.h" +#include "utldict.h" +#include "filesystem.h" +#include "in_buttons.h" +#include "datacache/imdlcache.h" + +extern ConVar mp_facefronttime, mp_feetyawrate, mp_ik; + +ConVar sv_playeranimstate_animtype( "sv_playeranimstate_animtype", "0", FCVAR_NONE, "The leg animation type used by the singleplayer animation state. 9way = 0, 8way = 1, GoldSrc = 2" ); +ConVar sv_playeranimstate_bodyyaw( "sv_playeranimstate_bodyyaw", "45.0", FCVAR_NONE, "The maximum body yaw used by the singleplayer animation state." ); +ConVar sv_playeranimstate_use_aim_sequences( "sv_playeranimstate_use_aim_sequences", "1", FCVAR_NONE, "Allows the singleplayer animation state to use aim sequences." ); + +#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f + +#define FIRESEQUENCE_LAYER (AIMSEQUENCE_LAYER+NUM_AIMSEQUENCE_LAYERS) +#define RELOADSEQUENCE_LAYER (FIRESEQUENCE_LAYER + 1) +#define NUM_LAYERS_WANTED (RELOADSEQUENCE_LAYER + 1) + +CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer ) +{ + MDLCACHE_CRITICAL_SECTION(); + + CSinglePlayerAnimState *pState = new CSinglePlayerAnimState( pPlayer ); + + // Setup the movement data. + CModAnimConfig movementData; + movementData.m_LegAnimType = (LegAnimType_t)sv_playeranimstate_animtype.GetInt(); + movementData.m_flMaxBodyYawDegrees = sv_playeranimstate_bodyyaw.GetFloat(); + movementData.m_bUseAimSequences = sv_playeranimstate_use_aim_sequences.GetBool(); + + pState->Init( pPlayer, movementData ); + + return pState; +} + +// Below this many degrees, slow down turning rate linearly +#define FADE_TURN_DEGREES 45.0f +// After this, need to start turning feet +#define MAX_TORSO_ANGLE 90.0f +// Below this amount, don't play a turning animation/perform IK +#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f + +//static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." ); +extern ConVar sv_backspeed; +extern ConVar mp_feetyawrate; +extern ConVar mp_facefronttime; +extern ConVar mp_ik; + +CSinglePlayerAnimState::CSinglePlayerAnimState( CBasePlayer *pPlayer ): m_pPlayer( pPlayer ) +{ +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CSinglePlayerAnimState::CalcMainActivity() +{ +#ifdef CLIENT_DLL + return ACT_IDLE; +#else + float speed = GetOuter()->GetAbsVelocity().Length2D(); + + if ( HandleJumping() ) + { + return ACT_HL2MP_JUMP; + } + else + { + Activity idealActivity = ACT_HL2MP_IDLE; + + if ( GetOuter()->GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) ) + { + speed = 0; + } + else + { + if ( GetOuter()->GetFlags() & FL_DUCKING ) + { + if ( speed > 0 ) + { + idealActivity = ACT_HL2MP_WALK_CROUCH; + } + else + { + idealActivity = ACT_HL2MP_IDLE_CROUCH; + } + } + else + { + if ( speed > 0 ) + { +#if EXPANDED_HL2DM_ACTIVITIES + if ( m_pPlayer->GetButtons() & IN_WALK ) + { + idealActivity = ACT_HL2MP_WALK; + } + else +#endif + { + idealActivity = ACT_HL2MP_RUN; + } + } + else + { + idealActivity = ACT_HL2MP_IDLE; + } + } + } + + return idealActivity; + } + + //return m_pPlayer->GetActivity(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim ) +{ + if ( playerAnim == PLAYER_ATTACK1 ) + { + m_iFireSequence = SelectWeightedSequence( TranslateActivity( ACT_HL2MP_GESTURE_RANGE_ATTACK ) ); + m_bFiring = m_iFireSequence != -1; + m_flFireCycle = 0; + } + else if ( playerAnim == PLAYER_ATTACK2 ) + { +#if EXPANDED_HL2DM_ACTIVITIES + m_iFireSequence = SelectWeightedSequence( TranslateActivity( ACT_HL2MP_GESTURE_RANGE_ATTACK2 ) ); +#else + m_iFireSequence = SelectWeightedSequence( TranslateActivity( ACT_HL2MP_GESTURE_RANGE_ATTACK ) ); +#endif + m_bFiring = m_iFireSequence != -1; + m_flFireCycle = 0; + } + else if ( playerAnim == PLAYER_JUMP ) + { + // Play the jump animation. + if (!m_bJumping) + { + m_bJumping = true; + m_bFirstJumpFrame = true; + m_flJumpStartTime = gpGlobals->curtime; + } + } + else if ( playerAnim == PLAYER_RELOAD ) + { + m_iReloadSequence = SelectWeightedSequence( TranslateActivity( ACT_HL2MP_GESTURE_RELOAD ) ); + if (m_iReloadSequence != -1) + { + // clear other events that might be playing in our layer + m_bWeaponSwitching = false; + m_fReloadPlaybackRate = 1.0f; + m_bReloading = true; + m_flReloadCycle = 0; + } + } + else if ( playerAnim == PLAYER_UNHOLSTER || playerAnim == PLAYER_HOLSTER ) + { + m_iWeaponSwitchSequence = SelectWeightedSequence( TranslateActivity( playerAnim == PLAYER_UNHOLSTER ? ACT_ARM : ACT_DISARM ) ); + if (m_iWeaponSwitchSequence != -1) + { + // clear other events that might be playing in our layer + m_bPlayingMisc = false; + m_bReloading = false; + + m_bWeaponSwitching = true; + m_flWeaponSwitchCycle = 0; + m_flMiscBlendOut = 0.1f; + m_flMiscBlendIn = 0.1f; + m_bMiscNoOverride = false; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CSinglePlayerAnimState::TranslateActivity( Activity actDesired ) +{ +#ifdef CLIENT_DLL + return actDesired; +#else + return m_pPlayer->Weapon_TranslateActivity( actDesired ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CSinglePlayerAnimState::HandleJumping() +{ + if ( m_bJumping ) + { + if ( m_bFirstJumpFrame ) + { + m_bFirstJumpFrame = false; + RestartMainSequence(); // Reset the animation. + } + + // Don't check if he's on the ground for a sec.. sometimes the client still has the + // on-ground flag set right when the message comes in. + if (m_flJumpStartTime > gpGlobals->curtime) + m_flJumpStartTime = gpGlobals->curtime; + if ( gpGlobals->curtime - m_flJumpStartTime > 0.2f) + { + if ( m_pOuter->GetFlags() & FL_ONGROUND || GetOuter()->GetGroundEntity() != NULL) + { + m_bJumping = false; + RestartMainSequence(); // Reset the animation. + } + } + } + + // Are we still jumping? If so, keep playing the jump animation. + return m_bJumping; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr ) +{ + CBasePlayerAnimState::ComputeSequences(pStudioHdr); + + ComputeFireSequence(); + ComputeMiscSequence(); + ComputeReloadSequence(); + ComputeWeaponSwitchSequence(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::AddMiscSequence( int iSequence, float flBlendIn, float flBlendOut, float flPlaybackRate, bool bHoldAtEnd, bool bOnlyWhenStill ) +{ + Assert( iSequence != -1 ); + + m_iMiscSequence = iSequence; + m_flMiscBlendIn = flBlendIn; + m_flMiscBlendOut = flBlendOut; + + m_bPlayingMisc = true; + m_bMiscHoldAtEnd = bHoldAtEnd; + m_bReloading = false; + m_flMiscCycle = 0; + m_bMiscOnlyWhenStill = bOnlyWhenStill; + m_bMiscNoOverride = true; + m_fMiscPlaybackRate = flPlaybackRate; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::ClearAnimationState() +{ + m_bJumping = false; + m_bFiring = false; + m_bReloading = false; + m_bWeaponSwitching = false; + m_bPlayingMisc = false; + m_flReloadBlendIn = 0.0f; + m_flReloadBlendOut = 0.0f; + m_flMiscBlendIn = 0.0f; + m_flMiscBlendOut = 0.0f; + CBasePlayerAnimState::ClearAnimationState(); +} + +void CSinglePlayerAnimState::ClearAnimationLayers() +{ + VPROF( "CBasePlayerAnimState::ClearAnimationLayers" ); + if ( !m_pOuter ) + return; + + m_pOuter->SetNumAnimOverlays( NUM_LAYERS_WANTED ); + for ( int i=0; i < m_pOuter->GetNumAnimOverlays(); i++ ) + { + m_pOuter->GetAnimOverlay( i )->SetOrder( CBaseAnimatingOverlay::MAX_OVERLAYS ); +#ifndef CLIENT_DLL + m_pOuter->GetAnimOverlay( i )->m_fFlags = 0; +#endif + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CSinglePlayerAnimState::CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle ) +{ + // TODO? + return m_pOuter->LookupSequence( "soldier_Aim_9_directions" ); +} + +void CSinglePlayerAnimState::UpdateLayerSequenceGeneric( int iLayer, bool &bEnabled, + float &flCurCycle, int &iSequence, bool bWaitAtEnd, + float fBlendIn, float fBlendOut, bool bMoveBlend, float fPlaybackRate, bool bUpdateCycle /* = true */ ) +{ + if ( !bEnabled ) + return; + + CStudioHdr *hdr = GetOuter()->GetModelPtr(); + if ( !hdr ) + return; + + if ( iSequence < 0 || iSequence >= hdr->GetNumSeq() ) + return; + + // Increment the fire sequence's cycle. + if ( bUpdateCycle ) + { + flCurCycle += m_pOuter->GetSequenceCycleRate( hdr, iSequence ) * gpGlobals->frametime * fPlaybackRate; + } + + // temp: if the sequence is looping, don't override it - we need better handling of looping anims, + // especially in misc layer from melee (right now the same melee attack is looped manually in asw_melee_system.cpp) + bool bLooping = m_pOuter->IsSequenceLooping( hdr, iSequence ); + + if ( flCurCycle > 1 && !bLooping ) + { + if ( iLayer == RELOADSEQUENCE_LAYER ) + { + m_bReloading = false; + } + if ( bWaitAtEnd ) + { + flCurCycle = 1; + } + else + { + // Not firing anymore. + bEnabled = false; + iSequence = 0; + return; + } + } + + // if this animation should blend out as we move, then check for dropping it completely since we're moving too fast + float speed = 0; + if (bMoveBlend) + { + Vector vel; + GetOuterAbsVelocity( vel ); + + float speed = vel.Length2D(); + + if (speed > 50) + { + bEnabled = false; + iSequence = 0; + return; + } + } + + // Now dump the state into its animation layer. + CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iLayer ); + + pLayer->m_flCycle = flCurCycle; + pLayer->m_nSequence = iSequence; + + pLayer->m_flPlaybackRate = fPlaybackRate; + pLayer->m_flWeight = 1.0f; + + if (iLayer == RELOADSEQUENCE_LAYER) + { + // blend this layer in and out for smooth reloading + if (flCurCycle < fBlendIn && fBlendIn>0) + { + pLayer->m_flWeight = ( clamp(flCurCycle / fBlendIn, + 0.001f, 1.0f) ); + } + else if (flCurCycle >= (1.0f - fBlendOut) && fBlendOut>0) + { + pLayer->m_flWeight = ( clamp((1.0f - flCurCycle) / fBlendOut, + 0.001f, 1.0f) ); + } + else + { + pLayer->m_flWeight = 1.0f; + } + } + else + { + pLayer->m_flWeight = 1.0f; + } + if (bMoveBlend) + { + // blend the animation out as we move faster + if (speed <= 50) + pLayer->m_flWeight = ( pLayer->m_flWeight * (50.0f - speed) / 50.0f ); + } + +#ifndef CLIENT_DLL + pLayer->m_fFlags |= ANIM_LAYER_ACTIVE; +#endif + pLayer->SetOrder( iLayer ); +} + +void CSinglePlayerAnimState::ComputeFireSequence() +{ + UpdateLayerSequenceGeneric( FIRESEQUENCE_LAYER, m_bFiring, m_flFireCycle, m_iFireSequence, false ); +} + +void CSinglePlayerAnimState::ComputeReloadSequence() +{ + UpdateLayerSequenceGeneric( RELOADSEQUENCE_LAYER, m_bReloading, m_flReloadCycle, m_iReloadSequence, false, m_flReloadBlendIn, m_flReloadBlendOut, false, m_fReloadPlaybackRate ); +} + +void CSinglePlayerAnimState::ComputeWeaponSwitchSequence() +{ + UpdateLayerSequenceGeneric( RELOADSEQUENCE_LAYER, m_bWeaponSwitching, m_flWeaponSwitchCycle, m_iWeaponSwitchSequence, false, 0, 0.5f ); +} + +// does misc gestures if we're not firing +void CSinglePlayerAnimState::ComputeMiscSequence() +{ + UpdateLayerSequenceGeneric( RELOADSEQUENCE_LAYER, m_bPlayingMisc, m_flMiscCycle, m_iMiscSequence, m_bMiscHoldAtEnd, m_flMiscBlendIn, m_flMiscBlendOut, m_bMiscOnlyWhenStill, m_fMiscPlaybackRate ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : float +//----------------------------------------------------------------------------- +float CSinglePlayerAnimState::GetCurrentMaxGroundSpeed() +{ + CStudioHdr *pStudioHdr = GetOuter()->GetModelPtr(); + + if ( pStudioHdr == NULL ) + return 1.0f; + + int iMoveX = GetOuter()->LookupPoseParameter( "move_x" ); + int iMoveY = GetOuter()->LookupPoseParameter( "move_y" ); + + float prevX = GetOuter()->GetPoseParameter( iMoveX ); + float prevY = GetOuter()->GetPoseParameter( iMoveY ); + + float d = MAX( fabs( prevX ), fabs( prevY ) ); + float newX, newY; + if ( d == 0.0 ) + { + newX = 1.0; + newY = 0.0; + } + else + { + newX = prevX / d; + newY = prevY / d; + } + + GetOuter()->SetPoseParameter( pStudioHdr, iMoveX, newX ); + GetOuter()->SetPoseParameter( pStudioHdr, iMoveY, newY ); + + float speed = GetOuter()->GetSequenceGroundSpeed(GetOuter()->GetSequence() ); + + GetOuter()->SetPoseParameter( pStudioHdr, iMoveX, prevX ); + GetOuter()->SetPoseParameter( pStudioHdr, iMoveY, prevY ); + + return speed; +} + +//----------------------------------------------------------------------------- +// Purpose: Override for backpeddling +// Input : dt - +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::ComputePoseParam_BodyYaw( void ) +{ + CBasePlayerAnimState::ComputePoseParam_BodyYaw(); + + //ComputePoseParam_BodyLookYaw(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::ComputePoseParam_BodyLookYaw( void ) +{ + // See if we even have a blender for pitch + int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" ); + if ( upper_body_yaw < 0 ) + { + return; + } + + // Assume upper and lower bodies are aligned and that we're not turning + float flGoalTorsoYaw = 0.0f; + int turning = TURN_NONE; + float turnrate = 360.0f; + + Vector vel; + + GetOuterAbsVelocity( vel ); + + bool isMoving = ( vel.Length() > 1.0f ) ? true : false; + + if ( !isMoving ) + { + // Just stopped moving, try and clamp feet + if ( m_flLastTurnTime <= 0.0f ) + { + m_flLastTurnTime = gpGlobals->curtime; + m_flLastYaw = GetOuter()->EyeAngles().y; + // Snap feet to be perfectly aligned with torso/eyes + m_flGoalFeetYaw = GetOuter()->EyeAngles().y; + m_flCurrentFeetYaw = m_flGoalFeetYaw; + m_nTurningInPlace = TURN_NONE; + } + + // If rotating in place, update stasis timer + + if ( m_flLastYaw != GetOuter()->EyeAngles().y ) + { + m_flLastTurnTime = gpGlobals->curtime; + m_flLastYaw = GetOuter()->EyeAngles().y; + } + + if ( m_flGoalFeetYaw != m_flCurrentFeetYaw ) + { + m_flLastTurnTime = gpGlobals->curtime; + } + + turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, m_AnimConfig.m_flMaxBodyYawDegrees, gpGlobals->frametime, m_flCurrentFeetYaw ); + + QAngle eyeAngles = GetOuter()->EyeAngles(); + QAngle vAngle = GetOuter()->GetLocalAngles(); + + // See how far off current feetyaw is from true yaw + float yawdelta = GetOuter()->EyeAngles().y - m_flCurrentFeetYaw; + yawdelta = AngleNormalize( yawdelta ); + + bool rotated_too_far = false; + + float yawmagnitude = fabs( yawdelta ); + + // If too far, then need to turn in place + if ( yawmagnitude > 45 ) + { + rotated_too_far = true; + } + + // Standing still for a while, rotate feet around to face forward + // Or rotated too far + // FIXME: Play an in place turning animation + if ( rotated_too_far || + ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) ) + { + m_flGoalFeetYaw = GetOuter()->EyeAngles().y; + m_flLastTurnTime = gpGlobals->curtime; + + /* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw; + if ( yd > 0 ) + { + m_nTurningInPlace = TURN_RIGHT; + } + else if ( yd < 0 ) + { + m_nTurningInPlace = TURN_LEFT; + } + else + { + m_nTurningInPlace = TURN_NONE; + } + + turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); + yawdelta = GetOuter()->EyeAngles().y - m_flCurrentFeetYaw;*/ + + } + + // Snap upper body into position since the delta is already smoothed for the feet + flGoalTorsoYaw = yawdelta; + m_flCurrentTorsoYaw = flGoalTorsoYaw; + } + else + { + m_flLastTurnTime = 0.0f; + m_nTurningInPlace = TURN_NONE; + m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->EyeAngles().y; + flGoalTorsoYaw = 0.0f; + m_flCurrentTorsoYaw = GetOuter()->EyeAngles().y - m_flCurrentFeetYaw; + } + + if ( turning == TURN_NONE ) + { + m_nTurningInPlace = turning; + } + + if ( m_nTurningInPlace != TURN_NONE ) + { + // If we're close to finishing the turn, then turn off the turning animation + if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION ) + { + m_nTurningInPlace = TURN_NONE; + } + } + + GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) ); + + /* + // FIXME: Adrian, what is this? + int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" ); + + if ( body_yaw >= 0 ) + { + GetOuter()->SetPoseParameter( body_yaw, 30 ); + } + */ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr ) +{ + // Get pitch from v_angle + float flPitch = m_flEyePitch; + + if ( flPitch > 180.0f ) + { + flPitch -= 360.0f; + } + flPitch = clamp( flPitch, -90, 90 ); + + // See if we have a blender for pitch + GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch ); + + ComputePoseParam_HeadPitch( pStudioHdr ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSinglePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr ) +{ + // Get pitch from v_angle + int iHeadPitch = GetOuter()->LookupPoseParameter("head_pitch"); + + float flPitch = m_flEyePitch; + + if ( flPitch > 180.0f ) + { + flPitch -= 360.0f; + } + flPitch = clamp( flPitch, -90, 90 ); + + GetOuter()->SetPoseParameter( pStudioHdr, iHeadPitch, flPitch ); +} diff --git a/mp/src/game/shared/mapbase/singleplayer_animstate.h b/mp/src/game/shared/mapbase/singleplayer_animstate.h new file mode 100644 index 00000000..a805dffc --- /dev/null +++ b/mp/src/game/shared/mapbase/singleplayer_animstate.h @@ -0,0 +1,110 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Single Player animation state 'handler'. This utility is used +// to evaluate the pose parameter value based on the direction +// and speed of the player. +// +// ------------------------------------------------------------------------------ +// +// This was originally based on the following VDC article: +// https://developer.valvesoftware.com/wiki/Fixing_the_player_animation_state_(Single_Player) +// +// It has been modified by Blixibon to derive from CBasePlayerAnimState instead and support 9-way blends. +// Much of the work done to make this derive from CBasePlayerAnimState utilized code from the Alien Swarm SDK. +// +//=============================================================================// + +#ifndef SINGLEPLAYER_ANIMSTATE_H +#define SINGLEPLAYER_ANIMSTATE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "cbase.h" +#include "base_playeranimstate.h" + +#ifdef CLIENT_DLL +#include "c_baseplayer.h" +#else +#include "player.h" +#endif + +#ifdef MAPBASE +// Special definition for differentiating between SP and HL2:DM anim states +#define SP_ANIM_STATE 1 +#endif + +class CSinglePlayerAnimState : public CBasePlayerAnimState +{ +public: + CSinglePlayerAnimState( CBasePlayer *pPlayer ); + + Activity CalcMainActivity(); + int CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle ); + float GetCurrentMaxGroundSpeed(); + + void SetPlayerAnimation( PLAYER_ANIM playerAnim ); + Activity TranslateActivity( Activity actDesired ); + + void ComputeSequences( CStudioHdr *pStudioHdr ); + + void AddMiscSequence( int iSequence, float flBlendIn = 0.0f, float flBlendOut = 0.0f, float flPlaybackRate = 1.0f, bool bHoldAtEnd = false, bool bOnlyWhenStill = false ); + + void ClearAnimationState(); + void ClearAnimationLayers(); + +private: + + bool HandleJumping(); + + void ComputeFireSequence(); + void ComputeReloadSequence(); + void ComputeWeaponSwitchSequence(); + void ComputeMiscSequence(); + + void UpdateLayerSequenceGeneric( int iLayer, bool &bEnabled, float &flCurCycle, + int &iSequence, bool bWaitAtEnd, + float fBlendIn=0.15f, float fBlendOut=0.15f, bool bMoveBlend = false, + float fPlaybackRate=1.0f, bool bUpdateCycle = true ); + + void ComputePoseParam_BodyYaw( void ); + void ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr ); + void ComputePoseParam_BodyLookYaw( void ); + void ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr ); + + CBasePlayer* m_pPlayer; + + // Current state variables. + bool m_bJumping; // Set on a jump event. + float m_flJumpStartTime; + bool m_bFirstJumpFrame; + + // Aim sequence plays reload while this is on. + bool m_bReloading; + float m_flReloadCycle; + int m_iReloadSequence; + float m_flReloadBlendOut, m_flReloadBlendIn; + float m_fReloadPlaybackRate; + + bool m_bWeaponSwitching; + float m_flWeaponSwitchCycle; + int m_iWeaponSwitchSequence; + + bool m_bPlayingMisc; + float m_flMiscCycle, m_flMiscBlendOut, m_flMiscBlendIn; + int m_iMiscSequence; + bool m_bMiscOnlyWhenStill, m_bMiscHoldAtEnd; + bool m_bMiscNoOverride; + float m_fMiscPlaybackRate; + bool m_bMiscCycleRewound; + float m_flMiscRewindCycle; + // This is set to true if ANY animation is being played in the fire layer. + bool m_bFiring; // If this is on, then it'll continue the fire animation in the fire layer + // until it completes. + int m_iFireSequence; // (For any sequences in the fire layer, including grenade throw). + float m_flFireCycle; +}; + +CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer ); + +#endif // SINGLEPLAYER_ANIMSTATE_H diff --git a/mp/src/game/shared/mapbase/vscript_consts_shared.cpp b/mp/src/game/shared/mapbase/vscript_consts_shared.cpp index b03ee0e8..2ced7086 100644 --- a/mp/src/game/shared/mapbase/vscript_consts_shared.cpp +++ b/mp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -8,6 +8,8 @@ #include "cbase.h" #include "activitylist.h" #include "in_buttons.h" +#include "rope_shared.h" +#include "eventlist.h" #ifdef CLIENT_DLL #include "c_ai_basenpc.h" #else @@ -66,7 +68,7 @@ BEGIN_SCRIPTENUM( RenderMode, "Render modes used by Get/SetRenderMode" ) DEFINE_ENUMCONST_NAMED( kRenderTransAdd, "Additive", "" ) DEFINE_ENUMCONST_NAMED( kRenderEnvironmental, "Environmental", "" ) DEFINE_ENUMCONST_NAMED( kRenderTransAddFrameBlend, "AdditiveFractionalFrame", "" ) - DEFINE_ENUMCONST_NAMED( kRenderTransAlphaAdd, "Alpha Add", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAlphaAdd, "AlphaAdd", "" ) DEFINE_ENUMCONST_NAMED( kRenderWorldGlow, "WorldSpaceGlow", "" ) DEFINE_ENUMCONST_NAMED( kRenderNone, "None", "" ) @@ -124,6 +126,15 @@ extern void RegisterWeaponScriptConstants(); void RegisterSharedScriptConstants() { + // "SERVER_DLL" is used in Source 2. +#ifdef GAME_DLL + ScriptRegisterConstantNamed( g_pScriptVM, 1, "SERVER_DLL", "" ); + ScriptRegisterConstantNamed( g_pScriptVM, 0, "CLIENT_DLL", "" ); +#else + ScriptRegisterConstantNamed( g_pScriptVM, 0, "SERVER_DLL", "" ); + ScriptRegisterConstantNamed( g_pScriptVM, 1, "CLIENT_DLL", "" ); +#endif + // // Activities // @@ -310,9 +321,102 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, MOVETYPE_OBSERVER, "Move type used in GetMoveType(), etc." ); ScriptRegisterConstant( g_pScriptVM, MOVETYPE_CUSTOM, "Move type used in GetMoveType(), etc." ); + // + // Animation Stuff + // + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_SERVER, "Animation event flag which indicates an event is supposed to be serverside only." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_SCRIPTED, "Animation event flag with an unknown purpose." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_SHARED, "Animation event flag which indicates an event is supposed to be shared between the server and client." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_WEAPON, "Animation event flag which indicates an event is part of a weapon." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_CLIENT, "Animation event flag which indicates an event is supposed to be clientside only." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_FACEPOSER, "Animation event flag with an unknown purpose. Presumably related to Faceposer." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_NEWEVENTSYSTEM, "Animation event flag which indicates an event is using the new system. This is often used by class-specific events from NPCs." ); + + // + // Ropes + // + ScriptRegisterConstant( g_pScriptVM, ROPE_RESIZE, "Try to keep the rope dangling the same amount even as the rope length changes. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_BARBED, "Hack option to draw like a barbed wire. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_COLLIDE, "Collide with the world. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_SIMULATE, "Is the rope valid? (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_BREAKABLE, "Can the endpoints detach? (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_USE_WIND, "Wind simulation on this rope. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_INITIAL_HANG, "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. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_PLAYER_WPN_ATTACH, "If this flag is set, then the second attachment must be a player. The rope will attach to \"buff_attach\" on the player's active weapon. This is a flag because it requires special code on the client to find the weapon. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_NO_GRAVITY, "Disable gravity on this rope. (for use in rope flags)" ); + ScriptRegisterConstant( g_pScriptVM, ROPE_NUMFLAGS, "The number of rope flags recognized by the game." ); + + ScriptRegisterConstantNamed( g_pScriptVM, Vector( ROPE_GRAVITY ), "ROPE_GRAVITY", "Default rope gravity vector." ); + + // + // Sounds + // + ScriptRegisterConstant( g_pScriptVM, CHAN_REPLACE, "The sound channel used when playing sounds through console commands." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_AUTO, "The default generic sound channel." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_WEAPON, "The sound channel for player and NPC weapons." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_VOICE, "The sound channel used for dialogue, voice lines, etc." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_ITEM, "The sound channel used for generic physics impact sounds, health/suit chargers, +use sounds." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_BODY, "The sound channel used for clothing, ragdoll impacts, footsteps, knocking/pounding/punching etc." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_STREAM, "The sound channel for sounds that can be delayed by an async load, i.e. aren't responses to particular events." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_STATIC, "The sound channel for constant/background sound that doesn't require any reaction." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_VOICE2, "An additional sound channel for voices. Used in TF2 for the announcer." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_VOICE_BASE, "The sound channel used for network voice data (online voice communications)." ); + + ScriptRegisterConstant( g_pScriptVM, VOL_NORM, "The standard volume value." ); + ScriptRegisterConstant( g_pScriptVM, PITCH_NORM, "The standard pitch value." ); + ScriptRegisterConstant( g_pScriptVM, PITCH_LOW, "The standard low pitch value." ); + ScriptRegisterConstant( g_pScriptVM, PITCH_HIGH, "The standard high pitch value." ); + + ScriptRegisterConstant( g_pScriptVM, SNDLVL_NONE, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_20dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_25dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_30dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_35dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_40dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_45dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_50dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_55dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_IDLE, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_60dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_65dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_STATIC, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_70dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_NORM, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_75dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_80dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_TALKING, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_85dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_90dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_95dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_100dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_105dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_110dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_120dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_130dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_GUNFIRE, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_140dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_150dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_180dB, "A standard value used for a sound's sound level." ); + + ScriptRegisterConstant( g_pScriptVM, SND_CHANGE_VOL, "Indicates a sound is a volume change to an already-playing sound." ); + ScriptRegisterConstant( g_pScriptVM, SND_CHANGE_PITCH, "Indicates a sound is a pitch change to an already-playing sound." ); + ScriptRegisterConstant( g_pScriptVM, SND_STOP, "Indicates a sound is stopping an already-playing sound." ); + ScriptRegisterConstant( g_pScriptVM, SND_SPAWNING, "Indicates a sound is spawning, used in some cases for ambients. Not networked." ); + ScriptRegisterConstant( g_pScriptVM, SND_DELAY, "Indicates a sound has an initial delay." ); + ScriptRegisterConstant( g_pScriptVM, SND_STOP_LOOPING, "Stops all looping sounds on an entity." ); + ScriptRegisterConstant( g_pScriptVM, SND_SPEAKER, "Indicates a sound is being played again by a microphone through a speaker." ); + ScriptRegisterConstant( g_pScriptVM, SND_SHOULDPAUSE, "Forces a sound to pause if the game is paused." ); + ScriptRegisterConstant( g_pScriptVM, SND_IGNORE_PHONEMES, "Prevents the entity emitting this sound from using its phonemes (no lip-syncing)." ); + ScriptRegisterConstant( g_pScriptVM, SND_IGNORE_NAME, "Used to change all sounds emitted by an entity, regardless of name." ); + ScriptRegisterConstant( g_pScriptVM, SND_DO_NOT_OVERWRITE_EXISTING_ON_CHANNEL, "Prevents a sound from interrupting other sounds on a channel (if the channel supports interruption)." ); + + ScriptRegisterConstant( g_pScriptVM, GENDER_NONE, "A standard value used to represent no specific gender. Usually used for sounds." ); + ScriptRegisterConstant( g_pScriptVM, GENDER_MALE, "A standard value used to represent male gender. Usually used for sounds." ); + ScriptRegisterConstant( g_pScriptVM, GENDER_FEMALE, "A standard value used to represent female gender. Usually used for sounds." ); + #ifdef GAME_DLL // - // Sound Types, Contexts, and Channels + // AI Sounds // (QueryHearSound hook can use these) // ScriptRegisterConstant( g_pScriptVM, SOUND_NONE, "Sound type used in QueryHearSound hooks, etc." ); @@ -483,7 +587,7 @@ void RegisterSharedScriptConstants() //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())" ); + 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." ); @@ -492,6 +596,12 @@ void RegisterSharedScriptConstants() 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." ); + + ScriptRegisterConstant( g_pScriptVM, D_ER, "'Error' relationship definition. Used by NPCs and players for relationship disposition." ); + ScriptRegisterConstant( g_pScriptVM, D_HT, "Denotes a 'Hate' relationship. Used by NPCs and players for relationship disposition." ); + ScriptRegisterConstant( g_pScriptVM, D_FR, "Denotes a 'Fear' relationship. Used by NPCs and players for relationship disposition." ); + ScriptRegisterConstant( g_pScriptVM, D_LI, "Denotes a 'Like' relationship. Used by NPCs and players for relationship disposition." ); + ScriptRegisterConstant( g_pScriptVM, D_NU, "Denotes a 'Neutral' relationship. Used by NPCs and players for relationship disposition." ); #endif // diff --git a/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp index c0668e8f..4c6d4817 100644 --- a/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/mp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -22,10 +22,15 @@ #include "globalstate.h" #include "vscript_server.h" #include "soundent.h" -#endif // !CLIENT_DLL +#include "rope.h" +#include "ai_basenpc.h" +#else +#include "c_rope.h" +#endif // CLIENT_DLL #include "con_nprint.h" #include "particle_parse.h" +#include "npcevent.h" #include "vscript_funcs_shared.h" #include "vscript_singletons.h" @@ -164,18 +169,18 @@ HSCRIPT SpawnEntityFromTable( const char *pszClassname, HSCRIPT hKV ) HSCRIPT EntIndexToHScript( int index ) { #ifdef GAME_DLL - edict_t *e = INDEXENT(index); - if ( e && !e->IsFree() ) - { - return ToHScript( GetContainingEntity( e ) ); - } + 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 ) ); - } + if ( index < NUM_ENT_ENTRIES ) + { + return ToHScript( CBaseEntity::Instance( index ) ); + } #endif - return NULL; + return NULL; } //----------------------------------------------------------------------------- @@ -253,8 +258,8 @@ void ScriptDispatchSpawn( HSCRIPT hEntity ) 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 ); + CTakeDamageInfo *damageInfo = new CTakeDamageInfo( ToEnt(hInflictor), ToEnt(hAttacker), flDamage, iDamageType ); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( damageInfo ); damageInfo->SetDamagePosition( vecDamagePos ); damageInfo->SetDamageForce( vecForce ); @@ -264,28 +269,54 @@ static HSCRIPT CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Ve static void DestroyDamageInfo( HSCRIPT hDamageInfo ) { - if (hDamageInfo) + CTakeDamageInfo *pInfo = HScriptToClass< CTakeDamageInfo >( hDamageInfo ); + if ( pInfo ) { - CTakeDamageInfo *pInfo = (CTakeDamageInfo*)g_pScriptVM->GetInstanceValue( hDamageInfo, GetScriptDescForClass( CTakeDamageInfo ) ); - if (pInfo) - { - g_pScriptVM->RemoveInstance( hDamageInfo ); - delete 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 ); } +void ScriptCalculateExplosiveDamageForce( HSCRIPT info, const Vector &vecDir, const Vector &vecForceOrigin, float flScale ) +{ + CTakeDamageInfo *pInfo = HScriptToClass< CTakeDamageInfo >( info ); + if ( pInfo ) + { + CalculateExplosiveDamageForce( pInfo, vecDir, vecForceOrigin, flScale ); + } +} + +void ScriptCalculateBulletDamageForce( HSCRIPT info, int iBulletType, const Vector &vecBulletDir, const Vector &vecForceOrigin, float flScale ) +{ + CTakeDamageInfo *pInfo = HScriptToClass< CTakeDamageInfo >( info ); + if ( pInfo ) + { + CalculateBulletDamageForce( pInfo, iBulletType, vecBulletDir, vecForceOrigin, flScale ); + } +} + +void ScriptCalculateMeleeDamageForce( HSCRIPT info, const Vector &vecMeleeDir, const Vector &vecForceOrigin, float flScale ) +{ + CTakeDamageInfo *pInfo = HScriptToClass< CTakeDamageInfo >( info ); + if ( pInfo ) + { + CalculateMeleeDamageForce( pInfo, vecMeleeDir, vecForceOrigin, flScale ); + } +} + +void ScriptGuessDamageForce( HSCRIPT info, const Vector &vecForceDir, const Vector &vecForceOrigin, float flScale ) +{ + CTakeDamageInfo *pInfo = HScriptToClass< CTakeDamageInfo >( info ); + if ( pInfo ) + { + GuessDamageForce( pInfo, vecForceDir, vecForceOrigin, flScale ); + } +} //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -BEGIN_SCRIPTDESC_ROOT_NAMED( CTraceInfoAccessor, "CGameTrace", "Handle for accessing trace_t info." ) - DEFINE_SCRIPT_CONSTRUCTOR() - +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptGameTrace, "CGameTrace", "trace_t" ) 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." ) @@ -313,120 +344,95 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CTraceInfoAccessor, "CGameTrace", "Handle for acces 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( Surface, "" ) + DEFINE_SCRIPTFUNC( 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." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( scriptsurfacedata_t, "surfacedata_t", "" ) + DEFINE_SCRIPTFUNC( GetFriction, "" ) + DEFINE_SCRIPTFUNC( GetThickness, "" ) - DEFINE_SCRIPTFUNC( GetJumpFactor, "The surface's jump factor." ) - DEFINE_SCRIPTFUNC( GetMaterialChar, "The surface's material character." ) + DEFINE_SCRIPTFUNC( GetJumpFactor, "" ) + DEFINE_SCRIPTFUNC( GetMaterialChar, "" ) - 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." ) + DEFINE_SCRIPTFUNC( GetSoundStepLeft, "" ) + DEFINE_SCRIPTFUNC( GetSoundStepRight, "" ) + DEFINE_SCRIPTFUNC( GetSoundImpactSoft, "" ) + DEFINE_SCRIPTFUNC( GetSoundImpactHard, "" ) + DEFINE_SCRIPTFUNC( GetSoundScrapeSmooth, "" ) + DEFINE_SCRIPTFUNC( GetSoundScrapeRough, "" ) + DEFINE_SCRIPTFUNC( GetSoundBulletImpact, "" ) + DEFINE_SCRIPTFUNC( GetSoundRolling, "" ) + DEFINE_SCRIPTFUNC( GetSoundBreak, "" ) + DEFINE_SCRIPTFUNC( GetSoundStrain, "" ) 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." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( CSurfaceScriptHelper, "csurface_t", "" ) + DEFINE_SCRIPTFUNC( 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." ) +BEGIN_SCRIPTDESC_ROOT( cplane_t, "" ) 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 ); + CScriptGameTrace *tr = new CScriptGameTrace(); - CBaseEntity *pLooker = ToEnt(entIgnore); - UTIL_TraceLine( vecStart, vecEnd, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + CBaseEntity *pIgnore = ToEnt( entIgnore ); + UTIL_TraceLine( vecStart, vecEnd, iMask, pIgnore, iCollisionGroup, tr ); - // The trace's destruction should destroy this automatically - CSurfaceScriptAccessor *surfaceInfo = new CSurfaceScriptAccessor( traceInfo->GetTrace().surface ); - HSCRIPT hSurface = g_pScriptVM->RegisterInstance( surfaceInfo ); - traceInfo->SetSurface( hSurface ); + tr->RegisterSurface(); + tr->RegisterPlane(); - HSCRIPT hPlane = g_pScriptVM->RegisterInstance( &(traceInfo->GetTrace().plane) ); - traceInfo->SetPlane( hPlane ); - - return hScript; + return tr->GetScriptInstance(); } 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 ); + CScriptGameTrace *tr = new CScriptGameTrace(); - CBaseEntity *pLooker = ToEnt(entIgnore); - UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + CBaseEntity *pIgnore = ToEnt( entIgnore ); + UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, iMask, pIgnore, iCollisionGroup, tr ); - // The trace's destruction should destroy this automatically - CSurfaceScriptAccessor *surfaceInfo = new CSurfaceScriptAccessor( traceInfo->GetTrace().surface ); - HSCRIPT hSurface = g_pScriptVM->RegisterInstance( surfaceInfo ); - traceInfo->SetSurface( hSurface ); + tr->RegisterSurface(); + tr->RegisterPlane(); - HSCRIPT hPlane = g_pScriptVM->RegisterInstance( &(traceInfo->GetTrace().plane) ); - traceInfo->SetPlane( hPlane ); - - return hScript; + return tr->GetScriptInstance(); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -BEGIN_SCRIPTDESC_ROOT( FireBulletsInfo_t, "Handle for accessing FireBulletsInfo_t info." ) +BEGIN_SCRIPTDESC_ROOT( FireBulletsInfo_t, "" ) 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( GetSource, "" ) + DEFINE_SCRIPTFUNC( SetSource, "" ) + DEFINE_SCRIPTFUNC( GetDirShooting, "" ) + DEFINE_SCRIPTFUNC( SetDirShooting, "" ) + DEFINE_SCRIPTFUNC( GetSpread, "" ) + DEFINE_SCRIPTFUNC( SetSpread, "" ) 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( GetAmmoType, "" ) + DEFINE_SCRIPTFUNC( SetAmmoType, "" ) - DEFINE_SCRIPTFUNC( GetTracerFreq, "Gets the tracer frequency." ) - DEFINE_SCRIPTFUNC( SetTracerFreq, "Sets the tracer frequency." ) + DEFINE_SCRIPTFUNC( GetTracerFreq, "" ) + DEFINE_SCRIPTFUNC( SetTracerFreq, "" ) 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" ) @@ -436,18 +442,16 @@ BEGIN_SCRIPTDESC_ROOT( FireBulletsInfo_t, "Handle for accessing FireBulletsInfo_ 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( GetDamageForceScale, "" ) + DEFINE_SCRIPTFUNC( SetDamageForceScale, "" ) - 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_NAMED( ScriptGetAttacker, "GetAttacker", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAttacker, "SetAttacker", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAdditionalIgnoreEnt, "GetAdditionalIgnoreEnt", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAdditionalIgnoreEnt, "SetAdditionalIgnoreEnt", "" ) 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(); //----------------------------------------------------------------------------- @@ -478,7 +482,7 @@ static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Ve { // The script is responsible for deleting this via DestroyFireBulletsInfo(). FireBulletsInfo_t *info = new FireBulletsInfo_t(); - HSCRIPT hScript = g_pScriptVM->RegisterInstance( info, true ); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( info ); info->SetShots( cShots ); info->SetSource( vecSrc ); @@ -492,19 +496,143 @@ static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Ve static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo ) { - g_pScriptVM->RemoveInstance( hBulletsInfo ); + FireBulletsInfo_t *pInfo = HScriptToClass< FireBulletsInfo_t >( hBulletsInfo ); + if ( pInfo ) + { + g_pScriptVM->RemoveInstance( hBulletsInfo ); + delete pInfo; + } } -// For the function in baseentity.cpp -FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ) +//----------------------------------------------------------------------------- +// animevent_t +//----------------------------------------------------------------------------- +CAnimEventTInstanceHelper g_AnimEventTInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT( scriptanimevent_t, "" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_AnimEventTInstanceHelper ) + + DEFINE_SCRIPTFUNC( GetEvent, "" ) + DEFINE_SCRIPTFUNC( SetEvent, "" ) + + DEFINE_SCRIPTFUNC( GetOptions, "" ) + DEFINE_SCRIPTFUNC( SetOptions, "" ) + + DEFINE_SCRIPTFUNC( GetCycle, "" ) + DEFINE_SCRIPTFUNC( SetCycle, "" ) + + DEFINE_SCRIPTFUNC( GetEventTime, "" ) + DEFINE_SCRIPTFUNC( SetEventTime, "" ) + + DEFINE_SCRIPTFUNC( GetType, "Gets the event's type flags. See the 'AE_TYPE_' set of constants for valid flags." ) + DEFINE_SCRIPTFUNC( SetType, "Sets the event's type flags. See the 'AE_TYPE_' set of constants for valid flags." ) + + DEFINE_SCRIPTFUNC( GetSource, "Gets the event's source entity." ) + DEFINE_SCRIPTFUNC( SetSource, "Sets the event's source entity." ) +END_SCRIPTDESC(); + +bool CAnimEventTInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) { - return HScriptToClass( hBulletsInfo ); + DevWarning( "VScript animevent_t.%s: animevent_t metamethod members are deprecated! Use 'script_help animevent_t' to see the correct functions.\n", pszKey ); + + animevent_t *ani = ((animevent_t *)p); + if (FStrEq( pszKey, "event" )) + variant = ani->event; + else if (FStrEq( pszKey, "options" )) + variant = ani->options; + else if (FStrEq( pszKey, "cycle" )) + variant = ani->cycle; + else if (FStrEq( pszKey, "eventtime" )) + variant = ani->eventtime; + else if (FStrEq( pszKey, "type" )) + variant = ani->type; + else if (FStrEq( pszKey, "source" )) + variant = ToHScript(ani->pSource); + else + return false; + + return true; } +bool CAnimEventTInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + DevWarning( "VScript animevent_t.%s: animevent_t metamethod members are deprecated! Use 'script_help animevent_t' to see the correct functions.\n", pszKey ); + + animevent_t *ani = ((animevent_t *)p); + if (FStrEq( pszKey, "event" )) + ani->event = variant; + else if (FStrEq( pszKey, "options" )) + ani->options = variant; + else if (FStrEq( pszKey, "cycle" )) + ani->cycle = variant; + else if (FStrEq( pszKey, "eventtime" )) + ani->eventtime = variant; + else if (FStrEq( pszKey, "type" )) + ani->type = variant; + else if (FStrEq( pszKey, "source" )) + { + CBaseEntity *pEnt = ToEnt( variant.m_hScript ); + if (pEnt) + ani->pSource = pEnt->GetBaseAnimating(); + } + else + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// EmitSound_t +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT_NAMED( ScriptEmitSound_t, "EmitSound_t", "" ) + DEFINE_SCRIPT_CONSTRUCTOR() + + DEFINE_SCRIPTFUNC( GetChannel, "" ) + DEFINE_SCRIPTFUNC( SetChannel, "" ) + + DEFINE_SCRIPTFUNC( GetSoundName, "Gets the sound's file path or soundscript name." ) + DEFINE_SCRIPTFUNC( SetSoundName, "Sets the sound's file path or soundscript name." ) + + DEFINE_SCRIPTFUNC( GetVolume, "(Note that this may not apply to soundscripts)" ) + DEFINE_SCRIPTFUNC( SetVolume, "(Note that this may not apply to soundscripts)" ) + + DEFINE_SCRIPTFUNC( GetSoundLevel, "Gets the sound's level in decibels. (Note that this may not apply to soundscripts)" ) + DEFINE_SCRIPTFUNC( SetSoundLevel, "Sets the sound's level in decibels. (Note that this may not apply to soundscripts)" ) + + DEFINE_SCRIPTFUNC( GetFlags, "Gets the sound's flags. See the 'SND_' set of constants." ) + DEFINE_SCRIPTFUNC( SetFlags, "Sets the sound's flags. See the 'SND_' set of constants." ) + + DEFINE_SCRIPTFUNC( GetSpecialDSP, "" ) + DEFINE_SCRIPTFUNC( SetSpecialDSP, "" ) + + DEFINE_SCRIPTFUNC( HasOrigin, "Returns true if the sound has an origin override." ) + DEFINE_SCRIPTFUNC( GetOrigin, "Gets the sound's origin override." ) + DEFINE_SCRIPTFUNC( SetOrigin, "Sets the sound's origin override." ) + DEFINE_SCRIPTFUNC( ClearOrigin, "Clears the sound's origin override if it has one." ) + + DEFINE_SCRIPTFUNC( GetSoundTime, "Gets the time the sound will begin, relative to Time()." ) + DEFINE_SCRIPTFUNC( SetSoundTime, "Sets the time the sound will begin, relative to Time()." ) + + DEFINE_SCRIPTFUNC( GetEmitCloseCaption, "Gets whether or not the sound will emit closed captioning/subtitles." ) + DEFINE_SCRIPTFUNC( SetEmitCloseCaption, "Sets whether or not the sound will emit closed captioning/subtitles." ) + + DEFINE_SCRIPTFUNC( GetWarnOnMissingCloseCaption, "Gets whether or not the sound will send a message to the console if there is no corresponding closed captioning token." ) + DEFINE_SCRIPTFUNC( SetWarnOnMissingCloseCaption, "Sets whether or not the sound will send a message to the console if there is no corresponding closed captioning token." ) + + DEFINE_SCRIPTFUNC( GetWarnOnDirectWaveReference, "Gets whether or not the sound will send a message to the console if it references a direct sound file instead of a soundscript." ) + DEFINE_SCRIPTFUNC( SetWarnOnDirectWaveReference, "Sets whether or not the sound will send a message to the console if it references a direct sound file instead of a soundscript." ) + + DEFINE_SCRIPTFUNC( GetSpeakerEntity, "Gets the sound's original source if it is being transmitted by a microphone." ) + DEFINE_SCRIPTFUNC( SetSpeakerEntity, "Sets the sound's original source if it is being transmitted by a microphone." ) + + DEFINE_SCRIPTFUNC( GetSoundScriptHandle, "" ) + DEFINE_SCRIPTFUNC( SetSoundScriptHandle, "" ) +END_SCRIPTDESC(); + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptUserCmd, "CUserCmd", "" ) DEFINE_SCRIPTFUNC( GetCommandNumber, "For matching server and client commands for debugging." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetTickCount, "GetTickCount", "The tick the client created this command." ) @@ -512,15 +640,15 @@ BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) 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( GetForwardMove, "" ) + DEFINE_SCRIPTFUNC( SetForwardMove, "" ) + DEFINE_SCRIPTFUNC( GetSideMove, "" ) + DEFINE_SCRIPTFUNC( SetSideMove, "" ) + DEFINE_SCRIPTFUNC( GetUpMove, "" ) + DEFINE_SCRIPTFUNC( SetUpMove, "" ) - DEFINE_SCRIPTFUNC( GetButtons, "Attack button states." ) - DEFINE_SCRIPTFUNC( SetButtons, "Sets attack button states." ) + DEFINE_SCRIPTFUNC( GetButtons, "Input button state." ) + DEFINE_SCRIPTFUNC( SetButtons, "Sets input button state." ) DEFINE_SCRIPTFUNC( GetImpulse, "Impulse command issued." ) DEFINE_SCRIPTFUNC( SetImpulse, "Sets impulse command issued." ) @@ -537,6 +665,32 @@ BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) DEFINE_SCRIPTFUNC( SetMouseY, "Sets mouse accum in y from create move." ) END_SCRIPTDESC(); +#ifdef GAME_DLL +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +#define DEFINE_ENEMY_INFO_SCRIPTFUNCS(name, desc) \ + DEFINE_SCRIPTFUNC_NAMED( Get##name, #name, "Get " desc ) \ + DEFINE_SCRIPTFUNC( Set##name, "Set " desc ) + +BEGIN_SCRIPTDESC_ROOT_NAMED( Script_AI_EnemyInfo_t, "AI_EnemyInfo_t", "Accessor for information about an enemy." ) + DEFINE_SCRIPTFUNC( Enemy, "" ) + DEFINE_SCRIPTFUNC( SetEnemy, "" ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastKnownLocation, "" ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastSeenLocation, "" ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastSeen, "" ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeFirstSeen, "" ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReacquired, "" ) + 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, "" ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( MobbedMe, "whether the enemy was part of a mob at some point." ) +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -620,6 +774,7 @@ static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vec //============================================================================= //============================================================================= +#ifdef CLIENT_DLL static int ScriptPrecacheModel( const char *modelname ) { return CBaseEntity::PrecacheModel( modelname ); @@ -629,8 +784,17 @@ static void ScriptPrecacheOther( const char *classname ) { UTIL_PrecacheOther( classname ); } +#else +static int ScriptPrecacheModel( const char *modelname, bool bPreload ) +{ + return CBaseEntity::PrecacheModel( modelname, bPreload ); +} + +static void ScriptPrecacheOther( const char *classname, const char *modelName ) +{ + UTIL_PrecacheOther( classname, modelName ); +} -#ifndef CLIENT_DLL // TODO: Move this? static void ScriptInsertSound( int iType, const Vector &vecOrigin, int iVolume, float flDuration, HSCRIPT hOwner, int soundChannelIndex, HSCRIPT hSoundTarget ) { @@ -678,8 +842,50 @@ static void ScriptEntitiesInSphere( HSCRIPT hTable, int listMax, const Vector &c static void ScriptDecalTrace( HSCRIPT hTrace, const char *decalName ) { - CTraceInfoAccessor *traceInfo = HScriptToClass(hTrace); - UTIL_DecalTrace( &traceInfo->GetTrace(), decalName ); + CScriptGameTrace *tr = HScriptToClass< CScriptGameTrace >( hTrace ); + if ( tr ) + { + UTIL_DecalTrace( tr, decalName ); + } +} + +static HSCRIPT ScriptCreateRope( HSCRIPT hStart, HSCRIPT hEnd, int iStartAttachment, int iEndAttachment, float ropeWidth, const char *pMaterialName, int numSegments, int ropeFlags ) +{ +#ifdef CLIENT_DLL + C_RopeKeyframe *pRope = C_RopeKeyframe::Create( ToEnt( hStart ), ToEnt( hEnd ), iStartAttachment, iEndAttachment, ropeWidth, pMaterialName, numSegments, ropeFlags ); +#else + CRopeKeyframe *pRope = CRopeKeyframe::Create( ToEnt( hStart ), ToEnt( hEnd ), iStartAttachment, iEndAttachment, ropeWidth, pMaterialName, numSegments ); + if (pRope) + pRope->m_RopeFlags |= ropeFlags; // HACKHACK +#endif + + return ToHScript( pRope ); +} + +#ifndef CLIENT_DLL +static HSCRIPT ScriptCreateRopeWithSecondPointDetached( HSCRIPT hStart, int iStartAttachment, int ropeLength, float ropeWidth, const char *pMaterialName, int numSegments, bool initialHang, int ropeFlags ) +{ + CRopeKeyframe *pRope = CRopeKeyframe::CreateWithSecondPointDetached( ToEnt( hStart ), iStartAttachment, ropeLength, ropeWidth, pMaterialName, numSegments, initialHang ); + if (pRope) + pRope->m_RopeFlags |= ropeFlags; // HACKHACK + + return ToHScript( pRope ); +} +#endif + +static void EmitSoundParamsOn( HSCRIPT hParams, HSCRIPT hEnt ) +{ + CBaseEntity *pEnt = ToEnt( hEnt ); + if (!pEnt) + return; + + ScriptEmitSound_t *pParams = (ScriptEmitSound_t*)g_pScriptVM->GetInstanceValue( hParams, GetScriptDescForClass( ScriptEmitSound_t ) ); + if (!pParams) + return; + + CPASAttenuationFilter filter( pEnt, pParams->m_pSoundName ); + + CBaseEntity::EmitSound( filter, pEnt->entindex(), *pParams ); } //----------------------------------------------------------------------------- @@ -732,28 +938,57 @@ bool ScriptIsClient() #endif } +bool ScriptIsWindows() +{ + return IsWindows(); +} + +bool ScriptIsLinux() +{ + return IsLinux(); +} + +bool ScriptIsOSX() +{ + return IsOSX(); +} + +bool ScriptIsPosix() +{ + return IsPosix(); +} + // Notification printing on the right edge of the screen void NPrint( int pos, const char* fmt ) { - engine->Con_NPrintf(pos, fmt); + engine->Con_NPrintf( pos, "%s", 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; + con_nprint_t info; - 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; + 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; + engine->Con_NXPrintf( &info, "%s", fmt ); } +static float IntervalPerTick() +{ + return gpGlobals->interval_per_tick; +} + +static int GetFrameCount() +{ + return gpGlobals->framecount; +} + + //============================================================================= //============================================================================= @@ -790,16 +1025,15 @@ void RegisterSharedScriptFunctions() 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." ); + ScriptRegisterFunction( g_pScriptVM, CreateDamageInfo, "" ); + ScriptRegisterFunction( g_pScriptVM, DestroyDamageInfo, "" ); 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." ); + ScriptRegisterFunction( g_pScriptVM, CreateFireBulletsInfo, "" ); + ScriptRegisterFunction( g_pScriptVM, DestroyFireBulletsInfo, "" ); 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." ); @@ -825,6 +1059,8 @@ void RegisterSharedScriptFunctions() // #ifndef CLIENT_DLL ScriptRegisterFunctionNamed( g_pScriptVM, ScriptInsertSound, "InsertAISound", "Inserts an AI sound." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, CAI_BaseNPC::GetActivityName, "GetActivityName", "Gets the name of the specified activity index." ); #endif // @@ -837,6 +1073,13 @@ void RegisterSharedScriptFunctions() 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, ScriptCreateRope, "CreateRope", "Creates a single rope between two entities. Can optionally follow specific attachments." ); +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCreateRopeWithSecondPointDetached, "CreateRopeWithSecondPointDetached", "Creates a single detached rope hanging from a point. Can optionally follow a specific start attachment." ); +#endif + + ScriptRegisterFunction( g_pScriptVM, EmitSoundParamsOn, "Play EmitSound_t params on an entity." ); + 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." ); @@ -850,12 +1093,14 @@ void RegisterSharedScriptFunctions() #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." ); + ScriptRegisterFunction( g_pScriptVM, IntervalPerTick, "Simulation tick interval" ); + ScriptRegisterFunction( g_pScriptVM, GetFrameCount, "Absolute frame counter" ); + //ScriptRegisterFunction( g_pScriptVM, GetTickCount, "Simulation ticks" ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsWindows, "IsWindows", "Returns true if the game is being run on a Windows machine." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsLinux, "IsLinux", "Returns true if the game is being run on a Linux machine." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsOSX, "IsOSX", "Returns true if the game is being run on an OSX machine." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsPosix, "IsPosix", "Returns true if the game is being run on a Posix machine." ); 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 index ad3192bf..bcf91741 100644 --- a/mp/src/game/shared/mapbase/vscript_funcs_shared.h +++ b/mp/src/game/shared/mapbase/vscript_funcs_shared.h @@ -11,24 +11,62 @@ #pragma once #endif +#include "npcevent.h" +#ifdef GAME_DLL +#include "ai_memory.h" +#endif + +//----------------------------------------------------------------------------- +// Exposes surfacedata_t to VScript +//----------------------------------------------------------------------------- +struct scriptsurfacedata_t : public surfacedata_t +{ +public: + float GetFriction() const { return physics.friction; } + float GetThickness() const { return physics.thickness; } + + float GetJumpFactor() const { return game.jumpFactor; } + char GetMaterialChar() const { return game.material; } + + const char* GetSoundStepLeft() const { return physprops->GetString( sounds.stepleft ); } + const char* GetSoundStepRight() const { return physprops->GetString( sounds.stepright ); } + const char* GetSoundImpactSoft() const { return physprops->GetString( sounds.impactSoft ); } + const char* GetSoundImpactHard() const { return physprops->GetString( sounds.impactHard ); } + const char* GetSoundScrapeSmooth() const { return physprops->GetString( sounds.scrapeSmooth ); } + const char* GetSoundScrapeRough() const { return physprops->GetString( sounds.scrapeRough ); } + const char* GetSoundBulletImpact() const { return physprops->GetString( sounds.bulletImpact ); } + const char* GetSoundRolling() const { return physprops->GetString( sounds.rolling ); } + const char* GetSoundBreak() const { return physprops->GetString( sounds.breakSound ); } + const char* GetSoundStrain() const { return physprops->GetString( sounds.strainSound ); } +}; + //----------------------------------------------------------------------------- // Exposes csurface_t to VScript //----------------------------------------------------------------------------- -class CSurfaceScriptAccessor +class CSurfaceScriptHelper { public: - CSurfaceScriptAccessor( csurface_t &surf ) { m_surf = &surf; m_surfaceData = g_pScriptVM->RegisterInstance( physprops->GetSurfaceData( m_surf->surfaceProps ) ); } - ~CSurfaceScriptAccessor() { delete m_surfaceData; } + // This class is owned by CScriptGameTrace, and cannot be accessed without being initialised in CScriptGameTrace::RegisterSurface() + //CSurfaceScriptHelper() : m_pSurface(NULL), m_hSurfaceData(NULL) {} - // cplane_t stuff - const char* Name() const { return m_surf->name; } - HSCRIPT SurfaceProps() const { return m_surfaceData; } + ~CSurfaceScriptHelper() + { + g_pScriptVM->RemoveInstance( m_hSurfaceData ); + } - void Destroy() { delete this; } + void Init( csurface_t *surf ) + { + m_pSurface = surf; + m_hSurfaceData = g_pScriptVM->RegisterInstance( + reinterpret_cast< scriptsurfacedata_t* >( physprops->GetSurfaceData( m_pSurface->surfaceProps ) ) ); + } + + const char* Name() const { return m_pSurface->name; } + HSCRIPT SurfaceProps() const { return m_hSurfaceData; } private: - csurface_t *m_surf; - HSCRIPT m_surfaceData; + csurface_t *m_pSurface; + HSCRIPT m_hSurfaceData; }; //----------------------------------------------------------------------------- @@ -55,70 +93,228 @@ class CPlaneTInstanceHelper : public IScriptInstanceHelper //----------------------------------------------------------------------------- // Exposes trace_t to VScript //----------------------------------------------------------------------------- -class CTraceInfoAccessor +class CScriptGameTrace : public CGameTrace { public: - ~CTraceInfoAccessor() + CScriptGameTrace() : m_surfaceAccessor(NULL), m_planeAccessor(NULL) { - if (m_surfaceAccessor) - { - CSurfaceScriptAccessor *pScriptSurface = HScriptToClass( m_surfaceAccessor ); - //g_pScriptVM->RemoveInstance( m_surfaceAccessor ); - delete pScriptSurface; - } - - //if (m_planeAccessor) - //{ - // g_pScriptVM->RemoveInstance( m_planeAccessor ); - //} + m_hScriptInstance = g_pScriptVM->RegisterInstance( this ); } - // 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(); } + ~CScriptGameTrace() + { + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + } - float FractionLeftSolid() const { return m_tr.fractionleftsolid; } - int HitGroup() const { return m_tr.hitgroup; } - int PhysicsBone() const { return m_tr.physicsbone; } + if ( m_surfaceAccessor ) + { + g_pScriptVM->RemoveInstance( m_surfaceAccessor ); + } - HSCRIPT Entity() const { return ToHScript(m_tr.m_pEnt); } + if ( m_planeAccessor ) + { + g_pScriptVM->RemoveInstance( m_planeAccessor ); + } + } - int HitBox() const { return m_tr.hitbox; } + void RegisterSurface() + { + m_surfaceHelper.Init( &surface ); + m_surfaceAccessor = g_pScriptVM->RegisterInstance( &m_surfaceHelper ); + } - // 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(); } + void RegisterPlane() + { + m_planeAccessor = g_pScriptVM->RegisterInstance( &plane ); + } - const Vector& StartPos() const { return m_tr.startpos; } - const Vector& EndPos() const { return m_tr.endpos; } + HSCRIPT GetScriptInstance() const + { + return m_hScriptInstance; + } - float Fraction() const { return m_tr.fraction; } +public: + float FractionLeftSolid() const { return fractionleftsolid; } + int HitGroup() const { return hitgroup; } + int PhysicsBone() const { return physicsbone; } - int Contents() const { return m_tr.contents; } - int DispFlags() const { return m_tr.dispFlags; } + HSCRIPT Entity() const { return ToHScript( m_pEnt ); } + int HitBox() const { return hitbox; } - bool AllSolid() const { return m_tr.allsolid; } - bool StartSolid() const { return m_tr.startsolid; } + const Vector& StartPos() const { return startpos; } + const Vector& EndPos() const { return endpos; } - HSCRIPT Surface() { return m_surfaceAccessor; } - void SetSurface( HSCRIPT hSurfAccessor ) { m_surfaceAccessor = hSurfAccessor; } + float Fraction() const { return fraction; } - HSCRIPT Plane() { return m_planeAccessor; } - void SetPlane( HSCRIPT hPlaneAccessor ) { m_planeAccessor = hPlaneAccessor; } + int Contents() const { return contents; } + int DispFlags() const { return dispFlags; } - trace_t &GetTrace() { return m_tr; } - void Destroy() { delete this; } + bool AllSolid() const { return allsolid; } + bool StartSolid() const { return startsolid; } + + HSCRIPT Surface() const { return m_surfaceAccessor; } + HSCRIPT Plane() const { return m_planeAccessor; } + + void Destroy() { delete this; } private: - trace_t m_tr; - HSCRIPT m_surfaceAccessor; HSCRIPT m_planeAccessor; + HSCRIPT m_hScriptInstance; + + CSurfaceScriptHelper m_surfaceHelper; + + CScriptGameTrace( const CScriptGameTrace& v ); }; +//----------------------------------------------------------------------------- +// Exposes animevent_t to VScript +//----------------------------------------------------------------------------- +struct scriptanimevent_t : public animevent_t +{ + int GetEvent() { return event; } + void SetEvent( int nEvent ) { event = nEvent; } + + const char *GetOptions() { return options; } + void SetOptions( const char *pOptions ) { options = pOptions; } + + float GetCycle() { return cycle; } + void SetCycle( float flCycle ) { cycle = flCycle; } + + float GetEventTime() { return eventtime; } + void SetEventTime( float flEventTime ) { eventtime = flEventTime; } + + int GetType() { return type; } + void SetType( int nType ) { eventtime = type; } + + HSCRIPT GetSource() { return ToHScript( pSource ); } + void SetSource( HSCRIPT hSource ) + { + CBaseEntity *pEnt = ToEnt( hSource ); + if (pEnt) + pSource = pEnt->GetBaseAnimating(); + } +}; + +class CAnimEventTInstanceHelper : public IScriptInstanceHelper +{ + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); +}; + +//----------------------------------------------------------------------------- +// Exposes EmitSound_t to VScript +//----------------------------------------------------------------------------- +struct ScriptEmitSound_t : public EmitSound_t +{ + int GetChannel() { return m_nChannel; } + void SetChannel( int nChannel ) { m_nChannel = nChannel; } + + const char *GetSoundName() { return m_pSoundName; } + void SetSoundName( const char *pSoundName ) { m_pSoundName = pSoundName; } + + float GetVolume() { return m_flVolume; } + void SetVolume( float flVolume ) { m_flVolume = flVolume; } + + int GetSoundLevel() { return m_SoundLevel; } + void SetSoundLevel( int iSoundLevel ) { m_SoundLevel = (soundlevel_t)iSoundLevel; } + + int GetFlags() { return m_nFlags; } + void SetFlags( int nFlags ) { m_nFlags = nFlags; } + + int GetSpecialDSP() { return m_nSpecialDSP; } + void SetSpecialDSP( int nSpecialDSP ) { m_nSpecialDSP = nSpecialDSP; } + + bool HasOrigin() { return m_pOrigin ? true : false; } + const Vector &GetOrigin() { return m_pOrigin ? *m_pOrigin : vec3_origin; } + void SetOrigin( const Vector &origin ) { static Vector tempOrigin; tempOrigin = origin; m_pOrigin = &tempOrigin; } + void ClearOrigin() { m_pOrigin = NULL; } + + float GetSoundTime() { return m_flSoundTime; } + void SetSoundTime( float flSoundTime ) { m_flSoundTime = flSoundTime; } + + float GetEmitCloseCaption() { return m_bEmitCloseCaption; } + void SetEmitCloseCaption( bool bEmitCloseCaption ) { m_bEmitCloseCaption = bEmitCloseCaption; } + + float GetWarnOnMissingCloseCaption() { return m_bWarnOnMissingCloseCaption; } + void SetWarnOnMissingCloseCaption( bool bWarnOnMissingCloseCaption ) { m_bWarnOnMissingCloseCaption = bWarnOnMissingCloseCaption; } + + float GetWarnOnDirectWaveReference() { return m_bWarnOnDirectWaveReference; } + void SetWarnOnDirectWaveReference( bool bWarnOnDirectWaveReference ) { m_bWarnOnDirectWaveReference = bWarnOnDirectWaveReference; } + + int GetSpeakerEntity() { return m_nSpeakerEntity; } + void SetSpeakerEntity( int nSpeakerEntity ) { m_nSpeakerEntity = nSpeakerEntity; } + + int GetSoundScriptHandle() { return m_hSoundScriptHandle; } + void SetSoundScriptHandle( int hSoundScriptHandle ) { m_hSoundScriptHandle = hSoundScriptHandle; } +}; + +//----------------------------------------------------------------------------- +// Exposes CUserCmd to VScript +//----------------------------------------------------------------------------- +class CScriptUserCmd : public CUserCmd +{ +public: + 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; } +}; + +#ifdef GAME_DLL +//----------------------------------------------------------------------------- +// Exposes AI_EnemyInfo_t to VScript +//----------------------------------------------------------------------------- +struct Script_AI_EnemyInfo_t : public AI_EnemyInfo_t +{ + #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 + #endif diff --git a/mp/src/game/shared/mapbase/vscript_singletons.cpp b/mp/src/game/shared/mapbase/vscript_singletons.cpp index 0f7fcec4..c5cca45b 100644 --- a/mp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/mp/src/game/shared/mapbase/vscript_singletons.cpp @@ -12,14 +12,29 @@ #include #include #include "ammodef.h" +#include "tier1/utlcommon.h" #ifndef CLIENT_DLL -#include "usermessages.h" #include "ai_squad.h" #endif // !CLIENT_DLL +#include "usermessages.h" #include "filesystem.h" #include "igameevents.h" +#include "engine/ivdebugoverlay.h" + +#ifdef CLIENT_DLL +#include "IEffects.h" +#include "fx.h" +#include "itempents.h" +#include "c_te_legacytempents.h" +#include "iefx.h" +#include "dlight.h" + +#if !defined(NO_STEAM) +#include "steam/steam_api.h" +#endif +#endif #include "vscript_singletons.h" @@ -351,21 +366,37 @@ END_SCRIPTDESC(); //============================================================================= // Game Event Listener // Based on Source 2 API +// +// NOTE: In Source 2 vscript (Lua) event listener contexts are tables that are +// passed to the callback function as the call environment. +// In mapbase implementation these are string identifiers because unlike Lua, +// Squirrel has closure methods such as 'bindenv' which can bind functions to specified environments. //============================================================================= + +// Define to use the older code that loads all events manually independent from the game event manager. +// Otherwise access event descriptors directly from engine. +//#define USE_OLD_EVENT_DESCRIPTORS 1 + class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSystem { public: - CScriptGameEventListener() : m_bActive(false) {} + CScriptGameEventListener() : m_bActive(false) + { +#ifdef _DEBUG + m_nEventTick = 0; +#endif + } + ~CScriptGameEventListener() { StopListeningForEvent(); } - intptr_t ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); + int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); void StopListeningForEvent(); public: - static bool StopListeningToGameEvent( intptr_t listener ); + static bool StopListeningToGameEvent( int listener ); static void StopListeningToAllGameEvents( const char* szContext ); public: @@ -373,88 +404,80 @@ public: void LevelShutdownPreEntity(); private: - bool m_bActive; - const char *m_pszContext; + //int m_index; HSCRIPT m_hCallback; + unsigned int m_iContextHash; + bool m_bActive; +#ifdef _DEBUG + int m_nEventTick; +#endif - static const char *FindContext( const char *szContext, CScriptGameEventListener *pIgnore = NULL ); - //inline const char *GetContext( CScriptGameEventListener *p ); - //inline const char *GetContext(); + static StringHashFunctor Hash; + static inline unsigned int HashContext( const char* c ) { return c ? Hash(c) : 0; } + + inline int GetIndex() + { + Assert( sizeof(CScriptGameEventListener*) == sizeof(int) ); + return reinterpret_cast(this); + } public: - static void DumpEventListeners(); -#ifndef CLIENT_DLL + enum // event data types, dependant on engine definitions + { + TYPE_LOCAL = 0, + TYPE_STRING = 1, + TYPE_FLOAT = 2, + TYPE_LONG = 3, + TYPE_SHORT = 4, + TYPE_BYTE = 5, + TYPE_BOOL = 6 + }; + static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); + +#ifdef USE_OLD_EVENT_DESCRIPTORS static void LoadAllEvents(); static void LoadEventsFromFile( const char *filename, const char *pathID = NULL ); - static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); -#endif // !CLIENT_DLL + static CUtlMap< unsigned int, KeyValues* > s_GameEvents; + static CUtlVector< KeyValues* > s_LoadedFiles; +#endif -private: -#ifndef CLIENT_DLL - static CUtlVector< KeyValues* > s_GameEvents; -#endif // !CLIENT_DLL - static CUtlVectorAutoPurge< CScriptGameEventListener* > s_GameEventListeners; +public: + //static int g_nIndexCounter; + static CUtlVectorAutoPurge< CScriptGameEventListener* > s_Listeners; +#if _DEBUG + static void DumpEventListeners(); +#endif }; -#ifndef CLIENT_DLL -CUtlVector< KeyValues* > CScriptGameEventListener::s_GameEvents; -#endif // !CLIENT_DLL -CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_GameEventListeners; +CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_Listeners; +StringHashFunctor CScriptGameEventListener::Hash; -#if 0 +#ifdef USE_OLD_EVENT_DESCRIPTORS +CUtlMap< unsigned int, KeyValues* > CScriptGameEventListener::s_GameEvents( DefLessFunc(unsigned int) ); +CUtlVector< KeyValues* > CScriptGameEventListener::s_LoadedFiles; +#endif + + +#if _DEBUG #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 +#else CON_COMMAND_F( dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) { CScriptGameEventListener::DumpEventListeners(); } -#endif // CLIENT_DLL +#endif #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; -} +#ifdef USE_OLD_EVENT_DESCRIPTORS //----------------------------------------------------------------------------- // Executed in LevelInitPreEntity //----------------------------------------------------------------------------- -#ifndef CLIENT_DLL void CScriptGameEventListener::LoadAllEvents() { // Listed in the same order they are loaded in GameEventManager @@ -473,10 +496,11 @@ void CScriptGameEventListener::LoadAllEvents() }; // Destroy old KeyValues - if ( s_GameEvents.Count() ) + if ( s_LoadedFiles.Count() ) { - for ( int i = 0; i < s_GameEvents.Count(); ++i ) - s_GameEvents[i]->deleteThis(); + for ( int i = s_LoadedFiles.Count(); i--; ) + s_LoadedFiles[i]->deleteThis(); + s_LoadedFiles.Purge(); s_GameEvents.Purge(); } @@ -496,26 +520,43 @@ void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const c if ( !pKV->LoadFromFile( filesystem, filename, pathID ) ) { - // CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Failed to load file %s, %s\n", filename, pathID ); + // CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Failed to load file [%s]%s\n", pathID, filename ); 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. + int count = 0; + 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" ) ) + if ( !V_stricmp( szVal, "string" ) ) { - sub->SetInt( NULL, 0 ); + sub->SetInt( NULL, TYPE_STRING ); + } + else if ( !V_stricmp( szVal, "bool" ) ) + { + sub->SetInt( NULL, TYPE_BOOL ); + } + else if ( !V_stricmp( szVal, "byte" ) ) + { + sub->SetInt( NULL, TYPE_BYTE ); + } + else if ( !V_stricmp( szVal, "short" ) ) + { + sub->SetInt( NULL, TYPE_SHORT ); + } + else if ( !V_stricmp( szVal, "long" ) ) + { + sub->SetInt( NULL, TYPE_LONG ); } else if ( !V_stricmp( szVal, "float" ) ) { - sub->SetFloat( NULL, 0.0f ); + sub->SetInt( NULL, TYPE_FLOAT ); } } // none : value is not networked @@ -527,104 +568,156 @@ void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const c // float : float, 32 bit } - CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded %s, %s\n", filename, pathID ); + // Store event subkeys + // Replace key so modevents can overwrite gameevents. + // It does not check for hash collisions, however. + s_GameEvents.InsertOrReplace( Hash( key->GetName() ), key ); + ++count; + } - s_GameEvents.AddToTail(pKV); + // Store files (allocated KV) + s_LoadedFiles.AddToTail( pKV ); + + CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded [%s]%s (%i)\n", pathID, filename, count ); } +#endif + +#if _DEBUG +void CScriptGameEventListener::DumpEventListeners() +{ + CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump start\n" ); + CGMsg( 0, CON_GROUP_VSCRIPT, "# ID CONTEXT\n" ); + FOR_EACH_VEC( s_Listeners, i ) + { + CGMsg( 0, CON_GROUP_VSCRIPT, " %d : %d : %u\n", i, + s_Listeners[i]->GetIndex(), + s_Listeners[i]->m_iContextHash ); + } + CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump end\n" ); +} +#endif + +void CScriptGameEventListener::LevelShutdownPreEntity() +{ + s_Listeners.FindAndFastRemove(this); + delete this; +} + +void CScriptGameEventListener::FireGameEvent( IGameEvent *event ) +{ +#ifdef _DEBUG + m_nEventTick = gpGlobals->tickcount; +#endif + ScriptVariant_t hTable; + g_pScriptVM->CreateTable( hTable ); + WriteEventData( event, hTable ); + g_pScriptVM->SetValue( hTable, "game_event_listener", GetIndex() ); + // g_pScriptVM->SetValue( hTable, "game_event_name", event->GetName() ); + g_pScriptVM->ExecuteFunction( m_hCallback, &hTable, 1, NULL, NULL, true ); + g_pScriptVM->ReleaseScript( hTable ); +} + +struct CGameEventDescriptor +{ + byte _0[36]; + KeyValues *m_pEventKeys; + //byte _1[22]; +}; + +class CGameEvent__// : public IGameEvent +{ +public: + virtual ~CGameEvent__(); // [0] + CGameEventDescriptor *m_pDescriptor; // 0x04 + //KeyValues *m_pEventData; // 0x08 +}; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CScriptGameEventListener::WriteEventData( IGameEvent *event, HSCRIPT hTable ) { - // TODO: Something more efficient than iterating through all the events that ever exist one by one +#ifdef USE_OLD_EVENT_DESCRIPTORS + int i = s_GameEvents.Find( Hash( event->GetName() ) ); + if ( i == s_GameEvents.InvalidIndex() ) + return; + KeyValues *pKV = s_GameEvents[i]; +#endif - const char *szEvent = event->GetName(); - for ( int i = 0; i < s_GameEvents.Count(); ++i ) +#if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS) + try { - KeyValues *pKV = s_GameEvents[i]; - for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) +#endif + +#if !defined(USE_OLD_EVENT_DESCRIPTORS) + KeyValues *pKV = reinterpret_cast< CGameEvent__* >(event)->m_pDescriptor->m_pEventKeys; +#endif + + for ( KeyValues *sub = pKV->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + const char *szKey = sub->GetName(); + switch ( sub->GetInt() ) { - 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; - } + case TYPE_LOCAL: + case TYPE_STRING: g_pScriptVM->SetValue( hTable, szKey, event->GetString( szKey ) ); break; + case TYPE_FLOAT: g_pScriptVM->SetValue( hTable, szKey, event->GetFloat ( szKey ) ); break; + case TYPE_BOOL: g_pScriptVM->SetValue( hTable, szKey, event->GetBool ( szKey ) ); break; + default: g_pScriptVM->SetValue( hTable, szKey, event->GetInt ( szKey ) ); } } -} -#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; - } - } + +#if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS) + // Access a bunch of KeyValues functions to validate it is the correct address. + // This may not always throw an exception when it is incorrect, but eventually it will. } - return NULL; + catch (...) + { + // CGameEvent or CGameEventDescriptor offsets did not match! + // This should mean these were modified in engine.dll. + // + // Implement this utility yourself by adding a function to get event descriptor keys + // either on CGameEventManager or on CGameEvent interfaces. + // On CGameEventManager downcast IGameEvent input to CGameEvent, then return event->descriptor->keys + // On CGameEvent return (member) descriptor->keys + // + // Finally assign it to pKV above. + + Warning("CScriptGameEventListener::WriteEventData internal error\n"); + Assert(0); + } +#endif } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -intptr_t CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) +int CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) { - m_bActive = true; + bool bValid; - char *psz; + if ( gameeventmanager && hFunc ) +#ifdef CLIENT_DLL + bValid = gameeventmanager->AddListener( this, szEvent, false ); +#else + bValid = gameeventmanager->AddListener( this, szEvent, true ); +#endif + else bValid = false; - if ( szContext && *szContext ) + if ( bValid ) { - 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 ); - } - } + m_iContextHash = HashContext( szContext ); + m_hCallback = hFunc; + m_bActive = true; + + s_Listeners.AddToTail( this ); + + return GetIndex(); } else { - psz = NULL; + delete this; + return 0; } - - 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 } //----------------------------------------------------------------------------- @@ -636,40 +729,37 @@ void CScriptGameEventListener::StopListeningForEvent() 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; + m_bActive = false; if ( gameeventmanager ) gameeventmanager->RemoveListener( this ); - m_bActive = false; +#ifdef _DEBUG + // Event listeners are iterated forwards in the game event manager, + // removing while iterating will cause it to skip one listener. + // + // Fix this in engine without altering any behaviour by + // changing event exeuction order to tail->head, + // changing listener removal to tail->head, + // changing listener addition to head + if ( m_nEventTick == gpGlobals->tickcount ) + { + Warning("CScriptGameEventListener stopped in the same frame it was fired. This will break other event listeners!\n"); + } +#endif } //----------------------------------------------------------------------------- // Stop the specified event listener. //----------------------------------------------------------------------------- -bool CScriptGameEventListener::StopListeningToGameEvent( intptr_t listener ) +bool CScriptGameEventListener::StopListeningToGameEvent( int listener ) { CScriptGameEventListener *p = reinterpret_cast(listener); // INT_TO_POINTER - bool bRemoved = s_GameEventListeners.FindAndFastRemove(p); + bool bRemoved = s_Listeners.FindAndFastRemove(p); if ( bRemoved ) { delete p; @@ -683,42 +773,16 @@ bool CScriptGameEventListener::StopListeningToGameEvent( intptr_t listener ) //----------------------------------------------------------------------------- void CScriptGameEventListener::StopListeningToAllGameEvents( const char* szContext ) { - if ( szContext ) + unsigned int hash = HashContext( szContext ); + for ( int i = s_Listeners.Count(); i--; ) { - if ( *szContext ) + CScriptGameEventListener *pCur = s_Listeners[i]; + if ( pCur->m_iContextHash == hash ) { - // 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; - } - } + s_Listeners.FastRemove(i); + delete pCur; } } -#if 0 - if ( !szContext ) - { - for ( int i = s_GameEventListeners.Count(); i--; ) - delete s_GameEventListeners[i]; - s_GameEventListeners.Purge(); - } -#endif } //============================================================================= @@ -792,9 +856,12 @@ static void FireGameEventLocal( const char* szEvent, HSCRIPT hTable ) } #endif // !CLIENT_DLL +static ScriptHook_t g_Hook_OnSave; +static ScriptHook_t g_Hook_OnRestore; + //============================================================================= // Save/Restore Utility -// Based on Source 2 API +// Based on L4D2 API //============================================================================= class CScriptSaveRestoreUtil : public CAutoGameSystem { @@ -803,94 +870,88 @@ public: static void RestoreTable( const char *szId, HSCRIPT hTable ); static void ClearSavedTable( const char *szId ); -// IGameSystem interface -public: +public: // IGameSystem + void OnSave() { if ( g_pScriptVM ) { + if ( GetScriptHookManager().IsEventHooked( "OnSave" ) ) + g_Hook_OnSave.Call( NULL, NULL, NULL ); + + // Legacy hook HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnSave" ); if ( hFunc ) { g_pScriptVM->Call( hFunc ); + g_pScriptVM->ReleaseScript( hFunc ); } } } +#ifdef CLIENT_DLL + // On the client, OnRestore() is called before VScript is actually restored, so this has to be called manually from VScript save/restore instead + void OnVMRestore() +#else void OnRestore() +#endif { if ( g_pScriptVM ) { + if ( GetScriptHookManager().IsEventHooked( "OnRestore" ) ) + g_Hook_OnRestore.Call( NULL, NULL, NULL ); + + // Legacy hook HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnRestore" ); if ( hFunc ) { g_pScriptVM->Call( hFunc ); + g_pScriptVM->ReleaseScript( hFunc ); } } } void Shutdown() { - FOR_EACH_VEC( m_aKeyValues, i ) - m_aKeyValues[i]->deleteThis(); - m_aKeyValues.Purge(); - m_aContext.PurgeAndDeleteElements(); + FOR_EACH_MAP_FAST( m_Lookup, i ) + m_Lookup[i]->deleteThis(); + m_Lookup.Purge(); } private: - static int GetIndexForContext( const char *szId ); - - // indices must match, always remove keeping order - static CUtlStringList m_aContext; - static CUtlVector m_aKeyValues; + static StringHashFunctor Hash; + static CUtlMap< unsigned int, KeyValues* > m_Lookup; } g_ScriptSaveRestoreUtil; -CUtlStringList CScriptSaveRestoreUtil::m_aContext; -CUtlVector CScriptSaveRestoreUtil::m_aKeyValues; - -int CScriptSaveRestoreUtil::GetIndexForContext( const char *szId ) +#ifdef CLIENT_DLL +void VScriptSaveRestoreUtil_OnVMRestore() { - int idx = -1; - FOR_EACH_VEC( m_aContext, i ) - { - if ( !V_stricmp( szId, m_aContext[i] ) ) - { - idx = i; - break; - } - } - return idx; + g_ScriptSaveRestoreUtil.OnVMRestore(); } +#endif + +CUtlMap< unsigned int, KeyValues* > CScriptSaveRestoreUtil::m_Lookup( DefLessFunc(unsigned int) ); +StringHashFunctor CScriptSaveRestoreUtil::Hash; //----------------------------------------------------------------------------- // Store a table with primitive values that will persist across level transitions and save loads. +// Case sensitive //----------------------------------------------------------------------------- void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable ) { - int idx = GetIndexForContext(szId); - KeyValues *pKV; + unsigned int hash = Hash(szId); - if ( idx == -1 ) + int idx = m_Lookup.Find( hash ); + if ( idx == m_Lookup.InvalidIndex() ) { 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); - } + m_Lookup.Insert( hash, pKV ); } else { - pKV = m_aKeyValues[idx]; + pKV = m_Lookup[idx]; pKV->Clear(); } @@ -916,20 +977,14 @@ void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable ) //----------------------------------------------------------------------------- void CScriptSaveRestoreUtil::RestoreTable( const char *szId, HSCRIPT hTable ) { - int idx = GetIndexForContext(szId); - - KeyValues *pKV; - - if ( idx == -1 ) + int idx = m_Lookup.Find( Hash(szId) ); + if ( idx == m_Lookup.InvalidIndex() ) { // DevWarning( 2, "RestoreTable could not find saved table with context '%s'\n", szId ); return; } - else - { - pKV = m_aKeyValues[idx]; - } + KeyValues *pKV = m_Lookup[idx]; FOR_EACH_SUBKEY( pKV, key ) { switch ( key->GetDataType() ) @@ -946,40 +1001,39 @@ void CScriptSaveRestoreUtil::RestoreTable( const char *szId, HSCRIPT hTable ) //----------------------------------------------------------------------------- void CScriptSaveRestoreUtil::ClearSavedTable( const char *szId ) { - int idx = GetIndexForContext(szId); - - if ( idx == -1 ) + int idx = m_Lookup.Find( Hash(szId) ); + if ( idx != m_Lookup.InvalidIndex() ) + { + m_Lookup[idx]->deleteThis(); + m_Lookup.RemoveAt( idx ); + } + else { // 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_READ_SIZE (64 * 1024 * 1024) // 64MB #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 { + // A singleton class with all static members is used to be able to free the read string on level shutdown, + // and register script funcs directly. Same reason applies to CScriptSaveRestoreUtil 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 ); + static bool FileWrite( const char *szFile, const char *szInput ); + static const char *FileRead( const char *szFile ); + static bool FileExists( const char *szFile ); // 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 ); + static bool KeyValuesWrite( const char *szFile, HSCRIPT hInput ); + static HSCRIPT KeyValuesRead( const char *szFile ); void LevelShutdownPostEntity() { @@ -988,27 +1042,19 @@ public: 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; + static char *m_pszReturnReadFile; } g_ScriptReadWrite; -const char *CScriptReadWriteFile::m_pszReturnReadFile = NULL; -//const char *CScriptReadWriteFile::m_pszReturnCRC32 = NULL; +char *CScriptReadWriteFile::m_pszReturnReadFile = NULL; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -bool CScriptReadWriteFile::ScriptFileWrite( const char *szFile, const char *szInput ) +bool CScriptReadWriteFile::FileWrite( const char *szFile, const char *szInput ) { size_t len = strlen(szInput); if ( len > SCRIPT_MAX_FILE_WRITE_SIZE ) @@ -1043,7 +1089,7 @@ bool CScriptReadWriteFile::ScriptFileWrite( const char *szFile, const char *szIn //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -const char *CScriptReadWriteFile::ScriptFileRead( const char *szFile ) +const char *CScriptReadWriteFile::FileRead( const char *szFile ) { char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); @@ -1061,19 +1107,49 @@ const char *CScriptReadWriteFile::ScriptFileRead( const char *szFile ) return NULL; } - CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); - if ( !g_pFullFileSystem->ReadFile( pszFullName, SCRIPT_RW_PATH_ID, buf, SCRIPT_MAX_FILE_READ_SIZE ) ) + FileHandle_t file = g_pFullFileSystem->Open( pszFullName, "rb", SCRIPT_RW_PATH_ID ); + if ( !file ) { return NULL; } - // first time calling, allocate - if ( !m_pszReturnReadFile ) - m_pszReturnReadFile = new char[SCRIPT_MAX_FILE_READ_SIZE]; + // Close the previous buffer + if (m_pszReturnReadFile) + g_pFullFileSystem->FreeOptimalReadBuffer( m_pszReturnReadFile ); - V_strncpy( const_cast(m_pszReturnReadFile), (const char*)buf.Base(), buf.Size() ); - buf.Purge(); - return m_pszReturnReadFile; + unsigned bufSize = g_pFullFileSystem->GetOptimalReadSize( file, size + 2 ); + m_pszReturnReadFile = (char*)g_pFullFileSystem->AllocOptimalReadBuffer( file, bufSize ); + + bool bRetOK = ( g_pFullFileSystem->ReadEx( m_pszReturnReadFile, bufSize, size, file ) != 0 ); + g_pFullFileSystem->Close( file ); // close file after reading + + if ( bRetOK ) + { + m_pszReturnReadFile[size] = 0; // null terminate file as EOF + //buffer[size+1] = 0; // double NULL terminating in case this is a unicode file + return m_pszReturnReadFile; + } + else + { + return NULL; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CScriptReadWriteFile::FileExists( 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; + } + + return g_pFullFileSystem->FileExists( pszFullName, SCRIPT_RW_PATH_ID ); } //----------------------------------------------------------------------------- @@ -1101,7 +1177,7 @@ const char *CScriptReadWriteFile::CRC32_Checksum( const char *szFilename ) //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -bool CScriptReadWriteFile::ScriptKeyValuesWrite( const char *szFile, HSCRIPT hInput ) +bool CScriptReadWriteFile::KeyValuesWrite( const char *szFile, HSCRIPT hInput ) { KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hInput ); if (!pKV) @@ -1114,7 +1190,7 @@ bool CScriptReadWriteFile::ScriptKeyValuesWrite( const char *szFile, HSCRIPT hIn 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) ); + DevWarning( 2, "Input is too large for a ScriptKeyValuesWrite ( %s / %d MB )\n", V_pretifymem(buf.Size(),2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) ); buf.Purge(); return false; } @@ -1143,7 +1219,7 @@ bool CScriptReadWriteFile::ScriptKeyValuesWrite( const char *szFile, HSCRIPT hIn //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -HSCRIPT CScriptReadWriteFile::ScriptKeyValuesRead( const char *szFile ) +HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile ) { char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); @@ -1157,7 +1233,7 @@ HSCRIPT CScriptReadWriteFile::ScriptKeyValuesRead( const char *szFile ) 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 ); + DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptKeyValuesRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE ); return NULL; } @@ -1172,162 +1248,2056 @@ HSCRIPT CScriptReadWriteFile::ScriptKeyValuesRead( const char *szFile ) 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 +// Network message helper +// (Unique to mapbase) +// +// Uses usermessages for server to client, UserCmd for client to server communication. +// The custom message name is hashed and sent as word with the message. //============================================================================= -#ifndef CLIENT_DLL -class CNetMsgScriptHelper + +static CNetMsgScriptHelper scriptnetmsg; +CNetMsgScriptHelper *g_ScriptNetMsg = &scriptnetmsg; + +#ifdef GAME_DLL +#define m_MsgIn_() m_MsgIn-> +#define DLL_LOC_STR "[Server]" +#else +#define m_MsgIn_() m_MsgIn. +#define DLL_LOC_STR "[Client]" +#endif + +#ifdef GAME_DLL +#define SCRIPT_NETMSG_WRITE_FUNC +#else +#define SCRIPT_NETMSG_WRITE_FUNC if ( m_bWriteIgnore ) { return; } +#endif + +#ifdef _DEBUG +#ifdef GAME_DLL +ConVar script_net_debug("script_net_debug", "0"); +#define DebugNetMsg( l, ... ) do { if (script_net_debug.GetInt() >= l) ConColorMsg( Color(100, 225, 255, 255), __VA_ARGS__ ); } while (0); +#else +ConVar script_net_debug("script_net_debug_client", "0"); +#define DebugNetMsg( l, ... ) do { if (script_net_debug.GetInt() >= l) ConColorMsg( Color(100, 225, 175, 255), __VA_ARGS__ ); } while (0); +#endif +#define DebugWarning(...) Warning( __VA_ARGS__ ) +#else +#define DebugNetMsg(...) (void)(0) +#define DebugWarning(...) (void)(0) +#endif + + +// Keep track of message names to print on failure +#ifdef _DEBUG +struct NetMsgHook_t { -private: - CRecipientFilter filter; - bf_write message; - byte data_msg[ MAX_USER_MSG_DATA ]; - - inline void SendMsg( bf_write *bf ) + void Set( const char *s ) { - bf_read buffer = bf_read(); - buffer.StartReading( message.GetData(), message.m_nDataBytes ); - bf->WriteBitsFromBuffer( &buffer, message.GetNumBitsWritten() ); - engine->MessageEnd(); + hash = CNetMsgScriptHelper::Hash( s ); + name = strdup(s); } -public: - inline void Reset() + ~NetMsgHook_t() { - message.StartWriting( data_msg, sizeof(data_msg) ); - filter.Reset(); + free( name ); } - void SendUserMessage( HSCRIPT player, const char *msg, bool bReliable ) + int hash; + char *name; +}; + +CUtlVector< NetMsgHook_t > g_NetMsgHooks; + +static const char *GetNetMsgName( int hash ) +{ + FOR_EACH_VEC( g_NetMsgHooks, i ) { - 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 ) ); + if ( g_NetMsgHooks[i].hash == hash ) + return g_NetMsgHooks[i].name; } + return 0; +} - void SendEntityMessage( HSCRIPT hEnt, bool bReliable ) +static const char *HasNetMsgCollision( int hash, const char *ignore ) +{ + FOR_EACH_VEC( g_NetMsgHooks, i ) { - CBaseEntity *entity = ToEnt(hEnt); - if ( !entity ) + if ( g_NetMsgHooks[i].hash == hash && V_strcmp( g_NetMsgHooks[i].name, ignore ) != 0 ) { - 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 ); + return g_NetMsgHooks[i].name; } } + return 0; +} +#endif // _DEBUG - void AddRecipientsByPVS( const Vector &pos ) + + +inline int CNetMsgScriptHelper::Hash( const char *key ) +{ + int hash = HashStringCaseless( key ); + Assert( hash < (1 << SCRIPT_NETMSG_HEADER_BITS) ); + return hash; +} + +void CNetMsgScriptHelper::WriteToBuffer( bf_write *bf ) +{ +#ifdef CLIENT_DLL + Assert( m_nQueueCount < ( 1 << SCRIPT_NETMSG_QUEUE_BITS ) ); + bf->WriteUBitLong( m_nQueueCount, SCRIPT_NETMSG_QUEUE_BITS ); + + DebugNetMsg( 2, DLL_LOC_STR " CNetMsgScriptHelper::WriteToBuffer() count(%d) size(%d)\n", + m_nQueueCount, m_MsgOut.GetNumBitsWritten() + SCRIPT_NETMSG_QUEUE_BITS ); +#endif + + bf->WriteBits( m_MsgOut.GetData(), m_MsgOut.GetNumBitsWritten() ); +} + +//----------------------------------------------------------------------------- +// Reset the current network message buffer +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::Reset() +{ + m_MsgOut.StartWriting( m_MsgData, sizeof(m_MsgData), 0 ); +#ifdef GAME_DLL + m_filter.Reset(); +#else + m_iLastBit = 0; +#endif +} + +//----------------------------------------------------------------------------- +// Create the storage for the receiver callback functions. +// Functions are handled in the VM, the storage table is here. +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::InitPostVM() +{ + ScriptVariant_t hHooks; + g_pScriptVM->CreateTable( hHooks ); + m_Hooks = (HSCRIPT)hHooks; +} + +void CNetMsgScriptHelper::LevelShutdownPreVM() +{ + Reset(); + if ( m_Hooks ) + g_pScriptVM->ReleaseScript( m_Hooks ); + m_Hooks = NULL; + +#ifdef CLIENT_DLL + m_bWriteReady = m_bWriteIgnore = false; + m_MsgIn.Reset(); +#else + m_MsgIn = NULL; +#endif + +#ifdef _DEBUG + g_NetMsgHooks.Purge(); +#endif +} + +#ifdef CLIENT_DLL + +bool CNetMsgScriptHelper::Init() // IGameSystem +{ + usermessages->HookMessage( "ScriptMsg", __MsgFunc_ScriptMsg ); + return true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::__MsgFunc_ScriptMsg( bf_read &msg ) +{ + g_ScriptNetMsg->ReceiveMessage( msg ); +} + +#endif // CLIENT_DLL + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +#ifdef GAME_DLL +void CNetMsgScriptHelper::ReceiveMessage( bf_read *msg, CBaseEntity *pPlayer ) +{ + m_MsgIn = msg; +#else +void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) +{ + m_MsgIn.StartReading( msg.m_pData, msg.m_nDataBytes ); +#endif + + DebugNetMsg( 2, DLL_LOC_STR " %s()\n", __FUNCTION__ ); + + // Don't do anything if there's no VM here. This can happen if a message from the server goes to a VM-less client, or vice versa. + if ( !g_pScriptVM ) { - filter.AddRecipientsByPVS(pos); + CGWarning( 0, CON_GROUP_VSCRIPT, DLL_LOC_STR " CNetMsgScriptHelper: No VM on receiving side\n" ); + return; } - void AddAllPlayers() +#ifdef GAME_DLL + int count = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_QUEUE_BITS ); + DebugNetMsg( 2, " msg count %d\n", count ); + while ( count-- ) +#endif { - filter.AddAllPlayers(); - } + int hash = m_MsgIn_()ReadWord(); -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 ) +#ifdef _DEBUG + const char *msgName = GetNetMsgName( hash ); + DebugNetMsg( 2, " -- begin msg [%d]%s\n", hash, msgName ); +#endif + + ScriptVariant_t hfn; + if ( g_pScriptVM->GetValue( m_Hooks, hash, &hfn ) ) { - EHANDLE hEnt = pEnt; - int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; - iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS); +#ifdef GAME_DLL + if ( g_pScriptVM->Call( hfn, NULL, true, NULL, pPlayer->m_hScriptInstance ) == SCRIPT_ERROR ) +#else + if ( g_pScriptVM->ExecuteFunction( hfn, NULL, 0, NULL, NULL, true ) == SCRIPT_ERROR ) +#endif + { +#ifdef _DEBUG + DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback '%s'\n", GetNetMsgName( hash ) ); +#else + DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback [%d]\n", hash ); +#endif + } + g_pScriptVM->ReleaseValue( hfn ); } else { - iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE; + DevWarning( 1, DLL_LOC_STR " NetMsg hook not found [%d]\n", hash ); } - message.WriteLong( iEncodedEHandle ); + + DebugNetMsg( 2, " -- end msg\n" ); + } +} + +//----------------------------------------------------------------------------- +// Start writing new custom network message +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::Start( const char *msg ) +{ + if ( !msg || !msg[0] ) + { + g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" ); + return; } -} g_ScriptNetMsg; + DebugNetMsg( 1, DLL_LOC_STR " %s() [%d]%s\n", __FUNCTION__, Hash( msg ), msg ); -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, "" ) +#ifdef CLIENT_DLL + // Client can write multiple messages in a frame before the usercmd is sent, + // this queue system ensures client messages are written to the cmd all at once. + // NOTE: All messages share the same buffer. + if ( !m_bWriteReady ) + { + Reset(); + m_nQueueCount = 0; + m_bWriteIgnore = false; + } + else if ( m_nQueueCount == ((1< client +// +// Sends an exclusive usermessage. +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::Send( HSCRIPT player, bool bReliable ) +{ + DebugNetMsg( 1, DLL_LOC_STR " %s() size(%d)\n", __FUNCTION__, GetNumBitsWritten() ); + + CBaseEntity *pPlayer = ToEnt(player); + if ( pPlayer ) + { + m_filter.AddRecipient( (CBasePlayer*)pPlayer ); + } + + if ( bReliable ) + { + m_filter.MakeReliable(); + } + + Assert( usermessages->LookupUserMessage( "ScriptMsg" ) != -1 ); + + DoSendUserMsg( &m_filter, usermessages->LookupUserMessage( "ScriptMsg" ) ); +} +#else // CLIENT_DLL +//----------------------------------------------------------------------------- +// client -> server +// +// Mark UserCmd delta ready. +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::Send() +{ + DebugNetMsg( 1, DLL_LOC_STR " %s() size(%d)\n", __FUNCTION__, m_bWriteIgnore ? 0 : GetNumBitsWritten() ); + + m_bWriteReady = true; +} +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::Receive( const char *msg, HSCRIPT func ) +{ + if ( !msg || !msg[0] ) + { + g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" ); + return; + } + +#ifdef _DEBUG + int hash = Hash( msg ); + + const char *psz = HasNetMsgCollision( hash, msg ); + AssertMsg3( !psz, DLL_LOC_STR " NetMsg hash collision! [%d] '%s', '%s'\n", hash, msg, psz ); + + NetMsgHook_t &hook = g_NetMsgHooks[ g_NetMsgHooks.AddToTail() ]; + hook.Set( msg ); +#endif + + if ( func ) + { + g_pScriptVM->SetValue( m_Hooks, Hash( msg ), func ); + } + else + { + g_pScriptVM->ClearValue( m_Hooks, Hash( msg ) ); + } +} + +#ifdef GAME_DLL +void CNetMsgScriptHelper::DoSendUserMsg( CRecipientFilter *filter, int type ) +{ + WriteToBuffer( engine->UserMessageBegin( filter, type ) ); + engine->MessageEnd(); +} + +void CNetMsgScriptHelper::DoSendEntityMsg( CBaseEntity *entity, bool reliable ) +{ + WriteToBuffer( engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable ) ); + engine->MessageEnd(); +} + +//----------------------------------------------------------------------------- +// Send a usermessage from the server to the client +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::SendUserMessage( HSCRIPT hPlayer, const char *msg, bool bReliable ) +{ + int msg_type = usermessages->LookupUserMessage(msg); + if ( msg_type == -1 ) + { + g_pScriptVM->RaiseException( UTIL_VarArgs("SendUserMessage: Unregistered message '%s'", msg) ); + return; + } + + CBaseEntity *pPlayer = ToEnt(hPlayer); + if ( pPlayer ) + { + m_filter.AddRecipient( (CBasePlayer*)pPlayer ); + } + + if ( bReliable ) + { + m_filter.MakeReliable(); + } + + DoSendUserMsg( &m_filter, msg_type ); +} + +//----------------------------------------------------------------------------- +// Send a message from a server side entity to its client side counterpart +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::SendEntityMessage( HSCRIPT hEnt, bool bReliable ) +{ + CBaseEntity *entity = ToEnt(hEnt); + if ( !entity ) + { + g_pScriptVM->RaiseException("SendEntityMessage: invalid entity"); + return; + } + + DoSendEntityMsg( entity, bReliable ); +} +#else +//----------------------------------------------------------------------------- +// Dispatch a usermessage on client +//----------------------------------------------------------------------------- +void CNetMsgScriptHelper::DispatchUserMessage( const char *msg ) +{ + bf_read buffer( m_MsgOut.GetData(), m_MsgOut.GetNumBytesWritten() ); + usermessages->DispatchUserMessage( usermessages->LookupUserMessage(msg), buffer ); +} +#endif // GAME_DLL + +void CNetMsgScriptHelper::WriteInt( int iValue, int bits ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteSBitLong( iValue, bits ); +} + +void CNetMsgScriptHelper::WriteUInt( int iValue, int bits ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteUBitLong( iValue, bits ); +} + +void CNetMsgScriptHelper::WriteByte( int iValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteByte( iValue ); +} + +void CNetMsgScriptHelper::WriteChar( int iValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteChar( iValue ); +} + +void CNetMsgScriptHelper::WriteShort( int iValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteShort( iValue ); +} + +void CNetMsgScriptHelper::WriteWord( int iValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteWord( iValue ); +} + +void CNetMsgScriptHelper::WriteLong( int iValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteLong( iValue ); +} + +void CNetMsgScriptHelper::WriteFloat( float flValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteFloat( flValue ); +} + +void CNetMsgScriptHelper::WriteNormal( float flValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteBitNormal( flValue ); +} + +void CNetMsgScriptHelper::WriteAngle( float flValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteBitAngle( flValue, 8 ); +} + +void CNetMsgScriptHelper::WriteCoord( float flValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteBitCoord( flValue ); +} + +void CNetMsgScriptHelper::WriteVec3Coord( const Vector& rgflValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteBitVec3Coord( rgflValue ); +} + +void CNetMsgScriptHelper::WriteVec3Normal( const Vector& rgflValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteBitVec3Normal( rgflValue ); +} + +void CNetMsgScriptHelper::WriteAngles( const QAngle& rgflValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteBitAngles( rgflValue ); +} + +void CNetMsgScriptHelper::WriteString( const char *sz ) +{ + SCRIPT_NETMSG_WRITE_FUNC + + // Larger strings can be written but cannot be read + Assert( V_strlen(sz) < SCRIPT_NETMSG_STRING_SIZE ); + + m_MsgOut.WriteString( sz ); +} + +void CNetMsgScriptHelper::WriteBool( bool bValue ) +{ + SCRIPT_NETMSG_WRITE_FUNC + m_MsgOut.WriteOneBit( bValue ? 1 : 0 ); +} + +void CNetMsgScriptHelper::WriteEntity( HSCRIPT hEnt ) +{ + SCRIPT_NETMSG_WRITE_FUNC + CBaseEntity *p = ToEnt(hEnt); + int i = p ? p->entindex() : 0; + m_MsgOut.WriteUBitLong( i, MAX_EDICT_BITS ); +} + +void CNetMsgScriptHelper::WriteEHandle( HSCRIPT hEnt ) +{ + SCRIPT_NETMSG_WRITE_FUNC + 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; + } + m_MsgOut.WriteLong( iEncodedEHandle ); +} + +int CNetMsgScriptHelper::ReadInt( int bits ) +{ + return m_MsgIn_()ReadSBitLong(bits); +} + +int CNetMsgScriptHelper::ReadUInt( int bits ) +{ + return m_MsgIn_()ReadUBitLong(bits); +} + +int CNetMsgScriptHelper::ReadByte() +{ + return m_MsgIn_()ReadByte(); +} + +int CNetMsgScriptHelper::ReadChar() +{ + return m_MsgIn_()ReadChar(); +} + +int CNetMsgScriptHelper::ReadShort() +{ + return m_MsgIn_()ReadShort(); +} + +int CNetMsgScriptHelper::ReadWord() +{ + return m_MsgIn_()ReadWord(); +} + +int CNetMsgScriptHelper::ReadLong() +{ + return m_MsgIn_()ReadLong(); +} + +float CNetMsgScriptHelper::ReadFloat() +{ + return m_MsgIn_()ReadFloat(); +} + +float CNetMsgScriptHelper::ReadNormal() +{ + return m_MsgIn_()ReadBitNormal(); +} + +float CNetMsgScriptHelper::ReadAngle() +{ + return m_MsgIn_()ReadBitAngle( 8 ); +} + +float CNetMsgScriptHelper::ReadCoord() +{ + return m_MsgIn_()ReadBitCoord(); +} + +const Vector& CNetMsgScriptHelper::ReadVec3Coord() +{ + static Vector vec3; + m_MsgIn_()ReadBitVec3Coord(vec3); + return vec3; +} + +const Vector& CNetMsgScriptHelper::ReadVec3Normal() +{ + static Vector vec3; + m_MsgIn_()ReadBitVec3Normal(vec3); + return vec3; +} + +const QAngle& CNetMsgScriptHelper::ReadAngles() +{ + static QAngle vec3; + m_MsgIn_()ReadBitAngles(vec3); + return vec3; +} + +const char* CNetMsgScriptHelper::ReadString() +{ + static char buf[ SCRIPT_NETMSG_STRING_SIZE ]; + m_MsgIn_()ReadString( buf, sizeof(buf) ); + return buf; +} + +bool CNetMsgScriptHelper::ReadBool() +{ + return m_MsgIn_()ReadOneBit(); +} + +HSCRIPT CNetMsgScriptHelper::ReadEntity() +{ + int index = m_MsgIn_()ReadUBitLong( MAX_EDICT_BITS ); + + if ( !index ) + return NULL; + +#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; +} + +HSCRIPT CNetMsgScriptHelper::ReadEHandle() +{ + int iEncodedEHandle = m_MsgIn_()ReadLong(); + if ( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE ) + return NULL; + int iEntry = iEncodedEHandle & ( (1 << MAX_EDICT_BITS) - 1 ); + int iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS; + return ToHScript( EHANDLE( iEntry, iSerialNum ) ); +} + +inline int CNetMsgScriptHelper::GetNumBitsWritten() +{ +#ifdef GAME_DLL + return m_MsgOut.GetNumBitsWritten() - SCRIPT_NETMSG_HEADER_BITS; +#else + return m_MsgOut.m_iCurBit - m_iLastBit - SCRIPT_NETMSG_HEADER_BITS; +#endif +} + + +BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "Network messages" ) + +#ifdef GAME_DLL + DEFINE_SCRIPTFUNC( SendUserMessage, "Send a usermessage from the server to the client" ) + DEFINE_SCRIPTFUNC( SendEntityMessage, "Send a message from a server side entity to its client side counterpart" ) + + // TODO: multiplayer +#else + DEFINE_SCRIPTFUNC( DispatchUserMessage, "Dispatch a usermessage on client" ) +#endif + + DEFINE_SCRIPTFUNC( Reset, "Reset the current network message buffer" ) + DEFINE_SCRIPTFUNC( Start, "Start writing new custom network message" ) + DEFINE_SCRIPTFUNC( Receive, "Set custom network message callback" ) + DEFINE_SCRIPTFUNC_NAMED( Receive, "Recieve", SCRIPT_HIDE ) // This was a typo until v6.3 +#ifdef GAME_DLL + DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" ) +#else + DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2044 bytes)" ) +#endif + + DEFINE_SCRIPTFUNC( WriteInt, "variable bit signed int" ) + DEFINE_SCRIPTFUNC( WriteUInt, "variable bit unsigned int" ) + DEFINE_SCRIPTFUNC( WriteByte, "8 bit unsigned char" ) + DEFINE_SCRIPTFUNC( WriteChar, "8 bit char" ) + DEFINE_SCRIPTFUNC( WriteShort, "16 bit short" ) + DEFINE_SCRIPTFUNC( WriteWord, "16 bit unsigned short" ) + DEFINE_SCRIPTFUNC( WriteLong, "32 bit long" ) + DEFINE_SCRIPTFUNC( WriteFloat, "32 bit float" ) + DEFINE_SCRIPTFUNC( WriteNormal, "12 bit" ) + DEFINE_SCRIPTFUNC( WriteAngle, "8 bit unsigned char" ) DEFINE_SCRIPTFUNC( WriteCoord, "" ) DEFINE_SCRIPTFUNC( WriteVec3Coord, "" ) - DEFINE_SCRIPTFUNC( WriteVec3Normal, "" ) + DEFINE_SCRIPTFUNC( WriteVec3Normal, "27 bit" ) DEFINE_SCRIPTFUNC( WriteAngles, "" ) - DEFINE_SCRIPTFUNC( WriteString, "" ) - DEFINE_SCRIPTFUNC( WriteEntity, "" ) - DEFINE_SCRIPTFUNC( WriteEHandle, "" ) - DEFINE_SCRIPTFUNC( WriteBool, "" ) + DEFINE_SCRIPTFUNC( WriteString, "max 512 bytes at once" ) + DEFINE_SCRIPTFUNC( WriteBool, "1 bit" ) + DEFINE_SCRIPTFUNC( WriteEntity, "11 bit (entindex)" ) + DEFINE_SCRIPTFUNC( WriteEHandle, "32 bit long" ) + + DEFINE_SCRIPTFUNC( ReadInt, "" ) + DEFINE_SCRIPTFUNC( ReadUInt, "" ) + DEFINE_SCRIPTFUNC( ReadByte, "" ) + DEFINE_SCRIPTFUNC( ReadChar, "" ) + DEFINE_SCRIPTFUNC( ReadShort, "" ) + DEFINE_SCRIPTFUNC( ReadWord, "" ) + DEFINE_SCRIPTFUNC( ReadLong, "" ) + DEFINE_SCRIPTFUNC( ReadFloat, "" ) + DEFINE_SCRIPTFUNC( ReadNormal, "" ) + DEFINE_SCRIPTFUNC( ReadAngle, "" ) + DEFINE_SCRIPTFUNC( ReadCoord, "" ) + DEFINE_SCRIPTFUNC( ReadVec3Coord, "" ) + DEFINE_SCRIPTFUNC( ReadVec3Normal, "" ) + DEFINE_SCRIPTFUNC( ReadAngles, "" ) + DEFINE_SCRIPTFUNC( ReadString, "" ) + DEFINE_SCRIPTFUNC( ReadBool, "" ) + DEFINE_SCRIPTFUNC( ReadEntity, "" ) + DEFINE_SCRIPTFUNC( ReadEHandle, "" ) + + DEFINE_SCRIPTFUNC( GetNumBitsWritten, "" ) + END_SCRIPTDESC(); -#endif // !CLIENT_DLL + + +#define RETURN_IF_CANNOT_DRAW_OVERLAY\ + if (engine->IsPaused())\ + 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 + + 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); + + 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 + + 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 + + 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(); + 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 + + 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 + + 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 + + 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 + + debugoverlay->AddTextOverlayRGB(origin, text_offset, flDuration, r, g, b, a, "%s", text); + } + void Grid( const Vector &vPosition ) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + debugoverlay->AddGridOverlay(vPosition); + } + void Text( const Vector &origin, const char *text, float flDuration ) + { + RETURN_IF_CANNOT_DRAW_OVERLAY + + 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 + + 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 ); + } + } + } +#ifndef CLIENT_DLL + 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 + } + } +#endif + void ClearAllOverlays() + { +#ifndef CLIENT_DLL + // Clear all entities of their debug overlays + for (CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity; pEntity = gEntList.NextEnt(pEntity)) + { + pEntity->m_debugOverlays = 0; + } +#endif + + 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." ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( SetDebugBits, "Set debug bits on entity" ) +#endif + DEFINE_SCRIPTFUNC( ClearAllOverlays, "Clear all debug overlays at once" ) +END_SCRIPTDESC(); + + + +//============================================================================= +// ConVars +//============================================================================= +class CScriptConCommand : public ConCommand, public ICommandCallback, public ICommandCompletionCallback +{ + typedef ConCommand BaseClass; + +public: + ~CScriptConCommand() + { + Unregister(); + } + + CScriptConCommand( const char *name, HSCRIPT fn, const char *helpString, int flags, ConCommand *pLinked = NULL ) + : BaseClass( name, this, helpString, flags, 0 ), + m_pLinked(pLinked), + m_hCallback(fn), + m_hCompletionCallback(NULL) + { + m_nCmdNameLen = V_strlen(name) + 1; + Assert( m_nCmdNameLen - 1 <= 128 ); + } + + void CommandCallback( const CCommand &command ) + { + int count = command.ArgC(); + ScriptVariant_t *vArgv = (ScriptVariant_t*)stackalloc( sizeof(ScriptVariant_t) * count ); + for ( int i = 0; i < count; ++i ) + { + vArgv[i] = command[i]; + } + ScriptVariant_t ret; + if ( g_pScriptVM->ExecuteFunction( m_hCallback, vArgv, count, &ret, NULL, true ) == SCRIPT_ERROR ) + { + DevWarning( 1, "CScriptConCommand: invalid callback for '%s'\n", command[0] ); + } + if ( m_pLinked && (ret.m_type == FIELD_BOOLEAN) && ret.m_bool ) + { + m_pLinked->Dispatch( command ); + } + } + + int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + Assert( g_pScriptVM ); + Assert( m_hCompletionCallback ); + + ScriptVariant_t hArray; + g_pScriptVM->CreateArray( hArray ); + + // split command name from partial, pass both separately to the script function + char *cmdname = (char*)stackalloc( m_nCmdNameLen ); + V_memcpy( cmdname, partial, m_nCmdNameLen - 1 ); + cmdname[ m_nCmdNameLen - 1 ] = 0; + + char argPartial[256]; + V_StrRight( partial, V_strlen(partial) - m_nCmdNameLen, argPartial, sizeof(argPartial) ); + + ScriptVariant_t args[3] = { cmdname, argPartial, hArray }; + if ( g_pScriptVM->ExecuteFunction( m_hCompletionCallback, args, 3, NULL, NULL, true ) == SCRIPT_ERROR ) + { + DevWarning( 1, "CScriptConCommand: invalid command completion callback for '%s'\n", cmdname ); + g_pScriptVM->ReleaseScript( hArray ); + return 0; + } + + int count = 0; + ScriptVariant_t val; + int it = -1; + while ( ( it = g_pScriptVM->GetKeyValue( hArray, it, NULL, &val ) ) != -1 ) + { + if ( val.m_type == FIELD_CSTRING ) + { + CUtlString &s = commands.Element( commands.AddToTail() ); + int len = V_strlen( val.m_pszString ); + + if ( len <= COMMAND_COMPLETION_ITEM_LENGTH - 1 ) + { + s.Set( val.m_pszString ); + } + else + { + s.SetDirect( val.m_pszString, COMMAND_COMPLETION_ITEM_LENGTH - 1 ); + } + + ++count; + } + g_pScriptVM->ReleaseValue(val); + + if ( count == COMMAND_COMPLETION_MAXITEMS ) + break; + } + g_pScriptVM->ReleaseScript( hArray ); + return count; + } + + void SetCompletionCallback( HSCRIPT fn ) + { + if ( m_hCompletionCallback ) + g_pScriptVM->ReleaseScript( m_hCompletionCallback ); + + if (fn) + { + if ( !BaseClass::IsRegistered() ) + return; + + BaseClass::m_pCommandCompletionCallback = this; + BaseClass::m_bHasCompletionCallback = true; + m_hCompletionCallback = fn; + } + else + { + BaseClass::m_pCommandCompletionCallback = NULL; + BaseClass::m_bHasCompletionCallback = false; + m_hCompletionCallback = NULL; + } + } + + void SetCallback( HSCRIPT fn ) + { + if (fn) + { + if ( !BaseClass::IsRegistered() ) + Register(); + + if ( m_hCallback ) + g_pScriptVM->ReleaseScript( m_hCallback ); + m_hCallback = fn; + } + else + { + Unregister(); + } + } + + inline void Unregister() + { + if ( g_pCVar && BaseClass::IsRegistered() ) + g_pCVar->UnregisterConCommand( this ); + + if ( g_pScriptVM ) + { + if ( m_hCallback ) + { + g_pScriptVM->ReleaseScript( m_hCallback ); + m_hCallback = NULL; + } + + SetCompletionCallback( NULL ); + } + } + + inline void Register() + { + if ( g_pCVar ) + g_pCVar->RegisterConCommand( this ); + } + + HSCRIPT m_hCallback; + ConCommand *m_pLinked; + HSCRIPT m_hCompletionCallback; + int m_nCmdNameLen; +}; + +class CScriptConVar : public ConVar +{ + typedef ConVar BaseClass; + +public: + ~CScriptConVar() + { + Unregister(); + } + + CScriptConVar( const char *pName, const char *pDefaultValue, const char *pHelpString, int flags/*, float fMin, float fMax*/ ) + : BaseClass( pName, pDefaultValue, flags, pHelpString ), + m_hCallback(NULL) + {} + + void SetChangeCallback( HSCRIPT fn ) + { + void ScriptConVarCallback( IConVar*, const char*, float ); + + if ( m_hCallback ) + g_pScriptVM->ReleaseScript( m_hCallback ); + + if (fn) + { + m_hCallback = fn; + BaseClass::InstallChangeCallback( (FnChangeCallback_t)ScriptConVarCallback ); + } + else + { + m_hCallback = NULL; + BaseClass::InstallChangeCallback( NULL ); + } + } + + inline void Unregister() + { + if ( g_pCVar && BaseClass::IsRegistered() ) + g_pCVar->UnregisterConCommand( this ); + + if ( g_pScriptVM ) + { + SetChangeCallback( NULL ); + } + } + + HSCRIPT m_hCallback; +}; + +static CUtlMap< unsigned int, bool > g_ConVarsBlocked( DefLessFunc(unsigned int) ); +static CUtlMap< unsigned int, bool > g_ConCommandsOverridable( DefLessFunc(unsigned int) ); +static CUtlMap< unsigned int, CScriptConCommand* > g_ScriptConCommands( DefLessFunc(unsigned int) ); +static CUtlMap< unsigned int, CScriptConVar* > g_ScriptConVars( DefLessFunc(unsigned int) ); + + +class CScriptConvarAccessor : public CAutoGameSystem +{ +public: + static inline unsigned int Hash( const char*sz ){ return HashStringCaseless(sz); } + +public: + inline void AddOverridable( const char *name ) + { + g_ConCommandsOverridable.InsertOrReplace( Hash(name), true ); + } + + inline bool IsOverridable( unsigned int hash ) + { + int idx = g_ConCommandsOverridable.Find( hash ); + return ( idx != g_ConCommandsOverridable.InvalidIndex() ); + } + + inline void AddBlockedConVar( const char *name ) + { + g_ConVarsBlocked.InsertOrReplace( Hash(name), true ); + } + + inline bool IsBlockedConvar( const char *name ) + { + int idx = g_ConVarsBlocked.Find( Hash(name) ); + return ( idx != g_ConVarsBlocked.InvalidIndex() ); + } + +public: + void RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ); + void SetCompletionCallback( const char *name, HSCRIPT fn ); + void UnregisterCommand( const char *name ); + void RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags ); + void SetChangeCallback( const char *name, HSCRIPT fn ); + + HSCRIPT GetCommandClient() + { +#ifdef GAME_DLL + return ToHScript( UTIL_GetCommandClient() ); +#else + return ToHScript( C_BasePlayer::GetLocalPlayer() ); +#endif + } +#ifdef GAME_DLL + const char *GetClientConvarValue( int index, const char* cvar ) + { + return engine->GetClientConVarValue( index, cvar ); + } +#endif +public: + bool Init(); + + void LevelShutdownPostEntity() + { + g_ScriptConCommands.PurgeAndDeleteElements(); + g_ScriptConVars.PurgeAndDeleteElements(); + } + +public: + float GetFloat( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetFloat(); + } + + int GetInt( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetInt(); + } + + bool GetBool( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetBool(); + } + + const char *GetStr( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + 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; + + if ( cvar.IsFlagSet( FCVAR_NOT_CONNECTED | FCVAR_SERVER_CANNOT_QUERY ) ) + return; + + if ( IsBlockedConvar( pszConVar ) ) + return; + + cvar.SetValue( value ); + } + +} g_ScriptConvarAccessor; + + +void CScriptConvarAccessor::RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConCommands.Find(hash); + if ( idx == g_ScriptConCommands.InvalidIndex() ) + { + ConCommandBase *pBase = g_pCVar->FindCommandBase(name); + if ( pBase && ( !pBase->IsCommand() || !IsOverridable(hash) ) ) + { + DevWarning( 1, "CScriptConvarAccessor::RegisterCommand unable to register blocked ConCommand: %s\n", name ); + return; + } + + if ( !fn ) + return; + + CScriptConCommand *p = new CScriptConCommand( name, fn, helpString, flags, static_cast< ConCommand* >(pBase) ); + g_ScriptConCommands.Insert( hash, p ); + } + else + { + CScriptConCommand *pCmd = g_ScriptConCommands[idx]; + pCmd->SetCallback( fn ); + //CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterCommand replacing command already registered: %s\n", name ); + } +} + +void CScriptConvarAccessor::SetCompletionCallback( const char *name, HSCRIPT fn ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConCommands.Find(hash); + if ( idx != g_ScriptConCommands.InvalidIndex() ) + { + g_ScriptConCommands[idx]->SetCompletionCallback( fn ); + } +} + +void CScriptConvarAccessor::UnregisterCommand( const char *name ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConCommands.Find(hash); + if ( idx != g_ScriptConCommands.InvalidIndex() ) + { + g_ScriptConCommands[idx]->Unregister(); + } +} + +void CScriptConvarAccessor::RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags ) +{ + Assert( g_pCVar ); + unsigned int hash = Hash(name); + int idx = g_ScriptConVars.Find(hash); + if ( idx == g_ScriptConVars.InvalidIndex() ) + { + if ( g_pCVar->FindCommandBase(name) ) + { + DevWarning( 1, "CScriptConvarAccessor::RegisterConvar unable to register blocked ConCommand: %s\n", name ); + return; + } + + CScriptConVar *p = new CScriptConVar( name, pDefaultValue, helpString, flags ); + g_ScriptConVars.Insert( hash, p ); + } + else + { + //CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterConvar convar %s already registered\n", name ); + } +} + +void CScriptConvarAccessor::SetChangeCallback( const char *name, HSCRIPT fn ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConVars.Find(hash); + if ( idx != g_ScriptConVars.InvalidIndex() ) + { + g_ScriptConVars[idx]->SetChangeCallback( fn ); + } +} + +void ScriptConVarCallback( IConVar *var, const char* pszOldValue, float flOldValue ) +{ + ConVar *cvar = (ConVar*)var; + const char *name = cvar->GetName(); + unsigned int hash = CScriptConvarAccessor::Hash( name ); + int idx = g_ScriptConVars.Find(hash); + if ( idx != g_ScriptConVars.InvalidIndex() ) + { + Assert( g_ScriptConVars[idx]->m_hCallback ); + + ScriptVariant_t args[5] = { name, pszOldValue, flOldValue, cvar->GetString(), cvar->GetFloat() }; + if ( g_pScriptVM->ExecuteFunction( g_ScriptConVars[idx]->m_hCallback, args, 5, NULL, NULL, true ) == SCRIPT_ERROR ) + { + DevWarning( 1, "CScriptConVar: invalid change callback for '%s'\n", name ); + } + } +} + + +bool CScriptConvarAccessor::Init() +{ + static bool bExecOnce = false; + if ( bExecOnce ) + return true; + bExecOnce = true; + + AddOverridable( "+attack" ); + AddOverridable( "+attack2" ); + AddOverridable( "+attack3" ); + AddOverridable( "+forward" ); + AddOverridable( "+back" ); + AddOverridable( "+moveleft" ); + AddOverridable( "+moveright" ); + AddOverridable( "+use" ); + AddOverridable( "+jump" ); + AddOverridable( "+zoom" ); + AddOverridable( "+reload" ); + AddOverridable( "+speed" ); + AddOverridable( "+walk" ); + AddOverridable( "+duck" ); + AddOverridable( "+strafe" ); + AddOverridable( "+alt1" ); + AddOverridable( "+alt2" ); + AddOverridable( "+grenade1" ); + AddOverridable( "+grenade2" ); + AddOverridable( "+showscores" ); + AddOverridable( "+voicerecord" ); + + AddOverridable( "-attack" ); + AddOverridable( "-attack2" ); + AddOverridable( "-attack3" ); + AddOverridable( "-forward" ); + AddOverridable( "-back" ); + AddOverridable( "-moveleft" ); + AddOverridable( "-moveright" ); + AddOverridable( "-use" ); + AddOverridable( "-jump" ); + AddOverridable( "-zoom" ); + AddOverridable( "-reload" ); + AddOverridable( "-speed" ); + AddOverridable( "-walk" ); + AddOverridable( "-duck" ); + AddOverridable( "-strafe" ); + AddOverridable( "-alt1" ); + AddOverridable( "-alt2" ); + AddOverridable( "-grenade1" ); + AddOverridable( "-grenade2" ); + AddOverridable( "-showscores" ); + AddOverridable( "-voicerecord" ); + + AddOverridable( "toggle_duck" ); + AddOverridable( "impulse" ); + AddOverridable( "use" ); + AddOverridable( "lastinv" ); + AddOverridable( "invnext" ); + AddOverridable( "invprev" ); + AddOverridable( "phys_swap" ); + AddOverridable( "slot0" ); + AddOverridable( "slot1" ); + AddOverridable( "slot2" ); + AddOverridable( "slot3" ); + AddOverridable( "slot4" ); + AddOverridable( "slot5" ); + AddOverridable( "slot6" ); + AddOverridable( "slot7" ); + AddOverridable( "slot8" ); + AddOverridable( "slot9" ); + AddOverridable( "slot10" ); + + AddOverridable( "save" ); + AddOverridable( "load" ); + + AddOverridable( "say" ); + AddOverridable( "say_team" ); + + + AddBlockedConVar( "con_enable" ); + AddBlockedConVar( "cl_allowdownload" ); + AddBlockedConVar( "cl_allowupload" ); + AddBlockedConVar( "cl_downloadfilter" ); + + return true; +} + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarAccessor, "CConvars", SCRIPT_SINGLETON "Provides an interface to convars." ) + DEFINE_SCRIPTFUNC( RegisterConvar, "register a new console variable." ) + DEFINE_SCRIPTFUNC( RegisterCommand, "register a console command." ) + DEFINE_SCRIPTFUNC( SetCompletionCallback, "callback is called with 3 parameters (cmd, partial, commands), user strings must be appended to 'commands' array" ) + DEFINE_SCRIPTFUNC( SetChangeCallback, "callback is called with 5 parameters (var, szOldValue, flOldValue, szNewValue, flNewValue)" ) + DEFINE_SCRIPTFUNC( UnregisterCommand, "unregister a console command." ) + DEFINE_SCRIPTFUNC( GetCommandClient, "returns the player who issued this console command." ) +#ifdef GAME_DLL + DEFINE_SCRIPTFUNC( GetClientConvarValue, "Get a convar keyvalue for a specified client" ) +#endif + 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(); + + +//============================================================================= +// Effects +// (Unique to mapbase) +// +// At the moment only clientside until a filtering method on server is finalised. +// +// TEs most of the time call IEffects (g_pEffects) or ITempEnts (tempents) on client, +// but they also record for tools recording mode. +// +// On client no TE is suppressed. +// TE flags are found at tempent.h +// +// TODO: +//============================================================================= +#ifdef CLIENT_DLL + +class CEffectsScriptHelper +{ +public: + void DynamicLight( int index, const Vector& origin, int r, int g, int b, int exponent, + float radius, float die, float decay, int style = 0, int flags = 0 ) + { + //te->DynamicLight( filter, delay, &origin, r, g, b, exponent, radius, die, decay ); + dlight_t *dl = effects->CL_AllocDlight( index ); + dl->origin = origin; + dl->color.r = r; + dl->color.g = g; + dl->color.b = b; + dl->color.exponent = exponent; + dl->radius = radius; + dl->die = gpGlobals->curtime + die; + dl->decay = decay; + dl->style = style; + dl->flags = flags; + } + + void Explosion( const Vector& pos, float scale, int radius, int magnitude, int flags ) + { + C_RecipientFilter filter; + filter.AddAllPlayers(); + // framerate, modelindex, normal and materialtype are unused + // radius for ragdolls + extern short g_sModelIndexFireball; + te->Explosion( filter, 0.0f, &pos, g_sModelIndexFireball, scale, 15, flags, radius, magnitude, &vec3_origin ); + } + +// void FXExplosion( const Vector& pos, const Vector& normal, int materialType = 'C' ) +// { +// // just the particles +// // materialtype only for debris. can be 'C','W' or anything else. +// FX_Explosion( const_cast(pos), const_cast(normal), materialType ); +// } + +// void ConcussiveExplosion( const Vector& pos, const Vector& normal ) +// { +// FX_ConcussiveExplosion( const_cast(pos), const_cast(normal) ); +// } + +// void MicroExplosion( const Vector& pos, const Vector& normal ) +// { +// FX_MicroExplosion( const_cast(pos), const_cast(normal) ); +// } + +// void MuzzleFlash( int type, HSCRIPT hEntity, int attachment, bool firstPerson ) +// { +// C_BaseEntity *p = ToEnt(hEntity); +// ClientEntityHandle_t ent = p ? (ClientEntityList().EntIndexToHandle)( p->entindex() ) : NULL;; +// tempents->MuzzleFlash( type, ent, attachment, firstPerson ); +// } + + void Sparks( const Vector& pos, int nMagnitude, int nTrailLength, const Vector& pDir ) + { + //te->Sparks( filter, delay, &pos, nMagnitude, nTrailLength, &pDir ); + //g_pEffects->Sparks( pos, nMagnitude, nTrailLength, &pDir ); + FX_ElectricSpark( pos, nMagnitude, nTrailLength, &pDir ); + } + + void MetalSparks( const Vector& pos, const Vector& dir ) + { + //g_pEffects->MetalSparks( pos, dir ); + FX_MetalSpark( pos, dir, dir ); + } + +// void Smoke( const Vector& pos, float scale, int framerate) +// { +// extern short g_sModelIndexSmoke; +// //te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, scale * 10.0f, framerate ); +// g_pEffects->Smoke( pos, g_sModelIndexSmoke, scale, framerate ); +// } + + void Dust( const Vector &pos, const Vector &dir, float size, float speed ) + { + //te->Dust( filter, delay, pos, dir, size, speed ); + //g_pEffects->Dust( pos, dir, size, speed ); + FX_Dust( pos, dir, size, speed ); + } + + void Bubbles( const Vector &mins, const Vector &maxs, float height, int modelindex, int count, float speed ) + { + //int bubbles = modelinfo->GetModelIndex( "sprites/bubble.vmt" ); + //te->Bubbles( filter, delay, &mins, &maxs, height, modelindex, count, speed ); + tempents->Bubbles( mins, maxs, height, modelindex, count, speed ); + } + +// void Fizz( const Vector& mins, const Vector& maxs, int modelIndex, int density, int current/*, int flags*/ ) +// { +// //te->Fizz( filter, delay, ent, modelindex, density, current ); +// //tempents->FizzEffect( ToEnt(ent), modelindex, density, current ); +// } + + void Sprite( const Vector &pos, const Vector &dir, float scale, int modelIndex, int rendermode, + int renderfx, int brightness, float life, int flags ) + { + //te->Sprite( filter, delay, &pos, modelindex, size, brightness ); + float a = (1.0 / 255.0) * brightness; + tempents->TempSprite( pos, dir, scale, modelIndex, rendermode, renderfx, a, life, flags ); + } + +// void PhysicsProp( float delay, int modelindex, int skin, const Vector& pos, const QAngle &angles, +// const Vector& vel, int flags, int effects ) +// { +// //te->PhysicsProp( filter, delay, modelindex, skin, pos, angles, vel, flags, effects ); +// tempents->PhysicsProp( modelindex, skin, pos, angles, vel, flags, effects ); +// } + + void ClientProjectile( const Vector& vecOrigin, const Vector& vecVelocity, const Vector& vecAccel, int modelindex, + int lifetime, HSCRIPT pOwner, const char *pszImpactEffect = NULL, const char *pszParticleEffect = NULL ) + { + //te->ClientProjectile( filter, delay, &vecOrigin, &vecVelocity, modelindex, lifetime, ToEnt(pOwner) ); + if ( pszImpactEffect && !(*pszImpactEffect) ) + pszImpactEffect = NULL; + if ( pszParticleEffect && !(*pszParticleEffect) ) + pszParticleEffect = NULL; + tempents->ClientProjectile( vecOrigin, vecVelocity, vecAccel, modelindex, lifetime, ToEnt(pOwner), pszImpactEffect, pszParticleEffect ); + } + +} g_ScriptEffectsHelper; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CEffectsScriptHelper, "CEffects", SCRIPT_SINGLETON "" ) + DEFINE_SCRIPTFUNC( DynamicLight, "" ) + DEFINE_SCRIPTFUNC( Explosion, "" ) + DEFINE_SCRIPTFUNC( Sparks, "" ) + DEFINE_SCRIPTFUNC( MetalSparks, "" ) + DEFINE_SCRIPTFUNC( Dust, "" ) + DEFINE_SCRIPTFUNC( Bubbles, "" ) + DEFINE_SCRIPTFUNC( Sprite, "" ) + DEFINE_SCRIPTFUNC( ClientProjectile, "" ) +END_SCRIPTDESC(); + + + +//============================================================================= +//============================================================================= + +extern CGlowObjectManager g_GlowObjectManager; + +class CScriptGlowObjectManager : public CAutoGameSystem +{ +public: + CUtlVector m_RegisteredObjects; + + void LevelShutdownPostEntity() + { + FOR_EACH_VEC( m_RegisteredObjects, i ) + g_GlowObjectManager.UnregisterGlowObject( m_RegisteredObjects[i] ); + m_RegisteredObjects.Purge(); + } + +public: + int Register( HSCRIPT hEntity, int r, int g, int b, int a, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded ) + { + Vector vGlowColor; + vGlowColor.x = r * ( 1.0f / 255.0f ); + vGlowColor.y = g * ( 1.0f / 255.0f ); + vGlowColor.z = b * ( 1.0f / 255.0f ); + float flGlowAlpha = a * ( 1.0f / 255.0f ); + int idx = g_GlowObjectManager.RegisterGlowObject( ToEnt(hEntity), vGlowColor, flGlowAlpha, bRenderWhenOccluded, bRenderWhenUnoccluded, -1 ); + m_RegisteredObjects.AddToTail( idx ); + return idx; + } + + void Unregister( int nGlowObjectHandle ) + { + if ( (nGlowObjectHandle < 0) || (nGlowObjectHandle >= g_GlowObjectManager.m_GlowObjectDefinitions.Count()) ) + return; + g_GlowObjectManager.UnregisterGlowObject( nGlowObjectHandle ); + m_RegisteredObjects.FindAndFastRemove( nGlowObjectHandle ); + } + + void SetEntity( int nGlowObjectHandle, HSCRIPT hEntity ) + { + g_GlowObjectManager.SetEntity( nGlowObjectHandle, ToEnt(hEntity) ); + } + + void SetColor( int nGlowObjectHandle, int r, int g, int b ) + { + Vector vGlowColor; + vGlowColor.x = r * ( 1.0f / 255.0f ); + vGlowColor.y = g * ( 1.0f / 255.0f ); + vGlowColor.z = b * ( 1.0f / 255.0f ); + g_GlowObjectManager.SetColor( nGlowObjectHandle, vGlowColor ); + } + + void SetAlpha( int nGlowObjectHandle, int a ) + { + float flGlowAlpha = a * ( 1.0f / 255.0f ); + g_GlowObjectManager.SetAlpha( nGlowObjectHandle, flGlowAlpha ); + } + + void SetRenderFlags( int nGlowObjectHandle, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded ) + { + g_GlowObjectManager.SetRenderFlags( nGlowObjectHandle, bRenderWhenOccluded, bRenderWhenUnoccluded ); + } + +} g_ScriptGlowObjectManager; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptGlowObjectManager, "CGlowObjectManager", SCRIPT_SINGLETON "" ) + DEFINE_SCRIPTFUNC( Register, "( HSCRIPT hEntity, int r, int g, int b, int a, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded )" ) + DEFINE_SCRIPTFUNC( Unregister, "" ) + DEFINE_SCRIPTFUNC( SetEntity, "" ) + DEFINE_SCRIPTFUNC( SetColor, "" ) + DEFINE_SCRIPTFUNC( SetAlpha, "" ) + DEFINE_SCRIPTFUNC( SetRenderFlags, "" ) +END_SCRIPTDESC(); + + +//============================================================================= +//============================================================================= + + +#if !defined(NO_STEAM) +class CScriptSteamAPI +{ +public: + const char *GetSteam2ID() + { + if ( !steamapicontext || !steamapicontext->SteamUser() ) + return NULL; + + CSteamID id = steamapicontext->SteamUser()->GetSteamID(); + + uint32 accountID = id.GetAccountID(); + uint32 steamInstanceID = 0; + uint32 high32bits = accountID % 2; + uint32 low32bits = accountID / 2; + + static char ret[48]; + V_snprintf( ret, sizeof(ret), "STEAM_%u:%u:%u", steamInstanceID, high32bits, low32bits ); + return ret; + } + + int GetSecondsSinceComputerActive() + { + if ( !steamapicontext || !steamapicontext->SteamUtils() ) + return 0; + + return steamapicontext->SteamUtils()->GetSecondsSinceComputerActive(); + } + + int GetCurrentBatteryPower() + { + if ( !steamapicontext || !steamapicontext->SteamUtils() ) + return 0; + + return steamapicontext->SteamUtils()->GetCurrentBatteryPower(); + } +#if 0 + const char *GetIPCountry() + { + if ( !steamapicontext || !steamapicontext->SteamUtils() ) + return NULL; + + const char *get = steamapicontext->SteamUtils()->GetIPCountry(); + if ( !get ) + return NULL; + + static char ret[3]; + V_strncpy( ret, get, 3 ); + + return ret; + } +#endif + const char *GetCurrentGameLanguage() + { + if ( !steamapicontext || !steamapicontext->SteamApps() ) + return NULL; + + const char *lang = steamapicontext->SteamApps()->GetCurrentGameLanguage(); + if ( !lang ) + return NULL; + + static char ret[16]; + V_strncpy( ret, lang, sizeof(ret) ); + + return ret; + } + const char *GetCurrentBetaName() + { + if ( !steamapicontext || !steamapicontext->SteamApps() ) + return NULL; + + static char ret[16]; + steamapicontext->SteamApps()->GetCurrentBetaName( ret, sizeof( ret ) ); + return ret; + } +#if 0 + bool IsSubscribedApp( int nAppID ) + { + if ( !steamapicontext || !steamapicontext->SteamApps() ) + return false; + + return steamapicontext->SteamApps()->BIsSubscribedApp( nAppID ); + } +#endif + bool IsAppInstalled( int nAppID ) + { + if ( !steamapicontext || !steamapicontext->SteamApps() ) + return false; + + return steamapicontext->SteamApps()->BIsAppInstalled( nAppID ); + } + +} g_ScriptSteamAPI; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptSteamAPI, "CSteamAPI", SCRIPT_SINGLETON "" ) + DEFINE_SCRIPTFUNC( GetSteam2ID, "" ) + //DEFINE_SCRIPTFUNC( IsVACBanned, "" ) + DEFINE_SCRIPTFUNC( GetSecondsSinceComputerActive, "Returns the number of seconds since the user last moved the mouse." ) + DEFINE_SCRIPTFUNC( GetCurrentBatteryPower, "Return the amount of battery power left in the current system in % [0..100], 255 for being on AC power" ) + //DEFINE_SCRIPTFUNC( GetIPCountry, "Returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database)" ) + DEFINE_SCRIPTFUNC( GetCurrentGameLanguage, "Gets the current language that the user has set as API language code. This falls back to the Steam UI language if the user hasn't explicitly picked a language for the title." ) + DEFINE_SCRIPTFUNC( GetCurrentBetaName, "Gets the name of the user's current beta branch. In Source SDK Base 2013 Singleplayer, this will usually return 'upcoming'." ) + //DEFINE_SCRIPTFUNC( IsSubscribedApp, "Returns true if the user is subscribed to the specified app ID." ) + DEFINE_SCRIPTFUNC( IsAppInstalled, "Returns true if the user has the specified app ID installed on their computer." ) +END_SCRIPTDESC(); +#endif // !NO_STEAM + +#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." ); + ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnSave, "OnSave", FIELD_VOID, "Called when the game is saved." ); + ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnRestore, "OnRestore", FIELD_VOID, "Called when the game is restored." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileWrite, "StringToFile", "Stores the string into the file" ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileRead, "FileToString", "Returns the string from the file, null if no file or file is too big." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileExists, "FileExists", "Returns true if the file exists." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesWrite, "KeyValuesToFile", "Stores the CScriptKeyValues into the file" ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesRead, "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." ); @@ -1339,8 +3309,16 @@ void RegisterScriptSingletons() g_pScriptVM->RegisterInstance( &g_ScriptNetPropManager, "NetProps" ); g_pScriptVM->RegisterInstance( &g_ScriptLocalize, "Localize" ); -#ifndef CLIENT_DLL - g_pScriptVM->RegisterInstance( &g_ScriptNetMsg, "NetMsg" ); + g_pScriptVM->RegisterInstance( g_ScriptNetMsg, "NetMsg" ); + g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" ); + g_pScriptVM->RegisterInstance( &g_ScriptConvarAccessor, "Convars" ); +#ifdef CLIENT_DLL + g_pScriptVM->RegisterInstance( &g_ScriptEffectsHelper, "effects" ); + g_pScriptVM->RegisterInstance( &g_ScriptGlowObjectManager, "GlowObjectManager" ); + +#if !defined(NO_STEAM) + g_pScriptVM->RegisterInstance( &g_ScriptSteamAPI, "steam" ); +#endif #endif // Singletons not unique to VScript (not declared or defined here) @@ -1350,7 +3328,9 @@ void RegisterScriptSingletons() g_pScriptVM->RegisterInstance( &g_AI_SquadManager, "Squads" ); #endif -#ifndef CLIENT_DLL +#ifdef USE_OLD_EVENT_DESCRIPTORS CScriptGameEventListener::LoadAllEvents(); -#endif // !CLIENT_DLL +#endif + + g_ScriptNetMsg->InitPostVM(); } diff --git a/mp/src/game/shared/mapbase/vscript_singletons.h b/mp/src/game/shared/mapbase/vscript_singletons.h index b190fe45..e514f4d9 100644 --- a/mp/src/game/shared/mapbase/vscript_singletons.h +++ b/mp/src/game/shared/mapbase/vscript_singletons.h @@ -5,12 +5,151 @@ // $NoKeywords: $ //============================================================================= -#ifndef VSCRIPT_FUNCS_MATH -#define VSCRIPT_FUNCS_MATH +#ifndef VSCRIPT_SINGLETONS_H +#define VSCRIPT_SINGLETONS_H #ifdef _WIN32 #pragma once #endif void RegisterScriptSingletons(); + + +#ifdef CLIENT_DLL +// usercmd +#define SCRIPT_NETMSG_DATA_SIZE ( ( 1 << 11 ) - 1 ) +#else +// usermsg +#define SCRIPT_NETMSG_DATA_SIZE MAX_USER_MSG_DATA +#endif + +#define SCRIPT_NETMSG_QUEUE_BITS 3 // determines the number of custom messages client can write to a usercmd +#define SCRIPT_NETMSG_HEADER_BITS (sizeof(word) << 3) +#define SCRIPT_NETMSG_STRING_SIZE 512 + + +#ifdef CLIENT_DLL +class CNetMsgScriptHelper : public CAutoGameSystem +#else +class CNetMsgScriptHelper +#endif +{ +#ifdef CLIENT_DLL +public: + bool m_bWriteReady; // dt ready to send +#endif + +private: +#ifdef GAME_DLL + bf_read *m_MsgIn; + CRecipientFilter m_filter; +#else + bf_read m_MsgIn; + unsigned int m_nQueueCount; + bool m_bWriteIgnore; +#endif + HSCRIPT m_Hooks; + bf_write m_MsgOut; + byte m_MsgData[ PAD_NUMBER( SCRIPT_NETMSG_DATA_SIZE, 4 ) ]; + +#ifdef CLIENT_DLL + int m_iLastBit; +#endif + +public: + CNetMsgScriptHelper() : m_Hooks(NULL) + +#ifdef CLIENT_DLL + , m_bWriteReady(0), m_bWriteIgnore(0), m_nQueueCount(0), m_iLastBit(0) +#else + , m_MsgIn(0) +#endif + + {} + +public: +#ifdef CLIENT_DLL + bool Init(); // IGameSystem + static void __MsgFunc_ScriptMsg( bf_read &msg ); +#endif + void LevelShutdownPreVM(); // Executed in CVScriptGameSystem + void InitPostVM(); + +#ifdef GAME_DLL + void ReceiveMessage( bf_read *msg, CBaseEntity *pPlayer ); +#else + void ReceiveMessage( bf_read &msg ); +#endif + void WriteToBuffer( bf_write *bf ); + +public: + inline void Reset(); + void Start( const char *msg ); +#ifdef GAME_DLL + void Send( HSCRIPT player, bool bReliable ); +#else + void Send(); +#endif + void Receive( const char *msg, HSCRIPT func ); + +#ifdef GAME_DLL + inline void DoSendUserMsg( CRecipientFilter *filter, int type ); + inline void DoSendEntityMsg( CBaseEntity *entity, bool reliable ); + + void SendUserMessage( HSCRIPT hPlayer, const char *msg, bool bReliable ); + void SendEntityMessage( HSCRIPT hEnt, bool bReliable ); +#else // CLIENT_DLL + void DispatchUserMessage( const char *msg ); +#endif + +public: + void WriteInt( int iValue, int bits ); + void WriteUInt( int iValue, int bits ); + void WriteByte( int iValue ); // 8 bit unsigned char + void WriteChar( int iValue ); // 8 bit char + void WriteShort( int iValue ); // 16 bit short + void WriteWord( int iValue ); // 16 bit unsigned short + void WriteLong( int iValue ); // 32 bit long + void WriteFloat( float flValue ); // 32 bit float + void WriteNormal( float flValue ); // 12 bit (1 + NORMAL_FRACTIONAL_BITS) + void WriteAngle( float flValue ); // 8 bit unsigned char + void WriteCoord( float flValue ); + void WriteVec3Coord( const Vector& rgflValue ); + void WriteVec3Normal( const Vector& rgflValue ); // 27 bit ( 3 + 2 * (1 + NORMAL_FRACTIONAL_BITS) ) + void WriteAngles( const QAngle& rgflValue ); + void WriteString( const char *sz ); + void WriteBool( bool bValue ); // 1 bit + void WriteEntity( HSCRIPT hEnt ); // 11 bit (entindex) + void WriteEHandle( HSCRIPT hEnt ); // 32 bit long + int ReadInt( int bits ); + int ReadUInt( int bits ); + int ReadByte(); + int ReadChar(); + int ReadShort(); + int ReadWord(); + int ReadLong(); + float ReadFloat(); + float ReadNormal(); + float ReadAngle(); + float ReadCoord(); + const Vector& ReadVec3Coord(); + const Vector& ReadVec3Normal(); + const QAngle& ReadAngles(); + const char* ReadString(); + bool ReadBool(); + HSCRIPT ReadEntity(); + HSCRIPT ReadEHandle(); + //int GetNumBitsLeft(); // unreliable on server because of usercmds. so just do away with it + int GetNumBitsWritten(); + +public: + static inline int Hash( const char *key ); +}; + +extern CNetMsgScriptHelper *g_ScriptNetMsg; + +#ifdef CLIENT_DLL +void VScriptSaveRestoreUtil_OnVMRestore(); +#endif + #endif diff --git a/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp index 9a537723..8878ead8 100644 --- a/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/mp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -175,8 +175,13 @@ CWeaponCustomScripted::CWeaponCustomScripted() 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 ) + { + if ( hook.CanRunInScope( m_ScriptScope ) ) + { + cached = hook.m_hFunc; + } + } if (cached) { @@ -396,9 +401,10 @@ bool CWeaponCustomScripted::Reload( void ) return BaseClass::Reload(); } -void CWeaponCustomScripted::Reload_NPC( void ) +void CWeaponCustomScripted::Reload_NPC( bool bPlaySound ) { - SIMPLE_VOID_OVERRIDE( Reload_NPC, NULL ); + ScriptVariant_t pArgs[] = { bPlaySound }; + SIMPLE_VOID_OVERRIDE( Reload_NPC, pArgs ); BaseClass::Reload_NPC(); } diff --git a/mp/src/game/shared/mapbase/weapon_custom_scripted.h b/mp/src/game/shared/mapbase/weapon_custom_scripted.h index dd9841dd..179ee155 100644 --- a/mp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/mp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -5,8 +5,8 @@ // $NoKeywords: $ //============================================================================= -#ifndef VSCRIPT_FUNCS_MATH -#define VSCRIPT_FUNCS_MATH +#ifndef WEAPON_CUSTOM_SCRIPTED_H +#define WEAPON_CUSTOM_SCRIPTED_H #ifdef _WIN32 #pragma once #endif @@ -77,7 +77,7 @@ public: void FinishReload( void ); void AbortReload( void ); bool Reload( void ); - void Reload_NPC( void ); + void Reload_NPC( bool bPlaySound = true ); // Weapon firing void PrimaryAttack( void ); // do "+ATTACK" diff --git a/mp/src/game/shared/movevars_shared.cpp b/mp/src/game/shared/movevars_shared.cpp index b5b94c72..563d96e3 100644 --- a/mp/src/game/shared/movevars_shared.cpp +++ b/mp/src/game/shared/movevars_shared.cpp @@ -36,7 +36,7 @@ float GetCurrentGravity( void ) ConVar sv_gravity ( "sv_gravity", DEFAULT_GRAVITY_STRING, FCVAR_NOTIFY | FCVAR_REPLICATED, "World gravity." ); -#if defined( DOD_DLL ) || defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) +#if defined( DOD_DLL ) || defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) || defined( MAPBASE ) ConVar sv_stopspeed ( "sv_stopspeed","100", FCVAR_NOTIFY | FCVAR_REPLICATED, "Minimum stopping speed when on ground." ); #else ConVar sv_stopspeed ( "sv_stopspeed","100", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Minimum stopping speed when on ground." ); @@ -48,7 +48,7 @@ ConVar sv_specaccelerate( "sv_specaccelerate", "5", FCVAR_NOTIFY | FCVAR_ARCHIVE ConVar sv_specspeed ( "sv_specspeed", "3", FCVAR_ARCHIVE | FCVAR_NOTIFY | FCVAR_REPLICATED); ConVar sv_specnoclip ( "sv_specnoclip", "1", FCVAR_ARCHIVE | FCVAR_NOTIFY | FCVAR_REPLICATED); -#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) +#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) || defined( MAPBASE ) ConVar sv_maxspeed ( "sv_maxspeed", "320", FCVAR_NOTIFY | FCVAR_REPLICATED); #else ConVar sv_maxspeed ( "sv_maxspeed", "320", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY); @@ -58,7 +58,7 @@ ConVar sv_maxspeed ( "sv_maxspeed", "320", FCVAR_NOTIFY | FCVAR_REPLICATED | FC ConVar sv_accelerate ( "sv_accelerate", "7", FCVAR_NOTIFY | FCVAR_REPLICATED); #else -#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) +#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) || defined( MAPBASE ) ConVar sv_accelerate ( "sv_accelerate", "10", FCVAR_NOTIFY | FCVAR_REPLICATED); #else ConVar sv_accelerate ( "sv_accelerate", "10", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY); @@ -66,7 +66,7 @@ ConVar sv_maxspeed ( "sv_maxspeed", "320", FCVAR_NOTIFY | FCVAR_REPLICATED | FC #endif//_XBOX -#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) +#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) || defined( MAPBASE ) ConVar sv_airaccelerate( "sv_airaccelerate", "10", FCVAR_NOTIFY | FCVAR_REPLICATED); ConVar sv_wateraccelerate( "sv_wateraccelerate", "10", FCVAR_NOTIFY | FCVAR_REPLICATED); ConVar sv_waterfriction( "sv_waterfriction", "1", FCVAR_NOTIFY | FCVAR_REPLICATED); @@ -82,13 +82,13 @@ ConVar sv_rollspeed ( "sv_rollspeed", "200", FCVAR_NOTIFY | FCVAR_REPLICATED | F ConVar sv_rollangle ( "sv_rollangle", "0", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Max view roll angle"); #endif // CSTRIKE_DLL -#if defined( DOD_DLL ) || defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) +#if defined( DOD_DLL ) || defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) || defined( MAPBASE ) ConVar sv_friction ( "sv_friction","4", FCVAR_NOTIFY | FCVAR_REPLICATED, "World friction." ); #else ConVar sv_friction ( "sv_friction","4", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "World friction." ); #endif // DOD_DLL || CSTRIKE_DLL -#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) +#if defined( CSTRIKE_DLL ) || defined( HL1MP_DLL ) || defined( MAPBASE ) ConVar sv_bounce ( "sv_bounce","0", FCVAR_NOTIFY | FCVAR_REPLICATED, "Bounce multiplier for when physically simulated objects collide with other objects." ); ConVar sv_maxvelocity ( "sv_maxvelocity","3500", FCVAR_REPLICATED, "Maximum speed any ballistically moving object is allowed to attain per axis." ); ConVar sv_stepsize ( "sv_stepsize","18", FCVAR_NOTIFY | FCVAR_REPLICATED ); diff --git a/mp/src/game/shared/ragdoll_shared.cpp b/mp/src/game/shared/ragdoll_shared.cpp index 3403426b..17d9e702 100644 --- a/mp/src/game/shared/ragdoll_shared.cpp +++ b/mp/src/game/shared/ragdoll_shared.cpp @@ -174,6 +174,10 @@ void RagdollSetupAnimatedFriction( IPhysicsEnvironment *pPhysEnv, ragdoll_t *rag } } +#ifdef MAPBASE +ConVar g_ragdoll_fixed_constraints_mass( "g_ragdoll_fixed_constraints_mass", "1000", FCVAR_REPLICATED ); +#endif + static void RagdollAddSolid( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms, solid_t &solid ) { if ( solid.index >= 0 && solid.index < params.pCollide->solidCount) @@ -186,7 +190,12 @@ static void RagdollAddSolid( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, { if ( params.fixedConstraints ) { +#ifdef MAPBASE + if (g_ragdoll_fixed_constraints_mass.GetFloat() != -1) + solid.params.mass = g_ragdoll_fixed_constraints_mass.GetFloat(); +#else solid.params.mass = 1000.f; +#endif } solid.params.rotInertiaLimit = 0.1; @@ -825,6 +834,33 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION m_iRagdollCount = 0; m_iSimulatedRagdollCount = 0; +#ifdef MAPBASE // From Alien Swarm SDK + // remove ragdolls with a forced retire time + for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next ) + { + next = m_LRU.Next(i); + + CBaseAnimating *pRagdoll = m_LRU[i].Get(); + + //Just ignore it until we're done burning/dissolving. + if ( pRagdoll && pRagdoll->GetEffectEntity() ) + continue; + + // ignore if it's not time to force retire this ragdoll + if ( m_LRU[i].GetForcedRetireTime() == 0.0f || gpGlobals->curtime < m_LRU[i].GetForcedRetireTime() ) + continue; + + //Msg(" Removing ragdoll %s due to forced retire time of %f (now = %f)\n", pRagdoll->GetModelName(), m_LRU[i].GetForcedRetireTime(), gpGlobals->curtime ); + +#ifdef CLIENT_DLL + pRagdoll->SUB_Remove(); +#else + pRagdoll->SUB_StartFadeOut( 0 ); +#endif + m_LRU.Remove(i); + } +#endif + // First, find ragdolls that are good candidates for deletion because they are not // visible at all, or are in a culled visibility box for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next ) @@ -842,12 +878,12 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION if ( m_LRU.Count() > iMaxRagdollCount ) { //Found one, we're done. - if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true ) + if ( ShouldRemoveThisRagdoll( pRagdoll ) == true ) { #ifdef CLIENT_DLL - m_LRU[ i ]->SUB_Remove(); + pRagdoll->SUB_Remove(); #else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); + pRagdoll->SUB_StartFadeOut( 0 ); #endif m_LRU.Remove(i); @@ -928,10 +964,11 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION } } + CBaseAnimating *pRemoveRagdoll = m_LRU[ furthestOne ].Get(); #ifdef CLIENT_DLL - m_LRU[ furthestOne ]->SUB_Remove(); + pRemoveRagdoll->SUB_Remove(); #else - m_LRU[ furthestOne ]->SUB_StartFadeOut( 0 ); + pRemoveRagdoll->SUB_StartFadeOut( 0 ); #endif } @@ -952,9 +989,9 @@ void CRagdollLRURetirement::Update( float frametime ) // EPISODIC VERSION continue; #ifdef CLIENT_DLL - m_LRU[ i ]->SUB_Remove(); + pRagdoll->SUB_Remove(); #else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); + pRagdoll->SUB_StartFadeOut( 0 ); #endif m_LRU.Remove(i); } @@ -984,6 +1021,33 @@ void CRagdollLRURetirement::Update( float frametime ) // Non-episodic version m_iRagdollCount = 0; m_iSimulatedRagdollCount = 0; +#ifdef MAPBASE // From Alien Swarm SDK + // remove ragdolls with a forced retire time + for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next ) + { + next = m_LRU.Next(i); + + CBaseAnimating *pRagdoll = m_LRU[i].Get(); + + //Just ignore it until we're done burning/dissolving. + if ( pRagdoll && pRagdoll->GetEffectEntity() ) + continue; + + // ignore if it's not time to force retire this ragdoll + if ( m_LRU[i].GetForcedRetireTime() == 0.0f || gpGlobals->curtime < m_LRU[i].GetForcedRetireTime() ) + continue; + + //Msg(" Removing ragdoll %s due to forced retire time of %f (now = %f)\n", pRagdoll->GetModelName(), m_LRU[i].GetForcedRetireTime(), gpGlobals->curtime ); + +#ifdef CLIENT_DLL + pRagdoll->SUB_Remove(); +#else + pRagdoll->SUB_StartFadeOut( 0 ); +#endif + m_LRU.Remove(i); + } +#endif + for ( i = m_LRU.Head(); i < m_LRU.InvalidIndex(); i = next ) { next = m_LRU.Next(i); @@ -999,12 +1063,12 @@ void CRagdollLRURetirement::Update( float frametime ) // Non-episodic version if ( m_LRU.Count() > iMaxRagdollCount ) { //Found one, we're done. - if ( ShouldRemoveThisRagdoll( m_LRU[i] ) == true ) + if ( ShouldRemoveThisRagdoll( pRagdoll ) == true ) { #ifdef CLIENT_DLL - m_LRU[ i ]->SUB_Remove(); + pRagdoll->SUB_Remove(); #else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); + pRagdoll->SUB_StartFadeOut( 0 ); #endif m_LRU.Remove(i); @@ -1048,9 +1112,9 @@ void CRagdollLRURetirement::Update( float frametime ) // Non-episodic version #endif #ifdef CLIENT_DLL - m_LRU[ i ]->SUB_Remove(); + pRagdoll->SUB_Remove(); #else - m_LRU[ i ]->SUB_StartFadeOut( 0 ); + pRagdoll->SUB_StartFadeOut( 0 ); #endif m_LRU.Remove(i); } @@ -1069,11 +1133,19 @@ ConVar g_ragdoll_important_maxcount( "g_ragdoll_important_maxcount", "2", FCVAR_ //----------------------------------------------------------------------------- // Move it to the top of the LRU //----------------------------------------------------------------------------- +#ifdef MAPBASE // From Alien Swarm SDK +void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant, float flForcedRetireTime ) +#else void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant ) +#endif { if ( bImportant ) { +#ifdef MAPBASE // From Alien Swarm SDK + m_LRUImportantRagdolls.AddToTail( CRagdollEntry( pRagdoll, flForcedRetireTime ) ); +#else m_LRUImportantRagdolls.AddToTail( pRagdoll ); +#endif if ( m_LRUImportantRagdolls.Count() > g_ragdoll_important_maxcount.GetInt() ) { @@ -1103,7 +1175,11 @@ void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImpo } } +#ifdef MAPBASE // From Alien Swarm SDK + m_LRU.AddToTail( CRagdollEntry( pRagdoll, flForcedRetireTime ) ); +#else m_LRU.AddToTail( pRagdoll ); +#endif } diff --git a/mp/src/game/shared/ragdoll_shared.h b/mp/src/game/shared/ragdoll_shared.h index f230e99f..5f4f7058 100644 --- a/mp/src/game/shared/ragdoll_shared.h +++ b/mp/src/game/shared/ragdoll_shared.h @@ -27,7 +27,11 @@ class CBoneAccessor; #include "bone_accessor.h" // UNDONE: Remove and make dynamic? +#ifdef MAPBASE +#define RAGDOLL_MAX_ELEMENTS 32 // Mapbase boosts this limit to the level of later Source games. +#else #define RAGDOLL_MAX_ELEMENTS 24 +#endif #define RAGDOLL_INDEX_BITS 5 // NOTE 1<= RAGDOLL_MAX_ELEMENTS #define CORE_DISSOLVE_FADE_START 0.2f @@ -79,6 +83,22 @@ struct ragdollparams_t bool fixedConstraints; }; +#ifdef MAPBASE // From Alien Swarm SDK +class CRagdollEntry +{ +public: + CRagdollEntry( CBaseAnimating *pRagdoll, float flForcedRetireTime ) : m_hRagdoll( pRagdoll ), m_flForcedRetireTime( flForcedRetireTime ) + { + } + CBaseAnimating* Get() { return m_hRagdoll.Get(); } + float GetForcedRetireTime() { return m_flForcedRetireTime; } + +private: + CHandle m_hRagdoll; + float m_flForcedRetireTime; +}; +#endif + //----------------------------------------------------------------------------- // This hooks the main game systems callbacks to allow the AI system to manage memory //----------------------------------------------------------------------------- @@ -94,7 +114,11 @@ public: virtual void FrameUpdatePostEntityThink( void ); // Move it to the top of the LRU +#ifdef MAPBASE // From Alien Swarm SDK + void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false, float flForcedRetireTime = 0.0f ); +#else void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false ); +#endif void SetMaxRagdollCount( int iMaxCount ){ m_iMaxRagdolls = iMaxCount; } virtual void LevelInitPreEntity( void ); @@ -102,8 +126,13 @@ public: private: typedef CHandle CRagdollHandle; +#ifdef MAPBASE + CUtlLinkedList< CRagdollEntry > m_LRU; + CUtlLinkedList< CRagdollEntry > m_LRUImportantRagdolls; +#else CUtlLinkedList< CRagdollHandle > m_LRU; CUtlLinkedList< CRagdollHandle > m_LRUImportantRagdolls; +#endif int m_iMaxRagdolls; int m_iSimulatedRagdollCount; diff --git a/mp/src/game/shared/shareddefs.h b/mp/src/game/shared/shareddefs.h index 3ea5074e..68ad3294 100644 --- a/mp/src/game/shared/shareddefs.h +++ b/mp/src/game/shared/shareddefs.h @@ -368,6 +368,14 @@ enum PLAYER_ANIM PLAYER_RELOAD, PLAYER_START_AIMING, PLAYER_LEAVE_AIMING, + +#ifdef MAPBASE + // New player animations from Mapbase + PLAYER_ATTACK2, + PLAYER_ATTACK3, + PLAYER_UNHOLSTER, + PLAYER_HOLSTER, +#endif }; #ifdef HL2_DLL @@ -679,6 +687,9 @@ enum FireBulletsFlags_t FIRE_BULLETS_DONT_HIT_UNDERWATER = 0x2, // If the shot hits its target underwater, don't damage it FIRE_BULLETS_ALLOW_WATER_SURFACE_IMPACTS = 0x4, // If the shot hits water surface, still call DoImpactEffect FIRE_BULLETS_TEMPORARY_DANGER_SOUND = 0x8, // Danger sounds added from this impact can be stomped immediately if another is queued +#ifdef MAPBASE + FIRE_BULLETS_NO_AUTO_GIB_TYPE = 0x10, // Don't automatically add DMG_ALWAYSGIB or DMG_NEVERGIB if m_flDamage is set +#endif }; @@ -1054,4 +1065,16 @@ enum }; #endif // TF_DLL || TF_CLIENT_DLL +#ifdef MAPBASE +// Developer commentary types +enum +{ + COMMENTARY_TYPE_AUDIO, // Play commentary audio (default) + + COMMENTARY_TYPE_TEXT, // Display text data + COMMENTARY_TYPE_IMAGE, // Display an image + COMMENTARY_TYPE_SCENE, // Play a VCD file +}; +#endif + #endif // SHAREDDEFS_H diff --git a/mp/src/game/shared/takedamageinfo.h b/mp/src/game/shared/takedamageinfo.h index 1cea7a98..b4aaff84 100644 --- a/mp/src/game/shared/takedamageinfo.h +++ b/mp/src/game/shared/takedamageinfo.h @@ -370,12 +370,12 @@ inline void CTakeDamageInfo::SetDamageCustom( int iDamageCustom ) inline int CTakeDamageInfo::GetDamageStats() const { - return m_iDamageCustom; + return m_iDamageStats; } inline void CTakeDamageInfo::SetDamageStats( int iDamageCustom ) { - m_iDamageCustom = iDamageCustom; + m_iDamageStats = iDamageCustom; } inline int CTakeDamageInfo::GetAmmoType() const diff --git a/mp/src/game/shared/usercmd.cpp b/mp/src/game/shared/usercmd.cpp index 84ffa243..1607eba3 100644 --- a/mp/src/game/shared/usercmd.cpp +++ b/mp/src/game/shared/usercmd.cpp @@ -10,12 +10,20 @@ #include "bitbuf.h" #include "checksum_md5.h" +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_singletons.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // TF2 specific, need enough space for OBJ_LAST items from tf_shareddefs.h #define WEAPON_SUBTYPE_BITS 6 +#ifdef MAPBASE_VSCRIPT +extern CNetMsgScriptHelper *g_ScriptNetMsg; +#endif + //----------------------------------------------------------------------------- // Purpose: Write a delta compressed user command. // Input : *buf - @@ -187,6 +195,22 @@ void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from ) buf->WriteOneBit( 0 ); } #endif + +#if defined( MAPBASE_VSCRIPT ) && defined( CLIENT_DLL ) + Assert( g_ScriptNetMsg ); + + if ( g_ScriptNetMsg->m_bWriteReady ) + { + buf->WriteOneBit( 1 ); + g_ScriptNetMsg->WriteToBuffer( buf ); + g_ScriptNetMsg->m_bWriteReady = false; + } + else + { + buf->WriteOneBit( 0 ); + } +#endif + } //----------------------------------------------------------------------------- @@ -196,7 +220,11 @@ void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from ) // *from - // Output : static void ReadUsercmd //----------------------------------------------------------------------------- +#if defined( MAPBASE_VSCRIPT ) && defined( GAME_DLL ) +void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from, CBaseEntity *pPlayer ) +#else void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from ) +#endif { // Assume no change *move = *from; @@ -303,4 +331,12 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from ) } } #endif + +#if defined( MAPBASE_VSCRIPT ) && defined( GAME_DLL ) + if ( buf->ReadOneBit() ) + { + g_ScriptNetMsg->ReceiveMessage( buf, pPlayer ); + } +#endif + } diff --git a/mp/src/game/shared/usercmd.h b/mp/src/game/shared/usercmd.h index f3ff1b1b..6f104b1a 100644 --- a/mp/src/game/shared/usercmd.h +++ b/mp/src/game/shared/usercmd.h @@ -133,39 +133,6 @@ 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; @@ -207,7 +174,11 @@ public: }; +#if defined( MAPBASE_VSCRIPT ) && defined( GAME_DLL ) +void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from, CBaseEntity *pPlayer ); +#else void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from ); +#endif void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from ); #endif // USERCMD_H diff --git a/mp/src/game/shared/usermessages.cpp b/mp/src/game/shared/usermessages.cpp index cf1beae3..6b3ad681 100644 --- a/mp/src/game/shared/usermessages.cpp +++ b/mp/src/game/shared/usermessages.cpp @@ -13,6 +13,10 @@ void RegisterUserMessages( void ); +#ifdef MAPBASE +void RegisterMapbaseUserMessages( void ); +#endif + //----------------------------------------------------------------------------- // Purpose: Force registration on .dll load // FIXME: Should this be a client/server system? @@ -21,6 +25,11 @@ CUserMessages::CUserMessages() { // Game specific registration function; RegisterUserMessages(); + +#ifdef MAPBASE + // Mapbase registration function; + RegisterMapbaseUserMessages(); +#endif } CUserMessages::~CUserMessages() diff --git a/mp/src/game/shared/util_shared.cpp b/mp/src/game/shared/util_shared.cpp index 988b2c49..183aed10 100644 --- a/mp/src/game/shared/util_shared.cpp +++ b/mp/src/game/shared/util_shared.cpp @@ -1126,7 +1126,7 @@ float CountdownTimer::Now( void ) const #endif -char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename ) +const char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename ) { const char *pValue = pSub->GetString( pName, NULL ); if ( !pValue ) diff --git a/mp/src/game/shared/util_shared.h b/mp/src/game/shared/util_shared.h index 549d1dc4..404bfe84 100644 --- a/mp/src/game/shared/util_shared.h +++ b/mp/src/game/shared/util_shared.h @@ -628,7 +628,7 @@ class RealTimeCountdownTimer : public CountdownTimer } }; -char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename = NULL ); +const char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename = NULL ); int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings ); diff --git a/mp/src/game/shared/vscript_shared.cpp b/mp/src/game/shared/vscript_shared.cpp index ebe997a8..9f1d610c 100644 --- a/mp/src/game/shared/vscript_shared.cpp +++ b/mp/src/game/shared/vscript_shared.cpp @@ -13,6 +13,9 @@ #include "characterset.h" #include "isaverestore.h" #include "gamerules.h" +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_singletons.h" +#endif IScriptVM * g_pScriptVM; extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); @@ -37,6 +40,15 @@ extern int vscript_token; int vscript_token_hack = vscript_token; #endif +static const char *pszExtensions[] = +{ + "", // SL_NONE + ".gm", // SL_GAMEMONKEY + ".nut", // SL_SQUIRREL + ".lua", // SL_LUA + ".py", // SL_PYTHON +}; + HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) @@ -46,20 +58,15 @@ HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) 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 ) { +#ifdef MAPBASE_VSCRIPT + CGWarning( 0, CON_GROUP_VSCRIPT, "Script file type (\"%s\", from \"%s\") does not match VM type (\"%s\")\n", pszIncomingExtension, pszScriptName, pszVMExtension ); +#else CGWarning( 0, CON_GROUP_VSCRIPT, "Script file type does not match VM type\n" ); +#endif return NULL; } @@ -142,6 +149,9 @@ bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMiss bool bSuccess = false; if ( hScript ) { + // player is not yet spawned, this block is always skipped. + // It is registered in CBasePlayer instead. +#ifndef MAPBASE #ifdef GAME_DLL if ( gpGlobals->maxClients == 1 ) { @@ -151,6 +161,7 @@ bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMiss g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); } } +#endif #endif bSuccess = ( g_pScriptVM->Run( hScript, hScope ) != SCRIPT_ERROR ); if ( !bSuccess ) @@ -163,12 +174,137 @@ bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMiss return bSuccess; } -#ifdef CLIENT_DLL -CON_COMMAND( script_client, "Run the text as a script" ) + +#ifdef MAPBASE_VSCRIPT + +// +// These functions are currently only used for "mapspawn_addon" scripts. +// +HSCRIPT VScriptCompileScriptAbsolute( const char *pszScriptName, bool bWarnMissing, const char *pszRootFolderName ) +{ + if ( !g_pScriptVM ) + { + return NULL; + } + + const char *pszVMExtension = pszExtensions[g_pScriptVM->GetLanguage()]; + const char *pszIncomingExtension = V_strrchr( pszScriptName , '.' ); + if ( pszIncomingExtension && V_strcmp( pszIncomingExtension, pszVMExtension ) != 0 ) + { + // Account for cases where there is no extension and the folder names just have dots (e.g. ".local") + if ( strchr( pszIncomingExtension, CORRECT_PATH_SEPARATOR ) ) + { + pszIncomingExtension = NULL; + } + else + { + CGWarning( 0, CON_GROUP_VSCRIPT, "Script file type (\"%s\", from \"%s\") does not match VM type (\"%s\")\n", pszIncomingExtension, pszScriptName, pszVMExtension ); + 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 + { + bool bResult = filesystem->ReadFile( scriptPath, NULL, bufferScript ); + + if ( !bResult && bWarnMissing ) + { + 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; + } + } + + // Attach the folder to the script ID + const char *pszFilename = V_strrchr( scriptPath, '/' ); + scriptPath.sprintf( "%s%s", pszRootFolderName, pszFilename ); + + HSCRIPT hScript = g_pScriptVM->CompileScript( pBase, scriptPath ); + 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; +} + +bool VScriptRunScriptAbsolute( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing, const char *pszRootFolderName ) +{ + 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 = VScriptCompileScriptAbsolute( pszScriptName, bWarnMissing, pszRootFolderName ); + 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_ScriptServerRunScriptDepth--; + return bSuccess; +} +#endif + + +#ifdef GAME_DLL +#define IsCommandIssuedByServerAdmin() UTIL_IsCommandIssuedByServerAdmin() #else -CON_COMMAND( script, "Run the text as a script" ) +#define IsCommandIssuedByServerAdmin() true +#endif + +#ifdef CLIENT_DLL +CON_COMMAND_F( script_client, "Run the text as a script", FCVAR_CHEAT ) +#else +CON_COMMAND_F( script, "Run the text as a script", FCVAR_CHEAT ) #endif { + if ( !IsCommandIssuedByServerAdmin() ) + return; + if ( !*args[1] ) { CGWarning( 0, CON_GROUP_VSCRIPT, "No function name specified\n" ); @@ -221,9 +357,15 @@ CON_COMMAND( script, "Run the text as a script" ) } } - -CON_COMMAND_SHARED( script_execute, "Run a vscript file" ) +#ifdef CLIENT_DLL +CON_COMMAND_F( script_execute_client, "Run a vscript file", FCVAR_CHEAT ) +#else +CON_COMMAND_F( script_execute, "Run a vscript file", FCVAR_CHEAT ) +#endif { + if ( !IsCommandIssuedByServerAdmin() ) + return; + if ( !*args[1] ) { CGWarning( 0, CON_GROUP_VSCRIPT, "No script specified\n" ); @@ -239,8 +381,15 @@ CON_COMMAND_SHARED( script_execute, "Run a vscript file" ) VScriptRunScript( args[1], true ); } -CON_COMMAND_SHARED( script_debug, "Connect the vscript VM to the script debugger" ) +#ifdef CLIENT_DLL +CON_COMMAND_F( script_debug_client, "Connect the vscript VM to the script debugger", FCVAR_CHEAT ) +#else +CON_COMMAND_F( script_debug, "Connect the vscript VM to the script debugger", FCVAR_CHEAT ) +#endif { + if ( !IsCommandIssuedByServerAdmin() ) + return; + if ( !g_pScriptVM ) { CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); @@ -249,8 +398,15 @@ CON_COMMAND_SHARED( script_debug, "Connect the vscript VM to the script debugger g_pScriptVM->ConnectDebugger(); } -CON_COMMAND_SHARED( script_help, "Output help for script functions, optionally with a search string" ) +#ifdef CLIENT_DLL +CON_COMMAND_F( script_help_client, "Output help for script functions, optionally with a search string", FCVAR_CHEAT ) +#else +CON_COMMAND_F( script_help, "Output help for script functions, optionally with a search string", FCVAR_CHEAT ) +#endif { + if ( !IsCommandIssuedByServerAdmin() ) + return; + if ( !g_pScriptVM ) { CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); @@ -262,11 +418,18 @@ CON_COMMAND_SHARED( script_help, "Output help for script functions, optionally w pszArg1 = args[1]; } - g_pScriptVM->Run( CFmtStr( "PrintHelp( \"%s\" );", pszArg1 ) ); + g_pScriptVM->Run( CFmtStr( "__Documentation.PrintHelp( \"%s\" );", pszArg1 ) ); } -CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) +#ifdef CLIENT_DLL +CON_COMMAND_F( script_dump_all_client, "Dump the state of the VM to the console", FCVAR_CHEAT ) +#else +CON_COMMAND_F( script_dump_all, "Dump the state of the VM to the console", FCVAR_CHEAT ) +#endif { + if ( !IsCommandIssuedByServerAdmin() ) + return; + if ( !g_pScriptVM ) { CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); @@ -274,3 +437,263 @@ CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) } g_pScriptVM->DumpState(); } + +//----------------------------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT +void RunAddonScripts() +{ + char searchPaths[4096]; + filesystem->GetSearchPath( "ADDON", true, searchPaths, sizeof( searchPaths ) ); + + for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) ) + { + char folderName[MAX_PATH]; + Q_FileBase( path, folderName, sizeof( folderName ) ); + + // mapspawn_addon + char fullpath[MAX_PATH]; + Q_snprintf( fullpath, sizeof( fullpath ), "%sscripts/vscripts/mapspawn_addon", path ); + Q_FixSlashes( fullpath ); + + VScriptRunScriptAbsolute( fullpath, NULL, false, folderName ); + } +} + +// UNDONE: "autorun" folder +/* +void RunAutorunScripts() +{ + FileFindHandle_t fileHandle; + char szDirectory[MAX_PATH]; + char szFileName[MAX_PATH]; + char szPartialScriptPath[MAX_PATH]; + + // TODO: Scanning for VM extension would make this more efficient + Q_strncpy( szDirectory, "scripts/vscripts/autorun/*", sizeof( szDirectory ) ); + + const char *pszScriptFile = filesystem->FindFirst( szDirectory, &fileHandle ); + while (pszScriptFile && fileHandle != FILESYSTEM_INVALID_FIND_HANDLE) + { + Q_FileBase( pszScriptFile, szFileName, sizeof( szFileName ) ); + Q_snprintf( szPartialScriptPath, sizeof( szPartialScriptPath ), "autorun/%s", szFileName ); + VScriptRunScript( szPartialScriptPath ); + + pszScriptFile = filesystem->FindNext( fileHandle ); + } + + // Non-shared scripts +#ifdef CLIENT_DLL + Q_strncpy( szDirectory, "scripts/vscripts/autorun/client/*", sizeof( szDirectory ) ); +#else + Q_strncpy( szDirectory, "scripts/vscripts/autorun/server/*", sizeof( szDirectory ) ); +#endif + + pszScriptFile = filesystem->FindFirst( szDirectory, &fileHandle ); + while (pszScriptFile && fileHandle != FILESYSTEM_INVALID_FIND_HANDLE) + { + Q_FileBase( pszScriptFile, szFileName, sizeof( szFileName ) ); +#ifdef CLIENT_DLL + Q_snprintf( szPartialScriptPath, sizeof( szPartialScriptPath ), "autorun/client/%s", szFileName ); +#else + Q_snprintf( szPartialScriptPath, sizeof( szPartialScriptPath ), "autorun/server/%s", szFileName ); +#endif + VScriptRunScript( szPartialScriptPath ); + + pszScriptFile = filesystem->FindNext( fileHandle ); + } +} +*/ +#endif + +//----------------------------------------------------------------------------- + +static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2; + +//----------------------------------------------------------------------------- + +class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + CVScriptSaveRestoreBlockHandler() : + m_InstanceMap( DefLessFunc(const char *) ) + { + } + const char *GetBlockName() + { +#ifdef CLIENT_DLL + return "VScriptClient"; +#else + return "VScriptServer"; +#endif + } + + //--------------------------------- + + 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; + } +#ifdef CLIENT_DLL + C_BaseEntity *pEnt = ClientEntityList().FirstBaseEntity(); +#else + CBaseEntity *pEnt = gEntList.FirstEnt(); +#endif + 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 ); + } +#ifdef CLIENT_DLL + pEnt = ClientEntityList().NextBaseEntity( pEnt ); +#else + pEnt = gEntList.NextEnt( pEnt ); +#endif + } + + 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 ); +#ifndef CLIENT_DLL + pEnt->RunPrecacheScripts(); +#endif + } + } + else + { + // Script system probably has no internal references + pEnt->m_iszScriptId = NULL_STRING; + } + } + m_InstanceMap.Purge(); + +#ifdef MAPBASE_VSCRIPT + GetScriptHookManager().OnRestore(); +#endif + +#if defined(MAPBASE_VSCRIPT) && defined(CLIENT_DLL) + VScriptSaveRestoreUtil_OnVMRestore(); +#endif + } + + + 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; +#ifdef CLIENT_DLL + if ( pEntity->GetEntityName() && pEntity->GetEntityName()[0] ) +#else + if ( pEntity->GetEntityName() != NULL_STRING ) +#endif + { + V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), pEntity->GetClassname(), STRING( pEntity->GetEntityName() ) ); + } + else + { + V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), pEntity->GetClassname() ); + } + 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/shared/vscript_shared.h b/mp/src/game/shared/vscript_shared.h index a71d20b2..50834220 100644 --- a/mp/src/game/shared/vscript_shared.h +++ b/mp/src/game/shared/vscript_shared.h @@ -31,9 +31,22 @@ inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = fal // Only allow scripts to create entities during map initialization bool IsEntityCreationAllowedInScripts( void ); +class ISaveRestoreBlockHandler; +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; + #ifdef MAPBASE_VSCRIPT void RegisterSharedScriptConstants(); void RegisterSharedScriptFunctions(); + +void RunAddonScripts(); #endif #endif // VSCRIPT_SHARED_H diff --git a/mp/src/game/shared/weapon_parse.cpp b/mp/src/game/shared/weapon_parse.cpp index 31ff154a..feda6e86 100644 --- a/mp/src/game/shared/weapon_parse.cpp +++ b/mp/src/game/shared/weapon_parse.cpp @@ -401,6 +401,14 @@ FileWeaponInfo_t::FileWeaponInfo_t() bShowUsageHint = false; m_bAllowFlipping = true; m_bBuiltRightHanded = true; +#ifdef MAPBASE + m_flViewmodelFOV = 0.0f; + m_flBobScale = 1.0f; + m_flSwayScale = 1.0f; + m_flSwaySpeedScale = 1.0f; + szDroppedModel[0] = 0; + m_bUsesHands = false; +#endif } #ifdef CLIENT_DLL @@ -466,6 +474,17 @@ 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; +#ifdef MAPBASE + m_flViewmodelFOV = pKeyValuesData->GetFloat( "viewmodel_fov", 0.0f ); + m_flBobScale = pKeyValuesData->GetFloat( "bob_scale", 1.0f ); + m_flSwayScale = pKeyValuesData->GetFloat( "sway_scale", 1.0f ); + m_flSwaySpeedScale = pKeyValuesData->GetFloat( "sway_speed_scale", 1.0f ); + + Q_strncpy( szDroppedModel, pKeyValuesData->GetString( "droppedmodel" ), MAX_WEAPON_STRING ); + + m_bUsesHands = ( pKeyValuesData->GetInt( "uses_hands", 0 ) != 0 ) ? true : false; +#endif + #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 diff --git a/mp/src/game/shared/weapon_parse.h b/mp/src/game/shared/weapon_parse.h index 56803dfa..28032744 100644 --- a/mp/src/game/shared/weapon_parse.h +++ b/mp/src/game/shared/weapon_parse.h @@ -116,6 +116,17 @@ public: bool m_bAllowFlipping; // False to disallow flipping the model, regardless of whether // it is built left or right handed. +#ifdef MAPBASE + float m_flViewmodelFOV; + float m_flBobScale; + float m_flSwayScale; + float m_flSwaySpeedScale; + + char szDroppedModel[MAX_WEAPON_STRING]; // Model of this weapon when dropped on the ground + + bool m_bUsesHands; +#endif + // CLIENT DLL // Sprite data, read from the data file int iSpriteCount; diff --git a/mp/src/materialsystem/stdshaders/BaseVSShader.cpp b/mp/src/materialsystem/stdshaders/BaseVSShader.cpp index 408e0b91..e03d2dcb 100644 --- a/mp/src/materialsystem/stdshaders/BaseVSShader.cpp +++ b/mp/src/materialsystem/stdshaders/BaseVSShader.cpp @@ -55,6 +55,9 @@ static ConVar mat_fullbright( "mat_fullbright","0", FCVAR_CHEAT ); ConVar r_flashlightbrightness( "r_flashlightbrightness", "0.25", FCVAR_CHEAT ); #ifdef MAPBASE +// This constant should change with each Mapbase update +ConVar mapbase_version_shaders( "mapbase_version_shaders", MAPBASE_VERSION, FCVAR_NONE, "The version of Mapbase currently being used in this mod's game_shader_dx9.dll" ); + 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 diff --git a/mp/src/materialsystem/stdshaders/SDK_core_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_core_ps2x.fxc new file mode 100644 index 00000000..3db01c24 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_core_ps2x.fxc @@ -0,0 +1,222 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC] +// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX] +// STATIC: "CUBEMAP" "0..1" +// STATIC: "FLOWMAP" "0..1" +// STATIC: "CORECOLORTEXTURE" "0..1" +// STATIC: "REFRACT" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" + +// SKIP: ( $REFRACT || $CORECOLORTEXTURE ) && $CUBEMAP + +#include "common_ps_fxc.h" + +sampler RefractSampler : register( s2 ); +sampler NormalSampler : register( s3 ); +#if CUBEMAP +sampler EnvmapSampler : register( s4 ); +#endif +#if FLOWMAP +sampler FlowmapSampler : register( s6 ); +#endif + +#if CORECOLORTEXTURE +sampler CoreColorSampler : register( s7 ); +#endif + +const HALF3 g_EnvmapTint : register( c0 ); +const HALF3 g_RefractTint : register( c1 ); +const HALF3 g_EnvmapContrast : register( c2 ); +const HALF3 g_EnvmapSaturation : register( c3 ); +const HALF2 g_RefractScale : register( c5 ); +#if FLOWMAP +const float g_Time : register( c6 ); +const float2 g_FlowScrollRate : register( c7 ); +const float g_CoreColorTexCoordOffset : register( c9 ); +#endif + +const float3 g_EyePos : register( c8 ); +const float4 g_FogParams : register( c11 ); + + +const float3 g_SphereCenter : register( c12 ); +const float3 g_SphereRadius : register( c15 ); + +float LengthThroughSphere( float3 vecRayOrigin, float3 vecRayDelta, + float3 vecSphereCenter, float flRadius, out float alpha ) +{ + // Solve using the ray equation + the sphere equation + // P = o + dt + // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 + // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 + // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + + // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + + // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 + // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + + // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 + // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a + // a = DotProduct( vecRayDelta, vecRayDelta ); + // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) + // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; + + float3 vecSphereToRay; + vecSphereToRay = vecRayOrigin - vecSphereCenter; + + float a = dot( vecRayDelta, vecRayDelta ); + + // This would occur in the case of a zero-length ray + // if ( a == 0.0f ) + // { + // *pT1 = *pT2 = 0.0f; + // return vecSphereToRay.LengthSqr() <= flRadius * flRadius; + // } + + float b = 2 * dot( vecSphereToRay, vecRayDelta ); + float c = dot( vecSphereToRay, vecSphereToRay ) - flRadius * flRadius; + float flDiscrim = b * b - 4 * a * c; + // if ( flDiscrim < 0.0f ) + // return 0.0f; + + float hack = flDiscrim; + flDiscrim = sqrt( flDiscrim ); + float oo2a = 0.5f / a; + + + //if( hack < 0.0f ) + //{ + // alpha = 0.0f; + // return 0.0f; + //} + //else + //{ + // alpha = 1.0f; + // return abs( flDiscrim ) * 2 * oo2a; + //} + + //replacing the if's above because if's in hlsl are bad..... + float fHackGreaterThanZero = step( 0.0f, hack ); + alpha = fHackGreaterThanZero; + return (fHackGreaterThanZero * (abs( flDiscrim ) * 2 * oo2a)); + + + // *pT1 = ( - b - flDiscrim ) * oo2a; + // *pT2 = ( - b + flDiscrim ) * oo2a; + // return true; +} + + +struct PS_INPUT +{ + float2 vBumpTexCoord : TEXCOORD0; // dudvMapAndNormalMapTexCoord + HALF3 vWorldVertToEyeVector : TEXCOORD1; + HALF3x3 tangentSpaceTranspose : TEXCOORD2; + float3 vRefractXYW : TEXCOORD5; + float3 projNormal : TEXCOORD6; + float4 worldPos_projPosZ : TEXCOORD7; +}; + +float4 main( PS_INPUT i ) : COLOR +{ + HALF3 result = 0.0f; + + HALF blend = 1.0f; + +#if FLOWMAP + // Mapbase tries to un-hack some of this code + //float3 g_SphereCenter = { 2688.0f, 12139.0f, 5170.0f }; + //float g_SphereDiameter = 430.0f; + //float g_SphereRadius = g_SphereDiameter * 0.5f; + + float g_SphereDiameter = g_SphereRadius * 2.0f; + + float3 tmp = i.worldPos_projPosZ.xyz - g_SphereCenter; + float hackRadius = 1.05f * sqrt( dot( tmp, tmp ) ); + + float sphereAlpha; + float lengthThroughSphere = LengthThroughSphere( g_EyePos, normalize( i.worldPos_projPosZ.xyz - g_EyePos ), + g_SphereCenter, /*g_SphereRadius*/ hackRadius, sphereAlpha ); + + float normalizedLengthThroughSphere = lengthThroughSphere / g_SphereDiameter; + + + float3 hackWorldSpaceNormal = normalize( i.worldPos_projPosZ.xyz - g_SphereCenter ); + float3 realFuckingNormal = abs( hackWorldSpaceNormal ); + hackWorldSpaceNormal = 0.5f * ( hackWorldSpaceNormal + 1.0f ); + + // hackWorldSpaceNormal = abs( hackWorldSpaceNormal ); + + // return float4( hackWorldSpaceNormal.x, 0.0f, 0.0f, 1.0f ); + + i.vBumpTexCoord.xy = 0.0f; + i.vBumpTexCoord.xy = realFuckingNormal.z * tex2D( FlowmapSampler, hackWorldSpaceNormal.xy ); + i.vBumpTexCoord.xy += realFuckingNormal.y * tex2D( FlowmapSampler, hackWorldSpaceNormal.xz ); + i.vBumpTexCoord.xy += realFuckingNormal.x * tex2D( FlowmapSampler, hackWorldSpaceNormal.yz ); + i.vBumpTexCoord.xy += g_Time * g_FlowScrollRate; + // return float4( i.vBumpTexCoord.xy, 0.0f, 0.0f ); +#endif + + // Load normal and expand range + HALF4 vNormalSample = tex2D( NormalSampler, i.vBumpTexCoord ); + // return vNormalSample; + HALF3 tangentSpaceNormal = vNormalSample * 2.0 - 1.0; + + HALF3 refractTintColor = g_RefractTint; + + // 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 = tangentSpaceNormal.xy; + HALF scale = vNormalSample.a * g_RefractScale.x; +#if FLOWMAP + scale *= normalizedLengthThroughSphere; +#endif + vRefractTexCoord *= scale; +#if FLOWMAP + float2 hackOffset = vRefractTexCoord; +#endif + vRefractTexCoord += vRefractTexCoordNoWarp; + + float3 colorWarp = tex2D( RefractSampler, vRefractTexCoord.xy ); + float3 colorNoWarp = tex2D( RefractSampler, vRefractTexCoordNoWarp.xy ); + + colorWarp *= refractTintColor; +#if REFRACT + result = lerp( colorNoWarp, colorWarp, blend ); + // return float4( 1.0f, 0.0f, 0.0f, 1.0f ); +#endif + +#if CUBEMAP + HALF specularFactor = vNormalSample.a; + + HALF3 worldSpaceNormal = mul( i.tangentSpaceTranspose, tangentSpaceNormal ); + + HALF3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, i.vWorldVertToEyeVector ); + HALF3 specularLighting = texCUBE( EnvmapSampler, reflectVect ); + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint; + 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 ); + result += specularLighting; +#endif + +#if CORECOLORTEXTURE && FLOWMAP + float4 coreColorTexel = tex2D( CoreColorSampler, hackOffset + float2( normalizedLengthThroughSphere, g_CoreColorTexCoordOffset ) ); + HALF4 rgba = HALF4( lerp( result, coreColorTexel, coreColorTexel.a /*normalizedLengthThroughSphere*/ ), sphereAlpha ); +#else + HALF4 rgba = HALF4( result, vNormalSample.a ); +#endif + + + float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.z, i.worldPos_projPosZ.z, i.worldPos_projPosZ.w ); + return FinalOutput( rgba, fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE ); +} + diff --git a/mp/src/materialsystem/stdshaders/SDK_core_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_core_vs20.fxc new file mode 100644 index 00000000..9b7c9652 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/SDK_core_vs20.fxc @@ -0,0 +1,103 @@ +// STATIC: "MODEL" "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[2] : register( SHADER_SPECIFIC_CONST_1 ); + +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 +}; + +struct VS_OUTPUT +{ + float4 vProjPos_POSITION : POSITION; + float vFog : FOG; + float2 vBumpTexCoord : TEXCOORD0; + float3 vTangentEyeVect : TEXCOORD1; + float3x3 tangentSpaceTranspose : TEXCOORD2; + float3 vRefractXYW : TEXCOORD5; + float4 projNormal_screenCoordW : TEXCOORD6; + float4 worldPos_projPosZ : TEXCOORD7; + float4 fogFactorW : COLOR1; +}; + +VS_OUTPUT main( const VS_INPUT v ) +{ + VS_OUTPUT o = ( VS_OUTPUT )0; + + 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 + + + // Projected position + float4 vProjPos = mul( float4( worldPos, 1 ), cViewProj ); + o.vProjPos_POSITION = vProjPos; + o.projNormal_screenCoordW.xyz = mul( worldNormal, cViewProj ); + + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); + + // 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 = o.vFog = CalcFog( vWorldPos, vProjPos, FOGTYPE_RANGE ); + + // Eye vector + float3 vWorldEyeVect = cEyePos - vWorldPos; + // 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] ); + + o.tangentSpaceTranspose[0] = worldTangentS; + o.tangentSpaceTranspose[1] = worldTangentT; + o.tangentSpaceTranspose[2] = worldNormal; + + return o; +} diff --git a/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc index 1dd834b5..ddca526a 100644 --- a/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc +++ b/mp/src/materialsystem/stdshaders/SDK_teeth_bump_vs20.fxc @@ -55,7 +55,7 @@ struct VS_INPUT struct VS_OUTPUT { float4 projPos : POSITION; -#if !defined( _X360 ) +#if !defined( _X360 ) && !defined( SHADER_MODEL_VS_3_0 ) && !INTRO float fog : FOG; #endif float2 baseTexCoord : TEXCOORD0; @@ -105,10 +105,9 @@ VS_OUTPUT main( const VS_INPUT v ) 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 ); + o.worldPos_projPosZ = float4( worldPos.xyz, vProjPos.z ); +#if !defined( _X360 )&& !defined( SHADER_MODEL_VS_3_0 ) && !INTRO + o.fog = CalcFixedFunctionFog( worldPos, g_FogType ); #endif // Needed for specular o.worldVertToEyeVector_Darkening.xyz = cEyePos - worldPos; 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 index e78ed5d8..695faa2b 100644 --- a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps20b.fxc +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps20b.fxc @@ -27,8 +27,8 @@ // STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] // STATIC: "DEPTHBLEND" "0..1" [ps20b] [ps30] // STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "ENVMAPFRESNEL" "0..1" [ps30] // STATIC: "SRGB_INPUT_ADAPTER" "0..1" [ps20b] -// STATIC: "CUBEMAP_SPHERE_LEGACY" "0..1" // DYNAMIC: "PIXELFOGTYPE" "0..1" [ps20] // DYNAMIC: "LIGHTING_PREVIEW" "0..2" [PC] @@ -51,6 +51,10 @@ // SKIP: ($DISTANCEALPHA == 0) && ($DISTANCEALPHAFROMDETAIL || $SOFT_MASK || $OUTLINE || $OUTER_GLOW) // SKIP: ($DETAILTEXTURE == 0) && ($DISTANCEALPHAFROMDETAIL) +// envmap stuff is meaningless if we're not using a cubemap +// SKIP: ( $CUBEMAP == 0 ) && ( ( $ENVMAPFRESNEL == 1 ) || ( $BASEALPHAENVMAPMASK == 1 ) ) +// SKIP: ( $CUBEMAP == 0 ) && ( $ENVMAPMASK == 1 ) && ( $SELFILLUM_ENVMAPMASK_ALPHA == 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] @@ -60,7 +64,7 @@ // SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] // DISTANCEALPHA-related skips -// SKIP: ($DISTANCEALPHA) && ($ENVMAPMASK || $BASEALPHAENVMAPMASK || $SELFILLUM || $SELFILLUM_ENVMAPMASK_ALPHA ) +// SKIP: ($DISTANCEALPHA) && ($ENVMAPMASK || $BASEALPHAENVMAPMASK || $SELFILLUM || $SELFILLUM_ENVMAPMASK_ALPHA || $ENVMAPFRESNEL) // SKIP: ($DISTANCEALPHA) && ($SEAMLESS_BASE || $SEAMLESS_DETAIL || $CUBEMAP || $LIGHTING_PREVIEW ) // SKIP: ($DISTANCEALPHA) && ($WRITEWATERFOGTODESTALPHA || $PIXELFOGTYPE || $FLASHLIGHT || $FLASHLIGHTSHADOWS || $SRGB_INPUT_ADAPTER ) @@ -87,6 +91,12 @@ const float4 g_SelfIllumTint_and_BlendFactor : register( c4 ); const float4 g_ShaderControls : register( c12 ); const float4 g_DepthFeatheringConstants : register( c13 ); +const float4 g_FresnelConstants : register( c14 ); +#define g_flFresnelBias g_FresnelConstants.x +#define g_flFresnelScale g_FresnelConstants.y +#define g_flFresnelExp g_FresnelConstants.z +#define g_flBaseAlphaEnvMapMaskExp g_FresnelConstants.w + const float4 g_EyePos_MinLight : register( c20 ); #define g_EyePos g_EyePos_MinLight.xyz #define g_fMinLighting g_EyePos_MinLight.w @@ -159,6 +169,8 @@ const float4 g_GlowColor : register( c6 ); const float4 g_DistanceAlphaParams : register( c7 ); #define SOFT_MASK_MAX g_DistanceAlphaParams.x #define SOFT_MASK_MIN g_DistanceAlphaParams.y +#define g_flBaseAlphaEnvMapMaskBias g_DistanceAlphaParams.z +#define g_flBaseAlphaEnvMapMaskScale g_DistanceAlphaParams.w const float4 g_OutlineColor : register( c8 ); #define OUTLINE_COLOR g_OutlineColor @@ -337,11 +349,19 @@ float4 main( PS_INPUT i ) : COLOR specularFactor *= envmapMaskTexel.xyz; } - if( bBaseAlphaEnvmapMask ) + if ( bBaseAlphaEnvmapMask ) { - specularFactor *= 1.0 - baseColor.a; // this blows! + specularFactor *= saturate( g_flBaseAlphaEnvMapMaskScale * pow( baseColor.a, g_flBaseAlphaEnvMapMaskExp ) + g_flBaseAlphaEnvMapMaskBias ); } + #if ( ENVMAPFRESNEL ) + { + float flFresnel = 1-saturate( dot( normalize( i.worldSpaceNormal.xyz ), normalize( i.worldVertToEyeVector.xyz ) ) ); + flFresnel = g_flFresnelScale * pow( flFresnel, g_flFresnelExp ) + g_flFresnelBias; + specularFactor *= flFresnel; + } + #endif + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); if( bDiffuseLighting || bVertexColor && !( bVertexColor && bDiffuseLighting ) ) { 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 index 5f6e549c..fc5ae3e3 100644 --- a/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps2x.fxc +++ b/mp/src/materialsystem/stdshaders/SDK_vertexlit_and_unlit_generic_ps2x.fxc @@ -23,8 +23,8 @@ // STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX] // STATIC: "DEPTHBLEND" "0..1" [ps20b] [ps30] // STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "ENVMAPFRESNEL" "0..1" [ps30] // STATIC: "SRGB_INPUT_ADAPTER" "0..1" [ps20b] -// STATIC: "CUBEMAP_SPHERE_LEGACY" "0..1" // DYNAMIC: "PIXELFOGTYPE" "0..1" [ps20] // DYNAMIC: "LIGHTING_PREVIEW" "0..2" [PC] @@ -50,6 +50,10 @@ // SKIP: ($DISTANCEALPHA == 0) && ($DISTANCEALPHAFROMDETAIL || $SOFT_MASK || $OUTLINE || $OUTER_GLOW) // SKIP: ($DETAILTEXTURE == 0) && ($DISTANCEALPHAFROMDETAIL) +// envmap stuff is meaningless if we're not using a cubemap +// SKIP: ( $CUBEMAP == 0 ) && ( ( $ENVMAPFRESNEL == 1 ) || ( $BASEALPHAENVMAPMASK == 1 ) ) +// SKIP: ( $CUBEMAP == 0 ) && ( $ENVMAPMASK == 1 ) && ( $SELFILLUM_ENVMAPMASK_ALPHA == 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] @@ -59,7 +63,7 @@ // SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTDEPTHFILTERMODE != 0 ) [ps30] // DISTANCEALPHA-related skips -// SKIP: ($DISTANCEALPHA) && ($ENVMAPMASK || $BASEALPHAENVMAPMASK || $SELFILLUM || $SELFILLUM_ENVMAPMASK_ALPHA ) +// SKIP: ($DISTANCEALPHA) && ($ENVMAPMASK || $BASEALPHAENVMAPMASK || $SELFILLUM || $SELFILLUM_ENVMAPMASK_ALPHA || $ENVMAPFRESNEL) // SKIP: ($DISTANCEALPHA) && ($SEAMLESS_BASE || $SEAMLESS_DETAIL || $CUBEMAP || $LIGHTING_PREVIEW ) // SKIP: ($DISTANCEALPHA) && ($WRITEWATERFOGTODESTALPHA || $PIXELFOGTYPE || $FLASHLIGHT || $FLASHLIGHTSHADOWS || $SRGB_INPUT_ADAPTER ) @@ -89,6 +93,12 @@ const float4 g_SelfIllumTint_and_BlendFactor : register( c4 ); const float4 g_ShaderControls : register( c12 ); const float4 g_DepthFeatheringConstants : register( c13 ); +const float4 g_FresnelConstants : register( c14 ); +#define g_flFresnelBias g_FresnelConstants.x +#define g_flFresnelScale g_FresnelConstants.y +#define g_flFresnelExp g_FresnelConstants.z +#define g_flBaseAlphaEnvMapMaskExp g_FresnelConstants.w + const float4 g_EyePos : register( c20 ); const float4 g_FogParams : register( c21 ); @@ -159,6 +169,8 @@ const float4 g_GlowColor : register( c6 ); const float4 g_DistanceAlphaParams : register( c7 ); #define SOFT_MASK_MAX g_DistanceAlphaParams.x #define SOFT_MASK_MIN g_DistanceAlphaParams.y +#define g_flBaseAlphaEnvMapMaskBias g_DistanceAlphaParams.z +#define g_flBaseAlphaEnvMapMaskScale g_DistanceAlphaParams.w const float4 g_OutlineColor : register( c8 ); #define OUTLINE_COLOR g_OutlineColor @@ -334,17 +346,26 @@ float4 main( PS_INPUT i ) : COLOR float3 specularFactor = 1.0f; float4 envmapMaskTexel; - if( bEnvmapMask ) + #if ( ENVMAPMASK ) { envmapMaskTexel = tex2D( EnvmapMaskSampler, i.baseTexCoord.xy ); specularFactor *= envmapMaskTexel.xyz; } + #endif - if( bBaseAlphaEnvmapMask ) + if ( bBaseAlphaEnvmapMask ) { - specularFactor *= 1.0 - baseColor.a; // this blows! + specularFactor *= saturate( g_flBaseAlphaEnvMapMaskScale * pow( baseColor.a, g_flBaseAlphaEnvMapMaskExp ) + g_flBaseAlphaEnvMapMaskBias ); } + #if ( ENVMAPFRESNEL ) + { + float flFresnel = 1-saturate( dot( normalize( i.worldSpaceNormal.xyz ), normalize( i.worldVertToEyeVector.xyz ) ) ); + flFresnel = g_flFresnelScale * pow( flFresnel, g_flFresnelExp ) + g_flFresnelBias; + specularFactor *= flFresnel; + } + #endif + float3 diffuseLighting = float3( 1.0f, 1.0f, 1.0f ); if( bDiffuseLighting || bVertexColor ) { diff --git a/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc b/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc index fc3ea25a..8863683e 100644 --- a/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc +++ b/mp/src/materialsystem/stdshaders/SDK_windowimposter_ps2x.fxc @@ -24,11 +24,11 @@ struct PS_INPUT float3 eyeToVertVector : TEXCOORD0; float4 vertexColor : COLOR; -#if PARALLAXCORRECT - float3 worldSpaceNormal : TEXCOORD1; -#endif + float4 worldPos_projPosZ : TEXCOORD1; // Necessary for pixel fog - float4 worldPos_projPosZ : TEXCOORD2; // Necessary for pixel fog +#if PARALLAXCORRECT + float3 worldSpaceNormal : TEXCOORD2; +#endif }; float4 main( PS_INPUT i ) : COLOR diff --git a/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc b/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc index f67676ee..3a6a9334 100644 --- a/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc +++ b/mp/src/materialsystem/stdshaders/SDK_windowimposter_vs20.fxc @@ -28,11 +28,11 @@ struct VS_OUTPUT float3 eyeToVertVector : TEXCOORD0; float4 vertexColor : COLOR; -#if PARALLAXCORRECT - float3 worldNormal : TEXCOORD1; -#endif + float4 worldPos_projPosZ : TEXCOORD1; // Necessary for pixel fog - float4 worldPos_projPosZ : TEXCOORD2; // Necessary for pixel fog +#if PARALLAXCORRECT + float3 worldNormal : TEXCOORD2; +#endif }; VS_OUTPUT main( const VS_INPUT v ) diff --git a/mp/src/materialsystem/stdshaders/core_dx9.cpp b/mp/src/materialsystem/stdshaders/core_dx9.cpp new file mode 100644 index 00000000..ceff0563 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/core_dx9.cpp @@ -0,0 +1,307 @@ +//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BaseVSShader.h" +#include "SDK_core_vs20.inc" +#include "SDK_core_ps20.inc" +#include "SDK_core_ps20b.inc" + +#define MAXBLUR 1 + +DEFINE_FALLBACK_SHADER( SDK_Core, SDK_Core_DX90 ) + +BEGIN_VS_SHADER( SDK_Core_DX90, + "Help for Core" ) + + BEGIN_SHADER_PARAMS + SHADER_PARAM_OVERRIDE( COLOR, SHADER_PARAM_TYPE_COLOR, "{255 255 255}", "unused", SHADER_PARAM_NOT_EDITABLE ) + SHADER_PARAM_OVERRIDE( ALPHA, SHADER_PARAM_TYPE_FLOAT, "1.0", "unused", SHADER_PARAM_NOT_EDITABLE ) + SHADER_PARAM( REFRACTAMOUNT, SHADER_PARAM_TYPE_FLOAT, "2", "" ) + SHADER_PARAM( REFRACTTINT, SHADER_PARAM_TYPE_COLOR, "[1 1 1]", "refraction tint" ) + SHADER_PARAM( NORMALMAP, SHADER_PARAM_TYPE_TEXTURE, "models/shadertest/shader1_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( TIME, SHADER_PARAM_TYPE_FLOAT, "0.0f", "" ) + SHADER_PARAM( ENVMAP, SHADER_PARAM_TYPE_TEXTURE, "shadertest/shadertest_env", "envmap" ) + SHADER_PARAM( ENVMAPFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "envmap frame number" ) + 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( FLOWMAP, SHADER_PARAM_TYPE_TEXTURE, "", "flowmap" ) + SHADER_PARAM( FLOWMAPFRAME, SHADER_PARAM_TYPE_INTEGER, "0", "frame number for $flowmap" ) + SHADER_PARAM( FLOWMAPSCROLLRATE, SHADER_PARAM_TYPE_VEC2, "[0 0", "2D rate to scroll $flowmap" ) + SHADER_PARAM( CORECOLORTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "", "" ); + SHADER_PARAM( CORECOLORTEXTUREFRAME, SHADER_PARAM_TYPE_INTEGER, "", "" ); + SHADER_PARAM( FLOWMAPTEXCOORDOFFSET, SHADER_PARAM_TYPE_FLOAT, "0.0", "" ); +#ifdef MAPBASE + SHADER_PARAM( SPHERECENTER, SHADER_PARAM_TYPE_VEC3, "2688.0, 12139.0, 5170.0", "The sphere's worldspace center (was previously hardcoded)" ); + SHADER_PARAM( SPHERERADIUS, SHADER_PARAM_TYPE_FLOAT, "215.0", "The sphere's worldspace radius (was previously hardcoded)" ); +#endif + END_SHADER_PARAMS + SHADER_INIT_PARAMS() + { + SET_FLAGS2( MATERIAL_VAR2_NEEDS_TANGENT_SPACES ); + SET_FLAGS( MATERIAL_VAR_TRANSLUCENT ); + 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[ENVMAPFRAME]->IsDefined() ) + { + params[ENVMAPFRAME]->SetIntValue( 0 ); + } + if( !params[BASETEXTURE]->IsDefined() ) + { + SET_FLAGS2( MATERIAL_VAR2_NEEDS_POWER_OF_TWO_FRAME_BUFFER_TEXTURE ); + } + } + + SHADER_FALLBACK + { + if( g_pHardwareConfig->GetDXSupportLevel() < 90 ) + return "Core_dx90"; + + return 0; + } + + SHADER_INIT + { + if (params[BASETEXTURE]->IsDefined() ) + { + LoadTexture( BASETEXTURE ); + } + if (params[NORMALMAP]->IsDefined() ) + { + LoadBumpMap( NORMALMAP ); + } + if ( params[ENVMAP]->IsDefined() ) + { + LoadCubeMap( ENVMAP ); + } + if ( params[FLOWMAP]->IsDefined() ) + { + LoadTexture( FLOWMAP ); + } + if ( params[CORECOLORTEXTURE]->IsDefined() ) + { + LoadTexture( CORECOLORTEXTURE ); + } + } + + inline void DrawPass( IMaterialVar **params, IShaderShadow* pShaderShadow, + IShaderDynamicAPI* pShaderAPI, int nPass, VertexCompressionType_t vertexCompression ) + { + bool bIsModel = IS_FLAG_SET( MATERIAL_VAR_MODEL ); + bool bHasEnvmap = params[ENVMAP]->IsTexture(); + bool bHasFlowmap = params[FLOWMAP]->IsTexture(); + bool bHasCoreColorTexture = params[CORECOLORTEXTURE]->IsTexture(); + + SHADOW_STATE + { + SetInitialShadowState( ); + + if( nPass == 0 ) + { + // Alpha test: FIXME: shouldn't this be handled in Shader_t::SetInitialShadowState + pShaderShadow->EnableAlphaTest( IS_FLAG_SET(MATERIAL_VAR_ALPHATEST) ); + } + else + { + pShaderShadow->DepthFunc( SHADER_DEPTHFUNC_EQUAL ); + EnableAlphaBlending( SHADER_BLEND_ONE, SHADER_BLEND_ONE ); + } + + // If envmap is not specified, the alpha channel is the translucency + // (If envmap *is* specified, alpha channel is the reflection amount) + if ( params[NORMALMAP]->IsTexture() && !bHasEnvmap ) + { + SetDefaultBlendingShadowState( NORMALMAP, false ); + } + + // source render target that contains the image that we are warping. + pShaderShadow->EnableTexture( SHADER_SAMPLER2, true ); + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER2, true ); + } + + // normal map + pShaderShadow->EnableTexture( SHADER_SAMPLER3, true ); + if( bHasEnvmap ) + { + // envmap + pShaderShadow->EnableTexture( SHADER_SAMPLER4, true ); + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) + { + pShaderShadow->EnableSRGBRead( SHADER_SAMPLER4, true ); + } + } + + if( bHasFlowmap ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER6, true ); + } + + if( bHasCoreColorTexture ) + { + pShaderShadow->EnableTexture( SHADER_SAMPLER7, true ); + } + + if( g_pHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) + { + pShaderShadow->EnableSRGBWrite( true ); + } + + unsigned int flags = VERTEX_POSITION | VERTEX_NORMAL; + int userDataSize = 0; + int nTexCoordCount = 1; + if( bIsModel ) + { + userDataSize = 4; + } + else + { + flags |= VERTEX_TANGENT_S | VERTEX_TANGENT_T; + } + + // This shader supports compressed vertices, so OR in that flag: + flags |= VERTEX_FORMAT_COMPRESSED; + + pShaderShadow->VertexShaderVertexFormat( flags, nTexCoordCount, NULL, userDataSize ); + + DECLARE_STATIC_VERTEX_SHADER( sdk_core_vs20 ); + SET_STATIC_VERTEX_SHADER_COMBO( MODEL, bIsModel ); + SET_STATIC_VERTEX_SHADER( sdk_core_vs20 ); + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_STATIC_PIXEL_SHADER( sdk_core_ps20b ); + SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap && ( nPass == 1 ) ); + SET_STATIC_PIXEL_SHADER_COMBO( FLOWMAP, bHasFlowmap ); + SET_STATIC_PIXEL_SHADER_COMBO( CORECOLORTEXTURE, bHasCoreColorTexture && ( nPass == 0 ) ); + SET_STATIC_PIXEL_SHADER_COMBO( REFRACT, nPass == 0 ); + SET_STATIC_PIXEL_SHADER( sdk_core_ps20b ); + } + else + { + DECLARE_STATIC_PIXEL_SHADER( sdk_core_ps20 ); + SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP, bHasEnvmap && ( nPass == 1 ) ); + SET_STATIC_PIXEL_SHADER_COMBO( FLOWMAP, bHasFlowmap ); + SET_STATIC_PIXEL_SHADER_COMBO( CORECOLORTEXTURE, bHasCoreColorTexture && ( nPass == 0 ) ); + SET_STATIC_PIXEL_SHADER_COMBO( REFRACT, nPass == 0 ); + SET_STATIC_PIXEL_SHADER( sdk_core_ps20 ); + } + + DefaultFog(); + } + DYNAMIC_STATE + { + pShaderAPI->SetDefaultState(); + + if ( params[BASETEXTURE]->IsTexture() ) + { + BindTexture( SHADER_SAMPLER2, BASETEXTURE, FRAME ); + } + else + { + pShaderAPI->BindStandardTexture( SHADER_SAMPLER2, TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0 ); + } + + BindTexture( SHADER_SAMPLER3, NORMALMAP, BUMPFRAME ); + + if( bHasEnvmap ) + { + BindTexture( SHADER_SAMPLER4, ENVMAP, ENVMAPFRAME ); + } + + if( bHasFlowmap ) + { + BindTexture( SHADER_SAMPLER6, FLOWMAP, FLOWMAPFRAME ); + } + + if( bHasCoreColorTexture ) + { + BindTexture( SHADER_SAMPLER7, CORECOLORTEXTURE, CORECOLORTEXTUREFRAME ); + } + + DECLARE_DYNAMIC_VERTEX_SHADER( sdk_core_vs20 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( SKINNING, pShaderAPI->GetCurrentNumBones() > 0 ); + SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); + SET_DYNAMIC_VERTEX_SHADER( sdk_core_vs20 ); + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_core_ps20b ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_core_ps20b ); + } + else + { + DECLARE_DYNAMIC_PIXEL_SHADER( sdk_core_ps20 ); + SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); + SET_DYNAMIC_PIXEL_SHADER( sdk_core_ps20 ); + } + + SetVertexShaderTextureTransform( VERTEX_SHADER_SHADER_SPECIFIC_CONST_1, BUMPTRANSFORM ); + + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) + { + SetPixelShaderConstant( 0, ENVMAPTINT ); + SetPixelShaderConstant( 1, REFRACTTINT ); + } + else + { + SetPixelShaderConstantGammaToLinear( 0, ENVMAPTINT ); + SetPixelShaderConstantGammaToLinear( 1, REFRACTTINT ); + } + SetPixelShaderConstant( 2, ENVMAPCONTRAST ); + SetPixelShaderConstant( 3, ENVMAPSATURATION ); + float c5[4] = { params[REFRACTAMOUNT]->GetFloatValue(), + params[REFRACTAMOUNT]->GetFloatValue(), 0.0f, 0.0f }; + pShaderAPI->SetPixelShaderConstant( 5, c5, 1 ); + + float eyePos[4]; + s_pShaderAPI->GetWorldSpaceCameraPosition( eyePos ); + s_pShaderAPI->SetPixelShaderConstant( 8, eyePos, 1 ); + pShaderAPI->SetPixelShaderFogParams( 11 ); + + + + if( bHasFlowmap ) + { + float curTime = pShaderAPI->CurrentTime(); + float timeVec[4] = { curTime, curTime, curTime, curTime }; + pShaderAPI->SetPixelShaderConstant( 6, timeVec, 1 ); + + SetPixelShaderConstant( 7, FLOWMAPSCROLLRATE ); + + SetPixelShaderConstant( 9, FLOWMAPTEXCOORDOFFSET ); + } + +#ifdef MAPBASE + SetPixelShaderConstant( 12, SPHERECENTER ); + SetPixelShaderConstant( 15, SPHERERADIUS ); +#endif + } + Draw(); + } + + SHADER_DRAW + { + DrawPass( params, pShaderShadow, pShaderAPI, 0, vertexCompression ); + DrawPass( params, pShaderShadow, pShaderAPI, 1, vertexCompression ); + } +END_SHADER + diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20.inc new file mode 100644 index 00000000..d6701fc7 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20.inc @@ -0,0 +1,162 @@ +#include "shaderlib/cshader.h" +class sdk_core_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_nFLOWMAP; +#ifdef _DEBUG + bool m_bFLOWMAP; +#endif +public: + void SetFLOWMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLOWMAP = i; +#ifdef _DEBUG + m_bFLOWMAP = true; +#endif + } + void SetFLOWMAP( bool i ) + { + m_nFLOWMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bFLOWMAP = true; +#endif + } +private: + int m_nCORECOLORTEXTURE; +#ifdef _DEBUG + bool m_bCORECOLORTEXTURE; +#endif +public: + void SetCORECOLORTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCORECOLORTEXTURE = i; +#ifdef _DEBUG + m_bCORECOLORTEXTURE = true; +#endif + } + void SetCORECOLORTEXTURE( bool i ) + { + m_nCORECOLORTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bCORECOLORTEXTURE = 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 + } +public: + sdk_core_ps20_Static_Index( ) + { +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bFLOWMAP = false; +#endif // _DEBUG + m_nFLOWMAP = 0; +#ifdef _DEBUG + m_bCORECOLORTEXTURE = false; +#endif // _DEBUG + m_nCORECOLORTEXTURE = 0; +#ifdef _DEBUG + m_bREFRACT = false; +#endif // _DEBUG + m_nREFRACT = 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_bFLOWMAP && m_bCORECOLORTEXTURE && m_bREFRACT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCUBEMAP ) + ( 4 * m_nFLOWMAP ) + ( 8 * m_nCORECOLORTEXTURE ) + ( 16 * m_nREFRACT ) + 0; + } +}; +#define shaderStaticTest_sdk_core_ps20 psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_FLOWMAP + psh_forgot_to_set_static_CORECOLORTEXTURE + psh_forgot_to_set_static_REFRACT + 0 +class sdk_core_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_core_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_core_ps20 psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20b.inc new file mode 100644 index 00000000..9b9a843d --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_ps20b.inc @@ -0,0 +1,187 @@ +#include "shaderlib/cshader.h" +class sdk_core_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_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_nFLOWMAP; +#ifdef _DEBUG + bool m_bFLOWMAP; +#endif +public: + void SetFLOWMAP( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nFLOWMAP = i; +#ifdef _DEBUG + m_bFLOWMAP = true; +#endif + } + void SetFLOWMAP( bool i ) + { + m_nFLOWMAP = i ? 1 : 0; +#ifdef _DEBUG + m_bFLOWMAP = true; +#endif + } +private: + int m_nCORECOLORTEXTURE; +#ifdef _DEBUG + bool m_bCORECOLORTEXTURE; +#endif +public: + void SetCORECOLORTEXTURE( int i ) + { + Assert( i >= 0 && i <= 1 ); + m_nCORECOLORTEXTURE = i; +#ifdef _DEBUG + m_bCORECOLORTEXTURE = true; +#endif + } + void SetCORECOLORTEXTURE( bool i ) + { + m_nCORECOLORTEXTURE = i ? 1 : 0; +#ifdef _DEBUG + m_bCORECOLORTEXTURE = 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 + } +public: + sdk_core_ps20b_Static_Index( ) + { +#ifdef _DEBUG + m_bCONVERT_TO_SRGB = true; +#endif // _DEBUG + m_nCONVERT_TO_SRGB = g_pHardwareConfig->NeedsShaderSRGBConversion(); +#ifdef _DEBUG + m_bCUBEMAP = false; +#endif // _DEBUG + m_nCUBEMAP = 0; +#ifdef _DEBUG + m_bFLOWMAP = false; +#endif // _DEBUG + m_nFLOWMAP = 0; +#ifdef _DEBUG + m_bCORECOLORTEXTURE = false; +#endif // _DEBUG + m_nCORECOLORTEXTURE = 0; +#ifdef _DEBUG + m_bREFRACT = false; +#endif // _DEBUG + m_nREFRACT = 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_bFLOWMAP && m_bCORECOLORTEXTURE && m_bREFRACT; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 2 * m_nCONVERT_TO_SRGB ) + ( 4 * m_nCUBEMAP ) + ( 8 * m_nFLOWMAP ) + ( 16 * m_nCORECOLORTEXTURE ) + ( 32 * m_nREFRACT ) + 0; + } +}; +#define shaderStaticTest_sdk_core_ps20b psh_forgot_to_set_static_CUBEMAP + psh_forgot_to_set_static_FLOWMAP + psh_forgot_to_set_static_CORECOLORTEXTURE + psh_forgot_to_set_static_REFRACT + 0 +class sdk_core_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_core_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_core_ps20b psh_forgot_to_set_dynamic_PIXELFOGTYPE + 0 diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_vs20.inc b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_vs20.inc new file mode 100644 index 00000000..ef6a680c --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/SDK_core_vs20.inc @@ -0,0 +1,112 @@ +#include "shaderlib/cshader.h" +class sdk_core_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 + } +public: + sdk_core_vs20_Static_Index( ) + { +#ifdef _DEBUG + m_bMODEL = false; +#endif // _DEBUG + m_nMODEL = 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; + Assert( bAllStaticVarsDefined ); +#endif // _DEBUG + return ( 4 * m_nMODEL ) + 0; + } +}; +#define shaderStaticTest_sdk_core_vs20 vsh_forgot_to_set_static_MODEL + 0 +class sdk_core_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_core_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_core_vs20 vsh_forgot_to_set_dynamic_COMPRESSED_VERTS + vsh_forgot_to_set_dynamic_SKINNING + 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 index 49caddbb..08f600e8 100644 --- 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 @@ -379,27 +379,6 @@ public: 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( ) { @@ -475,23 +454,19 @@ public: 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; + 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; 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; + 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 ) + 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 +#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 + 0 class sdk_vertexlit_and_unlit_generic_ps20_Dynamic_Index { private: 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 index f9e6eef0..6aa6aa67 100644 --- 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 @@ -442,27 +442,6 @@ public: 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( ) { @@ -550,23 +529,19 @@ public: 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; + 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; 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 +#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 + 0 class sdk_vertexlit_and_unlit_generic_ps20b_Dynamic_Index { private: 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 index 2414d77f..6e782166 100644 --- 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 @@ -422,24 +422,24 @@ public: #endif } private: - int m_nCUBEMAP_SPHERE_LEGACY; + int m_nENVMAPFRESNEL; #ifdef _DEBUG - bool m_bCUBEMAP_SPHERE_LEGACY; + bool m_bENVMAPFRESNEL; #endif public: - void SetCUBEMAP_SPHERE_LEGACY( int i ) + void SetENVMAPFRESNEL( int i ) { Assert( i >= 0 && i <= 1 ); - m_nCUBEMAP_SPHERE_LEGACY = i; + m_nENVMAPFRESNEL = i; #ifdef _DEBUG - m_bCUBEMAP_SPHERE_LEGACY = true; + m_bENVMAPFRESNEL = true; #endif } - void SetCUBEMAP_SPHERE_LEGACY( bool i ) + void SetENVMAPFRESNEL( bool i ) { - m_nCUBEMAP_SPHERE_LEGACY = i ? 1 : 0; + m_nENVMAPFRESNEL = i ? 1 : 0; #ifdef _DEBUG - m_bCUBEMAP_SPHERE_LEGACY = true; + m_bENVMAPFRESNEL = true; #endif } public: @@ -526,22 +526,22 @@ public: #endif // _DEBUG m_nBLENDTINTBYBASEALPHA = 0; #ifdef _DEBUG - m_bCUBEMAP_SPHERE_LEGACY = false; + m_bENVMAPFRESNEL = false; #endif // _DEBUG - m_nCUBEMAP_SPHERE_LEGACY = 0; + m_nENVMAPFRESNEL = 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; + 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_bENVMAPFRESNEL; 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 +#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_ENVMAPFRESNEL + 0 class sdk_vertexlit_and_unlit_generic_ps30_Dynamic_Index { private: diff --git a/mp/src/materialsystem/stdshaders/fxctmp9/engine_post_ps20b.inc b/mp/src/materialsystem/stdshaders/fxctmp9/engine_post_ps20b.inc new file mode 100644 index 00000000..ec25f0d9 --- /dev/null +++ b/mp/src/materialsystem/stdshaders/fxctmp9/engine_post_ps20b.inc @@ -0,0 +1,362 @@ +#include "shaderlib/cshader.h" +class 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: + 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_engine_post_ps20b psh_forgot_to_set_static_TOOL_MODE + psh_forgot_to_set_static_DEPTH_BLUR_ENABLE + 0 +class 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: + 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_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/game_shader_dx9_mapbase.vpc b/mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc index 40d64143..d6a8480d 100644 --- a/mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc +++ b/mp/src/materialsystem/stdshaders/game_shader_dx9_mapbase.vpc @@ -64,6 +64,8 @@ $Project $File "engine_post_dx9.cpp" $File "depthoffield_dx9.cpp" + + $File "core_dx9.cpp" } //$Shaders "mapbase_dx9_20b.txt" diff --git a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp index b2a8ddcc..6f64a15d 100644 --- a/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp @@ -1566,9 +1566,10 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** // 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 ); + // TODO: This is inefficient use of a constant; Something should be done about this in the future + static const float editorBlend[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + static const float regularBlend[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 21, (bEditorBlend ? editorBlend : regularBlend), 1 ); /* if (bEditorBlend) { diff --git a/mp/src/materialsystem/stdshaders/teeth.cpp b/mp/src/materialsystem/stdshaders/teeth.cpp index 6307a5f9..9543c798 100644 --- a/mp/src/materialsystem/stdshaders/teeth.cpp +++ b/mp/src/materialsystem/stdshaders/teeth.cpp @@ -222,7 +222,10 @@ BEGIN_VS_SHADER( SDK_Teeth_DX9, "Help for SDK_Teeth_DX9" ) float vEyePos_SpecExponent[4]; pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent ); - vEyePos_SpecExponent[3] = 0.0f; + if (g_pHardwareConfig->HasFastVertexTextures() || g_pHardwareConfig->SupportsPixelShaders_2_b()) + vEyePos_SpecExponent[3] = params[PHONGEXPONENT]->GetFloatValue(); + else + vEyePos_SpecExponent[3] = 0.0f; pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 ); if ( hasBump ) @@ -244,11 +247,6 @@ BEGIN_VS_SHADER( SDK_Teeth_DX9, "Help for SDK_Teeth_DX9" ) // ps_2_b version which does Phong if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) { - Vector4D vSpecExponent; - vSpecExponent[3] = params[PHONGEXPONENT]->GetFloatValue(); - - pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vSpecExponent.Base(), 1 ); - 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 ); @@ -278,10 +276,6 @@ BEGIN_VS_SHADER( SDK_Teeth_DX9, "Help for SDK_Teeth_DX9" ) SET_DYNAMIC_VERTEX_SHADER_COMBO( COMPRESSED_VERTS, (int)vertexCompression ); 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( sdk_teeth_bump_ps30 ); SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() ); SET_DYNAMIC_PIXEL_SHADER_COMBO( NUM_LIGHTS, lightState.m_nNumLights ); diff --git a/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp b/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp index f0c30116..23854678 100644 --- a/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/unlitgeneric_dx9.cpp @@ -78,6 +78,13 @@ BEGIN_VS_SHADER( SDK_UnlitGeneric, "Help for SDK_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." ) +#ifdef MAPBASE + SHADER_PARAM( ALLOWDIFFUSEMODULATION, SHADER_PARAM_TYPE_BOOL, "1", "Allow per-instance color modulation" ) + + SHADER_PARAM( ENVMAPFRESNELMINMAXEXP, SHADER_PARAM_TYPE_VEC3, "[0.0 1.0 2.0]", "Min/max fresnel range and exponent for vertexlitgeneric" ) + SHADER_PARAM( BASEALPHAENVMAPMASKMINMAXEXP, SHADER_PARAM_TYPE_VEC3, "[1.0 0.0 1.0]", "" ) +#endif + // vertexlitgeneric tree sway animation control (on unlitgeneric) SHADER_PARAM( TREESWAY, SHADER_PARAM_TYPE_INTEGER, "0", "" ) SHADER_PARAM( TREESWAYHEIGHT, SHADER_PARAM_TYPE_FLOAT, "1000", "" ) @@ -177,6 +184,13 @@ BEGIN_VS_SHADER( SDK_UnlitGeneric, "Help for SDK_UnlitGeneric" ) info.m_nDepthBlendScale = DEPTHBLENDSCALE; info.m_nReceiveFlashlight = RECEIVEFLASHLIGHT; +#ifdef MAPBASE + info.m_nAllowDiffuseModulation = ALLOWDIFFUSEMODULATION; + + info.m_nEnvMapFresnelMinMaxExp = ENVMAPFRESNELMINMAXEXP; + info.m_nBaseAlphaEnvMapMaskMinMaxExp = BASEALPHAENVMAPMASKMINMAXEXP; +#endif + info.m_nTreeSway = TREESWAY; info.m_nTreeSwayHeight = TREESWAYHEIGHT; info.m_nTreeSwayStartHeight = TREESWAYSTARTHEIGHT; diff --git a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp index 4252e823..5daa43f7 100644 --- a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp +++ b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9.cpp @@ -143,6 +143,11 @@ BEGIN_VS_SHADER( SDK_VertexLitGeneric, "Help for SDK_VertexLitGeneric" ) SHADER_PARAM( BLENDTINTCOLOROVERBASE, SHADER_PARAM_TYPE_FLOAT, "0", "blend between tint acting as a multiplication versus a replace" ) #ifdef MAPBASE + SHADER_PARAM( ALLOWDIFFUSEMODULATION, SHADER_PARAM_TYPE_BOOL, "1", "Allow per-instance color modulation" ) + + SHADER_PARAM( ENVMAPFRESNELMINMAXEXP, SHADER_PARAM_TYPE_VEC3, "[0.0 1.0 2.0]", "Min/max fresnel range and exponent for vertexlitgeneric" ) + SHADER_PARAM( BASEALPHAENVMAPMASKMINMAXEXP, SHADER_PARAM_TYPE_VEC3, "[1.0 0.0 1.0]", "" ) + SHADER_PARAM( PHONGDISABLEHALFLAMBERT, SHADER_PARAM_TYPE_BOOL, "0", "Disable half lambert for phong" ) #endif @@ -243,6 +248,11 @@ BEGIN_VS_SHADER( SDK_VertexLitGeneric, "Help for SDK_VertexLitGeneric" ) info.m_nTintReplacesBaseColor = BLENDTINTCOLOROVERBASE; #ifdef MAPBASE + info.m_nAllowDiffuseModulation = ALLOWDIFFUSEMODULATION; + + info.m_nEnvMapFresnelMinMaxExp = ENVMAPFRESNELMINMAXEXP; + info.m_nBaseAlphaEnvMapMaskMinMaxExp = BASEALPHAENVMAPMASKMINMAXEXP; + info.m_nPhongDisableHalfLambert = PHONGDISABLEHALFLAMBERT; #endif diff --git a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp index 4fded702..865a976b 100644 --- a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp +++ b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.cpp @@ -226,6 +226,21 @@ void InitParamsVertexLitGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** para InitIntParam( info.m_nDepthBlend, params, 0 ); InitFloatParam( info.m_nDepthBlendScale, params, 50.0f ); + +#ifdef MAPBASE + InitIntParam( info.m_nAllowDiffuseModulation, params, 1 ); + + if ( ( info.m_nEnvMapFresnelMinMaxExp != -1 ) && !params[info.m_nEnvMapFresnelMinMaxExp]->IsDefined() ) + { + params[info.m_nEnvMapFresnelMinMaxExp]->SetVecValue( 0.0f, 1.0f, 2.0f, 0.0f ); + } + if ( ( info.m_nBaseAlphaEnvMapMaskMinMaxExp != -1 ) && !params[info.m_nBaseAlphaEnvMapMaskMinMaxExp]->IsDefined() ) + { + // Default to min: 1 max: 0 exp: 1 so that we default to the legacy behavior for basealphaenvmapmask, which is 1-baseColor.a + // These default values translate to a scale of -1, bias of 1 and exponent 1 in the shader. + params[info.m_nBaseAlphaEnvMapMaskMinMaxExp]->SetVecValue( 1.0f, 0.0f, 1.0f, 0.0f ); + } +#endif InitIntParam( info.m_nTreeSway, params, 0 ); InitFloatParam( info.m_nTreeSwayHeight, params, 1000.0f ); @@ -460,18 +475,33 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial bool bHasSelfIllum = (!bHasFlashlight || IsX360() ) && IS_FLAG_SET( MATERIAL_VAR_SELFILLUM ); bool bHasEnvmapMask = (!bHasFlashlight || IsX360() ) && info.m_nEnvmapMask != -1 && params[info.m_nEnvmapMask]->IsTexture(); bool bHasSelfIllumFresnel = ( !IsTextureSet( info.m_nDetail, params ) ) && ( bHasSelfIllum ) && ( info.m_nSelfIllumFresnel != -1 ) && ( params[info.m_nSelfIllumFresnel]->GetIntValue() != 0 ); +#ifdef MAPBASE + bool bHasEnvMapFresnel = bHasEnvmap && IsBoolSet( info.m_nEnvmapFresnel, params ); +#endif bool bHasSelfIllumMask = bHasSelfIllum && IsTextureSet( info.m_nSelfIllumMask, params ); bool hasSelfIllumInEnvMapMask = ( info.m_nSelfIllumEnvMapMask_Alpha != -1 ) && ( params[info.m_nSelfIllumEnvMapMask_Alpha]->GetFloatValue() != 0.0 ) ; +#ifdef MAPBASE + if (!bHasEnvmap) + { + bHasEnvmapMask = hasSelfIllumInEnvMapMask; + } +#endif + if ( pShader->IsSnapshotting() ) { /*^*/ // printf("\t\t[2] snapshotting...\n"); +#ifdef MAPBASE + bool hasBaseAlphaEnvmapMask = bHasEnvmap && IS_FLAG_SET( MATERIAL_VAR_BASEALPHAENVMAPMASK ); + bool hasNormalMapAlphaEnvmapMask = bHasEnvmap && IS_FLAG_SET( MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ); +#else bool hasBaseAlphaEnvmapMask = IS_FLAG_SET( MATERIAL_VAR_BASEALPHAENVMAPMASK ); bool hasNormalMapAlphaEnvmapMask = IS_FLAG_SET( MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ); +#endif if ( info.m_nVertexAlphaTest != -1 && params[info.m_nVertexAlphaTest]->GetIntValue() > 0 ) @@ -488,7 +518,9 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial } bool bHasEnvmap = (!bHasFlashlight || IsX360() ) && ( info.m_nEnvmap != -1 ) && params[info.m_nEnvmap]->IsTexture(); +#ifndef MAPBASE bool bHasLegacyEnvSphereMap = bHasEnvmap && IS_FLAG_SET(MATERIAL_VAR_ENVMAPSPHERE); +#endif bool bHasNormal = bVertexLitGeneric || bHasEnvmap || bHasFlashlight || bSeamlessBase || bSeamlessDetail; if ( IsPC() ) { @@ -812,7 +844,9 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial 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 ); +#ifndef MAPBASE SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP_SPHERE_LEGACY, bHasLegacyEnvSphereMap ); +#endif SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); @@ -885,7 +919,9 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial 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 ); +#ifndef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( CUBEMAP_SPHERE_LEGACY, bHasLegacyEnvSphereMap ); +#endif SET_STATIC_PIXEL_SHADER_COMBO( DIFFUSELIGHTING, hasDiffuseLighting ); SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPMASK, bHasEnvmapMask ); SET_STATIC_PIXEL_SHADER_COMBO( BASEALPHAENVMAPMASK, hasBaseAlphaEnvmapMask ); @@ -904,6 +940,9 @@ 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 ); +#ifdef MAPBASE + SET_STATIC_PIXEL_SHADER_COMBO( ENVMAPFRESNEL, bHasEnvMapFresnel ); +#endif SET_STATIC_PIXEL_SHADER( sdk_vertexlit_and_unlit_generic_ps30 ); } #endif @@ -1083,9 +1122,26 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial { params[info.m_nOutlineColor]->GetVecValue( flConsts+12, 3 ); } +#ifdef MAPBASE + if ( info.m_nBaseAlphaEnvMapMaskMinMaxExp != -1 ) + { + flConsts[10] = params[ info.m_nBaseAlphaEnvMapMaskMinMaxExp ]->GetVecValue()[0]; + flConsts[11] = params[ info.m_nBaseAlphaEnvMapMaskMinMaxExp ]->GetVecValue()[1] - flConsts[10]; + } +#endif pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 5, flConsts, 5 ); } +#ifdef MAPBASE + else if ( info.m_nBaseAlphaEnvMapMaskMinMaxExp != -1 ) + { + float flConsts[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + flConsts[2] = params[ info.m_nBaseAlphaEnvMapMaskMinMaxExp ]->GetVecValue()[0]; + flConsts[3] = params[ info.m_nBaseAlphaEnvMapMaskMinMaxExp ]->GetVecValue()[1] - flConsts[2]; + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 7, flConsts, 1 ); + } +#endif if ( !g_pConfig->m_bFastNoBump ) { if ( bHasBump ) @@ -1125,6 +1181,29 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial pContextData->m_SemiStaticCmdsOut.BindTexture( pShader, SHADER_SAMPLER4, info.m_nEnvmapMask, info.m_nEnvmapMaskFrame ); } +#ifdef MAPBASE + if ( bHasEnvMapFresnel ) + { + float flConsts[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + params[ info.m_nEnvMapFresnelMinMaxExp ]->GetVecValue( flConsts, 3 ); + flConsts[1] -= flConsts[0]; // convert max fresnel into scale factor + + if ( info.m_nBaseAlphaEnvMapMaskMinMaxExp != -1 ) + { + flConsts[3] = params[ info.m_nBaseAlphaEnvMapMaskMinMaxExp ]->GetVecValue()[2]; // basealphaenvmapmask exponent in w + } + + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 14, flConsts, 1 ); + } + else if ( info.m_nBaseAlphaEnvMapMaskMinMaxExp != -1 ) + { + // still need to set exponent for basealphaenvmapmask + float flConsts[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + flConsts[3] = params[ info.m_nBaseAlphaEnvMapMaskMinMaxExp ]->GetVecValue()[2]; // basealphaenvmapmask exponent in w + pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 14, flConsts, 1 ); + } +#endif + if ( bHasSelfIllumFresnel && (!bHasFlashlight || IsX360() ) ) { float vConstScaleBiasExp[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; @@ -1470,14 +1549,29 @@ static void DrawVertexLitGeneric_DX9_Internal( CBaseVSShader *pShader, IMaterial #endif } - if ( ( info.m_nHDRColorScale != -1 ) && pShader->IsHDREnabled() ) +#ifdef MAPBASE + // material can choose to support per-instance modulation via $allowdiffusemodulation + bool bAllowDiffuseModulation = (info.m_nAllowDiffuseModulation == -1) ? true : (params[info.m_nAllowDiffuseModulation]->GetIntValue() != 0); + + if (bAllowDiffuseModulation) +#endif { - pShader->SetModulationPixelShaderDynamicState_LinearColorSpace_LinearScale( 1, params[info.m_nHDRColorScale]->GetFloatValue() ); + if ( ( info.m_nHDRColorScale != -1 ) && pShader->IsHDREnabled() ) + { + pShader->SetModulationPixelShaderDynamicState_LinearColorSpace_LinearScale( 1, params[info.m_nHDRColorScale]->GetFloatValue() ); + } + else + { + pShader->SetModulationPixelShaderDynamicState_LinearColorSpace( 1 ); + } } +#ifdef MAPBASE else { - pShader->SetModulationPixelShaderDynamicState_LinearColorSpace( 1 ); + float color[4] = { 1.0, 1.0, 1.0, 1.0 }; + pShaderAPI->SetPixelShaderConstant( 1, color ); } +#endif float eyePos[4]; pShaderAPI->GetWorldSpaceCameraPosition( eyePos ); diff --git a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h index 10bbbedd..207f72c2 100644 --- a/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h +++ b/mp/src/materialsystem/stdshaders/vertexlitgeneric_dx9_helper.h @@ -134,7 +134,16 @@ struct VertexLitGeneric_DX9_Vars_t int m_nTintReplacesBaseColor; #ifdef MAPBASE - // Parameter ported from Alien Swarm. See bPhongHalfLambert in DrawSkin_DX9_Internal() for more info. + // Parameters ported from Alien Swarm SDK shaders. + + // Utility param for disabling tinting on certain materials. + int m_nAllowDiffuseModulation; + + // $envmapfresnel on non-phong materials. + int m_nEnvMapFresnelMinMaxExp; + int m_nBaseAlphaEnvMapMaskMinMaxExp; + + // Disables $halflambert on phong materials. See bPhongHalfLambert in DrawSkin_DX9_Internal() for more info. int m_nPhongDisableHalfLambert; #endif diff --git a/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp b/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp index 99ac7914..d24ac04b 100644 --- a/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp +++ b/mp/src/materialsystem/stdshaders/windowimposter_dx90.cpp @@ -7,9 +7,9 @@ #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" +#include "SDK_windowimposter_vs20.inc" +#include "SDK_windowimposter_ps20.inc" +#include "SDK_windowimposter_ps20b.inc" diff --git a/mp/src/mathlib/polyhedron.cpp b/mp/src/mathlib/polyhedron.cpp index 5a858f19..5c57671a 100644 --- a/mp/src/mathlib/polyhedron.cpp +++ b/mp/src/mathlib/polyhedron.cpp @@ -1015,12 +1015,12 @@ CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL //Scan for onplane points connected to only other onplane/dead points, these points get downgraded to dead status. { - GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints; + GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalkLocl = pAllPoints; do { - if( pActivePointWalk->pPoint->planarity == POINT_ONPLANE ) + if( pActivePointWalkLocl->pPoint->planarity == POINT_ONPLANE ) { - GeneratePolyhedronFromPlanes_LineLL *pOnPlaneLineWalk = pActivePointWalk->pPoint->pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pOnPlaneLineWalk = pActivePointWalkLocl->pPoint->pConnectedLines; GeneratePolyhedronFromPlanes_LineLL *pStartLineWalk = pOnPlaneLineWalk; bool bDead = true; //assume it's dead and disprove do @@ -1047,7 +1047,7 @@ CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL if( bDead ) { - pActivePointWalk->pPoint->planarity = POINT_DEAD; + pActivePointWalkLocl->pPoint->planarity = POINT_DEAD; pOnPlaneLineWalk = pStartLineWalk; @@ -1059,8 +1059,8 @@ CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL } while( pOnPlaneLineWalk != pStartLineWalk ); } } - pActivePointWalk = pActivePointWalk->pNext; - } while( pActivePointWalk ); + pActivePointWalkLocl = pActivePointWalkLocl->pNext; + } while( pActivePointWalkLocl ); } #ifdef _DEBUG PlaneCutHistory.AddToTail( &pOutwardFacingPlanes[iCurrentPlane * 4] ); @@ -1337,17 +1337,17 @@ CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL //verify that the new point isn't sitting on top of another { - GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints; + GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalkLocl = pAllPoints; do { - if( pActivePointWalk->pPoint != pNewPoint ) + if( pActivePointWalkLocl->pPoint != pNewPoint ) { - Vector vDiff = pActivePointWalk->pPoint->ptPosition - pNewPoint->ptPosition; + Vector vDiff = pActivePointWalkLocl->pPoint->ptPosition - pNewPoint->ptPosition; AssertMsg_DumpPolyhedron( vDiff.Length() > fOnPlaneEpsilon, "Generated a point on top of another" ); } - pActivePointWalk = pActivePointWalk->pNext; - } while( pActivePointWalk ); + pActivePointWalkLocl = pActivePointWalk->pNext; + } while( pActivePointWalkLocl ); } #endif diff --git a/mp/src/public/ScratchPadUtils.cpp b/mp/src/public/ScratchPadUtils.cpp index dfb93b0d..6e020aaf 100644 --- a/mp/src/public/ScratchPadUtils.cpp +++ b/mp/src/public/ScratchPadUtils.cpp @@ -141,11 +141,11 @@ void CScratchPadGraph::UpdateTicksAndStuff( float flTime, float flValue ) // Extend the lines attached to the time labels. for ( int i=0; i < m_nTimeLabelsDrawn; i++ ) { - float flTime = m_flTimeOrigin + m_nTimeLabelsDrawn * m_flTimeLabelEveryNSeconds; + float flTimeLocl = m_flTimeOrigin + m_nTimeLabelsDrawn * m_flTimeLabelEveryNSeconds; m_pPad->DrawLine( - CSPVert((const Vector&) GetSamplePosition( flTime, m_flHighestValue )), - CSPVert((const Vector&) GetSamplePosition( flTime, flValue ) ) + CSPVert((const Vector&) GetSamplePosition( flTimeLocl, m_flHighestValue )), + CSPVert((const Vector&) GetSamplePosition( flTimeLocl, flValue ) ) ); } @@ -158,21 +158,21 @@ void CScratchPadGraph::UpdateTicksAndStuff( float flTime, float flValue ) { CTextParams params; - float flTime = m_flTimeOrigin + m_nTimeLabelsDrawn * m_flTimeLabelEveryNSeconds; + float flTimeLocl = m_flTimeOrigin + m_nTimeLabelsDrawn * m_flTimeLabelEveryNSeconds; params.m_bSolidBackground = true; - params.m_vPos = GetSamplePosition( flTime, m_flValueOrigin-5 ); + params.m_vPos = GetSamplePosition( flTimeLocl, m_flValueOrigin-5 ); params.m_bTwoSided = true; char str[512]; - Q_snprintf( str, sizeof( str ), "time: %.2f", flTime ); + Q_snprintf( str, sizeof( str ), "time: %.2f", flTimeLocl ); m_pPad->DrawText( str, params ); // Now draw the vertical line for the value.. m_pPad->DrawLine( - CSPVert( (const Vector&)GetSamplePosition( flTime, m_flValueOrigin ) ), - CSPVert( (const Vector&)GetSamplePosition( flTime, m_flHighestValue ) ) + CSPVert( (const Vector&)GetSamplePosition( flTimeLocl, m_flValueOrigin ) ), + CSPVert( (const Vector&)GetSamplePosition( flTimeLocl, m_flHighestValue ) ) ); diff --git a/mp/src/public/bitmap/imageformat.h b/mp/src/public/bitmap/imageformat.h index 3e8f7b13..0b3390f7 100644 --- a/mp/src/public/bitmap/imageformat.h +++ b/mp/src/public/bitmap/imageformat.h @@ -21,7 +21,7 @@ enum NormalDecodeMode_t }; // Forward declaration -#ifdef _WIN32 +#if defined(_WIN32) && _MSC_VER < 1900 typedef enum _D3DFORMAT D3DFORMAT; #endif @@ -106,7 +106,7 @@ enum ImageFormat NUM_IMAGE_FORMATS }; -#if defined( POSIX ) || defined( DX_TO_GL_ABSTRACTION ) +#if defined( POSIX ) || defined( DX_TO_GL_ABSTRACTION ) || _MSC_VER >= 1900 typedef enum _D3DFORMAT { D3DFMT_INDEX16, diff --git a/mp/src/public/bone_setup.cpp b/mp/src/public/bone_setup.cpp index 4630c730..7cfd0eb2 100644 --- a/mp/src/public/bone_setup.cpp +++ b/mp/src/public/bone_setup.cpp @@ -933,8 +933,8 @@ static void CalcVirtualAnimation( virtualmodel_t *pVModel, const CStudioHdr *pSt { if (pStudioHdr->boneFlags(i) & boneMask) { - int j = pSeqGroup->boneMap[i]; - if (j >= 0 && pweight[j] > 0.0f) + int l = pSeqGroup->boneMap[i]; + if (l >= 0 && pweight[l] > 0.0f) { if (animdesc.flags & STUDIO_DELTA) { @@ -943,13 +943,13 @@ static void CalcVirtualAnimation( virtualmodel_t *pVModel, const CStudioHdr *pSt } else if (pSeqLinearBones) { - q[i] = pSeqLinearBones->quat(j); - pos[i] = pSeqLinearBones->pos(j); + q[i] = pSeqLinearBones->quat(l); + pos[i] = pSeqLinearBones->pos(l); } else { - q[i] = pSeqbone[j].quat; - pos[i] = pSeqbone[j].pos; + q[i] = pSeqbone[l].quat; + pos[i] = pSeqbone[l].pos; } #ifdef STUDIO_ENABLE_PERF_COUNTERS pStudioHdr->m_nPerfUsedBones++; @@ -997,10 +997,9 @@ static void CalcVirtualAnimation( virtualmodel_t *pVModel, const CStudioHdr *pSt matrix3x4_t *boneToWorld = g_MatrixPool.Alloc(); CBoneBitList boneComputed; - int i; - for (i = 0; i < animdesc.numlocalhierarchy; i++) + for (int l = 0; i < animdesc.numlocalhierarchy; i++) { - mstudiolocalhierarchy_t *pHierarchy = animdesc.pHierarchy( i ); + mstudiolocalhierarchy_t *pHierarchy = animdesc.pHierarchy( l ); if ( !pHierarchy ) break; @@ -1141,10 +1140,9 @@ static void CalcAnimation( const CStudioHdr *pStudioHdr, Vector *pos, Quaternion matrix3x4_t *boneToWorld = g_MatrixPool.Alloc(); CBoneBitList boneComputed; - int i; - for (i = 0; i < animdesc.numlocalhierarchy; i++) + for (int j = 0; j < animdesc.numlocalhierarchy; j++) { - mstudiolocalhierarchy_t *pHierarchy = animdesc.pHierarchy( i ); + mstudiolocalhierarchy_t *pHierarchy = animdesc.pHierarchy( j ); if ( !pHierarchy ) break; @@ -5610,9 +5608,9 @@ bool Studio_AnimPosition( mstudioanimdesc_t *panim, float flCycle, Vector &vecPo vecAngle.y = vecAngle.y * (1 - f) + pmove->angle * f; if (iLoops != 0) { - mstudiomovement_t *pmove = panim->pMovement( panim->nummovements - 1 ); - vecPos = vecPos + iLoops * pmove->position; - vecAngle.y = vecAngle.y + iLoops * pmove->angle; + mstudiomovement_t *pmoveLocl = panim->pMovement( panim->nummovements - 1 ); + vecPos = vecPos + iLoops * pmoveLocl->position; + vecAngle.y = vecAngle.y + iLoops * pmoveLocl->angle; } return true; } diff --git a/mp/src/public/datamap.h b/mp/src/public/datamap.h index 34a1caf8..b25bb172 100644 --- a/mp/src/public/datamap.h +++ b/mp/src/public/datamap.h @@ -313,6 +313,12 @@ struct datamap_t static datamap_t *GetBaseMap(); \ template friend void DataMapAccess(T *, datamap_t **p); \ template friend datamap_t *DataMapInit(T *); + +#define DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE() \ + static datamap_t m_DataMap; \ + static datamap_t *GetBaseMap(); \ + template friend void ::DataMapAccess(T *, datamap_t **p); \ + template friend datamap_t *::DataMapInit(T *); #define DECLARE_DATADESC() \ DECLARE_SIMPLE_DATADESC() \ @@ -414,6 +420,8 @@ inline void DataMapAccess(T *ignored, datamap_t **p) *p = &T::m_DataMap; } +template datamap_t* DataMapInit(T*); + //----------------------------------------------------------------------------- class CDatadescGeneratedNameHolder diff --git a/mp/src/public/dt_utlvector_send.cpp b/mp/src/public/dt_utlvector_send.cpp index b10170de..06f78c64 100644 --- a/mp/src/public/dt_utlvector_send.cpp +++ b/mp/src/public/dt_utlvector_send.cpp @@ -134,7 +134,7 @@ void* SendProxy_LengthTable( const SendProp *pProp, const void *pStructBase, con // Note: you have to be DILIGENT about calling NetworkStateChanged whenever an element in your CUtlVector changes // since CUtlVector doesn't do this automatically. SendProp SendPropUtlVector( - char *pVarName, // Use SENDINFO_UTLVECTOR to generate these 4. + const char *pVarName, // Use SENDINFO_UTLVECTOR to generate these 4. int offset, // Used to generate pData in the function specified in varProxy. int sizeofVar, // The size of each element in the utlvector. EnsureCapacityFn ensureFn, // This is the value returned for elements out of the array's current range. diff --git a/mp/src/public/dt_utlvector_send.h b/mp/src/public/dt_utlvector_send.h index f2ba68c3..3684d5b5 100644 --- a/mp/src/public/dt_utlvector_send.h +++ b/mp/src/public/dt_utlvector_send.h @@ -43,7 +43,7 @@ // ) // SendProp SendPropUtlVector( - char *pVarName, // Use SENDINFO_UTLVECTOR to generate these first 4 parameters. + const char *pVarName, // Use SENDINFO_UTLVECTOR to generate these first 4 parameters. int offset, int sizeofVar, EnsureCapacityFn ensureFn, diff --git a/mp/src/public/haptics/haptic_utils.cpp b/mp/src/public/haptics/haptic_utils.cpp index b9c72b2f..e6d3288a 100644 --- a/mp/src/public/haptics/haptic_utils.cpp +++ b/mp/src/public/haptics/haptic_utils.cpp @@ -138,6 +138,12 @@ void ConnectHaptics(CreateInterfaceFn appFactory) HookHapticMessages(); } +#if _MSC_VER >= 1925 +// deleting haptics results in a warning about deleting something with a non-virtual destructor +// big yikes but we can't do anything about it as it's accessed via interface +#pragma warning (disable: 5205) +#endif + void DisconnectHaptics() { haptics->ShutdownHaptics(); diff --git a/mp/src/public/keyframe/keyframe.cpp b/mp/src/public/keyframe/keyframe.cpp index 1e08b0a3..425a172e 100644 --- a/mp/src/public/keyframe/keyframe.cpp +++ b/mp/src/public/keyframe/keyframe.cpp @@ -154,7 +154,7 @@ class CPositionInterpolator_Linear : public IPositionInterpolator { public: virtual void Release(); - virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ); + virtual void GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ); virtual void SetKeyPosition( int keyNum, Vector const &vPos ); virtual void InterpolatePosition( float time, Vector &vOut ); virtual bool ProcessKey( char const *pName, char const *pValue ) { return false; } @@ -171,7 +171,7 @@ void CPositionInterpolator_Linear::Release() { } -void CPositionInterpolator_Linear::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +void CPositionInterpolator_Linear::GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ) { *outName = "Linear"; *outMinKeyReq = 0; @@ -201,7 +201,7 @@ class CPositionInterpolator_CatmullRom : public IPositionInterpolator { public: virtual void Release(); - virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ); + virtual void GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ); virtual void SetKeyPosition( int keyNum, Vector const &vPos ); virtual void InterpolatePosition( float time, Vector &vOut ); virtual bool ProcessKey( char const *pName, char const *pValue ) { return false; } @@ -218,7 +218,7 @@ void CPositionInterpolator_CatmullRom::Release() { } -void CPositionInterpolator_CatmullRom::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +void CPositionInterpolator_CatmullRom::GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ) { *outName = "Catmull-Rom Spline"; *outMinKeyReq = -1; @@ -282,7 +282,7 @@ public: CPositionInterpolator_Rope(); virtual void Release(); - virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ); + virtual void GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ); virtual void SetKeyPosition( int keyNum, Vector const &vPos ); virtual void InterpolatePosition( float time, Vector &vOut ); virtual bool ProcessKey( char const *pName, char const *pValue ); @@ -319,7 +319,7 @@ void CPositionInterpolator_Rope::Release() delete this; } -void CPositionInterpolator_Rope::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +void CPositionInterpolator_Rope::GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ) { *outName = "Rope"; *outMinKeyReq = 0; @@ -433,7 +433,7 @@ typedef void (*RotationInterpolatorFunc_t)(float time, Quaternion &outRot); typedef struct { - char *szName; + const char *szName; RotationInterpolatorFunc_t pFunc; // defines the range of keys this interpolator needs to function @@ -458,7 +458,7 @@ int Motion_GetNumberOfRotationInterpolators( void ) return ARRAYSIZE(g_RotationInterpolators); } -bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, const char **outName, int *outMinKeyReq, int *outMaxKeyReq ) { if ( rotInterpNum < 0 || rotInterpNum >= Motion_GetNumberOfRotationInterpolators() ) { diff --git a/mp/src/public/keyframe/keyframe.h b/mp/src/public/keyframe/keyframe.h index b2cfd27c..4ee04824 100644 --- a/mp/src/public/keyframe/keyframe.h +++ b/mp/src/public/keyframe/keyframe.h @@ -14,7 +14,7 @@ class IPositionInterpolator public: virtual void Release() = 0; - virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) = 0; + virtual void GetDetails( const char **outName, int *outMinKeyReq, int *outMaxKeyReq ) = 0; virtual void SetKeyPosition( int keyNum, Vector const &vPos ) = 0; virtual void InterpolatePosition( float time, Vector &vOut ) = 0; @@ -34,7 +34,7 @@ IPositionInterpolator* Motion_GetPositionInterpolator( int interpNum ); // Rotation interpolators. int Motion_GetNumberOfRotationInterpolators( void ); -bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, char **outName, int *outMinKeyReq, int *outMaxKeyReq ); +bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, const char **outName, int *outMinKeyReq, int *outMaxKeyReq ); bool Motion_InterpolateRotation( float time, int interpFuncNum, Quaternion &outQuatRotation ); bool Motion_SetKeyAngles( int keyNum, Quaternion &quatAngles ); diff --git a/mp/src/public/materialsystem/MaterialSystemUtil.h b/mp/src/public/materialsystem/MaterialSystemUtil.h index 3f22e918..ddabcd22 100644 --- a/mp/src/public/materialsystem/MaterialSystemUtil.h +++ b/mp/src/public/materialsystem/MaterialSystemUtil.h @@ -72,6 +72,10 @@ public: void Init( char const* pTexture, const char *pTextureGroupName, bool bComplain = true ); void InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, ImageFormat fmt, int nFlags ); void InitRenderTarget( int w, int h, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, char *pStrOptionalName = NULL ); + void InitRenderTarget(int w, int h, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, const char* pStrOptionalName = NULL) + { + InitRenderTarget(w, h, sizeMode, fmt, depth, bHDR, const_cast(pStrOptionalName)); + } #if defined( _X360 ) // used when RT coupling is disparate (texture is DDR based, surface is EDRAM based) void InitRenderTargetTexture( int width, int height, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, char *pStrOptionalName = NULL ); diff --git a/mp/src/public/networkvar.h b/mp/src/public/networkvar.h index 142b35e9..46a92de0 100644 --- a/mp/src/public/networkvar.h +++ b/mp/src/public/networkvar.h @@ -744,9 +744,9 @@ private: const type* Base() const { return m_Value; } \ int Count() const { return count; } \ protected: \ - inline void NetworkStateChanged( int index ) \ + inline void NetworkStateChanged( int _index ) \ { \ - CHECK_USENETWORKVARS ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->stateChangedFn( &m_Value[index] ); \ + CHECK_USENETWORKVARS ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->stateChangedFn( &m_Value[_index] ); \ } \ type m_Value[count]; \ }; \ diff --git a/mp/src/public/responserules/response_host_interface.h b/mp/src/public/responserules/response_host_interface.h new file mode 100644 index 00000000..fa39bd88 --- /dev/null +++ b/mp/src/public/responserules/response_host_interface.h @@ -0,0 +1,66 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_HOST_INTERFACE_H +#define RESPONSE_HOST_INTERFACE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "filesystem.h" +class IUniformRandomStream; +class ICommandLine; + +namespace ResponseRules +{ + // FUNCTIONS YOU MUST IMPLEMENT IN THE HOST EXECUTABLE: + // These are functions that are mentioned in the header, but need their bodies implemented + // in the .dll that links against this lib. + // This is to wrap functions that previously came from the engine interface + // back when the response rules were inside the server.dll . Now that the rules + // are included into a standalone editor, we don't necessarily have an engine around, + // so there needs to be some other implementation. + abstract_class IEngineEmulator + { + public: + /// Given an input text buffer data pointer, parses a single token into the variable token and returns the new + /// reading position + virtual const char *ParseFile( const char *data, char *token, int maxlen ) = 0; + +#ifdef MAPBASE + /// (Optional) Same as ParseFile, but with casing preserved and escaped quotes supported + virtual const char *ParseFilePreserve( const char *data, char *token, int maxlen ) { return ParseFile( data, token, maxlen ); } +#endif + + /// Return a pointer to an IFileSystem we can use to read and process scripts. + virtual IFileSystem *GetFilesystem() = 0; + + /// Return a pointer to an instance of an IUniformRandomStream + virtual IUniformRandomStream *GetRandomStream() = 0 ; + + /// Return a pointer to a tier0 ICommandLine + virtual ICommandLine *GetCommandLine() = 0; + + /// Emulates the server's UTIL_LoadFileForMe + virtual byte *LoadFileForMe( const char *filename, int *pLength ) = 0; + + /// Emulates the server's UTIL_FreeFile + virtual void FreeFile( byte *buffer ) = 0; + + + /// Somewhere in the host executable you should define this symbol and + /// point it at a singleton instance. + static IEngineEmulator *s_pSingleton; + + // this is just a function that returns the pointer above -- just in + // case we need to define it differently. And I get asserts this way. + static IEngineEmulator *Get(); + }; +}; + + +#endif \ No newline at end of file diff --git a/mp/src/public/responserules/response_types.h b/mp/src/public/responserules/response_types.h new file mode 100644 index 00000000..2e80cc5a --- /dev/null +++ b/mp/src/public/responserules/response_types.h @@ -0,0 +1,480 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_TYPES_H +#define RESPONSE_TYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier1/interval.h" +#include "mathlib/compressed_vector.h" +#include "datamap.h" +#include "soundflags.h" +#include "tier1/utlsymbol.h" + +namespace ResponseRules +{ + /// Custom symbol table for the response rules. + extern CUtlSymbolTable g_RS; +}; + +#ifdef _MANAGED +// forward declare some editor types just so we can friend them. +namespace ResponseRulesCLI +{ + ref class ResponseQueryResult; +} +#endif + +namespace ResponseRules +{ + using ::DataMapAccess; + // using ::DataMapInit; + class CResponseSystem; + +#pragma pack(push,1) + template + struct response_interval_t + { + T start; + T range; + + interval_t &ToInterval( interval_t &dest ) const { dest.start = start; dest.range = range; return dest; } + void FromInterval( const interval_t &from ) { start = from.start; range = from.range; } + float Random() const { interval_t temp = { start, range }; return RandomInterval( temp ); } + }; + + typedef response_interval_t responseparams_interval_t; +#pragma pack(pop) + +#pragma pack(push,1) + struct AI_ResponseFollowup + { + + + // TODO: make less wasteful of memory, by using a symbol table. + const char *followup_concept; // 12 -- next response + const char *followup_contexts; // 16 + float followup_delay; // 20 + const char *followup_target; // 24 -- to whom is this despatched? + // AIConceptHandle_t hConcept; + const char *followup_entityiotarget; //< if this rule involves firing entity io + const char *followup_entityioinput; //< if this rule involves firing entity io + float followup_entityiodelay; + bool bFired; + + inline bool IsValid( void ) const { return (followup_concept && followup_contexts); } + inline void Invalidate() { followup_concept = NULL; followup_contexts = NULL; } + inline void SetFired( bool fired ) { bFired = fired; } + inline bool HasBeenFired() { return bFired; } + + AI_ResponseFollowup( void ) : followup_concept(NULL), followup_contexts(NULL), followup_delay(0), followup_target(NULL), followup_entityiotarget(NULL), followup_entityioinput(NULL), followup_entityiodelay(0), bFired(false) + {}; + AI_ResponseFollowup( char *_followup_concept, char *_followup_contexts, float _followup_delay, char *_followup_target, + char *_followup_entityiotarget, char *_followup_entityioinput, float _followup_entityiodelay ) : + followup_concept(_followup_concept), followup_contexts(_followup_contexts), followup_delay(_followup_delay), followup_target(_followup_target), + followup_entityiotarget(_followup_entityiotarget), followup_entityioinput(_followup_entityioinput), followup_entityiodelay(_followup_entityiodelay), + bFired(false) + {}; + }; +#pragma pack(pop) + + + enum ResponseType_t + { + RESPONSE_NONE = 0, + RESPONSE_SPEAK, + RESPONSE_SENTENCE, + RESPONSE_SCENE, + RESPONSE_RESPONSE, // A reference to another response by name + RESPONSE_PRINT, + RESPONSE_ENTITYIO, // poke an input on an entity +#ifdef MAPBASE + RESPONSE_VSCRIPT, // Run VScript code + RESPONSE_VSCRIPT_FILE, // Run a VScript file (bypasses ugliness and character limits when just using IncludeScript() with RESPONSE_VSCRIPT) +#endif + + NUM_RESPONSES, + }; + +#ifdef MAPBASE + // The "apply to world" context option has been replaced with a flag-based integer which can apply contexts to more things. + // + // New ones should be implemented in: + // CResponseSystem::BuildDispatchTables() - AI_ResponseSystem.cpp (with their own funcs for m_RuleDispatch) + // CRR_Response::Describe() - rr_response.cpp + // CAI_Expresser::SpeakDispatchResponse() - ai_speech.cpp + // + // Also mind that this is 8-bit + enum : uint8 + { + 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 + + +#pragma pack(push,1) + struct ResponseParams + { + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + enum + { + RG_DELAYAFTERSPEAK = (1<<0), + RG_SPEAKONCE = (1<<1), + RG_ODDS = (1<<2), + RG_RESPEAKDELAY = (1<<3), + RG_SOUNDLEVEL = (1<<4), + RG_DONT_USE_SCENE = (1<<5), + RG_STOP_ON_NONIDLE = (1<<6), + RG_WEAPONDELAY = (1<<7), + RG_DELAYBEFORESPEAK = (1<<8), + }; + + ResponseParams() + { + flags = 0; + odds = 100; + delay.start = 0; + delay.range = 0; + respeakdelay.start = 0; + respeakdelay.range = 0; + weapondelay.start = 0; + weapondelay.range = 0; + soundlevel = 0; + predelay.start = 0; + predelay.range = 0; + } + responseparams_interval_t delay; //4 + responseparams_interval_t respeakdelay; //8 + responseparams_interval_t weapondelay; //12 + + short odds; //14 + + short flags; //16 + byte soundlevel; //17 + + responseparams_interval_t predelay; //21 + + ALIGN32 AI_ResponseFollowup *m_pFollowup; + + }; +#pragma pack(pop) + + class CriteriaSet + { + public: + typedef CUtlSymbol CritSymbol_t; ///< just to make it clear that some symbols come out of our special static table + public: + CriteriaSet(); + CriteriaSet( const CriteriaSet& src ); + CriteriaSet( const char *criteria, const char *value ) ; // construct initialized with a key/value pair (convenience) + ~CriteriaSet(); + + static CritSymbol_t ComputeCriteriaSymbol( const char *criteria ); + void AppendCriteria( CritSymbol_t criteria, const char *value = "", float weight = 1.0f ); + void AppendCriteria( const char *criteria, const char *value = "", float weight = 1.0f ); + void AppendCriteria( const char *criteria, float value, float weight = 1.0f ); + void RemoveCriteria( const char *criteria ); + + void Describe() const; + + int GetCount() const; + int FindCriterionIndex( CritSymbol_t criteria ) const; + int FindCriterionIndex( const char *name ) const; + inline bool IsValidIndex( int index ) const; + + CritSymbol_t GetNameSymbol( int nIndex ) const; + inline static const char *SymbolToStr( const CritSymbol_t &symbol ); + const char *GetName( int index ) const; + const char *GetValue( int index ) const; + float GetWeight( int index ) const; + + /// Merge another CriteriaSet into this one. + void Merge( const CriteriaSet *otherCriteria ); + void Merge( const char *modifiers ); // add criteria parsed from a text string + + /// add all of the contexts herein onto an entity. all durations are infinite. + void WriteToEntity( CBaseEntity *pEntity ); + + // Accessors to things that need only be done under unusual circumstances. + inline void EnsureCapacity( int num ); + void Reset(); // clear out this criteria (should not be necessary) + + /// When this is true, calls to AppendCriteria on a criteria that already exists + /// will override the existing value. (This is the default behavior). Can be temporarily + /// set false to prevent such overrides. + inline void OverrideOnAppend( bool bOverride ) { m_bOverrideOnAppend = bOverride; } + + // For iteration from beginning to end (also should not be necessary except in + // save/load) + inline int Head() const; + inline int Next( int i ) const; // use with IsValidIndex above + + const static char kAPPLYTOWORLDPREFIX = '$'; + + /// A last minute l4d2 change: deferred contexts prefixed with a '$' + /// character are actually applied to the world. This matches the + /// related hack in CBaseEntity::AppplyContext. + /// This function works IN-PLACE on the "from" parameter. + /// any $-prefixed criteria in pFrom become prefixed by "world", + /// and are also written into pSetOnWorld. + /// *IF* a response matches using the modified criteria, then and only + /// then should you write back the criteria in pSetOnWorld to the world + /// entity, subsequent to the match but BEFORE the dispatch. + /// Returns the number of contexts modified. If it returns 0, then + /// pSetOnWorld is empty. + static int InterceptWorldSetContexts( CriteriaSet * RESTRICT pFrom, + CriteriaSet * RESTRICT pSetOnWorld ); + + private: + void RemoveCriteria( int idx, bool bTestForPrefix ); + + struct CritEntry_t + { + CritEntry_t() : + criterianame( UTL_INVAL_SYMBOL ), + weight( 0.0f ) + { + value[ 0 ] = 0; + } + + CritEntry_t( const CritEntry_t& src ) + { + criterianame = src.criterianame; + value[ 0 ] = 0; + weight = src.weight; + SetValue( src.value ); + } + + CritEntry_t& operator=( const CritEntry_t& src ) + { + if ( this == &src ) + return *this; + + criterianame = src.criterianame; + weight = src.weight; + SetValue( src.value ); + + return *this; + } + + static bool LessFunc( const CritEntry_t& lhs, const CritEntry_t& rhs ) + { + return lhs.criterianame < rhs.criterianame; + } + + void SetValue( char const *str ) + { + if ( !str ) + { + value[ 0 ] = 0; + } + else + { + Q_strncpy( value, str, sizeof( value ) ); + } + } + + CritSymbol_t criterianame; + char value[ 64 ]; + float weight; + }; + + static CUtlSymbolTable sm_CriteriaSymbols; + typedef CUtlRBTree< CritEntry_t, short > Dict_t; + Dict_t m_Lookup; + int m_nNumPrefixedContexts; // number of contexts prefixed with kAPPLYTOWORLDPREFIX + bool m_bOverrideOnAppend; + }; + + inline void CriteriaSet::EnsureCapacity( int num ) + { + m_Lookup.EnsureCapacity(num); + } + + //----------------------------------------------------------------------------- + // Purpose: Generic container for a response to a match to a criteria set + // This is what searching for a response returns + //----------------------------------------------------------------------------- + + class CRR_Response + { + public: + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + CRR_Response(); + CRR_Response( const CRR_Response &from ); + CRR_Response &operator=( const CRR_Response &from ); + ~CRR_Response(); + private: + void operator delete(void* p); // please do not new or delete CRR_Responses. + public: + + // void Release(); // we no longer encourage new and delete on these things + + void GetName( char *buf, size_t buflen ) const; + void GetResponse( char *buf, size_t buflen ) const; +#ifdef MAPBASE + void GetRule( char *buf, size_t buflen ) const; +#endif + const char* GetNamePtr() const; + const char* GetResponsePtr() const; + const ResponseParams *GetParams() const { return &m_Params; } + ResponseType_t GetType() const { return (ResponseType_t)m_Type; } + soundlevel_t GetSoundLevel() const; + float GetRespeakDelay() const; + float GetWeaponDelay() const; + bool GetSpeakOnce() const; + bool ShouldntUseScene( ) const; + bool ShouldBreakOnNonIdle( void ) const; + int GetOdds() const; + float GetDelay() const; + float GetPreDelay() const; + + inline bool IsEmpty() const; // true iff my response name is empty + void Invalidate() ; // wipe out my contents, mark me invalid + + // Get/set the contexts we apply to character and world after execution + void SetContext( const char *context ); + const char * GetContext( void ) const { return m_szContext; } + + // Get/set the score I matched with (under certain circumstances) + inline float GetMatchScore( void ) { return m_fMatchScore; } + inline void SetMatchScore( float f ) { m_fMatchScore = f; } + +#ifdef MAPBASE + int GetContextFlags() { return m_iContextFlags; } + bool IsApplyContextToWorld( void ) { return (m_iContextFlags & APPLYCONTEXT_WORLD) != 0; } + + inline short *GetInternalIndices() { return m_InternalIndices; } + inline void SetInternalIndices( short iGroup, short iWithinGroup ) { m_InternalIndices[0] = iGroup; m_InternalIndices[1] = iWithinGroup; } +#else + bool IsApplyContextToWorld( void ) { return m_bApplyContextToWorld; } +#endif + + void Describe( const CriteriaSet *pDebugCriteria = NULL ); + + void Init( ResponseType_t type, + const char *responseName, + const ResponseParams& responseparams, + const char *matchingRule, + const char *applyContext, + bool bApplyContextToWorld ); + +#ifdef MAPBASE + void Init( ResponseType_t type, + const char *responseName, + const ResponseParams& responseparams, + const char *matchingRule, + const char *applyContext, + int iContextFlags ); +#endif + + static const char *DescribeResponse( ResponseType_t type ); + + enum + { + MAX_RESPONSE_NAME = 64, + MAX_RULE_NAME = 64 + }; + + + private: + byte m_Type; + char m_szResponseName[ MAX_RESPONSE_NAME ]; + char m_szMatchingRule[ MAX_RULE_NAME ]; + + ResponseParams m_Params; + float m_fMatchScore; // when instantiated dynamically in SpeakFindResponse, the score of the rule that matched it. + + char * m_szContext; // context data we apply to character after running +#ifdef MAPBASE + int m_iContextFlags; + + // The response's original indices in the system. [0] is the group's index, [1] is the index within the group. + // For now, this is only set in prospecctive mode. It's used to call back to the ParserResponse and mark a prospectively chosen response as used. + short m_InternalIndices[2]; +#else + bool m_bApplyContextToWorld; +#endif + +#ifdef _MANAGED + friend ref class ResponseRulesCLI::ResponseQueryResult; +#endif + }; + + + + abstract_class IResponseFilter + { + public: + virtual bool IsValidResponse( ResponseType_t type, const char *pszValue ) = 0; + }; + + abstract_class IResponseSystem + { + public: + virtual ~IResponseSystem() {} + + virtual bool FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter = NULL ) = 0; + virtual void GetAllResponses( CUtlVector *pResponses ) = 0; + virtual void PrecacheResponses( bool bEnable ) = 0; + +#ifdef MAPBASE + // (Optional) Call this before and after using FindBestResponse() for a prospective lookup, e.g. a response that might not actually be used + // and should not trigger displayfirst, etc. + virtual void SetProspective( bool bToggle ) {}; + + // (Optional) Marks a prospective response as used + virtual void MarkResponseAsUsed( short iGroup, short iWithinGroup ) {}; +#endif + }; + + + + // INLINE FUNCTIONS + + // Used as a failsafe in finding responses. + bool CRR_Response::IsEmpty() const + { + return m_szResponseName[0] == 0; + } + + inline bool CriteriaSet::IsValidIndex( int index ) const + { + return ( index >= 0 && index < ((int)(m_Lookup.Count())) ); + } + + inline int CriteriaSet::Head() const + { + return m_Lookup.FirstInorder(); + } + + inline int CriteriaSet::Next( int i ) const + { + return m_Lookup.NextInorder(i); + } + + inline const char *CriteriaSet::SymbolToStr( const CritSymbol_t &symbol ) + { + return sm_CriteriaSymbols.String(symbol); + } + +} + +#include "rr_speechconcept.h" +#include "response_host_interface.h" + +#endif diff --git a/mp/src/public/responserules/rr_speechconcept.h b/mp/src/public/responserules/rr_speechconcept.h new file mode 100644 index 00000000..65b1bb6e --- /dev/null +++ b/mp/src/public/responserules/rr_speechconcept.h @@ -0,0 +1,57 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Class data for an AI Concept, an atom of response-driven dialog. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RR_SPEECHCONCEPT_H +#define RR_SPEECHCONCEPT_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "utlsymbol.h" + +#define RR_CONCEPTS_ARE_STRINGS 0 + + +typedef CUtlSymbolTable CRR_ConceptSymbolTable; + +namespace ResponseRules +{ +class CRR_Concept +{ +public: // local typedefs + typedef CUtlSymbol tGenericId; // an int-like type that can be used to refer to all concepts of this type + tGenericId m_iConcept; + +public: + CRR_Concept() {}; + // construct concept from a string. + CRR_Concept(const char *fromString); + + // Return as a string + const char *GetStringConcept() const; + static const char *GetStringForGenericId(tGenericId genericId); + + operator tGenericId() const { return m_iConcept; } + operator const char *() const { return GetStringConcept(); } + inline bool operator==(const CRR_Concept &other) // default is compare by concept ids + { + return m_iConcept == other.m_iConcept; + } + bool operator==(const char *pszConcept); + +protected: + +private: + // dupe a concept + // CRR_Concept& operator=(CRR_Concept &other); + CRR_Concept& operator=(const char *fromString); +}; +}; + + +#endif diff --git a/mp/src/public/saverestoretypes.h b/mp/src/public/saverestoretypes.h index 7868e2fb..21c2d484 100644 --- a/mp/src/public/saverestoretypes.h +++ b/mp/src/public/saverestoretypes.h @@ -181,7 +181,7 @@ class CGameSaveRestoreInfo { public: CGameSaveRestoreInfo() - : tableCount( 0 ), pTable( 0 ), m_pCurrentEntity( 0 ), m_EntityToIndex( 1024 ) + : m_iTableCount( 0 ), m_pTable( 0 ), m_pCurrentEntity( 0 ), m_EntityToIndex( 1024 ) { memset( &levelInfo, 0, sizeof( levelInfo ) ); modelSpaceOffset.Init( 0, 0, 0 ); @@ -189,8 +189,8 @@ public: void InitEntityTable( entitytable_t *pNewTable = NULL, int size = 0 ) { - pTable = pNewTable; - tableCount = size; + m_pTable = pNewTable; + m_iTableCount = size; for ( int i = 0; i < NumEntities(); i++ ) { @@ -200,17 +200,17 @@ public: entitytable_t *DetachEntityTable() { - entitytable_t *pReturn = pTable; - pTable = NULL; - tableCount = 0; + entitytable_t *pReturn = m_pTable; + m_pTable = NULL; + m_iTableCount = 0; return pReturn; } CBaseEntity *GetCurrentEntityContext() { return m_pCurrentEntity; } void SetCurrentEntityContext(CBaseEntity *pEntity) { m_pCurrentEntity = pEntity; } - int NumEntities() { return tableCount; } - entitytable_t *GetEntityInfo( int i ) { return (pTable + i); } + int NumEntities() { return m_iTableCount; } + entitytable_t *GetEntityInfo( int i ) { return (m_pTable + i); } float GetBaseTime() const { return levelInfo.time; } Vector GetLandmark() const { return ( levelInfo.fUseLandmark ) ? levelInfo.vecLandmarkOffset : vec3_origin; } @@ -218,13 +218,13 @@ public: { #ifdef GAME_DLL int i; - entitytable_t *pTable; + entitytable_t *m_pTable; int nEntities = NumEntities(); for ( i = 0; i < nEntities; i++ ) { - pTable = GetEntityInfo( i ); - m_EntityToIndex.Insert( CHashElement( pTable->hEnt.Get(), i ) ); + m_pTable = GetEntityInfo( i ); + m_EntityToIndex.Insert( CHashElement( m_pTable->hEnt.Get(), i ) ); } #endif } @@ -269,8 +269,8 @@ public: Vector modelSpaceOffset; // used only for globaly entity brushes modelled in different coordinate systems. private: - int tableCount; // Number of elements in the entity table - entitytable_t *pTable; // Array of entitytable_t elements (1 for each entity) + int m_iTableCount; // Number of elements in the entity table + entitytable_t *m_pTable; // Array of entitytable_t elements (1 for each entity) CBaseEntity *m_pCurrentEntity; // only valid during the save functions of this entity, NULL otherwise diff --git a/mp/src/public/scratchpad3d.h b/mp/src/public/scratchpad3d.h index 7e9aad66..30b2455c 100644 --- a/mp/src/public/scratchpad3d.h +++ b/mp/src/public/scratchpad3d.h @@ -51,7 +51,7 @@ public: m_pCachedRenderData = NULL; } - ~CBaseCommand() + virtual ~CBaseCommand() { ReleaseCachedRenderData(); } diff --git a/mp/src/public/sentence.cpp b/mp/src/public/sentence.cpp index bb7ade5e..8107bbf3 100644 --- a/mp/src/public/sentence.cpp +++ b/mp/src/public/sentence.cpp @@ -508,7 +508,7 @@ void CSentence::ParseWords( CUtlBuffer& buf ) // Parse phoneme int code; char phonemename[ 256 ]; - float start, end; + float startLocl, endLocl; float volume; code = atoi( token ); @@ -516,9 +516,9 @@ void CSentence::ParseWords( CUtlBuffer& buf ) buf.GetString( token ); Q_strncpy( phonemename, token, sizeof( phonemename ) ); buf.GetString( token ); - start = atof( token ); + startLocl = atof( token ); buf.GetString( token ); - end = atof( token ); + endLocl = atof( token ); buf.GetString( token ); volume = atof( token ); @@ -526,8 +526,8 @@ void CSentence::ParseWords( CUtlBuffer& buf ) assert( pt ); pt->SetPhonemeCode( code ); pt->SetTag( phonemename ); - pt->SetStartTime( start ); - pt->SetEndTime( end ); + pt->SetStartTime( startLocl ); + pt->SetEndTime( endLocl ); AddPhonemeTag( wt, pt ); } @@ -1308,9 +1308,9 @@ void CSentence::Append( float starttime, const CSentence& src ) // Offset times int c = newWord->m_Phonemes.Count(); - for ( int i = 0; i < c; ++i ) + for ( int j = 0; j < c; ++j ) { - CPhonemeTag *tag = newWord->m_Phonemes[ i ]; + CPhonemeTag *tag = newWord->m_Phonemes[ j ]; tag->AddStartTime( starttime ); tag->AddEndTime( starttime ); } diff --git a/mp/src/public/studio.cpp b/mp/src/public/studio.cpp index bf6fdb7d..2c96a458 100644 --- a/mp/src/public/studio.cpp +++ b/mp/src/public/studio.cpp @@ -516,7 +516,7 @@ void studiohdr_t::SetAttachmentBone( int iAttachment, int iBone ) // Purpose: //----------------------------------------------------------------------------- -char *studiohdr_t::pszNodeName( int iNode ) +const char *studiohdr_t::pszNodeName( int iNode ) { if (numincludemodels == 0) { @@ -565,7 +565,7 @@ int studiohdr_t::GetActivityListVersion( void ) virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); - int version = activitylistversion; + int versionLocl = activitylistversion; int i; for (i = 1; i < pVModel->m_group.Count(); i++) @@ -575,15 +575,15 @@ int studiohdr_t::GetActivityListVersion( void ) Assert( pStudioHdr ); - version = min( version, pStudioHdr->activitylistversion ); + versionLocl = min( versionLocl, pStudioHdr->activitylistversion ); } - return version; + return versionLocl; } -void studiohdr_t::SetActivityListVersion( int version ) const +void studiohdr_t::SetActivityListVersion( int iVersion ) const { - activitylistversion = version; + activitylistversion = iVersion; if (numincludemodels == 0) { @@ -601,7 +601,7 @@ void studiohdr_t::SetActivityListVersion( int version ) const Assert( pStudioHdr ); - pStudioHdr->SetActivityListVersion( version ); + pStudioHdr->SetActivityListVersion( iVersion ); } } @@ -1163,7 +1163,7 @@ void CStudioHdr::SetAttachmentBone( int iAttachment, int iBone ) // Purpose: //----------------------------------------------------------------------------- -char *CStudioHdr::pszNodeName( int iNode ) +const char *CStudioHdr::pszNodeName( int iNode ) { if (m_pVModel == NULL) { @@ -1444,9 +1444,9 @@ void CStudioHdr::RunFlexRules( const float *src, float *dest ) { int m = pops->d.index; int km = k - m; - for ( int i = km + 1; i < k; ++i ) + for ( int l = km + 1; l < k; ++l ) { - stack[ km ] *= stack[ i ]; + stack[ km ] *= stack[ l ]; } k = k - m + 1; } @@ -1456,9 +1456,9 @@ void CStudioHdr::RunFlexRules( const float *src, float *dest ) int m = pops->d.index; int km = k - m; float dv = stack[ km ]; - for ( int i = km + 1; i < k; ++i ) + for ( int l = km + 1; l < k; ++l ) { - dv *= stack[ i ]; + dv *= stack[ l ]; } stack[ km - 1 ] *= 1.0f - dv; k -= m; @@ -1716,7 +1716,7 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict // This stack may potentially grow very large; so if you have problems with it, // go to a utlmap or similar structure. unsigned int allocsize = (topActivity + 1) * sizeof(int); -#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression +//#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression allocsize = ALIGN_VALUE(allocsize,16); int * __restrict seqsPerAct = static_cast(stackalloc(allocsize)); memset(seqsPerAct, 0, allocsize); diff --git a/mp/src/public/studio.h b/mp/src/public/studio.h index bc70313b..9363f3c0 100644 --- a/mp/src/public/studio.h +++ b/mp/src/public/studio.h @@ -2149,7 +2149,7 @@ struct studiohdr_t int GetSequenceActivity( int iSequence ); void SetSequenceActivity( int iSequence, int iActivity ); int GetActivityListVersion( void ); - void SetActivityListVersion( int version ) const; + void SetActivityListVersion( int iVersion ) const; int GetEventListVersion( void ); void SetEventListVersion( int version ); @@ -2197,7 +2197,7 @@ struct studiohdr_t //public: int EntryNode( int iSequence ); int ExitNode( int iSequence ); - char *pszNodeName( int iNode ); + const char *pszNodeName( int iNode ); int GetTransition( int iFrom, int iTo ) const; int numflexdesc; @@ -2407,7 +2407,7 @@ public: int EntryNode( int iSequence ); int ExitNode( int iSequence ); - char *pszNodeName( int iNode ); + const char *pszNodeName( int iNode ); // FIXME: where should this one be? int GetTransition( int iFrom, int iTo ) const; diff --git a/mp/src/public/tier0/basetypes.h b/mp/src/public/tier0/basetypes.h index e8387b56..9bcaf90e 100644 --- a/mp/src/public/tier0/basetypes.h +++ b/mp/src/public/tier0/basetypes.h @@ -131,6 +131,70 @@ T Max( T const &val1, T const &val2 ) #define TRUE (!FALSE) #endif +//----------------------------------------------------------------------------- +// fsel +//----------------------------------------------------------------------------- +#ifndef _X360 + +#define fsel(c,x,y) ( (c) >= 0 ? (x) : (y) ) + +// integer conditional move +// if a >= 0, return x, else y +#define isel(a,x,y) ( ((a) >= 0) ? (x) : (y) ) + +// if x = y, return a, else b +#define ieqsel(x,y,a,b) (( (x) == (y) ) ? (a) : (b)) + +// if the nth bit of a is set (counting with 0 = LSB), +// return x, else y +// this is fast if nbit is a compile-time immediate +#define ibitsel(a, nbit, x, y) ( ( ((a) & (1 << (nbit))) != 0 ) ? (x) : (y) ) + +#else + +// __fsel(double fComparand, double fValGE, double fLT) == fComparand >= 0 ? fValGE : fLT +// this is much faster than if ( aFloat > 0 ) { x = .. } +// the XDK defines two intrinsics, one for floats and one for doubles -- it's the same +// opcode, but the __fself version tells the compiler not to do a wasteful unnecessary +// rounding op after each sel. +// #define fsel __fsel +FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) { return __fsel( fComparand, fValGE, fLT ); } +FORCEINLINE float fsel(float fComparand, float fValGE, float fLT) { return __fself( fComparand, fValGE, fLT ); } + +// if a >= 0, return x, else y +FORCEINLINE int isel( int a, int x, int y ) +{ + int mask = a >> 31; // arithmetic shift right, splat out the sign bit + return x + ((y - x) & mask); +}; + +// if a >= 0, return x, else y +FORCEINLINE unsigned isel( int a, unsigned x, unsigned y ) +{ + int mask = a >> 31; // arithmetic shift right, splat out the sign bit + return x + ((y - x) & mask); +}; + +// ( x == y ) ? a : b +FORCEINLINE unsigned ieqsel( unsigned x, unsigned y, unsigned a, unsigned b ) +{ + unsigned mask = (x == y) ? 0 : -1; + return a + ((b - a) & mask); +}; + +// ( x == y ) ? a : b +FORCEINLINE int ieqsel( int x, int y, int a, int b ) +{ + int mask = (x == y) ? 0 : -1; + return a + ((b - a) & mask); +}; + +// if the nth bit of a is set (counting with 0 = LSB), +// return x, else y +// this is fast if nbit is a compile-time immediate +#define ibitsel(a, nbit, x, y) ( (x) + (((y) - (x)) & (((a) & (1 << (nbit))) ? 0 : -1)) ) + +#endif #ifndef DONT_DEFINE_BOOL // Needed for Cocoa stuff to compile. typedef int BOOL; @@ -148,7 +212,6 @@ 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 diff --git a/mp/src/public/tier0/dbg.h b/mp/src/public/tier0/dbg.h index 5fa68adf..08ab8b29 100644 --- a/mp/src/public/tier0/dbg.h +++ b/mp/src/public/tier0/dbg.h @@ -15,7 +15,9 @@ #include "basetypes.h" #include "dbgflag.h" #include "platform.h" +#if _MSC_VER < 1900 #include +#endif #include #include @@ -250,10 +252,10 @@ DBG_INTERFACE struct SDL_Window * GetAssertDialogParent(); if (!(_exp)) \ { \ _SpewInfo( SPEW_ASSERT, __TFILE__, __LINE__ ); \ - SpewRetval_t ret = _SpewMessage("%s", static_cast( _msg )); \ + SpewRetval_t _ret = _SpewMessage("%s", static_cast( _msg )); \ CallAssertFailedNotifyFunc( __TFILE__, __LINE__, _msg ); \ _executeExp; \ - if ( ret == SPEW_DEBUGGER) \ + if ( _ret == SPEW_DEBUGGER) \ { \ if ( !ShouldUseNewAssertDialog() || DoNewAssertDialog( __TFILE__, __LINE__, _msg ) ) \ { \ diff --git a/mp/src/public/tier0/memalloc.h b/mp/src/public/tier0/memalloc.h index e0f9c16a..34e1ccb9 100644 --- a/mp/src/public/tier0/memalloc.h +++ b/mp/src/public/tier0/memalloc.h @@ -382,7 +382,12 @@ public: #pragma warning(disable:4290) #pragma warning(push) + +#if _MSC_VER < 1900 #include +#else + #include +#endif // MEM_DEBUG_CLASSNAME is opt-in. // Note: typeid().name() is not threadsafe, so if the project needs to access it in multiple threads diff --git a/mp/src/public/tier0/memdbgon.h b/mp/src/public/tier0/memdbgon.h index 7fd6e9ca..4e46839f 100644 --- a/mp/src/public/tier0/memdbgon.h +++ b/mp/src/public/tier0/memdbgon.h @@ -37,7 +37,7 @@ #include "commonmacros.h" #include "memalloc.h" -#if defined(USE_MEM_DEBUG) +#if USE_MEM_DEBUG #if defined( POSIX ) #define _NORMAL_BLOCK 1 @@ -91,7 +91,7 @@ inline void *MemAlloc_InlineCallocMemset( void *pMem, size_t nCount, size_t nEle } #endif -#define calloc(c, s) MemAlloc_InlineCallocMemset(malloc(c*s), c, s) +#define calloc(c, s) MemAlloc_InlineCallocMemset(malloc((c)*(s)), (c), (s)) #define free(p) g_pMemAlloc->Free( p ) #define _msize(p) g_pMemAlloc->GetSize( p ) #define _expand(p, s) _expand_NoLongerSupported(p, s) @@ -99,7 +99,7 @@ inline void *MemAlloc_InlineCallocMemset( void *pMem, size_t nCount, size_t nEle // -------------------------------------------------------- // Debug path -#if defined(USE_MEM_DEBUG) +#if USE_MEM_DEBUG #define malloc(s) g_pMemAlloc->Alloc( s, __FILE__, __LINE__) #define realloc(p, s) g_pMemAlloc->Realloc( p, s, __FILE__, __LINE__ ) @@ -231,7 +231,7 @@ inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString) #else -#if defined(USE_MEM_DEBUG) +#if USE_MEM_DEBUG #ifndef _STATIC_LINKED #pragma message ("Note: file includes crtdbg.h directly, therefore will cannot use memdbgon.h in non-debug build") #else diff --git a/mp/src/public/tier0/memoverride.cpp b/mp/src/public/tier0/memoverride.cpp index bec06c0e..98c67077 100644 --- a/mp/src/public/tier0/memoverride.cpp +++ b/mp/src/public/tier0/memoverride.cpp @@ -40,9 +40,17 @@ #define __cdecl #endif +#undef _malloc_dbg +#undef _calloc_dbg +#undef _free_dbg +#undef _CrtSetCheckCount +#undef _CrtGetCheckCount +#undef _CrtSetDebugFillThreshold + #if defined( _WIN32 ) && !defined( _X360 ) const char *MakeModuleFileName() { +#if _MSC_VER < 1900 if ( g_pMemAlloc->IsDebugHeap() ) { char *pszModuleName = (char *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH ); // small leak, debug only @@ -65,27 +73,36 @@ const char *MakeModuleFileName() return pszModuleName; } +#endif return NULL; } static void *AllocUnattributed( size_t nSize ) { +#if _MSC_VER < 1900 static const char *pszOwner = MakeModuleFileName(); if ( !pszOwner ) return g_pMemAlloc->Alloc(nSize); else return g_pMemAlloc->Alloc(nSize, pszOwner, 0); +#else + return g_pMemAlloc->Alloc(nSize); +#endif } static void *ReallocUnattributed( void *pMem, size_t nSize ) { +#if _MSC_VER < 1900 static const char *pszOwner = MakeModuleFileName(); if ( !pszOwner ) return g_pMemAlloc->Realloc(pMem, nSize); else return g_pMemAlloc->Realloc(pMem, nSize, pszOwner, 0); +#else + return g_pMemAlloc->Realloc(pMem, nSize); +#endif } #else @@ -108,6 +125,9 @@ inline void *ReallocUnattributed( void *pMem, size_t nSize ) // this magic only works under win32 // under linux this malloc() overrides the libc malloc() and so we // end up in a recursion (as g_pMemAlloc->Alloc() calls malloc) +#if _MSC_VER >= 1900 && !defined(_CRTNOALIAS) +#define _CRTNOALIAS +#endif #if _MSC_VER >= 1400 #define ALLOC_CALL _CRTNOALIAS _CRTRESTRICT #define FREE_CALL _CRTNOALIAS @@ -155,6 +175,11 @@ void* __cdecl _malloc_base( size_t nSize ) { return AllocUnattributed( nSize ); } +#elif _MSC_VER >= 1900 + __declspec(restrict) void* _malloc_base(size_t nSize) + { + return AllocUnattributed(nSize); + } #else void *_malloc_base( size_t nSize ) { @@ -162,24 +187,47 @@ void *_malloc_base( size_t nSize ) } #endif +#if _MSC_VER >= 1900 +__declspec(restrict) void* _calloc_base(size_t count, size_t nSize) +{ + void* pMem = AllocUnattributed(count * nSize); + memset(pMem, 0, count * nSize); + return pMem; +} +#else void *_calloc_base( size_t nSize ) { void *pMem = AllocUnattributed( nSize ); memset(pMem, 0, nSize); return pMem; } +#endif +#if _MSC_VER >= 1900 +__declspec(restrict) void* _realloc_base(void* pMem, size_t nSize) +{ + return ReallocUnattributed(pMem, nSize); +} +#else void *_realloc_base( void *pMem, size_t nSize ) { return ReallocUnattributed( pMem, nSize ); } +#endif +#if _MSC_VER >= 1900 +__declspec(restrict) void* _recalloc_base(void* pMem, size_t count, size_t nSize) +{ + return _recalloc(pMem, count, nSize); +} +#else void *_recalloc_base( void *pMem, size_t nSize ) { void *pMemOut = ReallocUnattributed( pMem, nSize ); memset(pMemOut, 0, nSize); return pMemOut; } +#endif void _free_base( void *pMem ) { @@ -200,7 +248,11 @@ void * __cdecl _malloc_crt(size_t size) void * __cdecl _calloc_crt(size_t count, size_t size) { +#if _MSC_VER >= 1900 + return _calloc_base(count, size); +#else return _calloc_base( count * size ); +#endif } void * __cdecl _realloc_crt(void *ptr, size_t size) @@ -210,17 +262,29 @@ void * __cdecl _realloc_crt(void *ptr, size_t size) void * __cdecl _recalloc_crt(void *ptr, size_t count, size_t size) { +#if _MSC_VER >= 1900 + return _recalloc_base(ptr, count, size); +#else return _recalloc_base( ptr, size * count ); +#endif } ALLOC_CALL void * __cdecl _recalloc ( void * memblock, size_t count, size_t size ) { - void *pMem = ReallocUnattributed( memblock, size * count ); - memset( pMem, 0, size * count ); - return pMem; + const size_t oldSize = _msize(memblock); + const size_t newSize = count * size; + void* pMemOut = ReallocUnattributed(memblock, newSize); + + if (newSize > oldSize) + memset(((char*)pMemOut) + oldSize, 0, newSize - oldSize); + + return pMemOut; } size_t _msize_base( void *pMem ) +#if _MSC_VER >= 1925 //VS2019+ + throw() +#endif { return g_pMemAlloc->GetSize(pMem); } @@ -485,6 +549,7 @@ void *__cdecl _calloc_dbg_impl( size_t nNum, size_t nSize, int nBlockUse, return _calloc_dbg( nNum, nSize, nBlockUse, szFileName, nLine ); } +#ifdef DEBUG void *__cdecl _realloc_dbg( void *pMem, size_t nNewSize, int nBlockUse, const char *pFileName, int nLine ) { @@ -498,6 +563,7 @@ void *__cdecl _expand_dbg( void *pMem, size_t nNewSize, int nBlockUse, Assert( 0 ); return NULL; } +#endif void __cdecl _free_dbg( void *pMem, int nBlockUse ) { @@ -505,6 +571,7 @@ void __cdecl _free_dbg( void *pMem, int nBlockUse ) g_pMemAlloc->Free(pMem); } +#ifdef DEBUG size_t __cdecl _msize_dbg( void *pMem, int nBlockUse ) { #ifdef _WIN32 @@ -514,6 +581,7 @@ size_t __cdecl _msize_dbg( void *pMem, int nBlockUse ) return 0; #endif } +#endif #ifdef _WIN32 @@ -614,6 +682,7 @@ ALLOC_CALL void * __cdecl _aligned_offset_recalloc( void * memblock, size_t coun extern "C" { +#ifdef DEBUG int _CrtDumpMemoryLeaks(void) { return 0; @@ -628,11 +697,25 @@ int _CrtSetDbgFlag( int nNewFlag ) { return g_pMemAlloc->CrtSetDbgFlag( nNewFlag ); } +#endif // 64-bit port. #define AFNAME(var) __p_ ## var #define AFRET(var) &var +#if _MSC_VER >= 1900 +int* __cdecl __p__crtDbgFlag(void) +{ + static int dummy = _CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF; + return &dummy; +} + +long* __cdecl __p__crtBreakAlloc(void) +{ + static long dummy = 0; + return &dummy; +} +#else int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF; int* AFNAME(_crtDbgFlag)(void) { @@ -644,12 +727,14 @@ long* AFNAME(_crtBreakAlloc) (void) { return AFRET(_crtBreakAlloc); } +#endif void __cdecl _CrtSetDbgBlockType( void *pMem, int nBlockUse ) { DebuggerBreak(); } +#ifdef DEBUG _CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook ) { DebuggerBreak(); @@ -710,13 +795,14 @@ void __cdecl _CrtDoForAllClientObjects( void (*pfn)(void *, void *), void * pCon { DebuggerBreak(); } - +#endif //----------------------------------------------------------------------------- // Methods in dbgrpt.cpp //----------------------------------------------------------------------------- long _crtAssertBusy = -1; +#ifdef DEBUG int __cdecl _CrtSetReportMode( int nReportType, int nReportMode ) { return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode ); @@ -731,6 +817,7 @@ _CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) { return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook ); } +#endif int __cdecl _CrtDbgReport( int nRptType, const char * szFile, int nLine, const char * szModule, const char * szFormat, ... ) @@ -863,7 +950,7 @@ ErrorHandlerRegistrar::ErrorHandlerRegistrar() _set_invalid_parameter_handler( VInvalidParameterHandler ); } -#if defined( _DEBUG ) +#if 0 // defined( _DEBUG ) // wrapper which passes no debug info; not available in debug #ifndef SUPPRESS_INVALID_PARAMETER_NO_INFO @@ -887,21 +974,41 @@ int __cdecl __crtMessageWindowW( int nRptType, const wchar_t * szFile, const wch int __cdecl _CrtDbgReportV( int nRptType, const wchar_t *szFile, int nLine, const wchar_t *szModule, const wchar_t *szFormat, va_list arglist ) { - Assert(0); + wchar_t buffer[256]; + vswprintf(buffer, 256, szFormat, arglist); + DevWarning("%ls", buffer); return 0; } int __cdecl _CrtDbgReportW( int nRptType, const wchar_t *szFile, int nLine, const wchar_t *szModule, const wchar_t *szFormat, ...) { - Assert(0); + wchar_t buffer[256]; + va_list args; + va_start(args, szFormat); + vswprintf(buffer, 256, szFormat, args); + va_end(args); + DevWarning("%ls", buffer); return 0; } +#if _MSC_VER >= 1900 +int __cdecl _VCrtDbgReportA(int nRptType, void* returnAddress, const char* szFile, int nLine, + const char* szModule, const char* szFormat, va_list arglist) +#else int __cdecl _VCrtDbgReportA( int nRptType, const wchar_t * szFile, int nLine, const wchar_t * szModule, const wchar_t * szFormat, va_list arglist ) +#endif { - Assert(0); +#if _MSC_VER >= 1900 + char buffer[256]; + vsnprintf(buffer, 256, szFormat, arglist); + DevWarning("%s", buffer); +#else + wchar_t buffer[256]; + vswprintf(buffer, 256, szFormat, arglist); + DevWarning("%ls", buffer); +#endif // _MSC_VER >= 1900 return 0; } @@ -927,13 +1034,12 @@ extern "C" int __cdecl _CrtGetCheckCount( void ) return __crtDebugCheckCount; } +#ifdef DEBUG // aligned offset debug extern "C" void * __cdecl _aligned_offset_recalloc_dbg( void * memblock, size_t count, size_t size, size_t align, size_t offset, const char * f_name, int line_n ) { Assert( IsPC() || 0 ); - void *pMem = ReallocUnattributed( memblock, size * count ); - memset( pMem, 0, size * count ); - return pMem; + return ReallocUnattributed(memblock, size * count); } extern "C" void * __cdecl _aligned_recalloc_dbg( void *memblock, size_t count, size_t size, size_t align, const char * f_name, int line_n ) @@ -950,12 +1056,16 @@ _CRT_REPORT_HOOK __cdecl _CrtGetReportHook( void ) { return NULL; } +#endif // DEBUG #endif + +#ifdef DEBUG int __cdecl _CrtReportBlockType(const void * pUserData) { return 0; } +#endif } // end extern "C" @@ -995,14 +1105,14 @@ void * __cdecl _heap_alloc_dbg( size_t nSize, int nBlockUse, const char * szFile static void * __cdecl realloc_help( void * pUserData, size_t * pnNewSize, int nBlockUse,const char * szFileName, int nLine, int fRealloc ) { - assert(0); // Shouldn't be needed + Assert(0); // Shouldn't be needed return NULL; } #else static void * __cdecl realloc_help( void * pUserData, size_t nNewSize, int nBlockUse, const char * szFileName, int nLine, int fRealloc) { - assert(0); // Shouldn't be needed + Assert(0); // Shouldn't be needed return NULL; } #endif @@ -1018,11 +1128,13 @@ void __cdecl _free_dbg_nolock( void * pUserData, int nBlockUse) _free_dbg(pUserData, 0); } +#ifdef DEBUG _CRT_ALLOC_HOOK __cdecl _CrtGetAllocHook ( void) { - assert(0); + Assert(0); return NULL; } +#endif static int __cdecl CheckBytes( unsigned char * pb, unsigned char bCheck, size_t nSize) { @@ -1030,12 +1142,13 @@ static int __cdecl CheckBytes( unsigned char * pb, unsigned char bCheck, size_t return bOkay; } - +#ifdef DEBUG _CRT_DUMP_CLIENT __cdecl _CrtGetDumpClient ( void) { - assert(0); + Assert(0); return NULL; } +#endif #if _MSC_VER >= 1400 static void __cdecl _printMemBlockData( _locale_t plocinfo, _CrtMemBlockHeader * pHead) @@ -1046,6 +1159,8 @@ static void __cdecl _CrtMemDumpAllObjectsSince_stat( const _CrtMemState * state, { } #endif + +#if defined(DEBUG) && _MSC_VER >= 1900 void * __cdecl _aligned_malloc_dbg( size_t size, size_t align, const char * f_name, int line_n) { return _aligned_malloc(size, align); @@ -1073,16 +1188,19 @@ void __cdecl _aligned_free_dbg( void * memblock) { _aligned_free(memblock); } +#endif // DEBUG +#if _MSC_VER < 1900 size_t __cdecl _CrtSetDebugFillThreshold( size_t _NewDebugFillThreshold) { assert(0); return 0; } +#endif //=========================================== // NEW!!! 64-bit - +#ifndef PROTECTED_THINGS_DISABLE char * __cdecl _strdup ( const char * string ) { int nSize = (int)strlen(string) + 1; @@ -1094,6 +1212,7 @@ char * __cdecl _strdup ( const char * string ) memcpy( pCopy, string, nSize ); return pCopy; } +#endif #if 0 _TSCHAR * __cdecl _tfullpath_dbg ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen, int nBlockUse, const char * szFileName, int nLine ) @@ -1343,6 +1462,12 @@ _CRTIMP extern uintptr_t __cdecl __threadhandle(void); /* Structure for each thread's data */ +#if _MSC_VER >= 1900 +typedef __crt_multibyte_data* pthreadmbcinfo; +typedef __crt_locale_data* pthreadlocinfo; +typedef __crt_locale_pointers _locale_tstruct; +#endif + struct _tiddata { unsigned long _tid; /* thread ID */ @@ -1386,7 +1511,7 @@ struct _tiddata { * the thread */ pthreadmbcinfo ptmbcinfo; - /* pointer to the copy of the locale informaton used by the thead */ + /* pointer to the copy of the locale information used by the thread */ pthreadlocinfo ptlocinfo; int _ownlocale; /* if 1, this thread owns its own locale */ @@ -1501,7 +1626,7 @@ struct _tiddata { * the thread */ pthreadmbcinfo ptmbcinfo; - /* pointer to the copy of the locale informaton used by the thead */ + /* pointer to the copy of the locale information used by the thread */ pthreadlocinfo ptlocinfo; int _ownlocale; /* if 1, this thread owns its own locale */ diff --git a/mp/src/public/tier0/platform.h b/mp/src/public/tier0/platform.h index 652c27c0..8a50b86b 100644 --- a/mp/src/public/tier0/platform.h +++ b/mp/src/public/tier0/platform.h @@ -400,7 +400,7 @@ typedef void * HINSTANCE; #define MAX_UNICODE_PATH MAX_PATH #endif -#define MAX_UNICODE_PATH_IN_UTF8 MAX_UNICODE_PATH*4 +#define MAX_UNICODE_PATH_IN_UTF8 (MAX_UNICODE_PATH*4) #ifdef GNUC #undef offsetof @@ -412,7 +412,7 @@ typedef void * HINSTANCE; #endif -#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression +#define ALIGN_VALUE( val, alignment ) ( ( (val) + (alignment) - 1 ) & ~( (alignment) - 1 ) ) // need macro for constant expression // Used to step into the debugger #if defined( _WIN32 ) && !defined( _X360 ) @@ -618,7 +618,16 @@ typedef void * HINSTANCE; #endif // GCC 3.4.1 has a bug in supporting forced inline of templated functions // this macro lets us not force inlining in that case - #define FORCEINLINE_TEMPLATE inline +#if __GNUC__ < 4 +#define FORCEINLINE_TEMPLATE inline +#else +#define FORCEINLINE_TEMPLATE inline __attribute__((always_inline)) +#endif +#if __cpp_constexpr >= 201304 +#define CONSTEXPR_FUNC constexpr +#else +#define CONSTEXPR_FUNC +#endif // #define __stdcall __attribute__ ((__stdcall__)) #endif @@ -718,6 +727,40 @@ typedef void * HINSTANCE; #pragma warning( disable : 4312 ) // conversion from 'unsigned int' to 'memhandle_t' of greater size #endif +// Detect C++11 support for "rvalue references" / "move semantics" / other C++11 (and up) stuff +#if defined(_MSC_VER) +#if _MSC_VER >= 1600 +#define VALVE_RVALUE_REFS 1 +#endif +#if _MSC_VER >= 1800 +#define VALVE_INITIALIZER_LIST_SUPPORT 1 +#define VALVE_EXPLICIT_CONVERSION_OP 1 +#endif +#elif defined(__clang__) +#if __has_extension(cxx_rvalue_references) +#define VALVE_RVALUE_REFS 1 +#endif +#if __has_feature(cxx_generalized_initializers) +#define VALVE_INITIALIZER_LIST_SUPPORT 1 +#endif +#if __has_feature(cxx_explicit_conversions) +#define VALVE_EXPLICIT_CONVERSION_OP 1 +#endif +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 ) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#define VALVE_RVALUE_REFS 1 +#define VALVE_INITIALIZER_LIST_SUPPORT 1 +#define VALVE_EXPLICIT_CONVERSION_OP 1 +#endif +#endif +#endif + +#ifdef VALVE_RVALUE_REFS +#include "tier0/valve_minmax_off.h" +#include +#include "tier0/valve_minmax_on.h" +#endif #ifdef POSIX #define _stricmp stricmp @@ -750,29 +793,6 @@ typedef uint32 HMODULE; typedef void *HANDLE; #endif -//----------------------------------------------------------------------------- -// fsel -//----------------------------------------------------------------------------- -#ifndef _X360 - -static FORCEINLINE float fsel(float fComparand, float fValGE, float fLT) -{ - return fComparand >= 0 ? fValGE : fLT; -} -static FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) -{ - return fComparand >= 0 ? fValGE : fLT; -} - -#else - -// __fsel(double fComparand, double fValGE, double fLT) == fComparand >= 0 ? fValGE : fLT -// this is much faster than if ( aFloat > 0 ) { x = .. } -#define fsel __fsel - -#endif - - //----------------------------------------------------------------------------- // FP exception handling //----------------------------------------------------------------------------- @@ -1305,6 +1325,15 @@ inline bool Plat_IsInDebugSession( bool bForceRecheck = false ) { return false; PLATFORM_INTERFACE bool Is64BitOS(); +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// General Mapbase version constants compiled into projects for versioning purposes +//----------------------------------------------------------------------------- +#define MAPBASE_VERSION "7.1" +#define MAPBASE_VER_INT 7100 // For use in #if in a similar fashion to macros like _MSC_VER +#endif + + //----------------------------------------------------------------------------- // XBOX Components valid in PC compilation space //----------------------------------------------------------------------------- @@ -1376,62 +1405,81 @@ inline const char *GetPlatformExt( void ) template inline T* Construct( T* pMemory ) { + HINT(pMemory != 0); return ::new( pMemory ) T; } template inline T* Construct( T* pMemory, ARG1 a1 ) { + HINT(pMemory != 0); return ::new( pMemory ) T( a1 ); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2 ) { + HINT(pMemory != 0); return ::new( pMemory ) T( a1, a2 ); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3 ) { + HINT(pMemory != 0); return ::new( pMemory ) T( a1, a2, a3 ); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4 ) { + HINT(pMemory != 0); return ::new( pMemory ) T( a1, a2, a3, a4 ); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4, ARG5 a5 ) { + HINT(pMemory != 0); return ::new( pMemory ) T( a1, a2, a3, a4, a5 ); } template inline void ConstructOneArg( T* pMemory, P const& arg) { + HINT(pMemory != 0); ::new( pMemory ) T(arg); } template inline void ConstructTwoArg( T* pMemory, P1 const& arg1, P2 const& arg2) { + HINT(pMemory != 0); ::new( pMemory ) T(arg1, arg2); } template inline void ConstructThreeArg( T* pMemory, P1 const& arg1, P2 const& arg2, P3 const& arg3) { + HINT(pMemory != 0); ::new( pMemory ) T(arg1, arg2, arg3); } template inline T* CopyConstruct( T* pMemory, T const& src ) { + HINT(pMemory != 0); return ::new( pMemory ) T(src); } + +#ifdef VALVE_RVALUE_REFS +template +inline void CopyConstruct(T* pMemory, T&& src) +{ + HINT(pMemory != 0); + ::new(pMemory)T(std::forward(src)); +} +#endif template inline void Destruct( T* pMemory ) diff --git a/mp/src/public/tier1/UtlSortVector.h b/mp/src/public/tier1/UtlSortVector.h index 9890c607..02756f70 100644 --- a/mp/src/public/tier1/UtlSortVector.h +++ b/mp/src/public/tier1/UtlSortVector.h @@ -244,7 +244,7 @@ void CUtlSortVector::QuickSort( LessFunc& less, int nLo ctx.m_pLessContext = m_pLessContext; ctx.m_pLessFunc = &less; - qsort_s( Base(), Count(), sizeof(T), (QSortCompareFunc_t)&CUtlSortVector::CompareHelper, &ctx ); + qsort_s( this->Base(), this->Count(), sizeof(T), (QSortCompareFunc_t)&CUtlSortVector::CompareHelper, &ctx ); } #else typedef int (__cdecl *QSortCompareFunc_t)( const void *, const void *); diff --git a/mp/src/public/tier1/byteswap.h b/mp/src/public/tier1/byteswap.h index 9b082541..9c866639 100644 --- a/mp/src/public/tier1/byteswap.h +++ b/mp/src/public/tier1/byteswap.h @@ -186,7 +186,7 @@ public: if( !m_bSwapBytes || ( sizeof(T) == 1 ) ) { // If we were just going to swap in place then return. - if( !inputBuffer ) + if( inputBuffer == outputBuffer ) return; // Otherwise copy the inputBuffer to the outputBuffer: diff --git a/mp/src/public/tier1/convar.h b/mp/src/public/tier1/convar.h index a08e1ce4..d145d362 100644 --- a/mp/src/public/tier1/convar.h +++ b/mp/src/public/tier1/convar.h @@ -21,7 +21,7 @@ #include "tier1/utlvector.h" #include "tier1/utlstring.h" #include "icvar.h" -#include "color.h" +#include "Color.h" #ifdef _WIN32 #define FORCEINLINE_CVAR FORCEINLINE @@ -301,6 +301,10 @@ private: ICommandCallback *m_pCommandCallback; }; +#ifdef MAPBASE_VSCRIPT + // Allow late modification of the completion callback. +public: +#endif union { FnCommandCompletionCallback m_fnCompletionCallback; @@ -308,6 +312,9 @@ private: }; bool m_bHasCompletionCallback : 1; +#ifdef MAPBASE_VSCRIPT +private: +#endif bool m_bUsingNewCommandCallback : 1; bool m_bUsingCommandCallbackInterface : 1; }; diff --git a/mp/src/public/tier1/interval.h b/mp/src/public/tier1/interval.h new file mode 100644 index 00000000..08f72610 --- /dev/null +++ b/mp/src/public/tier1/interval.h @@ -0,0 +1,20 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef INTERVAL_H +#define INTERVAL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" + + +interval_t ReadInterval( const char *pString ); +float RandomInterval( const interval_t &interval ); + +#endif // INTERVAL_H diff --git a/mp/src/public/tier1/mapbase_con_groups.h b/mp/src/public/tier1/mapbase_con_groups.h index c0e35626..7346092e 100644 --- a/mp/src/public/tier1/mapbase_con_groups.h +++ b/mp/src/public/tier1/mapbase_con_groups.h @@ -16,23 +16,48 @@ //static const Color CON_COLOR_DEV_VERBOSE( 192, 128, 192, 255 ); -// General -#define CON_GROUP_MAPBASE_MISC "Mapbase Misc." -#define CON_GROUP_PHYSICS "Physics" +enum ConGroupID_t +{ + // General + CON_GROUP_MAPBASE_MISC, // "Mapbase misc." + CON_GROUP_PHYSICS, // "Physics" + CON_GROUP_IO_SYSTEM, // "Entity I/O" + CON_GROUP_RESPONSE_SYSTEM, // "Response System" -// 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" + // Game + CON_GROUP_NPC_AI, // "NPC AI" + CON_GROUP_NPC_SCRIPTS, // "NPC scripts" + CON_GROUP_SPEECH_AI, // "Speech AI" + CON_GROUP_CHOREO, // "Choreo" -// VScript -#define CON_GROUP_VSCRIPT "VScript" -#define CON_GROUP_VSCRIPT_PRINT "VScript Print" + // VScript + CON_GROUP_VSCRIPT, // "VScript" + CON_GROUP_VSCRIPT_PRINT, // "VScript print" + + //-------------------------- + + // + // Mod groups can be inserted here + // + + //-------------------------- + + CON_GROUP_MAX, // Keep this at the end +}; // Mapbase console group message. -void CGMsg( int level, const char *pszGroup, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +void CGMsg( int level, ConGroupID_t nGroup, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 3, 4 ); #define CGWarning CGMsg +//----------------------------------------------------------------------------- + +class IBaseFileSystem; + +void InitConsoleGroups( IBaseFileSystem *filesystem ); + +void PrintAllConsoleGroups(); +void ToggleConsoleGroups( const char *pszQuery ); +void SetConsoleGroupIncludeNames( bool bToggle ); + #endif diff --git a/mp/src/public/tier1/mapbase_matchers_base.h b/mp/src/public/tier1/mapbase_matchers_base.h new file mode 100644 index 00000000..beb1d869 --- /dev/null +++ b/mp/src/public/tier1/mapbase_matchers_base.h @@ -0,0 +1,61 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: General matching functions for things like wildcards and !=. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef MAPBASE_MATCHERS_BASE_H +#define MAPBASE_MATCHERS_BASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +#define MAPBASE_MATCHERS 1 + +// 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 ); + +// Returns true if the specified string contains a wildcard character. +bool Matcher_ContainsWildcard( const char *pszQuery ); + +// 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/public/tier1/strtools.h b/mp/src/public/tier1/strtools.h index 12e0a0c4..da82d641 100644 --- a/mp/src/public/tier1/strtools.h +++ b/mp/src/public/tier1/strtools.h @@ -674,6 +674,16 @@ inline void V_wcscat( INOUT_Z_CAP(cchDest) wchar_t *dest, const wchar_t *src, in V_wcsncat( dest, src, cchDest, COPY_ALL_CHARACTERS ); } +// Reentrant strtok +inline static char* V_strtok_s( char *str, const char *delimiters, char **context ) +{ +#ifdef _MSC_VER + return strtok_s( str, delimiters, context ); +#elif POSIX + return strtok_r( str, delimiters, context ); +#endif +} + //----------------------------------------------------------------------------- // generic unique name helper functions //----------------------------------------------------------------------------- diff --git a/mp/src/public/vgui/MouseCode.h b/mp/src/public/vgui/MouseCode.h index 7ba16214..9b13fc81 100644 --- a/mp/src/public/vgui/MouseCode.h +++ b/mp/src/public/vgui/MouseCode.h @@ -18,6 +18,15 @@ namespace vgui { typedef ButtonCode_t MouseCode; + +static inline int MouseButtonBit(MouseCode code) +{ + if (code < MOUSE_FIRST || code > MOUSE_LAST) { + Assert(false); + return 0; + } + return 1 << (code - MOUSE_FIRST); +} } #endif // MOUSECODE_H diff --git a/mp/src/public/vgui_controls/Panel.h b/mp/src/public/vgui_controls/Panel.h index 174dea42..237a69e2 100644 --- a/mp/src/public/vgui_controls/Panel.h +++ b/mp/src/public/vgui_controls/Panel.h @@ -139,7 +139,7 @@ class IForceVirtualInheritancePanel // This is designed as an easy-access to the vgui-functionality; for more // low-level access to vgui functions use the IPanel/IClientPanel interfaces directly //----------------------------------------------------------------------------- -class Panel : public IClientPanel, virtual IForceVirtualInheritancePanel +class Panel : public IClientPanel, public virtual IForceVirtualInheritancePanel { DECLARE_CLASS_SIMPLE_NOBASE( Panel ); @@ -1020,7 +1020,7 @@ public: void VguiPanelGetSortedChildPanelList( Panel *pParentPanel, void *pSortedPanels ); -void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, char *pchFilter = NULL, int nFilterType = 0 ); +void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, const char *pchFilter = NULL, int nFilterType = 0 ); int VguiPanelNavigateSortedChildButtonList( void *pSortedPanels, int nDir ); diff --git a/mp/src/public/vgui_controls/TextImage.h b/mp/src/public/vgui_controls/TextImage.h index 20446e7b..97a918d1 100644 --- a/mp/src/public/vgui_controls/TextImage.h +++ b/mp/src/public/vgui_controls/TextImage.h @@ -108,6 +108,11 @@ public: const wchar_t *GetEllipsesPosition( void ) const { return m_pwszEllipsesPosition; } bool IsWrapping() const { return m_LineBreaks.Count() != 0; } +#ifdef MAPBASE + // Gets the relative y coordinates of all new lines created by newline (\n) characters. + void GetNewlinePositions( CUtlVector *pOutCoords, bool bIgnoreEmptyLines = true ); +#endif + protected: // truncate the _text string to fit into the draw width void SizeText(wchar_t *tempText, int stringLength); diff --git a/mp/src/public/vphysics_interface.h b/mp/src/public/vphysics_interface.h index d6e61935..1852ab8c 100644 --- a/mp/src/public/vphysics_interface.h +++ b/mp/src/public/vphysics_interface.h @@ -961,30 +961,6 @@ 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 index fb3a1b79..58f981e0 100644 --- a/mp/src/public/vscript/ivscript.h +++ b/mp/src/public/vscript/ivscript.h @@ -95,6 +95,12 @@ #ifndef IVSCRIPT_H #define IVSCRIPT_H +#include +#include + +#include "utlmap.h" +#include "utlvector.h" + #include "platform.h" #include "datamap.h" #include "appframework/IAppSystem.h" @@ -134,6 +140,8 @@ class KeyValues; // This has been moved up a bit for IScriptManager DECLARE_POINTER_HANDLE( HSCRIPT ); #define INVALID_HSCRIPT ((HSCRIPT)-1) + +typedef unsigned int HScriptRaw; #endif enum ScriptLanguage_t @@ -163,20 +171,6 @@ public: // //----------------------------------------------------------------------------- -#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, @@ -645,8 +639,21 @@ struct ScriptEnumDesc_t // //----------------------------------------------------------------------------- +// forwards T (and T&) if T is neither enum or an unsigned integer +// the overload for int below captures enums and unsigned integers and "bends" them to int +template +static inline typename std::enable_if::type>::value && !std::is_unsigned::type>::value, T&&>::type ToConstantVariant(T &&value) +{ + return std::forward(value); +} + +static inline int ToConstantVariant(int value) +{ + return value; +} + #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) +#define ScriptRegisterConstantNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ToConstantVariant(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. @@ -695,29 +702,16 @@ struct ScriptEnumDesc_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 ) \ + template <> ScriptClassDesc_t* GetScriptDesc(baseClass*); \ + template <> ScriptClassDesc_t* GetScriptDesc(className*); \ + ScriptClassDesc_t & g_##className##_ScriptDesc = *GetScriptDesc(nullptr); \ + template <> ScriptClassDesc_t* GetScriptDesc(className*) \ { \ - static bool bInitialized; \ - if ( bInitialized ) \ - { \ - return &g_##className##_ScriptDesc; \ - } \ - \ - bInitialized = true; \ - \ + static ScriptClassDesc_t g_##className##_ScriptDesc; \ typedef className _className; \ ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ + if (pDesc->m_pszClassname) return pDesc; \ pDesc->m_pszDescription = description; \ ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ @@ -766,6 +760,23 @@ struct ScriptEnumDesc_t pDesc->m_Hooks.AddToTail(pHook); \ } +// Static hooks (or "global" hooks) are not tied to specific classes +#define END_SCRIPTHOOK_STATIC( pVM ) \ + pVM->RegisterHook( pHook ); \ + } + +#define ScriptRegisterSimpleHook( pVM, 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; \ + pVM->RegisterHook( pHook ); \ + } + +#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 = ToConstantVariant(constant); pVM->RegisterConstant( &binding ); } while (0) + + #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 @@ -822,6 +833,8 @@ enum ScriptStatus_t class IScriptVM { public: + virtual ~IScriptVM() {} + virtual bool Init() = 0; virtual void Shutdown() = 0; @@ -873,6 +886,15 @@ public: //-------------------------------------------------------- virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; +#ifdef MAPBASE_VSCRIPT + //-------------------------------------------------------- + // Hooks + //-------------------------------------------------------- + // Persistent unique identifier for an HSCRIPT variable + virtual HScriptRaw HScriptToRaw( HSCRIPT val ) = 0; + virtual ScriptStatus_t ExecuteHookFunction( const char *pszEventName, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; +#endif + //-------------------------------------------------------- // External functions //-------------------------------------------------------- @@ -893,6 +915,11 @@ public: // External enums //-------------------------------------------------------- virtual void RegisterEnum( ScriptEnumDesc_t *pEnumDesc ) = 0; + + //-------------------------------------------------------- + // External hooks + //-------------------------------------------------------- + virtual void RegisterHook( ScriptHook_t *pHookDesc ) = 0; #endif //-------------------------------------------------------- @@ -932,6 +959,9 @@ public: 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 ); } +#ifdef MAPBASE_VSCRIPT + virtual bool SetValue( HSCRIPT hScope, const ScriptVariant_t& key, const ScriptVariant_t& val ) = 0; +#endif virtual void CreateTable( ScriptVariant_t &Table ) = 0; virtual int GetNumTableEntries( HSCRIPT hScope ) = 0; @@ -939,13 +969,19 @@ public: 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 ); } +#ifdef MAPBASE_VSCRIPT + virtual bool GetValue( HSCRIPT hScope, ScriptVariant_t key, ScriptVariant_t* pValue ) = 0; +#endif 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 bool ClearValue( HSCRIPT hScope, ScriptVariant_t pKey ) = 0; +#endif #ifdef MAPBASE_VSCRIPT - // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; + virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) = 0; #endif @@ -1094,6 +1130,20 @@ public: #endif }; +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT +template T *HScriptToClass( HSCRIPT hObj ) +{ + extern IScriptVM *g_pScriptVM; + return (hObj) ? (T*)g_pScriptVM->GetInstanceValue( hObj, GetScriptDesc( (T*)NULL ) ) : NULL; +} +#else +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) +#endif //----------------------------------------------------------------------------- // Script scope helper class @@ -1542,23 +1592,300 @@ typedef CScriptScopeT<> CScriptScope; #define VScriptAddEnumToRoot( enumVal ) g_pScriptVM->SetValue( #enumVal, (int)enumVal ) #ifdef MAPBASE_VSCRIPT + +// +// Map pointer iteration +// +#define FOR_EACH_MAP_PTR( mapName, iteratorName ) \ + for ( int iteratorName = (mapName)->FirstInorder(); (mapName)->IsUtlMap && iteratorName != (mapName)->InvalidIndex(); iteratorName = (mapName)->NextInorder( iteratorName ) ) + +#define FOR_EACH_MAP_PTR_FAST( mapName, iteratorName ) \ + for ( int iteratorName = 0; (mapName)->IsUtlMap && iteratorName < (mapName)->MaxElement(); ++iteratorName ) if ( !(mapName)->IsValidIndex( iteratorName ) ) continue; else + +#define FOR_EACH_VEC_PTR( vecName, iteratorName ) \ + for ( int iteratorName = 0; iteratorName < (vecName)->Count(); iteratorName++ ) + +//----------------------------------------------------------------------------- + +static void __UpdateScriptHooks( HSCRIPT hooksList ); + +//----------------------------------------------------------------------------- +// +// Keeps track of which events and scopes are hooked without polling this from the script VM on each request. +// Local cache is updated each time there is a change to script hooks: on Add, on Remove, on game restore +// +//----------------------------------------------------------------------------- +class CScriptHookManager +{ +private: + typedef CUtlVector< char* > contextmap_t; + typedef CUtlMap< HScriptRaw, contextmap_t* > scopemap_t; + typedef CUtlMap< char*, scopemap_t* > hookmap_t; + + HSCRIPT m_hfnHookFunc; + + // { [string event], { [HSCRIPT scope], { [string context], [HSCRIPT callback] } } } + hookmap_t m_HookList; + +public: + + CScriptHookManager() : m_HookList( DefLessFunc(char*) ), m_hfnHookFunc(NULL) + { + } + + HSCRIPT GetHookFunction() + { + return m_hfnHookFunc; + } + + // For global hooks + bool IsEventHooked( const char *szEvent ) + { + return m_HookList.Find( const_cast< char* >( szEvent ) ) != m_HookList.InvalidIndex(); + } + + bool IsEventHookedInScope( const char *szEvent, HSCRIPT hScope ) + { + extern IScriptVM *g_pScriptVM; + + Assert( hScope ); + + int eventIdx = m_HookList.Find( const_cast< char* >( szEvent ) ); + if ( eventIdx == m_HookList.InvalidIndex() ) + return false; + + scopemap_t *scopeMap = m_HookList.Element( eventIdx ); + return scopeMap->Find( g_pScriptVM->HScriptToRaw( hScope ) ) != scopeMap->InvalidIndex(); + } + + // + // On VM init, registers script func and caches the hook func. + // + void OnInit() + { + extern IScriptVM *g_pScriptVM; + + ScriptRegisterFunctionNamed( g_pScriptVM, __UpdateScriptHooks, "__UpdateScriptHooks", SCRIPT_HIDE ); + + ScriptVariant_t hHooks; + g_pScriptVM->GetValue( "Hooks", &hHooks ); + + Assert( hHooks.m_type == FIELD_HSCRIPT ); + + if ( hHooks.m_type == FIELD_HSCRIPT ) + { + m_hfnHookFunc = g_pScriptVM->LookupFunction( "Call", hHooks ); + } + + Clear(); + } + + // + // On VM shutdown, clear the cache. + // Not exactly necessary, as the cache will be cleared on VM init next time. + // + void OnShutdown() + { + extern IScriptVM *g_pScriptVM; + + if ( m_hfnHookFunc ) + g_pScriptVM->ReleaseFunction( m_hfnHookFunc ); + + m_hfnHookFunc = NULL; + + Clear(); + } + + // + // On VM restore, update local cache. + // + void OnRestore() + { + extern IScriptVM *g_pScriptVM; + + ScriptVariant_t hHooks; + g_pScriptVM->GetValue( "Hooks", &hHooks ); + + if ( hHooks.m_type == FIELD_HSCRIPT ) + { + // Existing m_hfnHookFunc is invalid + m_hfnHookFunc = g_pScriptVM->LookupFunction( "Call", hHooks ); + + HSCRIPT func = g_pScriptVM->LookupFunction( "__UpdateHooks", hHooks ); + g_pScriptVM->Call( func ); + g_pScriptVM->ReleaseFunction( func ); + g_pScriptVM->ReleaseValue( hHooks ); + } + } + + // + // Clear local cache. + // + void Clear() + { + if ( m_HookList.Count() ) + { + FOR_EACH_MAP_FAST( m_HookList, i ) + { + scopemap_t *scopeMap = m_HookList.Element(i); + + FOR_EACH_MAP_PTR_FAST( scopeMap, j ) + { + contextmap_t *contextMap = scopeMap->Element(j); + contextMap->PurgeAndDeleteElements(); + } + + char *szEvent = m_HookList.Key(i); + free( szEvent ); + + scopeMap->PurgeAndDeleteElements(); + } + + m_HookList.PurgeAndDeleteElements(); + } + } + + // + // Called from script, update local cache. + // + void Update( HSCRIPT hooksList ) + { + extern IScriptVM *g_pScriptVM; + + // Rebuild from scratch + Clear(); + { + ScriptVariant_t varEvent, varScopeMap; + int it = -1; + while ( ( it = g_pScriptVM->GetKeyValue( hooksList, it, &varEvent, &varScopeMap ) ) != -1 ) + { + // types are checked in script + Assert( varEvent.m_type == FIELD_CSTRING ); + Assert( varScopeMap.m_type == FIELD_HSCRIPT ); + + scopemap_t *scopeMap; + + int eventIdx = m_HookList.Find( const_cast< char* >( varEvent.m_pszString ) ); + if ( eventIdx != m_HookList.InvalidIndex() ) + { + scopeMap = m_HookList.Element( eventIdx ); + } + else + { + scopeMap = new scopemap_t( DefLessFunc(HScriptRaw) ); + m_HookList.Insert( strdup( varEvent.m_pszString ), scopeMap ); + } + + ScriptVariant_t varScope, varContextMap; + int it2 = -1; + while ( ( it2 = g_pScriptVM->GetKeyValue( varScopeMap, it2, &varScope, &varContextMap ) ) != -1 ) + { + Assert( varScope.m_type == FIELD_HSCRIPT ); + Assert( varContextMap.m_type == FIELD_HSCRIPT); + + contextmap_t *contextMap; + + int scopeIdx = scopeMap->Find( g_pScriptVM->HScriptToRaw( varScope.m_hScript ) ); + if ( scopeIdx != scopeMap->InvalidIndex() ) + { + contextMap = scopeMap->Element( scopeIdx ); + } + else + { + contextMap = new contextmap_t(); + scopeMap->Insert( g_pScriptVM->HScriptToRaw( varScope.m_hScript ), contextMap ); + } + + ScriptVariant_t varContext, varCallback; + int it3 = -1; + while ( ( it3 = g_pScriptVM->GetKeyValue( varContextMap, it3, &varContext, &varCallback ) ) != -1 ) + { + Assert( varContext.m_type == FIELD_CSTRING ); + Assert( varCallback.m_type == FIELD_HSCRIPT ); + + bool skip = false; + + FOR_EACH_VEC_PTR( contextMap, k ) + { + char *szContext = contextMap->Element(k); + if ( V_strcmp( szContext, varContext.m_pszString ) == 0 ) + { + skip = true; + break; + } + } + + if ( !skip ) + contextMap->AddToTail( strdup( varContext.m_pszString ) ); + + g_pScriptVM->ReleaseValue( varContext ); + g_pScriptVM->ReleaseValue( varCallback ); + } + + g_pScriptVM->ReleaseValue( varScope ); + g_pScriptVM->ReleaseValue( varContextMap ); + } + + g_pScriptVM->ReleaseValue( varEvent ); + g_pScriptVM->ReleaseValue( varScopeMap ); + } + } + } +#ifdef _DEBUG + void Dump() + { + extern IScriptVM *g_pScriptVM; + + FOR_EACH_MAP( m_HookList, i ) + { + scopemap_t *scopeMap = m_HookList.Element(i); + char *szEvent = m_HookList.Key(i); + + Msg( "%s [%p]\n", szEvent, (void*)scopeMap ); + Msg( "{\n" ); + + FOR_EACH_MAP_PTR( scopeMap, j ) + { + HScriptRaw hScope = scopeMap->Key(j); + contextmap_t *contextMap = scopeMap->Element(j); + + Msg( "\t(0x%X) [%p]\n", hScope, (void*)contextMap ); + Msg( "\t{\n" ); + + FOR_EACH_VEC_PTR( contextMap, k ) + { + char *szContext = contextMap->Element(k); + + Msg( "\t\t%-.50s\n", szContext ); + } + + Msg( "\t}\n" ); + } + + Msg( "}\n" ); + } + } +#endif +}; + +inline CScriptHookManager &GetScriptHookManager() +{ + static CScriptHookManager g_ScriptHookManager; + return g_ScriptHookManager; +} + +static void __UpdateScriptHooks( HSCRIPT hooksList ) +{ + GetScriptHookManager().Update( hooksList ); +} + + //----------------------------------------------------------------------------- // 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 { @@ -1575,49 +1902,65 @@ struct ScriptHook_t // ----------------------------------------------------------------- - // Cached for when CanRunInScope() is called before Call() + // Only valid between CanRunInScope() and Call() HSCRIPT m_hFunc; - // Checks if there's a function of this name which would run in this scope - HSCRIPT CanRunInScope( HSCRIPT hScope ) + ScriptHook_t() : + m_hFunc(NULL) { - 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 ) +#ifdef _DEBUG + // + // An uninitialised script scope will pass as null scope which is considered a valid hook scope (global hook) + // This should catch CanRunInScope() calls without CScriptScope::IsInitalised() checks first. + // + bool CanRunInScope( CScriptScope &hScope ) { - // TODO: Better crtieria for this? - if (hFunc) + Assert( hScope.IsInitialized() ); + return hScope.IsInitialized() && CanRunInScope( (HSCRIPT)hScope ); + } +#endif + + // Checks if there's a function of this name which would run in this scope + bool CanRunInScope( HSCRIPT hScope ) + { + // For now, assume null scope (which is used for global hooks) is always hooked + if ( !hScope || GetScriptHookManager().IsEventHookedInScope( m_desc.m_pszScriptName, hScope ) ) { - m_hFunc = hFunc; + m_hFunc = NULL; return true; } - return false; + + extern IScriptVM *g_pScriptVM; + + // Legacy support if the new system is not being used + m_hFunc = g_pScriptVM->LookupFunction( m_desc.m_pszScriptName, hScope ); + + return !!m_hFunc; } // Call the function + // NOTE: `bRelease` only exists for weapon_custom_scripted legacy script func caching 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 + // Call() should not be called without CanRunInScope() check first, it caches m_hFunc for legacy support + Assert( CanRunInScope( hScope ) ); + + // Legacy + if ( m_hFunc ) { 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 ); + ScriptStatus_t status = g_pScriptVM->ExecuteFunction( m_hFunc, NULL, 0, pReturn, hScope, true ); - if (bRelease) + if ( bRelease ) g_pScriptVM->ReleaseFunction( m_hFunc ); - m_hFunc = NULL; for (int i = 0; i < m_desc.m_Parameters.Count(); i++) @@ -1625,10 +1968,14 @@ struct ScriptHook_t g_pScriptVM->ClearValue( m_pszParameterNames[i] ); } - return true; + return status == SCRIPT_DONE; + } + // New Hook System + else + { + ScriptStatus_t status = g_pScriptVM->ExecuteHookFunction( m_desc.m_pszScriptName, pArgs, m_desc.m_Parameters.Count(), pReturn, hScope, true ); + return status == SCRIPT_DONE; } - - return false; } }; #endif diff --git a/mp/src/public/vscript/vscript_templates.h b/mp/src/public/vscript/vscript_templates.h index e23a9fe9..2d7058a3 100644 --- a/mp/src/public/vscript/vscript_templates.h +++ b/mp/src/public/vscript/vscript_templates.h @@ -137,29 +137,25 @@ inline void *ScriptConvertFuncPtrToVoid( FUNCPTR_TYPE pFunc ) #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 vtable_index_2; // If vtable_index_2 is odd, then (vtable_index_2 - 1) * 2 is the index into the vtable. }; - int delta; + int delta; // Offset from this-ptr to vtable }; - + 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 + if ( p->delta == 0 ) { + // No need to check whether this is a direct function pointer or not, + // this gets converted back to a "proper" member-function pointer in + // ScriptConvertFuncPtrFromVoid() to get invoked return p->funcadr; } + AssertMsg( 0, "Function pointer must be from primary vtable" ); } #else #error "Need to implement code to crack non-offset member function pointer case" @@ -257,8 +253,30 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p ) convert.mfp.m_delta = 0; return convert.pFunc; } -#elif defined( POSIX ) - AssertMsg( 0, "Note: This path has not been implemented yet." ); +#elif defined( GNUC ) + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + 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 (vtable_index_2 - 1) * 2 is the index into the vtable. + }; + int delta; // Offset from this-ptr to vtable + }; + + union FuncPtrConvertGnu + { + GnuMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertGnu convert; + convert.mfp.funcadr = p; + convert.mfp.delta = 0; + return convert.pFunc; + } #else #error "Need to implement code to crack non-offset member function pointer case" #endif diff --git a/mp/src/raytrace/raytrace.cpp b/mp/src/raytrace/raytrace.cpp index 9816560d..6def3bab 100644 --- a/mp/src/raytrace/raytrace.cpp +++ b/mp/src/raytrace/raytrace.cpp @@ -425,11 +425,11 @@ void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 T MulSIMD( SubSIMD(ReplicateX4(CurNode->SplittingPlaneValue), rays.origin[split_plane_number]),OneOverRayDir[split_plane_number]); - fltx4 active=CmpLeSIMD(TMin,TMax); // mask of which rays are active + fltx4 activeLocl=CmpLeSIMD(TMin,TMax); // mask of which rays are active // now, decide how to traverse children. can either do front,back, or do front and push // back. - fltx4 hits_front=AndSIMD(active,CmpGeSIMD(dist_to_sep_plane,TMin)); + fltx4 hits_front=AndSIMD(activeLocl,CmpGeSIMD(dist_to_sep_plane,TMin)); if (! IsAnyNegative(hits_front)) { // missed the front. only traverse back @@ -440,7 +440,7 @@ void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 T } else { - fltx4 hits_back=AndSIMD(active,CmpLeSIMD(dist_to_sep_plane,TMax)); + fltx4 hits_back=AndSIMD(activeLocl,CmpLeSIMD(dist_to_sep_plane,TMax)); if (! IsAnyNegative(hits_back) ) { // missed the back - only need to traverse front node diff --git a/mp/src/raytrace/trace2.cpp b/mp/src/raytrace/trace2.cpp index 4b1d19f6..d2e50557 100644 --- a/mp/src/raytrace/trace2.cpp +++ b/mp/src/raytrace/trace2.cpp @@ -155,14 +155,14 @@ void RayTracingEnvironment::RenderScene( fltx4 MaxT=ldir.length(); ldir.VectorNormalizeFast(); // now, compute shadow flag - FourRays myrays; - myrays.origin=surface_pos; + FourRays myraysLocl; + myraysLocl.origin=surface_pos; FourVectors epsilon=ldir; epsilon*=0.01; - myrays.origin+=epsilon; - myrays.direction=ldir; + myraysLocl.origin+=epsilon; + myraysLocl.direction=ldir; RayTracingResult shadowtest; - Trace4Rays(myrays,Four_Zeros,MaxT, &shadowtest); + Trace4Rays(myraysLocl,Four_Zeros,MaxT, &shadowtest); fltx4 unshadowed=CmpGtSIMD(shadowtest.HitDistance,MaxT); if (! (IsAllZeros(unshadowed))) { diff --git a/mp/src/responserules/runtime/criteriaset.cpp b/mp/src/responserules/runtime/criteriaset.cpp new file mode 100644 index 00000000..6d53e954 --- /dev/null +++ b/mp/src/responserules/runtime/criteriaset.cpp @@ -0,0 +1,479 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "rrbase.h" + +#include "utlmap.h" + +#include "tier1/mapbase_con_groups.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace ResponseRules; + +//----------------------------------------------------------------------------- +// Case-insensitive criteria symbol table +//----------------------------------------------------------------------------- +CUtlSymbolTable CriteriaSet::sm_CriteriaSymbols( 1024, 1024, true ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *raw - +// *key - +// keylen - +// *value - +// valuelen - +// *duration - +// Output : static bool +//----------------------------------------------------------------------------- +const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration, const char *entireContext ) +{ + char *colon1 = Q_strstr( raw, ":" ); + if ( !colon1 ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "SplitContext: warning, ignoring context '%s', missing colon separator!\n", raw ); + *key = *value = 0; + return NULL; + } + + int len = colon1 - raw; + Q_strncpy( key, raw, MIN( len + 1, keylen ) ); + key[ MIN( len, keylen - 1 ) ] = 0; + + bool last = false; + char *end = Q_strstr( colon1 + 1, "," ); + if ( !end ) + { + int remaining = Q_strlen( colon1 + 1 ); + end = colon1 + 1 + remaining; + last = true; + } + + char *colon2 = Q_strstr( colon1 + 1, ":" ); + if ( colon2 && ( colon2 < end ) ) + { + if ( duration ) + *duration = atof( colon2 + 1 ); + + char durationStartChar = *(colon2 + 1); + if ( durationStartChar < '0' || durationStartChar > '9' ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "SplitContext: warning, ignoring context '%s', missing comma separator! Entire context was '%s'.\n", raw, entireContext ); + *key = *value = 0; + return NULL; + } + + len = MIN( colon2 - ( colon1 + 1 ), valuelen - 1 ); + Q_strncpy( value, colon1 + 1, len + 1 ); + value[ len ] = 0; + } + else + { + if ( duration ) + *duration = 0.0; + + len = MIN( end - ( colon1 + 1 ), valuelen - 1 ); + Q_strncpy( value, colon1 + 1, len + 1 ); + value[ len ] = 0; + } + + return last ? NULL : end + 1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CriteriaSet::CriteriaSet() : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_bOverrideOnAppend(true), + m_nNumPrefixedContexts(0) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CriteriaSet::CriteriaSet( const CriteriaSet& src ) : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_nNumPrefixedContexts(src.m_nNumPrefixedContexts) +{ + m_Lookup.EnsureCapacity( src.m_Lookup.Count() ); + 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 ] ); + } +} + +CriteriaSet::CriteriaSet( const char *criteria, const char *value ) : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_bOverrideOnAppend(true) +{ + AppendCriteria(criteria,value); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CriteriaSet::~CriteriaSet() +{ +} + +//----------------------------------------------------------------------------- +// Computes a symbol for the criteria +//----------------------------------------------------------------------------- +CriteriaSet::CritSymbol_t CriteriaSet::ComputeCriteriaSymbol( const char *criteria ) +{ + return sm_CriteriaSymbols.AddString( criteria ); +} + + +//----------------------------------------------------------------------------- +// Computes a symbol for the criteria +//----------------------------------------------------------------------------- +void CriteriaSet::AppendCriteria( CriteriaSet::CritSymbol_t criteria, const char *value, float weight ) +{ + int idx = FindCriterionIndex( criteria ); + if ( idx == -1 ) + { + CritEntry_t entry; + entry.criterianame = criteria; + MEM_ALLOC_CREDIT(); + idx = m_Lookup.Insert( entry ); + if ( sm_CriteriaSymbols.String(criteria)[0] == kAPPLYTOWORLDPREFIX ) + { + m_nNumPrefixedContexts += 1; + } + } + else // criteria already existed + { + // bail out if override existing criteria is not allowed + if ( !m_bOverrideOnAppend ) + return; + } + + CritEntry_t *entry = &m_Lookup[ idx ]; + entry->SetValue( value ); + entry->weight = weight; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criteria - +// "" - +// 1.0f - +//----------------------------------------------------------------------------- +void CriteriaSet::AppendCriteria( const char *pCriteriaName, const char *value /*= ""*/, float weight /*= 1.0f*/ ) +{ + CUtlSymbol criteria = ComputeCriteriaSymbol( pCriteriaName ); + AppendCriteria( criteria, value, weight ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criteria - +// "" - +// 1.0f - +//----------------------------------------------------------------------------- +void CriteriaSet::AppendCriteria( const char *criteria, float value, float weight /*= 1.0f*/ ) +{ + char buf[32]; + V_snprintf( buf, 32, "%f", value ); + AppendCriteria( criteria, buf, weight ); +} + + +//----------------------------------------------------------------------------- +// Removes criteria in a set +//----------------------------------------------------------------------------- +void CriteriaSet::RemoveCriteria( const char *criteria ) +{ + const int idx = FindCriterionIndex( criteria ); + if ( idx == -1 ) + return; + + if ( criteria[0] == kAPPLYTOWORLDPREFIX ) + { + Assert( m_nNumPrefixedContexts > 0 ); + m_nNumPrefixedContexts = isel( m_nNumPrefixedContexts - 1, m_nNumPrefixedContexts - 1, 0 ); + } + RemoveCriteria( idx, false ); +} + +// bTestForIndex tells us whether the calling function has already checked for a +// $ prefix and decremented m_nNumPrefixedContexts appropriately (false), +// or if this function should do that (true). +void CriteriaSet::RemoveCriteria( int idx, bool bTestForPrefix ) +{ + Assert( m_Lookup.IsValidIndex(idx) ); + if ( bTestForPrefix ) + { + if ( sm_CriteriaSymbols.String( m_Lookup[idx].criterianame )[0] == kAPPLYTOWORLDPREFIX ) + { + Assert( m_nNumPrefixedContexts > 0 ); + m_nNumPrefixedContexts = isel( m_nNumPrefixedContexts - 1, m_nNumPrefixedContexts - 1, 0 ); + } + } + m_Lookup.RemoveAt( idx ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CriteriaSet::GetCount() const +{ + return m_Lookup.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : int +//----------------------------------------------------------------------------- +int CriteriaSet::FindCriterionIndex( CritSymbol_t criteria ) const +{ + CritEntry_t search; + search.criterianame = criteria; + int idx = m_Lookup.Find( search ); + return ( idx == m_Lookup.InvalidIndex() ) ? -1 : idx; +} + +int CriteriaSet::FindCriterionIndex( const char *name ) const +{ + CUtlSymbol criteria = ComputeCriteriaSymbol( name ); + return FindCriterionIndex( criteria ); +} + + +//----------------------------------------------------------------------------- +// Returns the name symbol +//----------------------------------------------------------------------------- +CriteriaSet::CritSymbol_t CriteriaSet::GetNameSymbol( int nIndex ) const +{ + if ( nIndex < 0 || nIndex >= (int)m_Lookup.Count() ) + return UTL_INVAL_SYMBOL; + + const CritEntry_t *entry = &m_Lookup[ nIndex ]; + return entry->criterianame; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : char const +//----------------------------------------------------------------------------- +const char *CriteriaSet::GetName( int index ) const +{ + if ( index < 0 || index >= (int)m_Lookup.Count() ) + return ""; + else + { + const char *pCriteriaName = sm_CriteriaSymbols.String( m_Lookup[ index ].criterianame ); + return pCriteriaName ? pCriteriaName : ""; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : char const +//----------------------------------------------------------------------------- +const char *CriteriaSet::GetValue( int index ) const +{ + if ( index < 0 || index >= (int)m_Lookup.Count() ) + return ""; + + const CritEntry_t *entry = &m_Lookup[ index ]; + return entry->value ? entry->value : ""; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : float +//----------------------------------------------------------------------------- +float CriteriaSet::GetWeight( int index ) const +{ + if ( index < 0 || index >= (int)m_Lookup.Count() ) + return 1.0f; + + const CritEntry_t *entry = &m_Lookup[ index ]; + return entry->weight; +} + + +//----------------------------------------------------------------------------- +// Purpose: Merge another criteria set into this one. +//----------------------------------------------------------------------------- +void CriteriaSet::Merge( const CriteriaSet * RESTRICT otherCriteria ) +{ + Assert(otherCriteria); + if (!otherCriteria) + return; + + // for now, just duplicate everything. + int count = otherCriteria->GetCount(); + EnsureCapacity( count + GetCount() ); + for ( int i = 0 ; i < count ; ++i ) + { + AppendCriteria( otherCriteria->GetNameSymbol(i), otherCriteria->GetValue(i), otherCriteria->GetWeight(i) ); + } +} + +void CriteriaSet::Merge( const char *modifiers ) // add criteria parsed from a text string +{ + // Always include any optional modifiers + if ( modifiers == NULL ) + return; + + 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, modifiers ); + + if( *key && *value ) + { + AppendCriteria( key, value, 1 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CriteriaSet::Describe() const +{ + // build an alphabetized representation of the set for printing + typedef CUtlMap tMap; + tMap m_TempMap( 0, m_Lookup.Count(), CaselessStringLessThan ); + + for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) ) + { + const CritEntry_t *entry = &m_Lookup[ i ]; + + m_TempMap.Insert( sm_CriteriaSymbols.String( entry->criterianame ), entry ); + } + + for ( tMap::IndexType_t i = m_TempMap.FirstInorder(); i != m_TempMap.InvalidIndex(); i = m_TempMap.NextInorder( i ) ) + { + // const CritEntry_t *entry = &m_TempMap[ i ]; + // const char *pCriteriaName = sm_CriteriaSymbols.String( entry->criterianame ); + + const char *name = m_TempMap.Key( i ); + const CritEntry_t *entry = m_TempMap.Element( i ); + if ( entry->weight != 1.0f ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s' (weight %f)\n", name, entry->value ? entry->value : "", entry->weight ); + } + else + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s'\n", name, entry->value ? entry->value : "" ); + } + } + + /* + for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) ) + { + const CritEntry_t *entry = &m_Lookup[ i ]; + + const char *pCriteriaName = sm_CriteriaSymbols.String( entry->criterianame ); + if ( entry->weight != 1.0f ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s' (weight %f)\n", pCriteriaName, entry->value ? entry->value : "", entry->weight ); + } + else + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s'\n", pCriteriaName, entry->value ? entry->value : "" ); + } + } + */ +} + + +void CriteriaSet::Reset() +{ + m_Lookup.Purge(); +} + +void CriteriaSet::WriteToEntity( CBaseEntity *pEntity ) +{ +#if 0 + if ( GetCount() < 1 ) + return; + + for ( int i = Head() ; IsValidIndex(i); i = Next(i) ) + { + pEntity->AddContext( GetName(i), GetValue(i), 0 ); + } +#else + AssertMsg( false, "CriteriaSet::WriteToEntity has not been ported from l4d2.\n" ); +#endif +} + +int CriteriaSet::InterceptWorldSetContexts( CriteriaSet * RESTRICT pFrom, CriteriaSet * RESTRICT pSetOnWorld ) +{ + // Assert( pFrom ); Assert( pTo ); Assert( pSetOnWorld ); + Assert( pSetOnWorld != pFrom ); + Assert( pSetOnWorld->GetCount() == 0 ); + + if ( pFrom->m_nNumPrefixedContexts == 0 ) + { + // nothing needs to be done to it. + return 0; + } + +#ifdef DEBUG + // save this off for later error checking. + const int nPrefixedContexts = pFrom->m_nNumPrefixedContexts; +#endif + + // make enough space for the expected output quantity. + pSetOnWorld->EnsureCapacity( pFrom->m_nNumPrefixedContexts ); + + // initialize a buffer with the "world" prefix (so we can use strncpy instead of snprintf and be much faster) + char buf[80] = { 'w', 'o', 'r', 'l', 'd', '\0' }; + const unsigned int PREFIXLEN = 5; // strlen("world") + + // create a second tree that has the appropriately renamed criteria, + // then swap it into pFrom + CriteriaSet rewrite; + rewrite.EnsureCapacity( pFrom->GetCount() + 1 ); + + for ( int i = pFrom->Head(); pFrom->IsValidIndex(i); i = pFrom->Next(i) ) + { + const char *pszName = pFrom->GetName( i ); + if ( pszName[0] == CriteriaSet::kAPPLYTOWORLDPREFIX ) + { // redirect to the world contexts + V_strncpy( buf+PREFIXLEN, pszName+1, sizeof(buf) - PREFIXLEN ); + rewrite.AppendCriteria( buf, pFrom->GetValue(i), pFrom->GetWeight(i) ); + pSetOnWorld->AppendCriteria( pszName+1, pFrom->GetValue(i), pFrom->GetWeight(i) ); + buf[PREFIXLEN] = 0; + } + else + { // does not need to be fiddled; do not write back to world + rewrite.AppendCriteria( pFrom->GetNameSymbol(i), pFrom->GetValue(i), pFrom->GetWeight(i) ); + } + } + AssertMsg2( pSetOnWorld->GetCount() == nPrefixedContexts, "Count of $ persistent RR contexts is inconsistent (%d vs %d)! Call Elan.", + pSetOnWorld->GetCount(), nPrefixedContexts ); + + pFrom->m_nNumPrefixedContexts = 0; + pFrom->m_Lookup.Swap(rewrite.m_Lookup); + return pSetOnWorld->GetCount(); +} diff --git a/mp/src/responserules/runtime/response_rules.vpc b/mp/src/responserules/runtime/response_rules.vpc new file mode 100644 index 00000000..9ab73afa --- /dev/null +++ b/mp/src/responserules/runtime/response_rules.vpc @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// response_rules.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR "..\.." +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;..\public\responserules" + $PreprocessorDefinitions "$BASE;RR_RUNTIME" + } +} + +$Project "responserules_runtime" +{ + $Folder "Source Files" + { + $File "criteriaset.cpp" + $File "response_system.cpp" + $File "response_system.h" + $File "response_types.cpp" + $File "response_types_internal.cpp" + $File "response_types_internal.h" + $File "rr_convars.cpp" + $File "rr_response.cpp" + $File "rr_speechconcept.cpp" + $File "rrrlib.cpp" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\responserules\response_host_interface.h" + $File "$SRCDIR\public\responserules\response_types.h" + $File "$SRCDIR\public\responserules\rr_speechconcept.h" + } +} \ No newline at end of file diff --git a/mp/src/responserules/runtime/response_system.cpp b/mp/src/responserules/runtime/response_system.cpp new file mode 100644 index 00000000..7ae9276c --- /dev/null +++ b/mp/src/responserules/runtime/response_system.cpp @@ -0,0 +1,2959 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" +#include "vstdlib/random.h" +#include "utlbuffer.h" +#include "tier1/interval.h" +#include "convar.h" +#include "fmtstr.h" +#include "generichash.h" +#include "tier1/mapbase_con_groups.h" +#ifdef MAPBASE +#include "tier1/mapbase_matchers_base.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// #pragma optimize( "", off ) + +using namespace ResponseRules; +static void CC_RR_Debug_ResponseConcept_Exclude( const CCommand &args ); +static ConCommand rr_debug_responseconcept_exclude( "rr_debugresponseconcept_exclude", CC_RR_Debug_ResponseConcept_Exclude, "Set a list of concepts to exclude from rr_debugresponseconcept. Separate multiple concepts with spaces. Call with no arguments to see current list. Call 'rr_debug_responseconcept_exclude !' to reset."); + +namespace ResponseRules +{ + // ick + // Wrap string lookup with a hash on the string so that all of the repetitive playerxxx type strings get bucketed out better + #define STRING_BUCKETS_COUNT 64 // Must be power of two + #define STRING_BUCKETS_MASK ( STRING_BUCKETS_COUNT - 1 ) + + CUtlRBTree g_ResponseStrings[ STRING_BUCKETS_COUNT ]; + class CResponseStringBuckets + { + public: + CResponseStringBuckets() + { + for ( int i = 0; i < ARRAYSIZE( g_ResponseStrings ); ++i ) + { + g_ResponseStrings[ i ].SetLessFunc( &StringLessThan ); + } + } + } g_ReponseStringBucketInitializer; + + const char *ResponseCopyString( const char *in ) + { + if ( !in ) + return NULL; + if ( !*in ) + return ""; + + int bucket = ( RR_HASH( in ) & STRING_BUCKETS_MASK ); + + CUtlRBTree &list = g_ResponseStrings[ bucket ]; + + int i = list.Find( in ); + if ( i != list.InvalidIndex() ) +{ + return list[i]; + } + + int len = Q_strlen( in ); + char *out = new char[ len + 1 ]; + Q_memcpy( out, in, len ); + out[ len ] = 0; + list.Insert( out ); + return out; + } +} + +IEngineEmulator *IEngineEmulator::Get() +{ + AssertMsg( IEngineEmulator::s_pSingleton, "Response rules fail: no IEngineEmulator" ); + return IEngineEmulator::s_pSingleton; +} + + +//----------------------------------------------------------------------------- +// Convars +//----------------------------------------------------------------------------- + + +ConVar rr_debugresponses( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring, 3 for noisy). If set to 4, it will only show response success/failure for npc_selected NPCs." ); +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)" ); +ConVar rr_debugresponseconcept( "rr_debugresponseconcept", "", FCVAR_NONE, "If set, rr_debugresponses will print only responses testing for the specified concept" ); +#define RR_DEBUGRESPONSES_SPECIALCASE 4 + +#ifdef MAPBASE +ConVar rr_disableemptyrules( "rr_disableemptyrules", "0", FCVAR_NONE, "Disables rules with no remaining responses, e.g. rules which use norepeat responses." ); +#endif + + + +//----------------------------------------------------------------------------- +// Copied from SoundParametersInternal.cpp +//----------------------------------------------------------------------------- + +#define SNDLVL_PREFIX "SNDLVL_" + +struct SoundLevelLookup +{ + soundlevel_t level; + char const *name; +}; + +// NOTE: Needs to reflect the soundlevel_t enum defined in soundflags.h +static SoundLevelLookup g_pSoundLevels[] = +{ + { SNDLVL_NONE, "SNDLVL_NONE" }, + { SNDLVL_20dB, "SNDLVL_20dB" }, + { SNDLVL_25dB, "SNDLVL_25dB" }, + { SNDLVL_30dB, "SNDLVL_30dB" }, + { SNDLVL_35dB, "SNDLVL_35dB" }, + { SNDLVL_40dB, "SNDLVL_40dB" }, + { SNDLVL_45dB, "SNDLVL_45dB" }, + { SNDLVL_50dB, "SNDLVL_50dB" }, + { SNDLVL_55dB, "SNDLVL_55dB" }, + { SNDLVL_IDLE, "SNDLVL_IDLE" }, + { SNDLVL_TALKING, "SNDLVL_TALKING" }, + { SNDLVL_60dB, "SNDLVL_60dB" }, + { SNDLVL_65dB, "SNDLVL_65dB" }, + { SNDLVL_STATIC, "SNDLVL_STATIC" }, + { SNDLVL_70dB, "SNDLVL_70dB" }, + { SNDLVL_NORM, "SNDLVL_NORM" }, + { SNDLVL_75dB, "SNDLVL_75dB" }, + { SNDLVL_80dB, "SNDLVL_80dB" }, + { SNDLVL_85dB, "SNDLVL_85dB" }, + { SNDLVL_90dB, "SNDLVL_90dB" }, + { SNDLVL_95dB, "SNDLVL_95dB" }, + { SNDLVL_100dB, "SNDLVL_100dB" }, + { SNDLVL_105dB, "SNDLVL_105dB" }, + { SNDLVL_110dB, "SNDLVL_110dB" }, + { SNDLVL_120dB, "SNDLVL_120dB" }, + { SNDLVL_130dB, "SNDLVL_130dB" }, + { SNDLVL_GUNFIRE, "SNDLVL_GUNFIRE" }, + { SNDLVL_140dB, "SNDLVL_140dB" }, + { SNDLVL_150dB, "SNDLVL_150dB" }, + { SNDLVL_180dB, "SNDLVL_180dB" }, +}; + +static soundlevel_t TextToSoundLevel( const char *key ) +{ + if ( !key ) + { + Assert( 0 ); + return SNDLVL_NORM; + } + + int c = ARRAYSIZE( g_pSoundLevels ); + + int i; + + for ( i = 0; i < c; i++ ) + { + SoundLevelLookup *entry = &g_pSoundLevels[ i ]; + if ( !Q_strcasecmp( key, entry->name ) ) + return entry->level; + } + + if ( !Q_strnicmp( key, SNDLVL_PREFIX, Q_strlen( SNDLVL_PREFIX ) ) ) + { + char const *val = key + Q_strlen( SNDLVL_PREFIX ); + int sndlvl = atoi( val ); + if ( sndlvl > 0 && sndlvl <= 180 ) + { + return ( soundlevel_t )sndlvl; + } + } + + DevMsg( "CSoundEmitterSystem: Unknown sound level %s\n", key ); + + return SNDLVL_NORM; +} + +CResponseSystem::ExcludeList_t CResponseSystem::m_DebugExcludeList( 4, 0 ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CResponseSystem::CResponseSystem() : + m_RootCommandHashes( 0, 0, DefLessFunc( unsigned int ) ), + m_FileDispatch( 0, 0, DefLessFunc( unsigned int ) ), + m_RuleDispatch( 0, 0, DefLessFunc( unsigned int ) ), + m_ResponseDispatch( 0, 0, DefLessFunc( unsigned int ) ), + m_ResponseGroupDispatch( 0, 0, DefLessFunc( unsigned int ) ) +{ + token[0] = 0; + m_bUnget = false; + m_bCustomManagable = false; +#ifdef MAPBASE + m_bInProspective = false; +#endif + + BuildDispatchTables(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CResponseSystem::~CResponseSystem() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CResponseSystem::GetCurrentScript( char *buf, size_t buflen ) +{ + Assert( buf ); + buf[ 0 ] = 0; + if ( m_ScriptStack.Count() <= 0 ) + return; + + if ( IEngineEmulator::Get()->GetFilesystem()->String( m_ScriptStack[ 0 ].name, buf, buflen ) ) + { + return; + } + buf[ 0 ] = 0; +} + +void CResponseSystem::PushScript( const char *scriptfile, unsigned char *buffer ) +{ + ScriptEntry e; + e.name = IEngineEmulator::Get()->GetFilesystem()->FindOrAddFileName( scriptfile ); + e.buffer = buffer; + e.currenttoken = (char *)e.buffer; + e.tokencount = 0; + m_ScriptStack.AddToHead( e ); +} + +void CResponseSystem::PopScript(void) +{ + Assert( m_ScriptStack.Count() >= 1 ); + if ( m_ScriptStack.Count() <= 0 ) + return; + + m_ScriptStack.Remove( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::Clear() +{ + m_Responses.RemoveAll(); + m_Criteria.RemoveAll(); +#ifdef MAPBASE + // Must purge to avoid issues with reloading the system + m_RulePartitions.PurgeAndDeleteElements(); +#else + m_RulePartitions.RemoveAll(); +#endif + m_Enumerations.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// found - +// Output : float +//----------------------------------------------------------------------------- +float CResponseSystem::LookupEnumeration( const char *name, bool& found ) +{ + int idx = m_Enumerations.Find( name ); + if ( idx == m_Enumerations.InvalidIndex() ) + { + found = false; + return 0.0f; + } + + + found = true; + return m_Enumerations[ idx ].value; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : matcher - +//----------------------------------------------------------------------------- +void CResponseSystem::ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ) +{ + if ( rawtoken[0] != '[' ) + { + Q_strncpy( token, rawtoken, bufsize ); + return; + } + + // Now lookup enumeration + bool found = false; + float f = LookupEnumeration( rawtoken, found ); + if ( !found ) + { + Q_strncpy( token, rawtoken, bufsize ); + ResponseWarning( "No such enumeration '%s'\n", token ); + return; + } + + Q_snprintf( token, bufsize, "%f", f ); +} + + +#ifndef MAPBASE // Already in mapbase_matchers_base +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 + +void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) +{ + const char *s = c->value; + if ( !s ) + { + matcher.valid = false; + return; + } + + const char *in = s; + + char token[ 128 ]; + char rawtoken[ 128 ]; + + token[ 0 ] = 0; + rawtoken[ 0 ] = 0; + + int n = 0; + + bool gt = false; + bool lt = false; + bool eq = false; + bool nt = false; +#ifdef MAPBASE + bool bit = false; +#endif + + bool done = false; + while ( !done ) + { + switch( *in ) + { + case '>': + { + gt = true; + Assert( !lt ); // Can't be both + } + break; + case '<': + { + lt = true; + Assert( !gt ); // Can't be both + } + break; + case '=': + { + eq = true; + } + break; + case ',': + case '\0': + { + rawtoken[ n ] = 0; + n = 0; + + // 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 ) + { + matcher.usemin = true; + matcher.minequals = eq; + matcher.minval = (float)atof( token ); + + matcher.isnumeric = true; + } + else if ( lt ) + { + matcher.usemax = true; + matcher.maxequals = eq; + matcher.maxval = (float)atof( token ); + + matcher.isnumeric = true; + } + else + { + if ( *in == ',' ) + { + // If there's a comma, this better have been a less than or a gt key + Assert( 0 ); + } + + matcher.notequal = nt; + + matcher.isnumeric = AppearsToBeANumber( token ); + } + + gt = lt = eq = nt = false; + + if ( !(*in) ) + { + done = true; + } + } + break; + case '!': + nt = true; + break; +#ifdef MAPBASE + case '~': + nt = true; + case '&': + bit = true; + break; +#endif + default: + rawtoken[ n++ ] = *in; + break; + } + + in++; + } + + matcher.SetToken( token ); + matcher.SetRaw( rawtoken ); + matcher.valid = true; +} + +bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose /*=false*/ ) +{ + if ( !m.valid ) + return false; + + float v = (float)atof( setValue ); + if ( setValue[0] == '[' ) + { + 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; + + if ( m.usemin ) + { + if ( m.minequals ) + { + if ( v < m.minval ) + return false; + } + else + { + if ( v <= m.minval ) + return false; + } + + ++minmaxcount; + } + + if ( m.usemax ) + { + if ( m.maxequals ) + { + if ( v > m.maxval ) + return false; + } + else + { + if ( v >= m.maxval ) + return false; + } + + ++minmaxcount; + } + + // Had one or both criteria and met them + if ( minmaxcount >= 1 ) + { + return true; + } + + if ( m.notequal ) + { + if ( m.isnumeric ) + { + if ( v == (float)atof( m.GetToken() ) ) + return false; + } + else + { +#ifdef MAPBASE + if ( Matcher_NamesMatch( m.GetToken(), setValue ) ) +#else + if ( !Q_stricmp( setValue, m.GetToken() ) ) +#endif + return false; + } + + return true; + } + + if ( m.isnumeric ) + { + // If the setValue is "", the NPC doesn't have the key at all, + // in which case we shouldn't match "0". + if ( !setValue || !setValue[0] ) + return false; + + return v == (float)atof( m.GetToken() ); + } + +#ifdef MAPBASE + return Matcher_NamesMatch( m.GetToken(), setValue ); +#else + return !Q_stricmp( setValue, m.GetToken() ) ? true : false; +#endif +} + +bool CResponseSystem::Compare( const char *setValue, Criteria *c, bool verbose /*= false*/ ) +{ + Assert( c ); + Assert( setValue ); + + bool bret = CompareUsingMatcher( setValue, c->matcher, verbose ); + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "'%20s' vs. '%20s' = ", setValue, c->value ); + + { + //DevMsg( "\n" ); + //m.Describe(); + } + } + return bret; +} + +float CResponseSystem::RecursiveScoreSubcriteriaAgainstRule( const CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ) +{ + float score = 0.0f; + int subcount = parent->subcriteria.Count(); + for ( int i = 0; i < subcount; i++ ) + { + int icriterion = parent->subcriteria[ i ]; + + bool excludesubrule = false; + if (verbose) + { + DevMsg( "\n" ); + } + score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, excludesubrule, verbose ); + } + + exclude = ( parent->required && score == 0.0f ) ? true : false; + + return score * parent->weight.GetFloat(); +} + +float CResponseSystem::RecursiveLookForCriteria( const CriteriaSet &criteriaSet, Criteria *pParent ) +{ + float flScore = 0.0f; + int nSubCount = pParent->subcriteria.Count(); + for ( int iSub = 0; iSub < nSubCount; ++iSub ) + { + int iCriteria = pParent->subcriteria[iSub]; + flScore += LookForCriteria( criteriaSet, iCriteria ); + } + + return flScore; +} + +float CResponseSystem::LookForCriteria( const CriteriaSet &criteriaSet, int iCriteria ) +{ + Criteria *pCriteria = &m_Criteria[iCriteria]; + if ( pCriteria->IsSubCriteriaType() ) + { + return RecursiveLookForCriteria( criteriaSet, pCriteria ); + } + + int iIndex = criteriaSet.FindCriterionIndex( pCriteria->nameSym ); + if ( iIndex == -1 ) + return 0.0f; + + Assert( criteriaSet.GetValue( iIndex ) ); + if ( Q_stricmp( criteriaSet.GetValue( iIndex ), pCriteria->value ) ) + return 0.0f; + + return 1.0f; +} + +float CResponseSystem::ScoreCriteriaAgainstRuleCriteria( const CriteriaSet& set, int icriterion, bool& exclude, bool verbose /*=false*/ ) +{ + Criteria *c = &m_Criteria[ icriterion ]; + + if ( c->IsSubCriteriaType() ) + { + return RecursiveScoreSubcriteriaAgainstRule( set, c, exclude, verbose ); + } + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " criterion '%25s':'%15s' ", m_Criteria.GetElementName( icriterion ), CriteriaSet::SymbolToStr(c->nameSym) ); + } + + exclude = false; + + float score = 0.0f; + + const char *actualValue = ""; + + /* + const char * RESTRICT critname = c->name; + CUtlSymbol sym(critname); + const char * nameDoubleCheck = sym.String(); + */ + int found = set.FindCriterionIndex( c->nameSym ); + if ( found != -1 ) + { + actualValue = set.GetValue( found ); + if ( !actualValue ) + { + Assert( 0 ); + return score; + } + } + + Assert( actualValue ); + + if ( Compare( actualValue, c, verbose ) ) + { + float w = set.GetWeight( found ); + score = w * c->weight.GetFloat(); + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "matched, weight %4.2f (s %4.2f x c %4.2f)", + score, w, c->weight.GetFloat() ); + } + } + else + { + if ( c->required ) + { + exclude = true; + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "failed (+exclude rule)" ); + } + } + else + { + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "failed" ); + } + } + } + + return score; +} + +float CResponseSystem::ScoreCriteriaAgainstRule( const CriteriaSet& set, ResponseRulePartition::tRuleDict &dict, int irule, bool verbose /*=false*/ ) +{ + Rule * RESTRICT rule = dict[ irule ]; + float score = 0.0f; + + bool bBeingWatched = false; + + // See if we're trying to debug this rule + const char *pszText = rr_debugrule.GetString(); + if ( pszText && pszText[0] && !Q_stricmp( pszText, dict.GetElementName( irule ) ) ) + { + bBeingWatched = true; + } + + if ( !rule->IsEnabled() ) + { + if ( bBeingWatched ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Rule is disabled.\n" ); + } + return 0.0f; + } + + if ( bBeingWatched ) + { + verbose = true; + } + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Scoring rule '%s' (%i)\n{\n", dict.GetElementName( irule ), irule+1 ); + } + + // Iterate set criteria + int count = rule->m_Criteria.Count(); + int i; + for ( i = 0; i < count; i++ ) + { + int icriterion = rule->m_Criteria[ i ]; + + bool exclude = false; + score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, exclude, verbose ); + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, ", score %4.2f\n", score ); + } + + if ( exclude ) + { + score = 0.0f; + break; + } + } + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "}\n" ); + } + + if ( rule->m_nForceWeight > 0 ) + { // this means override the cumulative weight of criteria and just force the rule's total score, + // assuming it matched at all. + return fsel( score - FLT_MIN, rule->m_nForceWeight, 0 ); + } + else + { + return score; +} +} + +void CResponseSystem::DebugPrint( int depth, const char *fmt, ... ) +{ + int indentchars = 3 * depth; + char *indent = (char *) stackalloc( indentchars + 1); + indent[ indentchars ] = 0; + while ( --indentchars >= 0 ) + { + indent[ indentchars ] = ' '; + } + + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start (argptr, fmt); + Q_vsnprintf (szText, sizeof( szText ), fmt, argptr); + va_end (argptr); + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "%s%s", indent, szText ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::ResetResponseGroups() +{ + int i; + int c = m_Responses.Count(); + for ( i = 0; i < c; i++ ) + { + m_Responses[ i ].Reset(); + } + +#ifdef MAPBASE + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + m_RulePartitions[ idx ].m_bEnabled = true; + } +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::DisableEmptyRules() +{ + if (rr_disableemptyrules.GetBool() == false) + return; + + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + Rule &rule = m_RulePartitions[ idx ]; + + // Set it as disabled in advance + rule.m_bEnabled = false; + + int c2 = rule.m_Responses.Count(); + for (int s = 0; s < c2; s++) + { + if (m_Responses[rule.m_Responses[s]].IsEnabled()) + { + // Re-enable it if there's any valid responses + rule.m_bEnabled = true; + break; + } + } + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Make certain responses unavailable by marking them as depleted +//----------------------------------------------------------------------------- +void CResponseSystem::FakeDepletes( ResponseGroup *g, IResponseFilter *pFilter ) +{ + m_FakedDepletes.RemoveAll(); + + // Fake depletion of unavailable choices + int c = g->group.Count(); + if ( pFilter && g->ShouldCheckRepeats() ) + { + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( r->depletioncount != g->GetDepletionCount() && !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + m_FakedDepletes.AddToTail( i ); + g->MarkResponseUsed( i ); + } + } + } + + // Fake depletion of choices that fail the odds check + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( RandomInt( 1, 100 ) > r->params.odds ) + { + m_FakedDepletes.AddToTail( i ); + g->MarkResponseUsed( i ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Restore responses that were faked as being depleted +//----------------------------------------------------------------------------- +void CResponseSystem::RevertFakedDepletes( ResponseGroup *g ) +{ + for ( int i = 0; i < m_FakedDepletes.Count(); i++ ) + { + g->group[ m_FakedDepletes[ i ] ].depletioncount = 0; + } + m_FakedDepletes.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *g - +// Output : int +//----------------------------------------------------------------------------- +int CResponseSystem::SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ) +{ + int c = g->group.Count(); + if ( !c ) + { + Assert( !"Expecting response group with >= 1 elements" ); + return -1; + } + + FakeDepletes( g, pFilter ); + + if ( !g->HasUndepletedChoices() ) + { + g->ResetDepletionCount(); + + FakeDepletes( g, pFilter ); + + if ( !g->HasUndepletedChoices() ) + return -1; + + // Disable the group if we looped through all the way + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); +#ifdef MAPBASE + DisableEmptyRules(); +#endif + return -1; + } + } + + bool checkrepeats = g->ShouldCheckRepeats(); + int depletioncount = g->GetDepletionCount(); + + float totalweight = 0.0f; + int slot = -1; + + if ( checkrepeats ) + { + int check= -1; + // Snag the first slot right away + if ( g->HasUndepletedFirst( check ) && check != -1 ) + { + slot = check; + } + + if ( slot == -1 && g->HasUndepletedLast( check ) && check != -1 ) + { + // If this is the only undepleted one, use it now + int i; + for ( i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( checkrepeats && + ( r->depletioncount == depletioncount ) ) + { + continue; + } + + if ( r->last ) + { + Assert( i == check ); + continue; + } + + // There's still another undepleted entry + break; + } + + // No more undepleted so use the r->last slot + if ( i >= c ) + { + slot = check; + } + } + } + + if ( slot == -1 ) + { + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( checkrepeats && + ( r->depletioncount == depletioncount ) ) + { + continue; + } + + // Always skip last entry here since we will deal with it above + if ( checkrepeats && r->last ) + continue; + + int prevSlot = slot; + + if ( !totalweight ) + { + slot = i; + } + + // Always assume very first slot will match + totalweight += r->weight.GetFloat(); + if ( !totalweight || IEngineEmulator::Get()->GetRandomStream()->RandomFloat(0,totalweight) < r->weight.GetFloat() ) + { + slot = i; + } + + if ( !checkrepeats && slot != prevSlot && pFilter && !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + slot = prevSlot; + totalweight -= r->weight.GetFloat(); + } + } + } + + if ( slot != -1 ) + { +#ifdef MAPBASE + // Don't mark responses as used in prospective mode + if (m_bInProspective == false) +#endif + g->MarkResponseUsed( slot ); + } + + // Revert fake depletion of unavailable choices + RevertFakedDepletes( g ); + + return slot; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : searchResult - +// depth - +// *name - +// verbose - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CResponseSystem::ResolveResponse( ResponseSearchResult& searchResult, int depth, const char *name, bool verbose /*= false*/, IResponseFilter *pFilter ) +{ + int responseIndex = m_Responses.Find( name ); + if ( responseIndex == m_Responses.InvalidIndex() ) + return false; + + ResponseGroup *g = &m_Responses[ responseIndex ]; + // Group has been disabled + if ( !g->IsEnabled() ) + return false; + + int c = g->group.Count(); + if ( !c ) + return false; + + int idx = 0; + + if ( g->IsSequential() ) + { + // See if next index is valid + int initialIndex = g->GetCurrentIndex(); + bool bFoundValid = false; + + do + { + idx = g->GetCurrentIndex(); + g->SetCurrentIndex( idx + 1 ); + if ( idx >= c ) + { + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); +#ifdef MAPBASE + DisableEmptyRules(); +#endif + return false; + } + idx = 0; + g->SetCurrentIndex( 0 ); + } + + if ( !pFilter || pFilter->IsValidResponse( g->group[idx].GetType(), g->group[idx].value ) ) + { + bFoundValid = true; + break; + } + + } while ( g->GetCurrentIndex() != initialIndex ); + + if ( !bFoundValid ) + return false; + } + else + { + idx = SelectWeightedResponseFromResponseGroup( g, pFilter ); + if ( idx < 0 ) + return false; + } + + if ( verbose ) + { + DebugPrint( depth, "%s\n", m_Responses.GetElementName( responseIndex ) ); + DebugPrint( depth, "{\n" ); + DescribeResponseGroup( g, idx, depth ); + } + + bool bret = true; + + ParserResponse *result = &g->group[ idx ]; + if ( result->type == RESPONSE_RESPONSE ) + { + // Recurse + bret = ResolveResponse( searchResult, depth + 1, result->value, verbose, pFilter ); + } + else + { + searchResult.action = result; + searchResult.group = g; + } + + if( verbose ) + { + DebugPrint( depth, "}\n" ); + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *group - +// selected - +// depth - +//----------------------------------------------------------------------------- +void CResponseSystem::DescribeResponseGroup( ResponseGroup *group, int selected, int depth ) +{ + int c = group->group.Count(); + + for ( int i = 0; i < c ; i++ ) + { + ParserResponse *r = &group->group[ i ]; + DebugPrint( depth + 1, "%s%20s : %40s %5.3f\n", + i == selected ? "-> " : " ", + CRR_Response::DescribeResponse( r->GetType() ), + r->value, + r->weight.GetFloat() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *rule - +// Output : CResponseSystem::Response +//----------------------------------------------------------------------------- +bool CResponseSystem::GetBestResponse( ResponseSearchResult& searchResult, Rule *rule, bool verbose /*=false*/, IResponseFilter *pFilter ) +{ + int c = rule->m_Responses.Count(); + if ( !c ) + return false; + + int index = IEngineEmulator::Get()->GetRandomStream()->RandomInt( 0, c - 1 ); + int groupIndex = rule->m_Responses[ index ]; + + ResponseGroup *g = &m_Responses[ groupIndex ]; + + // Group has been disabled + if ( !g->IsEnabled() ) + return false; + + int count = g->group.Count(); + if ( !count ) + return false; + + int responseIndex = 0; + + if ( g->IsSequential() ) + { + // See if next index is valid + int initialIndex = g->GetCurrentIndex(); + bool bFoundValid = false; + + do + { + responseIndex = g->GetCurrentIndex(); + g->SetCurrentIndex( responseIndex + 1 ); + if ( responseIndex >= count ) + { + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); +#ifdef MAPBASE + DisableEmptyRules(); +#endif + return false; + } + responseIndex = 0; + g->SetCurrentIndex( 0 ); + } + + if ( !pFilter || pFilter->IsValidResponse( g->group[responseIndex].GetType(), g->group[responseIndex].value ) ) + { + bFoundValid = true; + break; + } + + } while ( g->GetCurrentIndex() != initialIndex ); + + if ( !bFoundValid ) + return false; + } + else + { + responseIndex = SelectWeightedResponseFromResponseGroup( g, pFilter ); + if ( responseIndex < 0 ) + return false; + } + + + ParserResponse *r = &g->group[ responseIndex ]; + + int depth = 0; + + if ( verbose ) + { + DebugPrint( depth, "%s\n", m_Responses.GetElementName( groupIndex ) ); + DebugPrint( depth, "{\n" ); + + DescribeResponseGroup( g, responseIndex, depth ); + } + + bool bret = true; + + if ( r->type == RESPONSE_RESPONSE ) + { + bret = ResolveResponse( searchResult, depth + 1, r->value, verbose, pFilter ); + } + else + { + searchResult.action = r; + searchResult.group = g; + } + + if ( verbose ) + { + DebugPrint( depth, "}\n" ); + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// verbose - +// Output : int +// Warning: If you change this, be sure to also change +// ResponseSystemImplementationCLI::FindAllRulesMatchingCriteria(). +//----------------------------------------------------------------------------- +ResponseRulePartition::tIndex CResponseSystem::FindBestMatchingRule( const CriteriaSet& set, bool verbose, float &scoreOfBestMatchingRule ) +{ + CUtlVector< ResponseRulePartition::tIndex > bestrules(16,4); + float bestscore = 0.001f; + scoreOfBestMatchingRule = 0; + + CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > buckets( 0, 2 ); + m_RulePartitions.GetDictsForCriteria( &buckets, set ); + for ( int b = 0 ; b < buckets.Count() ; ++b ) + { + ResponseRulePartition::tRuleDict *prules = buckets[b]; + int c = prules->Count(); + int i; + for ( i = 0; i < c; i++ ) + { + float score = ScoreCriteriaAgainstRule( set, *prules, i, verbose ); + // Check equals so that we keep track of all matching rules + if ( score >= bestscore ) + { + // Reset bucket + if( score != bestscore ) + { + bestscore = score; + bestrules.RemoveAll(); + } + + // Add to bucket + bestrules.AddToTail( m_RulePartitions.IndexFromDictElem( prules, i ) ); + } + } + } + + int bestCount = bestrules.Count(); + if ( bestCount <= 0 ) + return m_RulePartitions.InvalidIdx(); + + scoreOfBestMatchingRule = bestscore ; + if ( bestCount == 1 ) + { + return bestrules[ 0 ] ; + } + else + { + // Randomly pick one of the tied matching rules + int idx = IEngineEmulator::Get()->GetRandomStream()->RandomInt( 0, bestCount - 1 ); + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Found %i matching rules, selecting slot %i\n", bestCount, idx ); + } + return bestrules[ idx ] ; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// Output : CRR_Response +//----------------------------------------------------------------------------- +bool CResponseSystem::FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter ) +{ + bool valid = false; + + int iDbgResponse = rr_debugresponses.GetInt(); + bool showRules = ( iDbgResponse >= 2 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ); + bool showResult = ( iDbgResponse >= 1 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ); + + // Look for match. verbose mode used to be at level 2, but disabled because the writers don't actually care for that info. + float scoreOfBestRule; + ResponseRulePartition::tIndex bestRule = FindBestMatchingRule( set, + ( iDbgResponse >= 3 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ), + scoreOfBestRule ); + + ResponseType_t responseType = RESPONSE_NONE; + ResponseParams rp; + + 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 ( m_RulePartitions.IsValid( bestRule ) ) + { + Rule * RESTRICT r = &m_RulePartitions[ bestRule ]; + + ResponseSearchResult result; + if ( GetBestResponse( result, r, showResult, pFilter ) ) + { + Q_strncpy( responseName, result.action->value, sizeof( responseName ) ); + responseType = result.action->GetType(); + rp = result.action->params; + rp.m_pFollowup = &result.action->m_followup; + } + + Q_strncpy( ruleName, m_RulePartitions.GetElementName( bestRule ), sizeof( ruleName ) ); + + // Disable the rule if it only allows for matching one time + if ( r->IsMatchOnce() ) + { + r->Disable(); + } + context = r->GetContext(); +#ifdef MAPBASE + contextflags = r->GetContextFlags(); + + // Sets the internal indices for the response to call back to later for prospective responses + // (NOTE: Performance not tested; Be wary of turning off the m_bInProspective check!) + if (m_bInProspective) + { + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + if (&m_Responses[i] == result.group) + { + ResponseGroup &group = m_Responses[i]; + for ( int j = 0; j < group.group.Count(); j++) + { + if (&group.group[j] == result.action) + { + response.SetInternalIndices( i, j ); + } + } + } + } + } +#else + bcontexttoworld = r->IsApplyContextToWorld(); +#endif + + response.SetMatchScore(scoreOfBestRule); + valid = true; + } + +#ifdef MAPBASE + response.Init( responseType, responseName, rp, ruleName, context, contextflags ); +#else + response.Init( responseType, responseName, rp, ruleName, context, bcontexttoworld ); +#endif + + if ( showResult ) + { + /* + // clipped -- chet doesn't really want this info + if ( valid ) + { + // Rescore the winner and dump to console + ScoreCriteriaAgainstRule( set, bestRule, true ); + } + */ + + + if ( valid || showRules ) + { + const char *pConceptFilter = rr_debugresponseconcept.GetString(); + // Describe the response, too + if ( V_strlen(pConceptFilter) > 0 && !rr_debugresponseconcept.GetBool() ) + { // filter for only one concept + if ( V_stricmp(pConceptFilter, set.GetValue(set.FindCriterionIndex("concept")) ) == 0 ) + { + response.Describe(&set); + } // else don't print + } + else + { + // maybe we need to filter *out* some concepts + if ( m_DebugExcludeList.IsValidIndex( m_DebugExcludeList.Head() ) ) + { + // we are excluding at least one concept + CRR_Concept test( set.GetValue(set.FindCriterionIndex("concept")) ); + if ( ! m_DebugExcludeList.IsValidIndex( m_DebugExcludeList.Find( test ) ) ) + { // if not found in exclude list, then print + response.Describe(&set); + } + } + else + { + // describe everything + response.Describe(&set); + } + } + } + } + + return valid; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::GetAllResponses( CUtlVector *pResponses ) +{ + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + ResponseGroup &group = m_Responses[i]; + + for ( int j = 0; j < group.group.Count(); j++) + { + ParserResponse &response = group.group[j]; + if ( response.type != RESPONSE_RESPONSE ) + { + /* + CRR_Response *pResponse = new CRR_Response; + pResponse->Init( response.GetType(), response.value, CriteriaSet(), response.params, NULL, NULL, false ); + pResponses->AddToTail(pResponse); + */ + pResponses->Element(pResponses->AddToTail()).Init( response.GetType(), response.value, response.params, NULL, NULL, false ); + } + } + } +} + +#ifdef MAPBASE +void CResponseSystem::MarkResponseAsUsed( short iGroup, short iWithinGroup ) +{ + if (m_Responses.Count() > (unsigned int)iGroup) + { + ResponseGroup &group = m_Responses[iGroup]; + if (group.group.Count() > (int)iWithinGroup) + { + group.MarkResponseUsed( iWithinGroup ); + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Marked response %s (%i) used\n", group.group[iWithinGroup].value, iWithinGroup ); + } + } +} +#endif + +void CResponseSystem::ParseInclude() +{ + char includefile[ 256 ]; + ParseToken(); + +#ifdef MAPBASE + char scriptfile[256]; + GetCurrentScript( scriptfile, sizeof( 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_strncat( includefile, "/", sizeof( includefile ) ); + Q_strncat( includefile, token, sizeof( includefile ) ); + } + 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 ( m_IncludedFiles.Find( includefile ) != NULL ) + { + return; + } + + MEM_ALLOC_CREDIT(); + + // Try and load it + CUtlBuffer buf; + if ( !IEngineEmulator::Get()->GetFilesystem()->ReadFile( includefile, "GAME", buf ) ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Unable to load #included script %s\n", includefile ); + return; + } + + LoadFromBuffer( includefile, (const char *)buf.PeekGet() ); +} + +void CResponseSystem::LoadFromBuffer( const char *scriptfile, const char *buffer ) +{ + COM_TimestampedLog( "CResponseSystem::LoadFromBuffer [%s] - Start", scriptfile ); + m_IncludedFiles.Allocate( scriptfile ); + PushScript( scriptfile, (unsigned char * )buffer ); + + if( rr_dumpresponses.GetBool() ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM,"Reading: %s\n", scriptfile ); + } + + while ( 1 ) + { + ParseToken(); + if ( !token[0] ) + { + break; + } + + unsigned int hash = RR_HASH( token ); + bool bSuccess = Dispatch( token, hash, m_FileDispatch ); + if ( !bSuccess ) + { + int byteoffset = m_ScriptStack[ 0 ].currenttoken - (const char *)m_ScriptStack[ 0 ].buffer; + + Error( "CResponseSystem::LoadFromBuffer: Unknown entry type '%s', expecting 'response', 'criterion', 'enumeration' or 'rules' in file %s(offset:%i)\n", + token, scriptfile, byteoffset ); + break; + } + } + + if ( m_ScriptStack.Count() == 1 ) + { + char cur[ 256 ]; + GetCurrentScript( cur, sizeof( cur ) ); + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "CResponseSystem: %s (%i rules, %i criteria, and %i responses)\n", + cur, m_RulePartitions.Count(), m_Criteria.Count(), m_Responses.Count() ); + + if( rr_dumpresponses.GetBool() ) + { + DumpRules(); + } + } + + PopScript(); + COM_TimestampedLog( "CResponseSystem::LoadFromBuffer [%s] - Finish", scriptfile ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::LoadRuleSet( const char *basescript ) +{ + float flStart = Plat_FloatTime(); + int length = 0; + unsigned char *buffer = (unsigned char *)IEngineEmulator::Get()->LoadFileForMe( basescript, &length ); + if ( length <= 0 || !buffer ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "CResponseSystem: failed to load %s\n", basescript ); + return; + } + + m_IncludedFiles.FreeAll(); + LoadFromBuffer( basescript, (const char *)buffer ); + + IEngineEmulator::Get()->FreeFile( buffer ); + + Assert( m_ScriptStack.Count() == 0 ); + float flEnd = Plat_FloatTime(); + COM_TimestampedLog( "CResponseSystem::LoadRuleSet took %f msec", 1000.0f * ( flEnd - flStart ) ); +} + +inline ResponseType_t ComputeResponseType( const char *s ) +{ + switch ( s[ 0 ] ) + { + default: + break; + case 's': + switch ( s[ 1 ] ) + { + default: + break; + case 'c': + return RESPONSE_SCENE; + case 'e': + return RESPONSE_SENTENCE; + case 'p': + return RESPONSE_SPEAK; + } + break; + case 'r': + return RESPONSE_RESPONSE; + case 'p': + return RESPONSE_PRINT; + case 'e': + return RESPONSE_ENTITYIO; +#ifdef MAPBASE + case 'v': + if (*(s + 7) == '_') + return RESPONSE_VSCRIPT_FILE; + else + return RESPONSE_VSCRIPT; +#endif + } + + return RESPONSE_NONE; +} + +void CResponseSystem::ParseResponse_Weight( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + newResponse.weight.SetFloat( (float)atof( token ) ); +} + +void CResponseSystem::ParseResponse_PreDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYBEFORESPEAK; + rp->predelay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_NoDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = 0; + rp->delay.range = 0; +} + +void CResponseSystem::ParseResponse_DefaultDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = AIS_DEF_MIN_DELAY; + rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); +} + +void CResponseSystem::ParseResponse_Delay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_SpeakOnce( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_SPEAKONCE; +} + +void CResponseSystem::ParseResponse_NoScene( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE; +} + +void CResponseSystem::ParseResponse_StopOnNonIdle( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; +} + +void CResponseSystem::ParseResponse_Odds( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_ODDS; + rp->odds = clamp( atoi( token ), 0, 100 ); +} + +void CResponseSystem::ParseResponse_RespeakDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY; + rp->respeakdelay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_WeaponDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_WEAPONDELAY; + rp->weapondelay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_Soundlevel( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL; + rp->soundlevel = (soundlevel_t)TextToSoundLevel( token ); +} + +void CResponseSystem::ParseResponse_DisplayFirst( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + newResponse.first = true; + group.m_bHasFirst = true; +} + +void CResponseSystem::ParseResponse_DisplayLast( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + newResponse.last = true; + group.m_bHasLast= true; +} + +void CResponseSystem::ParseResponse_Fire( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + // get target name + bool bSuc = ParseToken(); + if (!bSuc) + { + ResponseWarning( "FIRE token in response needs exactly three parameters." ); + return; + } + newResponse.m_followup.followup_entityiotarget = ResponseCopyString(token); + + bSuc = ParseToken(); + if (!bSuc) + { + ResponseWarning( "FIRE token in response needs exactly three parameters." ); + return; + } + newResponse.m_followup.followup_entityioinput = ResponseCopyString(token); + + bSuc = ParseToken(); + if (!bSuc) + { + ResponseWarning( "FIRE token in response needs exactly three parameters." ); + return; + } + newResponse.m_followup.followup_entityiodelay = atof( token ); + /* + m_followup.followup_entityioinput = ResponseCopyString(src.m_followup.followup_entityioinput); + m_followup.followup_entityiotarget = ResponseCopyString(src.m_followup.followup_entityiotarget); + */ +} + +void CResponseSystem::ParseResponse_Then( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + // eg, "subject TALK_ANSWER saidunplant:1 3" + bool bSuc = ParseToken(); + if (!bSuc) + { + AssertMsg(false, "THEN token in response lacked any further info.\n"); + ResponseWarning( "THEN token in response lacked any further info.\n" ); + return; + } + + newResponse.m_followup.followup_target = ResponseCopyString(token); + + bSuc = ParseToken(); // get another token + if (!bSuc) + { + AssertMsg1(false, "THEN token in response had a target '%s', but lacked any further info.\n", newResponse.m_followup.followup_target ); + ResponseWarning( "THEN token in response had a target '%s', but lacked any further info.\n", newResponse.m_followup.followup_target ); + return; + } + + newResponse.m_followup.followup_concept = ResponseCopyString( token ); + + + // Okay, this is totally asinine. + // Because the ParseToken() function will split foo:bar into three tokens + // (which is reasonable), but we have no safe way to parse the file otherwise + // because it's all behind an engine interface, it's necessary to parse all + // the tokens to the end of the line and catenate them, except for the last one + // which is the delay. That's crap. + bSuc = ParseToken(); + if (!bSuc) + { + AssertMsg(false, "THEN token in response lacked contexts.\n"); + ResponseWarning( "THEN token in response lacked contexts.\n" ); + return; + } + + // okay, as long as there is at least one more token, catenate the ones we + // see onto a temporary buffer. When we're down to the last token, that is + // the delay. + char buf[4096]; + buf[0] = '\0'; + while ( TokenWaiting() ) + { + Q_strncat( buf, token, 4096 ); + bSuc = ParseToken(); + AssertMsg(bSuc, "Token parsing mysteriously failed."); + } + + // down here, token is the last token, and buf is everything up to there. + newResponse.m_followup.followup_contexts = ResponseCopyString( buf ); + + newResponse.m_followup.followup_delay = atof( token ); +} + +void CResponseSystem::ParseOneResponse( const char *responseGroupName, ResponseGroup& group, ResponseParams *defaultParams ) +{ + ParserResponse &newResponse = group.group[ group.group.AddToTail() ]; + newResponse.weight.SetFloat( 1.0f ); + // inherit from group if appropriate + if (defaultParams) + { + newResponse.params = *defaultParams; + } + + ResponseParams *rp = &newResponse.params; + + newResponse.type = ComputeResponseType( token ); + if ( RESPONSE_NONE == newResponse.type ) +{ + ResponseWarning( "response entry '%s' with unknown response type '%s'\n", responseGroupName, token ); + return; +} + +#ifdef MAPBASE + // HACKHACK: Some response system usage in the pre-Alien Swarm system require response names to preserve casing or even have escaped quotes. + ParseTokenIntact(); +#else + ParseToken(); +#endif + newResponse.value = ResponseCopyString( token ); + + while ( TokenWaiting() ) + { + ParseToken(); + + unsigned int hash = RR_HASH( token ); + if ( DispatchParseResponse( token, hash, m_ResponseDispatch, newResponse, group, rp ) ) + { + continue; + } + + ResponseWarning( "response entry '%s' with unknown command '%s'\n", responseGroupName, token ); + } + +} + +void CResponseSystem::ParseResponseGroup_Start( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + if ( !Q_stricmp( token, "permitrepeats" ) ) + { + newGroup.m_bDepleteBeforeRepeat = false; + continue; + } + else if ( !Q_stricmp( token, "sequential" ) ) + { + newGroup.SetSequential( true ); + continue; + } + else if ( !Q_stricmp( token, "norepeat" ) ) + { + newGroup.SetNoRepeat( true ); + continue; + } + + ParseOneResponse( responseGroupName, newGroup, &groupResponseParams ); + } + } + +void CResponseSystem::ParseResponseGroup_PreDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYBEFORESPEAK; + groupResponseParams.predelay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_NoDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + groupResponseParams.delay.start = 0; + groupResponseParams.delay.range = 0; + } + +void CResponseSystem::ParseResponseGroup_DefaultDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + groupResponseParams.delay.start = AIS_DEF_MIN_DELAY; + groupResponseParams.delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); + } + +void CResponseSystem::ParseResponseGroup_Delay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + groupResponseParams.delay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_SpeakOnce( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_SPEAKONCE; + } + +void CResponseSystem::ParseResponseGroup_NoScene( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_DONT_USE_SCENE; + } + +void CResponseSystem::ParseResponseGroup_StopOnNonIdle( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; + } + +void CResponseSystem::ParseResponseGroup_Odds( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_ODDS; + groupResponseParams.odds = clamp( atoi( token ), 0, 100 ); + } + +void CResponseSystem::ParseResponseGroup_RespeakDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_RESPEAKDELAY; + groupResponseParams.respeakdelay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_WeaponDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_WEAPONDELAY; + groupResponseParams.weapondelay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_Soundlevel( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_SOUNDLEVEL; + groupResponseParams.soundlevel = (soundlevel_t)TextToSoundLevel( token ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::ParseResponse( void ) +{ + AI_ResponseParams groupResponseParams; // default response parameters inherited from single line format for group + + // Should have groupname at start + ParseToken(); + char responseGroupName[ 128 ]; + Q_strncpy( responseGroupName, token, sizeof( responseGroupName ) ); + + int slot = m_Responses.Insert( responseGroupName ); + ResponseGroup &newGroup = m_Responses[ slot ]; + + while ( 1 ) + { +#ifdef MAPBASE + if ( !ParseToken() || !Q_stricmp( token, "}" ) ) + { + break; + } +#else + ParseToken(); +#endif + + unsigned int hash = RR_HASH( token ); + + // Oops, part of next definition + if( IsRootCommand( hash ) ) + { + Unget(); + break; + } + + if ( DispatchParseResponseGroup( token, hash, m_ResponseGroupDispatch, responseGroupName, newGroup, groupResponseParams ) ) + { + continue; + } + + ParseOneResponse( responseGroupName, newGroup, &groupResponseParams ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criterion - +//----------------------------------------------------------------------------- +int CResponseSystem::ParseOneCriterion( const char *criterionName ) +{ + char key[ 128 ]; + char value[ 128 ]; + + Criteria *pNewCriterion = NULL; + + int idx; +#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] = Criteria(); + m_Criteria.SetElementName(existing, criterionName); + idx = existing; + pNewCriterion = &m_Criteria[ idx ]; + } +#else + if ( m_Criteria.Find( criterionName ) != m_Criteria.InvalidIndex() ) + { + static Criteria dummy; + pNewCriterion = &dummy; + + ResponseWarning( "Multiple definitions for criteria '%s' [%d]\n", criterionName, RR_HASH( criterionName ) ); + idx = m_Criteria.InvalidIndex(); + } +#endif + else + { + idx = m_Criteria.Insert( criterionName ); + pNewCriterion = &m_Criteria[ idx ]; + } + + bool gotbody = false; + + while ( TokenWaiting() || !gotbody ) + { +#ifdef MAPBASE + if ( !ParseToken() ) + { + break; + } +#else + ParseToken(); +#endif + + // Oops, part of next definition + if( IsRootCommand() ) + { + Unget(); + break; + } + + if ( !Q_stricmp( token, "{" ) ) + { + gotbody = true; + + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + // Look up subcriteria index + int idx = m_Criteria.Find( token ); + if ( idx != m_Criteria.InvalidIndex() ) + { + pNewCriterion->subcriteria.AddToTail( idx ); + } + else + { + ResponseWarning( "Skipping unrecongized subcriterion '%s' in '%s'\n", token, criterionName ); + } + } + continue; + } + else if ( !Q_stricmp( token, "required" ) ) + { + pNewCriterion->required = true; + } + else if ( !Q_stricmp( token, "weight" ) ) + { + ParseToken(); + pNewCriterion->weight.SetFloat( (float)atof( token ) ); + } + else + { + Assert( pNewCriterion->subcriteria.Count() == 0 ); + + // Assume it's the math info for a non-subcriteria resposne + Q_strncpy( key, token, sizeof( key ) ); + ParseToken(); + Q_strncpy( value, token, sizeof( value ) ); + + V_strlower( key ); + pNewCriterion->nameSym = CriteriaSet::ComputeCriteriaSymbol( key ); + pNewCriterion->value = ResponseCopyString( value ); + + gotbody = true; + } + } + + if ( !pNewCriterion->IsSubCriteriaType() ) + { + ComputeMatcher( pNewCriterion, pNewCriterion->matcher ); + } + + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseCriterion( void ) +{ + // Should have groupname at start + char criterionName[ 128 ]; + ParseToken(); + Q_strncpy( criterionName, token, sizeof( criterionName ) ); + + ParseOneCriterion( criterionName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseEnumeration( void ) +{ + char enumerationName[ 128 ]; + ParseToken(); + Q_strncpy( enumerationName, token, sizeof( enumerationName ) ); + + ParseToken(); + if ( Q_stricmp( token, "{" ) ) + { + ResponseWarning( "Expecting '{' in enumeration '%s', got '%s'\n", enumerationName, token ); + return; + } + + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + if ( Q_strlen( token ) <= 0 ) + { + ResponseWarning( "Expecting more tokens in enumeration '%s'\n", enumerationName ); + break; + } + + char key[ 128 ]; + + Q_strncpy( key, token, sizeof( key ) ); + ParseToken(); + float value = (float)atof( token ); + + char sz[ 128 ]; + Q_snprintf( sz, sizeof( sz ), "[%s::%s]", enumerationName, key ); + Q_strlower( sz ); + + Enumeration newEnum; + newEnum.value = value; + + if ( m_Enumerations.Find( sz ) == m_Enumerations.InvalidIndex() ) + { + m_Enumerations.Insert( sz, newEnum ); + } + /* + else + { + ResponseWarning( "Ignoring duplication enumeration '%s'\n", sz ); + } + */ + } +} + +void CResponseSystem::ParseRule_MatchOnce( Rule &newRule ) + { + newRule.m_bMatchOnce = true; + } + +#ifdef MAPBASE +void CResponseSystem::ParseRule_ApplyContextToWorld( Rule &newRule ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_WORLD; + } + +void CResponseSystem::ParseRule_ApplyContextToSquad( Rule &newRule ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_SQUAD; + } + +void CResponseSystem::ParseRule_ApplyContextToEnemy( Rule &newRule ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_ENEMY; + } +#else +void CResponseSystem::ParseRule_ApplyContextToWorld( Rule &newRule ) + { + newRule.m_bApplyContextToWorld = true; + } +#endif + +void CResponseSystem::ParseRule_ApplyContext( Rule &newRule ) + { + ParseToken(); + if ( newRule.GetContext() == NULL ) + { + newRule.SetContext( token ); + } + else + { + CFmtStrN<1024> newContext( "%s,%s", newRule.GetContext(), token ); + newRule.SetContext( newContext ); + } + } + +void CResponseSystem::ParseRule_Response( Rule &newRule ) + { + // Read them until we run out. + while ( TokenWaiting() ) + { + ParseToken(); + int idx = m_Responses.Find( token ); + if ( idx != m_Responses.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + newRule.m_Responses.AddToTail( idx ); + } + else + { + m_bParseRuleValid = false; + ResponseWarning( "No such response '%s' for rule '%s'\n", token, m_pParseRuleName ); + } + } +} + +/* +void CResponseSystem::ParseRule_ForceWeight( Rule &newRule ) +{ + ParseToken(); + if ( token[0] == 0 ) + { + // no token followed forceweight? + ResponseWarning( "Forceweight token in rule '%s' did not specify a numerical weight! Ignoring.\n", m_pParseRuleName ); + } + else + { + newRule.m_nForceWeight = atoi(token); + if ( newRule.m_nForceWeight == 0 ) + { + ResponseWarning( "Rule '%s' had forceweight '%s', which doesn't work out to a nonzero number. Ignoring.\n", + m_pParseRuleName, token ); + } + } + } +*/ + +void CResponseSystem::ParseRule_Criteria( Rule &newRule ) + { + // Read them until we run out. + while ( TokenWaiting() ) + { + ParseToken(); + + int idx = m_Criteria.Find( token ); + if ( idx != m_Criteria.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + newRule.m_Criteria.AddToTail( idx ); + } + else + { + m_bParseRuleValid = false; + ResponseWarning( "No such criterion '%s' for rule '%s'\n", token, m_pParseRuleName ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseRule( void ) +{ + static int instancedCriteria = 0; + + char ruleName[ 128 ]; + ParseToken(); + Q_strncpy( ruleName, token, sizeof( ruleName ) ); + + ParseToken(); + if ( Q_stricmp( token, "{" ) ) + { + ResponseWarning( "Expecting '{' in rule '%s', got '%s'\n", ruleName, token ); + return; + } + + // entries are "criteria", "response" or an in-line criteria to instance + Rule *newRule = new Rule; + + char sz[ 128 ]; + + m_bParseRuleValid = true; + m_pParseRuleName = ruleName; + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + { + break; + } + + if ( Q_strlen( token ) <= 0 ) + { + ResponseWarning( "Expecting more tokens in rule '%s'\n", ruleName ); + break; + } + + unsigned int hash = RR_HASH( token ); + if ( DispatchParseRule( token, hash, m_RuleDispatch, *newRule ) ) + continue; + + // It's an inline criteria, generate a name and parse it in + Q_snprintf( sz, sizeof( sz ), "[%s%03i]", ruleName, ++instancedCriteria ); + Unget(); + int idx = ParseOneCriterion( sz ); + if ( idx != m_Criteria.InvalidIndex() ) + { + newRule->m_Criteria.AddToTail( idx ); + } + } + + if ( m_bParseRuleValid ) + { + m_RulePartitions.GetDictForRule( this, newRule ).Insert( ruleName, newRule ); + } + else + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Discarded rule %s\n", ruleName ); + delete newRule; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CResponseSystem::GetCurrentToken() const +{ + if ( m_ScriptStack.Count() <= 0 ) + return -1; + + return m_ScriptStack[ 0 ].tokencount; +} + + +void CResponseSystem::ResponseWarning( const char *fmt, ... ) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, fmt); + Q_vsnprintf(string, sizeof(string), fmt,argptr); + va_end (argptr); + + char cur[ 256 ]; + GetCurrentScript( cur, sizeof( cur ) ); + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "%s(token %i) : %s", cur, GetCurrentToken(), string ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyCriteriaFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ) +{ + // Add criteria from this rule to global list in custom response system. + int nCriteriaCount = pSrcRule->m_Criteria.Count(); + for ( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria ) + { + int iSrcIndex = pSrcRule->m_Criteria[iCriteria]; + Criteria *pSrcCriteria = &m_Criteria[iSrcIndex]; + if ( pSrcCriteria ) + { + int iIndex = pCustomSystem->m_Criteria.Find( m_Criteria.GetElementName( iSrcIndex ) ); + if ( iIndex != pCustomSystem->m_Criteria.InvalidIndex() ) + { + pDstRule->m_Criteria.AddToTail( iIndex ); + continue; + } + + // Add the criteria. + Criteria dstCriteria; + + dstCriteria.nameSym = pSrcCriteria->nameSym ; + dstCriteria.value = ResponseCopyString( pSrcCriteria->value ); + dstCriteria.weight = pSrcCriteria->weight; + dstCriteria.required = pSrcCriteria->required; + dstCriteria.matcher = pSrcCriteria->matcher; + + int nSubCriteriaCount = pSrcCriteria->subcriteria.Count(); + for ( int iSubCriteria = 0; iSubCriteria < nSubCriteriaCount; ++iSubCriteria ) + { + int iSrcSubIndex = pSrcCriteria->subcriteria[iSubCriteria]; + Criteria *pSrcSubCriteria = &m_Criteria[iSrcSubIndex]; + if ( pSrcCriteria ) + { + int iSubIndex = pCustomSystem->m_Criteria.Find( pSrcSubCriteria->value ); + if ( iSubIndex != pCustomSystem->m_Criteria.InvalidIndex() ) + continue; + + // Add the criteria. + Criteria dstSubCriteria; + + dstSubCriteria.nameSym = pSrcSubCriteria->nameSym ; + dstSubCriteria.value = ResponseCopyString( pSrcSubCriteria->value ); + dstSubCriteria.weight = pSrcSubCriteria->weight; + dstSubCriteria.required = pSrcSubCriteria->required; + dstSubCriteria.matcher = pSrcSubCriteria->matcher; + + int iSubInsertIndex = pCustomSystem->m_Criteria.Insert( pSrcSubCriteria->value, dstSubCriteria ); + dstCriteria.subcriteria.AddToTail( iSubInsertIndex ); + } + } + + int iInsertIndex = pCustomSystem->m_Criteria.Insert( m_Criteria.GetElementName( iSrcIndex ), dstCriteria ); + pDstRule->m_Criteria.AddToTail( iInsertIndex ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyResponsesFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ) +{ + // Add responses from this rule to global list in custom response system. + int nResponseGroupCount = pSrcRule->m_Responses.Count(); + for ( int iResponseGroup = 0; iResponseGroup < nResponseGroupCount; ++iResponseGroup ) + { + int iSrcResponseGroup = pSrcRule->m_Responses[iResponseGroup]; + ResponseGroup *pSrcResponseGroup = &m_Responses[iSrcResponseGroup]; + if ( pSrcResponseGroup ) + { + // Add response group. + ResponseGroup dstResponseGroup; + + dstResponseGroup.m_bDepleteBeforeRepeat = pSrcResponseGroup->m_bDepleteBeforeRepeat; + dstResponseGroup.m_nDepletionCount = pSrcResponseGroup->m_nDepletionCount; + dstResponseGroup.m_bHasFirst = pSrcResponseGroup->m_bHasFirst; + dstResponseGroup.m_bHasLast = pSrcResponseGroup->m_bHasLast; + dstResponseGroup.m_bSequential = pSrcResponseGroup->m_bSequential; + dstResponseGroup.m_bNoRepeat = pSrcResponseGroup->m_bNoRepeat; + dstResponseGroup.m_bEnabled = pSrcResponseGroup->m_bEnabled; + dstResponseGroup.m_nCurrentIndex = pSrcResponseGroup->m_nCurrentIndex; + + int nSrcResponseCount = pSrcResponseGroup->group.Count(); + for ( int iResponse = 0; iResponse < nSrcResponseCount; ++iResponse ) + { + ParserResponse *pSrcResponse = &pSrcResponseGroup->group[iResponse]; + if ( pSrcResponse ) + { + // Add Response + ParserResponse dstResponse; + + dstResponse.weight = pSrcResponse->weight; + dstResponse.type = pSrcResponse->type; + dstResponse.value = ResponseCopyString( pSrcResponse->value ); + dstResponse.depletioncount = pSrcResponse->depletioncount; + dstResponse.first = pSrcResponse->first; + dstResponse.last = pSrcResponse->last; + + dstResponseGroup.group.AddToTail( dstResponse ); + } + } + + int iInsertIndex = pCustomSystem->m_Responses.Insert( m_Responses.GetElementName( iSrcResponseGroup ), dstResponseGroup ); + pDstRule->m_Responses.AddToTail( iInsertIndex ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyEnumerationsFrom( CResponseSystem *pCustomSystem ) +{ + int nEnumerationCount = m_Enumerations.Count(); + for ( int iEnumeration = 0; iEnumeration < nEnumerationCount; ++iEnumeration ) + { + Enumeration *pSrcEnumeration = &m_Enumerations[iEnumeration]; + if ( pSrcEnumeration ) + { + Enumeration dstEnumeration; + dstEnumeration.value = pSrcEnumeration->value; + pCustomSystem->m_Enumerations.Insert( m_Enumerations.GetElementName( iEnumeration ), dstEnumeration ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyRuleFrom( Rule *pSrcRule, ResponseRulePartition::tIndex iRule, CResponseSystem *pCustomSystem ) +{ + // Verify data. + Assert( pSrcRule ); + Assert( pCustomSystem ); + if ( !pSrcRule || !pCustomSystem ) + return; + + // New rule + Rule *dstRule = new Rule; + + 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 ); + + // Copy off responses. + CopyResponsesFrom( pSrcRule, dstRule, pCustomSystem ); + + // Copy off enumerations - Don't think we use these. + // CopyEnumerationsFrom( pCustomSystem ); + + // Add rule. + pCustomSystem->m_RulePartitions.GetDictForRule( this, dstRule ).Insert( m_RulePartitions.GetElementName( iRule ), dstRule ); +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::DumpRules() +{ + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%s\n", m_RulePartitions.GetElementName( idx ) ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::DumpDictionary( const char *pszName ) +{ + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\nDictionary: %s\n", pszName ); + + // int nRuleCount = m_Rules.Count(); + // for ( int iRule = 0; iRule < nRuleCount; ++iRule ) + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Rule %d/%d: %s\n", m_RulePartitions.BucketFromIdx( idx ), m_RulePartitions.PartFromIdx( idx ), m_RulePartitions.GetElementName( idx ) ); + + Rule *pRule = &m_RulePartitions[idx]; + + int nCriteriaCount = pRule->m_Criteria.Count(); + for( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria ) + { + int iRuleCriteria = pRule->m_Criteria[iCriteria]; + Criteria *pCriteria = &m_Criteria[iRuleCriteria]; + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Criteria %d: %s %s\n", iCriteria, CriteriaSet::SymbolToStr( pCriteria->nameSym ), pCriteria->value ); + } + + int nResponseGroupCount = pRule->m_Responses.Count(); + for ( int iResponseGroup = 0; iResponseGroup < nResponseGroupCount; ++iResponseGroup ) + { + int iRuleResponse = pRule->m_Responses[iResponseGroup]; + ResponseGroup *pResponseGroup = &m_Responses[iRuleResponse]; + + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " ResponseGroup %d: %s\n", iResponseGroup, m_Responses.GetElementName( iRuleResponse ) ); + + int nResponseCount = pResponseGroup->group.Count(); + for ( int iResponse = 0; iResponse < nResponseCount; ++iResponse ) + { + ParserResponse *pResponse = &pResponseGroup->group[iResponse]; + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Response %d: %s\n", iResponse, pResponse->value ); + } + } + } +} + +void CResponseSystem::BuildDispatchTables() +{ + m_RootCommandHashes.Insert( RR_HASH( "#include" ) ); + m_RootCommandHashes.Insert( RR_HASH( "response" ) ); + m_RootCommandHashes.Insert( RR_HASH( "enumeration" ) ); + m_RootCommandHashes.Insert( RR_HASH( "criterion" ) ); + m_RootCommandHashes.Insert( RR_HASH( "criteria" ) ); + m_RootCommandHashes.Insert( RR_HASH( "rule" ) ); + + m_FileDispatch.Insert( RR_HASH( "#include" ), &CResponseSystem::ParseInclude ); + m_FileDispatch.Insert( RR_HASH( "response" ), &CResponseSystem::ParseResponse ); + m_FileDispatch.Insert( RR_HASH( "criterion" ), &CResponseSystem::ParseCriterion ); + m_FileDispatch.Insert( RR_HASH( "criteria" ), &CResponseSystem::ParseCriterion ); + m_FileDispatch.Insert( RR_HASH( "rule" ), &CResponseSystem::ParseRule ); + m_FileDispatch.Insert( RR_HASH( "enumeration" ), &CResponseSystem::ParseEnumeration ); + + m_RuleDispatch.Insert( RR_HASH( "matchonce" ), &CResponseSystem::ParseRule_MatchOnce ); + m_RuleDispatch.Insert( RR_HASH( "applycontexttoworld" ), &CResponseSystem::ParseRule_ApplyContextToWorld ); +#ifdef MAPBASE + m_RuleDispatch.Insert( RR_HASH( "applycontexttosquad" ), &CResponseSystem::ParseRule_ApplyContextToSquad ); + m_RuleDispatch.Insert( RR_HASH( "applycontexttoenemy" ), &CResponseSystem::ParseRule_ApplyContextToEnemy ); +#endif + m_RuleDispatch.Insert( RR_HASH( "applycontext" ), &CResponseSystem::ParseRule_ApplyContext ); + m_RuleDispatch.Insert( RR_HASH( "response" ), &CResponseSystem::ParseRule_Response ); +// m_RuleDispatch.Insert( RR_HASH( "forceweight" ), &CResponseSystem::ParseRule_ForceWeight ); + m_RuleDispatch.Insert( RR_HASH( "criteria" ), &CResponseSystem::ParseRule_Criteria ); + m_RuleDispatch.Insert( RR_HASH( "criterion" ), &CResponseSystem::ParseRule_Criteria ); + + + m_ResponseDispatch.Insert( RR_HASH( "weight" ), &CResponseSystem::ParseResponse_Weight ); + m_ResponseDispatch.Insert( RR_HASH( "predelay" ), &CResponseSystem::ParseResponse_PreDelay ); + m_ResponseDispatch.Insert( RR_HASH( "nodelay" ), &CResponseSystem::ParseResponse_NoDelay ); + m_ResponseDispatch.Insert( RR_HASH( "defaultdelay" ), &CResponseSystem::ParseResponse_DefaultDelay ); + m_ResponseDispatch.Insert( RR_HASH( "delay" ), &CResponseSystem::ParseResponse_Delay ); + m_ResponseDispatch.Insert( RR_HASH( "speakonce" ), &CResponseSystem::ParseResponse_SpeakOnce ); + m_ResponseDispatch.Insert( RR_HASH( "noscene" ), &CResponseSystem::ParseResponse_NoScene ); + m_ResponseDispatch.Insert( RR_HASH( "stop_on_nonidle" ), &CResponseSystem::ParseResponse_StopOnNonIdle ); + m_ResponseDispatch.Insert( RR_HASH( "odds" ), &CResponseSystem::ParseResponse_Odds ); + m_ResponseDispatch.Insert( RR_HASH( "respeakdelay" ), &CResponseSystem::ParseResponse_RespeakDelay ); + m_ResponseDispatch.Insert( RR_HASH( "weapondelay" ), &CResponseSystem::ParseResponse_WeaponDelay ); + m_ResponseDispatch.Insert( RR_HASH( "soundlevel" ), &CResponseSystem::ParseResponse_Soundlevel ); + m_ResponseDispatch.Insert( RR_HASH( "displayfirst" ), &CResponseSystem::ParseResponse_DisplayFirst ); + m_ResponseDispatch.Insert( RR_HASH( "displaylast" ), &CResponseSystem::ParseResponse_DisplayLast ); + m_ResponseDispatch.Insert( RR_HASH( "fire" ), &CResponseSystem::ParseResponse_Fire ); + m_ResponseDispatch.Insert( RR_HASH( "then" ), &CResponseSystem::ParseResponse_Then ); + + m_ResponseGroupDispatch.Insert( RR_HASH( "{" ), &CResponseSystem::ParseResponseGroup_Start ); + m_ResponseGroupDispatch.Insert( RR_HASH( "predelay" ), &CResponseSystem::ParseResponseGroup_PreDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "nodelay" ), &CResponseSystem::ParseResponseGroup_NoDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "defaultdelay" ), &CResponseSystem::ParseResponseGroup_DefaultDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "delay" ), &CResponseSystem::ParseResponseGroup_Delay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "speakonce" ), &CResponseSystem::ParseResponseGroup_SpeakOnce ); + m_ResponseGroupDispatch.Insert( RR_HASH( "noscene" ), &CResponseSystem::ParseResponseGroup_NoScene ); + m_ResponseGroupDispatch.Insert( RR_HASH( "stop_on_nonidle" ), &CResponseSystem::ParseResponseGroup_StopOnNonIdle ); + m_ResponseGroupDispatch.Insert( RR_HASH( "odds" ), &CResponseSystem::ParseResponseGroup_Odds ); + m_ResponseGroupDispatch.Insert( RR_HASH( "respeakdelay" ), &CResponseSystem::ParseResponseGroup_RespeakDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "weapondelay" ), &CResponseSystem::ParseResponseGroup_WeaponDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "soundlevel" ), &CResponseSystem::ParseResponseGroup_Soundlevel ); +} + +bool CResponseSystem::Dispatch( char const *pToken, unsigned int uiHash, CResponseSystem::DispatchMap_t &rMap ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnResponseDispatch dispatch = rMap[ slot ]; + (this->*dispatch)(); + return true; + } + + return false; +} + +bool CResponseSystem::DispatchParseRule( char const *pToken, unsigned int uiHash, ParseRuleDispatchMap_t &rMap, Rule &newRule ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnParseRuleDispatch dispatch = rMap[ slot ]; + (this->*dispatch)( newRule ); + return true; + } + + return false; +} + +bool CResponseSystem::DispatchParseResponse( char const *pToken, unsigned int uiHash, ParseResponseDispatchMap_t &rMap, ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnParseResponseDispatch dispatch = rMap[ slot ]; + (this->*dispatch)( newResponse, group, rp ); + return true; + } + + return false; +} + +bool CResponseSystem::DispatchParseResponseGroup( char const *pToken, unsigned int uiHash, ParseResponseGroupDispatchMap_t &rMap, char const *responseGroupName, ResponseGroup& newGroup, AI_ResponseParams &groupResponseParams ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnParseResponseGroupDispatch dispatch = rMap[ slot ]; + (this->*dispatch)( responseGroupName, newGroup, groupResponseParams ); + return true; + } + + return false; +} + +unsigned int ResponseRulePartition::GetBucketForSpeakerAndConcept( const char *pszSpeaker, const char *pszConcept, const char *pszSubject ) +{ + // make sure is a power of two + COMPILE_TIME_ASSERT( ( N_RESPONSE_PARTITIONS & ( N_RESPONSE_PARTITIONS - 1 ) ) == 0 ); + + // hash together the speaker and concept strings, and mask off by the bucket mask + unsigned hashSpeaker = 0; // pszSpeaker ? HashStringCaseless( pszSpeaker ) : 0; + unsigned hashConcept = pszConcept ? HashStringCaseless( pszConcept ) : 0; + unsigned hashSubject = pszSubject ? HashStringCaseless( pszSubject ) : 0; + unsigned hashBrowns = ( ( hashSubject >> 3 ) ^ (hashSpeaker >> 1) ^ hashConcept ) & ( N_RESPONSE_PARTITIONS - 1 ); + return hashBrowns; +} + +const char *Rule::GetValueForRuleCriterionByName( CResponseSystem * RESTRICT pSystem, const CUtlSymbol &pCritNameSym ) +{ + const char * retval = NULL; + // for each rule criterion... + for ( int i = 0 ; i < m_Criteria.Count() ; ++i ) + { + retval = RecursiveGetValueForRuleCriterionByName( pSystem, &pSystem->m_Criteria[m_Criteria[i]], pCritNameSym ); + if ( retval != NULL ) + { + // we found a result, early out + break; + } + } + + return retval; +} + +const Criteria *Rule::GetPointerForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym ) +{ + const Criteria * retval = NULL; + // for each rule criterion... + for ( int i = 0 ; i < m_Criteria.Count() ; ++i ) + { + retval = RecursiveGetPointerForRuleCriterionByName( pSystem, &pSystem->m_Criteria[m_Criteria[i]], pCritNameSym ); + if ( retval != NULL ) + { + // we found a result, early out + break; + } + } + + return retval; +} + +const char *Rule::RecursiveGetValueForRuleCriterionByName( CResponseSystem * RESTRICT pSystem, + const Criteria * RESTRICT pCrit, const CUtlSymbol &pCritNameSym ) +{ + Assert( pCrit ); + if ( !pCrit ) return NULL; + if ( pCrit->IsSubCriteriaType() ) + { + // test each of the children (depth first) + const char *pRet = NULL; + for ( int i = 0 ; i < pCrit->subcriteria.Count() ; ++i ) + { + pRet = RecursiveGetValueForRuleCriterionByName( pSystem, &pSystem->m_Criteria[pCrit->subcriteria[i]], pCritNameSym ); + if ( pRet ) // if found something, early out + return pRet; + } + } + else // leaf criterion + { + if ( pCrit->nameSym == pCritNameSym ) + { + return pCrit->value; + } + else + { + return NULL; + } + } + + return NULL; +} + + +const Criteria *Rule::RecursiveGetPointerForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym ) +{ + Assert( pCrit ); + if ( !pCrit ) return NULL; + if ( pCrit->IsSubCriteriaType() ) + { + // test each of the children (depth first) + const Criteria *pRet = NULL; + for ( int i = 0 ; i < pCrit->subcriteria.Count() ; ++i ) + { + pRet = RecursiveGetPointerForRuleCriterionByName( pSystem, &pSystem->m_Criteria[pCrit->subcriteria[i]], pCritNameSym ); + if ( pRet ) // if found something, early out + return pRet; + } + } + else // leaf criterion + { + if ( pCrit->nameSym == pCritNameSym ) + { + return pCrit; + } + else + { + return NULL; + } + } + + return NULL; +} + + +static void CC_RR_Debug_ResponseConcept_Exclude( const CCommand &args ) +{ + // shouldn't use this extern elsewhere -- it's meant to be a hidden + // implementation detail + extern CRR_ConceptSymbolTable *g_pRRConceptTable; + Assert( g_pRRConceptTable ); + if ( !g_pRRConceptTable ) return; + + + // different things for different argument lengths + switch ( args.ArgC() ) + { + case 0: + { + AssertMsg( args.ArgC() > 0, "WTF error in ccommand parsing: zero arguments!\n" ); + return; + } + case 1: + { + // print usage info + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "Usage: rr_debugresponseconcept_exclude Concept1 Concept2 Concept3...\n"); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\tseparate multiple concepts with spaces.\n"); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\tcall with no arguments to see this message and a list of current excludes.\n"); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\tto reset the exclude list, type \"rr_debugresponseconcept_exclude !\"\n"); + + // print current excludes + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\nCurrent exclude list:\n" ); + if ( !CResponseSystem::m_DebugExcludeList.IsValidIndex( CResponseSystem::m_DebugExcludeList.Head() ) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t\n" ); + } + else + { + CResponseSystem::ExcludeList_t::IndexLocalType_t i; + for ( i = CResponseSystem::m_DebugExcludeList.Head() ; + CResponseSystem::m_DebugExcludeList.IsValidIndex(i) ; + i = CResponseSystem::m_DebugExcludeList.Next(i) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t%s\n", CResponseSystem::m_DebugExcludeList[i].GetStringConcept() ); + } + } + return; + } + case 2: + // deal with the erase operator + if ( args[1][0] == '!' ) + { + CResponseSystem::m_DebugExcludeList.Purge(); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "Exclude list emptied.\n" ); + return; + } + // else, FALL THROUGH: + default: + // add each arg to the exclude list + for ( int i = 1 ; i < args.ArgC() ; ++i ) + { + if ( !g_pRRConceptTable->Find(args[i]).IsValid() ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t'%s' is not a known concept (adding it anyway)\n", args[i] ); + } + CRR_Concept concept( args[i] ); + CResponseSystem::m_DebugExcludeList.AddToTail( concept ); + } + } +} +#if RR_DUMPHASHINFO_ENABLED +void ResponseRulePartition::PrintBucketInfo( CResponseSystem *pSys ) +{ + struct bucktuple_t + { + int nBucket; + int nCount; + bucktuple_t() : nBucket(-1), nCount(-1) {}; + bucktuple_t( int bucket, int count ) : nBucket(bucket), nCount(count) {}; + + static int __cdecl SortCompare( const bucktuple_t * a, const bucktuple_t * b ) + { + return a->nCount - b->nCount; + } + }; + + CUtlVector infos( N_RESPONSE_PARTITIONS, N_RESPONSE_PARTITIONS ); + + float nAverage = 0; + for ( int i = 0 ; i < N_RESPONSE_PARTITIONS ; ++i ) + { + int count = m_RuleParts[i].Count(); + infos.AddToTail( bucktuple_t( i, count ) ); + nAverage += count; + } + nAverage /= N_RESPONSE_PARTITIONS; + infos.Sort( bucktuple_t::SortCompare ); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%d buckets, %d total, %.2f average size\n", N_RESPONSE_PARTITIONS, Count(), nAverage ); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "8 shortest buckets:\n" ); + for ( int i = 0 ; i < 8 ; ++i ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t%d: %d\n", infos[i].nBucket, infos[i].nCount ); + } + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "8 longest buckets:\n" ); + for ( int i = infos.Count() - 1 ; i >= infos.Count() - 9 ; --i ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t%d: %d\n", infos[i].nBucket, infos[i].nCount ); + } + int nempty = 0; + for ( nempty = 0 ; nempty < infos.Count() ; ++nempty ) + { + if ( infos[nempty].nCount != 0 ) + break; + } + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%d empty buckets\n", nempty ); + + /* + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Contents of longest bucket\nwho\tconcept\n" ); + tRuleDict &bucket = m_RuleParts[infos[infos.Count()-1].nBucket]; + for ( tRuleDict::IndexType_t i = bucket.FirstInorder(); bucket.IsValidIndex(i); i = bucket.NextInorder(i) ) + { + Rule &rule = bucket.Element(i) ; + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%s\t%s\n", rule.GetValueForRuleCriterionByName( pSys, "who" ), rule.GetValueForRuleCriterionByName( pSys, CriteriaSet::ComputeCriteriaSymbol("concept") ) ); + } + */ +} +#endif \ No newline at end of file diff --git a/mp/src/responserules/runtime/response_system.h b/mp/src/responserules/runtime/response_system.h new file mode 100644 index 00000000..a862d761 --- /dev/null +++ b/mp/src/responserules/runtime/response_system.h @@ -0,0 +1,336 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: The CResponseSystem class. Don't include this header; include the response_types +// into which it is transcluded. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_SYSTEM_H +#define RESPONSE_SYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utldict.h" + +namespace ResponseRules +{ + typedef ResponseParams AI_ResponseParams ; + #define AI_CriteriaSet ResponseRules::CriteriaSet + + //----------------------------------------------------------------------------- + // Purpose: The database of all available responses. + // The Rules are partitioned based on a variety of factors (presently, + // speaker and concept) for faster lookup, basically a seperate-chained hash. + //----------------------------------------------------------------------------- + class CResponseSystem : public IResponseSystem + { + public: + CResponseSystem(); + ~CResponseSystem(); + + typedef void (CResponseSystem::*pfnResponseDispatch)( void ); + typedef void (CResponseSystem::*pfnParseRuleDispatch)( Rule & ); + typedef void (CResponseSystem::*pfnParseResponseDispatch)( ParserResponse &, ResponseGroup&, AI_ResponseParams * ); + typedef void (CResponseSystem::*pfnParseResponseGroupDispatch) ( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + + typedef CUtlMap< unsigned,pfnResponseDispatch > DispatchMap_t; + typedef CUtlMap< unsigned,pfnParseRuleDispatch > ParseRuleDispatchMap_t; + typedef CUtlMap< unsigned,pfnParseResponseDispatch > ParseResponseDispatchMap_t; + typedef CUtlMap< unsigned,pfnParseResponseGroupDispatch > ParseResponseGroupDispatchMap_t; + +#pragma region IResponseSystem + // IResponseSystem + virtual bool FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter = NULL ); + virtual void GetAllResponses( CUtlVector *pResponses ); + +#ifdef MAPBASE + virtual void SetProspective( bool bToggle ) { m_bInProspective = bToggle; } + + virtual void MarkResponseAsUsed( short iGroup, short iWithinGroup ); +#endif +#pragma endregion Implement interface from IResponseSystem + + virtual void Release() = 0; + + virtual void DumpRules(); + + bool IsCustomManagable() { return m_bCustomManagable; } + +#ifdef MAPBASE + virtual +#endif + void Clear(); + + void DumpDictionary( const char *pszName ); + + protected: + + void BuildDispatchTables(); + bool Dispatch( char const *pToken, unsigned int uiHash, DispatchMap_t &rMap ); + bool DispatchParseRule( char const *pToken, unsigned int uiHash, ParseRuleDispatchMap_t &rMap, Rule &newRule ); + bool DispatchParseResponse( char const *pToken, unsigned int uiHash, ParseResponseDispatchMap_t &rMap, ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + bool DispatchParseResponseGroup( char const *pToken, unsigned int uiHash, ParseResponseGroupDispatchMap_t &rMap, char const *responseGroupName, ResponseGroup& newGroup, AI_ResponseParams &groupResponseParams ); + + virtual const char *GetScriptFile( void ) = 0; + void LoadRuleSet( const char *setname ); + + void ResetResponseGroups(); + + float LookForCriteria( const CriteriaSet &criteriaSet, int iCriteria ); + float RecursiveLookForCriteria( const CriteriaSet &criteriaSet, Criteria *pParent ); + + public: + + void CopyRuleFrom( Rule *pSrcRule, ResponseRulePartition::tIndex iRule, CResponseSystem *pCustomSystem ); + void CopyCriteriaFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ); + void CopyResponsesFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ); + void CopyEnumerationsFrom( CResponseSystem *pCustomSystem ); + + //private: + + struct Enumeration + { + float value; + }; + + struct ResponseSearchResult + { + ResponseSearchResult() + { + group = NULL; + action = NULL; + } + + ResponseGroup *group; + ParserResponse *action; + }; + + inline bool ParseToken( void ) + { + if ( m_bUnget ) + { + m_bUnget = false; + return true; + } + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + m_ScriptStack[ 0 ].currenttoken = IEngineEmulator::Get()->ParseFile( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); + m_ScriptStack[ 0 ].tokencount++; + return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; + } + +#ifdef MAPBASE + inline bool ParseTokenIntact( void ) + { + if ( m_bUnget ) + { + m_bUnget = false; + return true; + } + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + m_ScriptStack[ 0 ].currenttoken = IEngineEmulator::Get()->ParseFilePreserve( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); + m_ScriptStack[ 0 ].tokencount++; + return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; + } +#endif + + inline void Unget() + { + m_bUnget = true; + } + + inline bool TokenWaiting( void ) + { + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + const char *p = m_ScriptStack[ 0 ].currenttoken; + + if ( !p ) + { + Error( "AI_ResponseSystem: Unxpected TokenWaiting() with NULL buffer in %s", (char * ) m_ScriptStack[ 0 ].name ); + return false; + } + + + while ( *p && *p!='\n') + { + // Special handler for // comment blocks + if ( *p == '/' && *(p+1) == '/' ) + return false; + + if ( !V_isspace( *p ) || isalnum( *p ) ) + return true; + + p++; + } + + return false; + } + + void ParseOneResponse( const char *responseGroupName, ResponseGroup& group, ResponseParams *defaultParams = NULL ); + + void ParseInclude( void ); + void ParseResponse( void ); + void ParseCriterion( void ); + void ParseRule( void ); + void ParseEnumeration( void ); + + private: + void ParseRule_MatchOnce( Rule &newRule ); + void ParseRule_ApplyContextToWorld( Rule &newRule ); +#ifdef MAPBASE + void ParseRule_ApplyContextToSquad( Rule &newRule ); + void ParseRule_ApplyContextToEnemy( Rule &newRule ); +#endif + void ParseRule_ApplyContext( Rule &newRule ); + void ParseRule_Response( Rule &newRule ); + //void ParseRule_ForceWeight( Rule &newRule ); + void ParseRule_Criteria( Rule &newRule ); + char const *m_pParseRuleName; + bool m_bParseRuleValid; + + void ParseResponse_Weight( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_PreDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_NoDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_DefaultDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Delay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_SpeakOnce( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_NoScene( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_StopOnNonIdle( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Odds( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_RespeakDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_WeaponDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Soundlevel( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_DisplayFirst( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_DisplayLast( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Fire( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Then( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + + void ParseResponseGroup_Start( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_PreDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_NoDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_DefaultDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_Delay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_SpeakOnce( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_NoScene( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_StopOnNonIdle( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_Odds( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_RespeakDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_WeaponDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_Soundlevel( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + +public: + int ParseOneCriterion( const char *criterionName ); + + bool Compare( const char *setValue, Criteria *c, bool verbose = false ); + bool CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose = false ); + void ComputeMatcher( Criteria *c, Matcher& matcher ); + void ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ); + float LookupEnumeration( const char *name, bool& found ); + + ResponseRulePartition::tIndex FindBestMatchingRule( const CriteriaSet& set, bool verbose, float &scoreOfBestMatchingRule ); + +#ifdef MAPBASE + void DisableEmptyRules(); +#endif + + float ScoreCriteriaAgainstRule( const CriteriaSet& set, ResponseRulePartition::tRuleDict &dict, int irule, bool verbose = false ); + float RecursiveScoreSubcriteriaAgainstRule( const CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ); + float ScoreCriteriaAgainstRuleCriteria( const CriteriaSet& set, int icriterion, bool& exclude, bool verbose = false ); + void FakeDepletes( ResponseGroup *g, IResponseFilter *pFilter ); + void RevertFakedDepletes( ResponseGroup *g ); + bool GetBestResponse( ResponseSearchResult& result, Rule *rule, bool verbose = false, IResponseFilter *pFilter = NULL ); + bool ResolveResponse( ResponseSearchResult& result, int depth, const char *name, bool verbose = false, IResponseFilter *pFilter = NULL ); + int SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ); + void DescribeResponseGroup( ResponseGroup *group, int selected, int depth ); + void DebugPrint( int depth, const char *fmt, ... ); + + void LoadFromBuffer( const char *scriptfile, const char *buffer ); + + void GetCurrentScript( char *buf, size_t buflen ); + int GetCurrentToken() const; + void SetCurrentScript( const char *script ); + + inline bool IsRootCommand( unsigned int hash ) const + { + int slot = m_RootCommandHashes.Find( hash ); + return slot != m_RootCommandHashes.InvalidIndex(); + } + + inline bool IsRootCommand() const + { + return IsRootCommand( RR_HASH( token ) ); + } + + void PushScript( const char *scriptfile, unsigned char *buffer ); + void PopScript(void); + + void ResponseWarning( const char *fmt, ... ); + + CUtlDict< ResponseGroup, short > m_Responses; + CUtlDict< Criteria, short > m_Criteria; + // CUtlDict< Rule, short > m_Rules; + ResponseRulePartition m_RulePartitions; + CUtlDict< Enumeration, short > m_Enumerations; + + CUtlVector m_FakedDepletes; + + char token[ 1204 ]; + + bool m_bUnget; + + bool m_bCustomManagable; + +#ifdef MAPBASE + // This is a hack specifically designed to fix displayfirst, speakonce, etc. in "prospective" response searches, + // especially the prospective lookups in followup responses. + // It works by preventing responses from being marked as "used". + bool m_bInProspective; +#endif + + struct ScriptEntry + { + unsigned char *buffer; + FileNameHandle_t name; + const char *currenttoken; + int tokencount; + }; + + CUtlVector< ScriptEntry > m_ScriptStack; + CStringPool m_IncludedFiles; + + DispatchMap_t m_FileDispatch; + ParseRuleDispatchMap_t m_RuleDispatch; + ParseResponseDispatchMap_t m_ResponseDispatch; + ParseResponseGroupDispatchMap_t m_ResponseGroupDispatch; + CUtlRBTree< unsigned int > m_RootCommandHashes; + + // for debugging purposes only: concepts to be emitted from rr_debugresponses 2 + typedef CUtlLinkedList< CRR_Concept, unsigned short, false, unsigned int > ExcludeList_t; + static ExcludeList_t m_DebugExcludeList; + + friend class CDefaultResponseSystemSaveRestoreBlockHandler; + friend class CResponseSystemSaveRestoreOps; + }; + + // Some globals inherited from AI_Speech.h: + const float AIS_DEF_MIN_DELAY = 2.8; // Minimum amount of time an NPCs will wait after someone has spoken before considering speaking again + const float AIS_DEF_MAX_DELAY = 3.2; // Maximum amount of time an NPCs will wait after someone has spoken before considering speaking again +} + +#endif // RESPONSE_SYSTEM_H \ No newline at end of file diff --git a/mp/src/responserules/runtime/response_types.cpp b/mp/src/responserules/runtime/response_types.cpp new file mode 100644 index 00000000..1d8f6b31 --- /dev/null +++ b/mp/src/responserules/runtime/response_types.cpp @@ -0,0 +1,281 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" + +#include "tier1/mapbase_con_groups.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace ResponseRules; + + +// bizarre function handed down from the misty days of yore +// and the original response system. a lot of stuff uses it +// and I can't be arsed to replace everything with the c stdlib +// stuff +namespace ResponseRules +{ + extern const char *ResponseCopyString( const char *in ); +}; + + +//-------------------- MATCHER ---------------------------------------------- + +Matcher::Matcher() +{ + valid = false; + isnumeric = false; + notequal = false; + usemin = false; + minequals = false; + usemax = false; + maxequals = false; +#ifdef MAPBASE + isbit = false; +#endif + maxval = 0.0f; + minval = 0.0f; + + token = UTL_INVAL_SYMBOL; + rawtoken = UTL_INVAL_SYMBOL; +} + +void Matcher::Describe( void ) +{ + if ( !valid ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " invalid!\n" ); + return; + } + char sz[ 128 ]; + + sz[ 0] = 0; + int minmaxcount = 0; + if ( usemin ) + { + Q_snprintf( sz, sizeof( sz ), ">%s%.3f", minequals ? "=" : "", minval ); + minmaxcount++; + } + if ( usemax ) + { + char sz2[ 128 ]; + Q_snprintf( sz2, sizeof( sz2 ), "<%s%.3f", maxequals ? "=" : "", maxval ); + + if ( minmaxcount > 0 ) + { + Q_strncat( sz, " and ", sizeof( sz ), COPY_ALL_CHARACTERS ); + } + Q_strncat( sz, sz2, sizeof( sz ), COPY_ALL_CHARACTERS ); + minmaxcount++; + } + + if ( minmaxcount >= 1 ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: %s\n", sz ); + return; + } + +#ifdef MAPBASE + if ( isbit ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: &%s%s\n", notequal ? "~" : "", GetToken() ); + return; + } +#endif + + if ( notequal ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: !=%s\n", GetToken() ); + return; + } + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: ==%s\n", GetToken() ); +} + +void Matcher::SetToken( char const *s ) +{ + token = g_RS.AddString( s ); +} + +void Matcher::SetRaw( char const *raw ) +{ + rawtoken = g_RS.AddString( raw ); +} + +char const *Matcher::GetToken() +{ + if ( token.IsValid() ) + { + return g_RS.String( token ); + } + return ""; +} + +char const *Matcher::GetRaw() +{ + if ( rawtoken.IsValid() ) + { + return g_RS.String( rawtoken ); + } + return ""; +} + +//-------------------- CRITERIA ---------------------------------------------- + +Criteria::Criteria() +{ + value = NULL; + weight.SetFloat( 1.0f ); + required = false; +} +Criteria::Criteria(const Criteria& src ) +{ + operator=( src ); +} + +Criteria::~Criteria() +{ + // do nothing because we don't own name and value anymore +} + +Criteria& Criteria::operator =(const Criteria& src ) +{ + if ( this == &src ) + return *this; + + nameSym = src.nameSym; + value = ResponseCopyString( src.value ); + weight = src.weight; + required = src.required; + + matcher = src.matcher; + + int c = src.subcriteria.Count(); + subcriteria.EnsureCapacity( c ); + for ( int i = 0; i < c; i++ ) + { + subcriteria.AddToTail( src.subcriteria[ i ] ); + } + + return *this; +} + + +//-------------------- RESPONSE ---------------------------------------------- + + + +ParserResponse::ParserResponse() : m_followup() +{ + type = RESPONSE_NONE; + value = NULL; + weight.SetFloat( 1.0f ); + depletioncount = 0; + first = false; + last = false; +} + +ParserResponse& ParserResponse::operator =( const ParserResponse& src ) +{ + if ( this == &src ) + return *this; + weight = src.weight; + type = src.type; + value = ResponseCopyString( src.value ); + depletioncount = src.depletioncount; + first = src.first; + last = src.last; + params = src.params; + + m_followup.followup_concept = ResponseCopyString(src.m_followup.followup_concept); + m_followup.followup_contexts = ResponseCopyString(src.m_followup.followup_contexts); + m_followup.followup_target = ResponseCopyString(src.m_followup.followup_target); + m_followup.followup_entityioinput = ResponseCopyString(src.m_followup.followup_entityioinput); + m_followup.followup_entityiotarget = ResponseCopyString(src.m_followup.followup_entityiotarget); + m_followup.followup_delay = src.m_followup.followup_delay; + m_followup.followup_entityiodelay = src.m_followup.followup_entityiodelay; + + return *this; +} + +ParserResponse::ParserResponse( const ParserResponse& src ) +{ + operator=( src ); +} + +ParserResponse::~ParserResponse() +{ + // nothing to do, since we don't own + // the strings anymore +} + +// ------------ RULE --------------- + +Rule::Rule() : m_nForceWeight(0) +{ + m_bMatchOnce = false; + m_bEnabled = true; + m_szContext = NULL; +#ifdef MAPBASE + m_iContextFlags = 0; +#else + m_bApplyContextToWorld = false; +#endif +} + +Rule& Rule::operator =( const Rule& src ) +{ + if ( this == &src ) + return *this; + + int i; + int c; + + c = src.m_Criteria.Count(); + m_Criteria.EnsureCapacity( c ); + for ( i = 0; i < c; i++ ) + { + m_Criteria.AddToTail( src.m_Criteria[ i ] ); + } + + c = src.m_Responses.Count(); + m_Responses.EnsureCapacity( c ); + for ( i = 0; i < c; i++ ) + { + m_Responses.AddToTail( src.m_Responses[ i ] ); + } + + 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 + m_nForceWeight = src.m_nForceWeight; + return *this; +} + +Rule::Rule( const Rule& src ) +{ + operator=(src); +} + +Rule::~Rule() +{ +} + +void Rule::SetContext( const char *context ) +{ + // we don't own the data we point to, so just update pointer + m_szContext = ResponseCopyString( context ); +} + + diff --git a/mp/src/responserules/runtime/response_types_internal.cpp b/mp/src/responserules/runtime/response_types_internal.cpp new file mode 100644 index 00000000..873f8759 --- /dev/null +++ b/mp/src/responserules/runtime/response_types_internal.cpp @@ -0,0 +1,172 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" +#ifdef MAPBASE +#include "convar.h" +#include "mapbase_matchers_base.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace ResponseRules; + +#ifdef MAPBASE +ConVar rr_bucket_name_who( "rr_bucket_name_who", "Classname", FCVAR_NONE, "The name of the criteria to use for the 'Who' bucket." ); // Default changed to "Classname" for HL2 +ConVar rr_bucket_name_concept( "rr_bucket_name_concept", "Concept", FCVAR_NONE, "The name of the criteria to use for the 'Concept' bucket." ); +ConVar rr_bucket_name_subject( "rr_bucket_name_subject", "Subject", FCVAR_NONE, "The name of the criteria to use for the 'Subject' bucket." ); +#endif + + + + +ResponseRulePartition::ResponseRulePartition() +{ + Assert(true); + COMPILE_TIME_ASSERT( kIDX_ELEM_MASK < (1 << 16) ); + COMPILE_TIME_ASSERT( (kIDX_ELEM_MASK & (kIDX_ELEM_MASK + 1)) == 0 ); /// assert is power of two minus one +} + +ResponseRulePartition::~ResponseRulePartition() +{ + RemoveAll(); +} + +ResponseRulePartition::tIndex ResponseRulePartition::IndexFromDictElem( tRuleDict* pDict, int elem ) +{ + Assert( pDict ); + // If this fails, you've tried to build an index for a rule that's not stored + // in this partition + Assert( pDict >= m_RuleParts && pDict < m_RuleParts + N_RESPONSE_PARTITIONS ); + AssertMsg1( elem <= kIDX_ELEM_MASK, "A rule dictionary has %d elements; this exceeds the 255 that can be packed into an index.\n", elem ); + + int bucket = pDict - m_RuleParts; + return ( bucket << 16 ) | ( elem & kIDX_ELEM_MASK ); // this is a native op on PPC +} + + +char const *ResponseRulePartition::GetElementName( const tIndex &i ) const +{ + Assert( IsValid(i) ); + return m_RuleParts[ BucketFromIdx(i) ].GetElementName( PartFromIdx(i) ); +} + + +Rule *ResponseRulePartition::FindByName( char const *name ) const +{ + int count; + + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + count = m_RuleParts[bukkit].Count(); + for ( int i = 0; i < count; ++i ) + { + if (V_strncmp( m_RuleParts[bukkit].GetElementName(i), name, CRR_Response::MAX_RULE_NAME ) == 0) + return m_RuleParts[bukkit][i]; + } + } + + return NULL; +} + + +int ResponseRulePartition::Count( void ) +{ + int count = 0 ; + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + count += m_RuleParts[bukkit].Count(); + } + + return count; +} + +void ResponseRulePartition::RemoveAll( void ) +{ + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + for ( int i = m_RuleParts[bukkit].FirstInorder(); i != m_RuleParts[bukkit].InvalidIndex(); i = m_RuleParts[bukkit].NextInorder( i ) ) + { + delete m_RuleParts[bukkit][ i ]; + } + m_RuleParts[bukkit].RemoveAll(); + } +} + +#ifdef MAPBASE +void ResponseRulePartition::PurgeAndDeleteElements() +{ + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + for ( int i = m_RuleParts[bukkit].FirstInorder(); i != m_RuleParts[bukkit].InvalidIndex(); i = m_RuleParts[bukkit].NextInorder( i ) ) + { + delete m_RuleParts[bukkit][ i ]; + } + m_RuleParts[bukkit].Purge(); + } +} +#endif + +// don't bucket "subject" criteria that prefix with operators, since stripping all that out again would +// be a big pain, and the most important rules that need subjects are tlk_remarks anyway. +static inline bool CanBucketBySubject( const char * RESTRICT pszSubject ) +{ + return pszSubject && + ( ( pszSubject[0] >= 'A' && pszSubject[0] <= 'Z' ) || + ( pszSubject[0] >= 'a' && pszSubject[0] <= 'z' ) ) +#ifdef MAPBASE + && !Matcher_ContainsWildcard( pszSubject ) +#endif + ; +} + +ResponseRulePartition::tRuleDict &ResponseRulePartition::GetDictForRule( CResponseSystem *pSystem, Rule *pRule ) +{ +#ifdef MAPBASE + const static CUtlSymbol kWHO = CriteriaSet::ComputeCriteriaSymbol( rr_bucket_name_who.GetString() ); + const static CUtlSymbol kCONCEPT = CriteriaSet::ComputeCriteriaSymbol( rr_bucket_name_concept.GetString() ); + const static CUtlSymbol kSUBJECT = CriteriaSet::ComputeCriteriaSymbol( rr_bucket_name_subject.GetString() ); +#else + const static CUtlSymbol kWHO = CriteriaSet::ComputeCriteriaSymbol("Who"); + const static CUtlSymbol kCONCEPT = CriteriaSet::ComputeCriteriaSymbol("Concept"); + const static CUtlSymbol kSUBJECT = CriteriaSet::ComputeCriteriaSymbol("Subject"); +#endif + + const char *pszSpeaker = pRule->GetValueForRuleCriterionByName( pSystem, kWHO ); + const char *pszConcept = pRule->GetValueForRuleCriterionByName( pSystem, kCONCEPT ); + const Criteria *pSubjCrit = pRule->GetPointerForRuleCriterionByName( pSystem, kSUBJECT ); + + return m_RuleParts[ + GetBucketForSpeakerAndConcept( pszSpeaker, pszConcept, + ( pSubjCrit && pSubjCrit->required && CanBucketBySubject(pSubjCrit->value) ) ? + pSubjCrit->value : + NULL ) + ]; +} + + +void ResponseRulePartition::GetDictsForCriteria( CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > *pResult, const CriteriaSet &criteria ) +{ + pResult->RemoveAll(); + pResult->EnsureCapacity( 2 ); + + // get the values for Who and Concept, which are what we bucket on + int speakerIdx = criteria.FindCriterionIndex( "Who" ); + const char *pszSpeaker = speakerIdx != -1 ? criteria.GetValue( speakerIdx ) : NULL ; + + int conceptIdx = criteria.FindCriterionIndex( "Concept" ); + const char *pszConcept = conceptIdx != -1 ? criteria.GetValue( conceptIdx ) : NULL ; + + int subjectIdx = criteria.FindCriterionIndex( "Subject" ); + const char *pszSubject = subjectIdx != -1 ? criteria.GetValue( subjectIdx ) : NULL ; + + pResult->AddToTail( &m_RuleParts[ GetBucketForSpeakerAndConcept(pszSpeaker, pszConcept, pszSubject) ] ); + // also try the rules not specifying subject + pResult->AddToTail( &m_RuleParts[ GetBucketForSpeakerAndConcept(pszSpeaker, pszConcept, NULL) ] ); + +} \ No newline at end of file diff --git a/mp/src/responserules/runtime/response_types_internal.h b/mp/src/responserules/runtime/response_types_internal.h new file mode 100644 index 00000000..08f31185 --- /dev/null +++ b/mp/src/responserules/runtime/response_types_internal.h @@ -0,0 +1,560 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_TYPES_INTERNAL_H +#define RESPONSE_TYPES_INTERNAL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "responserules/response_types.h" +#include "utldict.h" + + +namespace ResponseRules +{ + + inline unsigned FASTCALL HashStringConventional( const char *pszKey ) + { + unsigned hash = 0xAAAAAAAA; // Alternating 1's and 0's to maximize the effect of the later multiply and add + + for( ; *pszKey ; pszKey++ ) + { + hash = ( ( hash << 5 ) + hash ) + (uint8)(*pszKey); + } + + return hash; + } + + // Note: HashString causes collisions!!! +#define RR_HASH HashStringConventional + +#pragma pack(push,1) + + class Matcher + { + public: + Matcher(); + + void Describe( void ); + + float maxval; + float minval; + + bool valid : 1; //1 + bool isnumeric : 1; //2 + bool notequal : 1; //3 + bool usemin : 1; //4 + bool minequals : 1; //5 + bool usemax : 1; //6 + bool maxequals : 1; //7 +#ifdef MAPBASE + bool isbit : 1; //8 +#endif + + void SetToken( char const *s ); + + char const *GetToken(); + + void SetRaw( char const *raw ); + + char const *GetRaw(); + + private: + CUtlSymbol token; + CUtlSymbol rawtoken; + }; +#pragma pack(pop) + + struct Criteria + { + Criteria(); + Criteria& operator =(const Criteria& src ); + + Criteria(const Criteria& src ); + ~Criteria(); + + // Does this criterion recursively contain more criteria? + inline bool IsSubCriteriaType() const + { + return ( subcriteria.Count() > 0 ) ? true : false; + } + + // const char *name; + CUtlSymbol nameSym; + const char *value; + float16 weight; + bool required; + + Matcher matcher; + + // Indices into sub criteria + CUtlVectorConservative< unsigned short > subcriteria; + }; + +#pragma pack(push,1) + /// This is a response block as read from the file, + /// different from CRR_Response which is what is handed + /// back to queries. + struct ParserResponse + { + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + ParserResponse(); + ParserResponse( const ParserResponse& src ); + ParserResponse& operator =( const ParserResponse& src ); + ~ParserResponse(); + + ResponseType_t GetType() { return (ResponseType_t)type; } + + ResponseParams params; + + const char *value; // fixed up value spot // 4 + float16 weight; // 6 + + byte depletioncount; // 7 + byte type : 6; // 8 + byte first : 1; // + byte last : 1; // + + ALIGN32 AI_ResponseFollowup m_followup; // info on whether I should force the other guy to say something + }; +#pragma pack(pop) + +#pragma pack(push,1) + struct ResponseGroup + { + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + ResponseGroup() + { + // By default visit all nodes before repeating + m_bSequential = false; + m_bNoRepeat = false; + m_bEnabled = true; + m_nCurrentIndex = 0; + m_bDepleteBeforeRepeat = true; + m_nDepletionCount = 1; + m_bHasFirst = false; + m_bHasLast = false; + } + + ResponseGroup( const ResponseGroup& src ) + { + int c = src.group.Count(); + for ( int i = 0; i < c; i++ ) + { + group.AddToTail( src.group[ i ] ); + } + + m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; + m_nDepletionCount = src.m_nDepletionCount; + m_bHasFirst = src.m_bHasFirst; + m_bHasLast = src.m_bHasLast; + m_bSequential = src.m_bSequential; + m_bNoRepeat = src.m_bNoRepeat; + m_bEnabled = src.m_bEnabled; + m_nCurrentIndex = src.m_nCurrentIndex; + } + + ResponseGroup& operator=( const ResponseGroup& src ) + { + if ( this == &src ) + return *this; + int c = src.group.Count(); + for ( int i = 0; i < c; i++ ) + { + group.AddToTail( src.group[ i ] ); + } + + m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; + m_nDepletionCount = src.m_nDepletionCount; + m_bHasFirst = src.m_bHasFirst; + m_bHasLast = src.m_bHasLast; + m_bSequential = src.m_bSequential; + m_bNoRepeat = src.m_bNoRepeat; + m_bEnabled = src.m_bEnabled; + m_nCurrentIndex = src.m_nCurrentIndex; + return *this; + } + + bool HasUndepletedChoices() const + { + if ( !m_bDepleteBeforeRepeat ) + return true; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + if ( group[ i ].depletioncount != m_nDepletionCount ) + return true; + } + + return false; + } + + void MarkResponseUsed( int idx ) + { + if ( !m_bDepleteBeforeRepeat ) + return; + + if ( idx < 0 || idx >= group.Count() ) + { + Assert( 0 ); + return; + } + + group[ idx ].depletioncount = m_nDepletionCount; + } + + void ResetDepletionCount() + { + if ( !m_bDepleteBeforeRepeat ) + return; + ++m_nDepletionCount; + } + + void Reset() + { + ResetDepletionCount(); + SetEnabled( true ); + SetCurrentIndex( 0 ); + m_nDepletionCount = 1; + + for ( int i = 0; i < group.Count(); ++i ) + { + group[ i ].depletioncount = 0; + } + } + + bool HasUndepletedFirst( int& index ) + { + index = -1; + + if ( !m_bDepleteBeforeRepeat ) + return false; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &group[ i ]; + + if ( ( r->depletioncount != m_nDepletionCount ) && r->first ) + { + index = i; + return true; + } + } + + return false; + } + + bool HasUndepletedLast( int& index ) + { + index = -1; + + if ( !m_bDepleteBeforeRepeat ) + return false; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &group[ i ]; + + if ( ( r->depletioncount != m_nDepletionCount ) && r->last ) + { + index = i; + return true; + } + } + + return false; + } + + bool ShouldCheckRepeats() const { return m_bDepleteBeforeRepeat; } + int GetDepletionCount() const { return m_nDepletionCount; } + + bool IsSequential() const { return m_bSequential; } + void SetSequential( bool seq ) { m_bSequential = seq; } + + bool IsNoRepeat() const { return m_bNoRepeat; } + void SetNoRepeat( bool norepeat ) { m_bNoRepeat = norepeat; } + + bool IsEnabled() const { return m_bEnabled; } + void SetEnabled( bool enabled ) { m_bEnabled = enabled; } + + int GetCurrentIndex() const { return m_nCurrentIndex; } + void SetCurrentIndex( byte idx ) { m_nCurrentIndex = idx; } + + CUtlVector< ParserResponse > group; + + bool m_bEnabled; + + byte m_nCurrentIndex; + // Invalidation counter + byte m_nDepletionCount; + + // Use all slots before repeating any + bool m_bDepleteBeforeRepeat : 1; + bool m_bHasFirst : 1; + bool m_bHasLast : 1; + bool m_bSequential : 1; + bool m_bNoRepeat : 1; + }; +#pragma pack(pop) + +#pragma pack(push,1) + struct Rule + { + Rule(); + Rule( const Rule& src ); + ~Rule(); + Rule& operator =( const Rule& src ); + + void SetContext( const char *context ); + + const char *GetContext( void ) const { return m_szContext; } + + inline bool IsEnabled() const { return m_bEnabled; } + inline void Disable() { m_bEnabled = false; } + inline bool IsMatchOnce() const { return m_bMatchOnce; } +#ifdef MAPBASE + inline int GetContextFlags() const { return m_iContextFlags; } + inline bool IsApplyContextToWorld() const { return (m_iContextFlags & APPLYCONTEXT_WORLD) != 0; } +#else + inline bool IsApplyContextToWorld() const { return m_bApplyContextToWorld; } +#endif + + const char *GetValueForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym ); + const Criteria *GetPointerForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym ); + + // Indices into underlying criteria and response dictionaries + CUtlVectorConservative< unsigned short > m_Criteria; + CUtlVectorConservative< unsigned short> m_Responses; + + const char *m_szContext; + uint8 m_nForceWeight; + +#ifdef MAPBASE + // TODO: Could this cause any issues with the code optimization? + uint8 m_iContextFlags; +#else + bool m_bApplyContextToWorld : 1; +#endif + + bool m_bMatchOnce : 1; + bool m_bEnabled : 1; + + private: + // what is this, lisp? + const char *RecursiveGetValueForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym ); + const Criteria *RecursiveGetPointerForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym ); + }; +#pragma pack(pop) + + template + class CResponseDict : public CUtlMap + { + public: + CResponseDict() : CUtlMap( DefLessFunc( unsigned int ) ), m_ReverseMap( DefLessFunc( unsigned int ) ) + { + } + + I Insert( const char *pName, const T &element ) + { + extern const char *ResponseCopyString( const char *in ); + char const *pString = ResponseCopyString( pName ); + unsigned int hash = RR_HASH( pString ); + m_ReverseMap.Insert( hash, pString ); + return CUtlMap::Insert( hash, element ); + } + + I Insert( const char *pName ) + { + extern const char *ResponseCopyString( const char *in ); + char const *pString = ResponseCopyString( pName ); + unsigned int hash = RR_HASH( pString ); + m_ReverseMap.Insert( hash, pString ); + return CUtlMap::Insert( hash ); + } + + I Find( char const *pName ) const + { + unsigned int hash = RR_HASH( pName ); + return CUtlMap::Find( hash ); + } + + const char *GetElementName( I i ) + { + int k = this->Key( i ); + int slot = m_ReverseMap.Find( k ); + if ( slot == m_ReverseMap.InvalidIndex() ) + return ""; + return m_ReverseMap[ slot ]; + } + + const char *GetElementName( I i ) const + { + int k = this->Key( i ); + int slot = m_ReverseMap.Find( k ); + if ( slot == m_ReverseMap.InvalidIndex() ) + return ""; + return m_ReverseMap[ slot ]; + } + + private: + CUtlMap< unsigned int, const char * > m_ReverseMap; + + }; + + // define this to 1 to enable printing some occupancy + // information on the response system via concommmand + // rr_dumphashinfo + #define RR_DUMPHASHINFO_ENABLED 0 + // The Rules are partitioned based on a variety of factors (presently, + // speaker and concept) for faster lookup, basically a seperate-chained hash. + struct ResponseRulePartition + { + ResponseRulePartition( void ); + ~ResponseRulePartition(); + + typedef CResponseDict< Rule * > tRuleDict; + typedef uint32 tIndex; // an integer that can be used to find any rule in the dict + + /// get the appropriate m_rules dict for the provided rule + tRuleDict &GetDictForRule( CResponseSystem *pSystem, Rule *pRule ); + + /// get all bucket full of rules that might possibly match the given criteria. + /// (right now they are bucketed such that all rules that can possibly match a + /// criteria are in one of two dictionaries) + void GetDictsForCriteria( CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > *pResult, const CriteriaSet &criteria ); + + // dump everything. + void RemoveAll(); +#ifdef MAPBASE + void PurgeAndDeleteElements(); +#endif + + inline Rule &operator[]( tIndex idx ); + int Count( void ); // number of elements inside, but you can't iterate from 0 to this + char const *GetElementName( const tIndex &i ) const; + Rule *FindByName( char const *name ) const; + + /// given a dictionary and an element number inside that dict, + /// return a tIndex + tIndex IndexFromDictElem( tRuleDict* pDict, int elem ); + + // for iteration: + inline tIndex First( void ); + inline tIndex Next( const tIndex &idx ); + inline bool IsValid( const tIndex &idx ) const; + inline static tIndex InvalidIdx( void ) + { + return ((tIndex) -1); + } + + // used only for debug prints, do not rely on them otherwise + inline unsigned int BucketFromIdx( const tIndex &idx ) const ; + inline unsigned int PartFromIdx( const tIndex &idx ) const ; + + enum { + N_RESPONSE_PARTITIONS = 256, + kIDX_ELEM_MASK = 0xFFF, ///< this is used to mask the element number part of a ResponseRulePartition::tIndex + }; + +#if RR_DUMPHASHINFO_ENABLED + void PrintBucketInfo( CResponseSystem *pSys ); +#endif + + private: + tRuleDict m_RuleParts[N_RESPONSE_PARTITIONS]; + unsigned int GetBucketForSpeakerAndConcept( const char *pszSpeaker, const char *pszConcept, const char *pszSubject ); + }; + + // // // // // inline functions + + inline ResponseRulePartition::tIndex ResponseRulePartition::First( void ) + { + // find the first bucket that has anything + for ( int bucket = 0 ; bucket < N_RESPONSE_PARTITIONS; bucket++ ) + { + if ( m_RuleParts[bucket].Count() > 0 ) + return bucket << 16; + } + return InvalidIdx(); + } + + inline ResponseRulePartition::tIndex ResponseRulePartition::Next( const tIndex &idx ) + { + int bucket = BucketFromIdx( idx ); + unsigned int elem = PartFromIdx( idx ); + Assert( IsValid(idx) ); + AssertMsg( elem < kIDX_ELEM_MASK, "Too many response rules! Overflow! Doom!" ); + if ( elem + 1 < m_RuleParts[bucket].Count() ) + { + return idx+1; + } + else + { + // walk through the other buckets, skipping empty ones, until we find one with responses and give up. + while ( ++bucket < N_RESPONSE_PARTITIONS ) + { + if ( m_RuleParts[bucket].Count() > 0 ) + { + // 0th element in nth bucket + return bucket << 16; + } + } + + // out of buckets + return InvalidIdx(); + + } + } + + inline Rule &ResponseRulePartition::operator[]( tIndex idx ) + { + Assert( IsValid(idx) ); + return *m_RuleParts[ BucketFromIdx(idx) ][ PartFromIdx(idx) ] ; + } + + inline unsigned int ResponseRulePartition::BucketFromIdx( const tIndex &idx ) const + { + return idx >> 16; + } + + inline unsigned int ResponseRulePartition::PartFromIdx( const tIndex &idx ) const + { + return idx & kIDX_ELEM_MASK; + } + + inline bool ResponseRulePartition::IsValid( const tIndex & idx ) const + { + // make sure that the idx type for the dicts is still short + COMPILE_TIME_ASSERT( sizeof(m_RuleParts[0].FirstInorder()) == 2 ); + + if ( idx == -1 ) + return false; + + int bucket = idx >> 16; + unsigned int elem = idx & kIDX_ELEM_MASK; + + return ( bucket < N_RESPONSE_PARTITIONS && + elem < m_RuleParts[bucket].Count() ); + } + + //----------------------------------------------------------------------------- + // PARSER TYPES -- these are internal to the response system, and represent + // the objects as loaded from disk. + //----------------------------------------------------------------------------- + + +} + +#include "response_system.h" + +#endif \ No newline at end of file diff --git a/mp/src/responserules/runtime/rr_convars.cpp b/mp/src/responserules/runtime/rr_convars.cpp new file mode 100644 index 00000000..986ae0cd --- /dev/null +++ b/mp/src/responserules/runtime/rr_convars.cpp @@ -0,0 +1,14 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Convars used by the response rule system. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" +#include + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + diff --git a/mp/src/responserules/runtime/rr_response.cpp b/mp/src/responserules/runtime/rr_response.cpp new file mode 100644 index 00000000..b652e8b3 --- /dev/null +++ b/mp/src/responserules/runtime/rr_response.cpp @@ -0,0 +1,387 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "rrbase.h" + +#include +#include "tier1/mapbase_con_groups.h" + +/* +#include "AI_Criteria.h" +#include "ai_speech.h" +#include +#include "engine/IEngineSound.h" +*/ + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace ResponseRules; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRR_Response::CRR_Response() : m_fMatchScore(0) +{ + m_Type = ResponseRules::RESPONSE_NONE; + m_szResponseName[0] = 0; + m_szMatchingRule[0]=0; + m_szContext = NULL; +#ifdef MAPBASE + m_iContextFlags = 0; +#else + m_bApplyContextToWorld = false; +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CRR_Response::CRR_Response( const CRR_Response &from ) : m_fMatchScore(0) +{ + // Assert( (void*)(&m_Type) == (void*)this ); + Invalidate(); + memcpy( this, &from, sizeof(*this) ); + m_szContext = NULL; + SetContext( from.m_szContext ); +#ifdef MAPBASE + m_iContextFlags = from.m_iContextFlags; +#else + m_bApplyContextToWorld = from.m_bApplyContextToWorld; +#endif +} + + +//----------------------------------------------------------------------------- +CRR_Response &CRR_Response::operator=( const CRR_Response &from ) +{ + // Assert( (void*)(&m_Type) == (void*)this ); + Invalidate(); + memcpy( this, &from, sizeof(*this) ); + m_szContext = NULL; + SetContext( from.m_szContext ); +#ifdef MAPBASE + m_iContextFlags = from.m_iContextFlags; +#else + m_bApplyContextToWorld = from.m_bApplyContextToWorld; +#endif + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRR_Response::~CRR_Response() +{ + if (m_szContext) + delete[] m_szContext; +} + +void CRR_Response::Invalidate() +{ + if (m_szContext) + { + delete[] m_szContext; + m_szContext = NULL; + } + m_Type = ResponseRules::RESPONSE_NONE; + m_szResponseName[0] = 0; + // not really necessary: + /* + m_szMatchingRule[0]=0; + m_szContext = NULL; + m_bApplyContextToWorld = false; + */ +} + +// please do not new or delete CRR_Responses. +void CRR_Response::operator delete(void* p) +{ + AssertMsg(false, "DO NOT new or delete CRR_Response s."); + free(p); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *response - +// *criteria - +//----------------------------------------------------------------------------- +void CRR_Response::Init( ResponseType_t type, const char *responseName, const ResponseParams& responseparams, const char *ruleName, const char *applyContext, bool bApplyContextToWorld ) +{ + m_Type = type; + Q_strncpy( m_szResponseName, responseName, sizeof( m_szResponseName ) ); + // Copy underlying criteria + Q_strncpy( m_szMatchingRule, ruleName ? ruleName : "NULL", sizeof( m_szMatchingRule ) ); + m_Params = responseparams; + SetContext( applyContext ); +#ifdef MAPBASE + bApplyContextToWorld ? m_iContextFlags = APPLYCONTEXT_WORLD : m_iContextFlags = 0; +#else + m_bApplyContextToWorld = bApplyContextToWorld; +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : *response - +// *criteria - +//----------------------------------------------------------------------------- +void CRR_Response::Init( ResponseType_t type, const char *responseName, const ResponseParams& responseparams, const char *ruleName, const char *applyContext, int iContextFlags ) +{ + m_Type = type; + Q_strncpy( m_szResponseName, responseName, sizeof( m_szResponseName ) ); + // Copy underlying criteria + Q_strncpy( m_szMatchingRule, ruleName ? ruleName : "NULL", sizeof( m_szMatchingRule ) ); + m_Params = responseparams; + SetContext( applyContext ); + m_iContextFlags = iContextFlags; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Debug-print the response. You can optionally pass in the criteria +// used to come up with this response (usually present in the calling function) +// if you want to print that as well. DO NOT store the entire criteria set in +// CRR_Response just to make this debug print cleaner. +//----------------------------------------------------------------------------- +void CRR_Response::Describe( const CriteriaSet *pDebugCriteria ) +{ + if ( pDebugCriteria ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Search criteria:\n" ); + pDebugCriteria->Describe(); + } + + if ( m_szMatchingRule[ 0 ] ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Matched rule '%s', ", m_szMatchingRule ); + } + if ( m_szContext ) + { +#ifdef MAPBASE + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Contexts to set '%s' on ", m_szContext ); + if (m_iContextFlags & APPLYCONTEXT_WORLD) + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "world, " ); + else if (m_iContextFlags & APPLYCONTEXT_SQUAD) + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "squad, " ); + else if (m_iContextFlags & APPLYCONTEXT_ENEMY) + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "enemy, " ); + else + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "speaker, " ); +#else + DevMsg( "Contexts to set '%s' on %s, ", m_szContext, m_bApplyContextToWorld ? "world" : "speaker" ); +#endif + } + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "response %s = '%s'\n", DescribeResponse( (ResponseType_t)m_Type ), m_szResponseName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CRR_Response::GetName( char *buf, size_t buflen ) const +{ + Q_strncpy( buf, m_szResponseName, buflen ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CRR_Response::GetResponse( char *buf, size_t buflen ) const +{ + GetName( buf, buflen ); +} + + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CRR_Response::GetRule( char *buf, size_t buflen ) const +{ + Q_strncpy( buf, m_szMatchingRule, buflen ); +} +#endif + + +const char* ResponseRules::CRR_Response::GetNamePtr() const +{ + return m_szResponseName; +} +const char* ResponseRules::CRR_Response::GetResponsePtr() const +{ + return m_szResponseName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : char const +//----------------------------------------------------------------------------- +const char *CRR_Response::DescribeResponse( ResponseType_t type ) +{ + if ( (int)type < 0 || (int)type >= ResponseRules::NUM_RESPONSES ) + { + Assert( 0 ); + return "???CRR_Response bogus index"; + } + + switch( type ) + { + default: + { + Assert( 0 ); + } + // Fall through + case ResponseRules::RESPONSE_NONE: + return "RESPONSE_NONE"; + case ResponseRules::RESPONSE_SPEAK: + return "RESPONSE_SPEAK"; + case ResponseRules::RESPONSE_SENTENCE: + return "RESPONSE_SENTENCE"; + case ResponseRules::RESPONSE_SCENE: + return "RESPONSE_SCENE"; + case ResponseRules::RESPONSE_RESPONSE: + return "RESPONSE_RESPONSE"; + case ResponseRules::RESPONSE_PRINT: + return "RESPONSE_PRINT"; + case ResponseRules::RESPONSE_ENTITYIO: + return "RESPONSE_ENTITYIO"; +#ifdef MAPBASE + case ResponseRules::RESPONSE_VSCRIPT: + return "RESPONSE_VSCRIPT"; + case ResponseRules::RESPONSE_VSCRIPT_FILE: + return "RESPONSE_VSCRIPT_FILE"; +#endif + } + + return "RESPONSE_NONE"; +} + +/* +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRR_Response::Release() +{ + delete this; +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: +// Output : soundlevel_t +//----------------------------------------------------------------------------- +soundlevel_t CRR_Response::GetSoundLevel() const +{ + if ( m_Params.flags & ResponseParams::RG_SOUNDLEVEL ) + { + return (soundlevel_t)m_Params.soundlevel; + } + + return SNDLVL_TALKING; +} + +float CRR_Response::GetRespeakDelay( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_RESPEAKDELAY ) + { + interval_t temp; + m_Params.respeakdelay.ToInterval( temp ); + return RandomInterval( temp ); + } + + return 0.0f; +} + +float CRR_Response::GetWeaponDelay( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_WEAPONDELAY ) + { + interval_t temp; + m_Params.weapondelay.ToInterval( temp ); + return RandomInterval( temp ); + } + + return 0.0f; +} + +bool CRR_Response::GetSpeakOnce( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_SPEAKONCE ) + { + return true; + } + + return false; +} + +bool CRR_Response::ShouldntUseScene( void ) const +{ + return ( m_Params.flags & ResponseParams::RG_DONT_USE_SCENE ) != 0; +} + +bool CRR_Response::ShouldBreakOnNonIdle( void ) const +{ + return ( m_Params.flags & ResponseParams::RG_STOP_ON_NONIDLE ) != 0; +} + +int CRR_Response::GetOdds( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_ODDS ) + { + return m_Params.odds; + } + return 100; +} + +float CRR_Response::GetDelay() const +{ + if ( m_Params.flags & ResponseParams::RG_DELAYAFTERSPEAK ) + { + interval_t temp; + m_Params.delay.ToInterval( temp ); + return RandomInterval( temp ); + } + return 0.0f; +} + +float CRR_Response::GetPreDelay() const +{ + if ( m_Params.flags & ResponseParams::RG_DELAYBEFORESPEAK ) + { + interval_t temp; + m_Params.predelay.ToInterval( temp ); + return RandomInterval( temp ); + } + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets context string +// Output : void +//----------------------------------------------------------------------------- +void CRR_Response::SetContext( const char *context ) +{ + if (m_szContext) + { + delete[] m_szContext; + m_szContext = NULL; + } + + if ( context ) + { + int len = Q_strlen( context ); + m_szContext = new char[ len + 1 ]; + Q_memcpy( m_szContext, context, len ); + m_szContext[ len ] = 0; + } +} diff --git a/mp/src/responserules/runtime/rr_speechconcept.cpp b/mp/src/responserules/runtime/rr_speechconcept.cpp new file mode 100644 index 00000000..7e1e04ab --- /dev/null +++ b/mp/src/responserules/runtime/rr_speechconcept.cpp @@ -0,0 +1,73 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#if RR_CONCEPTS_ARE_STRINGS +#pragma error("RR_CONCEPTS_ARE_STRINGS no longer supported") +#else + +using namespace ResponseRules; + +// Used to turn ad-hoc concept from strings into numbers. +CRR_ConceptSymbolTable *g_pRRConceptTable = NULL; + +// Q&D hack to defer initialization of concept table until I can figure out where it +// really needs to come from. +static void InitializeRRConceptTable() +{ + if (g_pRRConceptTable == NULL) + { + g_pRRConceptTable = new CRR_ConceptSymbolTable( 64, 64, true ); + } +} + +// construct from string +CRR_Concept::CRR_Concept(const char *fromString) +{ + InitializeRRConceptTable(); + m_iConcept = g_pRRConceptTable->AddString(fromString); +} + +CRR_Concept &CRR_Concept::operator=(const char *fromString) +{ + InitializeRRConceptTable(); + m_iConcept = g_pRRConceptTable->AddString(fromString); + return *this; +} + +bool CRR_Concept::operator==(const char *pszConcept) +{ + int otherConcept = g_pRRConceptTable->Find(pszConcept); + return ( otherConcept != UTL_INVAL_SYMBOL && otherConcept == m_iConcept ); +} + +const char *CRR_Concept::GetStringConcept() const +{ + InitializeRRConceptTable(); + AssertMsg( m_iConcept.IsValid(), "AI Concept has invalid string symbol.\n" ); + const char * retval = g_pRRConceptTable->String(m_iConcept); + AssertMsg( retval, "An RR_Concept couldn't find its string in the symbol table!\n" ); + if (retval == NULL) + { + Warning( "An RR_Concept couldn't find its string in the symbol table!\n" ); + retval = ""; + } + return retval; +} + +const char *CRR_Concept::GetStringForGenericId(tGenericId genericId) +{ + InitializeRRConceptTable(); + return g_pRRConceptTable->String(genericId); +} + +#endif diff --git a/mp/src/responserules/runtime/rrbase.h b/mp/src/responserules/runtime/rrbase.h new file mode 100644 index 00000000..e7af74de --- /dev/null +++ b/mp/src/responserules/runtime/rrbase.h @@ -0,0 +1,59 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RRBASE_H +#define RRBASE_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _WIN32 +// Silence certain warnings +// #pragma warning(disable : 4244) // int or float down-conversion +// #pragma warning(disable : 4305) // int or float data truncation +// #pragma warning(disable : 4201) // nameless struct/union +// #pragma warning(disable : 4511) // copy constructor could not be generated +// #pragma warning(disable : 4675) // resolved overload was found by argument dependent lookup +#endif + +#ifdef _DEBUG +#define DEBUG 1 +#endif + +// Misc C-runtime library headers +#include +#include +#include + +// tier 0 +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "basetypes.h" + +// tier 1 +#include "tier1/strtools.h" +#include "utlvector.h" +#include "utlsymbol.h" + +// tier 2 +#include "string_t.h" + +// Shared engine/DLL constants +#include "const.h" +#include "edict.h" + +// app +#if defined(_X360) +#define DISABLE_DEBUG_HISTORY 1 +#endif + +#include "responserules/response_types.h" +#include "response_types_internal.h" +#include "responserules/response_host_interface.h" + + +#endif // CBASE_H diff --git a/mp/src/responserules/runtime/rrrlib.cpp b/mp/src/responserules/runtime/rrrlib.cpp new file mode 100644 index 00000000..0d2c49fd --- /dev/null +++ b/mp/src/responserules/runtime/rrrlib.cpp @@ -0,0 +1,13 @@ +/// PLACEHOLDER FILE FOR RESPONSE RULES RUNTIME LIBRARY + +#include "rrbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +namespace ResponseRules +{ + /// Custom symbol table for the response rules. + CUtlSymbolTable g_RS; +}; \ No newline at end of file diff --git a/mp/src/responserules/runtime/stdafx.cpp b/mp/src/responserules/runtime/stdafx.cpp new file mode 100644 index 00000000..4199359f --- /dev/null +++ b/mp/src/responserules/runtime/stdafx.cpp @@ -0,0 +1,11 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Builds the precompiled header for the game DLL +// +// $NoKeywords: $ +//=============================================================================// + + +#include "rrbase.h" + +// NOTE: DO NOT ADD ANY CODE OR HEADERS TO THIS FILE!!! diff --git a/mp/src/tier1/KeyValues.cpp b/mp/src/tier1/KeyValues.cpp index ad4af096..67477dca 100644 --- a/mp/src/tier1/KeyValues.cpp +++ b/mp/src/tier1/KeyValues.cpp @@ -30,6 +30,9 @@ #include "utlqueue.h" #include "UtlSortVector.h" #include "convar.h" +#ifdef MAPBASE +#include "icommandline.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include @@ -2209,6 +2212,28 @@ bool EvaluateConditional( const char *str ) if ( Q_stristr( str, "$POSIX" ) ) return IsPosix() ^ bNot; + +#ifdef MAPBASE + // Custom conditional + switch( str[bNot ? 1 : 0] ) + { + case '%': + { + // Look for a cvar + ConVarRef cvar( str + (bNot ? 2 : 1), true ); + if (cvar.IsValid()) + { + return cvar.GetBool() ^ bNot; + } + } break; + + case '-': + { + // Look for a command line param + return (CommandLine()->CheckParm( bNot ? str+1 : str ) != 0) ^ bNot; + } break; + } +#endif return false; } diff --git a/mp/src/tier1/bitbuf.cpp b/mp/src/tier1/bitbuf.cpp index 52a25e4d..e723bae9 100644 --- a/mp/src/tier1/bitbuf.cpp +++ b/mp/src/tier1/bitbuf.cpp @@ -1357,13 +1357,13 @@ int64 bf_read::ReadLongLong() float bf_read::ReadFloat() { - float ret; - Assert( sizeof(ret) == 4 ); - ReadBits(&ret, 32); + float retLocl; + Assert( sizeof(retLocl) == 4 ); + ReadBits(&retLocl, 32); // Swap the float, since ReadBits reads raw data - LittleFloat( &ret, &ret ); - return ret; + LittleFloat( &retLocl, &retLocl ); + return retLocl; } bool bf_read::ReadBytes(void *pOut, int nBytes) diff --git a/mp/src/tier1/convar.cpp b/mp/src/tier1/convar.cpp index 772586f2..28c7fa29 100644 --- a/mp/src/tier1/convar.cpp +++ b/mp/src/tier1/convar.cpp @@ -686,7 +686,10 @@ ConVar::~ConVar( void ) //----------------------------------------------------------------------------- void ConVar::InstallChangeCallback( FnChangeCallback_t callback ) { +#ifndef MAPBASE_VSCRIPT Assert( !m_pParent->m_fnChangeCallback || !callback ); +#endif + m_pParent->m_fnChangeCallback = callback; if ( m_pParent->m_fnChangeCallback ) diff --git a/mp/src/tier1/interval.cpp b/mp/src/tier1/interval.cpp new file mode 100644 index 00000000..38c90359 --- /dev/null +++ b/mp/src/tier1/interval.cpp @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier0/platform.h" +#include "interval.h" +#include "tier1/strtools.h" +#include "vstdlib/random.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pString - +// Output : interval_t +//----------------------------------------------------------------------------- +interval_t ReadInterval( const char *pString ) +{ + interval_t tmp; + + tmp.start = 0; + tmp.range = 0; + + char tempString[128]; + Q_strncpy( tempString, pString, sizeof(tempString) ); + + char *token = strtok( tempString, "," ); + if ( token ) + { + tmp.start = atof( token ); + token = strtok( NULL, "," ); + if ( token ) + { + tmp.range = atof( token ) - tmp.start; + } + } + + return tmp; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &interval - +// Output : float +//----------------------------------------------------------------------------- +float RandomInterval( const interval_t &interval ) +{ + float out = interval.start; + if ( interval.range != 0 ) + { + out += RandomFloat( 0, interval.range ); + } + + return out; +} diff --git a/mp/src/tier1/mapbase_con_groups.cpp b/mp/src/tier1/mapbase_con_groups.cpp index 533d5791..5f23b3f8 100644 --- a/mp/src/tier1/mapbase_con_groups.cpp +++ b/mp/src/tier1/mapbase_con_groups.cpp @@ -11,142 +11,157 @@ #include #include #include "basetypes.h" -#include "tier1/tier1.h" -#include "tier1/utldict.h" +#include "tier1.h" +#include "utldict.h" #include "Color.h" -#include "tier1/mapbase_con_groups.h" - -void CV_ColorChanged( IConVar *pConVar, const char *pOldString, float flOldValue ); +#include "mapbase_con_groups.h" +#include "KeyValues.h" +#include "filesystem.h" +#include "mapbase_matchers_base.h" struct ConGroup_t { - ConGroup_t( const char *_pszName, ConVar *pCvar ) + ConGroup_t( const char *_pszName, const char *_pszDescription ) { pszName = _pszName; - cvar = pCvar; + pszDescription = _pszDescription; + _clr.SetColor( 224, 224, 224, 255 ); // Default to a shade of gray } 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; + const char *pszDescription; Color _clr; - ConVar *cvar; - //bool bDisabled; + 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." ); +// TODO: Something more reliable? +static bool g_bIncludeConGroupNames = false; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -#define DEFINE_CON_GROUP(name, codename) { name, &con_group_##codename##_color } +//#define DEFINE_CON_GROUP(id, name, codename) { name, &con_group_##codename##_color } +#define DEFINE_CON_GROUP(id, name, description) { name, description } -ConGroup_t g_ConGroups[] = { +ConGroup_t g_ConGroups[CON_GROUP_MAX] = { // General - DEFINE_CON_GROUP( CON_GROUP_MAPBASE_MISC, mapbase_misc ), - DEFINE_CON_GROUP( CON_GROUP_PHYSICS, physics ), + DEFINE_CON_GROUP( CON_GROUP_MAPBASE_MISC, "Mapbase misc.", "Messages from misc. Mapbase functions, like map-specific files." ), + DEFINE_CON_GROUP( CON_GROUP_PHYSICS, "Physics", "Messages from physics-related events." ), + DEFINE_CON_GROUP( CON_GROUP_IO_SYSTEM, "Entity IO", "Messages from I/O events. (these display in developer 2)" ), + DEFINE_CON_GROUP( CON_GROUP_RESPONSE_SYSTEM, "Response System", "Messages from the Response System, a library primarily used for NPC speech." ), - // 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 ), + // Game + DEFINE_CON_GROUP( CON_GROUP_NPC_AI, "NPC AI", "Messages from NPC AI, etc. which display at various verbose levels." ), + DEFINE_CON_GROUP( CON_GROUP_NPC_SCRIPTS, "NPC scripts", "Messages from scripted_sequence, etc. (these display in developer 2)" ), + DEFINE_CON_GROUP( CON_GROUP_SPEECH_AI, "Speech AI", "Messages from response expressers. (these display in developer 1, 2, etc.)" ), + DEFINE_CON_GROUP( CON_GROUP_CHOREO, "Choreo", "Messages from choreographed scenes. (these display in developer 1, 2, etc.)" ), // VScript - DEFINE_CON_GROUP( CON_GROUP_VSCRIPT, vscript ), - DEFINE_CON_GROUP( CON_GROUP_VSCRIPT_PRINT, vscript_print ), + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT, "VScript", "Internal messages from VScript not produced by actual scripts." ), + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT_PRINT, "VScript print", "Messages from VScript's 'print' function." ), }; -void CV_ColorChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) +int FindConGroup( const char *pszName ) { - for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + for (int i = 0; i < CON_GROUP_MAX; 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]; + if (Q_stricmp( pszName, g_ConGroups[i].pszName ) == 0) + return i; } - return NULL; + return -1; } //----------------------------------------------------------------------------- +// Loads console groups //----------------------------------------------------------------------------- +void LoadConsoleGroupsFromFile( IBaseFileSystem *filesystem, const char *pszFileName, const char *pathID ) +{ + KeyValues *pGroupRoot = new KeyValues( "ConsoleGroups" ); -ConVar con_group_include_name( "con_group_include_name", "0", FCVAR_NONE, "Includes groups when printing." ); + pGroupRoot->LoadFromFile( filesystem, pszFileName, pathID ); -CON_COMMAND( con_group_list, "Prints a list of all console groups." ) + KeyValues *pGroup = NULL; + for ( pGroup = pGroupRoot->GetFirstTrueSubKey(); pGroup; pGroup = pGroup->GetNextTrueSubKey() ) + { + int index = FindConGroup( pGroup->GetName() ); + if (index != -1) + { + Color msgClr = pGroup->GetColor( "MessageColor" ); + + // Make sure the color isn't 0,0,0,0 before assigning + if (msgClr.GetRawColor() != 0) + g_ConGroups[index]._clr = msgClr; + + g_ConGroups[index].bDisabled = pGroup->GetBool( "Disabled", false ); + } + else + { + Warning( "Invalid console group %s (new groups should be defined in the code)\n", pGroup->GetName() ); + } + } + + pGroupRoot->deleteThis(); +} + +void InitConsoleGroups( IBaseFileSystem *filesystem ) +{ + LoadConsoleGroupsFromFile( filesystem, "scripts/mapbase_con_groups.txt", "MOD" ); + LoadConsoleGroupsFromFile( filesystem, "scripts/mod_con_groups.txt", "MOD" ); +} + +void PrintAllConsoleGroups() { Msg( "============================================================\n" ); - for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + for (int i = 0; i < CON_GROUP_MAX; i++) { - Msg( " # " ); - ConColorMsg( g_ConGroups[i].GetColor(), "%s", g_ConGroups[i].pszName ); - Msg( " - %s ", g_ConGroups[i].cvar->GetHelpText() ); + ConColorMsg( g_ConGroups[i].GetColor(), " # %s", g_ConGroups[i].pszName ); - //if (g_ConGroups[i].bDisabled) - // Msg("(DISABLED)"); + if (g_ConGroups[i].bDisabled) + Msg(" [DISABLED]"); + + Msg( " - %s ", g_ConGroups[i].pszDescription ); 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." ) +void ToggleConsoleGroups( const char *pszQuery ) { - const char *pszGroup = args.Arg( 1 ); + bool bMatched = false; + for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) { - if (V_stricmp(pszGroup, g_ConGroups[i].pszName) == 0) + if (Matcher_NamesMatch( pszQuery, g_ConGroups[i].pszName )) { 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; + bMatched = true; } } - Msg( "No group named \"%s\"\n", pszGroup ); + if (!bMatched) + Msg( "No groups matching \"%s\"\n", pszQuery ); } -*/ -void CGMsg( int level, const char *pszGroup, const tchar* pMsg, ... ) +void SetConsoleGroupIncludeNames( bool bToggle ) +{ + g_bIncludeConGroupNames = bToggle; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CGMsg( int level, ConGroupID_t nGroup, const tchar* pMsg, ... ) { // Return early if we're not at this level if (!IsSpewActive("developer", level)) @@ -158,22 +173,21 @@ void CGMsg( int level, const char *pszGroup, const tchar* pMsg, ... ) Q_vsnprintf( string, sizeof(string), pMsg, argptr ); va_end( argptr ); - ConGroup_t *pGroup = FindConGroup( pszGroup ); - if (pGroup) + Assert( nGroup >= 0 ); + Assert( nGroup < CON_GROUP_MAX ); + + ConGroup_t *pGroup = &g_ConGroups[nGroup]; + + if (pGroup->bDisabled) { - /*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 ); - } + // Do nothing + } + else if (g_bIncludeConGroupNames) + { + ConColorMsg(level, pGroup->GetColor(), "[%s] %s", pGroup->pszName, string); } else - DevMsg( level, string ); + { + ConColorMsg(level, pGroup->GetColor(), "%s", string); + } } diff --git a/mp/src/tier1/strtools.cpp b/mp/src/tier1/strtools.cpp index 2c5f72fa..bb2558ab 100644 --- a/mp/src/tier1/strtools.cpp +++ b/mp/src/tier1/strtools.cpp @@ -1411,7 +1411,7 @@ int _V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInByt size_t nMaxUTF8 = cubDestSizeInBytes; char *pIn = (char *)pUCS2; char *pOut = (char *)pUnicode; - if ( conv_t > 0 ) + if ( conv_t ) { cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); iconv_close( conv_t ); @@ -1451,7 +1451,7 @@ int _V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, i size_t nMaxUCS2 = cubDestSizeInBytes; char *pIn = (char*)pUnicode; char *pOut = pUCS2; - if ( conv_t > 0 ) + if ( conv_t ) { cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUCS2 ); iconv_close( conv_t ); @@ -1499,7 +1499,7 @@ int _V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes ) size_t nMaxUTF8 = cubDestSizeInBytes - 1; char *pIn = (char *)pUCS2; char *pOut = (char *)pUTF8; - if ( conv_t > 0 ) + if ( conv_t ) { const size_t nBytesToWrite = nMaxUTF8; cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); @@ -1544,7 +1544,7 @@ int _V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDes size_t nMaxUTF8 = cubDestSizeInBytes; char *pIn = (char *)pUTF8; char *pOut = (char *)pUCS2; - if ( conv_t > 0 ) + if ( conv_t ) { cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); iconv_close( conv_t ); diff --git a/mp/src/tier1/tier1.vpc b/mp/src/tier1/tier1.vpc index c9dd4e86..754bdbc3 100644 --- a/mp/src/tier1/tier1.vpc +++ b/mp/src/tier1/tier1.vpc @@ -39,6 +39,7 @@ $Project "tier1" $File "generichash.cpp" $File "ilocalize.cpp" $File "interface.cpp" + $File "interval.cpp" $File "KeyValues.cpp" $File "kvpacker.cpp" $File "lzmaDecoder.cpp" @@ -79,6 +80,7 @@ $Project "tier1" $File "snappy-sinksource.cpp" $File "snappy-stubs-internal.cpp" $File "mapbase_con_groups.cpp" [$MAPBASE] + $File "mapbase_matchers_base.cpp" [$MAPBASE] } // Select bits from the LZMA SDK to support lzmaDecoder.h @@ -161,6 +163,7 @@ $Project "tier1" $File "$SRCDIR\public\tier1\utlvector.h" $File "$SRCDIR\public\tier1\utlbinaryblock.h" $File "$SRCDIR\public\tier1\mapbase_con_groups.h" [$MAPBASE] + $File "$SRCDIR\public\tier1\mapbase_matchers_base.h" [$MAPBASE] $File "$SRCDIR\common\xbox\xboxstubs.h" [$WINDOWS] } } diff --git a/mp/src/utils/captioncompiler/captioncompiler.vpc b/mp/src/utils/captioncompiler/captioncompiler.vpc index da5a3b4e..3d1e24ad 100644 --- a/mp/src/utils/captioncompiler/captioncompiler.vpc +++ b/mp/src/utils/captioncompiler/captioncompiler.vpc @@ -14,7 +14,8 @@ $Configuration $Compiler { $AdditionalIncludeDirectories "$BASE,..\common,$SRCDIR\game\shared,.\" - $PreprocessorDefinitions "$BASE;captioncompiler" + $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE;captioncompiler" [($VS2015||$VS2017||$VS2019||$VS2022)] + $PreprocessorDefinitions "$BASE;captioncompiler" [!($VS2015||$VS2017||$VS2019||$VS2022)] } } diff --git a/mp/src/utils/vbsp/map.cpp b/mp/src/utils/vbsp/map.cpp index f59305ba..c479b279 100644 --- a/mp/src/utils/vbsp/map.cpp +++ b/mp/src/utils/vbsp/map.cpp @@ -2105,7 +2105,12 @@ void CMapFile::CheckForInstances( const char *pszFileName ) } char FDGPath[ MAX_PATH ]; +#ifdef MAPBASE + // Mapbase's FGD would be in a MOD path + if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "MOD", FDGPath, sizeof( FDGPath ) ) ) +#else if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) ) +#endif { if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, NULL, FDGPath, sizeof( FDGPath ) ) ) { @@ -2598,6 +2603,27 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec SetKeyValue( entity, szKey, epInstance->value ); } } + + // If the parent instance is within a relative path and no file relative to the main map exists, change it to be relative to the parent + char *pParentInstanceFile = ValueForKey( pInstanceEntity, "file" ); + if ( pParentInstanceFile[ 0 ] && (strchr( pParentInstanceFile, '\\' ) || strchr( pParentInstanceFile, '/' )) ) + { + char *pInstanceFile = ValueForKey( entity, "file" ); + if ( pInstanceFile[ 0 ] ) + { + char InstancePath[ MAX_PATH ]; + + if ( !DeterminePath( g_MainMapPath, pInstanceFile, InstancePath ) ) + { + strcpy( InstancePath, pParentInstanceFile ); + V_StripFilename( InstancePath ); + V_strncat( InstancePath, "\\", sizeof( InstancePath ) ); + V_strncat( InstancePath, pInstanceFile, sizeof( InstancePath ) ); + + SetKeyValue( entity, "file", InstancePath ); + } + } + } } #endif } @@ -2606,7 +2632,7 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i ); Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush ); Msg( " KV Pairs:\n" ); - for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next ) + for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next ) { Msg( " %s %s\n", ep->key, ep->value ); } @@ -2801,14 +2827,12 @@ bool LoadMapFile( const char *pszFileName ) #ifdef MAPBASE_VSCRIPT if ( g_pScriptVM ) { - HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnMapLoaded" ); - if ( hFunc ) + if (CMapFile::g_Hook_OnMapLoaded.CanRunInScope( NULL )) { // Use GetLoadingMap() //g_pScriptVM->SetValue( "map", g_LoadingMap->GetScriptInstance() ); - g_pScriptVM->Call( hFunc ); - g_pScriptVM->ReleaseFunction( hFunc ); + CMapFile::g_Hook_OnMapLoaded.Call( NULL, NULL, NULL ); //g_pScriptVM->ClearValue( "map" ); } diff --git a/mp/src/utils/vbsp/staticprop.cpp b/mp/src/utils/vbsp/staticprop.cpp index 15c354d3..3e59010d 100644 --- a/mp/src/utils/vbsp/staticprop.cpp +++ b/mp/src/utils/vbsp/staticprop.cpp @@ -571,6 +571,10 @@ static void SetLumpData( ) void EmitStaticProps() { +#ifdef MAPBASE + Msg("Placing static props...\n"); +#endif + CreateInterfaceFn physicsFactory = GetPhysicsFactory(); if ( physicsFactory ) { @@ -594,13 +598,43 @@ void EmitStaticProps() for ( i = 0; i < num_entities; ++i) { char* pEntity = ValueForKey(&entities[i], "classname"); +#ifdef MAPBASE + const int iInsertAsStatic = IntForKey( &entities[i], "insertasstaticprop" ); // If the key is absent, IntForKey will return 0. + bool bInsertAsStatic = g_bPropperInsertAllAsStatic; + + // 1 = No, 2 = Yes; Any other number will just use what g_bPropperInsertAllAsStatic is set as. + if ( iInsertAsStatic == 1 ) { bInsertAsStatic = false; } + else if ( iInsertAsStatic == 2 ) { bInsertAsStatic = true; } + + if ( !strcmp( pEntity, "static_prop" ) || !strcmp( pEntity, "prop_static" ) || ( !strcmp( pEntity, "propper_model" ) && bInsertAsStatic ) ) +#else if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static")) +#endif { StaticPropBuild_t build; GetVectorForKey( &entities[i], "origin", build.m_Origin ); GetAnglesForKey( &entities[i], "angles", build.m_Angles ); +#ifdef MAPBASE + if ( !strcmp( pEntity, "propper_model" ) ) + { + char* pModelName = ValueForKey( &entities[i], "modelname" ); + + // The modelname keyvalue lacks 'models/' at the start and '.mdl' at the end, so we have to add them. + char modelpath[MAX_VALUE]; + sprintf( modelpath, "models/%s.mdl", pModelName ); + + Msg( "Inserting propper_model (%.0f %.0f %.0f) as prop_static: %s\n", build.m_Origin[0], build.m_Origin[1], build.m_Origin[2], modelpath ); + + build.m_pModelName = modelpath; + } + else // Otherwise we just assume it's a normal prop_static + { + build.m_pModelName = ValueForKey( &entities[i], "model" ); + } +#else build.m_pModelName = ValueForKey( &entities[i], "model" ); +#endif build.m_Solid = IntForKey( &entities[i], "solid" ); build.m_Skin = IntForKey( &entities[i], "skin" ); build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" ); @@ -669,6 +703,13 @@ void EmitStaticProps() // strip this ent from the .bsp file entities[i].epairs = 0; } +#ifdef MAPBASE + else if ( g_bPropperStripEntities && !strncmp( pEntity, "propper_", 8 ) ) // Strip out any entities with 'propper_' in their classname, as they don't actually exist in-game. + { + Warning( "Not including %s in BSP compile due to it being a propper entity that isn't used in-game.\n", pEntity ); + entities[i].epairs = 0; + } +#endif } // Strip out lighting origins; has to be done here because they are used when diff --git a/mp/src/utils/vbsp/vbsp.cpp b/mp/src/utils/vbsp/vbsp.cpp index 724b8a09..a478fcd0 100644 --- a/mp/src/utils/vbsp/vbsp.cpp +++ b/mp/src/utils/vbsp/vbsp.cpp @@ -69,6 +69,8 @@ bool g_bNoHiddenManifestMaps = false; #ifdef MAPBASE bool g_bNoDefaultCubemaps = true; bool g_bSkyboxCubemaps = false; +bool g_bPropperInsertAllAsStatic = false; +bool g_bPropperStripEntities = false; int g_iDefaultCubemapSize = 32; #endif #ifdef MAPBASE_VSCRIPT @@ -1214,6 +1216,14 @@ int RunVBSP( int argc, char **argv ) Msg( "Default cubemap size = %i\n", g_iDefaultCubemapSize ); i++; } + else if ( !Q_stricmp( argv[i], "-defaultproppermodelsstatic" ) ) + { + g_bPropperInsertAllAsStatic = true; + } + else if ( !Q_stricmp( argv[i], "-strippropperentities" ) ) + { + g_bPropperStripEntities = true; + } #endif #ifdef MAPBASE_VSCRIPT else if ( !Q_stricmp( argv[i], "-scripting" ) ) @@ -1268,7 +1278,7 @@ int RunVBSP( int argc, char **argv ) } char szCommand[512]; - _snprintf( szCommand, sizeof( szCommand ), "PrintHelp( \"%s\" );", pszArg1 ); + _snprintf( szCommand, sizeof( szCommand ), "__Documentation.PrintHelp( \"%s\" );", pszArg1 ); g_pScriptVM->Run( szCommand ); } else diff --git a/mp/src/utils/vbsp/vbsp.h b/mp/src/utils/vbsp/vbsp.h index 3e9f44c0..9147876a 100644 --- a/mp/src/utils/vbsp/vbsp.h +++ b/mp/src/utils/vbsp/vbsp.h @@ -347,6 +347,8 @@ public: #ifdef MAPBASE_VSCRIPT HSCRIPT GetScriptInstance(); + static ScriptHook_t g_Hook_OnMapLoaded; + // VScript functions ALLOW_SCRIPT_ACCESS(); private: @@ -401,6 +403,10 @@ extern bool g_DisableWaterLighting; extern bool g_bAllowDetailCracks; extern bool g_bNoVirtualMesh; extern bool g_bNoHiddenManifestMaps; +#ifdef MAPBASE +extern bool g_bPropperInsertAllAsStatic; +extern bool g_bPropperStripEntities; +#endif extern char outbase[32]; extern char source[1024]; diff --git a/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp b/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp index 3894ddf3..8ef38c81 100644 --- a/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp +++ b/mp/src/utils/vbsp/vscript_funcs_vmfs.cpp @@ -83,6 +83,7 @@ static HSCRIPT VMFKV_LoadFromFile( const char *szFile ) KeyValues *pKV = new KeyValues( szFile ); if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, NULL ) ) { + pKV->deleteThis(); return NULL; } diff --git a/mp/src/utils/vbsp/vscript_vbsp.cpp b/mp/src/utils/vbsp/vscript_vbsp.cpp index 91fa41f6..bcb94845 100644 --- a/mp/src/utils/vbsp/vscript_vbsp.cpp +++ b/mp/src/utils/vbsp/vscript_vbsp.cpp @@ -11,6 +11,7 @@ #include "vbsp.h" #include "map.h" #include "fgdlib/fgdlib.h" +#include "convar.h" #include "vscript_vbsp.h" #include "vscript_vbsp.nut" @@ -42,6 +43,9 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); extern int vscript_token; int vscript_token_hack = vscript_token; +// HACKHACK: VScript library relies on developer convar existing +ConVar developer( "developer", "1", 0, "Set developer message level." ); // developer mode + HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) { if ( !g_pScriptVM ) @@ -165,6 +169,8 @@ bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMiss return bSuccess; } +ScriptHook_t CMapFile::g_Hook_OnMapLoaded; + BEGIN_SCRIPTDESC_ROOT( CMapFile, "Map file" ) DEFINE_SCRIPTFUNC( GetMins, "Get the map's mins." ) @@ -181,8 +187,35 @@ BEGIN_SCRIPTDESC_ROOT( CMapFile, "Map file" ) DEFINE_SCRIPTFUNC( GetNumEntities, "Get the number of entities in the map." ) + // + // Hooks + // + DEFINE_SIMPLE_SCRIPTHOOK( CMapFile::g_Hook_OnMapLoaded, "OnMapLoaded", FIELD_VOID, "Called when the NPC is deciding whether to hear a CSound or not." ) + END_SCRIPTDESC(); + +static float cvar_getf( const char* sz ) +{ + ConVarRef cvar(sz); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetFloat(); +} + +static bool cvar_setf( const char* sz, float val ) +{ + ConVarRef cvar(sz); + if ( !cvar.IsValid() ) + return false; + + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return false; + + cvar.SetValue(val); + return true; +} + static const char *GetSource() { return source; @@ -244,6 +277,9 @@ bool VScriptVBSPInit() { Log( "VSCRIPT VBSP: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + ScriptRegisterFunction( g_pScriptVM, cvar_getf, "Gets the value of the given cvar, as a float." ); + ScriptRegisterFunction( g_pScriptVM, cvar_setf, "Sets the value of the given cvar, as a float." ); + 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." ); diff --git a/mp/src/vgui2/vgui_controls/AnimationController.cpp b/mp/src/vgui2/vgui_controls/AnimationController.cpp index b8780a89..a0f1e819 100644 --- a/mp/src/vgui2/vgui_controls/AnimationController.cpp +++ b/mp/src/vgui2/vgui_controls/AnimationController.cpp @@ -31,6 +31,11 @@ using namespace vgui; static CUtlSymbolTable g_ScriptSymbols(0, 128, true); +#ifdef MAPBASE +// Allows animation sequences to be overridden by map-specific files +extern bool g_bUsingCustomHudAnimations; +#endif + // singleton accessor for animation controller for use by the vgui controls namespace vgui { @@ -319,18 +324,47 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) return false; } - int seqIndex; + int seqIndex = -1; UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token); - // Create a new sequence - seqIndex = m_Sequences.AddToTail(); +#ifdef MAPBASE + if (g_bUsingCustomHudAnimations) + { + // look through for the sequence + for (seqIndex = 0; seqIndex < m_Sequences.Count(); seqIndex++) + { + if (m_Sequences[seqIndex].name == nameIndex) + break; + } + + if (seqIndex >= m_Sequences.Count()) + seqIndex = -1; + else + { + // Clear some stuff + m_Sequences[seqIndex].cmdList.RemoveAll(); + } + } + + if (seqIndex == -1) +#endif + { + // Create a new sequence + seqIndex = m_Sequences.AddToTail(); + } + AnimSequence_t &seq = m_Sequences[seqIndex]; seq.name = nameIndex; seq.duration = 0.0f; // get the open brace or a conditional pMem = ParseFile(pMem, token, NULL); +#ifdef MAPBASE + // Fixes ! conditionals + if ( Q_stristr( token, "[$" ) || Q_stristr( token, "[!$" ) ) +#else if ( Q_stristr( token, "[$" ) ) +#endif { bAccepted = EvaluateConditional( token ); @@ -623,7 +657,12 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) // Look ahead one token for a conditional char *peek = ParseFile(pMem, token, NULL); +#ifdef MAPBASE + // Fixes ! conditionals + if ( Q_stristr( token, "[$" ) || Q_stristr( token, "[!$" ) ) +#else if ( Q_stristr( token, "[$" ) ) +#endif { if ( !EvaluateConditional( token ) ) { diff --git a/mp/src/vgui2/vgui_controls/Button.cpp b/mp/src/vgui2/vgui_controls/Button.cpp index cceb8043..406c3e13 100644 --- a/mp/src/vgui2/vgui_controls/Button.cpp +++ b/mp/src/vgui2/vgui_controls/Button.cpp @@ -695,12 +695,12 @@ void Button::SetMouseClickEnabled(MouseCode code,bool state) if(state) { //set bit to 1 - _mouseClickMask|=1<<((int)(code+1)); + _mouseClickMask|=MouseButtonBit(code); } else { //set bit to 0 - _mouseClickMask&=~(1<<((int)(code+1))); + _mouseClickMask&=~MouseButtonBit(code); } } @@ -709,7 +709,7 @@ void Button::SetMouseClickEnabled(MouseCode code,bool state) //----------------------------------------------------------------------------- bool Button::IsMouseClickEnabled(MouseCode code) { - if(_mouseClickMask&(1<<((int)(code+1)))) + if(_mouseClickMask&MouseButtonBit(code)) { return true; } diff --git a/mp/src/vgui2/vgui_controls/Panel.cpp b/mp/src/vgui2/vgui_controls/Panel.cpp index 5291c437..eb3ca9a8 100644 --- a/mp/src/vgui2/vgui_controls/Panel.cpp +++ b/mp/src/vgui2/vgui_controls/Panel.cpp @@ -96,6 +96,13 @@ static char *CopyString( const char *in ) return n; } +#ifdef MAPBASE +ConVar vgui_mapbase_custom_schemes( "vgui_mapbase_custom_schemes", "1" ); + +// This is used in mapbase_shared.cpp +HScheme g_iCustomClientSchemeOverride; +#endif + #ifdef STAGING_ONLY ConVar tf_strict_mouse_up_events( "tf_strict_mouse_up_events", "0", FCVAR_ARCHIVE, "Only allow Mouse-Release events to happens on panels we also Mouse-Downed in" ); #endif @@ -1687,17 +1694,31 @@ void Panel::DeletePanel() //----------------------------------------------------------------------------- HScheme Panel::GetScheme() { + HScheme iScheme; + if (m_iScheme) { - return m_iScheme; // return our internal scheme + iScheme = m_iScheme; // return our internal scheme } - - if (GetVParent()) // recurse down the heirarchy + else if (GetVParent()) // recurse down the heirarchy { - return ipanel()->GetScheme(GetVParent()); + iScheme = ipanel()->GetScheme(GetVParent()); + } + else + { + iScheme = scheme()->GetDefaultScheme(); } - return scheme()->GetDefaultScheme(); +#ifdef MAPBASE + // If a custom client scheme is available, use the custom scheme. + // TODO: Need a better way to detect that this panel actually uses ClientScheme.res + if (g_iCustomClientSchemeOverride != 0 && iScheme == scheme()->GetScheme( "ClientScheme" ) && vgui_mapbase_custom_schemes.GetBool()) + { + return g_iCustomClientSchemeOverride; + } +#endif + + return iScheme; } //----------------------------------------------------------------------------- @@ -8666,7 +8687,7 @@ void VguiPanelGetSortedChildPanelList( Panel *pParentPanel, void *pSortedPanels } } -void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ ) +void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, const char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ ) { CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); diff --git a/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp b/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp index 32df2fae..6f127558 100644 --- a/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp +++ b/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp @@ -18,7 +18,9 @@ #include #include +#if _MSC_VER < 1900 #include +#endif // memdbgon must be the last include file in a .cpp file!!! #include diff --git a/mp/src/vgui2/vgui_controls/TextImage.cpp b/mp/src/vgui2/vgui_controls/TextImage.cpp index 61532126..b6b9ff29 100644 --- a/mp/src/vgui2/vgui_controls/TextImage.cpp +++ b/mp/src/vgui2/vgui_controls/TextImage.cpp @@ -983,3 +983,117 @@ void TextImage::SetColorChangeStream( CUtlSortVector *pOutCoords, bool bIgnoreEmptyLines ) +{ + HFont font = GetFont(); + if (!_utext || font == INVALID_FONT ) + return; + + // Early out if there's no newlines in our text + if (wcschr( _utext, L'\n' ) == NULL) + return; + + if (m_bRecalculateTruncation) + { + if ( m_bWrap || m_bWrapCenter ) + { + RecalculateNewLinePositions(); + } + + RecalculateEllipsesPosition(); + } + + int lineHeight = surface()->GetFontTall( GetFont() ); + float x = 0.0f; + int y = 0; + int iIndent = 0; + + int px, py; + GetPos(px, py); + + int currentLineBreak = 0; + + if ( m_bWrapCenter && m_LineXIndent.Count() ) + { + x = m_LineXIndent[0]; + } + + for (wchar_t *wsz = _utext; *wsz != 0; wsz++) + { + wchar_t ch = wsz[0]; + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + + // check for special characters + if ( ch == '\r' || ch <= 8 ) + { + // ignore, just use \n for newlines + continue; + } + else if (ch == '\n') + { + // newline + iIndent++; + if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) + { + x = m_LineXIndent[iIndent]; + } + else + { + x = 0; + } + y += lineHeight; + + if (!bIgnoreEmptyLines || (*(wsz + 1) != 0 && wsz[1] != '\n')) + { + pOutCoords->AddToTail( y ); + } + + continue; + } + else if (ch == '&') + { + // "&&" means draw a single ampersand, single one is a shortcut character + if (wsz[1] == '&') + { + // just move on and draw the second ampersand + wsz++; + } + } + + // see if we've hit the truncated portion of the string + if (wsz == m_pwszEllipsesPosition) + { + // do nothing + } + + if (currentLineBreak != m_LineBreaks.Count()) + { + if (wsz == m_LineBreaks[currentLineBreak]) + { + // newline + iIndent++; + if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) + { + x = m_LineXIndent[iIndent]; + } + else + { + x = 0; + } + + y += lineHeight; + currentLineBreak++; + } + } + + // Underlined text wants to draw the spaces anyway + x += surface()->GetCharacterWidth(font, ch); + } +} +#endif diff --git a/mp/src/vgui2/vgui_controls/Tooltip.cpp b/mp/src/vgui2/vgui_controls/Tooltip.cpp index e18427eb..7f9738d2 100644 --- a/mp/src/vgui2/vgui_controls/Tooltip.cpp +++ b/mp/src/vgui2/vgui_controls/Tooltip.cpp @@ -6,8 +6,8 @@ // and implement another button here. //=============================================================================// -#include -#define PROTECTED_THINGS_DISABLE +//#include +//#define PROTECTED_THINGS_DISABLE #include #include diff --git a/mp/src/vgui2/vgui_controls/TreeView.cpp b/mp/src/vgui2/vgui_controls/TreeView.cpp index b7ad4d3b..c244edeb 100644 --- a/mp/src/vgui2/vgui_controls/TreeView.cpp +++ b/mp/src/vgui2/vgui_controls/TreeView.cpp @@ -418,7 +418,7 @@ class TreeNode : public Panel public: TreeNode(Panel *parent, TreeView *pTreeView); - ~TreeNode(); + virtual ~TreeNode(); void SetText(const char *pszText); void SetFont(HFont font); void SetKeyValues(KeyValues *data); diff --git a/mp/src/vpc_scripts/groups.vgc b/mp/src/vpc_scripts/groups.vgc index 0350d9c0..b7f4addd 100644 --- a/mp/src/vpc_scripts/groups.vgc +++ b/mp/src/vpc_scripts/groups.vgc @@ -23,11 +23,18 @@ $Group "game" "tier1" "vgui_controls" "vscript" + "responserules" +} + +$Group "shaderdlls" +{ + "game_shader_dx9" } $Group "shaders" { "game_shader_dx9" + "mathlib" } $Group "everything" @@ -41,6 +48,7 @@ $Group "everything" "mathlib" "motionmapper" "phonemeextractor" + "responserules" "raytrace" "qc_eyes" "server" @@ -66,3 +74,17 @@ $Group "dedicated" "tier1" } +$Group "maptools" +{ + "vbsp" + "vrad_dll" + "vrad_launcher" + "vvis_dll" + "vvis_launcher" + "fgdlib" + "mathlib" + "raytrace" + "tier1" + "vscript" +} + diff --git a/mp/src/vpc_scripts/projects.vgc b/mp/src/vpc_scripts/projects.vgc index 964683c5..4545fdbb 100644 --- a/mp/src/vpc_scripts/projects.vgc +++ b/mp/src/vpc_scripts/projects.vgc @@ -44,6 +44,11 @@ $Project "server" "game\server\server_hl2mp.vpc" [($WIN32||$POSIX) && $HL2MP] } +$Project "responserules" +{ + "responserules\runtime\response_rules.vpc" [($WINDOWS||$X360||$POSIX) && $NEW_RESPONSE_SYSTEM] +} + $Project "mathlib" { "mathlib\mathlib.vpc" [$WINDOWS||$X360||$POSIX] @@ -56,7 +61,7 @@ $Project "motionmapper" $Project "phonemeextractor" { - "utils\phonemeextractor\phonemeextractor.vpc" [$WIN32] + "utils\phonemeextractor\phonemeextractor.vpc" [$WIN32 && !($VS2015||$VS2017||$VS2019||$VS2022)] // Not currently working with newer toolsets; may be fixable with modification } $Project "raytrace" @@ -66,7 +71,7 @@ $Project "raytrace" $Project "qc_eyes" { - "utils\qc_eyes\qc_eyes.vpc" [$WIN32] + "utils\qc_eyes\qc_eyes.vpc" [$WIN32 && !($VS2015||$VS2017||$VS2019||$VS2022)] // Not currently working with newer toolsets; might be fixed by having C++ MFC for v141 build tools and/or C++ ATL for v141 build tools installed } $Project "serverplugin_empty" diff --git a/mp/src/vpc_scripts/source_base.vpc b/mp/src/vpc_scripts/source_base.vpc index ea658a5c..b70d2687 100644 --- a/mp/src/vpc_scripts/source_base.vpc +++ b/mp/src/vpc_scripts/source_base.vpc @@ -16,17 +16,14 @@ //$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" +// Mapbase functionality conditionals +$Conditional MAPBASE "1" // Equivalent to (and required for) our MAPBASE preprocessor defined below +$Conditional MAPBASE_MP "1" // Mapbase MP-specific changes +$Conditional MAPBASE_RPC "0" // Toggles Mapbase's Rich Presence Client implementations (requires discord-rpc.dll in game bin) +$Conditional MAPBASE_VSCRIPT "1" // Toggles VScript implementation (note: interfaces still exist, just the provided implementation is not present) +$Conditional NEW_RESPONSE_SYSTEM "1" // Toggles the new Response System library based on the Alien Swarm SDK -// 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" diff --git a/mp/src/vpc_scripts/source_dll_win32_base.vpc b/mp/src/vpc_scripts/source_dll_win32_base.vpc index 4b2a2852..fdac37d0 100644 --- a/mp/src/vpc_scripts/source_dll_win32_base.vpc +++ b/mp/src/vpc_scripts/source_dll_win32_base.vpc @@ -39,12 +39,14 @@ $Configuration $Compiler [$WIN32] { - $EnableEnhancedInstructionSet "Streaming SIMD Extensions (/arch:SSE)" + $EnableEnhancedInstructionSet "Streaming SIMD Extensions (/arch:SSE)" [!($VS2015||$VS2017||$VS2019||$VS2022)] + $EnableEnhancedInstructionSet "Streaming SIMD Extensions 2 (/arch:SSE2)" [($VS2015||$VS2017||$VS2019||$VS2022)] } $Linker { - $AdditionalDependencies "$BASE shell32.lib user32.lib advapi32.lib gdi32.lib comdlg32.lib ole32.lib" [$WIN32||$WIN64] + $AdditionalDependencies "$BASE shell32.lib user32.lib advapi32.lib gdi32.lib comdlg32.lib ole32.lib" [($WIN32||$WIN64) && !($VS2015||$VS2017||$VS2019||$VS2022)] + $AdditionalDependencies "$BASE shell32.lib user32.lib advapi32.lib gdi32.lib comdlg32.lib ole32.lib legacy_stdio_definitions.lib" [($WIN32||$WIN64) && ($VS2015||$VS2017||$VS2019||$VS2022)] $TargetMachine "MachineX86 (/MACHINE:X86)" [$WIN32] $TargetMachine "MachineX64 (/MACHINE:X64)" [$WIN64] // Suppress this pointless warning using the undocumented /ignore linker switch @@ -69,7 +71,7 @@ $Configuration "if ERRORLEVEL 1 goto BuildEventFailed" "\n" \ "if exist $QUOTE$(TargetDir)$(TargetName).map$QUOTE copy $QUOTE$(TargetDir)$(TargetName).map$QUOTE $OUTBINDIR\$(TargetName).map" "\n" $CommandLine "$BASE" "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $QUOTE$OUTBINDIR\$(TargetName).pdb$QUOTE $SRCDIR" "\n" [!$SOURCESDK] - $CommandLine "$BASE" "copy $QUOTE$(TargetDir)$(TargetName).pdb$QUOTE $OUTBINDIR\$(TargetName).pdb" "\n" \ + $CommandLine "$BASE" "copy $QUOTE$(TargetDir)$(TargetName).pdb$QUOTE $QUOTE$OUTBINDIR\$(TargetName).pdb$QUOTE" "\n" \ "if ERRORLEVEL 1 goto BuildEventFailed" "\n" \ "goto BuildEventOK" "\n" \ ":BuildEventFailed" "\n" \ @@ -114,7 +116,8 @@ $Project $CustomBuildStep { // General - $CommandLine "$QUOTE$(VCInstallDir)bin\ml.exe$QUOTE /safeseh /c /Cp /Zi /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE" + $CommandLine "$QUOTE$(VCInstallDir)bin\ml.exe$QUOTE /safeseh /c /Cp /Zi /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE" [!($VS2015||$VS2017||$VS2019||$VS2022)] + $CommandLine "$QUOTE$(VC_ExecutablePath_x64_x86)\ml.exe$QUOTE /safeseh /c /Cp /Zi /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE" [($VS2015||$VS2017||$VS2019||$VS2022)] $Description "Compiling pointeroverride.asm" $Outputs "$(IntDir)\$(InputName).obj" } @@ -138,9 +141,6 @@ $Project $Implib "$LIBPUBLIC\tier0" $Lib "$LIBPUBLIC\tier1" $Implib "$LIBPUBLIC\vstdlib" - - // Discord integration - $Lib "$LIBPUBLIC\discord-rpc" [$MAPBASE_RPC] } } diff --git a/mp/src/vpc_scripts/source_exe_win_win32_base.vpc b/mp/src/vpc_scripts/source_exe_win_win32_base.vpc index 47ce40fd..4d61b306 100644 --- a/mp/src/vpc_scripts/source_exe_win_win32_base.vpc +++ b/mp/src/vpc_scripts/source_exe_win_win32_base.vpc @@ -39,7 +39,8 @@ $Configuration $Compiler [$WIN32] { - $EnableEnhancedInstructionSet "Streaming SIMD Extensions (/arch:SSE)" + $EnableEnhancedInstructionSet "Streaming SIMD Extensions (/arch:SSE)" [!($VS2015||$VS2017||$VS2019||$VS2022)] + $EnableEnhancedInstructionSet "Streaming SIMD Extensions 2 (/arch:SSE2)" [($VS2015||$VS2017||$VS2019||$VS2022)] } $Linker @@ -66,7 +67,9 @@ $Configuration $PostBuildEvent [!$ANALYZE] { - $CommandLine "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $OUTBINDIR\$(TargetFileName) $SRCDIR" "\n" [!$SOURCESDK] + $CommandLine "if not exist $QUOTE$OUTBINDIR$QUOTE mkdir $QUOTE$OUTBINDIR$QUOTE" "\n" [($VS2015||$VS2017||$VS2019||$VS2022)] + $CommandLine "$BASE" "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $OUTBINDIR\$(TargetFileName) $SRCDIR" "\n" [!$SOURCESDK && ($VS2015||$VS2017||$VS2019||$VS2022)] + $CommandLine "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $OUTBINDIR\$(TargetFileName) $SRCDIR" "\n" [!$SOURCESDK && !($VS2015||$VS2017||$VS2019||$VS2022)] $CommandLine "$BASE" "copy $QUOTE$(TargetDir)$QUOTE$(TargetFileName) $OUTBINDIR\$(TargetFileName) >nul" "\n" \ "if ERRORLEVEL 1 goto BuildEventFailed" "\n" \ "if exist $QUOTE$(TargetDir)$QUOTE$(TargetName).map copy $QUOTE$(TargetDir)$QUOTE$(TargetName).map $OUTBINDIR\$(TargetName).map >nul" "\n" @@ -109,7 +112,8 @@ $Project $CustomBuildStep { // General - $CommandLine "$QUOTE$(VCInstallDir)bin\ml.exe$QUOTE /c /Cp /Zi /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE" + $CommandLine "$QUOTE$(VCInstallDir)bin\ml.exe$QUOTE /c /Cp /Zi /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE" [!($VS2015||$VS2017||$VS2019||$VS2022)] + $CommandLine "$QUOTE$(VC_ExecutablePath_x64_x86)\ml.exe$QUOTE /c /Cp /Zi /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE" [($VS2015||$VS2017||$VS2019||$VS2022)] $Description "Compiling pointeroverride.asm" $Outputs "$(IntDir)\$(InputName).obj" } diff --git a/mp/src/vpc_scripts/source_lib_win32_base.vpc b/mp/src/vpc_scripts/source_lib_win32_base.vpc index 02caa800..bea9d867 100644 --- a/mp/src/vpc_scripts/source_lib_win32_base.vpc +++ b/mp/src/vpc_scripts/source_lib_win32_base.vpc @@ -38,7 +38,8 @@ $Configuration $Compiler [$WIN32] { - $EnableEnhancedInstructionSet "Streaming SIMD Extensions (/arch:SSE)" + $EnableEnhancedInstructionSet "Streaming SIMD Extensions (/arch:SSE)" [!($VS2015||$VS2017||$VS2019||$VS2022)] + $EnableEnhancedInstructionSet "Streaming SIMD Extensions 2 (/arch:SSE2)" [($VS2015||$VS2017||$VS2019||$VS2022)] } $PreBuildEvent diff --git a/mp/src/vpc_scripts/source_win32_base.vpc b/mp/src/vpc_scripts/source_win32_base.vpc index 4d41cb31..199a2e67 100644 --- a/mp/src/vpc_scripts/source_win32_base.vpc +++ b/mp/src/vpc_scripts/source_win32_base.vpc @@ -3,16 +3,23 @@ // builds the analyze.vpc file will not be listed as a dependency. $Include "$SRCDIR\vpc_scripts\source_win32_analyze.vpc" [$ANALYZE] +// Mapbase - Implement any newer toolsets +$Include "$SRCDIR\vpc_scripts\newer_vs_toolsets.vpc" + $Configuration { $General { // Request a specific compiler toolset. - $PlatformToolset "v100" [$VS2010] - $PlatformToolset "v110_xp" [$VS2012 && !$ANALYZE] // VS 2012 targeting Windows XP - http://msdn.microsoft.com/en-us/library/vstudio/jj851139.aspx - $PlatformToolset "v110" [$VS2012 && $ANALYZE] // VS 2012 for /analyze - $PlatformToolset "v120_xp" [$VS2013 && !$ANALYZE] // VS 2013 targeting Windows XP - http://msdn.microsoft.com/en-us/library/vstudio/jj851139.aspx - $PlatformToolset "v120" [$VS2013 && $ANALYZE] // VS 2013 for /analyze + $PlatformToolset "v100" [$VS2010 && !($VS2015||$VS2017||$VS2019||$VS2022)] + $PlatformToolset "v110_xp" [$VS2012 && !$ANALYZE && !($VS2015||$VS2017||$VS2019||$VS2022)] // VS 2012 targeting Windows XP - http://msdn.microsoft.com/en-us/library/vstudio/jj851139.aspx + $PlatformToolset "v110" [$VS2012 && $ANALYZE && !($VS2015||$VS2017||$VS2019||$VS2022)] // VS 2012 for /analyze + $PlatformToolset "v120_xp" [$VS2013 && !$ANALYZE && !($VS2015||$VS2017||$VS2019||$VS2022)] // VS 2013 targeting Windows XP - http://msdn.microsoft.com/en-us/library/vstudio/jj851139.aspx + $PlatformToolset "v120" [$VS2013 && $ANALYZE && !($VS2015||$VS2017||$VS2019||$VS2022)] // VS 2013 for /analyze + $PlatformToolset "v140" [$VS2015] // VS 2015 + $PlatformToolset "v141" [$VS2017] // VS 2017 + $PlatformToolset "v142" [$VS2019] // VS 2019 + $PlatformToolset "v143" [$VS2022] // VS 2022 } $General @@ -32,6 +39,10 @@ $Configuration // warning C4316: object allocated on the heap may not be aligned 16 $DisableSpecificWarnings "$BASE;4316" [$VS2013] + + // warning C4838: conversion requires a narrowing conversion + // warning C4456-4459: variable shadowing. TODO: fix those! + $DisableSpecificWarnings "$BASE;4316;4838;4456;4457;4458;4459" [($VS2015||$VS2017||$VS2019||$VS2022)] // Having lots of warnings makes it harder to notice new, and possibly // important warnings, both on buildbot and in the output window. Lots diff --git a/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst b/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst index 19c18d7a..dd929346 100644 --- a/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst +++ b/mp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst @@ -50,16 +50,20 @@ Global Symbols 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) +.. js:function:: split(str, separators [, skipempty]) 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. + The parameter `skipempty` is a boolean (default false). If `skipempty` is true, empty strings are not added to array. :: eg. - local a = split("1.2-3;4/5",".-/;"); + local a = split("1.2-3;;4/5",".-/;"); + // the result will be [1,2,3,,4,5] + or + local b = split("1.2-3;;4/5",".-/;",true); // the result will be [1,2,3,4,5] diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp index 261b938b..776a9680 100644 --- a/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp @@ -205,8 +205,8 @@ 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)); + unsigned short s = (unsigned short)i; + sq_pushinteger(v, ((s << 8) | ((s >> 8) & 0x00FFu)) & 0xFFFFu); return 1; } diff --git a/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp b/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp index 919bd9e9..5747d8ed 100644 --- a/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp +++ b/mp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp @@ -12,6 +12,8 @@ #define MAX_WFORMAT_LEN 3 #define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar)) +static SQUserPointer rex_typetag = NULL; + static SQBool isfmtchr(SQChar ch) { switch(ch) { @@ -247,16 +249,16 @@ static SQInteger _string_rstrip(HSQUIRRELVM v) static SQInteger _string_split(HSQUIRRELVM v) { const SQChar *str,*seps; - SQChar *stemp; + SQInteger sepsize; + SQBool skipempty = SQFalse; sq_getstring(v,2,&str); - sq_getstring(v,3,&seps); - SQInteger sepsize = sq_getsize(v,3); + sq_getstringandsize(v,3,&seps,&sepsize); 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; + if(sq_gettop(v)>3) { + sq_getbool(v,4,&skipempty); + } + const SQChar *start = str; + const SQChar *end = str; sq_newarray(v,0); while(*end != '\0') { @@ -265,9 +267,10 @@ static SQInteger _string_split(HSQUIRRELVM v) { if(cur == seps[i]) { - *end = 0; - sq_pushstring(v,start,-1); - sq_arrayappend(v,-2); + if(!skipempty || (end != start)) { + sq_pushstring(v,start,end-start); + sq_arrayappend(v,-2); + } start = end + 1; break; } @@ -276,7 +279,7 @@ static SQInteger _string_split(HSQUIRRELVM v) } if(end != start) { - sq_pushstring(v,start,-1); + sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); } return 1; @@ -384,7 +387,9 @@ static SQInteger _string_endswith(HSQUIRRELVM v) #define SETUP_REX(v) \ SQRex *self = NULL; \ - sq_getinstanceup(v,1,(SQUserPointer *)&self,0); + if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer *)&self,rex_typetag))) { \ + return sq_throwerror(v,_SC("invalid type tag")); \ + } static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { @@ -465,6 +470,13 @@ static SQInteger _regexp_subexpcount(HSQUIRRELVM v) static SQInteger _regexp_constructor(HSQUIRRELVM v) { + SQRex *self = NULL; + if (SQ_FAILED(sq_getinstanceup(v, 1, (SQUserPointer *)&self, rex_typetag))) { + return sq_throwerror(v, _SC("invalid type tag")); + } + if (self != NULL) { + return sq_throwerror(v, _SC("invalid regexp object")); + } const SQChar *error,*pattern; sq_getstring(v,2,&pattern); SQRex *rex = sqstd_rex_compile(pattern,&error); @@ -499,7 +511,7 @@ static const SQRegFunction stringlib_funcs[]={ _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(split,-3,_SC(".ssb")), _DECL_FUNC(escape,2,_SC(".s")), _DECL_FUNC(startswith,3,_SC(".ss")), _DECL_FUNC(endswith,3,_SC(".ss")), @@ -512,6 +524,8 @@ SQInteger sqstd_register_stringlib(HSQUIRRELVM v) { sq_pushstring(v,_SC("regexp"),-1); sq_newclass(v,SQFalse); + rex_typetag = (SQUserPointer)rexobj_funcs; + sq_settypetag(v, -1, rex_typetag); SQInteger i = 0; while(rexobj_funcs[i].name != 0) { const SQRegFunction &f = rexobj_funcs[i]; diff --git a/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp b/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp index fb6545f9..5c03e839 100644 --- a/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp +++ b/mp/src/vscript/squirrel/squirrel/sqbaselib.cpp @@ -754,7 +754,7 @@ static SQInteger array_find(HSQUIRRELVM v) } -static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) +static bool _sort_compare(HSQUIRRELVM v, SQArray *arr, SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) { if(func < 0) { if(!v->ObjCmp(a,b,ret)) return false; @@ -765,15 +765,21 @@ static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger sq_pushroottable(v); v->Push(a); v->Push(b); + SQObjectPtr *valptr = arr->_values._vals; + SQUnsignedInteger precallsize = arr->_values.size(); 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))) { + if(SQ_FAILED(sq_getinteger(v, -1, &ret))) { v->Raise_Error(_SC("numeric value expected as return value of the compare function")); return false; } + if (precallsize != arr->_values.size() || valptr != arr->_values._vals) { + v->Raise_Error(_SC("array resized during sort operation")); + return false; + } sq_settop(v, top); return true; } @@ -792,7 +798,7 @@ static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteg maxChild = root2; } else { - if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret)) + if(!_sort_compare(v,arr,arr->_values[root2],arr->_values[root2 + 1],func,ret)) return false; if (ret > 0) { maxChild = root2; @@ -802,7 +808,7 @@ static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteg } } - if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret)) + if(!_sort_compare(v,arr,arr->_values[root],arr->_values[maxChild],func,ret)) return false; if (ret < 0) { if (root == maxChild) { diff --git a/mp/src/vscript/squirrel/squirrel/sqclass.cpp b/mp/src/vscript/squirrel/squirrel/sqclass.cpp index fc619616..53a29763 100644 --- a/mp/src/vscript/squirrel/squirrel/sqclass.cpp +++ b/mp/src/vscript/squirrel/squirrel/sqclass.cpp @@ -61,6 +61,9 @@ bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr _defaultvalues[_member_idx(temp)].val = val; return true; } + if (_members->CountUsed() >= MEMBER_MAX_COUNT) { + return false; + } if(belongs_to_static_table) { SQInteger mmidx; if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) && diff --git a/mp/src/vscript/squirrel/squirrel/sqclass.h b/mp/src/vscript/squirrel/squirrel/sqclass.h index 7d402172..60d3d21b 100644 --- a/mp/src/vscript/squirrel/squirrel/sqclass.h +++ b/mp/src/vscript/squirrel/squirrel/sqclass.h @@ -17,6 +17,7 @@ typedef sqvector SQClassMemberVec; #define MEMBER_TYPE_METHOD 0x01000000 #define MEMBER_TYPE_FIELD 0x02000000 +#define MEMBER_MAX_COUNT 0x00FFFFFF #define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD) #define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD) diff --git a/mp/src/vscript/squirrel/squirrel/sqtable.h b/mp/src/vscript/squirrel/squirrel/sqtable.h index 59db3317..8ca3ae7c 100644 --- a/mp/src/vscript/squirrel/squirrel/sqtable.h +++ b/mp/src/vscript/squirrel/squirrel/sqtable.h @@ -12,7 +12,7 @@ #define hashptr(p) ((SQHash)(((SQInteger)p) >> 3)) -inline SQHash HashObj(const SQObjectPtr &key) +inline SQHash HashObj(const SQObject &key) { switch(sq_type(key)) { case OT_STRING: return _string(key)->_hash; diff --git a/mp/src/vscript/vscript_bindings_base.cpp b/mp/src/vscript/vscript_bindings_base.cpp index 56da8435..4f20234a 100644 --- a/mp/src/vscript/vscript_bindings_base.cpp +++ b/mp/src/vscript/vscript_bindings_base.cpp @@ -50,100 +50,6 @@ static void ScriptColorPrintL( int r, int g, int b, const char *pszMsg ) 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(); //============================================================================= // @@ -203,6 +109,7 @@ BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); DEFINE_SCRIPTFUNC( TableToSubKeys, "Converts a script table to KeyValues." ); + DEFINE_SCRIPTFUNC( SubKeysToTable, "Converts to script table." ); DEFINE_SCRIPTFUNC_NAMED( ScriptFindOrCreateKey, "FindOrCreateKey", "Given a KeyValues object and a key name, find or create a KeyValues object associated with the key name" ); @@ -319,6 +226,19 @@ void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) } } +void CScriptKeyValues::SubKeysToTable( HSCRIPT hTable ) +{ + FOR_EACH_SUBKEY( m_pKeyValues, 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; + } + } +} + HSCRIPT CScriptKeyValues::ScriptFindOrCreateKey( const char *pszName ) { KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName, true); @@ -529,7 +449,6 @@ void RegisterBaseBindings( IScriptVM *pVM ) //----------------------------------------------------------------------------- - pVM->RegisterInstance( &g_ScriptConvarLookup, "Convars" ); pVM->RegisterInstance( &g_ScriptGlobalSys, "GlobalSys" ); //----------------------------------------------------------------------------- @@ -540,6 +459,9 @@ void RegisterBaseBindings( IScriptVM *pVM ) //----------------------------------------------------------------------------- + ScriptRegisterConstant( pVM, MAPBASE_VERSION, "The current Mapbase version according to when the VScript library was last compiled." ); + ScriptRegisterConstant( pVM, MAPBASE_VER_INT, "The current Mapbase version integer according to when the VScript library was last compiled." ); + // // Math/world // @@ -617,11 +539,12 @@ void RegisterBaseBindings( IScriptVM *pVM ) 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_CHEAT, "Only useable in singleplayer / debug / multiplayer & sv_cheats" ); 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_REPLICATED, "If this convar flag is set, it will enforce a serverside value on any clientside counterparts. (also known as FCVAR_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." ); diff --git a/mp/src/vscript/vscript_bindings_base.h b/mp/src/vscript/vscript_bindings_base.h index a2d9fb9a..2629aada 100644 --- a/mp/src/vscript/vscript_bindings_base.h +++ b/mp/src/vscript/vscript_bindings_base.h @@ -35,6 +35,7 @@ public: // Functions below are new with Mapbase void TableToSubKeys( HSCRIPT hTable ); + void SubKeysToTable( HSCRIPT hTable ); HSCRIPT ScriptFindOrCreateKey( const char *pszName ); diff --git a/mp/src/vscript/vscript_bindings_math.cpp b/mp/src/vscript/vscript_bindings_math.cpp index cb6c594d..cb1567d5 100644 --- a/mp/src/vscript/vscript_bindings_math.cpp +++ b/mp/src/vscript/vscript_bindings_math.cpp @@ -102,7 +102,6 @@ void ScriptMatrixSetColumn( const Vector& vecset, int column, HSCRIPT hMat1 ) matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); - static Vector outvec; MatrixSetColumn( vecset, column, *pMat1 ); } @@ -156,6 +155,49 @@ void ScriptSetScaleMatrix( float x, float y, float z, HSCRIPT hMat1 ) SetScaleMatrix( x, y, z, *pMat1 ); } +void ScriptMatrixScaleBy( float flScale, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixScaleBy( flScale, *pMat1 ); +} + +void ScriptMatrixScaleByZero( HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixScaleByZero( *pMat1 ); +} + +const Vector& ScriptMatrixGetTranslation( HSCRIPT hMat1 ) +{ + static Vector outvec; + outvec.Zero(); + if (!hMat1) + return outvec; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixGetTranslation( *pMat1, outvec ); + return outvec; +} + +void ScriptMatrixSetTranslation( const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixSetTranslation( vecset, *pMat1 ); +} + //============================================================================= // // Quaternion @@ -181,7 +223,7 @@ 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 ); + V_snprintf( pBuf, bufSize, "(Quaternion %p [%f %f %f %f])", (void*)pQuat, pQuat->x, pQuat->y, pQuat->z, pQuat->w ); return true; } @@ -390,6 +432,11 @@ float ScriptCalcDistanceToLineSegment( const Vector &point, const Vector &vLineA return CalcDistanceToLineSegment( point, vLineA, vLineB ); } +inline float ScriptExponentialDecay( float decayTo, float decayTime, float dt ) +{ + return ExponentialDecay( decayTo, decayTime, dt ); +} + void RegisterMathBaseBindings( IScriptVM *pVM ) { ScriptRegisterConstantNamed( pVM, ((float)(180.f / M_PI_F)), "RAD2DEG", "" ); @@ -422,7 +469,11 @@ void RegisterMathBaseBindings( IScriptVM *pVM ) 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." ); + ScriptRegisterFunctionNamed( pVM, ScriptSetScaleMatrix, "SetScaleMatrix", "Builds a scale matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixScaleBy, "MatrixScaleBy", "Scales a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixScaleByZero, "MatrixScaleByZero", "Scales a matrix by zero." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixGetTranslation, "MatrixGetTranslation", "Gets a matrix's translation." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixSetTranslation, "MatrixSetTranslation", "Sets a matrix's translation." ); // // Quaternion @@ -453,4 +504,12 @@ void RegisterMathBaseBindings( IScriptVM *pVM ) 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." ); + + ScriptRegisterFunction( pVM, SimpleSplineRemapVal, "remaps a value in [startInterval, startInterval+rangeInterval] from linear to spline using SimpleSpline" ); + ScriptRegisterFunction( pVM, SimpleSplineRemapValClamped, "remaps a value in [startInterval, startInterval+rangeInterval] from linear to spline using SimpleSpline" ); + ScriptRegisterFunction( pVM, Bias, "The curve is biased towards 0 or 1 based on biasAmt, which is between 0 and 1." ); + ScriptRegisterFunction( pVM, Gain, "Gain is similar to Bias, but biasAmt biases towards or away from 0.5." ); + ScriptRegisterFunction( pVM, SmoothCurve, "SmoothCurve maps a 0-1 value into another 0-1 value based on a cosine wave" ); + ScriptRegisterFunction( pVM, SmoothCurve_Tweak, "SmoothCurve peaks at flPeakPos, flPeakSharpness controls the sharpness of the peak" ); + ScriptRegisterFunctionNamed( pVM, ScriptExponentialDecay, "ExponentialDecay", "decayTo is factor the value should decay to in decayTime" ); } diff --git a/mp/src/vscript/vscript_squirrel.cpp b/mp/src/vscript/vscript_squirrel.cpp index 7d6795ca..67406ae0 100644 --- a/mp/src/vscript/vscript_squirrel.cpp +++ b/mp/src/vscript/vscript_squirrel.cpp @@ -31,14 +31,16 @@ #include "squirrel/squirrel/sqvm.h" #include "squirrel/squirrel/sqclosure.h" -#include "color.h" #include "tier1/utlbuffer.h" #include "tier1/mapbase_con_groups.h" +#include "tier1/convar.h" #include "vscript_squirrel.nut" #include +extern ConVar developer; + struct WriteStateMap { CUtlMap cache; @@ -66,8 +68,16 @@ struct WriteStateMap struct ReadStateMap { CUtlMap cache; +#ifdef _DEBUG + CUtlMap allocated; +#endif HSQUIRRELVM vm_; - ReadStateMap(HSQUIRRELVM vm) : cache(DefLessFunc(int)), vm_(vm) + ReadStateMap(HSQUIRRELVM vm) : + cache(DefLessFunc(int)), +#ifdef _DEBUG + allocated(DefLessFunc(int)), +#endif + vm_(vm) {} ~ReadStateMap() @@ -79,25 +89,48 @@ struct ReadStateMap } } - bool CheckCache(CUtlBuffer* pBuffer, HSQOBJECT** obj) + bool CheckCache(CUtlBuffer* pBuffer, HSQUIRRELVM vm, int * outmarker) { int marker = pBuffer->GetInt(); auto idx = cache.Find(marker); + +#ifdef _DEBUG + auto allocatedIdx = allocated.Find(marker); + bool hasSeen = allocatedIdx != allocated.InvalidIndex(); + if (!hasSeen) + { + allocated.Insert(marker, true); + } +#endif + if (idx != cache.InvalidIndex()) { - *obj = &cache[idx]; + sq_pushobject(vm, cache[idx]); return true; } else { - HSQOBJECT temp; - sq_resetobject(&temp); - auto idx = cache.Insert(marker, temp); - *obj = &cache[idx]; +#ifdef _DEBUG + Assert(!hasSeen); +#endif + *outmarker = marker; return false; } } + + void StoreInCache(int marker, HSQOBJECT& obj) + { + cache.Insert(marker, obj); + } + + void StoreTopInCache(int marker) + { + HSQOBJECT obj; + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + cache.Insert(marker, obj); + } }; class SquirrelVM : public IScriptVM @@ -152,6 +185,12 @@ public: //-------------------------------------------------------- virtual ScriptStatus_t ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) override; + //-------------------------------------------------------- + // Hooks + //-------------------------------------------------------- + virtual HScriptRaw HScriptToRaw( HSCRIPT val ) override; + virtual ScriptStatus_t ExecuteHookFunction( const char *pszEventName, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) override; + //-------------------------------------------------------- // External functions //-------------------------------------------------------- @@ -171,6 +210,11 @@ public: // External enums //-------------------------------------------------------- virtual void RegisterEnum(ScriptEnumDesc_t *pEnumDesc) override; + + //-------------------------------------------------------- + // External hooks + //-------------------------------------------------------- + virtual void RegisterHook(ScriptHook_t *pHookDesc) override; //-------------------------------------------------------- // External instances. Note class will be auto-registered. @@ -192,17 +236,20 @@ public: 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 bool SetValue(HSCRIPT hScope, const ScriptVariant_t& key, const ScriptVariant_t& val) 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 bool GetValue(HSCRIPT hScope, ScriptVariant_t key, ScriptVariant_t* pValue) override; virtual void ReleaseValue(ScriptVariant_t& value) override; virtual bool ClearValue(HSCRIPT hScope, const char* pszKey) override; + virtual bool ClearValue( HSCRIPT hScope, ScriptVariant_t pKey ) override; - // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) override; + virtual void CreateArray(ScriptVariant_t &arr, int size = 0) override; virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) override; //---------------------------------------------------------------------------- @@ -229,7 +276,7 @@ public: HSQOBJECT regexpClass_; }; -SQUserPointer TYPETAG_VECTOR = "VectorTypeTag"; +static char TYPETAG_VECTOR[] = "VectorTypeTag"; namespace SQVector { @@ -258,7 +305,7 @@ namespace SQVector return 0; } - SQInteger Get(HSQUIRRELVM vm) + SQInteger _get(HSQUIRRELVM vm) { const char* key = nullptr; sq_getstring(vm, 2, &key); @@ -284,7 +331,7 @@ namespace SQVector return 1; } - SQInteger Set(HSQUIRRELVM vm) + SQInteger _set(HSQUIRRELVM vm) { const char* key = nullptr; sq_getstring(vm, 2, &key); @@ -316,7 +363,7 @@ namespace SQVector return 0; } - SQInteger Add(HSQUIRRELVM vm) + SQInteger _add(HSQUIRRELVM vm) { Vector* v1 = nullptr; Vector* v2 = nullptr; @@ -338,7 +385,7 @@ namespace SQVector return 1; } - SQInteger Sub(HSQUIRRELVM vm) + SQInteger _sub(HSQUIRRELVM vm) { Vector* v1 = nullptr; Vector* v2 = nullptr; @@ -360,23 +407,20 @@ namespace SQVector return 1; } - SQInteger Multiply(HSQUIRRELVM vm) + 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)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } - SQObjectType paramType = sq_gettype(vm, 2); - float s = 0.0; Vector* v2 = nullptr; - if ((paramType & SQOBJECT_NUMERIC) && - SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + if ( SQ_SUCCEEDED(sq_getfloat(vm, 2, &s)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -387,8 +431,7 @@ namespace SQVector return 1; } - else if (paramType == OT_INSTANCE && - SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + else if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -401,27 +444,24 @@ namespace SQVector } else { - return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } } - SQInteger Divide(HSQUIRRELVM vm) + 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)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } - SQObjectType paramType = sq_gettype(vm, 2); - float s = 0.0; Vector* v2 = nullptr; - if ((paramType & SQOBJECT_NUMERIC) && - SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + if ( SQ_SUCCEEDED(sq_getfloat(vm, 2, &s)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -432,8 +472,7 @@ namespace SQVector return 1; } - else if (paramType == OT_INSTANCE && - SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + else if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -446,10 +485,239 @@ namespace SQVector } else { - return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } } + SQInteger _unm(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->x, -v1->y, -v1->z); + sq_remove(vm, -2); + + return 1; + } + + SQInteger weakref(HSQUIRRELVM vm) + { + sq_weakref(vm, 1); + return 1; + } + + SQInteger getclass(HSQUIRRELVM vm) + { + sq_getclass(vm, 1); + sq_push(vm, -1); + return 1; + } + + // multi purpose - copy from input vector, or init with 3 float input + SQInteger Set(HSQUIRRELVM vm) + { + SQInteger top = sq_gettop(vm); + Vector* v1 = nullptr; + + if ( top < 2 || SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) ) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + Vector* v2 = nullptr; + + if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) + { + if ( top != 2 ) + return sq_throwerror(vm, "Expected (Vector, Vector)"); + + VectorCopy( *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + float x, y, z; + + if ( top == 4 && + SQ_SUCCEEDED(sq_getfloat(vm, 2, &x)) && + SQ_SUCCEEDED(sq_getfloat(vm, 3, &y)) && + SQ_SUCCEEDED(sq_getfloat(vm, 4, &z)) ) + { + v1->Init( x, y, z ); + sq_pop( vm, 3 ); + + return 1; + } + + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + 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)"); + } + + VectorAdd( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + SQInteger Subtract(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)"); + } + + VectorSubtract( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + 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, Vector|float)"); + } + + Vector* v2 = nullptr; + + if ( SQ_SUCCEEDED(sq_getinstanceup( vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR )) ) + { + VectorMultiply( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + float flInput; + + if ( SQ_SUCCEEDED(sq_getfloat( vm, 2, &flInput )) ) + { + VectorMultiply( *v1, flInput, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + + 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, Vector|float)"); + } + + Vector* v2 = nullptr; + + if ( SQ_SUCCEEDED(sq_getinstanceup( vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR )) ) + { + VectorDivide( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + float flInput; + + if ( SQ_SUCCEEDED(sq_getfloat( vm, 2, &flInput )) ) + { + VectorDivide( *v1, flInput, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + + SQInteger DistTo(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->DistTo(*v2) ); + + return 1; + } + + SQInteger DistToSqr(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->DistToSqr(*v2) ); + + return 1; + } + + SQInteger IsEqualTo(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) < 2 || // bother checking > 3? + 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, float)"); + } + + float tolerance = 0.0f; + sq_getfloat( vm, 3, &tolerance ); + + sq_pushbool( vm, VectorsAreEqual( *v1, *v2, tolerance ) ); + + return 1; + } + SQInteger Length(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -545,30 +813,23 @@ namespace SQVector SQInteger Scale(HSQUIRRELVM vm) { Vector* v1 = nullptr; + float s = 0.0f; if (sq_gettop(vm) != 2 || - SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) { return sq_throwerror(vm, "Expected (Vector, float)"); } - float s = 0.0; + 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); - 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)"); - } + return 1; } SQInteger Dot(HSQUIRRELVM vm) @@ -601,6 +862,41 @@ namespace SQVector return 1; } + SQInteger FromKVString(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + const char* szInput; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getstring(vm, 2, &szInput)) ) + { + return sq_throwerror(vm, "Expected (Vector, string)"); + } + + float x = 0.0f, y = 0.0f, z = 0.0f; + + if ( sscanf( szInput, "%f %f %f", &x, &y, &z ) < 3 ) + { + // Return null while invalidating the input vector. + // This allows the user to easily check for input errors without halting. + + sq_pushnull(vm); + *v1 = vec3_invalid; + + return 1; + } + + v1->x = x; + v1->y = y; + v1->z = z; + + // return input vector + sq_remove( vm, -1 ); + + return 1; + } + SQInteger Cross(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -623,6 +919,25 @@ namespace SQVector return 1; } + SQInteger WithinAABox(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* mins = nullptr; + Vector* maxs = nullptr; + + if (sq_gettop(vm) != 3 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&mins, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 3, (SQUserPointer*)&maxs, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector, Vector)"); + } + + sq_pushbool( vm, v1->WithinAABox( *mins, *maxs ) ); + + return 1; + } + SQInteger ToString(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -633,7 +948,7 @@ namespace SQVector return sq_throwerror(vm, "Expected (Vector)"); } - sqstd_pushstringf(vm, "(vector: (%f, %f, %f))", v1->x, v1->y, v1->z); + sqstd_pushstringf(vm, "(Vector %p [%f %f %f])", (void*)v1, v1->x, v1->y, v1->z); return 1; } @@ -689,22 +1004,35 @@ namespace SQVector 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("_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("_unm"), _unm, 1, _SC(".")}, + {_SC("weakref"), weakref, 1, _SC(".")}, + {_SC("getclass"), getclass, 1, _SC(".")}, + {_SC("Set"), Set, -2, _SC("..nn")}, + {_SC("Add"), Add, 2, _SC("..")}, + {_SC("Subtract"), Subtract, 2, _SC("..")}, + {_SC("Multiply"), Multiply, 2, _SC("..")}, + {_SC("Divide"), Divide, 2, _SC("..")}, + {_SC("DistTo"), DistTo, 2, _SC("..")}, + {_SC("DistToSqr"), DistToSqr, 2, _SC("..")}, + {_SC("IsEqualTo"), IsEqualTo, -2, _SC("..n")}, {_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("Scale"), Scale, 2, _SC(".n")}, // identical to _multiply {_SC("Dot"), Dot, 2, _SC("..")}, {_SC("Cross"), Cross, 2, _SC("..")}, + {_SC("WithinAABox"), WithinAABox, 3, _SC("...")}, {_SC("ToKVString"), ToKVString, 1, _SC(".")}, + {_SC("FromKVString"), FromKVString, 2, _SC(".s")}, {_SC("_tostring"), ToString, 1, _SC(".")}, {_SC("_typeof"), TypeOf, 1, _SC(".")}, {_SC("_nexti"), Nexti, 2, _SC("..")}, @@ -762,7 +1090,8 @@ bool CreateParamCheck(const ScriptFunctionBinding_t& func, char* output) switch (func.m_desc.m_Parameters[i]) { case FIELD_FLOAT: - *output++ = 'n'; // NOTE: Can be int or float + case FIELD_INTEGER: + *output++ = 'n'; break; case FIELD_CSTRING: *output++ = 's'; @@ -770,9 +1099,6 @@ bool CreateParamCheck(const ScriptFunctionBinding_t& func, char* output) 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; @@ -815,7 +1141,7 @@ void PushVariant(HSQUIRRELVM vm, const ScriptVariant_t& value) sq_createinstance(vm, -1); SQUserPointer p; sq_getinstanceup(vm, -1, &p, 0); - new(p) Vector(value); + new(p) Vector(static_cast(value)); sq_remove(vm, -2); break; } @@ -1089,6 +1415,9 @@ SQInteger function_stub(HSQUIRRELVM vm) PushVariant(vm, retval); + if (retval.m_type == FIELD_VECTOR) + delete retval.m_pVector; + return pFunc->m_desc.m_ReturnType != FIELD_VOID; } @@ -1346,6 +1675,19 @@ SQInteger IsValid_stub(HSQUIRRELVM vm) return 1; } +SQInteger weakref_stub(HSQUIRRELVM vm) +{ + sq_weakref(vm, 1); + return 1; +} + +SQInteger getclass_stub(HSQUIRRELVM vm) +{ + sq_getclass(vm, 1); + sq_push(vm, -1); + return 1; +} + struct SquirrelSafeCheck { SquirrelSafeCheck(HSQUIRRELVM vm, int outputCount = 0) : @@ -1356,7 +1698,9 @@ struct SquirrelSafeCheck ~SquirrelSafeCheck() { - if (top_ != (sq_gettop(vm_) - outputCount_)) + SQInteger curtop = sq_gettop(vm_); + SQInteger diff = curtop - outputCount_; + if ( top_ != diff ) { Assert(!"Squirrel VM stack is not consistent"); Error("Squirrel VM stack is not consistent\n"); @@ -1408,8 +1752,24 @@ const char * ScriptDataTypeToName(ScriptDataType_t datatype) } } + +#define PushDocumentationRegisterFunction( szName ) \ + sq_pushroottable(vm); \ + sq_pushstring(vm, "__Documentation", -1); \ + sq_get(vm, -2); \ + sq_pushstring(vm, szName, -1); \ + sq_get(vm, -2); \ + sq_push(vm, -2); + +#define CallDocumentationRegisterFunction( paramcount ) \ + sq_call(vm, paramcount+1, SQFalse, SQFalse); \ + sq_pop(vm, 3); + void RegisterDocumentation(HSQUIRRELVM vm, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) { + if ( !developer.GetInt() ) + return; + SquirrelSafeCheck safeCheck(vm); if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) @@ -1440,20 +1800,18 @@ void RegisterDocumentation(HSQUIRRELVM vm, const ScriptFuncDescriptor_t& pFuncDe 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); + PushDocumentationRegisterFunction( "RegisterHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); } void RegisterClassDocumentation(HSQUIRRELVM vm, const ScriptClassDesc_t* pClassDesc) { + if ( !developer.GetInt() ) + return; + SquirrelSafeCheck safeCheck(vm); const char *name = pClassDesc->m_pszScriptName; @@ -1477,20 +1835,18 @@ void RegisterClassDocumentation(HSQUIRRELVM vm, const ScriptClassDesc_t* pClassD } // 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); + PushDocumentationRegisterFunction( "RegisterClassHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, base, -1); + sq_pushstring(vm, description, -1); + CallDocumentationRegisterFunction( 3 ); } void RegisterEnumDocumentation(HSQUIRRELVM vm, const ScriptEnumDesc_t* pClassDesc) { + if ( !developer.GetInt() ) + return; + SquirrelSafeCheck safeCheck(vm); if (pClassDesc->m_pszDescription && pClassDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) @@ -1499,20 +1855,18 @@ void RegisterEnumDocumentation(HSQUIRRELVM vm, const ScriptEnumDesc_t* pClassDes 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); + PushDocumentationRegisterFunction( "RegisterEnumHelp" ); + sq_pushstring(vm, name, -1); + sq_pushinteger(vm, pClassDesc->m_ConstantBindings.Count()); + sq_pushstring(vm, pClassDesc->m_pszDescription ? pClassDesc->m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); } void RegisterConstantDocumentation( HSQUIRRELVM vm, const ScriptConstantBinding_t* pConstDesc, const char *pszAsString, ScriptEnumDesc_t* pEnumDesc = nullptr ) { + if ( !developer.GetInt() ) + return; + SquirrelSafeCheck safeCheck(vm); if (pConstDesc->m_pszDescription && pConstDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) @@ -1532,20 +1886,18 @@ void RegisterConstantDocumentation( HSQUIRRELVM vm, const ScriptConstantBinding_ 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); + PushDocumentationRegisterFunction( "RegisterConstHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pConstDesc->m_pszDescription ? pConstDesc->m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); } void RegisterHookDocumentation(HSQUIRRELVM vm, const ScriptHook_t* pHook, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) { + if ( !developer.GetInt() ) + return; + SquirrelSafeCheck safeCheck(vm); if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) @@ -1579,20 +1931,18 @@ void RegisterHookDocumentation(HSQUIRRELVM vm, const ScriptHook_t* pHook, const 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); + PushDocumentationRegisterFunction( "RegisterHookHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); } void RegisterMemberDocumentation(HSQUIRRELVM vm, const ScriptMemberDesc_t& pDesc, ScriptClassDesc_t* pClassDesc = nullptr) { + if ( !developer.GetInt() ) + return; + SquirrelSafeCheck safeCheck(vm); if (pDesc.m_pszDescription && pDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) @@ -1609,21 +1959,21 @@ void RegisterMemberDocumentation(HSQUIRRELVM vm, const ScriptMemberDesc_t& pDesc 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); + // RegisterMemberHelp(name, signature, description) + PushDocumentationRegisterFunction( "RegisterMemberHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pDesc.m_pszDescription ? pDesc.m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); +} + +SQInteger GetDeveloperLevel(HSQUIRRELVM vm) +{ + sq_pushinteger( vm, developer.GetInt() ); + return 1; } @@ -1693,6 +2043,11 @@ bool SquirrelVM::Init() sq_pop(vm_, 1); } + sq_pushstring( vm_, "developer", -1 ); + sq_newclosure( vm_, &GetDeveloperLevel, 0 ); + //sq_setnativeclosurename( vm_, -1, "developer" ); + sq_newslot( vm_, -3, SQFalse ); + sq_pop(vm_, 1); } @@ -1992,6 +2347,65 @@ ScriptStatus_t SquirrelVM::ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* p return SCRIPT_DONE; } +HScriptRaw SquirrelVM::HScriptToRaw( HSCRIPT val ) +{ + Assert( val ); + Assert( val != INVALID_HSCRIPT ); + + HSQOBJECT *obj = (HSQOBJECT*)val; +#if 0 + if ( sq_isweakref(*obj) ) + return obj->_unVal.pWeakRef->_obj._unVal.raw; +#endif + return obj->_unVal.raw; +} + +ScriptStatus_t SquirrelVM::ExecuteHookFunction(const char *pszEventName, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT* pFunc = (HSQOBJECT*)GetScriptHookManager().GetHookFunction(); + sq_pushobject(vm_, *pFunc); + + // The call environment of the Hooks::Call function does not matter + // as the function does not access any member variables. + sq_pushroottable(vm_); + + sq_pushstring(vm_, pszEventName, -1); + + if (hScope) + sq_pushobject(vm_, *((HSQOBJECT*)hScope)); + else + sq_pushnull(vm_); // global hook + + for (int i = 0; i < nArgs; ++i) + { + PushVariant(vm_, pArgs[i]); + } + + bool hasReturn = pReturn != nullptr; + + if (SQ_FAILED(sq_call(vm_, nArgs + 3, 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_); @@ -2106,6 +2520,14 @@ bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) sq_newclosure(vm_, IsValid_stub, 0); sq_newslot(vm_, -3, SQFalse); + sq_pushstring(vm_, "weakref", -1); + sq_newclosure(vm_, weakref_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "getclass", -1); + sq_newclosure(vm_, getclass_stub, 0); + sq_newslot(vm_, -3, SQFalse); + for (int i = 0; i < pClassDesc->m_FunctionBindings.Count(); ++i) { @@ -2193,48 +2615,43 @@ void SquirrelVM::RegisterEnum(ScriptEnumDesc_t* pEnumDesc) if (!pEnumDesc) return; + sq_newtableex(vm_, pEnumDesc->m_ConstantBindings.Count()); + 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 ); + sq_push(vm_, -3); + sq_rawset(vm_, -3); for (int i = 0; i < pEnumDesc->m_ConstantBindings.Count(); ++i) { auto& scriptConstant = pEnumDesc->m_ConstantBindings[i]; + sq_pushstring(vm_, scriptConstant.m_pszScriptName, -1); + PushVariant(vm_, scriptConstant.m_data); + sq_rawset(vm_, -4); + 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 ); + sq_pop(vm_, 2); RegisterEnumDocumentation(vm_, pEnumDesc); } +void SquirrelVM::RegisterHook(ScriptHook_t* pHookDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pHookDesc); + + if (!pHookDesc) + return; + + RegisterHookDocumentation(vm_, pHookDesc, pHookDesc->m_desc, nullptr); +} + HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct) { SquirrelSafeCheck safeCheck(vm_); @@ -2362,7 +2779,6 @@ 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; } @@ -2439,6 +2855,41 @@ bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVarian return true; } +bool SquirrelVM::SetValue( HSCRIPT hScope, const ScriptVariant_t& key, const ScriptVariant_t& val ) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT obj = *(HSQOBJECT*)hScope; + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, obj); + } + else + { + sq_pushroottable(vm_); + } + + if ( sq_isarray(obj) ) + { + Assert( key.m_type == FIELD_INTEGER ); + + sq_pushinteger(vm_, key.m_int); + PushVariant(vm_, val); + + sq_set(vm_, -3); + } + else + { + PushVariant(vm_, key); + PushVariant(vm_, val); + + sq_newslot(vm_, -3, SQFalse); + } + + sq_pop(vm_, 1); + return true; +} + void SquirrelVM::CreateTable(ScriptVariant_t& Table) { SquirrelSafeCheck safeCheck(vm_); @@ -2454,15 +2905,17 @@ void SquirrelVM::CreateTable(ScriptVariant_t& Table) Table = (HSCRIPT)obj; } +// +// input table/array/class/instance/string +// 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; + // sq_getsize returns -1 on invalid input + return -1; } HSQOBJECT* scope = (HSQOBJECT*)hScope; @@ -2517,6 +2970,14 @@ int SquirrelVM::GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey bool SquirrelVM::GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) { +#ifdef _DEBUG + AssertMsg( pszKey, "FATAL: cannot get NULL" ); + + // Don't crash on debug + if ( !pszKey ) + return GetValue( hScope, ScriptVariant_t(0), pValue ); +#endif + SquirrelSafeCheck safeCheck(vm_); Assert(pValue); @@ -2555,6 +3016,47 @@ bool SquirrelVM::GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* p return true; } +bool SquirrelVM::GetValue( HSCRIPT hScope, ScriptVariant_t key, 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_); + } + + PushVariant(vm_, key); + + 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_); @@ -2599,7 +3101,34 @@ bool SquirrelVM::ClearValue(HSCRIPT hScope, const char* pszKey) sq_pop(vm_, 1); return true; } -/* + +bool SquirrelVM::ClearValue(HSCRIPT hScope, ScriptVariant_t pKey) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + PushVariant(vm_, pKey); + 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_); @@ -2614,7 +3143,7 @@ void SquirrelVM::CreateArray(ScriptVariant_t &arr, int size) arr = (HSCRIPT)obj; } -*/ + bool SquirrelVM::ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) { SquirrelSafeCheck safeCheck(vm_); @@ -2951,7 +3480,7 @@ void SquirrelVM::WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQI } else { - Warning("SquirrelVM::WriteObject: Unable to find instanceID for object of type %s, unable to serialize\n", + DevWarning("SquirrelVM::WriteObject: Unable to find instanceID for object of type %s, unable to serialize\n", pClassInstanceData->desc->m_pszClassname); pBuffer->PutString(""); } @@ -3027,6 +3556,7 @@ void SquirrelVM::WriteState(CUtlBuffer* pBuffer) int count = sq_getsize(vm_, 1); sq_pushnull(vm_); pBuffer->PutInt(count); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) { WriteObject(pBuffer, writeState, -2); @@ -3085,10 +3615,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } case OT_TABLE: { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } @@ -3096,8 +3625,7 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) int count = pBuffer->GetInt(); sq_newtableex(vm_, count); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); sq_push(vm_, -2); sq_setdelegate(vm_, -2); @@ -3115,17 +3643,15 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } case OT_ARRAY: { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } int count = pBuffer->GetInt(); sq_newarray(vm_, count); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); for (int i = 0; i < count; ++i) { @@ -3137,10 +3663,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } case OT_CLOSURE: { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } @@ -3152,8 +3677,8 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) sq_pushnull(vm_); break; } - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + + readState.StoreTopInCache(marker); } else { @@ -3165,6 +3690,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) break; } + vm_->Push(ret); + readState.StoreTopInCache(marker); + int noutervalues = _closure(ret)->_function->_noutervalues; for (int i = 0; i < noutervalues; ++i) { @@ -3186,10 +3714,6 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) _closure(ret)->_defaultparams[i] = obj; sq_poptop(vm_); } - - *obj = ret; - sq_addref(vm_, obj); - sq_pushobject(vm_, *obj); } ReadObject(pBuffer, readState); @@ -3198,10 +3722,12 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) sq_getstackobj(vm_, -1, &env); if (!sq_isnull(env)) { - if (_closure( *obj ) == nullptr) + HSQOBJECT obj; + sq_getstackobj(vm_, -2, &obj); + if (_closure(obj) == nullptr) Warning("Closure is null\n"); else - _closure(*obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); + _closure(obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); } sq_poptop(vm_); @@ -3226,10 +3752,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } case OT_CLASS: { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } @@ -3238,8 +3763,7 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) if (classType == VectorClassType) { sq_pushobject(vm_, vectorClass_); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); } else if (classType == NativeClassType) { @@ -3254,8 +3778,7 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) sq_pushnull(vm_); } sq_remove(vm_, -2); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); } else if (classType == ScriptClassType) { @@ -3267,8 +3790,7 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } sq_newclass(vm_, hasBase); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); sq_pushnull(vm_); ReadObject(pBuffer, readState); @@ -3292,10 +3814,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } case OT_INSTANCE: { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } @@ -3310,8 +3831,7 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) ReadObject(pBuffer, readState); sq_call(vm_, 2, SQTrue, SQFalse); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); sq_remove(vm_, -2); @@ -3340,8 +3860,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) if (sq_isinstance(singleton) && _instance(singleton)->_class == _class(klass)) { foundSingleton = true; - *obj = singleton; - sq_addref(vm_, obj); + + readState.StoreInCache(marker, singleton); + sq_addref(vm_, &singleton); sq_pop(vm_, 2); break; } @@ -3355,25 +3876,28 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) ((ScriptClassDesc_t*)typetag)->m_pszScriptName); } - sq_pushobject(vm_, *obj); + sq_pushobject(vm_, singleton); break; } + + HSQOBJECT obj; sq_createinstance(vm_, -1); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + readState.StoreInCache(marker, 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(); + 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; + _instance(obj)->_values[n] = val; sq_pop(vm_, 1); } } @@ -3436,10 +3960,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } case OT_FUNCPROTO: //internal usage only { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } @@ -3452,29 +3975,27 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) } vm_->Push(ret); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + readState.StoreTopInCache(marker); } case OT_OUTER: //internal usage only { - HSQOBJECT* obj = nullptr; - if (readState.CheckCache(pBuffer, &obj)) + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) { - sq_pushobject(vm_, *obj); break; } + SQOuter* outer = SQOuter::Create(_ss(vm_), nullptr); + vm_->Push(outer); + readState.StoreTopInCache(marker); + 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; } @@ -3496,10 +4017,12 @@ void SquirrelVM::ReadState(CUtlBuffer* pBuffer) sq_pushroottable(vm_); - HSQOBJECT* obj = nullptr; - readState.CheckCache(pBuffer, &obj); - sq_getstackobj(vm_, -1, obj); - sq_addref(vm_, obj); + HSQOBJECT obj; + int marker = 0; + readState.CheckCache(pBuffer, vm_, &marker); + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + readState.StoreInCache(marker, obj); int count = pBuffer->GetInt(); diff --git a/mp/src/vscript/vscript_squirrel.nut b/mp/src/vscript/vscript_squirrel.nut index f4d28227..5b76bbe3 100644 --- a/mp/src/vscript/vscript_squirrel.nut +++ b/mp/src/vscript/vscript_squirrel.nut @@ -1,73 +1,109 @@ static char g_Script_vscript_squirrel[] = R"vscript( //========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // -// Purpose: -// +// Purpose: +// //=============================================================================// Warning <- error; -function clamp(val,min,max) +function clamp( val, min, max ) { if ( max < min ) return max; - else if( val < min ) + if ( val < min ) return min; - else if( val > max ) + if ( val > max ) return max; - else - return val; + return val; } -function max(a,b) return a > b ? a : b +function max( a, b ) +{ + if ( a > b ) + return a; + return b; +} -function min(a,b) return a < b ? a : b +function min( a, b ) +{ + if ( a < b ) + return a; + return b; +} -function RemapVal(val, A, B, C, D) +function RemapVal( val, A, B, C, D ) { if ( A == B ) - return val >= B ? D : C; + { + if ( val >= B ) + return D; + return C; + }; return C + (D - C) * (val - A) / (B - A); } -function RemapValClamped(val, A, B, C, D) +function RemapValClamped( val, A, B, C, D ) { if ( A == B ) - return val >= B ? D : C; + { + if ( val >= B ) + return D; + return C; + }; + local cVal = (val - A) / (B - A); - cVal = (cVal < 0.0) ? 0.0 : (1.0 < cVal) ? 1.0 : cVal; + + if ( cVal <= 0.0 ) + return C; + + if ( cVal >= 1.0 ) + return D; + return C + (D - C) * cVal; } function Approach( target, value, speed ) { - local delta = target - value + local delta = target - value; - if( delta > speed ) - value += speed - else if( delta < (-speed) ) - value -= speed - else - value = target - - return value + if ( delta > speed ) + return value + speed; + if ( -speed > delta ) + return value - speed; + return target; } function AngleDistance( next, cur ) { local delta = next - cur - if ( delta < (-180.0) ) - delta += 360.0 - else if ( delta > 180.0 ) - delta -= 360.0 + if ( delta > 180.0 ) + return delta - 360.0; + if ( -180.0 > delta ) + return delta + 360.0; + return delta; +} - return delta +function FLerp( f1, f2, i1, i2, x ) +{ + return f1+(f2-f1)*(x-i1)/(i2-i1); +} + +function Lerp( f, A, B ) +{ + return A + ( B - A ) * f +} + +function SimpleSpline( f ) +{ + local ff = f * f; + return 3.0 * ff - 2.0 * ff * f; } function printl( text ) { - return ::print(text + "\n"); + return print(text + "\n"); } class CSimpleCallChainer @@ -106,231 +142,431 @@ class CSimpleCallChainer chain = null; } -DocumentedFuncs <- {} -DocumentedClasses <- {} -DocumentedEnums <- {} -DocumentedConsts <- {} -DocumentedHooks <- {} -DocumentedMembers <- {} +//--------------------------------------------------------- +// Hook handler +//--------------------------------------------------------- +local s_List = {} -function AddAliasedToTable(name, signature, description, table) +Hooks <- { - // 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] == '#') + // table, string, closure, string + function Add( scope, event, callback, context ) { - 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) + switch ( typeof scope ) { - if (k == 0 && v == "this") continue; - if (k > 1) text += (", "); - text += (v); + case "table": + case "instance": + case "class": + break; + default: + throw "invalid scope param"; } - text += (")\n"); + + if ( typeof event != "string" ) + throw "invalid event param"; + + if ( typeof callback != "function" ) + throw "invalid callback param"; + + if ( typeof context != "string" ) + throw "invalid context param"; + + if ( !(event in s_List) ) + s_List[event] <- {}; + + local t = s_List[event]; + + if ( !(scope in t) ) + t[scope] <- {}; + + t[scope][context] <- callback; + + return __UpdateHooks(); } - else + + function Remove( event, context ) { + local rem; + + if ( event ) + { + if ( event in s_List ) + { + foreach ( scope, ctx in s_List[event] ) + { + if ( context in ctx ) + { + delete ctx[context]; + } + + if ( !ctx.len() ) + { + if ( !rem ) + rem = []; + rem.append( event ); + rem.append( scope ); + } + } + } + } + else + { + foreach ( ev, t in s_List ) + { + foreach ( scope, ctx in t ) + { + if ( context in ctx ) + { + delete ctx[context]; + } + + if ( !ctx.len() ) + { + if ( !rem ) + rem = []; + rem.append( ev ); + rem.append( scope ); + } + } + } + } + + if ( rem ) + { + local c = rem.len() - 1; + for ( local i = 0; i < c; i += 2 ) + { + local ev = rem[i]; + local scope = rem[i+1]; + + if ( !s_List[ev][scope].len() ) + delete s_List[ev][scope]; + + if ( !s_List[ev].len() ) + delete s_List[ev]; + } + } + + return __UpdateHooks(); + } + + function Call( event, scope, ... ) + { + local firstReturn; + + if ( event in s_List ) + { + vargv.insert( 0, scope ); + + local t = s_List[event]; + if ( scope in t ) + { + foreach ( fn in t[scope] ) + { + //printf( "(%.4f) Calling hook %s:%s\n", Time(), event, context ); + + local r = fn.acall( vargv ); + if ( firstReturn == null ) + firstReturn = r; + } + } + else if ( !scope ) // global hook + { + foreach ( sc, ctx in t ) + { + vargv[0] = sc; + + foreach ( context, fn in ctx ) + { + //printf( "(%.4f) Calling hook (g) %s:%s\n", Time(), event, context ); + + local r = fn.acall( vargv ); + if ( firstReturn == null ) + firstReturn = r; + } + } + } + } + + return firstReturn; + } + + function __UpdateHooks() + { + return __UpdateScriptHooks( s_List ); + } +} + +//--------------------------------------------------------- +// Documentation +//--------------------------------------------------------- +__Documentation <- {} + +local developer = (delete developer)() + +if (developer) +{ + local DocumentedFuncs = {} + local DocumentedClasses = {} + local DocumentedEnums = {} + local DocumentedConsts = {} + local DocumentedHooks = {} + local DocumentedMembers = {} + + local 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 __Documentation::RegisterHelp(name, signature, description) + { + if (description.len() && description[0] == '#') + { + AddAliasedToTable(name, signature, description, DocumentedFuncs) + } + else + { + DocumentedFuncs[name] <- [signature, description]; + } + } + + function __Documentation::RegisterClassHelp(name, baseclass, description) + { + DocumentedClasses[name] <- [baseclass, description]; + } + + function __Documentation::RegisterEnumHelp(name, num_elements, description) + { + DocumentedEnums[name] <- [num_elements, description]; + } + + function __Documentation::RegisterConstHelp(name, signature, description) + { + if (description.len() && description[0] == '#') + { + AddAliasedToTable(name, signature, description, DocumentedConsts) + } + else + { + DocumentedConsts[name] <- [signature, description]; + } + } + + function __Documentation::RegisterHookHelp(name, signature, description) + { + DocumentedHooks[name] <- [signature, description]; + } + + function __Documentation::RegisterMemberHelp(name, signature, description) + { + DocumentedMembers[name] <- [signature, description]; + } + + local function printdoc( text ) + { + return ::printc(200,224,255,text); + } + + local function printdocl( text ) + { + return printdoc(text + "\n"); + } + + local 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); + } + + local 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); + } + + local 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); } - 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) + local function PrintEnum(name, doc) { - text += ("Value: null\n"); + 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); } - 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) + local function PrintConst(name, doc) { - // Is an aliased function - text += ("Signature: function " + name + "("); - foreach(k,v in this[name].getinfos().parameters) + local text = ("Constant: " + name + "\n"); + if (doc[0] == null) { - if (k == 0 && v == "this") continue; - if (k > 1) text += (", "); - text += (v); + text += ("Value: null\n"); } - 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)) + else { - foundMatches = true; - printfunc(name, doc) + text += ("Value: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); + } + + local 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); + } + + local function PrintMatches( pattern, docs, printfunc ) + { + local matches = []; + local always = pattern == "*"; + + foreach( name, doc in docs ) + { + if (always || name.tolower().find(pattern) != null || (doc[1].len() && doc[1].tolower().find(pattern) != null)) + { + matches.append( name ); + } + } + + if ( !matches.len() ) + return 0; + + matches.sort(); + + foreach( name in matches ) + printfunc( name, docs[name] ); + + return 1; + } + + function __Documentation::PrintHelp(pattern = "*") + { + local patternLower = pattern.tolower(); + + // Have a specific order + if (!( + PrintMatches( patternLower, DocumentedEnums, PrintEnum ) | + PrintMatches( patternLower, DocumentedConsts, PrintConst ) | + PrintMatches( patternLower, DocumentedClasses, PrintClass ) | + PrintMatches( patternLower, DocumentedFuncs, PrintFunc ) | + PrintMatches( patternLower, DocumentedMembers, PrintMember ) | + PrintMatches( patternLower, DocumentedHooks, PrintHook ) + )) + { + printdocl("Pattern " + pattern + " not found"); } } - - return foundMatches; } - -function PrintHelp(pattern = "*") +else { - local foundMatches = false; - local patternLower = pattern.tolower(); + __Documentation.RegisterHelp <- + __Documentation.RegisterClassHelp <- + __Documentation.RegisterEnumHelp <- + __Documentation.RegisterConstHelp <- + __Documentation.RegisterHookHelp <- + __Documentation.RegisterMemberHelp <- dummy - // 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"); + function __Documentation::PrintHelp( pattern = null ) + { + printcl(200, 224, 255, "Documentation is not enabled. To enable documentation, restart the server with the 'developer' cvar set to 1 or higher."); + } } -// 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." ); +if (developer) +{ + __Documentation.RegisterClassHelp( "Vector", "", "Basic 3-float Vector class." ); + __Documentation.RegisterHelp( "Vector::Length", "float Vector::Length()", "Return the vector's length." ); + __Documentation.RegisterHelp( "Vector::LengthSqr", "float Vector::LengthSqr()", "Return the vector's squared length." ); + __Documentation.RegisterHelp( "Vector::Length2D", "float Vector::Length2D()", "Return the vector's 2D length." ); + __Documentation.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." ); + __Documentation.RegisterHelp( "Vector::Normalized", "float Vector::Normalized()", "Return a normalized version of the vector." ); + __Documentation.RegisterHelp( "Vector::Norm", "void Vector::Norm()", "Normalize the vector in place." ); + __Documentation.RegisterHelp( "Vector::Scale", "vector Vector::Scale(float)", "Scale the vector's magnitude and return the result." ); + __Documentation.RegisterHelp( "Vector::Dot", "float Vector::Dot(vector)", "Return the dot/scalar product of two vectors." ); + __Documentation.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." ); + __Documentation.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." ); + __Documentation.RegisterMemberHelp( "Vector.x", "float Vector.x", "The vector's X coordinate on the cartesian X axis." ); + __Documentation.RegisterMemberHelp( "Vector.y", "float Vector.y", "The vector's Y coordinate on the cartesian Y axis." ); + __Documentation.RegisterMemberHelp( "Vector.z", "float Vector.z", "The vector's Z coordinate on the cartesian Z axis." ); + __Documentation.RegisterHelp( "clamp", "float clamp(float, float, float)", "" ); + __Documentation.RegisterHelp( "max", "float max(float, float)", "" ); + __Documentation.RegisterHelp( "min", "float min(float, float)", "" ); + __Documentation.RegisterHelp( "RemapVal", "float RemapVal(float, float, float, float, float)", "" ); + __Documentation.RegisterHelp( "RemapValClamped", "float RemapValClamped(float, float, float, float, float)", "" ); + __Documentation.RegisterHelp( "Approach", "float Approach(float, float, float)", "" ); + __Documentation.RegisterHelp( "AngleDistance", "float AngleDistance(float, float)", "" ); + __Documentation.RegisterHelp( "FLerp", "float FLerp(float, float, float, float, float)", "" ); + __Documentation.RegisterHelp( "Lerp", "float Lerp(float, float, float)", "" ); + __Documentation.RegisterHelp( "SimpleSpline", "float SimpleSpline(float)", "" ); +} )vscript"; \ No newline at end of file