From c448f194aeeb63dc33edaaa9f07b931c050f9772 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Fri, 14 Aug 2020 21:21:25 +0000 Subject: [PATCH] Mapbase v5.0 - Added keyvalue to hl2_gamerules which allows respawning in singleplayer - Added the game instructor system (including env_instructor_hint) from later Valve games using a VDC tutorial which adjusts the version from the Alien Swarm SDK to FPS rules and a Source 2013 environment; Also added new KV and icons for further control from mappers (tutorial mentioned by Maestra Fenix) - Added L4D/TF2 glows + point_glow entity as an all-purpose SDK-based off-shoot of tf_glow - Fixed weapon pickup sound not playing (reported by Sl0th and later Cvoxulary) - Fixed env_projectedtextures not updating on save/load - Added func_fake_worldportal, a spatial point_camera inspired by linked_portal_door based on SDK code alone (WIP, may be changed a lot in future updates) - Added option for point_camera and func_reflective_glass to use different render targets, therefore allowing multiple cameras and mirrors to be active at the same time - Added additional RT camera textures to choose from with a default of 3, but also controllable through a -numcameratextures command line param - Added adjustable convars for main view NearZ and skybox NearZ (suggested by someone recently, also suggested by Klems over a year ago) - Fixed map-specific localization files, cleaned up map-specific file code - Added a new block to gameinfo.txt which allows mods to automatically append their own command line parameters - Fixed math_lightpattern corruption when setting pattern/style while active - Fixed the "Touch" input crashing when given no entity - Added a way to add EFlags via keyvalue (suggested by Niker107) - Fixed ai_script_conditions not working without a NPC actor (reported by MetroHam) - Fixed point_radiation_source causing huge problems when intensity is 0, even though it was already advised against (reported by beefbacon) - Added "Mapbase" header to Mapbase-specific code files - Fixed an issue with updating sky_camera not obtaining area correctly, causing some entities to not draw in the skybox - Added "CopyFogController" and "CopyFogControllerWithScale" inputs to sky_camera, which copy fog parameters directly from a fog controller - Added "SetScale" input to sky_camera for live scale changing - Added convar to control player crouch speed multiplier (suggested by ArtyIF) - Added a ton of fixes for people running the Debug configuration of the codebase (partial credit to stepa2) - Added support for pre-defined enums and constants in VScript, starting with various values from the SDK code (damage types, trace masks, etc.) - Added limited support for Valve's Quaternion class in VScript - Added new instance helper capabilities, destructible game instances, and other misc. changes to VScript library - Replaced most of the VScript "accessor" classes with direct references to the original classes, as they were getting complicated fast and adding new VScript-only functions to the original classes might not be as bad as previously thought - Added base NPC hooks for AI sensing in VScript (allows control over sight and hearing), also exposed CSound for it - Added various functions and hooks for VPhysics integration in VScript - Added VScript-based custom suit devices - Expanded trace info exposed to VScript to allow plane and surface access (suggested by krassell) - Added ability to insert localization strings through VScript - Added various misc. VScript functions with various purposes, including reading/writing EFlags, movetypes, collision groups, etc. - Fixed VBSP not being able to correctly parse parallax corrected cubemaps in maps with instances --- CONTRIBUTING | 9 +- README | 127 +- sp/src/game/client/c_basecombatweapon.cpp | 8 +- sp/src/game/client/c_baseentity.cpp | 2 + sp/src/game/client/c_baseentity.h | 1 + sp/src/game/client/c_baselesson.cpp | 3876 +++++++++++++++++ sp/src/game/client/c_baselesson.h | 458 ++ .../game/client/c_func_reflective_glass.cpp | 131 + sp/src/game/client/c_func_reflective_glass.h | 5 + sp/src/game/client/c_gameinstructor.cpp | 1312 ++++++ sp/src/game/client/c_gameinstructor.h | 118 + sp/src/game/client/c_pixel_visibility.cpp | 2 + sp/src/game/client/c_point_camera.cpp | 35 + sp/src/game/client/c_point_camera.h | 7 + sp/src/game/client/c_rope.cpp | 2 +- sp/src/game/client/client_mapbase.vpc | 15 +- sp/src/game/client/clientmode_shared.cpp | 15 + sp/src/game/client/clientshadowmgr.cpp | 3 +- sp/src/game/client/hl2/c_basehlplayer.h | 6 + sp/src/game/client/hl2/hud_suitpower.cpp | 68 + sp/src/game/client/hud.cpp | 231 + sp/src/game/client/hud.h | 31 + sp/src/game/client/hud_locator_target.cpp | 2207 ++++++++++ sp/src/game/client/hud_locator_target.h | 184 + .../game/client/mapbase/c_func_clientclip.cpp | 2 +- .../mapbase/c_func_fake_worldportal.cpp | 226 + .../client/mapbase/c_func_fake_worldportal.h | 64 + sp/src/game/client/mapbase/c_point_glow.cpp | 103 + sp/src/game/client/view.cpp | 8 + sp/src/game/client/viewrender.cpp | 359 +- sp/src/game/client/viewrender.h | 10 + sp/src/game/client/vscript_client.cpp | 2 + sp/src/game/server/SkyCamera.cpp | 54 +- sp/src/game/server/SkyCamera.h | 4 + sp/src/game/server/TemplateEntities.cpp | 2 + sp/src/game/server/ai_activity.cpp | 2 +- sp/src/game/server/ai_basenpc.cpp | 52 +- sp/src/game/server/ai_memory.cpp | 2 +- sp/src/game/server/ai_scriptconditions.cpp | 5 + sp/src/game/server/baseanimating.cpp | 5 +- sp/src/game/server/baseanimating.h | 4 + sp/src/game/server/baseentity.cpp | 98 +- sp/src/game/server/baseentity.h | 16 + sp/src/game/server/env_instructor_hint.cpp | 229 + sp/src/game/server/env_projectedtexture.cpp | 1 + sp/src/game/server/func_reflective_glass.cpp | 31 + sp/src/game/server/hl2/hl2_client.cpp | 15 + sp/src/game/server/hl2/hl2_player.cpp | 112 +- sp/src/game/server/hl2/hl2_player.h | 13 + sp/src/game/server/hl2/npc_antlionguard.cpp | 7 + sp/src/game/server/logicentities.cpp | 2 + sp/src/game/server/mapbase/GlobalStrings.cpp | 2 +- sp/src/game/server/mapbase/GlobalStrings.h | 2 +- .../game/server/mapbase/SystemConvarMod.cpp | 2 +- sp/src/game/server/mapbase/SystemConvarMod.h | 2 +- sp/src/game/server/mapbase/ai_grenade.cpp | 2 +- sp/src/game/server/mapbase/ai_grenade.h | 2 +- sp/src/game/server/mapbase/ai_monitor.cpp | 2 +- .../game/server/mapbase/ai_weaponmodifier.cpp | 2 +- .../server/mapbase/closecaption_entity.cpp | 2 +- sp/src/game/server/mapbase/datadesc_mod.cpp | 2 +- sp/src/game/server/mapbase/datadesc_mod.h | 2 +- .../game/server/mapbase/expandedrs_combine.h | 2 +- .../game/server/mapbase/func_clientclip.cpp | 2 +- .../server/mapbase/func_fake_worldportal.cpp | 100 + .../server/mapbase/logic_eventlistener.cpp | 8 +- .../server/mapbase/logic_externaldata.cpp | 2 +- .../mapbase/logic_register_activator.cpp | 8 +- sp/src/game/server/mapbase/logic_skill.cpp | 2 +- .../server/mapbase/point_advanced_finder.cpp | 2 +- .../game/server/mapbase/point_copy_size.cpp | 2 +- .../game/server/mapbase/point_damageinfo.cpp | 2 +- .../server/mapbase/point_entity_replace.cpp | 2 +- sp/src/game/server/mapbase/point_glow.cpp | 73 + .../game/server/mapbase/point_projectile.cpp | 2 +- .../server/mapbase/point_radiation_source.cpp | 8 +- sp/src/game/server/mapbase/variant_tools.h | 2 +- sp/src/game/server/physics_prop_ragdoll.cpp | 31 + sp/src/game/server/physics_prop_ragdoll.h | 11 + sp/src/game/server/player.cpp | 12 +- sp/src/game/server/player.h | 5 + sp/src/game/server/point_camera.cpp | 12 + sp/src/game/server/point_camera.h | 2 + sp/src/game/server/server_mapbase.vpc | 7 +- sp/src/game/server/soundent.cpp | 25 + sp/src/game/server/soundent.h | 6 + sp/src/game/server/vscript_server.cpp | 2 + sp/src/game/shared/ai_activity.h | 1 + sp/src/game/shared/baseentity_shared.cpp | 23 +- sp/src/game/shared/gamemovement.cpp | 7 +- sp/src/game/shared/gamerules.h | 4 + sp/src/game/shared/hl2/hl2_gamerules.cpp | 36 + sp/src/game/shared/hl2/hl2_gamerules.h | 6 + sp/src/game/shared/mapbase/MapEdit.cpp | 2 +- sp/src/game/shared/mapbase/MapEdit.h | 2 +- .../game/shared/mapbase/mapbase_game_log.cpp | 2 +- sp/src/game/shared/mapbase/mapbase_rpc.cpp | 4 +- sp/src/game/shared/mapbase/mapbase_shared.cpp | 193 +- sp/src/game/shared/mapbase/matchers.cpp | 2 +- sp/src/game/shared/mapbase/matchers.h | 2 +- .../shared/mapbase/vscript_consts_shared.cpp | 411 ++ .../game/shared/mapbase/vscript_funcs_hl2.cpp | 2 +- .../shared/mapbase/vscript_funcs_math.cpp | 202 +- .../game/shared/mapbase/vscript_funcs_math.h | 65 +- .../shared/mapbase/vscript_funcs_shared.cpp | 247 +- .../shared/mapbase/vscript_funcs_shared.h | 166 +- sp/src/game/shared/sceneentity_shared.cpp | 2 +- sp/src/game/shared/shareddefs.h | 57 +- sp/src/game/shared/usercmd.h | 33 + sp/src/game/shared/util_shared.cpp | 53 + sp/src/game/shared/util_shared.h | 3 + sp/src/game/shared/vscript_shared.h | 1 + sp/src/game/shared/weapon_parse.cpp | 6 + sp/src/public/const.h | 4 +- sp/src/public/mathlib/vector.h | 5 + sp/src/public/tier1/convar.h | 23 + sp/src/public/tier1/utlsymbol.h | 83 +- sp/src/public/vphysics_interface.h | 10 + sp/src/public/vscript/ivscript.h | 116 + sp/src/utils/vbsp/map.cpp | 14 +- sp/src/vpc_scripts/source_base.vpc | 2 - sp/src/vscript/vscript.cpp | 10 + sp/src/vscript/vscript.vpc | 1 + sp/src/vscript/vscript_squirrel.cpp | 628 ++- 124 files changed, 12655 insertions(+), 406 deletions(-) create mode 100644 sp/src/game/client/c_baselesson.cpp create mode 100644 sp/src/game/client/c_baselesson.h create mode 100644 sp/src/game/client/c_gameinstructor.cpp create mode 100644 sp/src/game/client/c_gameinstructor.h create mode 100644 sp/src/game/client/hud_locator_target.cpp create mode 100644 sp/src/game/client/hud_locator_target.h create mode 100644 sp/src/game/client/mapbase/c_func_fake_worldportal.cpp create mode 100644 sp/src/game/client/mapbase/c_func_fake_worldportal.h create mode 100644 sp/src/game/client/mapbase/c_point_glow.cpp create mode 100644 sp/src/game/server/env_instructor_hint.cpp create mode 100644 sp/src/game/server/mapbase/func_fake_worldportal.cpp create mode 100644 sp/src/game/server/mapbase/point_glow.cpp create mode 100644 sp/src/game/shared/mapbase/vscript_consts_shared.cpp diff --git a/CONTRIBUTING b/CONTRIBUTING index 2c16bdb1d9..c855b06908 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -26,10 +26,15 @@ All contributions must follow the following rules: full permission of the contribution's original creator(s). * Code contributions are not obliged to follow Mapbase's preprocessor conventions (e.g. #ifdef MAPBASE), - although it's acceptable. + although it is acceptable. -If your contribution is accepted, you may be listed in Mapbase's credits: + * If you are contributing a file you created yourself specifically for Mapbase, you are required to + use the custom "Mapbase - Source 2013" header used in other Mapbase files as of Mapbase v5.0. + You are encouraged to append an "Author(s)" part to that header in your file in order to clarify who wrote it. + +If your contribution is accepted, you may be listed in Mapbase's credits and the README's external content list: https://github.com/mapbase-source/source-sdk-2013/wiki/Mapbase-Credits#Contributors + https://github.com/mapbase-source/source-sdk-2013/blob/master/README You may also receive the "Contributor" or "Major Contributor" role on Mapbase's Discord server if you are a member of it. diff --git a/README b/README index ff26851139..f9d61ee61f 100644 --- a/README +++ b/README @@ -1,21 +1,126 @@ +//=================================================================================================================================================== + Mapbase - Source 2013 + https://github.com/mapbase-source/source-sdk-2013 + https://www.moddb.com/mods/mapbase + +//=================================================================================================================================================== + +This repository contains code from Mapbase, a modification of the Source 2013 SDK which serves as a combined package +of general-purpose improvements, fixes, and utility features for mods. + +Mapbase's main content in this repository may include: + +- 80+ custom entities (new logic entities, filters, etc.) +- Hundreds of Inputs/Outputs/KeyValues additions and modifications +- Custom SDK_ shaders with improvements adapted from Alien Swarm SDK code, including projected texture fixes and radial fog +- Custom VScript implementation based on public Squirrel API and Alien Swarm SDK interfaces/descriptions +- Additional gameplay control for Half-Life 2 mods, including grenade features for more NPCs and singleplayer respawning +- More map-specific capabilities for maps unable to branch into their own mods, e.g. MP mods or map compilations +- View rendering changes for drawing 3D skyboxes and RT-based entities +- Countless other fixes and improvements + +//=================================================================================================================================================== + +Mapbase is an open-source project and its contents can be distributed and used at the discretion of its users. However, this project represents many parts of +the Source modding community packaged into a whole, so credit is taken very seriously. + + Up-to-date information about Mapbase content usage and credit are addressed in this article on Mapbase's wiki: + https://github.com/mapbase-source/source-sdk-2013/wiki/Using-Mapbase-Content + +//=================================================================================================================================================== + +>>>>>>>> EXTERNAL CONTENT USED IN MAPBASE <<<<<<<< + +The Mapbase project is a combination of original code from its creators, code contributed by other Source modders, and code borrowed from open-source articles +and repositories (especially ones which are specifically published as free source code). One of Mapbase's goals is to make the most popular fixes and the most obscure +or complicated code changes accessible and easy to use for level designers and other kinds of Source modders who would otherwise have no idea how to implement them. + +*** DISCLAIMER: Mapbase has a strict no-leak-content policy and only allows content created directly by contributors or content originating from open-source repositories. +If you believe any content in Mapbase originates from any leak or unauthorized source (from Valve or otherwise), please contact Blixibon immediately. +Mapbase is intended to be usable by everyone, including licensed Source projects and Steam mods. *** + +The Alien Swarm SDK was used to backport features and code from newer branches of Source into a Source 2013/Half-Life 2 environment. +Mapbase also implements some of Tony Sergi's code changes from the Source 2007 SDK codebase. Both SDKs are publicly distributed by Valve and are available on Steam. + +Here's a list of Mapbase's other known external code sources: -This repository contains the code and game files of the Source 2013 modification known as Mapbase. +- https://github.com/95Navigator/insolence-2013 (Initial custom shader code and projected texture improvements) +- https://github.com/Biohazard90/g-string_2013 (Custom shadow filters, included indirectly via Insolence repo) +- https://github.com/KyleGospo/City-17-Episode-One-Source (Brush phong and projected texture changes, included indirectly via Insolence repo) +- https://github.com/DownFall-Team/DownFall (Multiple skybox code and fix for ent_fire delay not using floats; Also used as a guide to port certain Alien Swarm SDK changes to Source 2013, +including radial fog, rope code, and treesway) +- https://github.com/DeathByNukes/source-sdk-2013 (VBSP manifest fixes) +- https://github.com/entropy-zero/source-sdk-2013 (skill_changed game event) -The projected texture fixes within the "ASW_PROJECTED_TEXTURES" preprocessor and most of the shader changes involving projected textures are cumulative changes from Insolence's repository using fixes from Alien Swarm, G-String, and City 17: Episode One. (https://github.com/95Navigator/insolence-2013, https://github.com/Biohazard90/g-string_2013, https://github.com/KyleGospo/City-17-Episode-One-Source) -The original code for phong reflections on LightmappedGeneric-derived shaders are from City 17: Episode One. (https://github.com/KyleGospo/City-17-Episode-One-Source) -The Alien Swarm-based radial fog and rope code as well as the multiple skybox support are from Half-Life 2: Downfall. (https://github.com/DownFall-Team/DownFall) -The dynamic RTT shadow angles code is from Saul Rennison on the VDC. (https://developer.valvesoftware.com/wiki/Dynamic_RTT_shadow_angles_in_Source_2007) -The vortigaunt LOS fix is from Half-Life 2: Community Edition (dky.tehkingd.u in particular). (https://gitlab.com/RaraCerberus/HL2CE) -The parallax corrected cubemap code was originally created by Brian Charles. (https://developer.valvesoftware.com/wiki/Parallax_Corrected_Cubemaps) -The custom VScript library was created by reductor for Mapbase. (https://github.com/mapbase-source/source-sdk-2013/pull/5) -Various other code and contributions were based off of pull requests in the Source 2013 SDK (https://github.com/ValveSoftware/source-sdk-2013/pulls) and snippets on the Valve Developer Community (http://developer.valvesoftware.com/). +//--------------------------------------------------------------------------------------------------------------------------------------------------- -All of the work mentioned above was open source when it was borrowed. +Valve Developer Community (VDC) sources: -More credits can be found here: +- https://developer.valvesoftware.com/wiki/Dynamic_RTT_shadow_angles_in_Source_2007 (Dynamic RTT shadow angles by Saul Rennison) +- https://developer.valvesoftware.com/wiki/Parallax_Corrected_Cubemaps (Parallax corrected cubemaps implementation from Brian Charles) +- https://developer.valvesoftware.com/wiki/Adding_the_Game_Instructor (ASW SDK game instructor adapted to Source 2013 by Kolesias123; was implemented based on a translated article) +- https://developer.valvesoftware.com/wiki/Brush_ladders (Functional func_ladders in Source 2013) +- https://developer.valvesoftware.com/wiki/CAreaPortalOneWay (func_areaportal_oneway) +- https://developer.valvesoftware.com/wiki/Implementing_Discord_RPC (Discord RPC implementation; Mapbase has its own framework originally based on this article) +- https://developer.valvesoftware.com/wiki/Rain_splashes (NOTE: This code is not completely used in Mapbase, but may still exist in its codebase) +- https://developer.valvesoftware.com/wiki/Hand_Viewmodels (NOTE: This code is not completely supported by default because Mapbase does not yet have weapon viewmodels which support +interchangeable arms; this may change in the future) + +- https://developer.valvesoftware.com/wiki/General_SDK_Snippets_%26_Fixes (Various snippets on the article, including the dropship gun fix) +- https://developer.valvesoftware.com/wiki/Memory_Leak_Fixes (Most of these snippets were applied in later SDK updates, but some were not and have been added to Mapbase) +- https://developer.valvesoftware.com/wiki/Env_projectedtexture/fixes (Misc. env_projectedtexture fixes; Some of these are superceded by Alien Swarm-based changes and not used) +- https://developer.valvesoftware.com/wiki/Scenes.image (Original raw VCD file support; Code was improved for Mapbase and the article was later updated with it) +- https://developer.valvesoftware.com/wiki/Extending_Prop_Sphere (prop_sphere customization) +- https://developer.valvesoftware.com/wiki/TF2_Glow_Effect_(2013_SDK) (Glow effect) +- https://developer.valvesoftware.com/wiki/CFuncMoveLinear_ParentingFix (func_movelinear parenting fix; Code was improved for Mapbase and the article was later updated with it) +- https://developer.valvesoftware.com/wiki/Viewmodel_Prediction_Fix (Viewmodel prediction fix) +- https://developer.valvesoftware.com/wiki/Owner#Collisions_with_owner (FSOLID_COLLIDE_WITH_OWNER flag) +- https://developer.valvesoftware.com/wiki/Npc_clawscanner#Strider_Scout_Issue (npc_clawscanner strider scout fix) +- https://developer.valvesoftware.com/wiki/Ambient_generic:_stop_and_toggle_fix (Fixes for stopping/toggling ambient_generic) +- https://developer.valvesoftware.com/wiki/Func_clip_vphysics ("Start Disabled" keyvalue fix) + +//--------------------------------------------------------------------------------------------------------------------------------------------------- + +Direct contributions: + +- https://github.com/mapbase-source/source-sdk-2013/pull/5 (Custom VScript implementation by ReDucTor; was placed into feature branch before being merged in a subsequent PR) +- https://github.com/mapbase-source/source-sdk-2013/pull/3 ("playvideo" command playback fix from Avantate) +- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux fixes from z33ky) +- Demo autorecord code provided by Klems +- cc_emit crash fix provided by 1upD +- Custom HL2 ammo crate models created by Rara (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code) +- Combine lock hardware on door01_left.mdl created by Kralich (This is asset-based and not reflected in the code) +- npc_vehicledriver fixes provided by CrAzY + +//--------------------------------------------------------------------------------------------------------------------------------------------------- + +Other sources: + +- Various code from Source SDK 2013 pull requests on the GitHub repository (https://github.com/ValveSoftware/source-sdk-2013/pulls): +-- https://github.com/ValveSoftware/source-sdk-2013/pull/441 (Smooth scrape sound oversight fix) +-- https://github.com/ValveSoftware/source-sdk-2013/pull/436 (VRAD debug counter fix + thread bump) +-- https://github.com/ValveSoftware/source-sdk-2013/pull/416 (Ragdoll null pointer dereference fix) +-- https://github.com/ValveSoftware/source-sdk-2013/pull/401 (func_rot_button "Starts locked" flag fix) +-- https://github.com/ValveSoftware/source-sdk-2013/pull/391 (VBSP func_detail smoothing group fix) +-- https://github.com/ValveSoftware/source-sdk-2013/pull/362 (npc_manhack npc_maker fix; Adjusted for formatting and save/restore in Mapbase) +- https://github.com/momentum-mod/game/blob/1d066180b3bf74830c51e6914d46c40b0bea1fc2/mp/src/game/server/player.cpp#L6543 (spec_goto fix) +- Poison zombie barnacle crash fix implemented based on a snippet from HL2: Plus posted by Agent Agrimar on Discord (Mapbase makes the barnacle recognize it as poison just like poison headcrabs) +- https://gamebanana.com/skins/172192 (Airboat handling fix; This is asset-based and not reflected in the code) +- Vortigaunt LOS fix originally created by dky.tehkingd.u for HL2:CE +- https://combineoverwiki.net/wiki/File:Combine_main_symbol.svg ("icon_combine" instructor icon in "materials/vgui/hud/gameinstructor_hl2_1"; This is asset-based and not reflected in the code) + +//--------------------------------------------------------------------------------------------------------------------------------------------------- + +If there is anything missing from this list, please contact Blixibon. + +//=================================================================================================================================================== + +Aside from the content list above, Mapbase has more descriptive and up-to-date credits on this wiki article: https://github.com/mapbase-source/source-sdk-2013/wiki/Mapbase-Credits +//=================================================================================================================================================== + Please see the Source SDK 2013 license below: diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index 2d2fc3d2cd..639fd4c581 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -85,9 +85,7 @@ static inline bool ShouldDrawLocalPlayerViewModel( void ) { #if defined( PORTAL ) return false; -#else - -#ifdef MAPBASE +#elif MAPBASE // We shouldn't draw the viewmodel externally. C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); if (localplayer) @@ -101,15 +99,13 @@ static inline bool ShouldDrawLocalPlayerViewModel( void ) } // Since we already have the local player, check its own ShouldDrawThisPlayer() to avoid extra checks - return localplayer->ShouldDrawThisPlayer(); + return !localplayer->ShouldDrawThisPlayer(); } else return false; #else return !C_BasePlayer::ShouldDrawLocalPlayer(); #endif - -#endif } //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index eceec4d87f..b4f9e404b2 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -459,6 +459,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + + DEFINE_SCRIPTFUNC( entindex, "" ) #endif END_SCRIPTDESC(); diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index 515ba21ffc..a37140d467 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -868,6 +868,7 @@ class C_BaseEntity : public IClientEntity void SetSize( const Vector &vecMin, const Vector &vecMax ); // UTIL_SetSize( pev, mins, maxs ); char const *GetClassname( void ); char const *GetDebugName( void ); + virtual const char *GetPlayerName() const { return NULL; } static int PrecacheModel( const char *name ); static bool PrecacheSound( const char *name ); static void PrefetchSound( const char *name ); diff --git a/sp/src/game/client/c_baselesson.cpp b/sp/src/game/client/c_baselesson.cpp new file mode 100644 index 0000000000..a968ea83a9 --- /dev/null +++ b/sp/src/game/client/c_baselesson.cpp @@ -0,0 +1,3876 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler implementations for instruction players how to play +// +//=============================================================================// + +#include "cbase.h" + +#include "c_baselesson.h" +#include "c_gameinstructor.h" + +#include "hud_locator_target.h" +#include "c_world.h" +#include "iinput.h" +#include "ammodef.h" +#include "vprof.h" +#include "view.h" +#include "vstdlib/ikeyvaluessystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Configuración +//========================================================= + +#define LESSON_PRIORITY_MAX 1000 +#define LESSON_PRIORITY_NONE 0 +#define LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED 1.5f +#define LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED 0.1f +#define LESSON_DISTANCE_UPDATE_RATE 0.25f + +// See comments in UtlSymbol on why this is useful and how it works +IMPLEMENT_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol ); + +extern ConVar gameinstructor_verbose; +extern ConVar gameinstructor_verbose_lesson; +extern ConVar gameinstructor_find_errors; + +#ifdef MAPBASE +// Mapbase was originally going to use a HL2-style default color (245,232,179). +// This is no longer the case, but mods are free to change this cvar in their config files. +ConVar gameinstructor_default_captioncolor( "gameinstructor_default_captioncolor", "255,255,255", FCVAR_NONE ); +ConVar gameinstructor_default_bindingcolor( "gameinstructor_default_bindingcolor", "0,0,0", FCVAR_NONE ); +#endif + +// +// CGameInstructorLesson +// + +Color CBaseLesson::m_rgbaVerboseHeader = Color( 255, 128, 64, 255 ); +Color CBaseLesson::m_rgbaVerbosePlain = Color( 64, 128, 255, 255 ); +Color CBaseLesson::m_rgbaVerboseName = Color( 255, 255, 255, 255 ); +Color CBaseLesson::m_rgbaVerboseOpen = Color( 0, 255, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseClose = Color( 255, 0, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseSuccess = Color( 255, 255, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseUpdate = Color( 255, 0, 255, 255 ); + + +//========================================================= +// Constructor +//========================================================= +CBaseLesson::CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity ) +{ + COMPILE_TIME_ASSERT( sizeof( CGameInstructorSymbol ) == sizeof( CUtlSymbol ) ); + + m_stringName = pchName; + m_stringReplaceKey = ""; + m_bIsDefaultHolder = bIsDefaultHolder; + m_bIsOpenOpportunity = bIsOpenOpportunity; + + Init(); +} + +//========================================================= +// Destructor +//========================================================= +CBaseLesson::~CBaseLesson() +{ + // Remove from root's children list + if ( m_pRoot ) + m_pRoot->m_OpenOpportunities.FindAndRemove(this); + + else + { + for ( int i = 0; i < m_OpenOpportunities.Count(); ++i ) + { + // Remove from children if they are still around + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + pLesson->m_pRoot = NULL; + } + } +} + +//========================================================= +//========================================================= +void CBaseLesson::AddPrerequisite( const char *pchLessonName ) +{ + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "\t%s: ", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Adding prereq " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pchLessonName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + const CBaseLesson *pPrerequisite = GetGameInstructor().GetLesson( pchLessonName ); + + if ( !pPrerequisite ) + { + DevWarning( "Prerequisite %s added by lesson %s doesn't exist!\n", pchLessonName, GetName() ); + return; + } + + m_Prerequisites.AddToTail(pPrerequisite); +} + +//========================================================= +//========================================================= +void CBaseLesson::SetRoot( CBaseLesson *pRoot ) +{ + m_pRoot = pRoot; + + if ( m_pRoot->m_OpenOpportunities.Find( this ) == -1 ) + m_pRoot->m_OpenOpportunities.AddToTail( this ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::ShouldShowSpew() +{ + // @DEBUG + return true; + + if ( gameinstructor_verbose_lesson.GetString()[ 0 ] == '\0' ) + return false; + + return ( Q_stristr( GetName(), gameinstructor_verbose_lesson.GetString() ) != NULL ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::NoPriority() const +{ + return ( m_iPriority == LESSON_PRIORITY_NONE ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsLocked() const +{ + if ( m_fLockDuration == 0.0f ) + return false; + + if ( !IsInstructing() || !IsVisible() ) + return false; + + float fLockTime = m_fLockTime; + + if ( fLockTime == 0.0f ) + fLockTime = m_fStartTime; + + return ( gpGlobals->curtime > m_fStartTime + LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED && gpGlobals->curtime < fLockTime + m_fLockDuration ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsLearned() const +{ + if ( m_iDisplayLimit > 0 && m_iDisplayCount >= m_iDisplayLimit ) + return true; + + if ( m_iSuccessLimit > 0 && m_iSuccessCount >= m_iSuccessLimit ) + return true; + + return false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::PrerequisitesHaveBeenMet() const +{ + for ( int i = 0; i < m_Prerequisites.Count(); ++i ) + { + if ( !m_Prerequisites[ i ]->IsLearned() ) + { + // Failed a prereq + return false; + } + } + + // All prereqs passed + return true; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsTimedOut() +{ + VPROF_BUDGET( "CBaseLesson::IsTimedOut", "GameInstructor" ); + + // Check for no timeout + if ( m_fTimeout == 0.0f ) + return false; + + float fStartTime = m_fStartTime; + + if ( GetRoot()->IsLearned() ) + { + if ( !m_bBumpWithTimeoutWhenLearned ) + { + // Time out instantly if we've learned this and don't want to keep it open for priority bumping + return true; + } + else + { + // It'll never be active, so lets use timeout based on when it was initialized + fStartTime = m_fInitTime; + } + } + + if ( !fStartTime ) + { + if ( !m_bCanTimeoutWhileInactive ) + { + return false; + } + + // Not active, so lets use timeout based on when it was initialized + fStartTime = m_fInitTime; + } + + bool bTimedOut = ( fStartTime + m_fTimeout < gpGlobals->curtime ); + + if ( bTimedOut ) + SetCloseReason( "Timed out." ); + + return bTimedOut; +} + +//========================================================= +//========================================================= +void CBaseLesson::ResetDisplaysAndSuccesses() +{ + m_iDisplayCount = 0; + m_bSuccessCounted = false; + m_iSuccessCount = 0; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IncDisplayCount() +{ + if ( m_iDisplayCount < m_iDisplayLimit ) + { + m_iDisplayCount++; + return true; + } + + return false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IncSuccessCount() +{ + if ( m_iSuccessCount < m_iSuccessLimit ) + { + m_iSuccessCount++; + return true; + } + + return false; +} + +//========================================================= +//========================================================= +void CBaseLesson::Init() +{ + m_pRoot = NULL; + m_bSuccessCounted = false; + + SetCloseReason( "None given." ); + + m_iPriority = LESSON_PRIORITY_MAX; // Set to invalid value to ensure that it is actually set later on + m_iInstanceType = LESSON_INSTANCE_MULTIPLE; + m_iFixedInstancesMax = 1; + m_bReplaceOnlyWhenStopped = false; + m_iTeam = TEAM_ANY; + m_bOnlyKeyboard = false; + m_bOnlyGamepad = false; + + m_iDisplayLimit = 0; + m_iDisplayCount = 0; + m_bWasDisplayed = false; + + m_iSuccessLimit = 0; + m_iSuccessCount = 0; + + m_fLockDuration = 0.0f; + m_bCanOpenWhenDead = false; + m_bBumpWithTimeoutWhenLearned = false; + m_bCanTimeoutWhileInactive = false; + m_fTimeout = 0.0f; + + m_fInitTime = gpGlobals->curtime; + m_fStartTime = 0.0f; + m_fLockTime = 0.0f; + + m_fUpdateInterval = 0.5; + m_bHasPlayedSound = false; + + m_szStartSound = "Instructor.LessonStart"; + m_szLessonGroup = ""; + + m_iNumDelayedPlayerSwaps = 0; +} + +//========================================================= +//========================================================= +void CBaseLesson::TakePlaceOf( CBaseLesson *pLesson ) +{ + // Transfer over marked as displayed so a replaced lesson won't count as an extra display + m_bWasDisplayed = pLesson->m_bWasDisplayed; + pLesson->m_bWasDisplayed = false; +} + +//========================================================= +//========================================================= +void CBaseLesson::MarkSucceeded() +{ + if ( !m_bSuccessCounted ) + { + GetGameInstructor().MarkSucceeded( GetName() ); + m_bSuccessCounted = true; + } +} + +//========================================================= +//========================================================= +void CBaseLesson::CloseOpportunity( const char *pchReason ) +{ + SetCloseReason(pchReason); + m_bIsOpenOpportunity = false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::DoDelayedPlayerSwaps() const +{ + // A bot has swapped places with a player or player with a bot... + // At the time of the actual swap there was no client representation for the new player... + // So that swap was queued up and now we're going to make things right! + while ( m_iNumDelayedPlayerSwaps ) + { + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].iNewUserID ); + + if ( !pNewPlayer ) + { + // There is still no client representation of the new player, we'll have to try again later + if ( gameinstructor_verbose.GetInt() > 1 ) + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tFailed delayed player swap!" ); + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 1 ) + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tSuccessful delayed player swap!" ); + + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].phHandleToChange->Set( pNewPlayer ); + m_iNumDelayedPlayerSwaps--; + } + + return true; +} + + +// +// CTextLesson +// + +//========================================================= +//========================================================= +void CTextLesson::Init() +{ + m_szDisplayText = ""; + m_szDisplayParamText = ""; + m_szBinding = ""; + m_szGamepadBinding = ""; +} + +//========================================================= +//========================================================= +void CTextLesson::Start() +{ + // TODO: Display some text + //m_szDisplayText +} + +//========================================================= +//========================================================= +void CTextLesson::Stop() +{ + // TODO: Clean up text +} + +// +// CIconLesson +// + +void CIconLesson::Init() +{ + m_hIconTarget = NULL; + m_szVguiTargetName = ""; + m_szVguiTargetLookup = ""; + m_nVguiTargetEdge = 0; + + m_hLocatorTarget = -1; + m_bFixedPosition = false; + m_bNoIconTarget = false; + m_bAllowNodrawTarget = false; + + m_bVisible = true; + m_bShowWhenOccluded = true; + m_bNoOffscreen = false; + m_bForceCaption = false; + + m_szOnscreenIcon = ""; + m_szOffscreenIcon = ""; + + m_flUpOffset = 0.0f; + m_flRelativeUpOffset = 0.0f; + m_fFixedPositionX = 0.0f; + m_fFixedPositionY = 0.0f; + + m_fRange = 0.0f; + m_fCurrentDistance = 0.0f; + + m_fOnScreenStartTime = 0.0f; + m_fUpdateDistanceTime = 0.0f; + + m_iFlags = LOCATOR_ICON_FX_NONE; +#ifdef MAPBASE + m_szCaptionColor = gameinstructor_default_captioncolor.GetString(); +#else + m_szCaptionColor = "255,255,255";// Default to white +#endif +} + +//========================================================= +//========================================================= +void CIconLesson::Start() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + // Display some text + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + // Wanted one, but couldn't get it + CloseOpportunity( "Icon Target handle went invalid before the lesson started!" ); + } + + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + CLocatorTarget *pLocatorTarget = NULL; + + if ( m_hLocatorTarget != -1 ) + { + // Lets try the handle that we've held on to + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + { + // It's gone stale, get a new target + m_hLocatorTarget = Locator_AddTarget(); + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + } + } + else + { + // Get a new target + m_hLocatorTarget = Locator_AddTarget(); + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + } + + if( m_hLocatorTarget == -1 || !pLocatorTarget ) + { + CloseOpportunity( "Could not get a handle for new locator target. Too many targets in use!" ); + return; + } + + pLocatorTarget->AddIconEffects( m_iFlags ); + pLocatorTarget->SetCaptionColor( GetCaptionColorString() ); + UpdateLocatorTarget( pLocatorTarget, pIconTarget ); + + // Update occlusion data + Locator_ComputeTargetIconPositionFromHandle( m_hLocatorTarget ); +} + +//========================================================= +//========================================================= +void CIconLesson::Stop() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + if ( m_hLocatorTarget != -1 ) + Locator_RemoveTarget( m_hLocatorTarget ); + + m_fOnScreenStartTime = 0.0f; +} + +//========================================================= +//========================================================= +void CIconLesson::Update() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + CloseOpportunity( "Lost our icon target handle returned NULL." ); + } + + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + if ( !pLocatorTarget ) + { + // Temp instrumentation to catch a bug - possibly calling Update without having called Start? + Warning( "Problem in lesson %s: Locator_GetTargetFromHandle returned null for handle %d.\n IsInstanceActive: %s. IsInstructing: %s. IsLearned: %s\n", + GetName(), m_hLocatorTarget, + (IsInstanceActive() ? "yes" : "no"), + (IsInstructing() ? "yes" : "no"), + (IsLearned() ? "yes" : "no") ); + CloseOpportunity( "Lost locator target handle." ); + return; + } + + UpdateLocatorTarget( pLocatorTarget, pIconTarget ); + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + // Check if it has been onscreen long enough to count as being displayed + if ( m_fOnScreenStartTime == 0.0f ) + { + if ( pLocatorTarget->IsOnScreen() && ( IsPresentComplete() || ( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) ) + { + // Is either static or has finished presenting and is on screen + m_fOnScreenStartTime = gpGlobals->curtime; + } + } + else + { + if ( !pLocatorTarget->IsOnScreen() ) + { + // Was visible before, but it isn't now + m_fOnScreenStartTime = 0.0f; + } + else if ( gpGlobals->curtime - m_fOnScreenStartTime >= LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED ) + { + // Lesson on screen long enough to be counted as displayed + m_bWasDisplayed = true; + } + } + + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + // Update it's distance from the local player + C_BaseEntity *pTarget = m_hIconTarget.Get(); + + if ( !pLocalPlayer || !pTarget || pLocalPlayer == pTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pTarget->WorldSpaceCenter() ); + } + + m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE; + } +} + +void CIconLesson::UpdateInactive() +{ + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + CloseOpportunity( "Lost our icon target handle returned NULL." ); + } + + m_fCurrentDistance = 0.0f; + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + // Update it's distance from the local player + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( !pLocalPlayer || pLocalPlayer == pIconTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pIconTarget->WorldSpaceCenter() ); + } + + m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE; + } +} + +bool CIconLesson::ShouldDisplay() const +{ + VPROF_BUDGET( "CIconLesson::ShouldDisplay", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return false; + } + + if ( m_fRange > 0.0f && m_fCurrentDistance > m_fRange ) + { + // Distance to target is more than the max range + return false; + } + + if ( !m_bShowWhenOccluded && m_hLocatorTarget >= 0 ) + { + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( pLocatorTarget && pLocatorTarget->IsOccluded() ) + { + // Target is occluded and doesn't want to be shown when occluded + return false; + } + } + + // Ok to display + return true; +} + +bool CIconLesson::IsVisible() const +{ + VPROF_BUDGET( "CIconLesson::IsVisible", "GameInstructor" ); + + if( m_hLocatorTarget == -1 ) + { + // If it doesn't want a target, it's "visible" otherwise we'll have to call it invisible + return m_bNoIconTarget; + } + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + if ( !pLocatorTarget ) + { + return false; + } + + return pLocatorTarget->IsVisible(); +} + +void CIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID ) +{ + BaseClass::SwapOutPlayers( iOldUserID, iNewUserID ); + + if ( m_bNoIconTarget ) + return; + + // Get the player pointers from the user IDs + C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID ); + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID ); + + if ( pOldPlayer == m_hIconTarget.Get() ) + { + if ( pNewPlayer ) + { + m_hIconTarget = pNewPlayer; + } + else + { + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hIconTarget; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } +} + +void CIconLesson::TakePlaceOf( CBaseLesson *pLesson ) +{ + BaseClass::TakePlaceOf( pLesson ); + + const CIconLesson *pIconLesson = dynamic_cast( pLesson ); + + if ( pIconLesson ) + { + if ( pIconLesson->m_hLocatorTarget != -1 ) + { + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( pIconLesson->m_hLocatorTarget ); + + if ( pLocatorTarget ) + { + // This one draw right to the hud... use it's icon target handle + m_hLocatorTarget = pIconLesson->m_hLocatorTarget; + } + } + + m_fOnScreenStartTime = pIconLesson->m_fOnScreenStartTime; + } +} + +void CIconLesson::SetLocatorBinding( CLocatorTarget * pLocatorTarget ) +{ + if ( IsX360() /*|| input->ControllerModeActive()*/ ) + { + // Try to use gamepad bindings first + if ( m_szGamepadBinding.String()[ 0 ] != '\0' ) + { + // Found gamepad binds! + pLocatorTarget->SetBinding( m_szGamepadBinding.String() ); + } + else + { + // No gamepad binding, so fallback to the regular binding + pLocatorTarget->SetBinding( m_szBinding.String() ); + } + } + else + { + // Always use the regular binding when the gamepad is disabled + pLocatorTarget->SetBinding( m_szBinding.String() ); + } +} + +bool CIconLesson::IsPresentComplete() +{ + if ( m_hLocatorTarget == -1 ) + return false; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return false; + + return !pLocatorTarget->IsPresenting(); +} + +void CIconLesson::PresentStart() +{ + if ( m_hLocatorTarget == -1 ) + return; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return; + + pLocatorTarget->StartPresent(); +} + +void CIconLesson::PresentEnd() +{ + if ( m_hLocatorTarget == -1 ) + return; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return; + + pLocatorTarget->EndPresent(); +} + +void CIconLesson::UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget ) +{ + if ( m_bFixedPosition ) + { + pLocatorTarget->m_bOriginInScreenspace = true; + pLocatorTarget->m_vecOrigin.x = m_fFixedPositionX; + pLocatorTarget->m_vecOrigin.y = m_fFixedPositionY; + pLocatorTarget->SetVguiTargetName( m_szVguiTargetName.String() ); + pLocatorTarget->SetVguiTargetLookup( m_szVguiTargetLookup.String() ); + pLocatorTarget->SetVguiTargetEdge( m_nVguiTargetEdge ); + } + else + { + pLocatorTarget->m_bOriginInScreenspace = false; +#ifdef MAPBASE + pLocatorTarget->m_vecOrigin = GetIconTargetPosition( pIconTarget ) + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset ); +#else + pLocatorTarget->m_vecOrigin = pIconTarget->EyePosition() + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset ); +#endif + pLocatorTarget->SetVguiTargetName( "" ); + } + + const char *pchDisplayParamText = m_szDisplayParamText.String(); +#ifdef INFESTED_DLL + char szCustomName[ 256 ]; +#endif + + // Check if the parameter is the be the player display name + if ( Q_stricmp( pchDisplayParamText, "use_name" ) == 0 ) + { + // Fix up the player display name + C_BasePlayer *pPlayer = ToBasePlayer( pIconTarget ); + if ( pPlayer ) + { + pchDisplayParamText = pPlayer->GetPlayerName(); + } + else + { + bool bNoName = true; + +#ifdef INFESTED_DLL + C_ASW_Marine *pMarine = dynamic_cast< C_ASW_Marine* >( pIconTarget ); + if ( pMarine ) + { + C_ASW_Marine_Resource *pMR = pMarine->GetMarineResource(); + if ( pMR ) + { + pMR->GetDisplayName( szCustomName, sizeof( szCustomName ) ); + pchDisplayParamText = szCustomName; + bNoName = false; + } + } +#endif + + if ( bNoName ) + { + // It's not a player! + pchDisplayParamText = ""; + } + } + } + + pLocatorTarget->SetCaptionText( m_szDisplayText.String(), pchDisplayParamText ); + SetLocatorBinding( pLocatorTarget ); + pLocatorTarget->SetOnscreenIconTextureName( m_szOnscreenIcon.String() ); + pLocatorTarget->SetOffscreenIconTextureName( m_szOffscreenIcon.String() ); + pLocatorTarget->SetVisible( m_bVisible ); + + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if( !m_bFixedPosition && + ( ( pLocalPlayer != NULL && pLocalPlayer == m_hIconTarget ) || + GetClientWorldEntity() == m_hIconTarget ) ) + { + // Mark this icon as a static icon that draws in a fixed + // location on the hud rather than tracking an object + // in 3D space. + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_STATIC ); + } + else + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NONE ); + } + + if ( m_bNoOffscreen ) + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN ); + } + else + { + pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN ); + } + + if( m_bForceCaption || IsLocked() ) + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION ); + } + else + { + pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION ); + } + + pLocatorTarget->Update(); + + if ( pLocatorTarget->m_bIsDrawing ) + { + if ( !m_bHasPlayedSound ) + { + GetGameInstructor().PlaySound( m_szStartSound.String() ); + m_bHasPlayedSound = true; + } + } +} + +#ifdef MAPBASE +Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget ) +{ + switch (m_iIconTargetPos) + { + default: + case ICON_TARGET_EYE_POSITION: + return pIconTarget->EyePosition(); + + case ICON_TARGET_ORIGIN: + return pIconTarget->GetAbsOrigin(); + + case ICON_TARGET_CENTER: + return pIconTarget->WorldSpaceCenter(); + } +} +#endif + +// +// CScriptedIconLesson +// + +// Linking variables to scriptable entries is done here! +// The first parameter correlates to the case insensitive string name read from scripts. +// This macro generates code that passes this consistent variable data in to other macros +#define LESSON_VARIABLE_FACTORY \ + LESSON_VARIABLE_MACRO_EHANDLE( VOID, m_hLocalPlayer, EHANDLE ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( LOCAL_PLAYER, m_hLocalPlayer, EHANDLE ) \ + LESSON_VARIABLE_MACRO( OUTPUT, m_fOutput, float ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( ENTITY1, m_hEntity1, EHANDLE ) \ + LESSON_VARIABLE_MACRO_EHANDLE( ENTITY2, m_hEntity2, EHANDLE ) \ + LESSON_VARIABLE_MACRO_STRING( STRING1, m_szString1, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( STRING2, m_szString2, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( INTEGER1, m_iInteger1, int ) \ + LESSON_VARIABLE_MACRO( INTEGER2, m_iInteger2, int ) \ + LESSON_VARIABLE_MACRO( FLOAT1, m_fFloat1, float ) \ + LESSON_VARIABLE_MACRO( FLOAT2, m_fFloat2, float ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( ICON_TARGET, m_hIconTarget, EHANDLE ) \ + LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_NAME, m_szVguiTargetName, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_LOOKUP, m_szVguiTargetLookup, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( VGUI_TARGET_EDGE, m_nVguiTargetEdge, int ) \ + LESSON_VARIABLE_MACRO( FIXED_POSITION_X, m_fFixedPositionX, float ) \ + LESSON_VARIABLE_MACRO( FIXED_POSITION_Y, m_fFixedPositionY, float ) \ + LESSON_VARIABLE_MACRO_BOOL( FIXED_POSITION, m_bFixedPosition, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( NO_ICON_TARGET, m_bNoIconTarget, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( ALLOW_NODRAW_TARGET, m_bAllowNodrawTarget, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( VISIBLE, m_bVisible, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( SHOW_WHEN_OCCLUDED, m_bShowWhenOccluded, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( NO_OFFSCREEN, m_bNoOffscreen, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( FORCE_CAPTION, m_bForceCaption, bool ) \ + LESSON_VARIABLE_MACRO_STRING( ONSCREEN_ICON, m_szOnscreenIcon, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( OFFSCREEN_ICON, m_szOffscreenIcon, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( ICON_OFFSET, m_flUpOffset, float ) \ + LESSON_VARIABLE_MACRO( ICON_RELATIVE_OFFSET, m_flRelativeUpOffset, float ) \ + LESSON_VARIABLE_MACRO( RANGE, m_fRange, float ) \ + \ + LESSON_VARIABLE_MACRO( FLAGS, m_iFlags, int ) \ + LESSON_VARIABLE_MACRO_STRING( CAPTION_COLOR, m_szCaptionColor, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( GROUP, m_szLessonGroup, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO_STRING( CAPTION, m_szDisplayText, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( CAPTION_PARAM, m_szDisplayParamText, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( BINDING, m_szBinding, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( GAMEPAD_BINDING, m_szGamepadBinding, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( PRIORITY, m_iPriority, int ) \ + LESSON_VARIABLE_MACRO_STRING( REPLACE_KEY, m_stringReplaceKey, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( LOCK_DURATION, m_fLockDuration, float ) \ + LESSON_VARIABLE_MACRO_BOOL( CAN_OPEN_WHEN_DEAD, m_bCanOpenWhenDead, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( BUMP_WITH_TIMEOUT_WHEN_LEARNED, m_bBumpWithTimeoutWhenLearned, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( CAN_TIMEOUT_WHILE_INACTIVE, m_bCanTimeoutWhileInactive, bool ) \ + LESSON_VARIABLE_MACRO( TIMEOUT, m_fTimeout, float ) \ + LESSON_VARIABLE_MACRO( UPDATE_INTERVAL, m_fUpdateInterval, float ) \ + LESSON_VARIABLE_MACRO_STRING( START_SOUND, m_szStartSound, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( ICON_TARGET_POS, m_iIconTargetPos, int ) \ + + +// Create keyvalues name symbol +#define LESSON_VARIABLE_SYMBOL( _varEnum, _varName, _varType ) static int g_n##_varEnum##Symbol; + +#define LESSON_VARIABLE_INIT_SYMBOL( _varEnum, _varName, _varType ) g_n##_varEnum##Symbol = KeyValuesSystem()->GetSymbolForString( #_varEnum ); + +#define LESSON_SCRIPT_STRING_ADD_TO_MAP( _varEnum, _varName, _varType ) g_NameToTypeMap.Insert( #_varEnum, LESSON_VARIABLE_##_varEnum## ); + +// Create enum value +#define LESSON_VARIABLE_ENUM( _varEnum, _varName, _varType ) LESSON_VARIABLE_##_varEnum##, + +// Init info call +#define LESSON_VARIABLE_INIT_INFO_CALL( _varEnum, _varName, _varType ) g_pLessonVariableInfo[ LESSON_VARIABLE_##_varEnum## ].Init_##_varEnum##(); + +// Init info +#define LESSON_VARIABLE_INIT_INFO( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = LessonParamTypeFromString( #_varType ); \ + } + +#define LESSON_VARIABLE_INIT_INFO_BOOL( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = FIELD_BOOLEAN; \ + } + +#define LESSON_VARIABLE_INIT_INFO_EHANDLE( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = FIELD_EHANDLE; \ + } + +#define LESSON_VARIABLE_INIT_INFO_STRING( _varEnum, _varName, _varType ) \ + void Init_##_varEnum##() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \ + varType = FIELD_STRING; \ + } + +// Copy defaults into this scripted lesson into a new one +#define LESSON_VARIABLE_DEFAULT( _varEnum, _varName, _varType ) ( _varName = m_pDefaultHolder->_varName ); + +// Copy a variable from this scripted lesson into a new one +#define LESSON_VARIABLE_COPY( _varEnum, _varName, _varType ) ( pOpenLesson->_varName = _varName ); + +// Return the first param if pchName is the same as the second param +#define LESSON_SCRIPT_STRING( _type, _string ) \ + if ( Q_stricmp( pchName, _string ) == 0 )\ + {\ + return _type;\ + } + +// Wrapper for using this macro in the factory +#define LESSON_SCRIPT_STRING_GENERAL( _varEnum, _varName, _varType ) LESSON_SCRIPT_STRING( LESSON_VARIABLE_##_varEnum##, #_varEnum ) + +// Process the element action on this variable +#define PROCESS_LESSON_ACTION( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum##:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float ); + +#define PROCESS_LESSON_ACTION_EHANDLE( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum##:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float, eventParam_BaseEntity, eventParam_string ); + +#define PROCESS_LESSON_ACTION_STRING( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum##:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, &_varName, &pLessonElement->szParam, eventParam_string ); + +// Init the variable from the script (or a convar) +#define LESSON_VARIABLE_INIT( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + const char *pchParam = pSubKey->GetString(); \ + if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \ + { \ + ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \ + if ( tempCVar.IsValid() ) \ + { \ + _varName = static_cast<_varType>( tempCVar.GetFloat() ); \ + } \ + else \ + { \ + _varName = static_cast<_varType>( 0.0f ); \ + } \ + } \ + else \ + { \ + _varName = static_cast<_varType>( pSubKey->GetFloat() ); \ + } \ + } + +#define LESSON_VARIABLE_INIT_BOOL( _varEnum, _varName, _varType ) \ + else if ( Q_stricmp( #_varEnum, pSubKey->GetName() ) == 0 ) \ + { \ + _varName = pSubKey->GetBool(); \ + } + +#define LESSON_VARIABLE_INIT_EHANDLE( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + DevWarning( "Can't initialize an EHANDLE from the instructor lesson script." ); \ + } + +#define LESSON_VARIABLE_INIT_STRING( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + const char *pchParam = pSubKey->GetString(); \ + if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \ + { \ + ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \ + if ( tempCVar.IsValid() ) \ + { \ + _varName = tempCVar.GetString(); \ + } \ + else \ + { \ + _varName = ""; \ + } \ + } \ + else \ + { \ + _varName = pSubKey->GetString(); \ + } \ + } + +// Gets a scripted variable by offset and casts it to the proper type +#define LESSON_VARIABLE_GET_FROM_OFFSET( _type, _offset ) *static_cast<_type*>( static_cast( static_cast( static_cast( this ) ) + _offset ) ) + + +// Enum of scripted variables +enum LessonVariable +{ + // Run enum macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_ENUM + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + LESSON_VARIABLE_TOTAL +}; + +// Declare the keyvalues symbols for the keynames +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_SYMBOL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + +// String lookup prototypes +LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames = true ); +_fieldtypes LessonParamTypeFromString( const char *pchName ); +int LessonActionFromString( const char *pchName ); + + +// This is used to get type info an variable offsets from the variable enumerated value +class LessonVariableInfo +{ +public: + + LessonVariableInfo() + : iOffset( 0 ), varType( FIELD_VOID ) + { + } + + // Run init info macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_BOOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_STRING + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + +public: + + int iOffset; + _fieldtypes varType; +}; + +LessonVariableInfo g_pLessonVariableInfo[ LESSON_VARIABLE_TOTAL ]; + + +const LessonVariableInfo *GetLessonVariableInfo( int iLessonVariable ) +{ + Assert( iLessonVariable >= 0 && iLessonVariable < LESSON_VARIABLE_TOTAL ); + + if ( g_pLessonVariableInfo[ 0 ].varType == FIELD_VOID ) + { + // Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_CALL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + return &(g_pLessonVariableInfo[ iLessonVariable ]); +} + +static CUtlDict< LessonVariable, int > g_NameToTypeMap; +static CUtlDict< fieldtype_t, int > g_TypeToParamTypeMap; +CUtlDict< int, int > CScriptedIconLesson::LessonActionMap; + +CScriptedIconLesson::~CScriptedIconLesson() +{ + if ( m_pDefaultHolder ) + { + delete m_pDefaultHolder; + m_pDefaultHolder = NULL; + } +} + + +void CScriptedIconLesson::Init() +{ + m_hLocalPlayer.Set( NULL ); + m_fOutput = 0.0f; + m_hEntity1.Set( NULL ); + m_hEntity2.Set( NULL ); + m_szString1 = ""; + m_szString2 = ""; + m_iInteger1 = 0; + m_iInteger2 = 0; + m_fFloat1 = 0.0f; + m_fFloat2 = 0.0f; + + m_fUpdateEventTime = 0.0f; + m_pDefaultHolder = NULL; + m_iScopeDepth = 0; + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing scripted lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + if ( !IsDefaultHolder() ) + { + if ( !IsOpenOpportunity() ) + { + // Initialize from the key value file + InitFromKeys( GetGameInstructor().GetScriptKeys() ); + + if ( m_iPriority >= LESSON_PRIORITY_MAX ) + { + DevWarning( "Priority level not set for lesson: %s\n", GetName() ); + } + + // We use this to remember variable defaults to be reset before each open attempt + m_pDefaultHolder = new CScriptedIconLesson( GetName(), true, false ); + CScriptedIconLesson *pOpenLesson = m_pDefaultHolder; + + // Run copy macros on all default scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // Listen for open events + for ( int iLessonEvent = 0; iLessonEvent < m_OpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_OpenEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + + // Listen for close events + for ( int iLessonEvent = 0; iLessonEvent < m_CloseEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_CloseEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + + // Listen for success events + for ( int iLessonEvent = 0; iLessonEvent < m_SuccessEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_SuccessEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String()); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + } + else + { + // This is an open lesson! Get the root for reference + const CScriptedIconLesson *pLesson = static_cast( GetGameInstructor().GetLesson( GetName() ) ); + SetRoot( const_cast( pLesson ) ); + } + } +} + +void CScriptedIconLesson::InitPrerequisites() +{ + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing prereqs for scripted lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + for ( int iPrerequisit = 0; iPrerequisit < m_PrerequisiteNames.Count(); ++iPrerequisit ) + { + const char *pPrerequisiteName = m_PrerequisiteNames[ iPrerequisit ].String(); + AddPrerequisite( pPrerequisiteName ); + } +} + +void CScriptedIconLesson::OnOpen() +{ + VPROF_BUDGET( "CScriptedIconLesson::OnOpen", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + const CScriptedIconLesson *pLesson = static_cast( GetRoot() ); + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_OnOpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->m_OnOpenEvents[ iLessonEvent ]); + + if ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "GAME INSTRUCTOR: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "OnOpen event " ); + ConColorMsg( Color( 0, 255, 0, 255 ), "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( Color( 64, 128, 255, 255 ), "received for lesson \"%s\"...\n", GetName() ); + } + + ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + BaseClass::OnOpen(); +} + +void CScriptedIconLesson::Update() +{ + VPROF_BUDGET( "CScriptedIconLesson::Update", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + const CScriptedIconLesson *pLesson = static_cast( GetRoot() ); + + if ( gpGlobals->curtime >= m_fUpdateEventTime ) + { + bool bShowSpew = ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() ); + + int iVerbose = gameinstructor_verbose.GetInt(); + if ( gameinstructor_verbose.GetInt() == 1 ) + { + // Force the verbose level from 1 to 0 for update events + gameinstructor_verbose.SetValue( 0 ); + } + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_UpdateEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->m_UpdateEvents[ iLessonEvent ]); + + if ( bShowSpew ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Update event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + gameinstructor_verbose.SetValue( iVerbose ); + + // Wait before doing update events again + m_fUpdateEventTime = gpGlobals->curtime + m_fUpdateInterval; + } + + BaseClass::Update(); +} + +void CScriptedIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID ) +{ + BaseClass::SwapOutPlayers( iOldUserID, iNewUserID ); + + // Get the player pointers from the user IDs + C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID ); + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID ); + + if ( pOldPlayer == m_hEntity1.Get() ) + { + if ( pNewPlayer ) + { + m_hEntity1 = pNewPlayer; + } + else + { + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity1; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } + + if ( pOldPlayer == m_hEntity2.Get() ) + { + if ( pNewPlayer ) + { + m_hEntity2 = pNewPlayer; + } + else + { + + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity2; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } +} + +void CScriptedIconLesson::FireGameEvent( IGameEvent *event ) +{ + VPROF_BUDGET( "CScriptedIconLesson::FireGameEvent", "GameInstructor" ); + + if ( m_bDisabled ) + return; + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + if ( !C_BasePlayer::GetLocalPlayer() ) + return; + + // Check that this lesson is allowed for the current input device + if( m_bOnlyKeyboard /*&& input->ControllerModeActive()*/ ) + return; + + if( m_bOnlyGamepad /*&& !input->ControllerModeActive()*/ ) + return; + + // Check that this lesson is for the proper team + CBasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( m_iTeam != TEAM_ANY && pLocalPlayer && pLocalPlayer->GetTeamNumber() != m_iTeam ) + { + // This lesson is intended for a different team + return; + } + + const char *name = event->GetName(); + + // Open events run on the root + ProcessOpenGameEvents( this, name, event ); + + // Close and success events run on the children + const CUtlVector < CBaseLesson * > *pChildren = GetChildren(); + for ( int iChild = 0; iChild < pChildren->Count(); ++iChild ) + { + CScriptedIconLesson *pScriptedChild = dynamic_cast( (*pChildren)[ iChild ] ); + + pScriptedChild->ProcessCloseGameEvents( this, name, event ); + pScriptedChild->ProcessSuccessGameEvents( this, name, event ); + } +} + +void CScriptedIconLesson::ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN && GetGameInstructor().IsLessonOfSameTypeOpen( this ) ) + { + // We don't want more than one of this type, and there is already one open + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pRootLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "open events NOT processed (there is already an open lesson of this type).\n" ); + } + + return; + } + + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_OpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_OpenEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( m_pDefaultHolder ) + { + // Run copy from default macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_DEFAULT + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\tAll elements returned true. Opening!\n" ); + } + + MEM_ALLOC_CREDIT(); + CScriptedIconLesson *pOpenLesson = new CScriptedIconLesson( GetName(), false, true ); + + // Run copy macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + if ( GetGameInstructor().OpenOpportunity( pOpenLesson ) ) + { + pOpenLesson->OnOpen(); + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN ) + { + // This one is open and we only want one! So, we're done. + // Other open events may be listening for the same events... skip them! + return; + } + } + } + } + } +} + +void CScriptedIconLesson::ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_CloseEvents.Count() && IsOpenOpportunity(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_CloseEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tAll elements returned true. Closing!\n" ); + } + + CloseOpportunity( "Close event elements completed." ); + } + } + } +} + +void CScriptedIconLesson::ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_SuccessEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_SuccessEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\tAll elements returned true. Succeeding!\n" ); + } + + MarkSucceeded(); + } + } + } +} + +LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames ) +{ + int slot = g_NameToTypeMap.Find( pchName ); + if ( slot != g_NameToTypeMap.InvalidIndex() ) + return g_NameToTypeMap[ slot ]; + + if ( bWarnOnInvalidNames ) + { + AssertMsg( 0, "Invalid scripted lesson variable!" ); + DevWarning( "Invalid scripted lesson variable: %s\n", pchName ); + } + + return LESSON_VARIABLE_TOTAL; +} + +_fieldtypes LessonParamTypeFromString( const char *pchName ) +{ + int slot = g_TypeToParamTypeMap.Find( pchName ); + if ( slot != g_TypeToParamTypeMap.InvalidIndex() ) + return g_TypeToParamTypeMap[ slot ]; + + DevWarning( "Invalid scripted lesson variable/param type: %s\n", pchName ); + return FIELD_VOID; +} + +int LessonActionFromString( const char *pchName ) +{ + int slot = CScriptedIconLesson::LessonActionMap.Find( pchName ); + if ( slot != CScriptedIconLesson::LessonActionMap.InvalidIndex() ) + return CScriptedIconLesson::LessonActionMap[ slot ]; + + DevWarning( "Invalid scripted lesson action: %s\n", pchName ); + return LESSON_ACTION_NONE; +} + +void CScriptedIconLesson::InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ) +{ + KeyValues *pSubKey = NULL; + for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + char szSubKeyName[ 256 ]; + Q_strcpy( szSubKeyName, pSubKey->GetName() ); + + char *pchToken = strtok( szSubKeyName, " " ); + LessonVariable iVariable = LessonVariableFromString( pchToken ); + + pchToken = strtok ( NULL, "" ); + int iAction = LESSON_ACTION_NONE; + bool bNot = false; + bool bOptionalParam = false; + + if ( !pchToken || pchToken[ 0 ] == '\0' ) + { + DevWarning( "No action specified for variable: \"%s\"\n", pSubKey->GetName() ); + } + else + { + if ( pchToken[ 0 ] == '?' ) + { + pchToken++; + bOptionalParam = true; + } + + if ( pchToken[ 0 ] == '!' ) + { + pchToken++; + bNot = true; + } + + iAction = LessonActionFromString( pchToken ); + } + + Q_strcpy( szSubKeyName, pSubKey->GetString() ); + + pchToken = strtok( szSubKeyName, " " ); + _fieldtypes paramType = LessonParamTypeFromString( pchToken ); + + char *pchParam = ""; + + if ( paramType != FIELD_VOID ) + { + pchToken = strtok ( NULL, "" ); + pchParam = pchToken; + } + + if ( !pchParam ) + { + DevWarning( "No parameter specified for action: \"%s\"\n", pSubKey->GetName() ); + } + else + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t\tElement \"%s %s\" added.\n", pSubKey->GetName(), pSubKey->GetString() ); + } + + // See if our param is a scripted var + LessonVariable iParamVarIndex = LessonVariableFromString( pchParam, false ); + + pLessonElements->AddToTail( LessonElement_t( iVariable, iAction, bNot, bOptionalParam, pchParam, iParamVarIndex, paramType ) ); + } + } +} + +void CScriptedIconLesson::InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ) +{ + for ( int i = 0; i < pLessonElements2->Count(); ++i ) + { + pLessonElements->AddToTail( LessonElement_t( (*pLessonElements2)[ i ] ) ); + } +} + +void CScriptedIconLesson::InitFromKeys( KeyValues *pKey ) +{ + if ( !pKey ) + return; + + static int s_nInstanceTypeSymbol = KeyValuesSystem()->GetSymbolForString( "instance_type" ); + static int s_nReplaceKeySymbol = KeyValuesSystem()->GetSymbolForString( "replace_key" ); + static int s_nFixedInstancesMaxSymbol = KeyValuesSystem()->GetSymbolForString( "fixed_instances_max" ); + static int s_nReplaceOnlyWhenStopped = KeyValuesSystem()->GetSymbolForString( "replace_only_when_stopped" ); + static int s_nTeamSymbol = KeyValuesSystem()->GetSymbolForString( "team" ); + static int s_nOnlyKeyboardSymbol = KeyValuesSystem()->GetSymbolForString( "only_keyboard" ); + static int s_nOnlyGamepadSymbol = KeyValuesSystem()->GetSymbolForString( "only_gamepad" ); + static int s_nDisplayLimitSymbol = KeyValuesSystem()->GetSymbolForString( "display_limit" ); + static int s_nSuccessLimitSymbol = KeyValuesSystem()->GetSymbolForString( "success_limit" ); + static int s_nPreReqSymbol = KeyValuesSystem()->GetSymbolForString( "prereq" ); + static int s_nOpenSymbol = KeyValuesSystem()->GetSymbolForString( "open" ); + static int s_nCloseSymbol = KeyValuesSystem()->GetSymbolForString( "close" ); + static int s_nSuccessSymbol = KeyValuesSystem()->GetSymbolForString( "success" ); + static int s_nOnOpenSymbol = KeyValuesSystem()->GetSymbolForString( "onopen" ); + static int s_nUpdateSymbol = KeyValuesSystem()->GetSymbolForString( "update" ); + + KeyValues *pSubKey = NULL; + for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + if ( pSubKey->GetNameSymbol() == s_nInstanceTypeSymbol ) + { + m_iInstanceType = LessonInstanceType( pSubKey->GetInt() ); + } + else if ( pSubKey->GetNameSymbol() == s_nReplaceKeySymbol ) + { + m_stringReplaceKey = pSubKey->GetString(); + } + else if ( pSubKey->GetNameSymbol() == s_nFixedInstancesMaxSymbol ) + { + m_iFixedInstancesMax = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nReplaceOnlyWhenStopped ) + { + m_bReplaceOnlyWhenStopped = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nTeamSymbol ) + { + m_iTeam = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nOnlyKeyboardSymbol ) + { + m_bOnlyKeyboard = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nOnlyGamepadSymbol ) + { + m_bOnlyGamepad = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nDisplayLimitSymbol ) + { + m_iDisplayLimit = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nSuccessLimitSymbol ) + { + m_iSuccessLimit = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nPreReqSymbol ) + { + CGameInstructorSymbol pName; + pName = pSubKey->GetString(); + m_PrerequisiteNames.AddToTail( pName ); + } + else if ( pSubKey->GetNameSymbol() == s_nOpenSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddOpenEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nCloseSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddCloseEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nSuccessSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddSuccessEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nOnOpenSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddOnOpenEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding onopen event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nUpdateSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddUpdateEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding update event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + + // Run intialize from key macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_BOOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_STRING + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } +} + +bool CScriptedIconLesson::ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ) +{ + VPROF_BUDGET( "CScriptedIconLesson::ProcessElements", "GameInstructor" ); + + m_hLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + bool bSuccess = true; + int nContinueScope = -1; + m_iScopeDepth = 0; + + if ( gameinstructor_find_errors.GetBool() ) + { + // Just run them all to check for errors! + for ( int iElement = 0; iElement < pElements->Count(); ++iElement ) + { + ProcessElement( event, &((*pElements)[ iElement ] ), false ); + } + + return false; + } + + // Process each element until a step fails + for ( int iElement = 0; iElement < pElements->Count(); ++iElement ) + { + if ( nContinueScope == m_iScopeDepth ) + { + nContinueScope = -1; + } + + if ( !ProcessElement( event, &((*pElements)[ iElement ]), nContinueScope != -1 ) ) + { + // This element failed + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tPrevious element returned false.\n" ); + } + + nContinueScope = m_iScopeDepth - 1; + + if ( nContinueScope < 0 ) + { + // No outer scope to worry about, we're done + bSuccess = false; + break; + } + } + } + + return bSuccess; +} + +bool CScriptedIconLesson::ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope ) +{ + VPROF_BUDGET( "CScriptedIconLesson::ProcessElement", "GameInstructor" ); + + if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_IN ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeIn()\n" ); + } + + m_iScopeDepth++; + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_OUT ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeOut()\n" ); + } + + m_iScopeDepth--; + return true; + } + + if ( bInFailedScope ) + { + // Only scope upkeep is done when we're in a failing scope... bail! + return true; + } + + if ( pLessonElement->iAction == LESSON_ACTION_CLOSE ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tCloseOpportunity()\n" ); + } + + CloseOpportunity( "Close action." ); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_SUCCESS ) + { + // Special case for succeeding (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tMarkSucceeded()\n" ); + } + + MarkSucceeded(); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_LOCK ) + { + // Special case for setting the starting point for the lesson to stay locked from (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tm_fLockTime = gpGlobals->curtime\n" ); + } + + m_fLockTime = gpGlobals->curtime; + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_COMPLETE ) + { + // Special case for checking presentation status (we don't need variables for this) + bool bPresentComplete = IsPresentComplete(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tIsPresentComplete() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", ( bPresentComplete ) ? ( "true" ) : ( "false" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( pLessonElement->bNot ) ? ( "!= true\n" ) : ( "== true\n" ) ); + } + + return ( pLessonElement->bNot ) ? ( !bPresentComplete ) : ( bPresentComplete ); + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_START ) + { + // Special case for setting presentation status (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentStart()\n" ); + } + + PresentStart(); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_END ) + { + // Special case for setting presentation status (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentEnd()\n" ); + } + + PresentEnd(); + return true; + } + + // These values temporarily hold the parameter's value + const char *pParamName = pLessonElement->szParam.String(); + float eventParam_float = 0.0f; + char eventParam_string[ 256 ]; + eventParam_string[ 0 ] = '\0'; + C_BaseEntity *eventParam_BaseEntity = NULL; + + // Get the value from the event parameter based on its type + switch ( pLessonElement->paramType ) + { + case FIELD_FLOAT: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ); + break; + case FIELD_INTEGER: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ); + break; + case FIELD_BOOLEAN: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ); + break; + case FIELD_STRING: + eventParam_float = static_cast( atoi( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ) ); + break; + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = event->GetFloat( pParamName ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' || pParamName[ 0 ] == '.' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = Q_atof( pParamName ); + } + else + { + DevWarning( "Invalid event field name and not a float \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_INTEGER: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ); + break; + case FIELD_INTEGER: + eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ); + break; + case FIELD_BOOLEAN: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ); + break; + case FIELD_STRING: + eventParam_float = atof( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ); + break; + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = static_cast( event->GetInt( pParamName ) ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = static_cast( Q_atoi( pParamName ) ); + } + else + { + DevWarning( "Invalid event field name and not an integer \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_STRING: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_STRING: + Q_strncpy( eventParam_string, &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String(), sizeof( eventParam_string ) ); + break; + case FIELD_FLOAT: + Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%f", LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ); + break; + case FIELD_INTEGER: + Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%i", LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ); + break; + case FIELD_BOOLEAN: + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else + { + const char *pchEventString = NULL; + + if ( event && !(event->IsEmpty( pParamName )) ) + { + pchEventString = event->GetString( pParamName ); + } + + if ( pchEventString && pchEventString[0] ) + { + Q_strcpy( eventParam_string, pchEventString ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else + { + // This param doesn't exist, try parsing the string + Q_strncpy( eventParam_string, pParamName, sizeof( eventParam_string ) ); + } + } + break; + + case FIELD_BOOLEAN: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_INTEGER: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_BOOLEAN: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_EHANDLE: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = ( event->GetBool( pParamName ) ) ? ( 1.0f ) : ( 0.0f ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( pParamName[ 0 ] == '0' || pParamName[ 0 ] == '1' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = Q_atof( pParamName ) != 0.0f; + } + else + { + DevWarning( "Invalid event field name and not an boolean \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_CUSTOM: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_EHANDLE: + eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get(); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + break; + case FIELD_FLOAT: + case FIELD_INTEGER: + case FIELD_BOOLEAN: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_BaseEntity = UTIL_PlayerByUserId( event->GetInt( pParamName ) ); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( Q_stricmp( pParamName, "null" ) == 0 ) + { + // They explicitly want a null pointer + eventParam_BaseEntity = NULL; + } + else + { + DevWarning( "Invalid event field name \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_EHANDLE: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_EHANDLE: + eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get(); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + break; + case FIELD_FLOAT: + case FIELD_INTEGER: + case FIELD_BOOLEAN: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + int iEntID = event->GetInt( pParamName ); + if ( iEntID >= NUM_ENT_ENTRIES ) + { + AssertMsg( 0, "Invalid entity ID used in game event field!" ); + DevWarning( "Invalid entity ID used in game event (%s) for param (%s).", event->GetName(), pParamName ); + return false; + } + + eventParam_BaseEntity = C_BaseEntity::Instance( iEntID ); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( Q_stricmp( pParamName, "null" ) == 0 ) + { + // They explicitly want a null pointer + eventParam_BaseEntity = NULL; + } + else if ( Q_stricmp( pParamName, "world" ) == 0 ) + { + // They explicitly want the world + eventParam_BaseEntity = GetClientWorldEntity(); + } + else + { + DevWarning( "Invalid event field name \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_EMBEDDED: + { + // The parameter is a convar + ConVarRef tempCVar( pParamName ); + if ( tempCVar.IsValid() ) + { + eventParam_float = tempCVar.GetFloat(); + Q_strncpy( eventParam_string, tempCVar.GetString(), sizeof( eventParam_string ) ); + } + else + { + DevWarning( "Invalid convar name \"%s\".\n", pParamName ); + return false; + } + } + break; + } + + // Do the action to the specified variable + switch ( pLessonElement->iVariable ) + { + // Run process action macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO PROCESS_LESSON_ACTION +#define LESSON_VARIABLE_MACRO_BOOL PROCESS_LESSON_ACTION +#define LESSON_VARIABLE_MACRO_EHANDLE PROCESS_LESSON_ACTION_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING PROCESS_LESSON_ACTION_STRING + LESSON_VARIABLE_FACTORY; +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + return true; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &fVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + switch ( iAction ) + { + case LESSON_ACTION_SET: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar = fParam; + return true; + + case LESSON_ACTION_ADD: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] += [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar += fParam; + return true; + + case LESSON_ACTION_SUBTRACT: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] -= [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar -= fParam; + return true; + + case LESSON_ACTION_MULTIPLY: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] *= [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar *= fParam; + return true; + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + return ( bNot ) ? ( fVar != fParam ) : ( fVar == fParam ); + + case LESSON_ACTION_LESS_THAN: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + return ( bNot ) ? ( fVar >= fParam ) : ( fVar < fParam ); + + case LESSON_ACTION_HAS_BIT: + { + int iTemp1 = static_cast( fVar ); + int iTemp2 = ( 1 << static_cast( fParam ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "& [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X", iTemp2 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == 0\n" ) : ( ") != 0\n" ) ); + } + + return ( bNot ) ? ( ( iTemp1 & iTemp2 ) == 0 ) : ( ( iTemp1 & iTemp2 ) != 0 ); + } + + case LESSON_ACTION_BIT_COUNT_IS: + { + int iTemp1 = UTIL_CountNumBitsSet( static_cast( fVar ) ); + int iTemp2 = static_cast( fParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 ); + } + + return ( bNot ) ? ( iTemp1 != iTemp2 ) : ( iTemp1 == iTemp2 ); + } + + case LESSON_ACTION_BIT_COUNT_LESS_THAN: + { + int iTemp1 = UTIL_CountNumBitsSet( static_cast( fVar ) ); + int iTemp2 = static_cast( fParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " >= [%s] " ) : ( " < [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 ); + } + + return ( bNot ) ? ( iTemp1 >= iTemp2 ) : ( iTemp1 < iTemp2 ); + } + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &iVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + float fTemp = static_cast( iVar ); + bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam ); + + iVar = static_cast( fTemp ); + return bRetVal; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + float fTemp = ( bVar ) ? ( 1.0f ) : ( 0.0f ); + bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam ); + + bVar = ( fTemp != 0.0f ); + return bRetVal; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam ) +{ + // First try to let the mod act on the action + /*bool bModHandled = false; + bool bModReturn = Mod_ProcessElementAction( iAction, bNot, pchVarName, hVar, pchParamName, fParam, pParam, pchParam, bModHandled ); + + if ( bModHandled ) + { + return bModReturn; + }*/ + + C_BaseEntity *pVar = hVar.Get(); + + switch ( iAction ) + { + case LESSON_ACTION_SET: + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]\n", pchVarName, pchParamName->String() ); + } + + hVar = pParam; + return true; + } + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t[%s] != [%s]\n" ) : ( "\t[%s] == [%s]\n" ), pchVarName, pchParamName->String() ); + } + + return ( bNot ) ? ( pVar != pParam ) : ( pVar == pParam ); + + case LESSON_ACTION_GET_DISTANCE: + { + if ( !pVar || !pParam ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] )", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" ); + } + + return false; + } + + C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL ); + C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL ); + + Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() ); + Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() ); + + m_fOutput = vVarPos.DistTo( vParamPos ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] ) ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + } + + return true; + } + + case LESSON_ACTION_GET_ANGULAR_DISTANCE: + { + if ( !pVar || !pParam ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] )", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" ); + } + + return false; + } + + C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL ); + C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL ); + + Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() ); + Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() ); + + Vector vVarToParam = vParamPos - vVarPos; + VectorNormalize( vVarToParam ); + + Vector vVarForward; + + if ( pVar->IsPlayer() ) + { + AngleVectors( static_cast< C_BasePlayer* >( pVar )->EyeAngles(), &vVarForward, NULL, NULL ); + } + else + { + pVar->GetVectors( &vVarForward, NULL, NULL ); + } + + // Set the distance in degrees + m_fOutput = ( vVarToParam.Dot( vVarForward ) - 1.0f ) * -90.0f; + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] ) ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + } + + return true; + } + + case LESSON_ACTION_GET_PLAYER_DISPLAY_NAME: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [stringINVALID], [%s]->GetPlayerName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use string2 if it was specified, otherwise, use string1 + CGameInstructorSymbol *pString; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pString = &m_szString2; + pchParamNameTemp = "string2"; + } + else + { + pString = &m_szString1; + pchParamNameTemp = "string1"; + } + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + *pString = pVar->GetPlayerName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pString->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return true; + } + + case LESSON_ACTION_CLASSNAME_IS: + { + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pVar->GetClassname() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return ( bNot ) ? ( !FClassnameIs( pVar, pchParam ) ) : ( FClassnameIs( pVar, pchParam ) ); + } + + case LESSON_ACTION_TEAM_IS: + { + int iTemp = static_cast( fParam ); + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetTeamNumber() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + } + + return ( bNot ) ? ( pVar->GetTeamNumber() != iTemp ) : ( pVar->GetTeamNumber() == iTemp ); + } + + case LESSON_ACTION_MODELNAME_IS: + { + C_BaseAnimating *pBaseAnimating = dynamic_cast( pVar ); + + if ( !pBaseAnimating ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseAnimating returned NULL!\n" ); + } + + return false; + } + + const char *pchModelName = "-no model-"; + CStudioHdr *pModel = pBaseAnimating->GetModelPtr(); + if ( pModel ) + { + const studiohdr_t *pRenderHDR = pModel->GetRenderHdr(); + if ( pRenderHDR ) + { + pchModelName = pRenderHDR->name; + } + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pchModelName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + } + + return ( bNot ) ? ( Q_stricmp( pchModelName, pchParam ) != 0 ) : ( Q_stricmp( pchModelName, pchParam ) == 0 ); + } + + case LESSON_ACTION_HEALTH_LESS_THAN: + { + int iTemp = static_cast( fParam ); + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetHealth() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + } + + return ( bNot ) ? ( pVar->GetHealth() >= iTemp ) : ( pVar->GetHealth() < iTemp ); + } + + case LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN: + { + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", pVar->HealthFraction() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + float fHealthPercentage = 1.0f; + + if ( pVar->GetMaxHealth() != 0.0f ) + { + fHealthPercentage = pVar->HealthFraction(); + } + + return ( bNot ) ? ( fHealthPercentage >= fParam ) : ( fHealthPercentage < fParam ); + } + + case LESSON_ACTION_GET_ACTIVE_WEAPON: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = [%s]->GetActiveWeapon()\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( pBaseCombatCharacter->GetActiveWeapon() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + } + + return true; + } + + case LESSON_ACTION_WEAPON_IS: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pBaseCombatWeapon->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + } + + return ( bNot ) ? ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) != 0 ) : ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) == 0 ); + } + + case LESSON_ACTION_WEAPON_HAS: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return ( bNot ) ? ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) == NULL ) : ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) != NULL ); + } + + case LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + C_BaseCombatWeapon *pWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + // @TODO + /*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pWeapon->GetWpnData().szClassName ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + }*/ + + return true; + } + + case LESSON_ACTION_GET_WEAPON_SLOT: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") ...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + // @TODO + /*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pchParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + }*/ + + return true; + } + + case LESSON_ACTION_GET_WEAPON_IN_SLOT: + { + int nTemp = static_cast( fParam ); + + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + m_hEntity1 = pBaseCombatCharacter->GetWeapon( nTemp ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return true; + } + + case LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + float fClip1Percentage = 100.0f; + + if ( pBaseCombatWeapon->UsesClipsForAmmo1() ) + { + fClip1Percentage = 100.0f * ( static_cast( pBaseCombatWeapon->Clip1() ) / static_cast( pBaseCombatWeapon->GetMaxClip1() ) ); + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f ", fClip1Percentage ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + } + + return ( bNot ) ? ( fClip1Percentage >= fParam ) : ( fClip1Percentage < fParam ); + } + + case LESSON_ACTION_WEAPON_AMMO_LOW: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is full + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ ); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoLow = ( iPlayerAmmo < ( iMaxAmmo / 3 ) ); + + if ( bNot ) + { + bAmmoLow = !bAmmoLow; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30 " ) : ( ")->AmmoPercentage() < 30 " ) ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bAmmoLow ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return bAmmoLow; + } + + case LESSON_ACTION_WEAPON_AMMO_FULL: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is full + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ ); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoFull = ( iPlayerAmmo >= iMaxAmmo ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoFull ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return ( bNot ) ? ( !bAmmoFull ) : ( bAmmoFull ); + } + + case LESSON_ACTION_WEAPON_AMMO_EMPTY: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is empty + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoEmpty = ( iPlayerAmmo <= 0 ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoEmpty ) ? ( "true" ) : ( "false" ) ); + ConColorMsg(CBaseLesson::m_rgbaVerbosePlain, " )\n" ); + } + + return ( bNot ) ? ( !bAmmoEmpty ) : ( bAmmoEmpty ); + } + + /*case LESSON_ACTION_WEAPON_CAN_USE: + { + C_BaseCombatWeapon *pBaseCombatWeapon = dynamic_cast( pParam ); + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam BaseCombatWeapon returned NULL!\n" ); + } + + return false; + } + + bool bCanEquip = pBasePlayer->Weapon_CanUse( pBaseCombatWeapon ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s]) " ) : ( "\t[%s]->Weapon_CanUse([%s]) " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bCanEquip ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return ( bNot ) ? ( !bCanEquip ) : ( bCanEquip ); + }*/ + + case LESSON_ACTION_USE_TARGET_IS: + { + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() ); + } + + return ( bNot ) ? ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) != pParam ) : ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) == pParam ); + } + + case LESSON_ACTION_GET_USE_TARGET: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName ); + } + + return true; + } + + /*case LESSON_ACTION_GET_POTENTIAL_USE_TARGET: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetPotentialUseEntity() ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName ); + } + + return true; + }*/ + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam ) +{ + switch ( iAction ) + { + case LESSON_ACTION_REFERENCE_OPEN: + { + const CBaseLesson *pLesson = GetGameInstructor().GetLesson( pchParamName->String() ); + if ( !pLesson ) + { + DevWarning( "Invalid lesson specified: \"%s\".", pchParamName->String() ); + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( Color( 64, 128, 255, 255 ), ( bNot ) ? ( "\t!( [\"%s\"]->IsInstanceActive() " ) : ( "\t( [\"%s\"]->IsInstanceActive() " ), pchParamName->String() ); + ConColorMsg( Color( 255, 255, 255, 255 ), "\"%s\"", (pLesson->IsInstanceActive() ? "true" : "false") ); + ConColorMsg( Color( 64, 128, 255, 255 ), " )\n" ); + } + + return ( bNot ) ? ( !pLesson->IsInstanceActive() ) : ( pLesson->IsInstanceActive() ); + } + + case LESSON_ACTION_SET: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy([%s], [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + *pchVar = pchParam; + return true; + + case LESSON_ACTION_ADD: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcat([%s], [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + char szTemp[ 256 ]; + Q_strncpy( szTemp, pchVar->String(), sizeof( szTemp ) ); + Q_strncat( szTemp, pchParam, sizeof( szTemp ) ); + + *pchVar = szTemp; + return true; + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + } + + return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) != 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) == 0 ); + + case LESSON_ACTION_HAS_PREFIX: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tStringHasPrefix([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == false\n" ) : ( ") == true\n" ) ); + } + + return ( bNot ) ? ( !StringHasPrefix( pchVar->String(), pchParam ) ) : ( StringHasPrefix( pchVar->String(), pchParam ) ); + + case LESSON_ACTION_LESS_THAN: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") >= 0\n" ) : ( ") < 0\n" ) ); + } + + return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) >= 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) < 0 ); + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +LessonEvent_t * CScriptedIconLesson::AddOpenEvent() +{ + int iNewLessonEvent = m_OpenEvents.AddToTail(); + return &(m_OpenEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddCloseEvent() +{ + int iNewLessonEvent = m_CloseEvents.AddToTail(); + return &(m_CloseEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddSuccessEvent() +{ + int iNewLessonEvent = m_SuccessEvents.AddToTail(); + return &(m_SuccessEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddOnOpenEvent() +{ + int iNewLessonEvent = m_OnOpenEvents.AddToTail(); + return &(m_OnOpenEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddUpdateEvent() +{ + int iNewLessonEvent = m_UpdateEvents.AddToTail(); + return &(m_UpdateEvents[ iNewLessonEvent ]); +} + +// Static method to init the keyvalues symbols used for comparisons +void CScriptedIconLesson::PreReadLessonsFromFile() +{ + static bool bFirstTime = true; + if ( !bFirstTime ) + return; + bFirstTime = false; + + // Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_SYMBOL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // And build the map of variable name to enum + // Run string to int macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_BOOL LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_STRING LESSON_SCRIPT_STRING_ADD_TO_MAP + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // Set up mapping of field types + g_TypeToParamTypeMap.Insert( "float", FIELD_FLOAT ); + g_TypeToParamTypeMap.Insert( "string", FIELD_STRING ); + g_TypeToParamTypeMap.Insert( "int", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "integer", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "short", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "long", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "bool", FIELD_BOOLEAN ); + g_TypeToParamTypeMap.Insert( "player", FIELD_CUSTOM ); + g_TypeToParamTypeMap.Insert( "entity", FIELD_EHANDLE ); + g_TypeToParamTypeMap.Insert( "convar", FIELD_EMBEDDED ); + g_TypeToParamTypeMap.Insert( "void", FIELD_VOID ); + + // Set up the lesson action map + + CScriptedIconLesson::LessonActionMap.Insert( "scope in", LESSON_ACTION_SCOPE_IN ); + CScriptedIconLesson::LessonActionMap.Insert( "scope out", LESSON_ACTION_SCOPE_OUT ); + CScriptedIconLesson::LessonActionMap.Insert( "close", LESSON_ACTION_CLOSE ); + CScriptedIconLesson::LessonActionMap.Insert( "success", LESSON_ACTION_SUCCESS ); + CScriptedIconLesson::LessonActionMap.Insert( "lock", LESSON_ACTION_LOCK ); + CScriptedIconLesson::LessonActionMap.Insert( "present complete", LESSON_ACTION_PRESENT_COMPLETE ); + CScriptedIconLesson::LessonActionMap.Insert( "present start", LESSON_ACTION_PRESENT_START ); + CScriptedIconLesson::LessonActionMap.Insert( "present end", LESSON_ACTION_PRESENT_END ); + + CScriptedIconLesson::LessonActionMap.Insert( "reference open", LESSON_ACTION_REFERENCE_OPEN ); + + CScriptedIconLesson::LessonActionMap.Insert( "set", LESSON_ACTION_SET ); + CScriptedIconLesson::LessonActionMap.Insert( "add", LESSON_ACTION_ADD ); + CScriptedIconLesson::LessonActionMap.Insert( "subtract", LESSON_ACTION_SUBTRACT ); + CScriptedIconLesson::LessonActionMap.Insert( "multiply", LESSON_ACTION_MULTIPLY ); + CScriptedIconLesson::LessonActionMap.Insert( "is", LESSON_ACTION_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "less than", LESSON_ACTION_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "has prefix", LESSON_ACTION_HAS_PREFIX ); + CScriptedIconLesson::LessonActionMap.Insert( "has bit", LESSON_ACTION_HAS_BIT ); + CScriptedIconLesson::LessonActionMap.Insert( "bit count is", LESSON_ACTION_BIT_COUNT_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "bit count less than", LESSON_ACTION_BIT_COUNT_LESS_THAN ); + + CScriptedIconLesson::LessonActionMap.Insert( "get distance", LESSON_ACTION_GET_DISTANCE ); + CScriptedIconLesson::LessonActionMap.Insert( "get angular distance", LESSON_ACTION_GET_ANGULAR_DISTANCE ); + CScriptedIconLesson::LessonActionMap.Insert( "get player display name", LESSON_ACTION_GET_PLAYER_DISPLAY_NAME ); + CScriptedIconLesson::LessonActionMap.Insert( "classname is", LESSON_ACTION_CLASSNAME_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "modelname is", LESSON_ACTION_MODELNAME_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "team is", LESSON_ACTION_TEAM_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "health less than", LESSON_ACTION_HEALTH_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "health percentage less than", LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "get active weapon", LESSON_ACTION_GET_ACTIVE_WEAPON ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon is", LESSON_ACTION_WEAPON_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon has", LESSON_ACTION_WEAPON_HAS ); + CScriptedIconLesson::LessonActionMap.Insert( "get active weapon slot", LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "get weapon slot", LESSON_ACTION_GET_WEAPON_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "get weapon in slot", LESSON_ACTION_GET_WEAPON_IN_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "clip percentage less than", LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo low", LESSON_ACTION_WEAPON_AMMO_LOW ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo full", LESSON_ACTION_WEAPON_AMMO_FULL ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo empty", LESSON_ACTION_WEAPON_AMMO_EMPTY ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon can use", LESSON_ACTION_WEAPON_CAN_USE ); + CScriptedIconLesson::LessonActionMap.Insert( "use target is", LESSON_ACTION_USE_TARGET_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "get use target", LESSON_ACTION_GET_USE_TARGET ); + CScriptedIconLesson::LessonActionMap.Insert( "get potential use target", LESSON_ACTION_GET_POTENTIAL_USE_TARGET ); + + // Add mod actions to the map + //Mod_PreReadLessonsFromFile(); +} diff --git a/sp/src/game/client/c_baselesson.h b/sp/src/game/client/c_baselesson.h new file mode 100644 index 0000000000..d23ee2b7cd --- /dev/null +++ b/sp/src/game/client/c_baselesson.h @@ -0,0 +1,458 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler for instruction players how to play +// +//=============================================================================// + +#ifndef _C_BASELESSON_H_ +#define _C_BASELESSON_H_ + + +#include "GameEventListener.h" +#include "hud_locator_target.h" + + +#define DECLARE_LESSON( _lessonClassName, _baseLessonClassName ) \ + typedef _baseLessonClassName BaseClass;\ + typedef _lessonClassName ThisClass;\ + _lessonClassName( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity )\ + : _baseLessonClassName( pchName, bIsDefaultHolder, bIsOpenOpportunity )\ + {\ + Init();\ + } + + +enum LessonInstanceType +{ + LESSON_INSTANCE_MULTIPLE, + LESSON_INSTANCE_SINGLE_OPEN, + LESSON_INSTANCE_FIXED_REPLACE, + LESSON_INSTANCE_SINGLE_ACTIVE, + + LESSON_INSTANCE_TYPE_TOTAL +}; + + +// This is used to solve a problem where bots can take the place of a player, where on or the other don't have valid entities on the client at the same time +#define MAX_DELAYED_PLAYER_SWAPS 8 + +struct delayed_player_swap_t +{ + CHandle *phHandleToChange; + int iNewUserID; + + delayed_player_swap_t( void ) + { + phHandleToChange = NULL; + iNewUserID = -1; + } +}; + + +abstract_class CBaseLesson : public CGameEventListener +{ +public: + CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity ); + virtual ~CBaseLesson( void ); + + void AddPrerequisite( const char *pchLessonName ); + + const CGameInstructorSymbol& GetNameSymbol( void ) const { return m_stringName; } + const char * GetName( void ) const { return m_stringName.String(); } + int GetPriority( void ) const { return m_iPriority; } + const char * GetCloseReason( void ) const { return m_stringCloseReason.String(); } + void SetCloseReason( const char *pchReason ) { m_stringCloseReason = pchReason; } + + CBaseLesson* GetRoot( void ) const { return m_pRoot; } + void SetRoot( CBaseLesson *pRoot ); + const CUtlVector < CBaseLesson * >* GetChildren( void ) const { return &m_OpenOpportunities; } + + float GetInitTime( void ) { return m_fInitTime; } + void SetStartTime( void ) { m_fStartTime = gpGlobals->curtime; } + void ResetStartTime( void ) { m_fStartTime = 0.0f; m_bHasPlayedSound = false; } + + bool ShouldShowSpew( void ); + bool NoPriority( void ) const; + bool IsDefaultHolder( void ) const { return m_bIsDefaultHolder; } + bool IsOpenOpportunity( void ) const { return m_bIsOpenOpportunity; } + bool IsLocked( void ) const; + bool CanOpenWhenDead( void ) const { return m_bCanOpenWhenDead; } + bool IsInstructing( void ) const { return ( m_fStartTime > 0.0f ); } + bool IsLearned( void ) const; + bool PrerequisitesHaveBeenMet( void ) const; + bool IsTimedOut( void ); + + int InstanceType( void ) const { return m_iInstanceType; } + const CGameInstructorSymbol& GetReplaceKeySymbol( void ) const { return m_stringReplaceKey; } + const char* GetReplaceKey( void ) const { return m_stringReplaceKey.String(); } + int GetFixedInstancesMax( void ) const { return m_iFixedInstancesMax; } + bool ShouldReplaceOnlyWhenStopped( void ) const { return m_bReplaceOnlyWhenStopped; } + void SetInstanceActive( bool bInstanceActive ) { m_bInstanceActive = bInstanceActive; } + bool IsInstanceActive( void ) const { return m_bInstanceActive; } + + void ResetDisplaysAndSuccesses( void ); + bool IncDisplayCount( void ); + bool IncSuccessCount( void ); + void SetDisplayCount( int iDisplayCount ) { m_iDisplayCount = iDisplayCount; } + void SetSuccessCount( int iSuccessCount ) { m_iSuccessCount = iSuccessCount; } + int GetDisplayCount( void ) const { return m_iDisplayCount; } + int GetSuccessCount( void ) const { return m_iSuccessCount; } + int GetDisplayLimit( void ) const { return m_iDisplayLimit; } + int GetSuccessLimit( void ) const { return m_iSuccessLimit; } + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void InitPrerequisites( void ) {}; + virtual void Start( void ) = 0; + virtual void Stop( void ) = 0; + virtual void OnOpen( void ) {}; + virtual void Update( void ) {}; + virtual void UpdateInactive( void ) {}; + + virtual bool ShouldDisplay( void ) const { return true; } + virtual bool IsVisible( void ) const { return true; } + virtual bool WasDisplayed( void ) const { return m_bWasDisplayed ? true : false; } + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ) {} + virtual void TakePlaceOf( CBaseLesson *pLesson ); + + const char *GetGroup() { return m_szLessonGroup.String(); } + void SetEnabled( bool bEnabled ) { m_bDisabled = !bEnabled; } + +protected: + void MarkSucceeded( void ); + void CloseOpportunity( const char *pchReason ); + bool DoDelayedPlayerSwaps( void ) const; + +private: + + CBaseLesson *m_pRoot; + CUtlVector < CBaseLesson * > m_OpenOpportunities; + CUtlVector < const CBaseLesson * > m_Prerequisites; + + CGameInstructorSymbol m_stringCloseReason; + CGameInstructorSymbol m_stringName; + + bool m_bInstanceActive : 1; + bool m_bSuccessCounted : 1; + bool m_bIsDefaultHolder : 1; + bool m_bIsOpenOpportunity : 1; + +protected: + LessonInstanceType m_iInstanceType; + + int m_iPriority; + CGameInstructorSymbol m_stringReplaceKey; + int m_iFixedInstancesMax; + bool m_bReplaceOnlyWhenStopped; + int m_iTeam; + bool m_bOnlyKeyboard; + bool m_bOnlyGamepad; + + int m_iDisplayLimit; + int m_iDisplayCount; + bool m_bWasDisplayed; + int m_iSuccessLimit; + int m_iSuccessCount; + + float m_fLockDuration; + float m_fTimeout; + float m_fInitTime; + float m_fStartTime; + float m_fLockTime; + float m_fUpdateInterval; + bool m_bHasPlayedSound; + + CGameInstructorSymbol m_szStartSound; + CGameInstructorSymbol m_szLessonGroup; + + bool m_bCanOpenWhenDead; + bool m_bBumpWithTimeoutWhenLearned; + bool m_bCanTimeoutWhileInactive; + bool m_bDisabled; + + // Right now we can only queue up 4 swaps... + // this number can be increased if more entity handle scripted variables are added + mutable delayed_player_swap_t m_pDelayedPlayerSwap[ MAX_DELAYED_PLAYER_SWAPS ]; + mutable int m_iNumDelayedPlayerSwaps; + +public: + + // Colors for console spew in verbose mode + static Color m_rgbaVerboseHeader; + static Color m_rgbaVerbosePlain; + static Color m_rgbaVerboseName; + static Color m_rgbaVerboseOpen; + static Color m_rgbaVerboseClose; + static Color m_rgbaVerboseSuccess; + static Color m_rgbaVerboseUpdate; +}; + + +class CTextLesson : public CBaseLesson +{ +public: + DECLARE_LESSON( CTextLesson, CBaseLesson ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + +protected: + + CGameInstructorSymbol m_szDisplayText; + CGameInstructorSymbol m_szDisplayParamText; + CGameInstructorSymbol m_szBinding; + CGameInstructorSymbol m_szGamepadBinding; +}; + + +class CIconLesson : public CTextLesson +{ +public: + DECLARE_LESSON( CIconLesson, CTextLesson ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + virtual void Update( void ); + virtual void UpdateInactive( void ); + + virtual bool ShouldDisplay( void ) const; + virtual bool IsVisible( void ) const; + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ); + virtual void TakePlaceOf( CBaseLesson *pLesson ); + + void SetLocatorBinding( CLocatorTarget * pLocatorTarget ); + + const char *GetCaptionColorString() { return m_szCaptionColor.String(); } + + bool IsPresentComplete( void ); + void PresentStart( void ); + void PresentEnd( void ); + +private: + virtual void UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget ); + +#ifdef MAPBASE + Vector GetIconTargetPosition( C_BaseEntity *pIconTarget ); +#endif + +protected: + CHandle m_hIconTarget; + CGameInstructorSymbol m_szVguiTargetName; + CGameInstructorSymbol m_szVguiTargetLookup; + int m_nVguiTargetEdge; + float m_flUpOffset; + float m_flRelativeUpOffset; + float m_fFixedPositionX; + float m_fFixedPositionY; + + int m_hLocatorTarget; + int m_iFlags; + + float m_fRange; + float m_fCurrentDistance; + float m_fOnScreenStartTime; + float m_fUpdateDistanceTime; + + CGameInstructorSymbol m_szOnscreenIcon; + CGameInstructorSymbol m_szOffscreenIcon; + CGameInstructorSymbol m_szCaptionColor; + + bool m_bFixedPosition; + bool m_bNoIconTarget; + bool m_bAllowNodrawTarget; + bool m_bVisible; + bool m_bShowWhenOccluded; + bool m_bNoOffscreen; + bool m_bForceCaption; + +#ifdef MAPBASE + int m_iIconTargetPos; + + enum + { + ICON_TARGET_EYE_POSITION, + ICON_TARGET_ORIGIN, + ICON_TARGET_CENTER, + }; +#endif +}; + +enum LessonAction +{ + LESSON_ACTION_NONE, + + LESSON_ACTION_SCOPE_IN, + LESSON_ACTION_SCOPE_OUT, + LESSON_ACTION_CLOSE, + LESSON_ACTION_SUCCESS, + LESSON_ACTION_LOCK, + LESSON_ACTION_PRESENT_COMPLETE, + LESSON_ACTION_PRESENT_START, + LESSON_ACTION_PRESENT_END, + + LESSON_ACTION_REFERENCE_OPEN, + + LESSON_ACTION_SET, + LESSON_ACTION_ADD, + LESSON_ACTION_SUBTRACT, + LESSON_ACTION_MULTIPLY, + LESSON_ACTION_IS, + LESSON_ACTION_LESS_THAN, + LESSON_ACTION_HAS_PREFIX, + LESSON_ACTION_HAS_BIT, + LESSON_ACTION_BIT_COUNT_IS, + LESSON_ACTION_BIT_COUNT_LESS_THAN, + + LESSON_ACTION_GET_DISTANCE, + LESSON_ACTION_GET_ANGULAR_DISTANCE, + LESSON_ACTION_GET_PLAYER_DISPLAY_NAME, + LESSON_ACTION_CLASSNAME_IS, + LESSON_ACTION_MODELNAME_IS, + LESSON_ACTION_TEAM_IS, + LESSON_ACTION_HEALTH_LESS_THAN, + LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN, + LESSON_ACTION_GET_ACTIVE_WEAPON, + LESSON_ACTION_WEAPON_IS, + LESSON_ACTION_WEAPON_HAS, + LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT, + LESSON_ACTION_GET_WEAPON_SLOT, + LESSON_ACTION_GET_WEAPON_IN_SLOT, + LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN, + LESSON_ACTION_WEAPON_AMMO_LOW, + LESSON_ACTION_WEAPON_AMMO_FULL, + LESSON_ACTION_WEAPON_AMMO_EMPTY, + LESSON_ACTION_WEAPON_CAN_USE, + LESSON_ACTION_USE_TARGET_IS, + LESSON_ACTION_GET_USE_TARGET, + LESSON_ACTION_GET_POTENTIAL_USE_TARGET, + + // Enum continued in Mod_LessonAction + LESSON_ACTION_MOD_START, +}; + +struct LessonElement_t +{ + int iVariable; + int iParamVarIndex; + int iAction; + _fieldtypes paramType; + CGameInstructorSymbol szParam; + bool bNot : 1; + bool bOptionalParam : 1; + + LessonElement_t( int p_iVariable, int p_iAction, bool p_bNot, bool p_bOptionalParam, const char *pchParam, int p_iParamVarIndex, _fieldtypes p_paramType ) + { + iVariable = p_iVariable; + iAction = p_iAction; + bNot = p_bNot; + bOptionalParam = p_bOptionalParam; + szParam = pchParam; + iParamVarIndex = p_iParamVarIndex; + paramType = p_paramType; + } + + LessonElement_t( const LessonElement_t &p_LessonElement ) + { + iVariable = p_LessonElement.iVariable; + iAction = p_LessonElement.iAction; + bNot = p_LessonElement.bNot; + bOptionalParam = p_LessonElement.bOptionalParam; + szParam = p_LessonElement.szParam; + iParamVarIndex = p_LessonElement.iParamVarIndex; + paramType = p_LessonElement.paramType; + } +}; + +struct LessonEvent_t +{ + CUtlVector< LessonElement_t > elements; + CGameInstructorSymbol szEventName; +}; + +class CScriptedIconLesson : public CIconLesson +{ +public: + DECLARE_LESSON( CScriptedIconLesson, CIconLesson ) + + virtual ~CScriptedIconLesson( void ); + + static void PreReadLessonsFromFile( void ); + static void Mod_PreReadLessonsFromFile( void ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void InitPrerequisites( void ); + virtual void OnOpen( void ); + virtual void Update( void ); + + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ); + + virtual void FireGameEvent( IGameEvent *event ); + virtual void ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + virtual void ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + virtual void ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + + CUtlVector< LessonEvent_t >& GetOpenEvents( void ) { return m_OpenEvents; } + CUtlVector< LessonEvent_t >& GetCloseEvents( void ) { return m_CloseEvents; } + CUtlVector< LessonEvent_t >& GetSuccessEvents( void ) { return m_SuccessEvents; } + CUtlVector< LessonEvent_t >& GetOnOpenEvents( void ) { return m_OnOpenEvents; } + CUtlVector< LessonEvent_t >& GetUpdateEvents( void ) { return m_UpdateEvents; } + + bool ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ); + +private: + void InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ); + void InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ); + + void InitFromKeys( KeyValues *pKey ); + + bool ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope ); + + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam ); + + // Implemented per mod so they can have custom actions + bool Mod_ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam, bool &bModHandled ); + + LessonEvent_t * AddOpenEvent( void ); + LessonEvent_t * AddCloseEvent( void ); + LessonEvent_t * AddSuccessEvent( void ); + LessonEvent_t * AddOnOpenEvent( void ); + LessonEvent_t * AddUpdateEvent( void ); + +private: + static CUtlDict< int, int > CScriptedIconLesson::LessonActionMap; + + EHANDLE m_hLocalPlayer; + float m_fOutput; + CHandle m_hEntity1; + CHandle m_hEntity2; + CGameInstructorSymbol m_szString1; + CGameInstructorSymbol m_szString2; + int m_iInteger1; + int m_iInteger2; + float m_fFloat1; + float m_fFloat2; + + CUtlVector< CGameInstructorSymbol > m_PrerequisiteNames; + CUtlVector< LessonEvent_t > m_OpenEvents; + CUtlVector< LessonEvent_t > m_CloseEvents; + CUtlVector< LessonEvent_t > m_SuccessEvents; + CUtlVector< LessonEvent_t > m_OnOpenEvents; + CUtlVector< LessonEvent_t > m_UpdateEvents; + + float m_fUpdateEventTime; + CScriptedIconLesson *m_pDefaultHolder; + + int m_iScopeDepth; + + // Need this to get offsets to scripted variables + friend class LessonVariableInfo; + friend int LessonActionFromString( const char *pchName ); +}; + + +#endif // _C_BASELESSON_H_ diff --git a/sp/src/game/client/c_func_reflective_glass.cpp b/sp/src/game/client/c_func_reflective_glass.cpp index 994d62ba2a..4e3040d34e 100644 --- a/sp/src/game/client/c_func_reflective_glass.cpp +++ b/sp/src/game/client/c_func_reflective_glass.cpp @@ -6,6 +6,9 @@ //===========================================================================// #include "cbase.h" #include "view_shared.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -23,10 +26,27 @@ class C_FuncReflectiveGlass : public C_BaseEntity virtual bool ShouldDraw(); +#ifdef MAPBASE + virtual void OnDataChanged( DataUpdateType_t type ); + ITexture *ReflectionRenderTarget(); + ITexture *RefractionRenderTarget(); + + char m_iszReflectRenderTarget[64]; + char m_iszRefractRenderTarget[64]; + ITexture *m_pReflectRenderTarget; + ITexture *m_pRefractRenderTarget; +#endif + C_FuncReflectiveGlass *m_pNext; }; IMPLEMENT_CLIENTCLASS_DT( C_FuncReflectiveGlass, DT_FuncReflectiveGlass, CFuncReflectiveGlass ) + +#ifdef MAPBASE + RecvPropString( RECVINFO( m_iszReflectRenderTarget ) ), + RecvPropString( RECVINFO( m_iszRefractRenderTarget ) ), +#endif + END_RECV_TABLE() @@ -47,6 +67,11 @@ C_FuncReflectiveGlass* GetReflectiveGlassList() //----------------------------------------------------------------------------- C_FuncReflectiveGlass::C_FuncReflectiveGlass() { +#ifdef MAPBASE + m_iszReflectRenderTarget[0] = '\0'; + m_iszRefractRenderTarget[0] = '\0'; +#endif + g_ReflectiveGlassList.Insert( this ); } @@ -114,5 +139,111 @@ bool IsReflectiveGlassInView( const CViewSetup& view, cplane_t &plane ) return false; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Iterates through reflective glass instead of just picking one +//----------------------------------------------------------------------------- +C_BaseEntity *NextReflectiveGlass( C_BaseEntity *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum, ITexture **pRenderTargets ) +{ + // Early out if no cameras + C_FuncReflectiveGlass *pReflectiveGlass = NULL; + if (!pStart) + pReflectiveGlass = GetReflectiveGlassList(); + else + pReflectiveGlass = ((C_FuncReflectiveGlass*)pStart)->m_pNext; + + cplane_t localPlane; + Vector vecOrigin, vecWorld, vecDelta; + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) + { + if ( pReflectiveGlass->IsDormant() ) + continue; + + if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) + continue; + + Vector vecMins, vecMaxs; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + if ( R_CullBox( vecMins, vecMaxs, frustum ) ) + continue; + + const model_t *pModel = pReflectiveGlass->GetModel(); + const matrix3x4_t& mat = pReflectiveGlass->EntityToWorldTransform(); + + int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); + for ( int i = 0; i < nCount; ++i ) + { + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + + MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space + VectorTransform( vecOrigin, mat, vecWorld ); + + if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + continue; + + VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull + if ( vecDelta.Dot( plane.normal ) >= 0 ) + continue; + + if (pRenderTargets != NULL) + { + pRenderTargets[0] = pReflectiveGlass->ReflectionRenderTarget(); + pRenderTargets[1] = pReflectiveGlass->RefractionRenderTarget(); + } + + return pReflectiveGlass; + } + } + + return NULL; +} + +void C_FuncReflectiveGlass::OnDataChanged( DataUpdateType_t type ) +{ + // Reset render textures + m_pReflectRenderTarget = NULL; + m_pRefractRenderTarget = NULL; + + return BaseClass::OnDataChanged( type ); +} + +ITexture *C_FuncReflectiveGlass::ReflectionRenderTarget() +{ + if (m_iszReflectRenderTarget[0] != '\0') + { + if (!m_pReflectRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pReflectRenderTarget = materials->FindTexture( m_iszReflectRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pReflectRenderTarget) + return m_pReflectRenderTarget; + } + + return NULL; + //return GetWaterReflectionTexture(); +} + +ITexture *C_FuncReflectiveGlass::RefractionRenderTarget() +{ + if (m_iszRefractRenderTarget[0] != '\0') + { + if (!m_pRefractRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pRefractRenderTarget = materials->FindTexture( m_iszRefractRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pRefractRenderTarget) + return m_pRefractRenderTarget; + } + + return NULL; + //return GetWaterRefractionTexture(); +} +#endif + diff --git a/sp/src/game/client/c_func_reflective_glass.h b/sp/src/game/client/c_func_reflective_glass.h index 48e1491ee9..ab52ab8812 100644 --- a/sp/src/game/client/c_func_reflective_glass.h +++ b/sp/src/game/client/c_func_reflective_glass.h @@ -21,6 +21,11 @@ class CViewSetup; //----------------------------------------------------------------------------- bool IsReflectiveGlassInView( const CViewSetup& view, cplane_t &plane ); +#ifdef MAPBASE +C_BaseEntity *NextReflectiveGlass( C_BaseEntity *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum, ITexture **pRenderTargets = NULL ); +#endif + #endif // C_FUNC_REFLECTIVE_GLASS diff --git a/sp/src/game/client/c_gameinstructor.cpp b/sp/src/game/client/c_gameinstructor.cpp new file mode 100644 index 0000000000..ff5f0e538e --- /dev/null +++ b/sp/src/game/client/c_gameinstructor.cpp @@ -0,0 +1,1312 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler implementations for instruction players how to play +// +//=============================================================================// + +#include "cbase.h" + +#include "c_gameinstructor.h" +#include "c_baselesson.h" +#include "filesystem.h" +#include "vprof.h" +#include "ixboxsystem.h" +#include "tier0/icommandline.h" +#include "iclientmode.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Configuration +//========================================================= + +#define MOD_DIR "MOD" +#define GAMEINSTRUCTOR_SCRIPT_FILE "scripts/instructor_lessons.txt" +#define GAMEINSTRUCTOR_MOD_SCRIPT_FILE "scripts/mod_lessons.txt" + +// Game instructor auto game system instantiation +C_GameInstructor g_GameInstructor; +C_GameInstructor &GetGameInstructor() +{ + return g_GameInstructor; +} + +void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ); +void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ); + +extern ConVar sv_gameinstructor_disable; + +//========================================================= +// Comandos de consola +//========================================================= + +ConVar gameinstructor_verbose("gameinstructor_verbose", "0", FCVAR_CHEAT, "Set to 1 for standard debugging or 2 (in combo with gameinstructor_verbose_lesson) to show update actions."); +ConVar gameinstructor_verbose_lesson("gameinstructor_verbose_lesson", "", FCVAR_CHEAT, "Display more verbose information for lessons have this name." ); +ConVar gameinstructor_find_errors("gameinstructor_find_errors", "1", FCVAR_CHEAT, "Set to 1 and the game instructor will run EVERY scripted command to uncover errors." ); + +ConVar gameinstructor_enable( "gameinstructor_enable", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Display in game lessons that teach new players.", GameInstructorEnable_ChangeCallback ); +ConVar gameinstructor_start_sound_cooldown( "gameinstructor_start_sound_cooldown", "4.0", FCVAR_NONE, "Number of seconds forced between similar lesson start sounds." ); + +ConVar sv_gameinstructor_disable( "sv_gameinstructor_disable", "0", FCVAR_REPLICATED, "Force all clients to disable their game instructors.", SVGameInstructorDisable_ChangeCallback ); + +//========================================================= +// Activa o Desactiva el Instructor del lado del cliente. +//========================================================= +void EnableDisableInstructor() +{ + bool bEnabled = ( !sv_gameinstructor_disable.GetBool() && gameinstructor_enable.GetBool() ); + + // Game instructor has been enabled, so init it! + if ( bEnabled ) + GetGameInstructor().Init(); + + // Game instructor has been disabled, so shut it down! + else + GetGameInstructor().Shutdown(); +} + +//========================================================= +//========================================================= +void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if ( ( flOldValue != 0.0f ) != gameinstructor_enable.GetBool() ) + EnableDisableInstructor(); +} + +//========================================================= +//========================================================= +void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue ) +{ + if ( !engine ) + return; + + EnableDisableInstructor(); +} + + +//========================================================= +// Initialize the Instructor +//========================================================= +bool C_GameInstructor::Init() +{ +// if ( &GetGameInstructor() == this ) + // return true; + + // Instructor deactivated, don't initialize. + if ( !gameinstructor_enable.GetBool() || sv_gameinstructor_disable.GetBool() ) + return true; + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing...\n" ); + } + + m_bNoDraw = false; + m_bHiddenDueToOtherElements = false; + + m_iCurrentPriority = 0; + m_hLastSpectatedPlayer = NULL; + m_bSpectatedPlayerChanged = false; + + m_szPreviousStartSound[0] = '\0'; + m_fNextStartSoundTime = 0; + + ReadLessonsFromFile( GAMEINSTRUCTOR_MOD_SCRIPT_FILE ); + ReadLessonsFromFile( GAMEINSTRUCTOR_SCRIPT_FILE ); + + InitLessonPrerequisites(); + ReadSaveData(); + + ListenForGameEvent("gameinstructor_draw"); + ListenForGameEvent("gameinstructor_nodraw"); + + ListenForGameEvent("round_end"); + ListenForGameEvent("round_start"); + ListenForGameEvent("player_death"); + ListenForGameEvent("player_team"); + ListenForGameEvent("player_disconnect"); + ListenForGameEvent("map_transition"); + ListenForGameEvent("game_newmap"); + ListenForGameEvent("set_instructor_group_enabled"); + + EvaluateLessonsForGameRules(); + return true; +} + +//========================================================= +// Shut down the instructor +//========================================================= +void C_GameInstructor::Shutdown() +{ + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Shutting down...\n" ); + } + + CloseAllOpenOpportunities(); + WriteSaveData(); + + // Removemos todas las lecciones. + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + if ( m_Lessons[ i ] ) + { + m_Lessons[ i ]->StopListeningForAllEvents(); + delete m_Lessons[ i ]; + m_Lessons[ i ] = NULL; + } + } + + m_Lessons.RemoveAll(); + m_LessonGroupConVarToggles.RemoveAll(); + + // Paramos de escuchar eventos. + StopListeningForAllEvents(); +} + +//========================================================= +//========================================================= +void C_GameInstructor::UpdateHiddenByOtherElements() +{ + //bool bHidden = Mod_HiddenByOtherElements(); + bool bHidden = false; + + if ( bHidden && !m_bHiddenDueToOtherElements ) + StopAllLessons(); + + m_bHiddenDueToOtherElements = bHidden; +} + +//========================================================= +//========================================================= +void C_GameInstructor::Update( float frametime ) +{ + VPROF_BUDGET( "C_GameInstructor::Update", "GameInstructor" ); + + UpdateHiddenByOtherElements(); + + // Instructor deactivated. + if ( !gameinstructor_enable.GetBool() || m_bNoDraw || m_bHiddenDueToOtherElements ) + return; + + if ( gameinstructor_find_errors.GetBool() ) + { + FindErrors(); + gameinstructor_find_errors.SetValue(0); + } + + if ( IsConsole() ) + { + // On X360 we want to save when they're not connected + // They aren't in game + if ( !engine->IsInGame() ) + WriteSaveData(); + else + { + const char *levelName = engine->GetLevelName(); + + // The are in game, but it's a background map + if ( levelName && levelName[0] && engine->IsLevelMainMenuBackground() ) + WriteSaveData(); + } + } + + if ( m_bSpectatedPlayerChanged ) + { + // Safe spot to clean out stale lessons if spectator changed + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Spectated player changed...\n" ); + } + + CloseAllOpenOpportunities(); + m_bSpectatedPlayerChanged = false; + } + + // Loop through all the lesson roots and reset their active status + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_ACTIVE ) + pRootLesson->SetInstanceActive(false); + } + + int iCurrentPriority = 0; + + // Loop through all the open lessons + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + + // This opportunity has closed + if ( !pLesson->IsOpenOpportunity() || pLesson->IsTimedOut() ) + { + CloseOpportunity( pLesson ); + continue; + } + + // Lesson should be displayed, so it can affect priority + CBaseLesson *pRootLesson = pLesson->GetRoot(); + bool bShouldDisplay = pLesson->ShouldDisplay(); + bool bIsLocked = pLesson->IsLocked(); + + if ( ( bShouldDisplay || bIsLocked ) && + ( pLesson->GetPriority() >= m_iCurrentPriority || pLesson->NoPriority() || bIsLocked ) && + ( pRootLesson && ( pRootLesson->InstanceType() != LESSON_INSTANCE_SINGLE_ACTIVE || !pRootLesson->IsInstanceActive() ) ) ) + { + // Lesson is at the highest priority level, isn't violating instance rules, and has met all the prerequisites + if ( UpdateActiveLesson( pLesson, pRootLesson ) || pRootLesson->IsLearned() ) + { + // Lesson is active + if ( pLesson->IsVisible() || pRootLesson->IsLearned() ) + { + pRootLesson->SetInstanceActive( true ); + + // This active or learned lesson has the highest priority so far + if ( iCurrentPriority < pLesson->GetPriority() && !pLesson->NoPriority() ) + iCurrentPriority = pLesson->GetPriority(); + } + } + else + { + // On second thought, this shouldn't have been displayed + bShouldDisplay = false; + } + } + else + { + // Lesson shouldn't be displayed right now + UpdateInactiveLesson( pLesson ); + } + } + + // Set the priority for next frame + if ( gameinstructor_verbose.GetInt() > 1 && m_iCurrentPriority != iCurrentPriority ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Priority changed from " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%i ", m_iCurrentPriority ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "to " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "%i", iCurrentPriority ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + m_iCurrentPriority = iCurrentPriority; +} + +//========================================================= +//========================================================= +void C_GameInstructor::FireGameEvent( IGameEvent *event ) +{ + VPROF_BUDGET( "C_GameInstructor::FireGameEvent", "GameInstructor" ); + const char *name = event->GetName(); + + if ( Q_strcmp( name, "gameinstructor_draw" ) == 0 ) + { + if ( m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to draw...\n" ); + } + + m_bNoDraw = false; + } + } + else if ( Q_strcmp( name, "gameinstructor_nodraw" ) == 0 ) + { + if ( !m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to not draw...\n" ); + } + + m_bNoDraw = true; + StopAllLessons(); + } + } + else if ( Q_strcmp( name, "round_end" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round ended...\n" ); + } + + CloseAllOpenOpportunities(); + + if ( IsPC() ) + { + // Good place to backup our counts + WriteSaveData(); + } + } + else if ( Q_strcmp( name, "round_start" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round started...\n" ); + } + + CloseAllOpenOpportunities(); + + EvaluateLessonsForGameRules(); + } + else if ( Q_strcmp( name, "player_death" ) == 0 ) + { + #if !defined(NO_STEAM) && defined(USE_CEG) + Steamworks_TestSecret(); + Steamworks_SelfCheck(); + #endif + + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + + if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player died...\n" ); + } + + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + if ( !pRootLesson->CanOpenWhenDead() ) + CloseOpportunity( pLesson ); + } + } + } + else if ( Q_strcmp( name, "player_team" ) == 0 ) + { + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + + if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) && + ( event->GetInt( "team" ) != event->GetInt( "oldteam" ) || event->GetBool( "disconnect" ) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player changed team (or disconnected)...\n" ); + } + + CloseAllOpenOpportunities(); + } + + EvaluateLessonsForGameRules(); + } + else if ( Q_strcmp( name, "player_disconnect" ) == 0 ) + { + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player disconnected...\n" ); + } + + CloseAllOpenOpportunities(); + } + } + else if ( Q_strcmp( name, "map_transition" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Map transition...\n" ); + } + + CloseAllOpenOpportunities(); + + if ( m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "[INSTRUCTOR]: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" ); + } + + m_bNoDraw = false; + } + + if ( IsPC() ) + { + // Good place to backup our counts + WriteSaveData(); + } + } + else if ( Q_strcmp( name, "game_newmap" ) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "New map...\n" ); + } + + CloseAllOpenOpportunities(); + + if ( m_bNoDraw ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "[INSTRUCTOR]: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" ); + } + + m_bNoDraw = false; + } + + if ( IsPC() ) + { + // Good place to backup our counts + WriteSaveData(); + } + } + + else if ( Q_strcmp( name, "set_instructor_group_enabled" ) == 0 ) + { + const char *pszGroup = event->GetString( "group" ); + bool bEnabled = event->GetInt( "enabled" ) != 0; + + if ( pszGroup && pszGroup[0] ) + SetLessonGroupEnabled(pszGroup, bEnabled); + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::DefineLesson( CBaseLesson *pLesson ) +{ + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "defined.\n" ); + } + + m_Lessons.AddToTail( pLesson ); +} + +//========================================================= +//========================================================= +const CBaseLesson * C_GameInstructor::GetLesson( const char *pchLessonName ) +{ + return GetLesson_Internal( pchLessonName ); +} + +//========================================================= +//========================================================= +bool C_GameInstructor::IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const +{ + for ( int i = 0; i < m_OpenOpportunities.Count(); ++i ) + { + CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ]; + + if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() ) + return true; + } + + return false; +} + +//========================================================= +//========================================================= +bool C_GameInstructor::ReadSaveData() +{ + // for external playtests, don't ever read in persisted instructor state, always start fresh + if ( CommandLine()->FindParm( "-playtest" ) ) + return true; + + if ( m_bHasLoadedSaveData ) + return true; + + // Always reset state first in case storage device + // was declined or ends up in faulty state + ResetDisplaysAndSuccesses(); + + m_bHasLoadedSaveData = true; + +#ifdef _X360 + DevMsg( "Read Game Instructor for splitscreen slot %d\n", m_nSplitScreenSlot ); + + if ( m_nSplitScreenSlot < 0 ) + return false; + + if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() ) + return false; + + int iController = XBX_GetUserId( m_nSplitScreenSlot ); + + if ( iController < 0 || XBX_GetUserIsGuest( iController ) ) + { + // Can't read data for guests + return false; + } + + DWORD nStorageDevice = XBX_GetStorageDeviceId( iController ); + if ( !XBX_DescribeStorageDevice( nStorageDevice ) ) + return false; +#endif + + char szFilename[_MAX_PATH]; + +#ifdef _X360 + if ( IsX360() ) + { + XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) ); + int nLen = strlen( szFilename ); + Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" ); + } + else +#endif + { + Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" ); + } + + KeyValues *data = new KeyValues( "Game Instructor Counts" ); + KeyValues::AutoDelete autoDelete(data); + + if ( data->LoadFromFile( g_pFullFileSystem, szFilename, NULL ) ) + { + int nVersion = 0; + + for ( KeyValues *pKey = data->GetFirstSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() ) + { + CBaseLesson *pLesson = GetLesson_Internal( pKey->GetName() ); + + if ( pLesson ) + { + pLesson->SetDisplayCount( pKey->GetInt( "display", 0 ) ); + pLesson->SetSuccessCount( pKey->GetInt( "success", 0 ) ); + + if ( Q_strcmp( pKey->GetName(), "version number" ) == 0 ) + { + nVersion = pLesson->GetSuccessCount(); + } + } + } + + CBaseLesson *pLessonVersionNumber = GetLesson_Internal( "version number" ); + if ( pLessonVersionNumber && !pLessonVersionNumber->IsLearned() ) + { + ResetDisplaysAndSuccesses(); + pLessonVersionNumber->SetSuccessCount( pLessonVersionNumber->GetSuccessLimit() ); + m_bDirtySaveData = true; + } + + + return true; + } + + // Couldn't read from the file + return false; +} + +//========================================================= +//========================================================= +bool C_GameInstructor::WriteSaveData() +{ + if ( engine->IsPlayingDemo() ) + return false; + + if ( !m_bDirtySaveData ) + return true; + +#ifdef _X360 + float flPlatTime = Plat_FloatTime(); + + static ConVarRef host_write_last_time( "host_write_last_time" ); + if ( host_write_last_time.IsValid() ) + { + float flTimeSinceLastWrite = flPlatTime - host_write_last_time.GetFloat(); + if ( flTimeSinceLastWrite < 3.5f ) + { + // Prevent writing to the same storage device twice in less than 3 second succession for TCR success! + // This happens after leaving a game in splitscreen. + //DevMsg( "Waiting to write Game Instructor for splitscreen slot %d... (%.1f seconds remain)\n", m_nSplitScreenSlot, 3.5f - flTimeSinceLastWrite ); + return false; + } + } +#endif + + // Always mark as clean state to avoid re-entry on + // subsequent frames when storage device might be + // in a yet-unmounted state. + m_bDirtySaveData = false; + +#ifdef _X360 + DevMsg( "Write Game Instructor for splitscreen slot %d at time: %.1f\n", m_nSplitScreenSlot, flPlatTime ); + + if ( m_nSplitScreenSlot < 0 ) + return false; + + if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() ) + return false; + + int iController = XBX_GetUserId( m_nSplitScreenSlot ); + + if ( iController < 0 || XBX_GetUserIsGuest( iController ) ) + { + // Can't save data for guests + return false; + } + + DWORD nStorageDevice = XBX_GetStorageDeviceId( iController ); + if ( !XBX_DescribeStorageDevice( nStorageDevice ) ) + return false; +#endif + + // Build key value data to save + KeyValues *data = new KeyValues( "Game Instructor Counts" ); + KeyValues::AutoDelete autoDelete(data); + + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + CBaseLesson *pLesson = m_Lessons[i]; + + int iDisplayCount = pLesson->GetDisplayCount(); + int iSuccessCount = pLesson->GetSuccessCount(); + + if ( iDisplayCount || iSuccessCount ) + { + // We've got some data worth saving + KeyValues *pKVData = new KeyValues( pLesson->GetName() ); + + if ( iDisplayCount ) + pKVData->SetInt( "display", iDisplayCount ); + + if ( iSuccessCount ) + pKVData->SetInt( "success", iSuccessCount ); + + data->AddSubKey( pKVData ); + } + } + + // Save it! + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + data->RecursiveSaveToFile( buf, 0 ); + + char szFilename[_MAX_PATH]; + +#ifdef _X360 + if ( IsX360() ) + { + XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) ); + int nLen = strlen( szFilename ); + Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" ); + } + else +#endif + { + Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" ); + filesystem->CreateDirHierarchy( "save", "MOD" ); + } + + bool bWriteSuccess = filesystem->WriteFile( szFilename, MOD_DIR, buf ); + +#ifdef _X360 + if ( xboxsystem ) + { + xboxsystem->FinishContainerWrites( iController ); + } +#endif + + return bWriteSuccess; +} + +//========================================================= +//========================================================= +void C_GameInstructor::RefreshDisplaysAndSuccesses() +{ + m_bHasLoadedSaveData = false; + ReadSaveData(); +} + +//========================================================= +//========================================================= +void C_GameInstructor::ResetDisplaysAndSuccesses() +{ + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Reset all lesson display and success counts.\n" ); + } + + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + m_Lessons[ i ]->ResetDisplaysAndSuccesses(); + } + + m_bDirtySaveData = false; +} + +//========================================================= +//========================================================= +void C_GameInstructor::MarkDisplayed( const char *pchLessonName ) +{ + CBaseLesson *pLesson = GetLesson_Internal(pchLessonName); + + if ( !pLesson ) + return; + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as displayed.\n" ); + } + + if ( pLesson->IncDisplayCount() ) + m_bDirtySaveData = true; +} + +//========================================================= +//========================================================= +void C_GameInstructor::MarkSucceeded(const char *pchLessonName) +{ + CBaseLesson *pLesson = GetLesson_Internal(pchLessonName); + + if ( !pLesson ) + return; + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as succeeded.\n" ); + } + + if ( pLesson->IncSuccessCount() ) + m_bDirtySaveData = true; +} + +//========================================================= +//========================================================= +void C_GameInstructor::PlaySound( const char *pchSoundName ) +{ + // emit alert sound + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( pLocalPlayer ) + { + // Local player exists + if ( pchSoundName[ 0 ] != '\0' && Q_strcmp( m_szPreviousStartSound, pchSoundName ) != 0 ) + { + Q_strcpy( m_szPreviousStartSound, pchSoundName ); + m_fNextStartSoundTime = 0.0f; + } + + if ( gpGlobals->curtime >= m_fNextStartSoundTime && pchSoundName[ 0 ] != '\0' ) + { + // A sound was specified, so play it! + pLocalPlayer->EmitSound( pchSoundName ); + m_fNextStartSoundTime = gpGlobals->curtime + gameinstructor_start_sound_cooldown.GetFloat(); + } + } +} + +//========================================================= +//========================================================= +bool C_GameInstructor::OpenOpportunity( CBaseLesson *pLesson ) +{ + // Get the root lesson + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + if ( !pRootLesson ) + { + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because root lesson could not be found).\n" ); + } + + delete pLesson; + return false; + } + + C_BasePlayer *pLocalPlayer = GetLocalPlayer(); + + if ( !pRootLesson->CanOpenWhenDead() && ( !pLocalPlayer || !pLocalPlayer->IsAlive() ) ) + { + // If the player is dead don't allow lessons that can't be opened when dead + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because player is dead and can_open_when_dead not set).\n" ); + } + + delete pLesson; + return false; + } + + if ( !pRootLesson->PrerequisitesHaveBeenMet() ) + { + // If the prereqs haven't been met, don't open it + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because prereqs haven't been met).\n" ); + } + + delete pLesson; + return false; + } + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_FIXED_REPLACE ) + { + CBaseLesson *pLessonToReplace = NULL; + CBaseLesson *pLastReplacableLesson = NULL; + + int iInstanceCount = 0; + + // Check how many are already open + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ]; + + if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() && + pOpenOpportunity->GetReplaceKeySymbol() == pLesson->GetReplaceKeySymbol() ) + { + iInstanceCount++; + + if ( pRootLesson->ShouldReplaceOnlyWhenStopped() ) + { + if ( !pOpenOpportunity->IsInstructing() ) + { + pLastReplacableLesson = pOpenOpportunity; + } + } + else + { + pLastReplacableLesson = pOpenOpportunity; + } + + if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() ) + { + pLessonToReplace = pLastReplacableLesson; + break; + } + } + } + + if ( pLessonToReplace ) + { + // Take the place of the previous instance + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "replacing open lesson of same type.\n" ); + } + + pLesson->TakePlaceOf( pLessonToReplace ); + CloseOpportunity( pLessonToReplace ); + } + else if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() ) + { + // Don't add another lesson of this type + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (there is too many started lessons of this type).\n" ); + } + + delete pLesson; + return false; + } + } + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "opened.\n" ); + } + + m_OpenOpportunities.AddToTail( pLesson ); + + return true; +} + +//========================================================= +//========================================================= +void C_GameInstructor::DumpOpenOpportunities() +{ + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open lessons...\n" ); + + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CBaseLesson *pRootLesson = pLesson->GetRoot(); + + Color color; + + if ( pLesson->IsInstructing() ) + { + // Green + color = CBaseLesson::m_rgbaVerboseOpen; + } + else if ( pRootLesson->IsLearned() && pLesson->GetPriority() >= m_iCurrentPriority ) + { + // Yellow + color = CBaseLesson::m_rgbaVerboseSuccess; + } + else + { + // Red + color = CBaseLesson::m_rgbaVerboseClose; + } + + ConColorMsg( color, "\t%s\n", pLesson->GetName() ); + } +} + +//========================================================= +//========================================================= +KeyValues * C_GameInstructor::GetScriptKeys() +{ + return m_pScriptKeys; +} + +//========================================================= +//========================================================= +C_BasePlayer * C_GameInstructor::GetLocalPlayer() +{ + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + // If we're not a developer, don't do the special spectator hook ups + if ( !developer.GetBool() ) + return pLocalPlayer; + + // If there is no local player and we're not spectating, just return that + if ( !pLocalPlayer || pLocalPlayer->GetTeamNumber() != TEAM_SPECTATOR ) + return pLocalPlayer; + + // We're purely a spectator let's get lessons of the person we're spectating + C_BasePlayer *pSpectatedPlayer = NULL; + + if ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE ) + pSpectatedPlayer = ToBasePlayer( pLocalPlayer->GetObserverTarget() ); + + if ( m_hLastSpectatedPlayer != pSpectatedPlayer ) + { + // We're spectating someone new! Close all the stale lessons! + m_bSpectatedPlayerChanged = true; + m_hLastSpectatedPlayer = pSpectatedPlayer; + } + + return pSpectatedPlayer; +} + +//========================================================= +//========================================================= +void C_GameInstructor::EvaluateLessonsForGameRules() +{ + // Enable everything by default + for ( int i = 0; i < m_Lessons.Count(); ++i ) + m_Lessons[ i ]->SetEnabled(true); + + // Then see if we should disable anything + for ( int nConVar = 0; nConVar < m_LessonGroupConVarToggles.Count(); ++nConVar ) + { + LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[ nConVar ]); + + if ( pLessonGroupConVarToggle->var.IsValid() ) + { + if ( pLessonGroupConVarToggle->var.GetBool() ) + SetLessonGroupEnabled( pLessonGroupConVarToggle->szLessonGroupName, false ); + } + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::SetLessonGroupEnabled( const char *pszGroup, bool bEnabled ) +{ + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + if ( !Q_stricmp(pszGroup, m_Lessons[i]->GetGroup()) ) + m_Lessons[i]->SetEnabled( bEnabled ); + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::FindErrors() +{ + // Loop through all the lesson and run all their scripted actions + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + CScriptedIconLesson *pLesson = dynamic_cast( m_Lessons[ i ] ); + if ( pLesson ) + { + // Process all open events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOpenEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetOpenEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all close events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetCloseEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetCloseEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all success events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetSuccessEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetSuccessEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all on open events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOnOpenEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetOnOpenEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetUpdateEvents().Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->GetUpdateEvents()[ iLessonEvent ]); + pLesson->ProcessElements( NULL, &(pLessonEvent->elements) ); + } + } + } +} + +//========================================================= +//========================================================= +bool C_GameInstructor::UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson ) +{ + VPROF_BUDGET( "C_GameInstructor::UpdateActiveLesson", "GameInstructor" ); + + bool bIsOpen = pLesson->IsInstructing(); + + if ( !bIsOpen && !pRootLesson->IsLearned() ) + { + pLesson->SetStartTime(); + pLesson->Start(); + + // Check to see if it successfully started + bIsOpen = ( pLesson->IsOpenOpportunity() && pLesson->ShouldDisplay() ); + + if ( bIsOpen ) + { + // Lesson hasn't been started and hasn't been learned + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Started lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + else + { + pLesson->Stop(); + pLesson->ResetStartTime(); + } + } + + if ( bIsOpen ) + { + // Update the running lesson + pLesson->Update(); + return true; + } + else + { + pLesson->UpdateInactive(); + return false; + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::UpdateInactiveLesson( CBaseLesson *pLesson ) +{ + VPROF_BUDGET( "C_GameInstructor::UpdateInactiveLesson", "GameInstructor" ); + + if ( pLesson->IsInstructing() ) + { + // Lesson hasn't been stopped + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Stopped lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + pLesson->Stop(); + pLesson->ResetStartTime(); + } + + pLesson->UpdateInactive(); +} + +//========================================================= +//========================================================= +CBaseLesson * C_GameInstructor::GetLesson_Internal( const char *pchLessonName ) +{ + for ( int i = 0; i < m_Lessons.Count(); ++i ) + { + CBaseLesson *pLesson = m_Lessons[ i ]; + + if ( Q_strcmp( pLesson->GetName(), pchLessonName ) == 0 ) + { + return pLesson; + } + } + + return NULL; +} + +//========================================================= +//========================================================= +void C_GameInstructor::StopAllLessons() +{ + // Stop all the current lessons + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + UpdateInactiveLesson( pLesson ); + } +} + +//========================================================= +//========================================================= +void C_GameInstructor::CloseAllOpenOpportunities() +{ + // Clear out all the open opportunities + for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i ) + { + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + CloseOpportunity( pLesson ); + } + + Assert( m_OpenOpportunities.Count() == 0 ); +} + +//========================================================= +//========================================================= +void C_GameInstructor::CloseOpportunity( CBaseLesson *pLesson ) +{ + UpdateInactiveLesson( pLesson ); + + if ( pLesson->WasDisplayed() ) + MarkDisplayed( pLesson->GetName() ); + + if ( gameinstructor_verbose.GetInt() > 0 ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "closed for reason: " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%s\n", pLesson->GetCloseReason() ); + } + + pLesson->StopListeningForAllEvents(); + + m_OpenOpportunities.FindAndRemove( pLesson ); + delete pLesson; +} + +//========================================================= +//========================================================= +void C_GameInstructor::ReadLessonsFromFile( const char *pchFileName ) +{ + // Static init function + CScriptedIconLesson::PreReadLessonsFromFile(); + MEM_ALLOC_CREDIT(); + + KeyValues *pLessonKeys = new KeyValues("instructor_lessons"); + KeyValues::AutoDelete autoDelete(pLessonKeys); + + pLessonKeys->LoadFromFile(g_pFullFileSystem, pchFileName, NULL); + + for ( m_pScriptKeys = pLessonKeys->GetFirstTrueSubKey(); m_pScriptKeys; m_pScriptKeys = m_pScriptKeys->GetNextTrueSubKey() ) + { + if ( Q_stricmp(m_pScriptKeys->GetName(), "GroupConVarToggle") == 0 ) + { + // Add convar group toggler to the list + int nLessonGroupConVarToggle = m_LessonGroupConVarToggles.AddToTail( LessonGroupConVarToggle_t( m_pScriptKeys->GetString( "convar" ) ) ); + LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[nLessonGroupConVarToggle]); + + Q_strcpy( pLessonGroupConVarToggle->szLessonGroupName, m_pScriptKeys->GetString("group") ); + continue; + } + + // Ensure that lessons aren't added twice + if ( GetLesson_Internal(m_pScriptKeys->GetName()) ) + { + DevWarning("Lesson \"%s\" defined twice!\n", m_pScriptKeys->GetName()); + continue; + } + + CScriptedIconLesson *pNewLesson = new CScriptedIconLesson(m_pScriptKeys->GetName(), false, false); + GetGameInstructor().DefineLesson(pNewLesson); + } + + m_pScriptKeys = NULL; +} + +//========================================================= +//========================================================= +void C_GameInstructor::InitLessonPrerequisites() +{ + for ( int i = 0; i < m_Lessons.Count(); ++i ) + m_Lessons[ i ]->InitPrerequisites(); +} + +//========================================================= +// Commands +//========================================================= + +CON_COMMAND_F( gameinstructor_reload_lessons, "Shuts down all open lessons and reloads them from the script file.", FCVAR_CHEAT ) +{ + GetGameInstructor().Shutdown(); + GetGameInstructor().Init(); +} + +CON_COMMAND_F( gameinstructor_reset_counts, "Resets all display and success counts to zero.", FCVAR_NONE ) +{ + GetGameInstructor().ResetDisplaysAndSuccesses(); +} + +CON_COMMAND_F( gameinstructor_dump_open_lessons, "Gives a list of all currently open lessons.", FCVAR_CHEAT ) +{ + GetGameInstructor().DumpOpenOpportunities(); +} diff --git a/sp/src/game/client/c_gameinstructor.h b/sp/src/game/client/c_gameinstructor.h new file mode 100644 index 0000000000..00c97c66e4 --- /dev/null +++ b/sp/src/game/client/c_gameinstructor.h @@ -0,0 +1,118 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler for instruction players how to play +// +//=============================================================================// + +#ifndef _C_GAMEINSTRUCTOR_H_ +#define _C_GAMEINSTRUCTOR_H_ + + +#include "GameEventListener.h" +#include "vgui_controls/phandle.h" + +class CBaseLesson; + + +struct LessonGroupConVarToggle_t +{ + ConVarRef var; + char szLessonGroupName[ 64 ]; + + LessonGroupConVarToggle_t( const char *pchConVarName ) : + var( pchConVarName ) + { + } +}; + + +class C_GameInstructor : public CAutoGameSystemPerFrame, public CGameEventListener +{ +public: + C_GameInstructor() : CAutoGameSystemPerFrame( "C_GameInstructor" ) + { + m_bHasLoadedSaveData = false; + m_bDirtySaveData = false; + } + + // Methods of IGameSystem + virtual bool Init( void ); + virtual void Shutdown( void ); + virtual void Update( float frametime ); + + void UpdateHiddenByOtherElements( void ); + bool Mod_HiddenByOtherElements( void ); + + virtual void FireGameEvent( IGameEvent *event ); + + void DefineLesson( CBaseLesson *pLesson ); + + const CBaseLesson * GetLesson( const char *pchLessonName ); + bool IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const; + + bool ReadSaveData( void ); + bool WriteSaveData( void ); + void RefreshDisplaysAndSuccesses( void ); + void ResetDisplaysAndSuccesses( void ); + void MarkDisplayed( const char *pchLessonName ); + void MarkSucceeded( const char *pchLessonName ); + + void PlaySound( const char *pchSoundName ); + + bool OpenOpportunity( CBaseLesson *pLesson ); + + void DumpOpenOpportunities( void ); + + KeyValues * GetScriptKeys( void ); + C_BasePlayer * GetLocalPlayer( void ); + + void EvaluateLessonsForGameRules( void ); + void SetLessonGroupEnabled( const char *pszGroup, bool bEnabled ); + + // Mapbase needs this to be public for map-specific file system + void ReadLessonsFromFile( const char *pchFileName ); + +private: + void FindErrors( void ); + + bool UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson ); + void UpdateInactiveLesson( CBaseLesson *pLesson ); + + CBaseLesson * GetLesson_Internal( const char *pchLessonName ); + + void StopAllLessons( void ); + + void CloseAllOpenOpportunities( void ); + void CloseOpportunity( CBaseLesson *pLesson ); + + void InitLessonPrerequisites( void ); + +private: + CUtlVector < CBaseLesson* > m_Lessons; + CUtlVector < CBaseLesson* > m_OpenOpportunities; + + CUtlVector < LessonGroupConVarToggle_t > m_LessonGroupConVarToggles; + + KeyValues *m_pScriptKeys; + + bool m_bNoDraw; + bool m_bHiddenDueToOtherElements; + + int m_iCurrentPriority; + EHANDLE m_hLastSpectatedPlayer; + bool m_bSpectatedPlayerChanged; + + char m_szPreviousStartSound[ 128 ]; + float m_fNextStartSoundTime; + + bool m_bHasLoadedSaveData; + bool m_bDirtySaveData; +}; + +C_GameInstructor &GetGameInstructor(); + +void GameInstructor_Init(); +void GameInstructor_Shutdown(); + + +#endif // _C_GAMEINSTRUCTOR_H_ diff --git a/sp/src/game/client/c_pixel_visibility.cpp b/sp/src/game/client/c_pixel_visibility.cpp index 8ff507b1b8..340616fdb3 100644 --- a/sp/src/game/client/c_pixel_visibility.cpp +++ b/sp/src/game/client/c_pixel_visibility.cpp @@ -429,8 +429,10 @@ void CPixelVisibilityQuery::IssueQuery( IMatRenderContext *pRenderContext, float return; } } +#ifndef MAPBASE // Mapbase can also query visibility several times via multiple point_cameras, etc. #ifndef PORTAL // FIXME: In portal we query visibility multiple times per frame because of portal renders! Assert ( ( m_frameIssued != gpGlobals->framecount ) || UseVR() ); +#endif #endif m_frameIssued = gpGlobals->framecount; diff --git a/sp/src/game/client/c_point_camera.cpp b/sp/src/game/client/c_point_camera.cpp index e4228422a6..30c109a32d 100644 --- a/sp/src/game/client/c_point_camera.cpp +++ b/sp/src/game/client/c_point_camera.cpp @@ -27,6 +27,7 @@ IMPLEMENT_CLIENTCLASS_DT( C_PointCamera, DT_PointCamera, CPointCamera ) RecvPropInt( RECVINFO( m_bUseScreenAspectRatio ) ), #ifdef MAPBASE RecvPropInt( RECVINFO( m_iSkyMode ) ), + RecvPropString( RECVINFO( m_iszRenderTarget ) ), #endif END_RECV_TABLE() @@ -43,6 +44,10 @@ C_PointCamera::C_PointCamera() m_bActive = false; m_bFogEnable = false; +#ifdef MAPBASE + m_iszRenderTarget[0] = '\0'; +#endif + g_PointCameraList.Insert( this ); } @@ -56,6 +61,16 @@ bool C_PointCamera::ShouldDraw() return false; } +void C_PointCamera::OnDataChanged( DataUpdateType_t type ) +{ +#ifdef MAPBASE + // Reset render texture + m_pRenderTarget = NULL; +#endif + + return BaseClass::OnDataChanged( type ); +} + float C_PointCamera::GetFOV() { return m_FOV; @@ -117,6 +132,26 @@ void C_PointCamera::GetToolRecordingState( KeyValues *msg ) } #ifdef MAPBASE +extern ITexture *GetCameraTexture( void ); +extern void AddReleaseFunc( void ); + +ITexture *C_PointCamera::RenderTarget() +{ + if (m_iszRenderTarget[0] != '\0') + { + if (!m_pRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pRenderTarget = materials->FindTexture( m_iszRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pRenderTarget) + return m_pRenderTarget; + } + + return GetCameraTexture(); +} + IMPLEMENT_CLIENTCLASS_DT( C_PointCameraOrtho, DT_PointCameraOrtho, CPointCameraOrtho ) RecvPropInt( RECVINFO( m_bOrtho ) ), RecvPropArray( RecvPropFloat( RECVINFO( m_OrthoDimensions[0] ) ), m_OrthoDimensions ), diff --git a/sp/src/game/client/c_point_camera.h b/sp/src/game/client/c_point_camera.h index 7ec2bd1e86..c77e9c2061 100644 --- a/sp/src/game/client/c_point_camera.h +++ b/sp/src/game/client/c_point_camera.h @@ -29,6 +29,9 @@ class C_PointCamera : public C_BaseEntity // C_BaseEntity. virtual bool ShouldDraw(); + // Mapbase uses this for m_iszRenderTarget + virtual void OnDataChanged( DataUpdateType_t type ); + float GetFOV(); float GetResolution(); bool IsFogEnabled(); @@ -42,6 +45,8 @@ class C_PointCamera : public C_BaseEntity virtual void GetOrthoDimensions(float &up, float &dn, float &lf, float &rt) const {} SkyboxVisibility_t SkyMode() { return m_iSkyMode; } + + ITexture *RenderTarget(); #endif virtual void GetToolRecordingState( KeyValues *msg ); @@ -58,6 +63,8 @@ class C_PointCamera : public C_BaseEntity bool m_bUseScreenAspectRatio; #ifdef MAPBASE SkyboxVisibility_t m_iSkyMode; + ITexture *m_pRenderTarget; + char m_iszRenderTarget[64]; #endif public: diff --git a/sp/src/game/client/c_rope.cpp b/sp/src/game/client/c_rope.cpp index 859663b30d..450faf7877 100644 --- a/sp/src/game/client/c_rope.cpp +++ b/sp/src/game/client/c_rope.cpp @@ -524,7 +524,7 @@ void CRopeManager::DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_ IMaterial *pMaterial = bShadowDepth ? g_pSplineCableShadowdepth : pRenderCache[iRenderCache].m_pSolidMaterial; // Need to make sure that all rope materials use the splinerope shader since there are a lot of assumptions about how the shader interfaces with this code. - AssertOnce( V_stricmp( pMaterial->GetShaderName(), "SDK_Cable" ) == 0 ); // splinerope + AssertOnce( V_strstr( pMaterial->GetShaderName(), "SDK_Cable" ) != NULL ); // splinerope pRenderContext->Bind( pMaterial ); diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index f245e32236..fa9204eb32 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -8,9 +8,10 @@ $Configuration { $Compiler { - $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS" + $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS;GLOWS_ENABLE" $PreprocessorDefinitions "$BASE;MAPBASE_RPC;DISCORD_RPC;STEAM_RPC" [$MAPBASE_RPC] + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } @@ -21,6 +22,12 @@ $Project $File "c_env_global_light.cpp" $File "worldlight.cpp" $File "worldlight.h" + $File "c_baselesson.cpp" + $File "c_baselesson.h" + $File "c_gameinstructor.cpp" + $File "c_gameinstructor.h" + $File "hud_locator_target.cpp" + $File "hud_locator_target.h" $Folder "Mapbase" { @@ -32,10 +39,16 @@ $Project $File "$SRCDIR\game\shared\mapbase\matchers.cpp" $File "$SRCDIR\game\shared\mapbase\matchers.h" $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_funcs_math.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_math.h" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_consts_shared.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\c_func_clientclip.cpp" + $File "mapbase\c_func_fake_worldportal.cpp" + $File "mapbase\c_func_fake_worldportal.h" + $File "mapbase\c_point_glow.cpp" } $Folder "HL2 DLL" diff --git a/sp/src/game/client/clientmode_shared.cpp b/sp/src/game/client/clientmode_shared.cpp index 0f8d494880..7f818ed03d 100644 --- a/sp/src/game/client/clientmode_shared.cpp +++ b/sp/src/game/client/clientmode_shared.cpp @@ -65,6 +65,10 @@ extern ConVar replay_rendersetting_renderglow; #include "econ_item_description.h" #endif +#ifdef GLOWS_ENABLE +#include "clienteffectprecachesystem.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -89,6 +93,13 @@ extern ConVar voice_modenable; extern bool IsInCommentaryMode( void ); +#ifdef GLOWS_ENABLE +CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffectsGlow ) +CLIENTEFFECT_MATERIAL( "dev/glow_color" ) +CLIENTEFFECT_MATERIAL( "dev/halo_add_to_screen" ) +CLIENTEFFECT_REGISTER_END_CONDITIONAL( engine->GetDXSupportLevel() >= 90 ) +#endif + #ifdef VOICE_VOX_ENABLE void VoxCallback( IConVar *var, const char *oldString, float oldFloat ) { @@ -764,6 +775,10 @@ int ClientModeShared::HudElementKeyInput( int down, ButtonCode_t keynum, const c //----------------------------------------------------------------------------- bool ClientModeShared::DoPostScreenSpaceEffects( const CViewSetup *pSetup ) { +#ifdef GLOWS_ENABLE + g_GlowObjectManager.RenderGlowEffects( pSetup, 0 ); +#endif + #if defined( REPLAY_ENABLED ) if ( engine->IsPlayingDemo() ) { diff --git a/sp/src/game/client/clientshadowmgr.cpp b/sp/src/game/client/clientshadowmgr.cpp index c8e71c5e5a..13e30d1cd0 100644 --- a/sp/src/game/client/clientshadowmgr.cpp +++ b/sp/src/game/client/clientshadowmgr.cpp @@ -3235,7 +3235,8 @@ void CClientShadowMgr::PreRender() { #ifdef ASW_PROJECTED_TEXTURES // only update shadows once per frame - Assert( gpGlobals->framecount != m_nPrevFrameCount ); + if( gpGlobals->framecount == m_nPrevFrameCount ) + return; m_nPrevFrameCount = gpGlobals->framecount; #endif diff --git a/sp/src/game/client/hl2/c_basehlplayer.h b/sp/src/game/client/hl2/c_basehlplayer.h index 1a9f783630..fe838cad76 100644 --- a/sp/src/game/client/hl2/c_basehlplayer.h +++ b/sp/src/game/client/hl2/c_basehlplayer.h @@ -38,6 +38,12 @@ class C_BaseHLPlayer : public C_BasePlayer bool IsFlashlightActive( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_FLASHLIGHT; } bool IsBreatherActive( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_BREATHER; } +#ifdef MAPBASE + bool IsCustomDevice0Active( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_CUSTOM0; } + bool IsCustomDevice1Active( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_CUSTOM1; } + bool IsCustomDevice2Active( void ) { return m_HL2Local.m_bitsActiveDevices & bits_SUIT_DEVICE_CUSTOM2; } +#endif + virtual int DrawModel( int flags ); virtual void BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ); diff --git a/sp/src/game/client/hl2/hud_suitpower.cpp b/sp/src/game/client/hl2/hud_suitpower.cpp index 416c9300cb..f1af00acfd 100644 --- a/sp/src/game/client/hl2/hud_suitpower.cpp +++ b/sp/src/game/client/hl2/hud_suitpower.cpp @@ -104,12 +104,27 @@ void CHudSuitPower::OnThink( void ) bool breatherActive = pPlayer->IsBreatherActive(); int activeDevices = (int)flashlightActive + (int)sprintActive + (int)breatherActive; +#ifdef MAPBASE + activeDevices += (int)pPlayer->IsCustomDevice0Active() + (int)pPlayer->IsCustomDevice1Active() + (int)pPlayer->IsCustomDevice2Active(); +#endif + if (activeDevices != m_iActiveSuitDevices) { m_iActiveSuitDevices = activeDevices; switch ( m_iActiveSuitDevices ) { +#ifdef MAPBASE + case 6: + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerSixItemsActive"); + break; + case 5: + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerFiveItemsActive"); + break; + case 4: + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerFourItemsActive"); + break; +#endif default: case 3: g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SuitAuxPowerThreeItemsActive"); @@ -251,6 +266,59 @@ void CHudSuitPower::Paint() } ypos += text2_gap; } + +#ifdef MAPBASE + if (pPlayer->IsCustomDevice0Active()) + { + tempString = g_pVGuiLocalize->Find("#Mapbase_Hud_DEVICE0"); + + surface()->DrawSetTextPos(text2_xpos, ypos); + + if (tempString) + { + surface()->DrawPrintText(tempString, wcslen(tempString)); + } + else + { + surface()->DrawPrintText(L"CUSTOM 0", wcslen(L"CUSTOM 0")); + } + ypos += text2_gap; + } + + if (pPlayer->IsCustomDevice1Active()) + { + tempString = g_pVGuiLocalize->Find("#Mapbase_Hud_DEVICE1"); + + surface()->DrawSetTextPos(text2_xpos, ypos); + + if (tempString) + { + surface()->DrawPrintText(tempString, wcslen(tempString)); + } + else + { + surface()->DrawPrintText(L"CUSTOM 1", wcslen(L"CUSTOM 1")); + } + ypos += text2_gap; + } + + if (pPlayer->IsCustomDevice2Active()) + { + tempString = g_pVGuiLocalize->Find("#Mapbase_Hud_DEVICE2"); + + surface()->DrawSetTextPos(text2_xpos, ypos); + + if (tempString) + { + surface()->DrawPrintText(tempString, wcslen(tempString)); + } + else + { + surface()->DrawPrintText(L"CUSTOM 2", wcslen(L"CUSTOM 2")); + } + ypos += text2_gap; + } +#endif } } diff --git a/sp/src/game/client/hud.cpp b/sp/src/game/client/hud.cpp index 2455e25297..a7d77a32ed 100644 --- a/sp/src/game/client/hud.cpp +++ b/sp/src/game/client/hud.cpp @@ -472,6 +472,8 @@ void CHud::Init( void ) } FreeHudTextureList( textureList ); + + HudIcons().Init(); } //----------------------------------------------------------------------------- @@ -1200,3 +1202,232 @@ CON_COMMAND_F( testhudanim, "Test a hud element animation.\n\tArguments: GetViewportAnimationController()->StartAnimationSequence( args[1] ); } +CHudIcons::CHudIcons() : + m_bHudTexturesLoaded( false ) +{ +} + +CHudIcons::~CHudIcons() +{ + int c = m_Icons.Count(); + for ( int i = c - 1; i >= 0; i-- ) + { + CHudTexture *tex = m_Icons[ i ]; + g_HudTextureMemoryPool.Free( tex ); + } + m_Icons.Purge(); +} + +void CHudIcons::Init() +{ + if ( m_bHudTexturesLoaded ) + return; + + m_bHudTexturesLoaded = true; + CUtlDict< CHudTexture *, int > textureList; + + // check to see if we have sprites for this res; if not, step down + LoadHudTextures( textureList, "scripts/hud_textures", NULL ); + LoadHudTextures( textureList, "scripts/mod_textures", NULL ); + + LoadHudTextures( textureList, "scripts/instructor_textures", NULL ); +#ifdef HL2_CLIENT_DLL + LoadHudTextures( textureList, "scripts/instructor_textures_hl2", NULL ); +#endif + LoadHudTextures( textureList, "scripts/instructor_modtextures", NULL ); + + int c = textureList.Count(); + for ( int index = 0; index < c; index++ ) + { + CHudTexture* tex = textureList[ index ]; + AddSearchableHudIconToList( *tex ); + } + + FreeHudTextureList( textureList ); +} + +void CHudIcons::Shutdown() +{ + m_bHudTexturesLoaded = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture *CHudIcons::AddUnsearchableHudIconToList( CHudTexture& texture ) +{ + // These names are composed based on the texture file name + char composedName[ 512 ]; + + if ( texture.bRenderUsingFont ) + { + Q_snprintf( composedName, sizeof( composedName ), "%s_c%i", + texture.szTextureFile, texture.cCharacterInFont ); + } + else + { + Q_snprintf( composedName, sizeof( composedName ), "%s_%i_%i_%i_%i", + texture.szTextureFile, texture.rc.left, texture.rc.top, texture.rc.right, texture.rc.bottom ); + } + + CHudTexture *icon = GetIcon( composedName ); + if ( icon ) + { + return icon; + } + + CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc(); + *newTexture = texture; + + SetupNewHudTexture( newTexture ); + + int idx = m_Icons.Insert( composedName, newTexture ); + return m_Icons[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture *CHudIcons::AddSearchableHudIconToList( CHudTexture& texture ) +{ + CHudTexture *icon = GetIcon( texture.szShortName ); + if ( icon ) + { + return icon; + } + + CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc(); + *newTexture = texture; + + SetupNewHudTexture( newTexture ); + + int idx = m_Icons.Insert( texture.szShortName, newTexture ); + return m_Icons[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to an icon in the list +//----------------------------------------------------------------------------- +CHudTexture *CHudIcons::GetIcon( const char *szIcon ) +{ + int i = m_Icons.Find( szIcon ); + if ( i == m_Icons.InvalidIndex() ) + return NULL; + + return m_Icons[ i ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets texture handles for the hud icon +//----------------------------------------------------------------------------- +void CHudIcons::SetupNewHudTexture( CHudTexture *t ) +{ + if ( t->bRenderUsingFont ) + { + vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); + t->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( t->szTextureFile, true ); + t->rc.top = 0; + t->rc.left = 0; + t->rc.right = vgui::surface()->GetCharacterWidth( t->hFont, t->cCharacterInFont ); + t->rc.bottom = vgui::surface()->GetFontTall( t->hFont ); + } + else + { + // Set up texture id and texture coordinates + t->textureId = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( t->textureId, t->szTextureFile, false, false ); + + int wide, tall; + vgui::surface()->DrawGetTextureSize( t->textureId, wide, tall ); + + t->texCoords[ 0 ] = (float)(t->rc.left + 0.5f) / (float)wide; + t->texCoords[ 1 ] = (float)(t->rc.top + 0.5f) / (float)tall; + t->texCoords[ 2 ] = (float)(t->rc.right - 0.5f) / (float)wide; + t->texCoords[ 3 ] = (float)(t->rc.bottom - 0.5f) / (float)tall; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudIcons::RefreshHudTextures() +{ + if ( !m_bHudTexturesLoaded ) + { + Assert( 0 ); + return; + } + + CUtlDict< CHudTexture *, int > textureList; + + // check to see if we have sprites for this res; if not, step down + LoadHudTextures( textureList, "scripts/hud_textures", NULL ); + LoadHudTextures( textureList, "scripts/mod_textures", NULL ); + + LoadHudTextures( textureList, "scripts/instructor_textures", NULL ); + + + // fix up all the texture icons first + int c = textureList.Count(); + for ( int index = 0; index < c; index++ ) + { + CHudTexture *tex = textureList[ index ]; + Assert( tex ); + + CHudTexture *icon = GetIcon( tex->szShortName ); + if ( !icon ) + continue; + + // Update file + Q_strncpy( icon->szTextureFile, tex->szTextureFile, sizeof( icon->szTextureFile ) ); + + if ( !icon->bRenderUsingFont ) + { + // Update subrect + icon->rc = tex->rc; + + // Keep existing texture id, but now update texture file and texture coordinates + vgui::surface()->DrawSetTextureFile( icon->textureId, icon->szTextureFile, false, false ); + + // Get new texture dimensions in case it changed + int wide, tall; + vgui::surface()->DrawGetTextureSize( icon->textureId, wide, tall ); + + // Assign coords + icon->texCoords[ 0 ] = (float)(icon->rc.left + 0.5f) / (float)wide; + icon->texCoords[ 1 ] = (float)(icon->rc.top + 0.5f) / (float)tall; + icon->texCoords[ 2 ] = (float)(icon->rc.right - 0.5f) / (float)wide; + icon->texCoords[ 3 ] = (float)(icon->rc.bottom - 0.5f) / (float)tall; + } + } + + FreeHudTextureList( textureList ); + + // fixup all the font icons + vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); + for (int i = m_Icons.First(); m_Icons.IsValidIndex(i); i = m_Icons.Next(i)) + { + CHudTexture *icon = m_Icons[i]; + if ( !icon ) + continue; + + // Update file + if ( icon->bRenderUsingFont ) + { + icon->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( icon->szTextureFile, true ); + icon->rc.top = 0; + icon->rc.left = 0; + icon->rc.right = vgui::surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont ); + icon->rc.bottom = vgui::surface()->GetFontTall( icon->hFont ); + } + } +} + + +static CHudIcons g_HudIcons; + +CHudIcons &HudIcons() +{ + return g_HudIcons; +} + diff --git a/sp/src/game/client/hud.h b/sp/src/game/client/hud.h index b623a3ebc0..52ff0580bf 100644 --- a/sp/src/game/client/hud.h +++ b/sp/src/game/client/hud.h @@ -194,6 +194,37 @@ class CHud extern CHud gHUD; +//----------------------------------------------------------------------------- +// Purpose: CHudIcons +//----------------------------------------------------------------------------- +class CHudIcons +{ +public: + CHudIcons(); + ~CHudIcons(); + + void Init(); + void Shutdown(); + + CHudTexture *GetIcon( const char *szIcon ); + + // loads a new icon into the list, without duplicates + CHudTexture *AddUnsearchableHudIconToList( CHudTexture& texture ); + CHudTexture *AddSearchableHudIconToList( CHudTexture& texture ); + + void RefreshHudTextures(); + +private: + + void SetupNewHudTexture( CHudTexture *t ); + bool m_bHudTexturesLoaded; + // Global list of known icons + CUtlDict< CHudTexture *, int > m_Icons; + +}; + +CHudIcons &HudIcons(); + //----------------------------------------------------------------------------- // Global fonts used in the client DLL //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/hud_locator_target.cpp b/sp/src/game/client/hud_locator_target.cpp new file mode 100644 index 0000000000..8f0d4c0c0a --- /dev/null +++ b/sp/src/game/client/hud_locator_target.cpp @@ -0,0 +1,2207 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: See header file +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "hud_locator_target.h" +#include "iclientmode.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "iinput.h" +#include "view.h" +#include "hud.h" +#include "hudelement.h" +#include "vgui_int.h" + +#include "hud_macros.h" +#include "iclientmode.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#define ICON_SIZE 0.04f // Icons are ScreenWidth() * ICON_SIZE wide. +#define ICON_GAP 5 // Number of pixels between the icon and the text + +#define OFFSCREEN_ICON_POSITION_RADIUS 100 +#define BUTTON_FONT_HANDLE m_hCaptionFont + +#define ICON_DIST_TOO_FAR (60.0f * 12.0f) + +#define MIN_ICON_ALPHA 0.5 +#define MAX_ICON_ALPHA 1 + +ConVar locator_icon_min_size_non_ss( "locator_icon_min_size_non_ss", "1.0", FCVAR_NONE, "Minimum scale of the icon on the screen" ); +ConVar locator_icon_max_size_non_ss( "locator_icon_max_size_non_ss", "1.5", FCVAR_NONE, "Maximum scale of the icon on the screen" ); + +#define MIN_ICON_SCALE locator_icon_min_size_non_ss.GetFloat() +#define MAX_ICON_SCALE locator_icon_max_size_non_ss.GetFloat() + +#define LOCATOR_OCCLUSION_TEST_RATE 0.25f + +enum +{ + DRAW_ARROW_NO = 0, + DRAW_ARROW_UP, + DRAW_ARROW_DOWN, + DRAW_ARROW_LEFT, + DRAW_ARROW_RIGHT +}; + +ConVar locator_fade_time( "locator_fade_time", "0.3", FCVAR_NONE, "Number of seconds it takes for a lesson to fully fade in/out." ); +ConVar locator_lerp_speed( "locator_lerp_speed", "5.0f", FCVAR_NONE, "Speed that static lessons move along the Y axis." ); +ConVar locator_lerp_rest( "locator_lerp_rest", "2.25f", FCVAR_NONE, "Number of seconds before moving from the center." ); +ConVar locator_lerp_time( "locator_lerp_time", "1.75f", FCVAR_NONE, "Number of seconds to lerp before reaching final destination" ); +ConVar locator_pulse_time( "locator_pulse_time", "1.0f", FCVAR_NONE, "Number of seconds to pulse after changing icon or position" ); +ConVar locator_start_at_crosshair( "locator_start_at_crosshair", "0", FCVAR_NONE, "Start position at the crosshair instead of the top middle of the screen." ); + +ConVar locator_topdown_style( "locator_topdown_style", "0", FCVAR_NONE, "Topdown games set this to handle distance and offscreen location differently." ); + +ConVar locator_background_style( "locator_background_style", "0", FCVAR_NONE, "Setting this to 1 will show rectangle backgrounds behind the items word-bubble pointers." ); +ConVar locator_background_color( "locator_background_color", "255 255 255 5", FCVAR_NONE, "The default color for the background." ); +ConVar locator_background_border_color( "locator_background_border_color", "255 255 255 15", FCVAR_NONE, "The default color for the border." ); +ConVar locator_background_thickness_x( "locator_background_thickness_x", "8", FCVAR_NONE, "How many pixels the background borders the left and right." ); +ConVar locator_background_thickness_y( "locator_background_thickness_y", "0", FCVAR_NONE, "How many pixels the background borders the top and bottom." ); +ConVar locator_background_shift_x( "locator_background_shift_x", "3", FCVAR_NONE, "How many pixels the background is shifted right." ); +ConVar locator_background_shift_y( "locator_background_shift_y", "1", FCVAR_NONE, "How many pixels the background is shifted down." ); +ConVar locator_background_border_thickness( "locator_background_border_thickness", "3", FCVAR_NONE, "How many pixels the background borders the left and right." ); + +ConVar locator_target_offset_x( "locator_target_offset_x", "0", FCVAR_NONE, "How many pixels to offset the locator from the target position." ); +ConVar locator_target_offset_y( "locator_target_offset_y", "0", FCVAR_NONE, "How many pixels to offset the locator from the target position." ); + +ConVar locator_text_drop_shadow( "locator_text_drop_shadow", "1", FCVAR_NONE, "If enabled, a drop shadow is drawn behind caption text. PC only." ); +ConVar locator_text_glow( "locator_text_glow", "0", FCVAR_NONE, "If enabled, a glow is drawn behind caption text" ); +ConVar locator_text_glow_color( "locator_text_glow_color", "255 255 255 255", FCVAR_NONE, "Color of text glow" ); + +ConVar locator_split_maxwide_percent( "locator_split_maxwide_percent", "0.80f", FCVAR_CHEAT ); +ConVar locator_split_len( "locator_split_len", "0.5f", FCVAR_CHEAT ); + +#ifdef MAPBASE +extern ConVar gameinstructor_default_bindingcolor; +#endif + + +//------------------------------------ +CLocatorTarget::CLocatorTarget( void ) +{ + Deactivate( true ); + + PrecacheMaterial("vgui/hud/icon_arrow_left"); + PrecacheMaterial("vgui/hud/icon_arrow_right"); + PrecacheMaterial("vgui/hud/icon_arrow_up"); + PrecacheMaterial("vgui/hud/icon_arrow_down"); + PrecacheMaterial("vgui/hud/icon_arrow_plain"); +} + +//------------------------------------ +void CLocatorTarget::Activate( int serialNumber ) +{ + m_serialNumber = serialNumber; + m_frameLastUpdated = gpGlobals->framecount; + m_isActive = true; + + m_bVisible = true; + m_bOnscreen = true; + m_alpha = 0; + m_fadeStart = gpGlobals->curtime; + + m_offsetX = m_offsetY = 0; + + int iStartX = ScreenWidth() / 2; + int iStartY = ScreenHeight() / 4; + + // We want to start lessons at the players crosshair, cause that's where they're looking! + if ( locator_start_at_crosshair.GetBool() ) + vgui::input()->GetCursorPos( iStartX, iStartY ); + + m_lastXPos = iStartX; + m_lastYPos = iStartY; + + m_drawArrowDirection = DRAW_ARROW_NO; + m_lerpStart = gpGlobals->curtime; + m_pulseStart = gpGlobals->curtime; + m_declutterIndex = 0; + m_lastDeclutterIndex = 0; + + AddIconEffects(LOCATOR_ICON_FX_FADE_IN); + +#ifdef MAPBASE + // Mods are capable of using a custom binding color + CSplitString colorValues( gameinstructor_default_bindingcolor.GetString(), "," ); + + int r,g,b; + r = g = b = 0; + + if (colorValues.Count() == 3) + { + r = atoi( colorValues[0] ); + g = atoi( colorValues[1] ); + b = atoi( colorValues[2] ); + } + + m_bindingColor.SetColor( r, g, b, 255 ); +#endif +} + +//------------------------------------ +void CLocatorTarget::Deactivate( bool bNoFade ) +{ + if ( bNoFade || m_alpha == 0 || + ( m_bOccluded && !( m_iEffectsFlags & LOCATOR_ICON_FX_FORCE_CAPTION ) ) || + ( !m_bOnscreen && ( m_iEffectsFlags & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) ) + { + m_bOriginInScreenspace = false; + + m_serialNumber = -1; + m_isActive = false; + m_frameLastUpdated = 0; + m_pIcon_onscreen = NULL; + m_pIcon_offscreen = NULL; + m_bDrawControllerButton = false; + m_bDrawControllerButtonOffscreen = false; + m_iEffectsFlags = LOCATOR_ICON_FX_NONE; + m_captionWide = 0; + + m_pchDrawBindingName = NULL; + m_pchDrawBindingNameOffscreen = NULL; + m_widthScale_onscreen = 1.0f; + m_bOccluded = false; + m_alpha = 0; + m_bIsDrawing = false; + m_bVisible = false; + + m_szVguiTargetName = ""; + m_szVguiTargetLookup = ""; + m_hVguiTarget = NULL; + m_nVguiTargetEdge = vgui::Label::a_northwest; + + m_szBinding = ""; + m_iBindingTick = 0; + m_flNextBindingTick = 0.0f; + m_flNextOcclusionTest = 0.0f; + m_iBindingChoicesCount = 0; + + m_wszCaption.RemoveAll(); + m_wszCaption.AddToTail( (wchar_t)0 ); + } + else if ( !( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) ) + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( 1.0f - static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN ); + } +} + +//------------------------------------ +void CLocatorTarget::Update() +{ + m_frameLastUpdated = gpGlobals->framecount; + + if ( m_bVisible && ( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) ) + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( 1.0f - static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + } +} + +int CLocatorTarget::GetIconX( void ) +{ + return m_iconX + ( IsOnScreen() ? locator_target_offset_x.GetInt()+m_offsetX : 0 ); +} + +int CLocatorTarget::GetIconY( void ) +{ + return m_iconY + ( IsOnScreen() ? locator_target_offset_y.GetInt()+m_offsetY : 0 ); +} + +int CLocatorTarget::GetIconCenterX( void ) +{ + return m_centerX + locator_target_offset_x.GetInt() + m_offsetX; +} + +int CLocatorTarget::GetIconCenterY( void ) +{ + return m_centerY + locator_target_offset_y.GetInt() + m_offsetY; +} + +void CLocatorTarget::SetVisible( bool bVisible ) +{ + // They are already the same + if ( m_bVisible == bVisible ) + return; + + m_bVisible = bVisible; + + if ( bVisible ) + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_IN ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + } + else + { + // Determine home much time it would have spent fading to reach the current alpha + float flAssumedFadeTime; + flAssumedFadeTime = ( 1.0f - static_cast( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat(); + + // Set the fade + m_fadeStart = gpGlobals->curtime - flAssumedFadeTime; + AddIconEffects( LOCATOR_ICON_FX_FADE_OUT ); + RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN ); + } +} + +bool CLocatorTarget::IsVisible( void ) +{ + return m_bVisible; +} + +void CLocatorTarget::SetCaptionText( const char *pszText, const char *pszParam ) +{ + wchar_t outbuf[ 256 ]; + outbuf[ 0 ] = L'\0'; + + if ( pszParam && pszParam[ 0 ] != '\0' ) + { + wchar_t wszParamBuff[ 128 ]; + wchar_t *pLocalizedParam = NULL; + + if ( pszParam[ 0 ] == '#' ) + { + pLocalizedParam = g_pVGuiLocalize->Find( pszParam ); + } + + if ( !pLocalizedParam ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszParam, wszParamBuff, sizeof( wszParamBuff ) ); + pLocalizedParam = wszParamBuff; + } + + wchar_t wszTextBuff[ 128 ]; + wchar_t *pLocalizedText = NULL; + + if ( pszText[ 0 ] == '#' ) + pLocalizedText = g_pVGuiLocalize->Find( pszText ); + + if ( !pLocalizedText ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) ); + pLocalizedText = wszTextBuff; + } + + wchar_t buf[ 256 ]; + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), pLocalizedText, 1, pLocalizedParam ); + + UTIL_ReplaceKeyBindings( buf, sizeof( buf ), outbuf, sizeof( outbuf ) ); + } + else + { + wchar_t wszTextBuff[ 128 ]; + wchar_t *pLocalizedText = NULL; + + if ( pszText[ 0 ] == '#' ) + { + pLocalizedText = g_pVGuiLocalize->Find( pszText ); + } + + if ( !pLocalizedText ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) ); + pLocalizedText = wszTextBuff; + } + + wchar_t buf[ 256 ]; + Q_wcsncpy( buf, pLocalizedText, sizeof( buf ) ); + + UTIL_ReplaceKeyBindings( buf, sizeof(buf), outbuf, sizeof( outbuf ) ); + } + + int len = wcslen( outbuf ) + 1; + m_wszCaption.RemoveAll(); + m_wszCaption.EnsureCount( len ); + Q_wcsncpy( m_wszCaption.Base(), outbuf, len * sizeof( wchar_t ) ); +} + +void CLocatorTarget::SetCaptionColor( const char *pszCaptionColor ) +{ + int r,g,b; + r = g = b = 0; + + CSplitString colorValues( pszCaptionColor, "," ); + + if( colorValues.Count() == 3 ) + { + r = atoi( colorValues[0] ); + g = atoi( colorValues[1] ); + b = atoi( colorValues[2] ); + + m_captionColor.SetColor( r,g,b, 255 ); + } + else + { + DevWarning( "caption_color format incorrect. RRR,GGG,BBB expected.\n"); + } +} + +bool CLocatorTarget::IsStatic() +{ + return ( ( m_iEffectsFlags & LOCATOR_ICON_FX_STATIC ) || IsPresenting() ); +} + +bool CLocatorTarget::IsPresenting() +{ + return ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() ); +} + +void CLocatorTarget::StartTimedLerp() +{ + if ( gpGlobals->curtime - m_lerpStart > locator_lerp_rest.GetFloat() ) + { + m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat(); + } +} + +void CLocatorTarget::StartPresent() +{ + m_lerpStart = gpGlobals->curtime; +} + + +void CLocatorTarget::EndPresent() +{ + if ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() ) + { + m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat(); + } +} + +void CLocatorTarget::UpdateVguiTarget( void ) +{ + const char *pchVguiTargetName = m_szVguiTargetName.String(); + + if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' ) + { + m_hVguiTarget = NULL; + return; + } + + // Get the appropriate token based on the binding + if ( m_iBindingChoicesCount > 0 ) + { + int nTagetToken = m_iBindChoicesOriginalToken[ m_iBindingTick % m_iBindingChoicesCount ]; + + for ( int nToken = 0; nToken < nTagetToken && pchVguiTargetName; ++nToken ) + { + pchVguiTargetName = strchr( pchVguiTargetName, ';' ); + + if ( pchVguiTargetName ) + { + pchVguiTargetName++; + } + } + + if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' ) + { + // There wasn't enough tokens, just use the first + pchVguiTargetName = m_szVguiTargetName.String(); + } + } + + m_hVguiTarget = g_pClientMode->GetViewport(); +} + +void CLocatorTarget::SetVguiTargetName( const char *pchVguiTargetName ) +{ + if ( Q_strcmp( m_szVguiTargetName.String(), pchVguiTargetName ) == 0 ) + return; + + m_szVguiTargetName = pchVguiTargetName; + + UpdateVguiTarget(); +} + +void CLocatorTarget::SetVguiTargetLookup( const char *pchVguiTargetLookup ) +{ + m_szVguiTargetLookup = pchVguiTargetLookup; +} + +void CLocatorTarget::SetVguiTargetEdge( int nVguiEdge ) +{ + m_nVguiTargetEdge = nVguiEdge; +} + +vgui::Panel *CLocatorTarget::GetVguiTarget( void ) +{ + return (vgui::Panel *)m_hVguiTarget.Get(); +} + +//------------------------------------ +void CLocatorTarget::SetOnscreenIconTextureName( const char *pszTexture ) +{ + if ( Q_strcmp( m_szOnscreenTexture.String(), pszTexture ) == 0 ) + return; + + m_szOnscreenTexture = pszTexture; + m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name. + m_pulseStart = gpGlobals->curtime; +} + +//------------------------------------ +void CLocatorTarget::SetOffscreenIconTextureName( const char *pszTexture ) +{ + if ( Q_strcmp( m_szOffscreenTexture.String(), pszTexture ) == 0 ) + return; + + m_szOffscreenTexture = pszTexture; + m_pIcon_offscreen = NULL; // Ditto + m_pulseStart = gpGlobals->curtime; +} + +//------------------------------------ +void CLocatorTarget::SetBinding( const char *pszBinding ) +{ + int iAllowJoystick = -1; + + /*if ( !IsX360() ) + { + // Only show joystick binds if it's enabled and non-joystick if it's disabled + iAllowJoystick = input->ControllerModeActive(); + }*/ + + bool bIsControllerNow = ( iAllowJoystick != 0 ); + + if ( m_bWasControllerLast == bIsControllerNow ) + { + // We haven't toggled joystick enabled recently, so if it's the same bind, bail + if ( Q_strcmp( m_szBinding.String(), pszBinding ) == 0 ) + return; + } + + m_bWasControllerLast = bIsControllerNow; + + m_szBinding = pszBinding; + m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name. + m_pIcon_offscreen = NULL; // ditto. + m_flNextBindingTick = gpGlobals->curtime + 0.75f; + + // Get a list of all the keys bound to these actions + m_iBindingChoicesCount = 0; + + // Tokenize the binding name (could be more than one binding) + int nOriginalToken = 0; + const char *pchToken = m_szBinding.String(); + char szToken[ 128 ]; + + pchToken = nexttoken( szToken, pchToken, ';', sizeof( szToken ) ); + + while ( pchToken ) + { + // Get the first parameter + int iTokenBindingCount = 0; + const char *pchBinding = engine->Key_LookupBindingExact( szToken ); + + while ( m_iBindingChoicesCount < MAX_LOCATOR_BINDINGS_SHOWN && pchBinding ) + { + m_pchBindingChoices[ m_iBindingChoicesCount ] = pchBinding; + m_iBindChoicesOriginalToken[ m_iBindingChoicesCount ] = nOriginalToken; + ++m_iBindingChoicesCount; + ++iTokenBindingCount; + + pchBinding = engine->Key_LookupBindingExact( szToken ); + } + + nOriginalToken++; + pchToken = nexttoken( szToken, pchToken, ';', sizeof( szToken ) ); + } + + m_pulseStart = gpGlobals->curtime; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CLocatorTarget::UseBindingImage( char *pchIconTextureName, size_t bufSize ) +{ + if ( m_iBindingChoicesCount <= 0 ) + { + if ( IsX360() ) + { + Q_strncpy( pchIconTextureName, "icon_blank", bufSize ); + } + else + { + Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize ); + return "#GameUI_Icons_NONE"; + } + + return NULL; + } + + // Cycle through the list of binds at a rate of 2 per second + const char *pchBinding = m_pchBindingChoices[ m_iBindingTick % m_iBindingChoicesCount ]; + + // We counted at least one binding... this should not be NULL! + Assert( pchBinding ); + + if ( IsX360() ) + { + // Use a blank background for the button icons + Q_strncpy( pchIconTextureName, "icon_blank", bufSize ); + return pchBinding; + } + + /*if ( input->ControllerModeActive() && + ( Q_strcmp( pchBinding, "A_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "B_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "X_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "Y_BUTTON" ) == 0 || + Q_strcmp( pchBinding, "L_SHOULDER" ) == 0 || + Q_strcmp( pchBinding, "R_SHOULDER" ) == 0 || + Q_strcmp( pchBinding, "L_TRIGGER" ) == 0 || + Q_strcmp( pchBinding, "R_TRIGGER" ) == 0 || + Q_strcmp( pchBinding, "BACK" ) == 0 || + Q_strcmp( pchBinding, "START" ) == 0 || + Q_strcmp( pchBinding, "STICK1" ) == 0 || + Q_strcmp( pchBinding, "STICK2" ) == 0 || + Q_strcmp( pchBinding, "UP" ) == 0 || + Q_strcmp( pchBinding, "DOWN" ) == 0 || + Q_strcmp( pchBinding, "LEFT" ) == 0 || + Q_strcmp( pchBinding, "RIGHT" ) == 0 ) ) + { + // Use a blank background for the button icons + Q_strncpy( pchIconTextureName, "icon_blank", bufSize ); + return pchBinding; + }*/ + + if ( Q_strcmp( pchBinding, "MOUSE1" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseLeft", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MOUSE2" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseRight", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MOUSE3" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseThree", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MWHEELUP" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseWheel_up", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "MWHEELDOWN" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_mouseWheel_down", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "UPARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_up", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "LEFTARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_left", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "DOWNARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_down", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "RIGHTARROW" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_right", bufSize ); + return NULL; + } + else if ( Q_strcmp( pchBinding, "SEMICOLON" ) == 0 || + Q_strcmp( pchBinding, "INS" ) == 0 || + Q_strcmp( pchBinding, "DEL" ) == 0 || + Q_strcmp( pchBinding, "HOME" ) == 0 || + Q_strcmp( pchBinding, "END" ) == 0 || + Q_strcmp( pchBinding, "PGUP" ) == 0 || + Q_strcmp( pchBinding, "PGDN" ) == 0 || + Q_strcmp( pchBinding, "PAUSE" ) == 0 || + Q_strcmp( pchBinding, "F10" ) == 0 || + Q_strcmp( pchBinding, "F11" ) == 0 || + Q_strcmp( pchBinding, "F12" ) == 0 ) + { + Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize ); + return pchBinding; + } + else if ( Q_strlen( pchBinding ) <= 2 ) + { + Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize ); + return pchBinding; + } + else if ( Q_strlen( pchBinding ) <= 6 ) + { + Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize ); + return pchBinding; + } + else + { + Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize ); + return pchBinding; + } + + return pchBinding; +} + +//----------------------------------------------------------------------------- +int CLocatorTarget::GetIconWidth( void ) +{ + return m_wide; +} + +//----------------------------------------------------------------------------- +int CLocatorTarget::GetIconHeight( void ) +{ + return m_tall; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLocatorPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CLocatorPanel, vgui::EditablePanel ); +public: + CLocatorPanel( vgui::Panel *parent, const char *name ); + ~CLocatorPanel( void ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PerformLayout( void ); + virtual void OnTick( void ); + virtual void PaintBackground( void ); + virtual void Paint( void ); + void ValidateTexture( int *pTextureID, const char *pszTextureName ); + bool ValidateTargetTextures( CLocatorTarget *pTarget ); + bool IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance ); + virtual void PaintTarget( CLocatorTarget *pTarget ); + + void DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer ); + void DrawStaticIcon( CLocatorTarget *pTarget ); + void DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow ); + void DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, int direction ); + void DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline ); + int GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont ); + void DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController ); + void ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition ); + void CalculateOcclusion( CLocatorTarget *pTarget ); + + void DrawSimpleArrow( int x, int y, int iconWide, int iconTall ); + void GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos ); + + CLocatorTarget *GetPointerForHandle( int hTarget ); + int AddTarget(); + void RemoveTarget( int hTarget ); + + void GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ); + + void DeactivateAllTargets(); + void CollectGarbage(); + + // Animation + void AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart ); + void AnimateIconPosition( int flags, int *x, int *y ); + void AnimateIconAlpha( int flags, int *alpha, float fadeStart ); + +private: + + CPanelAnimationVar( vgui::HFont, m_hCaptionFont, "font", "InstructorTitle" ); + CPanelAnimationVar( vgui::HFont, m_hCaptionFont_ss, "font", "InstructorTitle_ss" ); + CPanelAnimationVar( vgui::HFont, m_hCaptionGlowFont, "font", "InstructorTitleGlow" ); + CPanelAnimationVar( vgui::HFont, m_hCaptionGlowFont_ss, "font", "InstructorTitleGlow_ss" ); + + CPanelAnimationVar( vgui::HFont, m_hButtonFont, "font", "InstructorButtons" ); + + CPanelAnimationVar( vgui::HFont, m_hButtonFont_ss, "font", "InstructorButtons_ss" ); + CPanelAnimationVar( vgui::HFont, m_hKeysFont, "font", "InstructorKeyBindings" ); + + + CPanelAnimationVar( int, m_iShouldWrapStaticLocators, "WrapStaticLocators", "0" ); + + static int m_serializer; // Used to issue unique serial numbers to targets, for use as handles + int m_textureID_ArrowRight; + int m_textureID_ArrowLeft; + int m_textureID_ArrowUp; + int m_textureID_ArrowDown; + int m_textureID_SimpleArrow; + + int m_staticIconPosition;// Helps us stack static icons + + CLocatorTarget m_targets[MAX_LOCATOR_TARGETS]; +}; + +//----------------------------------------------------------------------------- +// Local variables +//----------------------------------------------------------------------------- +static CLocatorPanel *s_pLocatorPanel; + +inline CLocatorPanel * GetPlayerLocatorPanel() +{ + //if ( !engine->IsLocalPlayerResolvable() ) + //return NULL; + + Assert( s_pLocatorPanel ); + return s_pLocatorPanel; +} + + +//----------------------------------------------------------------------------- +// Static variable initialization +//----------------------------------------------------------------------------- +int CLocatorPanel::m_serializer = 1000; // Serial numbers start at 1000 + +//----------------------------------------------------------------------------- +// This is the interface function that other systems use to send us targets +//----------------------------------------------------------------------------- +int Locator_AddTarget() +{ + if( s_pLocatorPanel == NULL ) + { + // Locator has not been used yet. Construct it. + CLocatorPanel *pLocator = new CLocatorPanel( g_pClientMode->GetViewport(), "LocatorPanel" ); + vgui::SETUP_PANEL(pLocator); + pLocator->SetBounds( 0, 0, ScreenWidth(), ScreenHeight() ); + pLocator->SetPos( 0, 0 ); + pLocator->SetVisible( true ); + vgui::ivgui()->AddTickSignal( pLocator->GetVPanel() ); + } + + Assert( s_pLocatorPanel != NULL ); + return s_pLocatorPanel ? s_pLocatorPanel->AddTarget() : -1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void Locator_RemoveTarget( int hTarget ) +{ + if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() ) + pPanel->RemoveTarget( hTarget ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CLocatorTarget *Locator_GetTargetFromHandle( int hTarget ) +{ + if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() ) + return pPanel->GetPointerForHandle( hTarget ); + else + return NULL; +} + +void Locator_ComputeTargetIconPositionFromHandle( int hTarget ) +{ + if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() ) + { + if ( CLocatorTarget *pTarget = pPanel->GetPointerForHandle( hTarget ) ) + { + if( !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) + { + // It's not presenting in the middle of the screen, so figure out it's position + pPanel->ComputeTargetIconPosition( pTarget, !pTarget->IsPresenting() ); + pPanel->CalculateOcclusion( pTarget ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CLocatorPanel::CLocatorPanel( Panel *parent, const char *name ) : EditablePanel(parent,name) +{ + Assert( s_pLocatorPanel == NULL ); + DeactivateAllTargets(); + + s_pLocatorPanel = this; + m_textureID_ArrowRight = -1; + m_textureID_ArrowLeft = -1; + m_textureID_ArrowUp = -1; + m_textureID_ArrowDown = -1; + m_textureID_SimpleArrow = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CLocatorPanel::~CLocatorPanel( void ) +{ + Assert( s_pLocatorPanel == this ); + s_pLocatorPanel = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Applies scheme settings +//----------------------------------------------------------------------------- +void CLocatorPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings("resource/UI/Locator.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + vgui::Panel *pPanel = FindChildByName( "LocatorBG" ); + + if ( pPanel ) + pPanel->SetPos( (GetWide() - pPanel->GetWide()) * 0.5, (GetTall() - pPanel->GetTall()) * 0.5 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given an offscreen target position, compute the 'compass position' +// so that we can draw an icon on the imaginary circle around the crosshair +// that indicates which way the player should turn to bring the target into view. +//----------------------------------------------------------------------------- +void CLocatorPanel::GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ) +{ + // Player Data + Vector playerPosition = MainViewOrigin(); + QAngle playerAngles = MainViewAngles(); + + Vector forward, right, up(0,0,1); + AngleVectors (playerAngles, &forward, NULL, NULL ); + forward.z = 0; + VectorNormalize(forward); + CrossProduct( up, forward, right ); + float front = DotProduct(vecDelta, forward); + float side = DotProduct(vecDelta, right); + *xpos = flRadius * -side; + *ypos = flRadius * -front; + + // Get the rotation (yaw) + *flRotation = atan2(*xpos,*ypos) + M_PI; + *flRotation *= 180 / M_PI; + + float yawRadians = -(*flRotation) * M_PI / 180.0f; + float ca = cos( yawRadians ); + float sa = sin( yawRadians ); + + // Rotate it around the circle, squash Y to make an oval rather than a circle + *xpos = (int)((ScreenWidth() / 2) + (flRadius * sa)); + *ypos = (int)((ScreenHeight() / 2) - (flRadius * 0.6f * ca)); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLocatorPanel::DeactivateAllTargets() +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + m_targets[ i ].Deactivate( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Deactivate any target that has not been updated within several frames +//----------------------------------------------------------------------------- +void CLocatorPanel::CollectGarbage() +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + if( m_targets[ i ].m_isActive ) + { + if( gpGlobals->framecount - m_targets[ i ].m_frameLastUpdated > 20 ) + m_targets[ i ].Deactivate(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Provide simple animation by modifying the width and height of the +// icon before it is drawn. +//----------------------------------------------------------------------------- +void CLocatorPanel::AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart ) +{ + float flScale = MIN_ICON_SCALE; + float scaleDelta = MAX_ICON_SCALE - MIN_ICON_SCALE; + + float newWide = *wide; + float newTall = *tall; + + if( flags & LOCATOR_ICON_FX_PULSE_SLOW || gpGlobals->curtime - fPulseStart < locator_pulse_time.GetFloat() ) + { + flScale += scaleDelta * fabs( sin( ( gpGlobals->curtime - fPulseStart ) * M_PI ) ); + } + else if( flags & LOCATOR_ICON_FX_PULSE_FAST ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 2 * M_PI ) ); + } + else if( flags & LOCATOR_ICON_FX_PULSE_URGENT ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 4 * M_PI ) ); + } + + if ( newWide > newTall ) + { + // Get scale to make width change by only the standard height amount of pixels + int iHeightDelta = (int)(newTall * flScale - newTall); + flScale = ( newWide + iHeightDelta ) / newWide; + } + + newWide = newWide * flScale; + newTall = newTall * flScale; + + *wide = newWide; + *tall = newTall; +} + +//----------------------------------------------------------------------------- +// Purpose: Modify the alpha of the icon before it is drawn. +//----------------------------------------------------------------------------- +void CLocatorPanel::AnimateIconAlpha( int flags, int *alpha, float fadeStart ) +{ + float flScale = MIN_ICON_ALPHA; + float scaleDelta = MAX_ICON_ALPHA - MIN_ICON_ALPHA; + + if( flags & LOCATOR_ICON_FX_ALPHA_SLOW ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 3 ) ); + } + else if( flags & LOCATOR_ICON_FX_ALPHA_FAST ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 7 ) ); + } + else if( flags & LOCATOR_ICON_FX_ALPHA_URGENT ) + { + flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 10 ) ); + } + else + { + flScale = MAX_ICON_ALPHA; + } + + if ( flags & LOCATOR_ICON_FX_FADE_OUT ) + { + flScale *= MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() ); + } + else if ( flags & LOCATOR_ICON_FX_FADE_IN ) + { + flScale *= MAX_ICON_ALPHA - MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() ); + } + + *alpha = static_cast( 255.0f * flScale ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::AnimateIconPosition( int flags, int *x, int *y ) +{ + int newX = *x; + int newY = *y; + + if( flags & LOCATOR_ICON_FX_SHAKE_NARROW ) + { + newX += RandomInt( -2, 2 ); + newY += RandomInt( -2, 2 ); + } + else if( flags & LOCATOR_ICON_FX_SHAKE_WIDE ) + { + newX += RandomInt( -5, 5 ); + newY += RandomInt( -5, 5 ); + } + + *x = newX; + *y = newY; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::OnTick( void ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::PaintBackground( void ) +{ + return; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLocatorPanel::Paint( void ) +{ + ValidateTexture( &m_textureID_ArrowLeft, "vgui/hud/icon_arrow_left" ); + ValidateTexture( &m_textureID_ArrowRight, "vgui/hud/icon_arrow_right" ); + ValidateTexture( &m_textureID_ArrowUp, "vgui/hud/icon_arrow_up" ); + ValidateTexture( &m_textureID_ArrowDown, "vgui/hud/icon_arrow_down" ); + ValidateTexture( &m_textureID_SimpleArrow, "vgui/hud/icon_arrow_plain" ); + + // reset the static icon position. This is the y position at which the first + // static icon will be drawn. This value will be incremented by the height of + // each static icon drawn, which forces the next static icon to be drawn below + // the previous one, creating a little fixed, vertical stack below the crosshair. + m_staticIconPosition = ScreenHeight() / 6; + + // Time now to draw the 'dynamic' icons, the icons which help players locate things + // in actual world space. + + //---------- + // Batch 1 + // Go through all of the active locator targets and compute where to draw the icons + // that represent each of them. This builds a poor man's draw list by updating the + // m_iconX, m_iconY members of each locator target. + CUtlVectorFixed< CLocatorTarget *, MAX_LOCATOR_TARGETS > vecValid; + + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + CLocatorTarget *pLocatorTarget = &(m_targets[ i ]); + + // Reset drawing state for this frame... set back to true when it's finally draws + pLocatorTarget->m_bIsDrawing = false; + + if ( ( !pLocatorTarget->m_bVisible && !pLocatorTarget->m_alpha ) || !pLocatorTarget->m_isActive ) + { + // Don't want to be visible and have finished fading + continue; + } + + vecValid.AddToTail( pLocatorTarget ); + + // This prevents an error that if a locator was fading as the map transitioned + pLocatorTarget->m_fadeStart = fpmin( pLocatorTarget->m_fadeStart, gpGlobals->curtime ); + + if( !( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) + { + // It's not presenting in the middle of the screen, so figure out it's position + ComputeTargetIconPosition( pLocatorTarget, !pLocatorTarget->IsPresenting() ); + CalculateOcclusion( pLocatorTarget ); + + pLocatorTarget->m_lastDeclutterIndex = pLocatorTarget->m_declutterIndex; + pLocatorTarget->m_declutterIndex = 0; + } + } + + //---------- + // Batch 2 + // Now that we know where each icon _wants_ to be drawn, we grovel through them and + // push apart any icons that are too close to one another. This helps to unclutter + // the display and ensure the maximum number of legible icons and captions. Obviously + // this process changes where some icons will be drawn. Bubble sort, but tiny data set. + int iTolerance = 1.25 * (ScreenWidth() * ICON_SIZE); + int iterations = 0;// Count iterations, don't go infinite in the event of some weird case. + bool bStillUncluttering = true; + static int MAX_UNCLUTTER_ITERATIONS = 10; + while( iterations < MAX_UNCLUTTER_ITERATIONS && bStillUncluttering ) + { + iterations++; + bStillUncluttering = false; + + for( int i = 0 ; i < vecValid.Count() ; ++i ) + { + CLocatorTarget *pLocatorTarget1 = vecValid[ i ]; + + for( int j = i + 1 ; j < vecValid.Count() ; ++j ) + { + CLocatorTarget *pLocatorTarget2 = vecValid[ j ]; + + // Don't attempt to declutter icons if one or both is attempting to fade out + bool bLocatorsFullyActive = !((pLocatorTarget1->GetIconEffectsFlags()|pLocatorTarget2->GetIconEffectsFlags()) & LOCATOR_ICON_FX_FADE_OUT); + + if ( bLocatorsFullyActive && IconsAreIntersecting( *pLocatorTarget1, *pLocatorTarget2, iTolerance ) ) + { + // Unclutter. Lift whichever icon is highest a bit higher + if( pLocatorTarget1->m_iconY < pLocatorTarget2->m_iconY ) + { + pLocatorTarget1->m_iconY = pLocatorTarget2->m_iconY - iTolerance; + pLocatorTarget1->m_centerY = pLocatorTarget2->m_centerY - iTolerance; + pLocatorTarget1->m_declutterIndex -= 1; + } + else + { + pLocatorTarget2->m_iconY = pLocatorTarget1->m_iconY - iTolerance; + pLocatorTarget2->m_centerY = pLocatorTarget1->m_centerY - iTolerance; + pLocatorTarget2->m_declutterIndex -= 1; + } + + bStillUncluttering = true; + } + } + } + } + + if( iterations == MAX_UNCLUTTER_ITERATIONS ) + { + DevWarning( "Game instructor hit MAX_UNCLUTTER_ITERATIONS!\n"); + } + + float flLocatorLerpRest = locator_lerp_rest.GetFloat(); + float flLocatorLerpTime = locator_lerp_time.GetFloat(); + + //---------- + // Batch 3 + // Draw each of the icons. + for( int i = 0 ; i < vecValid.Count() ; i++ ) + { + CLocatorTarget *pLocatorTarget = vecValid[ i ]; + // Back to lerping for these guys + if ( pLocatorTarget->m_lastDeclutterIndex != pLocatorTarget->m_declutterIndex ) + { + // It wants to be popped to another position... do it smoothly + pLocatorTarget->StartTimedLerp(); + } + + // Lerp to the desired position + float flLerpTime = gpGlobals->curtime - pLocatorTarget->m_lerpStart; + + if ( flLerpTime >= flLocatorLerpRest && flLerpTime < flLocatorLerpRest + flLocatorLerpTime ) + { + // Lerp slow to fast + float fInterp = 1.0f - ( ( flLocatorLerpTime - ( flLerpTime - flLocatorLerpRest ) ) / flLocatorLerpTime ); + + // Get our desired position + float iconX = pLocatorTarget->m_iconX; + float iconY = pLocatorTarget->m_iconY; + + // Get the distance we need to go to reach it + float diffX = fabsf( pLocatorTarget->m_iconX - pLocatorTarget->m_lastXPos ); + float diffY = fabsf( pLocatorTarget->m_iconY - pLocatorTarget->m_lastYPos ); + + // Go from our current position toward the desired position as quick as the interp allows + pLocatorTarget->m_iconX = static_cast( Approach( iconX, pLocatorTarget->m_lastXPos, diffX * fInterp ) ); + pLocatorTarget->m_iconY = static_cast( Approach( iconY, pLocatorTarget->m_lastYPos, diffY * fInterp ) ); + + // Get how much our position changed and apply it to the center values + int iOffsetX = pLocatorTarget->m_iconX - iconX; + int iOffsetY = pLocatorTarget->m_iconY - iconY; + + pLocatorTarget->m_centerX += iOffsetX; + pLocatorTarget->m_centerY += iOffsetY; + + if ( iOffsetX < 3 && iOffsetY < 3 ) + { + // Near our target! Stop lerping! + flLerpTime = flLocatorLerpRest + flLocatorLerpTime; + } + } + + PaintTarget( pLocatorTarget ); + } + + CollectGarbage(); +} + +//----------------------------------------------------------------------------- +// Purpose: A helper function to save on typing. Make sure our texture ID's +// stay valid. +//----------------------------------------------------------------------------- +void CLocatorPanel::ValidateTexture( int *pTextureID, const char *pszTextureName ) +{ + if( *pTextureID == -1 ) + { + *pTextureID = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( *pTextureID, pszTextureName, true, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame before painting the targets. Ensures that the +// target's textures are properly cached. +//----------------------------------------------------------------------------- +bool CLocatorPanel::ValidateTargetTextures( CLocatorTarget *pTarget ) +{ + bool bBindingTick = false; + + if ( gpGlobals->curtime >= pTarget->m_flNextBindingTick ) + { + if ( pTarget->m_iBindingChoicesCount > 1 ) + { + bBindingTick = true; + pTarget->m_iBindingTick++; + } + + pTarget->m_flNextBindingTick = gpGlobals->curtime + 0.75f; + + pTarget->UpdateVguiTarget(); + } + + bool bUsesBinding = ( Q_stricmp( pTarget->GetOnscreenIconTextureName(), "use_binding" ) == 0 ); + + if( !pTarget->m_pIcon_onscreen || !pTarget->m_pIcon_offscreen || ( bUsesBinding && bBindingTick ) ) + { + char szIconTextureName[ 256 ]; + if ( bUsesBinding ) + { + const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) ); + pTarget->m_bDrawControllerButton = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 ); + + pTarget->DrawBindingName( pchDrawBindingName ); + } + else + { + pTarget->m_bDrawControllerButton = false; + Q_strcpy( szIconTextureName, pTarget->GetOnscreenIconTextureName() ); + pTarget->DrawBindingName( NULL ); + } + + // This target's texture ID is dirty, meaning the target is about to be drawn + // for the first time, or about to be drawn for the first time since a texture + // was changed. + if ( Q_strlen(szIconTextureName) == 0 ) + { + DevWarning("Locator Target has no onscreen texture name!\n"); + return false; + } + else + { + pTarget->m_pIcon_onscreen = HudIcons().GetIcon( szIconTextureName ); + if ( pTarget->m_pIcon_onscreen ) + { + pTarget->m_widthScale_onscreen = static_cast< float >( pTarget->m_pIcon_onscreen->Width() ) / pTarget->m_pIcon_onscreen->Height(); + } + else + { + pTarget->m_widthScale_onscreen = 1.0f; + } + } + + if ( Q_stricmp( pTarget->GetOffscreenIconTextureName() , "use_binding" ) == 0 ) + { + const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) ); + pTarget->m_bDrawControllerButtonOffscreen = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 ); + + pTarget->DrawBindingNameOffscreen( pchDrawBindingName ); + } + else + { + pTarget->m_bDrawControllerButtonOffscreen = false; + Q_strcpy( szIconTextureName, pTarget->GetOffscreenIconTextureName() ); + pTarget->DrawBindingNameOffscreen( NULL ); + } + + if( Q_strlen(szIconTextureName) == 0 ) + { + if( !pTarget->m_pIcon_onscreen ) + { + DevWarning("Locator Target has no offscreen texture name and can't fall back!\n"); + } + else + { + // The onscreen texture is valid, so default behavior is to use that. + pTarget->m_pIcon_offscreen = pTarget->m_pIcon_onscreen; + const char *pchDrawBindingName = pTarget->DrawBindingName(); + pTarget->DrawBindingNameOffscreen( pchDrawBindingName ); + } + } + else + { + pTarget->m_pIcon_offscreen = HudIcons().GetIcon( szIconTextureName ); + } + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Compute where on the screen to draw the icon for this target. +//----------------------------------------------------------------------------- +void CLocatorPanel::ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition ) +{ + int iconX; + int iconY; + + // Measure the delta and the dist from this player to this target. + Vector vecTarget = pTarget->m_vecOrigin; + Vector vecDelta = vecTarget - MainViewOrigin(); + + if ( pTarget->m_bOriginInScreenspace ) + { + // Coordinates are already in screenspace + pTarget->m_distFromPlayer = 0.0f; + + iconX = vecTarget.x * ScreenWidth(); + iconY = vecTarget.y * ScreenHeight(); + pTarget->m_targetX = iconX; + pTarget->m_targetY = iconY; + } + else + { + pTarget->m_distFromPlayer = VectorNormalize( vecDelta ); + + if ( GetVectorInScreenSpace( vecTarget, iconX, iconY ) ) + { + // NOTE: GetVectorInScreenSpace returns false in an edge case where the + // target is very far off screen... just us the old values + pTarget->m_targetX = iconX; + pTarget->m_targetY = iconY; + } + } + + pTarget->m_drawArrowDirection = DRAW_ARROW_NO; + + float fTitleSafeInset = ScreenWidth() * 0.075f; + + if( iconX < fTitleSafeInset || iconX > ScreenWidth() - fTitleSafeInset ) + { + // It's off the screen left or right. + if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Back to lerping + pTarget->StartTimedLerp(); + pTarget->m_pulseStart = gpGlobals->curtime; + } + + if ( bSetPosition ) + { + pTarget->m_bOnscreen = false; + } + + GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY ); + + Vector vCenter = pTarget->m_vecOrigin; + if( MainViewRight().Dot( vCenter - MainViewOrigin() ) > 0 ) + pTarget->m_drawArrowDirection = DRAW_ARROW_RIGHT; + else + pTarget->m_drawArrowDirection = DRAW_ARROW_LEFT; + } + else if( iconY < fTitleSafeInset || iconY > ScreenHeight() - fTitleSafeInset ) + { + // It's off the screen up or down. + if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Back to lerping + pTarget->StartTimedLerp(); + pTarget->m_pulseStart = gpGlobals->curtime; + } + + if ( bSetPosition ) + { + pTarget->m_bOnscreen = false; + } + + GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY ); + + Vector vCenter = pTarget->m_vecOrigin; + if( MainViewUp().Dot( vCenter - MainViewOrigin() ) > 0 ) + pTarget->m_drawArrowDirection = DRAW_ARROW_UP; + else + pTarget->m_drawArrowDirection = DRAW_ARROW_DOWN; + } + else + { + if ( !pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Back to lerping + pTarget->StartTimedLerp(); + pTarget->m_pulseStart = gpGlobals->curtime; + } + + pTarget->m_bOnscreen = true; + } + + if ( bSetPosition ) + { + int tall = ScreenWidth() * ICON_SIZE; + int wide = tall * pTarget->m_widthScale_onscreen; + + // Animate the icon + AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart ); + AnimateIconPosition( pTarget->GetIconEffectsFlags(), &iconX, &iconY ); + AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart ); + + if( pTarget->m_distFromPlayer > ICON_DIST_TOO_FAR && !locator_topdown_style.GetBool() ) + { + // Make the icon smaller + wide = wide >> 1; + tall = tall >> 1; + } + + pTarget->m_centerX = iconX; + pTarget->m_centerY = iconY; + + pTarget->m_iconX = pTarget->m_centerX - ( wide >> 1 ); + pTarget->m_iconY = pTarget->m_centerY - ( tall >> 1 ); + pTarget->m_wide = wide; + pTarget->m_tall = tall; + } +} + +void CLocatorPanel::CalculateOcclusion( CLocatorTarget *pTarget ) +{ + if ( gpGlobals->curtime >= pTarget->m_flNextOcclusionTest ) + { + pTarget->m_flNextOcclusionTest = gpGlobals->curtime + LOCATOR_OCCLUSION_TEST_RATE; + + // Assume the target is not occluded. + pTarget->m_bOccluded = false; + + if ( pTarget->m_bOriginInScreenspace ) + return; + + trace_t tr; + UTIL_TraceLine( pTarget->m_vecOrigin, MainViewOrigin(), (CONTENTS_SOLID|CONTENTS_MOVEABLE), NULL, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction < 1.0f ) + { + pTarget->m_bOccluded = true; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is not valid until after you have computed the onscreen +// icon position for each target! +//----------------------------------------------------------------------------- +bool CLocatorPanel::IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance ) +{ + if( first.m_bOnscreen != second.m_bOnscreen ) + { + // We only declutter onscreen icons against other onscreen icons and vice-versa. + return false; + } + + if( first.IsStatic() || second.IsStatic() ) + { + // Static icons don't count. + return false; + } + + if( abs(first.GetIconY() - second.GetIconY()) < iTolerance ) + { + // OK, we need the Y-check first. Now we have to see if these icons and their captions overlap. + int firstWide = iTolerance + first.m_captionWide; + int secondWide = iTolerance + second.m_captionWide; + + if( abs(first.GetIconX() - second.GetIconX()) < (firstWide + secondWide) / 2 ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw this target on the locator. +// +// IF onscreen and visible, draw no icon, draw no arrows +// IF onscreen and occluded, draw icon transparently, draw no arrows +// IF offscreen, draw icon, draw an arrow indicating the direction to the target +//----------------------------------------------------------------------------- +void CLocatorPanel::PaintTarget( CLocatorTarget *pTarget ) +{ + bool bNewTexture = ValidateTargetTextures( pTarget ); + + if ( bNewTexture ) + { + // Refigure the width/height for the new texture + int tall = ScreenWidth() * ICON_SIZE; + int wide = tall * pTarget->m_widthScale_onscreen; + + AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart ); + + pTarget->m_wide = wide; + pTarget->m_tall = tall; + } + + // A static icon just draws with other static icons in a stack under the crosshair. + // Once displayed, they do not move. The are often used for notifiers. + if( pTarget->IsStatic() ) + { + DrawStaticIcon( pTarget ); + return; + } + + if ( !pTarget->m_bOnscreen && ( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) + { + // Doesn't draw when offscreen... reset it's alpha so it has to fade in again + pTarget->m_fadeStart = gpGlobals->curtime; + pTarget->m_alpha = 0; + } + else + { + // Save these coordinates for later lerping + pTarget->m_lastXPos = pTarget->m_iconX; + pTarget->m_lastYPos = pTarget->m_iconY; + + // Draw when it's on screen or allowed to draw offscreen + DrawDynamicIcon( pTarget, pTarget->HasCaptionText(), pTarget->m_bOnscreen ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the caption-like background with word-bubble style pointer +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer ) +{ + if ( locator_background_style.GetInt() == 0 || pTarget->m_alpha == 0 ) + return; + + /* + int nPosX = pTarget->GetIconX() + locator_background_shift_x.GetInt() - locator_background_thickness_x.GetInt() / 2; + int nPosY = pTarget->GetIconY() + locator_background_shift_y.GetInt() - locator_background_thickness_y.GetInt() / 2; + int nBackgroundWide = nWide + locator_background_thickness_x.GetInt(); + int nBackgroundTall = nTall + locator_background_thickness_y.GetInt(); + + nPointerX = clamp( nPointerX, -0.5f * ScreenWidth(), ScreenWidth() * 1.5f ); + nPointerY = clamp( nPointerY, -0.5f * ScreenHeight(), ScreenHeight() * 1.5f ); + + float fAlpha = static_cast( pTarget->m_alpha ) / 255.0f; + + Color rgbaBackground = locator_background_color.GetColor(); + rgbaBackground[ 3 ] *= fAlpha; + + Color rgbaBorder = locator_background_border_color.GetColor(); + rgbaBorder[ 3 ] *= fAlpha; + */ + + DevMsg("[TODO] vgui::surface()->DrawWordBubble \n"); + + //vgui::surface()->DrawWordBubble( nPosX, nPosY, nPosX + nBackgroundWide, nPosY + nBackgroundTall, locator_background_border_thickness.GetInt(), rgbaBackground, rgbaBorder, bPointer, nPointerX, nPointerY, ScreenWidth() * ICON_SIZE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw an icon with the group of static icons. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawStaticIcon( CLocatorTarget *pTarget ) +{ + int centerX = ScreenWidth() / 2; + int centerY = ScreenHeight() / 2; + centerY += m_staticIconPosition; + + int iconTall = ScreenWidth() * ICON_SIZE; + int iconWide = iconTall * pTarget->m_widthScale_onscreen; + + pTarget->m_centerX = centerX; + pTarget->m_centerY = centerY; + + // Animate the icon + AnimateIconSize( pTarget->GetIconEffectsFlags(), &iconWide, &iconTall, pTarget->m_pulseStart ); + AnimateIconPosition( pTarget->GetIconEffectsFlags(), ¢erX, ¢erY ); + AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart ); + + // Figure out the caption width + pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + bool bDrawMultilineCaption = false; + + if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res + { + if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) ) + { + // we will double-line this + pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat(); + bDrawMultilineCaption = true; + } + } + int totalWide = iconWide + ICON_GAP + pTarget->m_captionWide; + pTarget->m_iconX = centerX - totalWide * 0.5f; + pTarget->m_iconY = centerY - ( iconTall >> 1 ); + + // Lerp by speed on the Y axis + float iconY = pTarget->m_iconY; + + float diffY = fabsf( pTarget->m_iconY - pTarget->m_lastYPos ); + + float flLerpSpeed = gpGlobals->frametime * locator_lerp_speed.GetFloat(); + pTarget->m_iconY = static_cast( Approach( iconY, pTarget->m_lastYPos, MAX( 3.0f, flLerpSpeed * diffY ) ) ); + pTarget->m_centerY += ( pTarget->m_iconY - iconY ); + + pTarget->m_lastXPos = pTarget->m_iconX; + pTarget->m_lastYPos = pTarget->m_iconY; + + pTarget->m_bIsDrawing = true; + + vgui::Panel *pVguiTarget = pTarget->GetVguiTarget(); + + if ( pVguiTarget ) + { + int nPanelX, nPanelY; + nPanelX = 0; + nPanelY = 0; + + vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge(); + + int nWide = pVguiTarget->GetWide(); + int nTall = pVguiTarget->GetTall(); + + /* + const char *pchLookup = pTarget->GetVguiTargetLookup(); + if ( pchLookup[ 0 ] != '\0' ) + { + bool bLookupSuccess = false; + bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nPanelX, nPanelY, nWide, nTall ); + + Assert( bLookupSuccess ); + } + */ + + if ( nVguiTargetEdge == vgui::Label::a_north || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_south ) + { + nPanelX += nWide / 2; + } + else if ( nVguiTargetEdge == vgui::Label::a_northeast || + nVguiTargetEdge == vgui::Label::a_east || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nPanelX += nWide; + } + + if ( nVguiTargetEdge == vgui::Label::a_west || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_east ) + { + nPanelY += nTall / 2; + } + else if ( nVguiTargetEdge == vgui::Label::a_southwest || + nVguiTargetEdge == vgui::Label::a_south || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nPanelY += nTall; + } + + pVguiTarget->LocalToScreen( nPanelX, nPanelY ); + + DrawPointerBackground( pTarget, nPanelX, nPanelY, totalWide, iconTall, true ); + } + else + { + DrawPointerBackground( pTarget, pTarget->m_centerX, pTarget->m_centerY, totalWide, iconTall, false ); + } + + if ( pTarget->m_pIcon_onscreen ) + { + if ( !pTarget->m_bDrawControllerButton ) + { + // Don't draw the icon if we're on 360 and have a binding to draw + pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iconWide, iconTall, Color( 255, 255, 255, pTarget->m_alpha ) ); + } + } + + DrawTargetCaption( pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY(), bDrawMultilineCaption ); + if ( pTarget->DrawBindingName() ) + { + DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iconWide>>1), pTarget->GetIconY() + (iconTall>>1), pTarget->m_bDrawControllerButton ); + } + + // Draw the arrow. + int iArrowSize = ScreenWidth() * ICON_SIZE; // Always square width + DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iArrowSize, iArrowSize, pTarget->m_captionWide + ICON_GAP, pTarget->m_drawArrowDirection ); + + pTarget->m_bOnscreen = true; + + // Move the static icon position so the next static icon drawn this frame below this one. + m_staticIconPosition += iconTall + (iconTall>>2); + // Move down a little more if this one was multi-line + if ( bDrawMultilineCaption ) + { + m_staticIconPosition += (iconTall>>2); + } + return; +} + +//----------------------------------------------------------------------------- +// Purpose: Position and animate this target's icon on the screen. Based on +// options, draw the indicator arrows (arrows that point to the +// direction the player should turn to see the icon), text caption, +// and the 'simple' arrow which just points down to indicate the +// item the icon represents. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow ) +{ + int alpha = pTarget->m_alpha; + + if( pTarget->m_bOccluded && !( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || locator_topdown_style.GetBool() ) ) + { + return; + } + + // Draw the icon! + vgui::surface()->DrawSetColor( 255, 255, 255, alpha ); + + int iWide = pTarget->m_wide; + + if ( !pTarget->m_bOnscreen ) + { + // Width is always square for offscreen icons + iWide /= pTarget->m_widthScale_onscreen; + } + + // Figure out the caption width + pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + bool bDrawMultilineCaption = false; + + if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res + { + if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) ) + { + // we will double-line this + pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat(); + bDrawMultilineCaption = true; + } + } + + int totalWide = iWide; + + bool bShouldDrawCaption = ( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || (!pTarget->m_bOccluded && pTarget->m_distFromPlayer <= ICON_DIST_TOO_FAR) || locator_topdown_style.GetBool() ); + + if( pTarget->m_bOnscreen && bDrawCaption && bShouldDrawCaption ) + { + totalWide += ( ICON_GAP + pTarget->m_captionWide ); + } + + pTarget->m_bIsDrawing = true; + + int nTargetX, nTargetY; + + vgui::Panel *pVguiTarget = pTarget->GetVguiTarget(); + + if ( pVguiTarget ) + { + nTargetX = 0; + nTargetY = 0; + + vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge(); + + int nWide = pVguiTarget->GetWide(); + int nTall = pVguiTarget->GetTall(); + + /* + const char *pchLookup = pTarget->GetVguiTargetLookup(); + + if ( pchLookup[ 0 ] != '\0' ) + { + bool bLookupSuccess = false; + bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nTargetX, nTargetY, nWide, nTall ); + Assert( bLookupSuccess ); + } + */ + + if ( nVguiTargetEdge == vgui::Label::a_north || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_south ) + { + nTargetX += nWide / 2; + } + else if ( nVguiTargetEdge== vgui::Label::a_northeast || + nVguiTargetEdge == vgui::Label::a_east || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nTargetX += nWide; + } + + if ( nVguiTargetEdge == vgui::Label::a_west || + nVguiTargetEdge == vgui::Label::a_center || + nVguiTargetEdge == vgui::Label::a_east ) + { + nTargetY += nTall / 2; + } + else if ( nVguiTargetEdge == vgui::Label::a_southwest || + nVguiTargetEdge == vgui::Label::a_south || + nVguiTargetEdge == vgui::Label::a_southeast ) + { + nTargetY += nTall; + } + + pVguiTarget->LocalToScreen( nTargetX, nTargetY ); + } + else if ( !pTarget->m_bOnscreen ) + { + nTargetX = pTarget->m_targetX; + nTargetY = pTarget->m_targetY; + } + else + { + nTargetX = pTarget->m_centerX; + nTargetY = pTarget->m_centerY; + } + + if ( pTarget->m_bOnscreen ) + { + DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true ); + } + else + { + // Offscreen we need to point the pointer toward out offscreen target + DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true ); + } + + if( pTarget->m_bOnscreen && pTarget->m_pIcon_onscreen ) + { + if ( !pTarget->m_bDrawControllerButton ) + { + pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, Color( 255, 255, 255, alpha ) ); + } + } + else if ( pTarget->m_pIcon_offscreen ) + { + if ( !pTarget->m_bDrawControllerButtonOffscreen ) + { + pTarget->m_pIcon_offscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, Color( 255, 255, 255, alpha ) ); + } + } + + if( !pTarget->m_bOnscreen ) + { + if ( pTarget->DrawBindingNameOffscreen() ) + { + DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButtonOffscreen ); + } + + if ( locator_background_style.GetInt() == 0 ) + { + // Draw the arrow. + DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, 0, pTarget->m_drawArrowDirection ); + } + } + else if( bShouldDrawCaption ) + { + if( bDrawCaption ) + { + //ScreenWidth() * * pTarget->m_widthScale_onscreen + DrawTargetCaption( pTarget, pTarget->GetIconCenterX() + ICON_GAP + pTarget->GetIconWidth() * 0.5, pTarget->GetIconCenterY(), bDrawMultilineCaption ); + } + if ( pTarget->DrawBindingName() ) + { + DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButton ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Some targets have text captions. Draw the text. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline ) +{ + // Draw the caption + vgui::surface()->DrawSetTextFont( m_hCaptionFont ); + int fontTall = vgui::surface()->GetFontTall( m_hCaptionFont ); + int iCaptionWidth = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + if ( bDrawMultiline ) + { + iCaptionWidth *= locator_split_len.GetFloat(); + } + + // Mapbase fixes glow and shadow not working in multiline + bool bDrawGlow = locator_text_glow.GetBool(); + bool bDrawShadow = !IsConsole() && locator_text_drop_shadow.GetBool(); // Only draw drop shadow on PC because it looks crappy on a TV + + if ( !bDrawMultiline ) + { + if ( bDrawGlow ) + { + vgui::surface()->DrawSetTextFont( m_hCaptionGlowFont ); + Color glowColor = locator_text_glow_color.GetColor(); + vgui::surface()->DrawSetTextColor( glowColor.r(), glowColor.g(), glowColor.b(), ( glowColor.a() / 255.0f ) * pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 ); + vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() ); + vgui::surface()->DrawSetTextFont( m_hCaptionFont ); + } + + if ( bDrawShadow ) + { + // Draw black text (drop shadow) + vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x, y - (fontTall >>1) ); + vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() ); + } + + // Draw text + vgui::surface()->DrawSetTextColor( pTarget->m_captionColor.r(),pTarget->m_captionColor.g(),pTarget->m_captionColor.b(), pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 ); + vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() ); + } + else + { + int charX = x-1; + int charY = y - ( fontTall >> 1 ) - 1; + + int iWidth = 0; + + const wchar_t *pString = pTarget->GetCaptionText(); + int len = Q_wcslen( pString ); + + Color glowColor = locator_text_glow_color.GetColor(); + for ( int iChar = 0; iChar < len; ++ iChar ) + { + int charW = vgui::surface()->GetCharacterWidth( m_hCaptionFont, pString[ iChar ] ); + iWidth += charW; + + if ( iWidth > pTarget->m_captionWide && pString[iChar] == L' ' ) + { + charY += fontTall; + charX = x-1; + iWidth = 0; + } + + if ( bDrawGlow ) + { + vgui::surface()->DrawSetTextFont( m_hCaptionGlowFont ); + vgui::surface()->DrawSetTextColor( glowColor.r(), glowColor.g(), glowColor.b(), ( glowColor.a() / 255.0f ) * pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( charX - 1, charY ); + vgui::surface()->DrawUnicodeChar( pString[iChar] ); + vgui::surface()->DrawSetTextFont( m_hCaptionFont ); + } + + if ( bDrawShadow ) + { + // Draw black text (drop shadow) + vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( charX, charY + 1 ); + vgui::surface()->DrawUnicodeChar( pString[iChar] ); + } + + // Draw text + vgui::surface()->DrawSetTextColor( pTarget->m_captionColor.r(),pTarget->m_captionColor.g(),pTarget->m_captionColor.b(), pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( charX, charY ); + vgui::surface()->DrawUnicodeChar( pString[iChar] ); + charX += charW; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Figure out how wide (pixels) a string will be if rendered with this font +// +//----------------------------------------------------------------------------- +int CLocatorPanel::GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont ) +{ + int iWidth = 0; + + for ( int iChar = 0; iChar < Q_wcslen( pString ); ++ iChar ) + { + iWidth += vgui::surface()->GetCharacterWidth( hFont, pString[ iChar ] ); + } + + return iWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: Some targets' captions contain information about key bindings that +// should be displayed to the player. Do so. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController ) +{ + if ( !bController && !IsConsole() ) + { + // Draw the caption + vgui::surface()->DrawSetTextFont( m_hKeysFont ); + int fontTall = vgui::surface()->GetFontTall( m_hKeysFont ); + + char szBinding[ 256 ]; + Q_strcpy( szBinding, pchBindingName ? pchBindingName : "" ); + + if ( Q_strcmp( szBinding, "SEMICOLON" ) == 0 ) + { + Q_strcpy( szBinding, ";" ); + } + else if ( Q_strlen( szBinding ) == 1 && szBinding[ 0 ] >= 'a' && szBinding[ 0 ] <= 'z' ) + { + // Make single letters uppercase + szBinding[ 0 ] += ( 'A' - 'a' ); + } + + wchar_t wszCaption[ 64 ]; + g_pVGuiLocalize->ConstructString( wszCaption, sizeof(wchar_t)*64, szBinding, NULL ); + + int iWidth = GetScreenWidthForCaption( wszCaption, m_hKeysFont ); + +#ifdef MAPBASE + // Mods are capable of choosing a custom color + vgui::surface()->DrawSetTextColor( pTarget->m_bindingColor.r(), pTarget->m_bindingColor.g(), pTarget->m_bindingColor.b(), pTarget->m_alpha ); +#else + // Draw black text + vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha ); +#endif + vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - 1, y - (fontTall >>1) - 1 ); + vgui::surface()->DrawUnicodeString( wszCaption ); + } + else + { + // Draw the caption + wchar_t wszCaption[ 64 ]; + + vgui::surface()->DrawSetTextFont( BUTTON_FONT_HANDLE ); + int fontTall = vgui::surface()->GetFontTall( BUTTON_FONT_HANDLE ); + + char szBinding[ 256 ]; + + // Turn localized string into icon character + Q_snprintf( szBinding, sizeof( szBinding ), "#GameUI_Icons_%s", pchBindingName ); + g_pVGuiLocalize->ConstructString( wszCaption, sizeof( wszCaption ), g_pVGuiLocalize->Find( szBinding ), 0 ); + g_pVGuiLocalize->ConvertUnicodeToANSI( wszCaption, szBinding, sizeof( szBinding ) ); + + int iWidth = GetScreenWidthForCaption( wszCaption, BUTTON_FONT_HANDLE ); + + int iLargeIconShift = MAX( 0, iWidth - ( ScreenWidth() * ICON_SIZE + ICON_GAP + ICON_GAP ) ); + + // Draw the button + vgui::surface()->DrawSetTextColor( 255,255,255, pTarget->m_alpha ); + vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - iLargeIconShift, y - (fontTall >>1) ); + vgui::surface()->DrawUnicodeString( wszCaption ); + + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw an arrow to indicate that a target is offscreen +// +// iconWide is sent to this function so that the arrow knows how to straddle +// the icon that it is being drawn near. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, int direction ) +{ + int wide = iconWide; + int tall = iconTall; + + //tall = wide = ScreenWidth() * ICON_SIZE; + + switch( direction ) + { + case DRAW_ARROW_LEFT: + vgui::surface()->DrawSetTexture( m_textureID_ArrowLeft ); + x -= wide; + y += iconTall / 2 - tall / 2; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + case DRAW_ARROW_RIGHT: + vgui::surface()->DrawSetTexture( m_textureID_ArrowRight ); + x += iconWide + textWidth; + y += iconTall / 2 - tall / 2; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + case DRAW_ARROW_UP: + vgui::surface()->DrawSetTexture( m_textureID_ArrowUp ); + x += iconWide / 2 - wide / 2; + y -= tall; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + case DRAW_ARROW_DOWN: + vgui::surface()->DrawSetTexture( m_textureID_ArrowDown ); + x += iconWide / 2 - wide / 2; + y += iconTall; + vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall ); + break; + + default: + // Do not draw. + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draws a very simple arrow that points down. +//----------------------------------------------------------------------------- +void CLocatorPanel::DrawSimpleArrow( int x, int y, int iconWide, int iconTall ) +{ + vgui::surface()->DrawSetTexture( m_textureID_SimpleArrow ); + + y += iconTall; + + vgui::surface()->DrawTexturedRect( x, y, x + iconWide, y + iconTall ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLocatorPanel::GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos ) +{ + float xpos, ypos; + float flRotation; + float flRadius = YRES(OFFSCREEN_ICON_POSITION_RADIUS); + + if ( locator_topdown_style.GetBool() ) + { + flRadius *= clamp( flDist / 600.0f, 1.75f, 3.0f ); + } + + GetTargetPosition( vecDelta, flRadius, &xpos, &ypos, &flRotation ); + + *pXPos = xpos; + *pYPos = ypos; +} + +//----------------------------------------------------------------------------- +// Purpose: Given a handle, return the pointer to the proper locator target. +//----------------------------------------------------------------------------- +CLocatorTarget *CLocatorPanel::GetPointerForHandle( int hTarget ) +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + if( m_targets[ i ].m_isActive && m_targets[ i ].m_serialNumber == hTarget ) + { + return &m_targets[ i ]; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CLocatorPanel::AddTarget() +{ + for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ ) + { + if( !m_targets[ i ].m_isActive ) + { + m_targets[ i ].Activate( m_serializer ); + m_serializer++; + + return m_targets[ i ].m_serialNumber; + } + } + + DevWarning( "Locator Panel has no free targets!\n" ); + return -1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLocatorPanel::RemoveTarget( int hTarget ) +{ + CLocatorTarget *pTarget = GetPointerForHandle( hTarget ); + + if( pTarget ) + { + pTarget->Deactivate(); + } +} + diff --git a/sp/src/game/client/hud_locator_target.h b/sp/src/game/client/hud_locator_target.h new file mode 100644 index 0000000000..06799325b5 --- /dev/null +++ b/sp/src/game/client/hud_locator_target.h @@ -0,0 +1,184 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: Add entities to this system, and the Locator will maintain an arrow +// on the HUD that points to the entities when they are offscreen. +// +//============================================================================= + +#ifndef L4D_HUD_LOCATOR_H +#define L4D_HUD_LOCATOR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/PHandle.h" + + +#define MAX_LOCATOR_BINDINGS_SHOWN 8 +#define MAX_LOCATOR_TARGETS 10 +#define LOCATOR_FLAGS_NONE 0x00000000 + +#define LOCATOR_ICON_FX_NONE 0x00000000 +#define LOCATOR_ICON_FX_PULSE_SLOW 0x00000001 +#define LOCATOR_ICON_FX_PULSE_FAST 0x00000002 +#define LOCATOR_ICON_FX_PULSE_URGENT 0x00000004 +#define LOCATOR_ICON_FX_ALPHA_SLOW 0x00000008 +#define LOCATOR_ICON_FX_ALPHA_FAST 0x00000010 +#define LOCATOR_ICON_FX_ALPHA_URGENT 0x00000020 +#define LOCATOR_ICON_FX_SHAKE_NARROW 0x00000040 +#define LOCATOR_ICON_FX_SHAKE_WIDE 0x00000080 +#define LOCATOR_ICON_FX_STATIC 0x00000100 // This icon draws at a fixed location on the HUD. +#define LOCATOR_ICON_FX_NO_OFFSCREEN 0x00000200 +#define LOCATOR_ICON_FX_FORCE_CAPTION 0x00000400 // Always draw the caption, even when the icon is occluded. +#define LOCATOR_ICON_FX_FADE_OUT 0x00000800 // Set when deactivated so it can smoothly vanish +#define LOCATOR_ICON_FX_FADE_IN 0x00001000 // Set when activated so it can smoothly appear + +#include "tier1/UtlSymbol.h" + +// See comments in UtlSymbol on why this is useful +DECLARE_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol ); + +//----------------------------------------------------------------------------- +// This class represents a single target to be tracked by the locator +//----------------------------------------------------------------------------- +class CLocatorTarget +{ +public: + bool m_bOriginInScreenspace; + Vector m_vecOrigin; // The location in the world to draw on the locator + + // ONLY the locator panel should fiddle with these fields. + bool m_isActive; + int m_serialNumber; + int m_frameLastUpdated; + bool m_bOnscreen; + bool m_bOccluded; + bool m_bVisible; + bool m_bIsDrawing; + float m_distFromPlayer; + CHudTexture *m_pIcon_onscreen; + CHudTexture *m_pIcon_offscreen; + int m_iBindingTick; + float m_flNextBindingTick; + float m_flNextOcclusionTest; + int m_iBindingChoicesCount; + const char *(m_pchBindingChoices[ MAX_LOCATOR_BINDINGS_SHOWN ]); + int m_iBindChoicesOriginalToken[ MAX_LOCATOR_BINDINGS_SHOWN ]; + + // Fields for drawing + int m_targetX; // screen X position of the actual target + int m_targetY; // screen Y position of the actual target + int m_iconX; // screen X position (top) + int m_iconY; // screen Y position (left) + int m_centerX; // screen X position (center) + int m_centerY; // screen Y position (center) + int m_wide; // draw width of icon (may be different from frame to frame as the icon's size animates, for instance) + int m_tall; // draw height of icon '' '' + float m_widthScale_onscreen; // for icons that are wider than standard + int m_alpha; // + float m_fadeStart; // time stamp when fade out started + float m_lerpStart; // time stamp when lerping started + float m_pulseStart; // time stamp when pulsing started + int m_declutterIndex; // sort order from the declutterer + int m_lastDeclutterIndex; // last sort order from the declutterer + int m_drawArrowDirection; // Whether to draw an arrow indicating this target is off-screen, also tells us which arrow to draw (left, up, etc.) + int m_captionWide; // How wide (pixels) my caption is. + bool m_bDrawControllerButton; + bool m_bDrawControllerButtonOffscreen; + int m_offsetX; // User-specified X offset which is applied in screenspace + int m_offsetY; // User-specified Y offset which is applied in screenspace + + // Fields for interpolating icon position + float m_flTimeLerpDone; // How much time left before this icon arrives where it is supposed to be. + int m_lastXPos; // screen X position last frame + int m_lastYPos; // '' Y + + CLocatorTarget( void ); + void Activate( int serialNumber ); + void Deactivate( bool bNoFade = false ); + void Update(); + + int GetIconX( void ); + int GetIconY( void ); + int GetIconCenterX( void ); + int GetIconCenterY( void ); + int GetIconWidth( void ); + int GetIconHeight( void ); + + void AddIconEffects( int add ) { m_iEffectsFlags |= add; } + void RemoveIconEffects( int remove ) { m_iEffectsFlags &= ~remove; } + int GetIconEffectsFlags() { return m_iEffectsFlags; } + void SetCaptionColor( Color col ) { m_captionColor = col; } + void SetCaptionColor( const char *pszCaptionColor ); + bool IsStatic(); + bool IsPresenting(); + void StartTimedLerp(); + void StartPresent(); + void EndPresent(); + + void UpdateVguiTarget( void ); + vgui::Panel *GetVguiTarget( void ); + void SetVguiTargetName( const char *pchVguiTargetName ); + const char *GetVguiTargetName( void ) { return m_szVguiTargetName.String(); } + void SetVguiTargetLookup( const char *pchVguiTargetLookup ); + const char *GetVguiTargetLookup( void ) { return m_szVguiTargetLookup.String(); } + void SetVguiTargetEdge( int nVguiEdge ); + int GetVguiTargetEdge( void ) const { return m_nVguiTargetEdge; } + + void SetOnscreenIconTextureName( const char *pszTexture ); + void SetOffscreenIconTextureName( const char *pszTexture ); + void SetBinding( const char *pszBinding ); + const char *UseBindingImage( char *pchIconTextureName, size_t bufSize ); + + const char *GetOnscreenIconTextureName() { return m_szOnscreenTexture.String(); } + const char *GetOffscreenIconTextureName() { return m_szOffscreenTexture.String(); } + const char *GetBinding() { return m_szBinding.String(); } + + void SetVisible( bool bVisible ); + bool IsVisible( void ); + + void SetCaptionText( const char *pszText, const char *pszParam ); + const wchar_t *GetCaptionText( void ) { return (const wchar_t *)m_wszCaption.Base(); } + bool HasCaptionText( void ) { return m_wszCaption.Count() > 1; } + + void DrawBindingName( const char *pchDrawName ) { m_pchDrawBindingName = pchDrawName; } + void DrawBindingNameOffscreen( const char *pchDrawName ) { m_pchDrawBindingNameOffscreen = pchDrawName; } + + const char *DrawBindingName( void ) { return m_pchDrawBindingName; } + const char *DrawBindingNameOffscreen( void ) { return m_pchDrawBindingNameOffscreen; } + + bool IsOnScreen() { return m_bOnscreen; } + bool IsOccluded() { return m_bOccluded; } + + +private: + CGameInstructorSymbol m_szVguiTargetName; + CGameInstructorSymbol m_szVguiTargetLookup; + vgui::DHANDLE m_hVguiTarget; + int m_nVguiTargetEdge; + + CGameInstructorSymbol m_szOnscreenTexture; + CGameInstructorSymbol m_szOffscreenTexture; + CGameInstructorSymbol m_szBinding; + + bool m_bWasControllerLast; + const char *m_pchDrawBindingName; + const char *m_pchDrawBindingNameOffscreen; + int m_iEffectsFlags; + CUtlVector< wchar_t > m_wszCaption; + +public: + Color m_captionColor; +#ifdef MAPBASE + Color m_bindingColor; +#endif +}; + +extern int Locator_AddTarget(); +extern void Locator_RemoveTarget( int hTarget ); +CLocatorTarget *Locator_GetTargetFromHandle( int hTarget ); +void Locator_ComputeTargetIconPositionFromHandle( int hTarget ); + + +#endif // L4D_HUD_LOCATOR_H \ No newline at end of file diff --git a/sp/src/game/client/mapbase/c_func_clientclip.cpp b/sp/src/game/client/mapbase/c_func_clientclip.cpp index 03cc50d43b..f6595949c1 100644 --- a/sp/src/game/client/mapbase/c_func_clientclip.cpp +++ b/sp/src/game/client/mapbase/c_func_clientclip.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp b/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp new file mode 100644 index 0000000000..9d92675e1d --- /dev/null +++ b/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp @@ -0,0 +1,226 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// (basically a combination of point_camera and func_reflective_glass) +// +// $NoKeywords: $ +//===========================================================================// +#include "cbase.h" +#include "view_shared.h" +#include "viewrender.h" +#include "c_func_fake_worldportal.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT( C_FuncFakeWorldPortal, DT_FuncFakeWorldPortal, CFuncFakeWorldPortal ) + + RecvPropEHandle( RECVINFO( m_hTargetPlane ) ), + RecvPropVector( RECVINFO( m_PlaneAngles ) ), + RecvPropInt( RECVINFO( m_iSkyMode ) ), + RecvPropFloat( RECVINFO( m_flScale ) ), + RecvPropString( RECVINFO( m_iszRenderTarget ) ), + RecvPropEHandle( RECVINFO( m_hFogController ) ), + +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +C_EntityClassList g_FakeWorldPortalList; +template<> C_FuncFakeWorldPortal *C_EntityClassList::m_pClassList = NULL; + +C_FuncFakeWorldPortal* GetFakeWorldPortalList() +{ + return g_FakeWorldPortalList.m_pClassList; +} + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal::C_FuncFakeWorldPortal() +{ + m_iszRenderTarget[0] = '\0'; + + g_FakeWorldPortalList.Insert( this ); +} + +C_FuncFakeWorldPortal::~C_FuncFakeWorldPortal() +{ + g_FakeWorldPortalList.Remove( this ); +} + + +bool C_FuncFakeWorldPortal::ShouldDraw() +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Do we have a fake world portal in view? +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal *IsFakeWorldPortalInView( const CViewSetup& view, cplane_t &plane ) +{ + // Early out if no cameras + C_FuncFakeWorldPortal *pReflectiveGlass = GetFakeWorldPortalList(); + if ( !pReflectiveGlass ) + return NULL; + + Frustum_t frustum; + GeneratePerspectiveFrustum( view.origin, view.angles, view.zNear, view.zFar, view.fov, view.m_flAspectRatio, frustum ); + + cplane_t localPlane; + Vector vecOrigin, vecWorld, vecDelta, vecForward; + AngleVectors( view.angles, &vecForward, NULL, NULL ); + + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) + { + if ( pReflectiveGlass->IsDormant() ) + continue; + + if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) + continue; + + Vector vecMins, vecMaxs; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + if ( R_CullBox( vecMins, vecMaxs, frustum ) ) + continue; + + const model_t *pModel = pReflectiveGlass->GetModel(); + const matrix3x4_t& mat = pReflectiveGlass->EntityToWorldTransform(); + + int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); + for ( int i = 0; i < nCount; ++i ) + { + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + + MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space + VectorTransform( vecOrigin, mat, vecWorld ); + + if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + continue; + + VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull + if ( vecDelta.Dot( plane.normal ) >= 0 ) + continue; + + // Must have valid plane + if ( !pReflectiveGlass->m_hTargetPlane ) + continue; + + return pReflectiveGlass; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Iterates through fake world portals instead of just picking one +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum ) +{ + // Early out if no cameras + C_FuncFakeWorldPortal *pReflectiveGlass = NULL; + if (!pStart) + pReflectiveGlass = GetFakeWorldPortalList(); + else + pReflectiveGlass = pStart->m_pNext; + + cplane_t localPlane; + Vector vecOrigin, vecWorld, vecDelta; + for ( ; pReflectiveGlass != NULL; pReflectiveGlass = pReflectiveGlass->m_pNext ) + { + if ( pReflectiveGlass->IsDormant() ) + continue; + + if ( pReflectiveGlass->m_iViewHideFlags & (1 << CurrentViewID()) ) + continue; + + Vector vecMins, vecMaxs; + pReflectiveGlass->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + if ( R_CullBox( vecMins, vecMaxs, frustum ) ) + continue; + + const model_t *pModel = pReflectiveGlass->GetModel(); + const matrix3x4_t& mat = pReflectiveGlass->EntityToWorldTransform(); + + int nCount = modelinfo->GetBrushModelPlaneCount( pModel ); + for ( int i = 0; i < nCount; ++i ) + { + modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecOrigin ); + + MatrixTransformPlane( mat, localPlane, plane ); // Transform to world space + VectorTransform( vecOrigin, mat, vecWorld ); + + if ( view.origin.Dot( plane.normal ) <= plane.dist ) // Check for view behind plane + continue; + + VectorSubtract( vecWorld, view.origin, vecDelta ); // Backface cull + if ( vecDelta.Dot( plane.normal ) >= 0 ) + continue; + + // Must have valid plane + if ( !pReflectiveGlass->m_hTargetPlane ) + continue; + + return pReflectiveGlass; + } + } + + return NULL; +} + +void C_FuncFakeWorldPortal::OnDataChanged( DataUpdateType_t type ) +{ + // Reset render texture + m_pRenderTarget = NULL; + + // Reset fog + m_pFog = NULL; + + return BaseClass::OnDataChanged( type ); +} + +extern ITexture *GetWaterReflectionTexture( void ); + +ITexture *C_FuncFakeWorldPortal::RenderTarget() +{ + if (m_iszRenderTarget[0] != '\0') + { + if (!m_pRenderTarget) + { + // We don't use a CTextureReference for this because we don't want to shut down the texture on removal/change + m_pRenderTarget = materials->FindTexture( m_iszRenderTarget, TEXTURE_GROUP_RENDER_TARGET ); + } + + if (m_pRenderTarget) + return m_pRenderTarget; + } + + return GetWaterReflectionTexture(); +} + +fogparams_t *C_FuncFakeWorldPortal::GetFog() +{ + if (m_pFog) + return m_pFog; + + if (m_hFogController) + { + C_FogController *pFogController = dynamic_cast(m_hFogController.Get()); + if (pFogController) + { + m_pFog = &pFogController->m_fog; + } + else + { + Warning("%s is not an env_fog_controller\n", m_hFogController->GetEntityName()); + } + } + + return NULL; +} diff --git a/sp/src/game/client/mapbase/c_func_fake_worldportal.h b/sp/src/game/client/mapbase/c_func_fake_worldportal.h new file mode 100644 index 0000000000..3fc4418b61 --- /dev/null +++ b/sp/src/game/client/mapbase/c_func_fake_worldportal.h @@ -0,0 +1,64 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// (basically a combination of point_camera and func_reflective_glass) +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef C_FUNC_FAKE_WORLDPORTAL +#define C_FUNC_FAKE_WORLDPORTAL + +#ifdef _WIN32 +#pragma once +#endif + +struct cplane_t; +class CViewSetup; + +class C_FuncFakeWorldPortal : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_FuncFakeWorldPortal, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_FuncFakeWorldPortal(); + virtual ~C_FuncFakeWorldPortal(); + + virtual bool ShouldDraw(); + virtual void OnDataChanged( DataUpdateType_t type ); + + SkyboxVisibility_t SkyMode() { return m_iSkyMode; } + + ITexture *RenderTarget(); + + fogparams_t *GetFog(); + +public: + + EHANDLE m_hTargetPlane; + QAngle m_PlaneAngles; + SkyboxVisibility_t m_iSkyMode; + float m_flScale; + + EHANDLE m_hFogController; + fogparams_t *m_pFog; + + char m_iszRenderTarget[64]; + ITexture *m_pRenderTarget; + + C_FuncFakeWorldPortal *m_pNext; +}; + +//----------------------------------------------------------------------------- +// Do we have reflective glass in view? If so, what's the reflection plane? +//----------------------------------------------------------------------------- +C_FuncFakeWorldPortal *IsFakeWorldPortalInView( const CViewSetup& view, cplane_t &plane ); + +C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, cplane_t &plane, + const Frustum_t &frustum ); + + +#endif // C_FUNC_FAKE_WORLDPORTAL + + diff --git a/sp/src/game/client/mapbase/c_point_glow.cpp b/sp/src/game/client/mapbase/c_point_glow.cpp new file mode 100644 index 0000000000..590e0f3e8c --- /dev/null +++ b/sp/src/game/client/mapbase/c_point_glow.cpp @@ -0,0 +1,103 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase off-shoot of tf_glow (created using SDK code only) +// +//===========================================================================// + +#include "cbase.h" +#include "glow_outline_effect.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +class C_PointGlow : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_PointGlow, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + ~C_PointGlow(); + + enum + { + GLOW_VIS_ALWAYS, + GLOW_VIS_NOT_WHEN_VISIBLE, + GLOW_VIS_ONLY_WHEN_VISIBLE, + }; + + void OnDataChanged( DataUpdateType_t type ); + + CGlowObject *GetGlowObject( void ){ return m_pGlowEffect; } + void UpdateGlowEffect( void ); + void DestroyGlowEffect( void ); + + EHANDLE m_hGlowTarget; + color32 m_GlowColor; + + bool m_bGlowDisabled; + CGlowObject *m_pGlowEffect; +}; + +IMPLEMENT_CLIENTCLASS_DT( C_PointGlow, DT_PointGlow, CPointGlow ) + RecvPropEHandle( RECVINFO( m_hGlowTarget ) ), + RecvPropInt( RECVINFO( m_GlowColor ), 0, RecvProxy_IntToColor32 ), + RecvPropBool( RECVINFO( m_bGlowDisabled ) ), +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_PointGlow::~C_PointGlow() +{ + DestroyGlowEffect(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointGlow::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + UpdateGlowEffect(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointGlow::UpdateGlowEffect( void ) +{ + // destroy the existing effect + if ( m_pGlowEffect ) + { + DestroyGlowEffect(); + } + + // create a new effect + if ( !m_bGlowDisabled ) + { + Vector4D vecColor( m_GlowColor.r, m_GlowColor.g, m_GlowColor.b, m_GlowColor.a ); + for (int i = 0; i < 4; i++) + { + if (vecColor[i] == 0.0f) + continue; + + vecColor[i] /= 255.0f; + } + + m_pGlowEffect = new CGlowObject( m_hGlowTarget, vecColor.AsVector3D(), vecColor.w, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointGlow::DestroyGlowEffect( void ) +{ + if ( m_pGlowEffect ) + { + delete m_pGlowEffect; + m_pGlowEffect = NULL; + } +} diff --git a/sp/src/game/client/view.cpp b/sp/src/game/client/view.cpp index a7be86f351..212947357c 100644 --- a/sp/src/game/client/view.cpp +++ b/sp/src/game/client/view.cpp @@ -126,6 +126,9 @@ ConVar gl_clear( "gl_clear", "0"); ConVar gl_clear_randomcolor( "gl_clear_randomcolor", "0", FCVAR_CHEAT, "Clear the back buffer to random colors every frame. Helps spot open seams in geometry." ); static ConVar r_farz( "r_farz", "-1", FCVAR_CHEAT, "Override the far clipping plane. -1 means to use the value in env_fog_controller." ); +#ifdef MAPBASE +static ConVar r_nearz( "r_nearz", "-1", FCVAR_CHEAT, "Override the near clipping plane. -1 means to use the default value (usually 7)." ); +#endif static ConVar cl_demoviewoverride( "cl_demoviewoverride", "0", 0, "Override view during demo playback" ); @@ -602,6 +605,11 @@ static QAngle s_DbgSetupAngles; //----------------------------------------------------------------------------- float CViewRender::GetZNear() { +#ifdef MAPBASE + if (r_nearz.GetFloat() > 0) + return r_nearz.GetFloat(); +#endif + return VIEW_NEARZ; } diff --git a/sp/src/game/client/viewrender.cpp b/sp/src/game/client/viewrender.cpp index bba94f78e4..045da85919 100644 --- a/sp/src/game/client/viewrender.cpp +++ b/sp/src/game/client/viewrender.cpp @@ -74,6 +74,10 @@ #include "c_point_camera.h" #endif // USE_MONITORS +#ifdef MAPBASE +#include "mapbase/c_func_fake_worldportal.h" +#endif + // Projective textures #include "C_Env_Projected_Texture.h" @@ -119,6 +123,10 @@ static ConVar r_drawtranslucentrenderables( "r_drawtranslucentrenderables", "1", static ConVar r_drawopaquerenderables( "r_drawopaquerenderables", "1", FCVAR_CHEAT ); static ConVar r_threaded_renderables( "r_threaded_renderables", "0" ); +#ifdef MAPBASE +static ConVar r_skybox_use_complex_views( "r_skybox_use_complex_views", "0", FCVAR_CHEAT, "Enable complex views in skyboxes, like reflective glass" ); +#endif + // FIXME: This is not static because we needed to turn it off for TF2 playtests ConVar r_DrawDetailProps( "r_DrawDetailProps", "1", FCVAR_NONE, "0=Off, 1=Normal, 2=Wireframe" ); @@ -172,6 +180,10 @@ extern ConVar cl_leveloverview; extern ConVar localplayer_visionflags; +#ifdef MAPBASE +static ConVar r_nearz_skybox( "r_nearz_skybox", "2.0", FCVAR_CHEAT ); +#endif + //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- @@ -484,6 +496,11 @@ class CBaseWorldView : public CRendering3dView void SSAO_DepthPass(); void DrawDepthOfField(); + +#ifdef MAPBASE + virtual ITexture *GetRefractionTexture() { return GetWaterRefractionTexture(); } + virtual ITexture *GetReflectionTexture() { return GetWaterReflectionTexture(); } +#endif }; @@ -670,6 +687,11 @@ class CReflectiveGlassView : public CSimpleWorldView void Draw(); cplane_t m_ReflectionPlane; + +#ifdef MAPBASE + ITexture *GetReflectionTexture() { return m_pRenderTarget; } + ITexture *m_pRenderTarget; +#endif }; class CRefractiveGlassView : public CSimpleWorldView @@ -687,6 +709,11 @@ class CRefractiveGlassView : public CSimpleWorldView void Draw(); cplane_t m_ReflectionPlane; + +#ifdef MAPBASE + ITexture *GetRefractionTexture() { return m_pRenderTarget; } + ITexture *m_pRenderTarget; +#endif }; @@ -1969,6 +1996,27 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT { CViewSetup viewMiddle = GetView( STEREO_EYE_MONO ); DrawMonitors( viewMiddle ); + +#ifdef MAPBASE + // Any fake world portals? + Frustum_t frustum; + GeneratePerspectiveFrustum( view.origin, view.angles, view.zNear, view.zFar, view.fov, view.m_flAspectRatio, frustum ); + + cplane_t portalPlane; + //C_FuncFakeWorldPortal *pPortalEnt = IsFakeWorldPortalInView( view, portalPlane ); + //if ( pPortalEnt ) + C_FuncFakeWorldPortal *pPortalEnt = NextFakeWorldPortal( NULL, view, portalPlane, frustum ); + while ( pPortalEnt != NULL ) + { + ITexture *pCameraTarget = pPortalEnt->RenderTarget(); + int width = pCameraTarget->GetActualWidth(); + int height = pCameraTarget->GetActualHeight(); + + DrawFakeWorldPortal( pCameraTarget, pPortalEnt, viewMiddle, C_BasePlayer::GetLocalPlayer(), 0, 0, width, height, view, portalPlane ); + + pPortalEnt = NextFakeWorldPortal( pPortalEnt, view, portalPlane, frustum ); + } +#endif } #endif @@ -2593,6 +2641,34 @@ void CViewRender::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &view { tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "bCheapWater" ); cplane_t glassReflectionPlane; +#ifdef MAPBASE + // New expansions allow for custom render targets and multiple mirror renders + Frustum_t frustum; + GeneratePerspectiveFrustum( viewIn.origin, viewIn.angles, viewIn.zNear, viewIn.zFar, viewIn.fov, viewIn.m_flAspectRatio, frustum ); + + ITexture *pTextureTargets[2]; + C_BaseEntity *pReflectiveGlass = NextReflectiveGlass( NULL, viewIn, glassReflectionPlane, frustum, pTextureTargets ); + while ( pReflectiveGlass != NULL ) + { + if (pTextureTargets[0]) + { + CRefPtr pGlassReflectionView = new CReflectiveGlassView( this ); + pGlassReflectionView->m_pRenderTarget = pTextureTargets[0]; + pGlassReflectionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); + AddViewToScene( pGlassReflectionView ); + } + + if (pTextureTargets[1]) + { + CRefPtr pGlassRefractionView = new CRefractiveGlassView( this ); + pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); + pGlassRefractionView->m_pRenderTarget = pTextureTargets[1]; + AddViewToScene( pGlassRefractionView ); + } + + pReflectiveGlass = NextReflectiveGlass( pReflectiveGlass, viewIn, glassReflectionPlane, frustum, pTextureTargets ); + } +#else if ( IsReflectiveGlassInView( viewIn, glassReflectionPlane ) ) { CRefPtr pGlassReflectionView = new CReflectiveGlassView( this ); @@ -2603,6 +2679,7 @@ void CViewRender::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &view pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); AddViewToScene( pGlassRefractionView ); } +#endif CRefPtr pNoWaterView = new CSimpleWorldView( this ); pNoWaterView->Setup( viewIn, nClearFlags, bDrawSkybox, fogVolumeInfo, info, pCustomVisibility ); @@ -3321,6 +3398,199 @@ bool CViewRender::DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_Poin return true; } +#ifdef MAPBASE +ConVar r_fakeworldportal_debug("r_fakeworldportal_debug", "0"); + +//----------------------------------------------------------------------------- +// Purpose: Sets up scene and renders WIP fake world portal view. +// Based on code from monitors, mirrors, and 3D skyboxes. +// It's also terrible right now. +// +// Input : cameraNum - +// &cameraView +// *localPlayer - +// x - +// y - +// width - +// height - +// highend - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldPortal *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, + int x, int y, int width, int height, + const CViewSetup &mainView, cplane_t &ourPlane ) +{ +#ifdef USE_MONITORS + VPROF_INCREMENT_COUNTER( "cameras rendered", 1 ); + // Setup fog state for the camera. + fogparams_t oldFogParams; + float flOldZFar = 0.0f; + + // If fog should be disabled instead of using the player's controller, a blank fog controller can just be used + bool fogEnabled = true; //pCameraEnt->IsFogEnabled(); + + CViewSetup monitorView = cameraView; + + fogparams_t *pFogParams = NULL; + + if ( fogEnabled ) + { + if ( !localPlayer ) + return false; + + pFogParams = localPlayer->GetFogParams(); + + // Save old fog data. + oldFogParams = *pFogParams; + + if ( pCameraEnt->GetFog() ) + { + *pFogParams = *pCameraEnt->GetFog(); + } + } + + monitorView.width = width; + monitorView.height = height; + monitorView.x = x; + monitorView.y = y; + + monitorView.origin = mainView.origin; + monitorView.angles = mainView.angles; + + // Temporary debug stuff + static float flLastDebugTime = 0.0f; + bool bDebug = r_fakeworldportal_debug.GetBool() && gpGlobals->curtime > flLastDebugTime; + + QAngle angTargetAngles = pCameraEnt->m_hTargetPlane->GetAbsAngles() + pCameraEnt->m_PlaneAngles; + + // RED - First origin + if (bDebug) + debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 0, 0, 128, 10.0f ); + + // Make sure the origin and angles are relative to the target plane + monitorView.origin -= pCameraEnt->GetAbsOrigin(); + + // scale origin by sky scale + if ( pCameraEnt->m_flScale > 0 ) + { + float scale = 1.0f / pCameraEnt->m_flScale; + VectorScale( monitorView.origin, scale, monitorView.origin ); + } + + // YELLOW - Main origin + if (bDebug) + debugoverlay->AddBoxOverlay( pCameraEnt->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 224, 0, 128, 10.0f ); + + // Make sure our angles are relative to the main plane, just like the origin + QAngle angOurAngles; + VectorAngles( ourPlane.normal * -1, angOurAngles ); + //angles -= angOurAngles; + + // First, create a matrix for the sky's angles. + matrix3x4_t matSkyAngles; + AngleMatrix( angTargetAngles - angOurAngles, matSkyAngles ); + + Vector vecSkyForward, vecSkyRight, vecSkyUp; + + // Get vectors from our original angles. + Vector vPlayerForward, vPlayerRight, vPlayerUp; + AngleVectors( monitorView.angles, &vPlayerForward, &vPlayerRight, &vPlayerUp ); + + VectorTransform( vPlayerForward, matSkyAngles, vecSkyForward ); + VectorTransform( vPlayerRight, matSkyAngles, vecSkyRight ); + VectorTransform( vPlayerUp, matSkyAngles, vecSkyUp ); + + // Normalize them. + VectorNormalize( vecSkyForward ); + VectorNormalize( vecSkyRight ); + VectorNormalize( vecSkyUp ); + + Quaternion quat; + BasisToQuaternion( vecSkyForward, vecSkyRight, vecSkyUp, quat ); + QuaternionAngles( quat, monitorView.angles ); + + // End of code mostly lifted from projected texture screenspace stuff + // ---------------------------------------------------------------------- + + // Now just rotate our origin with that matrix. + // We create a copy of the origin since VectorRotate doesn't want in1 to be the same variable as the destination. + VectorRotate(Vector(monitorView.origin), matSkyAngles, monitorView.origin); + + // BLUE - Target origin + if (bDebug) + debugoverlay->AddBoxOverlay( pCameraEnt->m_hTargetPlane->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 0, 0, 255, 128, 10.0f ); + + monitorView.origin += pCameraEnt->m_hTargetPlane->GetAbsOrigin(); + + // GREEN - Final origin + if (bDebug) + { + debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 0, 255, 0, 128, 10.0f ); + + flLastDebugTime = gpGlobals->curtime + 5.0f; + } + + + monitorView.fov = mainView.fov; + monitorView.m_bOrtho = false; + monitorView.m_flAspectRatio = 0.0f; + monitorView.m_bViewToProjectionOverride = false; + + // @MULTICORE (toml 8/11/2006): this should be a renderer.... + int nClearFlags = (VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_OBEY_STENCIL); + bool bDrew3dSkybox = false; + SkyboxVisibility_t nSkyMode = pCameraEnt->SkyMode(); + + Frustum frustum; + render->Push3DView( monitorView, nClearFlags, pRenderTarget, (VPlane *)frustum ); + + // + // Monitor sky handling + // + if ( pCameraEnt->SkyMode() == SKYBOX_3DSKYBOX_VISIBLE ) + { + // if the 3d skybox world is drawn, then don't draw the normal skybox + CSkyboxView *pSkyView = new CSkyboxView( this ); + if ( ( bDrew3dSkybox = pSkyView->Setup( monitorView, &nClearFlags, &nSkyMode ) ) != false ) + { + AddViewToScene( pSkyView ); + } + SafeRelease( pSkyView ); + } + + Vector4D plane; + + // Combine the target angles and the plane angles + Vector vecAnglesNormal( angTargetAngles.x, angTargetAngles.y, angTargetAngles.z ); + VectorNormalize( vecAnglesNormal ); + VectorCopy( vecAnglesNormal, plane.AsVector3D() ); + + // TODO: How do we get a good value for this!?!? + //plane.w = m_OurPlane.dist + 0.1f; + plane.w = -32.0f + 0.1f; + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->PushCustomClipPlane( plane.Base() ); + + ViewDrawScene( bDrew3dSkybox, nSkyMode, monitorView, nClearFlags, VIEW_MONITOR ); + + pRenderContext->PopCustomClipPlane(); + render->PopView( frustum ); + + // Reset the world fog parameters. + if ( fogEnabled ) + { + if ( pFogParams ) + { + *pFogParams = oldFogParams; + } + monitorView.zFar = flOldZFar; + } +#endif // USE_MONITORS + return true; +} +#endif + void CViewRender::DrawMonitors( const CViewSetup &cameraView ) { #ifdef PORTAL @@ -3354,6 +3624,17 @@ void CViewRender::DrawMonitors( const CViewSetup &cameraView ) if ( !pCameraEnt->IsActive() || pCameraEnt->IsDormant() ) continue; +#ifdef MAPBASE + // Check if the camera has its own render target + // (Multiple render target support) + if ( pCameraTarget != pCameraEnt->RenderTarget() ) + { + pCameraTarget = pCameraEnt->RenderTarget(); + width = pCameraTarget->GetActualWidth(); + height = pCameraTarget->GetActualHeight(); + } +#endif + if ( !DrawOneMonitor( pCameraTarget, cameraNum, pCameraEnt, cameraView, player, 0, 0, width, height ) ) continue; @@ -4949,8 +5230,9 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR // if you can get really close to the skybox geometry it's possible that you'll be able to clip into it // with this near plane. If so, move it in a bit. It's at 2.0 to give us more precision. That means you // need to keep the eye position at least 2 * scale away from the geometry in the skybox - zNear = 2.0; #ifdef MAPBASE + zNear = r_nearz_skybox.GetFloat(); + // Use the fog's farz if specified if (m_pSky3dParams->fog.farz > 0) { @@ -4959,6 +5241,8 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR m_pSky3dParams->fog.farz ); } else +#else + zNear = 2.0; #endif zFar = MAX_TRACE_LENGTH; @@ -5040,6 +5324,51 @@ void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostR DrawTranslucentRenderables( true, false ); DrawNoZBufferTranslucentRenderables(); +#ifdef MAPBASE + // Allows reflective glass to be drawn in the skybox. + // New expansions also allow for custom render targets and multiple mirror renders + if (r_skybox_use_complex_views.GetBool()) + { + VisibleFogVolumeInfo_t fogVolumeInfo; + render->GetVisibleFogVolume( origin, &fogVolumeInfo ); + + WaterRenderInfo_t info; + info.m_bCheapWater = true; + info.m_bRefract = false; + info.m_bReflect = false; + info.m_bReflectEntities = false; + info.m_bDrawWaterSurface = false; + info.m_bOpaqueWater = true; + + cplane_t glassReflectionPlane; + Frustum_t frustum; + GeneratePerspectiveFrustum( origin, angles, zNear, zFar, fov, m_flAspectRatio, frustum ); + + ITexture *pTextureTargets[2]; + C_BaseEntity *pReflectiveGlass = NextReflectiveGlass( NULL, (*this), glassReflectionPlane, frustum, pTextureTargets ); + while ( pReflectiveGlass != NULL ) + { + if (pTextureTargets[0]) + { + CRefPtr pGlassReflectionView = new CReflectiveGlassView( m_pMainView ); + pGlassReflectionView->m_pRenderTarget = pTextureTargets[0]; + pGlassReflectionView->Setup( (*this), VIEW_CLEAR_DEPTH, true, fogVolumeInfo, info, glassReflectionPlane ); + m_pMainView->AddViewToScene( pGlassReflectionView ); + } + + if (pTextureTargets[1]) + { + CRefPtr pGlassRefractionView = new CRefractiveGlassView( m_pMainView ); + pGlassRefractionView->m_pRenderTarget = pTextureTargets[1]; + pGlassRefractionView->Setup( (*this), VIEW_CLEAR_DEPTH, true, fogVolumeInfo, info, glassReflectionPlane ); + m_pMainView->AddViewToScene( pGlassRefractionView ); + } + + pReflectiveGlass = NextReflectiveGlass( pReflectiveGlass, (*this), glassReflectionPlane, frustum, pTextureTargets ); + } + } +#endif + m_pMainView->DisableFog(); CGlowOverlay::UpdateSkyOverlays( zFar, m_bCacheFullSceneState ); @@ -5423,7 +5752,7 @@ bool CBaseWorldView::AdjustView( float waterHeight ) { if( m_DrawFlags & DF_RENDER_REFRACTION ) { - ITexture *pTexture = GetWaterRefractionTexture(); + ITexture *pTexture = GetRefractionTexture(); // Use the aspect ratio of the main view! So, don't recompute it here x = y = 0; @@ -5435,7 +5764,7 @@ bool CBaseWorldView::AdjustView( float waterHeight ) if( m_DrawFlags & DF_RENDER_REFLECTION ) { - ITexture *pTexture = GetWaterReflectionTexture(); + ITexture *pTexture = GetReflectionTexture(); // If the main view is overriding the projection matrix (for Stereo or // some other nefarious purpose) make sure to include any Y offset in @@ -5497,14 +5826,14 @@ void CBaseWorldView::PushView( float waterHeight ) pRenderContext->SetHeightClipMode( clipMode ); // Have to re-set up the view since we reset the size - render->Push3DView( *this, m_ClearFlags, GetWaterRefractionTexture(), GetFrustum() ); + render->Push3DView( *this, m_ClearFlags, GetRefractionTexture(), GetFrustum() ); return; } if( m_DrawFlags & DF_RENDER_REFLECTION ) { - ITexture *pTexture = GetWaterReflectionTexture(); + ITexture *pTexture = GetReflectionTexture(); pRenderContext->SetFogZ( waterHeight ); @@ -5558,11 +5887,11 @@ void CBaseWorldView::PopView() // these renders paths used their surfaces, so blit their results if ( m_DrawFlags & DF_RENDER_REFRACTION ) { - pRenderContext->CopyRenderTargetToTextureEx( GetWaterRefractionTexture(), NULL, NULL ); + pRenderContext->CopyRenderTargetToTextureEx( GetRefractionTexture(), NULL, NULL ); } if ( m_DrawFlags & DF_RENDER_REFLECTION ) { - pRenderContext->CopyRenderTargetToTextureEx( GetWaterReflectionTexture(), NULL, NULL ); + pRenderContext->CopyRenderTargetToTextureEx( GetReflectionTexture(), NULL, NULL ); } } @@ -6439,7 +6768,11 @@ void CReflectiveGlassView::Setup( const CViewSetup &view, int nClearFlags, bool bool CReflectiveGlassView::AdjustView( float flWaterHeight ) { +#ifdef MAPBASE + ITexture *pTexture = GetReflectionTexture(); +#else ITexture *pTexture = GetWaterReflectionTexture(); +#endif // Use the aspect ratio of the main view! So, don't recompute it here x = y = 0; @@ -6465,7 +6798,11 @@ bool CReflectiveGlassView::AdjustView( float flWaterHeight ) void CReflectiveGlassView::PushView( float waterHeight ) { +#ifdef MAPBASE + render->Push3DView( *this, m_ClearFlags, GetReflectionTexture(), GetFrustum() ); +#else render->Push3DView( *this, m_ClearFlags, GetWaterReflectionTexture(), GetFrustum() ); +#endif Vector4D plane; VectorCopy( m_ReflectionPlane.normal, plane.AsVector3D() ); @@ -6531,7 +6868,11 @@ void CRefractiveGlassView::Setup( const CViewSetup &view, int nClearFlags, bool bool CRefractiveGlassView::AdjustView( float flWaterHeight ) { +#ifdef MAPBASE + ITexture *pTexture = GetRefractionTexture(); +#else ITexture *pTexture = GetWaterRefractionTexture(); +#endif // Use the aspect ratio of the main view! So, don't recompute it here x = y = 0; @@ -6543,7 +6884,11 @@ bool CRefractiveGlassView::AdjustView( float flWaterHeight ) void CRefractiveGlassView::PushView( float waterHeight ) { +#ifdef MAPBASE + render->Push3DView( *this, m_ClearFlags, GetRefractionTexture(), GetFrustum() ); +#else render->Push3DView( *this, m_ClearFlags, GetWaterRefractionTexture(), GetFrustum() ); +#endif Vector4D plane; VectorMultiply( m_ReflectionPlane.normal, -1, plane.AsVector3D() ); diff --git a/sp/src/game/client/viewrender.h b/sp/src/game/client/viewrender.h index c3e02a4b39..2312c4d532 100644 --- a/sp/src/game/client/viewrender.h +++ b/sp/src/game/client/viewrender.h @@ -37,6 +37,10 @@ class CReplayScreenshotTaker; class CStunEffect; #endif // HL2_EPISODIC +#ifdef MAPBASE + class C_FuncFakeWorldPortal; +#endif + //----------------------------------------------------------------------------- // Data specific to intro mode to control rendering. //----------------------------------------------------------------------------- @@ -447,6 +451,12 @@ class CViewRender : public IViewRender, bool DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_PointCamera *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, int x, int y, int width, int height ); +#ifdef MAPBASE + bool DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldPortal *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, + int x, int y, int width, int height, + const CViewSetup &mainView, cplane_t &ourPlane ); +#endif + // Drawing primitives bool ShouldDrawViewModel( bool drawViewmodel ); void DrawViewModels( const CViewSetup &view, bool drawViewmodel ); diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 9a9aa42abf..f340ecd16c 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -496,11 +496,13 @@ bool VScriptClientInit() #ifdef MAPBASE_VSCRIPT g_pScriptVM->RegisterAllClasses(); + g_pScriptVM->RegisterAllEnums(); g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); IGameSystem::RegisterVScriptAllSystems(); + RegisterSharedScriptConstants(); RegisterSharedScriptFunctions(); #else //g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); diff --git a/sp/src/game/server/SkyCamera.cpp b/sp/src/game/server/SkyCamera.cpp index 5ef2f90069..be9716d478 100644 --- a/sp/src/game/server/SkyCamera.cpp +++ b/sp/src/game/server/SkyCamera.cpp @@ -91,11 +91,15 @@ BEGIN_DATADESC( CSkyCamera ) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOffFog", InputTurnOffFog ), DEFINE_INPUTFUNC( FIELD_COLOR32, "SetFogColor", InputSetFogColor ), DEFINE_INPUTFUNC( FIELD_COLOR32, "SetFogColorSecondary", InputSetFogColorSecondary ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "CopyFogController", InputCopyFogController ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "CopyFogControllerWithScale", InputCopyFogControllerWithScale ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFarZ", InputSetFarZ ), DEFINE_INPUTFUNC( FIELD_COLOR32, "SetSkyColor", InputSetSkyColor ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetScale", InputSetScale ), + DEFINE_THINKFUNC( UpdateThink ), #endif @@ -259,7 +263,7 @@ bool CSkyCamera::DoUpdate( bool bUpdateData ) // this thinking is only used to update area and other parameters // Getting into another area is unlikely, but if it's not expensive, I guess it's okay. - int area = engine->GetArea( m_skyboxData.origin ); + int area = engine->GetArea( GetAbsOrigin() ); if (m_skyboxData.area != area) { m_skyboxData.area = area; @@ -407,4 +411,52 @@ void CSkyCamera::InputSetFogColor( inputdata_t &inputdata ) { m_skyboxData.fog.c void CSkyCamera::InputSetFogColorSecondary( inputdata_t &inputdata ) { m_skyboxData.fog.colorSecondary = inputdata.value.Color32(); } void CSkyCamera::InputSetFarZ( inputdata_t &inputdata ) { m_skyboxData.fog.farz = inputdata.value.Int(); } + +void CSkyCamera::InputCopyFogController( inputdata_t &inputdata ) +{ + CFogController *pFogController = dynamic_cast(inputdata.value.Entity().Get()); + if (!pFogController) + return; + + m_skyboxData.fog.dirPrimary = pFogController->m_fog.dirPrimary; + m_skyboxData.fog.colorPrimary = pFogController->m_fog.colorPrimary; + m_skyboxData.fog.colorSecondary = pFogController->m_fog.colorSecondary; + //m_skyboxData.fog.colorPrimaryLerpTo = pFogController->m_fog.colorPrimaryLerpTo; + //m_skyboxData.fog.colorSecondaryLerpTo = pFogController->m_fog.colorSecondaryLerpTo; + m_skyboxData.fog.start = pFogController->m_fog.start; + m_skyboxData.fog.end = pFogController->m_fog.end; + m_skyboxData.fog.farz = pFogController->m_fog.farz; + m_skyboxData.fog.maxdensity = pFogController->m_fog.maxdensity; + + //m_skyboxData.fog.startLerpTo = pFogController->m_fog.startLerpTo; + //m_skyboxData.fog.endLerpTo = pFogController->m_fog.endLerpTo; + //m_skyboxData.fog.lerptime = pFogController->m_fog.lerptime; + //m_skyboxData.fog.duration = pFogController->m_fog.duration; + //m_skyboxData.fog.enable = pFogController->m_fog.enable; + m_skyboxData.fog.blend = pFogController->m_fog.blend; +} + +void CSkyCamera::InputCopyFogControllerWithScale( inputdata_t &inputdata ) +{ + CFogController *pFogController = dynamic_cast(inputdata.value.Entity().Get()); + if (!pFogController) + return; + + m_skyboxData.fog.dirPrimary = pFogController->m_fog.dirPrimary; + m_skyboxData.fog.colorPrimary = pFogController->m_fog.colorPrimary; + m_skyboxData.fog.colorSecondary = pFogController->m_fog.colorSecondary; + //m_skyboxData.fog.colorPrimaryLerpTo = pFogController->m_fog.colorPrimaryLerpTo; + //m_skyboxData.fog.colorSecondaryLerpTo = pFogController->m_fog.colorSecondaryLerpTo; + m_skyboxData.fog.start = pFogController->m_fog.start * m_skyboxData.scale; + m_skyboxData.fog.end = pFogController->m_fog.end * m_skyboxData.scale; + m_skyboxData.fog.farz = pFogController->m_fog.farz != -1 ? (pFogController->m_fog.farz * m_skyboxData.scale) : pFogController->m_fog.farz; + m_skyboxData.fog.maxdensity = pFogController->m_fog.maxdensity; + + //m_skyboxData.fog.startLerpTo = pFogController->m_fog.startLerpTo; + //m_skyboxData.fog.endLerpTo = pFogController->m_fog.endLerpTo; + //m_skyboxData.fog.lerptime = pFogController->m_fog.lerptime; + //m_skyboxData.fog.duration = pFogController->m_fog.duration; + //m_skyboxData.fog.enable = pFogController->m_fog.enable; + m_skyboxData.fog.blend = pFogController->m_fog.blend; +} #endif diff --git a/sp/src/game/server/SkyCamera.h b/sp/src/game/server/SkyCamera.h index d5438c67ad..a193656818 100644 --- a/sp/src/game/server/SkyCamera.h +++ b/sp/src/game/server/SkyCamera.h @@ -72,10 +72,14 @@ class CSkyCamera : public CLogicalEntity void InputSetFogColor( inputdata_t &data ); void InputSetFogColorSecondary( inputdata_t &data ); void InputSetFogMaxDensity( inputdata_t &inputdata ); + void InputCopyFogController( inputdata_t &inputdata ); + void InputCopyFogControllerWithScale( inputdata_t &inputdata ); void InputSetFarZ( inputdata_t &data ); void InputSetSkyColor( inputdata_t &inputdata ) { m_skyboxData.skycolor = inputdata.value.Color32(); } + + void InputSetScale( inputdata_t &inputdata ) { m_skyboxData.scale = inputdata.value.Int(); } #endif public: diff --git a/sp/src/game/server/TemplateEntities.cpp b/sp/src/game/server/TemplateEntities.cpp index 86398a483d..b2a54fd1ea 100644 --- a/sp/src/game/server/TemplateEntities.cpp +++ b/sp/src/game/server/TemplateEntities.cpp @@ -398,7 +398,9 @@ void Templates_StartUniqueInstance( void ) //----------------------------------------------------------------------------- char *Templates_GetEntityIOFixedMapData( int iIndex ) { +#ifndef MAPBASE // This code also runs when the point_template's script scope is active Assert( Templates_IndexRequiresEntityIOFixup( iIndex ) ); +#endif // First time through? if ( !g_Templates[iIndex]->pszFixedMapData ) diff --git a/sp/src/game/server/ai_activity.cpp b/sp/src/game/server/ai_activity.cpp index 475bdbce18..b88c114289 100644 --- a/sp/src/game/server/ai_activity.cpp +++ b/sp/src/game/server/ai_activity.cpp @@ -2179,7 +2179,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_RUN_AR2 ); ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR2 ); - //ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2 ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2 ); //ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR2 ); diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index cd0e236bcc..88aeba5684 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -2293,6 +2293,28 @@ bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) if( ShouldIgnoreSound( pSound ) ) return false; +#ifdef MAPBASE_VSCRIPT + if (HSCRIPT hFunc = LookupScriptFunction("QueryHearSound")) + { + HSCRIPT hSound = g_pScriptVM->RegisterInstance( pSound ); + g_pScriptVM->SetValue( "sound", hSound ); + + ScriptVariant_t functionReturn; + bool bValid = true; + if ( CallScriptFunctionHandle( hFunc, &functionReturn ) ) + { + if (functionReturn.m_bool == false) + bValid = false; + } + + g_pScriptVM->ClearValue( "sound" ); + g_pScriptVM->RemoveInstance( hSound ); + + if (bValid == false) + return false; + } +#endif + return true; } @@ -2300,12 +2322,34 @@ bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) bool CAI_BaseNPC::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC ) { + bool bValid = true; + if ( bOnlyHateOrFearIfNPC && pEntity->IsNPC() ) { Disposition_t disposition = IRelationType( pEntity ); - return ( disposition == D_HT || disposition == D_FR ); + bValid = ( disposition == D_HT || disposition == D_FR ); } - return true; + +#ifdef MAPBASE_VSCRIPT + if (bValid) + { + if (HSCRIPT hFunc = LookupScriptFunction("QuerySeeEntity")) + { + g_pScriptVM->SetValue( "entity", ToHScript(pEntity) ); + + ScriptVariant_t functionReturn; + if ( CallScriptFunctionHandle( hFunc, &functionReturn ) ) + { + if (functionReturn.m_bool == false) + bValid = false; + } + + g_pScriptVM->ClearValue( "entity" ); + } + } +#endif + + return bValid; } //----------------------------------------------------------------------------- @@ -6555,13 +6599,13 @@ Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) } #ifdef MAPBASE_VSCRIPT - if ( m_ScriptScope.IsInitialized() ) + if (HSCRIPT hFunc = LookupScriptFunction("NPC_TranslateActivity")) { g_pScriptVM->SetValue( "activity", GetActivityName(eNewActivity) ); g_pScriptVM->SetValue( "activity_id", (int)eNewActivity ); ScriptVariant_t functionReturn; - if( CallScriptFunction( "NPC_TranslateActivity", &functionReturn ) ) + if ( CallScriptFunctionHandle( hFunc, &functionReturn ) ) { if (functionReturn.m_type == FIELD_INTEGER) { diff --git a/sp/src/game/server/ai_memory.cpp b/sp/src/game/server/ai_memory.cpp index 46cd09f8eb..0af73a8957 100644 --- a/sp/src/game/server/ai_memory.cpp +++ b/sp/src/game/server/ai_memory.cpp @@ -164,7 +164,7 @@ BEGIN_SCRIPTDESC_ROOT( AI_EnemyInfo_t, "Accessor for information about an 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 unfortgettable." ) + 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 diff --git a/sp/src/game/server/ai_scriptconditions.cpp b/sp/src/game/server/ai_scriptconditions.cpp index 18afdc3f6a..205bcfb9cc 100644 --- a/sp/src/game/server/ai_scriptconditions.cpp +++ b/sp/src/game/server/ai_scriptconditions.cpp @@ -859,6 +859,11 @@ void CAI_ScriptConditions::OnEntitySpawned( CBaseEntity *pEntity ) if ( pEntity->MyNPCPointer() == NULL ) return; +#ifdef MAPBASE + if ( m_Actor == NULL_STRING ) + return; +#endif + if ( pEntity->NameMatches( m_Actor ) ) { if ( ActorInList( pEntity ) == false ) diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index a71d344bad..a2f5e39c83 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -312,6 +312,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( GetSkin, "Gets the model's skin" ) + DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) #endif DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") @@ -2247,7 +2249,8 @@ HSCRIPT CBaseAnimating::ScriptGetSequenceKeyValues( int iSequence ) // UNDONE: who calls ReleaseInstance on this??? Does name need to be unique??? - hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues ); + // Allow VScript to delete this when the instance is removed. + hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues, true ); } return hScript; diff --git a/sp/src/game/server/baseanimating.h b/sp/src/game/server/baseanimating.h index da43ef40de..ce9b77dc53 100644 --- a/sp/src/game/server/baseanimating.h +++ b/sp/src/game/server/baseanimating.h @@ -202,6 +202,10 @@ class CBaseAnimating : public CBaseEntity int ScriptSelectWeightedSequence( int activity, int curSequence ) { return SelectWeightedSequence( (Activity)activity, curSequence ); } HSCRIPT ScriptGetSequenceKeyValues( int iSequence ); + + // For VScript + int GetSkin() { return m_nSkin; } + void SetSkin( int iSkin ) { m_nSkin = iSkin; } #endif // These return the attachment in the space of the entity diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index ac299bfd16..a8b70b8dc1 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -2193,7 +2193,12 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) #ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOriginAngles, "SetOriginAngles", "Set both the origin and the angles" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOriginAnglesVelocity, "SetOriginAnglesVelocity", "Set the origin, the angles, and the velocity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) #endif DEFINE_SCRIPTFUNC_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) @@ -2265,6 +2270,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetColorB", "Set the render color's B value" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetAlpha", "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( GetSpawnFlags, "Get spawnflags" ) DEFINE_SCRIPTFUNC( AddSpawnFlags, "Add spawnflag(s)" ) DEFINE_SCRIPTFUNC( RemoveSpawnFlags, "Remove spawnflag(s)" ) @@ -2278,10 +2286,25 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( SetEffects, "Set effect(s)" ) DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + 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." ) #endif DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) @@ -2986,6 +3009,30 @@ void CBaseEntity::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) int otherIndex = !index; CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex]; +#ifdef MAPBASE_VSCRIPT + if (HSCRIPT hFunc = LookupScriptFunction("VPhysicsCollision")) + { + // TODO: Unique class for collision events + g_pScriptVM->SetValue( "entity", ScriptVariant_t( pHitEntity->GetScriptInstance() ) ); + g_pScriptVM->SetValue( "speed", pEvent->collisionSpeed ); + + Vector vecContactPoint; + pEvent->pInternalData->GetContactPoint( vecContactPoint ); + g_pScriptVM->SetValue( "point", vecContactPoint ); + + Vector vecSurfaceNormal; + pEvent->pInternalData->GetSurfaceNormal( vecSurfaceNormal ); + g_pScriptVM->SetValue( "normal", vecSurfaceNormal ); + + CallScriptFunctionHandle( hFunc, NULL ); + + g_pScriptVM->ClearValue( "entity" ); + g_pScriptVM->ClearValue( "speed" ); + g_pScriptVM->ClearValue( "point" ); + g_pScriptVM->ClearValue( "normal" ); + } +#endif + // Don't make sounds / effects if neither entity is MOVETYPE_VPHYSICS. The game // physics should have done so. if ( GetMoveType() != MOVETYPE_VPHYSICS && pHitEntity->GetMoveType() != MOVETYPE_VPHYSICS ) @@ -7999,7 +8046,10 @@ void CBaseEntity::InputSetCollisionGroup( inputdata_t& inputdata ) //----------------------------------------------------------------------------- void CBaseEntity::InputTouch( inputdata_t& inputdata ) { - Touch(inputdata.value.Entity()); + if (inputdata.value.Entity()) + Touch( inputdata.value.Entity() ); + else + Warning( "%s InputTouch: Can't touch null entity", GetDebugName() ); } //----------------------------------------------------------------------------- @@ -8185,6 +8235,36 @@ bool CBaseEntity::CallScriptFunction(const char* pFunctionName, ScriptVariant_t* return false; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Gets a function handle +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::LookupScriptFunction(const char* pFunctionName) +{ + START_VMPROFILE + + if (!m_ScriptScope.IsInitialized()) + { + return NULL; + } + + return m_ScriptScope.LookupFunction(pFunctionName); +} + +//----------------------------------------------------------------------------- +// Calls and releases a function handle (ASSUMES SCRIPT SCOPE AND FUNCTION ARE VALID!) +//----------------------------------------------------------------------------- +bool CBaseEntity::CallScriptFunctionHandle(HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn) +{ + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + UPDATE_VMPROFILE + + return true; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -9539,7 +9619,12 @@ HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void ) // UNDONE: who calls ReleaseInstance on this??? Does name need to be unique??? +#ifdef MAPBASE_VSCRIPT + // Allow VScript to delete this when the instance is removed. + hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues, true ); +#else hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues ); +#endif /* KeyValues *pParticleEffects = pModelKeyValues->FindKey("Particles"); @@ -9684,6 +9769,17 @@ HSCRIPT CBaseEntity::ScriptEntityToWorldTransform( void ) { return ScriptCreateMatrixInstance( 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; +} #endif diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index fa4dea179d..fe03c1ff99 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -1963,6 +1963,11 @@ class CBaseEntity : public IServerEntity void RunPrecacheScripts(void); void RunOnPostSpawnScripts(void); +#ifdef MAPBASE_VSCRIPT + HSCRIPT LookupScriptFunction(const char* pFunctionName); + bool CallScriptFunctionHandle(HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn); +#endif + HSCRIPT ScriptGetMoveParent(void); HSCRIPT ScriptGetRootMoveParent(); HSCRIPT ScriptFirstMoveChild(void); @@ -1988,7 +1993,12 @@ class CBaseEntity : public IServerEntity const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } #ifdef MAPBASE_VSCRIPT + void ScriptSetOriginAngles(const Vector &vecOrigin, const QAngle &angAngles) { Teleport(&vecOrigin, &angAngles, NULL); } + void ScriptSetOriginAnglesVelocity(const Vector &vecOrigin, const QAngle &angAngles, const Vector &vecVelocity) { Teleport(&vecOrigin, &angAngles, &vecVelocity); } + HSCRIPT ScriptEntityToWorldTransform( void ); + + HSCRIPT ScriptGetPhysicsObject( void ); #endif const char* ScriptGetModelName(void) const; @@ -2031,6 +2041,12 @@ class CBaseEntity : public IServerEntity 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 string_t m_iszVScripts; diff --git a/sp/src/game/server/env_instructor_hint.cpp b/sp/src/game/server/env_instructor_hint.cpp new file mode 100644 index 0000000000..32b7504142 --- /dev/null +++ b/sp/src/game/server/env_instructor_hint.cpp @@ -0,0 +1,229 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: An entity for creating instructor hints entirely with map logic +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "baseentity.h" +#include "world.h" + +#ifdef INFESTED_DLL + #include "asw_marine.h" + #include "asw_player.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CEnvInstructorHint : public CPointEntity +{ +public: + DECLARE_CLASS( CEnvInstructorHint, CPointEntity ); + DECLARE_DATADESC(); + +private: + void InputShowHint( inputdata_t &inputdata ); + void InputEndHint( inputdata_t &inputdata ); + +#ifdef MAPBASE + void InputSetCaption( inputdata_t &inputdata ) { m_iszCaption = inputdata.value.StringID(); } +#endif + + string_t m_iszReplace_Key; + string_t m_iszHintTargetEntity; + int m_iTimeout; + string_t m_iszIcon_Onscreen; + string_t m_iszIcon_Offscreen; + string_t m_iszCaption; + string_t m_iszActivatorCaption; + color32 m_Color; + float m_fIconOffset; + float m_fRange; + uint8 m_iPulseOption; + uint8 m_iAlphaOption; + uint8 m_iShakeOption; + bool m_bStatic; + bool m_bNoOffscreen; + bool m_bForceCaption; + string_t m_iszBinding; + bool m_bAllowNoDrawTarget; + bool m_bLocalPlayerOnly; +#ifdef MAPBASE + string_t m_iszStartSound; + int m_iHintTargetPos; +#endif +}; + +LINK_ENTITY_TO_CLASS( env_instructor_hint, CEnvInstructorHint ); + +BEGIN_DATADESC( CEnvInstructorHint ) + + DEFINE_KEYFIELD( m_iszReplace_Key, FIELD_STRING, "hint_replace_key" ), + DEFINE_KEYFIELD( m_iszHintTargetEntity, FIELD_STRING, "hint_target" ), + DEFINE_KEYFIELD( m_iTimeout, FIELD_INTEGER, "hint_timeout" ), + DEFINE_KEYFIELD( m_iszIcon_Onscreen, FIELD_STRING, "hint_icon_onscreen" ), + DEFINE_KEYFIELD( m_iszIcon_Offscreen, FIELD_STRING, "hint_icon_offscreen" ), + DEFINE_KEYFIELD( m_iszCaption, FIELD_STRING, "hint_caption" ), + DEFINE_KEYFIELD( m_iszActivatorCaption, FIELD_STRING, "hint_activator_caption" ), + DEFINE_KEYFIELD( m_Color, FIELD_COLOR32, "hint_color" ), + DEFINE_KEYFIELD( m_fIconOffset, FIELD_FLOAT, "hint_icon_offset" ), + DEFINE_KEYFIELD( m_fRange, FIELD_FLOAT, "hint_range" ), + DEFINE_KEYFIELD( m_iPulseOption, FIELD_CHARACTER, "hint_pulseoption" ), + DEFINE_KEYFIELD( m_iAlphaOption, FIELD_CHARACTER, "hint_alphaoption" ), + DEFINE_KEYFIELD( m_iShakeOption, FIELD_CHARACTER, "hint_shakeoption" ), + DEFINE_KEYFIELD( m_bStatic, FIELD_BOOLEAN, "hint_static" ), + DEFINE_KEYFIELD( m_bNoOffscreen, FIELD_BOOLEAN, "hint_nooffscreen" ), + DEFINE_KEYFIELD( m_bForceCaption, FIELD_BOOLEAN, "hint_forcecaption" ), + DEFINE_KEYFIELD( m_iszBinding, FIELD_STRING, "hint_binding" ), + DEFINE_KEYFIELD( m_bAllowNoDrawTarget, FIELD_BOOLEAN, "hint_allow_nodraw_target" ), + DEFINE_KEYFIELD( m_bLocalPlayerOnly, FIELD_BOOLEAN, "hint_local_player_only" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszStartSound, FIELD_STRING, "hint_start_sound" ), + DEFINE_KEYFIELD( m_iHintTargetPos, FIELD_INTEGER, "hint_target_pos" ), +#endif + + DEFINE_INPUTFUNC( FIELD_STRING, "ShowHint", InputShowHint ), + DEFINE_INPUTFUNC( FIELD_VOID, "EndHint", InputEndHint ), + +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "SetCaption", InputSetCaption ), +#endif + +END_DATADESC() + + +#define LOCATOR_ICON_FX_PULSE_SLOW 0x00000001 +#define LOCATOR_ICON_FX_ALPHA_SLOW 0x00000008 +#define LOCATOR_ICON_FX_SHAKE_NARROW 0x00000040 +#define LOCATOR_ICON_FX_STATIC 0x00000100 // This icon draws at a fixed location on the HUD. + +//----------------------------------------------------------------------------- +// Purpose: Input handler for showing the message and/or playing the sound. +//----------------------------------------------------------------------------- +void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) +{ + IGameEvent * event = gameeventmanager->CreateEvent( "instructor_server_hint_create", false ); + if ( event ) + { + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, m_iszHintTargetEntity ); + if( pTargetEntity == NULL && !m_bStatic ) + pTargetEntity = inputdata.pActivator; + + if( pTargetEntity == NULL ) + pTargetEntity = GetWorldEntity(); + + char szColorString[128]; + Q_snprintf( szColorString, sizeof( szColorString ), "%.3d,%.3d,%.3d", m_Color.r, m_Color.g, m_Color.b ); + + int iFlags = 0; + + iFlags |= (m_iPulseOption == 0) ? 0 : (LOCATOR_ICON_FX_PULSE_SLOW << (m_iPulseOption - 1)); + iFlags |= (m_iAlphaOption == 0) ? 0 : (LOCATOR_ICON_FX_ALPHA_SLOW << (m_iAlphaOption - 1)); + iFlags |= (m_iShakeOption == 0) ? 0 : (LOCATOR_ICON_FX_SHAKE_NARROW << (m_iShakeOption - 1)); + iFlags |= m_bStatic ? LOCATOR_ICON_FX_STATIC : 0; + + CBasePlayer *pActivator = NULL; + bool bFilterByActivator = m_bLocalPlayerOnly; + +#ifdef INFESTED_DLL + CASW_Marine *pMarine = dynamic_cast( inputdata.pActivator ); + if ( pMarine ) + { + pActivator = pMarine->GetCommander(); + } +#else + if ( inputdata.value.StringID() != NULL_STRING ) + { + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String() ); + pActivator = dynamic_cast( pTarget ); + if ( pActivator ) + { + bFilterByActivator = true; + } + } + else + { + if ( GameRules()->IsMultiplayer() == false ) + { + pActivator = UTIL_GetLocalPlayer(); + } + else + { + Warning( "Failed to play server side instructor hint: no player specified for hint\n" ); + Assert( 0 ); + } + } +#endif + + const char *pActivatorCaption = m_iszActivatorCaption.ToCStr(); + if ( !pActivatorCaption || pActivatorCaption[ 0 ] == '\0' ) + { + pActivatorCaption = m_iszCaption.ToCStr(); + } + + event->SetString( "hint_name", GetEntityName().ToCStr() ); + event->SetString( "hint_replace_key", m_iszReplace_Key.ToCStr() ); + event->SetInt( "hint_target", pTargetEntity->entindex() ); + event->SetInt( "hint_activator_userid", ( pActivator ? pActivator->GetUserID() : 0 ) ); + event->SetInt( "hint_timeout", m_iTimeout ); + event->SetString( "hint_icon_onscreen", m_iszIcon_Onscreen.ToCStr() ); + event->SetString( "hint_icon_offscreen", m_iszIcon_Offscreen.ToCStr() ); + event->SetString( "hint_caption", m_iszCaption.ToCStr() ); + event->SetString( "hint_activator_caption", pActivatorCaption ); + event->SetString( "hint_color", szColorString ); + event->SetFloat( "hint_icon_offset", m_fIconOffset ); + event->SetFloat( "hint_range", m_fRange ); + event->SetInt( "hint_flags", iFlags ); + event->SetString( "hint_binding", m_iszBinding.ToCStr() ); + event->SetBool( "hint_allow_nodraw_target", m_bAllowNoDrawTarget ); + event->SetBool( "hint_nooffscreen", m_bNoOffscreen ); + event->SetBool( "hint_forcecaption", m_bForceCaption ); + event->SetBool( "hint_local_player_only", bFilterByActivator ); +#ifdef MAPBASE + event->SetString( "hint_start_sound", m_iszStartSound.ToCStr() ); + event->SetInt( "hint_target_pos", m_iHintTargetPos ); +#endif + + gameeventmanager->FireEvent( event ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvInstructorHint::InputEndHint( inputdata_t &inputdata ) +{ + IGameEvent * event = gameeventmanager->CreateEvent( "instructor_server_hint_stop", false ); + if ( event ) + { + event->SetString( "hint_name", GetEntityName().ToCStr() ); + + gameeventmanager->FireEvent( event ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: A generic target entity that gets replicated to the client for instructor hint targetting +//----------------------------------------------------------------------------- +class CInfoInstructorHintTarget : public CPointEntity +{ +public: + DECLARE_CLASS( CInfoInstructorHintTarget, CPointEntity ); + + virtual int UpdateTransmitState( void ) // set transmit filter to transmit always + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( info_target_instructor_hint, CInfoInstructorHintTarget ); + +BEGIN_DATADESC( CInfoInstructorHintTarget ) + +END_DATADESC() diff --git a/sp/src/game/server/env_projectedtexture.cpp b/sp/src/game/server/env_projectedtexture.cpp index 78bd58bd4f..0299fbd098 100644 --- a/sp/src/game/server/env_projectedtexture.cpp +++ b/sp/src/game/server/env_projectedtexture.cpp @@ -21,6 +21,7 @@ BEGIN_DATADESC( CEnvProjectedTexture ) DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ), #ifdef MAPBASE DEFINE_KEYFIELD( m_bDontFollowTarget, FIELD_BOOLEAN, "dontfollowtarget" ), + DEFINE_FIELD( m_bAlwaysUpdate, FIELD_BOOLEAN ), #endif DEFINE_FIELD( m_bState, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_flLightFOV, FIELD_FLOAT, "lightfov" ), diff --git a/sp/src/game/server/func_reflective_glass.cpp b/sp/src/game/server/func_reflective_glass.cpp index d68840ef9d..187556b5e9 100644 --- a/sp/src/game/server/func_reflective_glass.cpp +++ b/sp/src/game/server/func_reflective_glass.cpp @@ -15,13 +15,44 @@ class CFuncReflectiveGlass : public CFuncBrush DECLARE_DATADESC(); DECLARE_CLASS( CFuncReflectiveGlass, CFuncBrush ); DECLARE_SERVERCLASS(); + + CFuncReflectiveGlass() + { +#ifdef MAPBASE + m_iszReflectRenderTarget = AllocPooledString( "_rt_WaterReflection" ); + m_iszRefractRenderTarget = AllocPooledString( "_rt_WaterRefraction" ); +#endif + } + +#ifdef MAPBASE + void InputSetReflectRenderTarget( inputdata_t &inputdata ) { m_iszReflectRenderTarget = inputdata.value.StringID(); } + void InputSetRefractRenderTarget( inputdata_t &inputdata ) { m_iszRefractRenderTarget = inputdata.value.StringID(); } + + CNetworkVar( string_t, m_iszReflectRenderTarget ); + CNetworkVar( string_t, m_iszRefractRenderTarget ); +#endif }; // automatically hooks in the system's callbacks BEGIN_DATADESC( CFuncReflectiveGlass ) + +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iszReflectRenderTarget, FIELD_STRING, "ReflectRenderTarget" ), + DEFINE_KEYFIELD( m_iszRefractRenderTarget, FIELD_STRING, "RefractRenderTarget" ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetReflectRenderTarget", InputSetReflectRenderTarget ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRefractRenderTarget", InputSetRefractRenderTarget ), +#endif + END_DATADESC() LINK_ENTITY_TO_CLASS( func_reflective_glass, CFuncReflectiveGlass ); IMPLEMENT_SERVERCLASS_ST( CFuncReflectiveGlass, DT_FuncReflectiveGlass ) + +#ifdef MAPBASE + SendPropStringT( SENDINFO( m_iszReflectRenderTarget ) ), + SendPropStringT( SENDINFO( m_iszRefractRenderTarget ) ), +#endif + END_SEND_TABLE() diff --git a/sp/src/game/server/hl2/hl2_client.cpp b/sp/src/game/server/hl2/hl2_client.cpp index 6125de82c9..4cd9dd7bd2 100644 --- a/sp/src/game/server/hl2/hl2_client.cpp +++ b/sp/src/game/server/hl2/hl2_client.cpp @@ -137,6 +137,21 @@ void respawn( CBaseEntity *pEdict, bool fCopyCorpse ) // respawn player pEdict->Spawn(); } +#ifdef MAPBASE + else if (g_pGameRules->AllowSPRespawn()) + { + // In SP respawns, only create corpse if drawing externally + CBasePlayer *pPlayer = (CBasePlayer*)pEdict; + if ( fCopyCorpse && pPlayer->m_bDrawPlayerModelExternally ) + { + // make a copy of the dead body for appearances sake + pPlayer->CreateCorpse(); + } + + // respawn player + pPlayer->Spawn(); + } +#endif else { // restart the entire server engine->ServerCommand("reload\n"); diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index d04992fd2c..0378d80d3b 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -230,6 +230,8 @@ class CLogicPlayerProxy : public CLogicalEntity COutputInt m_RequestedPlayerArmor; COutputFloat m_RequestedPlayerAuxPower; COutputFloat m_RequestedPlayerFlashBattery; + + COutputEvent m_OnPlayerSpawn; #endif void InputRequestPlayerHealth( inputdata_t &inputdata ); @@ -602,6 +604,11 @@ BEGIN_ENT_SCRIPTDESC( CHL2_Player, CBasePlayer, "The HL2 player entity." ) DEFINE_SCRIPTFUNC_NAMED( SuitPower_GetCurrentPercentage, "GetAuxPower", "Gets the player's available aux power." ) DEFINE_SCRIPTFUNC( GetFlashlightBattery, "Gets the energy available in the player's flashlight. If the legacy (aux power-based) flashlight is enabled, this returns the aux power." ) + DEFINE_SCRIPTFUNC( InitCustomSuitDevice, "Initializes a custom suit device. (just sets drain rate for now)" ) + DEFINE_SCRIPTFUNC( AddCustomSuitDevice, "Adds a custom suit device ID. (1-3)" ) + DEFINE_SCRIPTFUNC( RemoveCustomSuitDevice, "Removes a custom suit device ID. (1-3)" ) + DEFINE_SCRIPTFUNC( IsCustomSuitDeviceActive, "Checks if a custom suit device is active." ) + END_SCRIPTDESC(); #endif @@ -633,6 +640,16 @@ CHL2_Player::CHL2_Player() #endif CSuitPowerDevice SuitDeviceBreather( bits_SUIT_DEVICE_BREATHER, 6.7f ); // 100 units in 15 seconds (plus three padded seconds) +#ifdef MAPBASE +// Default: 100 units in 8 seconds +CSuitPowerDevice SuitDeviceCustom[] = +{ + { bits_SUIT_DEVICE_CUSTOM0, 12.5f }, + { bits_SUIT_DEVICE_CUSTOM1, 12.5f }, + { bits_SUIT_DEVICE_CUSTOM2, 12.5f }, +}; +#endif + IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player) SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable), @@ -1322,6 +1339,13 @@ void CHL2_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) } #ifdef MAPBASE +void CHL2_Player::SpawnedAtPoint( CBaseEntity *pSpawnPoint ) +{ + FirePlayerProxyOutput( "OnPlayerSpawn", variant_t(), this, pSpawnPoint ); +} + +//----------------------------------------------------------------------------- + ConVar hl2_use_hl2dm_anims( "hl2_use_hl2dm_anims", "0", FCVAR_NONE, "Allows SP HL2 players to use HL2:DM animations (for custom player models)" ); void CHL2_Player::ResetAnimation( void ) @@ -1522,6 +1546,17 @@ void CHL2_Player::Spawn(void) BaseClass::Spawn(); +#ifdef MAPBASE + // Ported from CHL2MP_Player. Fixes issues with respawning players in SP + if ( !IsObserver() ) + { + pl.deadflag = false; + RemoveSolidFlags( FSOLID_NOT_SOLID ); + + RemoveEffects( EF_NODRAW ); + } +#endif + // // Our player movement speed is set once here. This will override the cl_xxxx // cvars unless they are set to be lower than this. @@ -3093,6 +3128,14 @@ void CHL2_Player::Event_Killed( const CTakeDamageInfo &info ) #ifdef MAPBASE FirePlayerProxyOutput( "PlayerDied", variant_t(), info.GetAttacker(), this ); + + if (IsSuitEquipped()) + { + // Make sure all devices are deactivated (for respawn) + m_HL2Local.m_bitsActiveDevices = 0x00000000; + m_flSuitPowerLoad = 0; + m_flTimeAllSuitDevicesOff = gpGlobals->curtime; + } #else FirePlayerProxyOutput( "PlayerDied", variant_t(), this, this ); #endif @@ -4336,6 +4379,64 @@ void CHL2_Player::DisplayLadderHudHint() #endif//CLIENT_DLL } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CHL2_Player::InitCustomSuitDevice( int iDeviceID, float flDrainRate ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("InitCustomSuitDevice : \"%i\" is not a valid custom device slot\n", iDeviceID); + return; + } + + SuitDeviceCustom[iDeviceID].SetDeviceDrainRate( flDrainRate ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CHL2_Player::AddCustomSuitDevice( int iDeviceID ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("AddCustomSuitDevice : \"%i\" is not a valid custom device slot\n", iDeviceID); + return; + } + + SuitPower_AddDevice( SuitDeviceCustom[iDeviceID] ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CHL2_Player::RemoveCustomSuitDevice( int iDeviceID ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("AddCustomSuitDevice : \"%i\" is not a valid custom device slot\n", iDeviceID); + return; + } + + SuitPower_RemoveDevice( SuitDeviceCustom[iDeviceID] ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CHL2_Player::IsCustomSuitDeviceActive( int iDeviceID ) +{ + if (iDeviceID < 0 || iDeviceID > 2) + { + Warning("IsCustomSuitDeviceActive : \"%i\" is not a valid custom device slot\n", iDeviceID); + return false; + } + + return SuitPower_IsDeviceActive( SuitDeviceCustom[iDeviceID] ); +} +#endif + //----------------------------------------------------------------------------- // Shuts down sounds //----------------------------------------------------------------------------- @@ -4370,10 +4471,14 @@ void CHL2_Player::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) #ifdef MAPBASE const char *CHL2_Player::GetOverrideStepSound( const char *pszBaseStepSoundName ) { - const char *szSound = GetContextValue(FindContextByName("footsteps")); - if (szSound[0] != '\0') + int idx = FindContextByName("footsteps"); + if (idx != -1) { - return szSound; + const char *szSound = GetContextValue(idx); + if (szSound[0] != '\0') + { + return szSound; + } } return pszBaseStepSoundName; } @@ -4466,6 +4571,7 @@ BEGIN_DATADESC( CLogicPlayerProxy ) DEFINE_OUTPUT( m_RequestedPlayerArmor, "PlayerArmor" ), DEFINE_OUTPUT( m_RequestedPlayerAuxPower, "PlayerAuxPower" ), DEFINE_OUTPUT( m_RequestedPlayerFlashBattery, "PlayerFlashBattery" ), + DEFINE_OUTPUT( m_OnPlayerSpawn, "OnPlayerSpawn" ), #endif DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerHealth", InputRequestPlayerHealth ), DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightSlowDrain", InputSetFlashlightSlowDrain ), diff --git a/sp/src/game/server/hl2/hl2_player.h b/sp/src/game/server/hl2/hl2_player.h index 58ad64cd89..89dc16ba34 100644 --- a/sp/src/game/server/hl2/hl2_player.h +++ b/sp/src/game/server/hl2/hl2_player.h @@ -75,6 +75,9 @@ class CSuitPowerDevice else return m_flDrainRate; } +#ifdef MAPBASE + void SetDeviceDrainRate( float flDrainRate ) { m_flDrainRate = flDrainRate; } +#endif }; //============================================================================= @@ -124,6 +127,9 @@ class CHL2_Player : public CBasePlayer virtual void ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ); #ifdef MAPBASE + // For the logic_playerproxy output + void SpawnedAtPoint( CBaseEntity *pSpawnPoint ); + void ResetAnimation( void ); void SetAnimation( PLAYER_ANIM playerAnim ); @@ -334,6 +340,13 @@ class CHL2_Player : public CBasePlayer // HUD HINTS void DisplayLadderHudHint(); +#ifdef MAPBASE + void InitCustomSuitDevice( int iDeviceID, float flDrainRate ); + void AddCustomSuitDevice( int iDeviceID ); + void RemoveCustomSuitDevice( int iDeviceID ); + bool IsCustomSuitDeviceActive( int iDeviceID ); +#endif + CSoundPatch *m_sndLeeches; CSoundPatch *m_sndWaterSplashes; diff --git a/sp/src/game/server/hl2/npc_antlionguard.cpp b/sp/src/game/server/hl2/npc_antlionguard.cpp index 2635f28ca7..9c6dfa3099 100644 --- a/sp/src/game/server/hl2/npc_antlionguard.cpp +++ b/sp/src/game/server/hl2/npc_antlionguard.cpp @@ -2627,8 +2627,15 @@ class CTraceFilterSkipPhysics : public CTraceFilter if ( !pEntity->IsNPC() && pEntity->GetMoveType() == MOVETYPE_VPHYSICS ) { IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject(); +#ifdef MAPBASE + // A MOVETYPE_VPHYSICS object without a VPhysics object is an odd edge case, but it's evidently possible + // since my game crashed after an antlion guard tried to see me through an EP2 jalopy. + // Perhaps that's a sign of an underlying issue? + if ( pPhysics && pPhysics->IsMoveable() && pPhysics->GetMass() < m_minMass ) +#else Assert(pPhysics); if ( pPhysics->IsMoveable() && pPhysics->GetMass() < m_minMass ) +#endif return false; } diff --git a/sp/src/game/server/logicentities.cpp b/sp/src/game/server/logicentities.cpp index fc8d09d0b6..98c2e4bcc2 100644 --- a/sp/src/game/server/logicentities.cpp +++ b/sp/src/game/server/logicentities.cpp @@ -6413,6 +6413,7 @@ bool CMathLightPattern::KeyValue( const char *szKeyName, const char *szValue ) void CMathLightPattern::InputSetStyle( inputdata_t &inputdata ) { m_iszPattern = AllocPooledString(GetDefaultLightstyleString(inputdata.value.Int())); + m_NextLetter = 0; } //----------------------------------------------------------------------------- @@ -6421,6 +6422,7 @@ void CMathLightPattern::InputSetStyle( inputdata_t &inputdata ) void CMathLightPattern::InputSetPattern( inputdata_t &inputdata ) { m_iszPattern = inputdata.value.StringID(); + m_NextLetter = 0; } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/mapbase/GlobalStrings.cpp b/sp/src/game/server/mapbase/GlobalStrings.cpp index e7fb989e11..4abd6f6382 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.cpp +++ b/sp/src/game/server/mapbase/GlobalStrings.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ================== +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================== // // Purpose: See GlobalStrings.h for more information. // diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index 9a0ff4d244..336ff622cd 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ================== +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================== // // Purpose: Shared global string library. // diff --git a/sp/src/game/server/mapbase/SystemConvarMod.cpp b/sp/src/game/server/mapbase/SystemConvarMod.cpp index 001f23475d..727e0c9df7 100644 --- a/sp/src/game/server/mapbase/SystemConvarMod.cpp +++ b/sp/src/game/server/mapbase/SystemConvarMod.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Mostly just Mapbase's convar mod code. // diff --git a/sp/src/game/server/mapbase/SystemConvarMod.h b/sp/src/game/server/mapbase/SystemConvarMod.h index b20707a46f..faaafc27b3 100644 --- a/sp/src/game/server/mapbase/SystemConvarMod.h +++ b/sp/src/game/server/mapbase/SystemConvarMod.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: modifiedconvarts_t from CommentarySystem.cpp moved to a header file so Mapbase can use it. // diff --git a/sp/src/game/server/mapbase/ai_grenade.cpp b/sp/src/game/server/mapbase/ai_grenade.cpp index 387a45fb7d..e14b47a3fe 100644 --- a/sp/src/game/server/mapbase/ai_grenade.cpp +++ b/sp/src/game/server/mapbase/ai_grenade.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index 02e3158adb..7f1ea8c13a 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/mapbase/ai_monitor.cpp b/sp/src/game/server/mapbase/ai_monitor.cpp index ab437c8cba..754ff56d9b 100644 --- a/sp/src/game/server/mapbase/ai_monitor.cpp +++ b/sp/src/game/server/mapbase/ai_monitor.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: An entity that watches an NPC for certain things. // diff --git a/sp/src/game/server/mapbase/ai_weaponmodifier.cpp b/sp/src/game/server/mapbase/ai_weaponmodifier.cpp index bc7eee10fc..2ddec2eac9 100644 --- a/sp/src/game/server/mapbase/ai_weaponmodifier.cpp +++ b/sp/src/game/server/mapbase/ai_weaponmodifier.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Be warned, because this entity is TERRIBLE! // diff --git a/sp/src/game/server/mapbase/closecaption_entity.cpp b/sp/src/game/server/mapbase/closecaption_entity.cpp index dfb33381a5..a166660eb6 100644 --- a/sp/src/game/server/mapbase/closecaption_entity.cpp +++ b/sp/src/game/server/mapbase/closecaption_entity.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/mapbase/datadesc_mod.cpp b/sp/src/game/server/mapbase/datadesc_mod.cpp index 45baeb497d..6ef4c6c063 100644 --- a/sp/src/game/server/mapbase/datadesc_mod.cpp +++ b/sp/src/game/server/mapbase/datadesc_mod.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/mapbase/datadesc_mod.h b/sp/src/game/server/mapbase/datadesc_mod.h index 8d1d297961..e724259b53 100644 --- a/sp/src/game/server/mapbase/datadesc_mod.h +++ b/sp/src/game/server/mapbase/datadesc_mod.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/mapbase/expandedrs_combine.h b/sp/src/game/server/mapbase/expandedrs_combine.h index 5179a7f50e..01206d2ffc 100644 --- a/sp/src/game/server/mapbase/expandedrs_combine.h +++ b/sp/src/game/server/mapbase/expandedrs_combine.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Response system properties (concepts, etc.) shared by npc_combine_s and npc_metropolice // diff --git a/sp/src/game/server/mapbase/func_clientclip.cpp b/sp/src/game/server/mapbase/func_clientclip.cpp index e3fbe58ee9..1bfbf1d722 100644 --- a/sp/src/game/server/mapbase/func_clientclip.cpp +++ b/sp/src/game/server/mapbase/func_clientclip.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: A special brush that collides with clientside entities, primarily ragdolls. // diff --git a/sp/src/game/server/mapbase/func_fake_worldportal.cpp b/sp/src/game/server/mapbase/func_fake_worldportal.cpp new file mode 100644 index 0000000000..3184d2fa3c --- /dev/null +++ b/sp/src/game/server/mapbase/func_fake_worldportal.cpp @@ -0,0 +1,100 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only. +// (basically a combination of point_camera and func_reflective_glass) +// +//===========================================================================// + +#include "cbase.h" +#include "modelentities.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class CFuncFakeWorldPortal : public CFuncBrush +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CFuncFakeWorldPortal, CFuncBrush ); + DECLARE_SERVERCLASS(); + + CFuncFakeWorldPortal() + { + // Equivalent to SKYBOX_2DSKYBOX_VISIBLE, the original sky setting + m_iSkyMode = 2; + } + +public: + + virtual void Spawn( void ) + { + BaseClass::Spawn(); + + if (m_target != NULL_STRING) + { + m_hTargetPlane = gEntList.FindEntityByName( NULL, m_target, this ); + if (!m_hTargetPlane) + Warning("%s: Invalid target plane \"%s\"!\n", GetDebugName(), STRING(m_target)); + } + else + { + Warning("%s: No target plane!\n", GetDebugName()); + } + + if (m_iszFogController != NULL_STRING) + { + m_hFogController = gEntList.FindEntityByName( NULL, m_iszFogController, this ); + if (!m_hFogController) + Warning("%s: Invalid fog controller \"%s\"!\n", GetDebugName(), STRING(m_iszFogController)); + } + } + + // Input handlers + void InputSetTargetPlane( inputdata_t &inputdata ) { m_hTargetPlane = inputdata.value.Entity(); if (m_hTargetPlane) { m_target = m_hTargetPlane->GetEntityName(); } } + void InputSetTargetPlaneAngle( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_PlaneAngles.Init(vec.x, vec.y, vec.z); } + void InputSetSkyMode( inputdata_t &inputdata ) { m_iSkyMode = inputdata.value.Int(); } + void InputSetRenderTarget( inputdata_t &inputdata ) { m_iszRenderTarget = inputdata.value.StringID(); } + void InputSetFogController( inputdata_t &inputdata ) { m_hFogController = inputdata.value.Entity(); if (m_hFogController) { m_iszFogController = m_hFogController->GetEntityName(); } } + +private: + + CNetworkHandle( CBaseEntity, m_hTargetPlane ); + CNetworkQAngle( m_PlaneAngles ); + CNetworkVar( int, m_iSkyMode ); + CNetworkVar( float, m_flScale ); + CNetworkVar( string_t, m_iszRenderTarget ); + + CNetworkHandle( CBaseEntity, m_hFogController ); + string_t m_iszFogController; +}; + +// automatically hooks in the system's callbacks +BEGIN_DATADESC( CFuncFakeWorldPortal ) + + DEFINE_FIELD( m_hTargetPlane, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_PlaneAngles, FIELD_VECTOR, "PlaneAngles" ), + DEFINE_KEYFIELD( m_iSkyMode, FIELD_INTEGER, "SkyMode" ), + DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "scale" ), + DEFINE_KEYFIELD( m_iszRenderTarget, FIELD_STRING, "RenderTarget" ), + DEFINE_FIELD( m_hFogController, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iszFogController, FIELD_STRING, "FogController" ), + + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetTargetPlane", InputSetTargetPlane ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetTargetPlaneAngle", InputSetTargetPlaneAngle ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSkyMode", InputSetSkyMode ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRenderTarget", InputSetRenderTarget ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetFogController", InputSetFogController ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( func_fake_worldportal, CFuncFakeWorldPortal ); + +IMPLEMENT_SERVERCLASS_ST( CFuncFakeWorldPortal, DT_FuncFakeWorldPortal ) + + SendPropEHandle( SENDINFO( m_hTargetPlane ) ), + SendPropVector( SENDINFO( m_PlaneAngles ), -1, SPROP_COORD ), + SendPropInt( SENDINFO( m_iSkyMode ) ), + SendPropFloat( SENDINFO( m_flScale ) ), + SendPropStringT( SENDINFO( m_iszRenderTarget ) ), + SendPropEHandle( SENDINFO( m_hFogController ) ), + +END_SEND_TABLE() diff --git a/sp/src/game/server/mapbase/logic_eventlistener.cpp b/sp/src/game/server/mapbase/logic_eventlistener.cpp index 4935b80e17..8f0f0058f6 100644 --- a/sp/src/game/server/mapbase/logic_eventlistener.cpp +++ b/sp/src/game/server/mapbase/logic_eventlistener.cpp @@ -1,8 +1,10 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ==== +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ==== // -// Attempts to replicate logic_eventlistener from Portal 2. +// Purpose: Source SDK-based replication of logic_eventlistener from later versions +// of Source. // -// This is, in fact, a RECREATION! This is not an actual port. +// This is based entirely on Source 2013 code and Portal 2's FGD entry. +// It does not actually use code from Portal 2 or later. // //============================================================================= diff --git a/sp/src/game/server/mapbase/logic_externaldata.cpp b/sp/src/game/server/mapbase/logic_externaldata.cpp index 89500b6515..3e1a7915a7 100644 --- a/sp/src/game/server/mapbase/logic_externaldata.cpp +++ b/sp/src/game/server/mapbase/logic_externaldata.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/mapbase/logic_register_activator.cpp b/sp/src/game/server/mapbase/logic_register_activator.cpp index dac374237a..79a61daba8 100644 --- a/sp/src/game/server/mapbase/logic_register_activator.cpp +++ b/sp/src/game/server/mapbase/logic_register_activator.cpp @@ -1,8 +1,10 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ==== +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ==== // -// Attempts to replicate logic_register_activator from Portal 2. +// Purpose: Source SDK-based replication of logic_register_activator from later versions +// of Source. // -// This is, in fact, a RECREATION! This is not an actual port. +// This is based entirely on Source 2013 code and Portal 2's FGD entry. +// It does not actually use code from Portal 2 or later. // //============================================================================= diff --git a/sp/src/game/server/mapbase/logic_skill.cpp b/sp/src/game/server/mapbase/logic_skill.cpp index 505e933e7d..2d47a7cb1b 100644 --- a/sp/src/game/server/mapbase/logic_skill.cpp +++ b/sp/src/game/server/mapbase/logic_skill.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Controls and detects difficulty level changes // diff --git a/sp/src/game/server/mapbase/point_advanced_finder.cpp b/sp/src/game/server/mapbase/point_advanced_finder.cpp index 760df59789..27f9f19491 100644 --- a/sp/src/game/server/mapbase/point_advanced_finder.cpp +++ b/sp/src/game/server/mapbase/point_advanced_finder.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: A ballsier version of point_entity_finder. // Originally called logic_entityfinder because a lot of this was written diff --git a/sp/src/game/server/mapbase/point_copy_size.cpp b/sp/src/game/server/mapbase/point_copy_size.cpp index a79be2bfc9..379a65791a 100644 --- a/sp/src/game/server/mapbase/point_copy_size.cpp +++ b/sp/src/game/server/mapbase/point_copy_size.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Copies size. // diff --git a/sp/src/game/server/mapbase/point_damageinfo.cpp b/sp/src/game/server/mapbase/point_damageinfo.cpp index 65d388faca..74e7b40688 100644 --- a/sp/src/game/server/mapbase/point_damageinfo.cpp +++ b/sp/src/game/server/mapbase/point_damageinfo.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: A special entity for afflicting damage as specific as possible. // diff --git a/sp/src/game/server/mapbase/point_entity_replace.cpp b/sp/src/game/server/mapbase/point_entity_replace.cpp index e2f6654dd8..6152d24c8d 100644 --- a/sp/src/game/server/mapbase/point_entity_replace.cpp +++ b/sp/src/game/server/mapbase/point_entity_replace.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Replaces a thing with another thing. // diff --git a/sp/src/game/server/mapbase/point_glow.cpp b/sp/src/game/server/mapbase/point_glow.cpp new file mode 100644 index 0000000000..877aec8342 --- /dev/null +++ b/sp/src/game/server/mapbase/point_glow.cpp @@ -0,0 +1,73 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase off-shoot of tf_glow (created using SDK code only) +// +//============================================================================= + +#include "cbase.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPointGlow : public CPointEntity +{ + DECLARE_CLASS( CPointGlow, CPointEntity ); +public: + + int UpdateTransmitState( void ) { return SetTransmitState( FL_EDICT_ALWAYS ); } + + void Spawn( void ); + + void SetGlowTarget( CBaseEntity *pActivator, CBaseEntity *pCaller ) { m_hGlowTarget = gEntList.FindEntityByName(NULL, m_target, this, pActivator, pCaller); } + + // Inputs + void InputSetTarget( inputdata_t &inputdata ) { BaseClass::InputSetTarget(inputdata); SetGlowTarget( inputdata.pActivator, inputdata.pCaller ); } + + void InputEnable( inputdata_t &inputdata ) { m_bGlowDisabled = false; SetGlowTarget( inputdata.pActivator, inputdata.pCaller ); } + void InputDisable( inputdata_t &inputdata ) { m_bGlowDisabled = true; } + void InputToggle( inputdata_t &inputdata ) { m_bGlowDisabled ? InputEnable(inputdata) : InputDisable(inputdata); } + + void InputSetGlowColor( inputdata_t &inputdata ) { m_GlowColor = inputdata.value.Color32(); } + + CNetworkHandle( CBaseEntity, m_hGlowTarget ); + CNetworkColor32( m_GlowColor ); + CNetworkVar( bool, m_bGlowDisabled ); + + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); +}; + +LINK_ENTITY_TO_CLASS( point_glow, CPointGlow ); + + +BEGIN_DATADESC( CPointGlow ) + + // Keys + DEFINE_KEYFIELD( m_GlowColor, FIELD_COLOR32, "GlowColor" ), + DEFINE_FIELD( m_hGlowTarget, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_bGlowDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + DEFINE_INPUTFUNC( FIELD_COLOR32, "SetGlowColor", InputSetGlowColor ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CPointGlow, DT_PointGlow ) + SendPropEHandle( SENDINFO( m_hGlowTarget ) ), + SendPropInt( SENDINFO( m_GlowColor ), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt ), + SendPropBool( SENDINFO( m_bGlowDisabled ) ), +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointGlow::Spawn() +{ + m_hGlowTarget = gEntList.FindEntityByName( NULL, m_target, this ); + + BaseClass::Spawn(); +} diff --git a/sp/src/game/server/mapbase/point_projectile.cpp b/sp/src/game/server/mapbase/point_projectile.cpp index 9a32176d59..7909c55d1c 100644 --- a/sp/src/game/server/mapbase/point_projectile.cpp +++ b/sp/src/game/server/mapbase/point_projectile.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Fires projectiles. What else is there to say? // diff --git a/sp/src/game/server/mapbase/point_radiation_source.cpp b/sp/src/game/server/mapbase/point_radiation_source.cpp index fca0f1b315..febe0c1ad8 100644 --- a/sp/src/game/server/mapbase/point_radiation_source.cpp +++ b/sp/src/game/server/mapbase/point_radiation_source.cpp @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ==== +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ==== // // An entity that triggers the player's geiger counter. // @@ -124,7 +124,11 @@ void CPointRadiationSource::RadiationThink( void ) float flRange = pPlayer->GetAbsOrigin().DistTo((GetAbsOrigin())); if (m_flRadius <= 0 || flRange < m_flRadius) { - Assert( m_flIntensity > 0 ); + if (m_flIntensity == 0) + { + Warning("%s: INTENSITY IS ZERO!!! Can't notify of radiation\n", GetDebugName()); + return; + } //flRange *= 3.0f; flRange /= m_flIntensity; diff --git a/sp/src/game/server/mapbase/variant_tools.h b/sp/src/game/server/mapbase/variant_tools.h index 358b232de1..21e631fb2d 100644 --- a/sp/src/game/server/mapbase/variant_tools.h +++ b/sp/src/game/server/mapbase/variant_tools.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: // diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index f7a3daad79..f91a0e899e 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -159,6 +159,19 @@ BEGIN_DATADESC(CRagdollProp) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CRagdollProp, CBaseAnimating, "Ragdoll physics prop." ) + + DEFINE_SCRIPTFUNC( GetSourceClassName, "Gets the ragdoll's source classname." ) + DEFINE_SCRIPTFUNC( SetSourceClassName, "Sets the ragdoll's source classname." ) + DEFINE_SCRIPTFUNC( HasPhysgunInteraction, "Checks if the ragdoll has the specified interaction." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObject, "GetRagdollObject", "Gets the ragdoll object of the specified index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObjectCount, "GetRagdollObjectCount", "Gets the number of ragdoll objects on this ragdoll." ) + +END_SCRIPTDESC() +#endif + //----------------------------------------------------------------------------- // Disable auto fading under dx7 or when level fades are specified //----------------------------------------------------------------------------- @@ -1803,6 +1816,24 @@ void CRagdollProp::InputFadeAndRemove( inputdata_t &inputdata ) FadeOut( 0.0f, flFadeDuration ); } +#ifdef MAPBASE_VSCRIPT +HSCRIPT CRagdollProp::ScriptGetRagdollObject( int iIndex ) +{ + if (iIndex < 0 || iIndex > m_ragdoll.listCount) + { + Warning("%s GetRagdollObject: Index %i not valid (%i objects)\n", GetDebugName(), iIndex, m_ragdoll.listCount); + return NULL; + } + + return g_pScriptVM->RegisterInstance( m_ragdoll.list[iIndex].pObject ); +} + +int CRagdollProp::ScriptGetRagdollObjectCount() +{ + return m_ragdoll.listCount; +} +#endif + void Ragdoll_GetAngleOverrideString( char *pOut, int size, CBaseEntity *pEntity ) { CRagdollProp *pRagdoll = dynamic_cast(pEntity); diff --git a/sp/src/game/server/physics_prop_ragdoll.h b/sp/src/game/server/physics_prop_ragdoll.h index 5f33dd0c7c..0a2a14b053 100644 --- a/sp/src/game/server/physics_prop_ragdoll.h +++ b/sp/src/game/server/physics_prop_ragdoll.h @@ -22,6 +22,9 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics { DECLARE_CLASS( CRagdollProp, CBaseAnimating ); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: CRagdollProp( void ); @@ -59,6 +62,9 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics virtual IResponseSystem *GetResponseSystem(); virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set ); void SetSourceClassName( const char *pClassname ); +#ifdef MAPBASE + const char *GetSourceClassName() { return STRING( m_strSourceClassName ); } +#endif // Physics attacker virtual CBasePlayer *HasPhysicsAttacker( float dt ); @@ -112,6 +118,11 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics void InputTurnOff( inputdata_t &inputdata ); void InputFadeAndRemove( inputdata_t &inputdata ); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetRagdollObject( int iIndex ); + int ScriptGetRagdollObjectCount(); +#endif + DECLARE_DATADESC(); protected: diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index a48650a531..f193ddca73 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -3797,13 +3797,12 @@ void CBasePlayer::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) #ifdef MAPBASE_VSCRIPT // Movement hook for VScript - if ( m_ScriptScope.IsInitialized() ) + if ( HSCRIPT hFunc = LookupScriptFunction("PlayerRunCommand") ) { - CUserCmdAccessor accessor = CUserCmdAccessor( ucmd ); - HSCRIPT hCmd = g_pScriptVM->RegisterInstance( &accessor ); + HSCRIPT hCmd = g_pScriptVM->RegisterInstance( ucmd ); g_pScriptVM->SetValue( "command", hCmd ); - CallScriptFunction( "PlayerRunCommand", NULL ); + CallScriptFunctionHandle( hFunc, NULL ); g_pScriptVM->ClearValue( "command" ); g_pScriptVM->RemoveInstance( hCmd ); @@ -5144,7 +5143,12 @@ void CBasePlayer::Spawn( void ) if ( !m_fGameHUDInitialized ) g_pGameRules->SetDefaultPlayerTeam( this ); +#ifdef MAPBASE + CBaseEntity *pSpawnPoint = g_pGameRules->GetPlayerSpawnSpot( this ); + SpawnedAtPoint( pSpawnPoint ); +#else g_pGameRules->GetPlayerSpawnSpot( this ); +#endif m_Local.m_bDucked = false;// This will persist over round restart if you hold duck otherwise. m_Local.m_bDucking = false; diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index d185aa6235..885bdf05f3 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -294,6 +294,11 @@ class CBasePlayer : public CBaseCombatCharacter virtual void SharedSpawn(); // Shared between client and server. virtual void ForceRespawn( void ); +#ifdef MAPBASE + // For the logic_playerproxy output + virtual void SpawnedAtPoint( CBaseEntity *pSpawnPoint ) {} +#endif + virtual void InitialSpawn( void ); virtual void InitHUD( void ) {} virtual void ShowViewPortPanel( const char * name, bool bShow = true, KeyValues *data = NULL ); diff --git a/sp/src/game/server/point_camera.cpp b/sp/src/game/server/point_camera.cpp index c900be8f0c..626bc61b07 100644 --- a/sp/src/game/server/point_camera.cpp +++ b/sp/src/game/server/point_camera.cpp @@ -54,6 +54,8 @@ CPointCamera::CPointCamera() #ifdef MAPBASE // Equivalent to SKYBOX_2DSKYBOX_VISIBLE, the original sky setting m_iSkyMode = 2; + + m_iszRenderTarget = AllocPooledString( "_rt_Camera" ); #endif g_PointCameraList.Insert( this ); @@ -192,6 +194,13 @@ void CPointCamera::InputSetOnAndTurnOthersOff( inputdata_t &inputdata ) while ((pEntity = gEntList.FindEntityByClassname( pEntity, "point_camera" )) != NULL) { CPointCamera *pCamera = (CPointCamera*)pEntity; + +#ifdef MAPBASE + // Do not turn off cameras which use different render targets + if (pCamera->m_iszRenderTarget.Get() != m_iszRenderTarget.Get()) + continue; +#endif + pCamera->InputSetOff( inputdata ); } @@ -229,6 +238,7 @@ BEGIN_DATADESC( CPointCamera ) DEFINE_KEYFIELD( m_bUseScreenAspectRatio, FIELD_BOOLEAN, "UseScreenAspectRatio" ), #ifdef MAPBASE DEFINE_KEYFIELD( m_iSkyMode, FIELD_INTEGER, "SkyMode" ), + DEFINE_KEYFIELD( m_iszRenderTarget, FIELD_STRING, "RenderTarget" ), #endif DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ), @@ -247,6 +257,7 @@ BEGIN_DATADESC( CPointCamera ) DEFINE_INPUTFUNC( FIELD_VOID, "SetOff", InputSetOff ), #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSkyMode", InputSetSkyMode ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetRenderTarget", InputSetRenderTarget ), #endif END_DATADESC() @@ -263,6 +274,7 @@ IMPLEMENT_SERVERCLASS_ST( CPointCamera, DT_PointCamera ) SendPropInt( SENDINFO( m_bUseScreenAspectRatio ), 1, SPROP_UNSIGNED ), #ifdef MAPBASE SendPropInt( SENDINFO( m_iSkyMode ) ), + SendPropStringT( SENDINFO( m_iszRenderTarget ) ), #endif END_SEND_TABLE() diff --git a/sp/src/game/server/point_camera.h b/sp/src/game/server/point_camera.h index 4056190eb0..c669ab82cd 100644 --- a/sp/src/game/server/point_camera.h +++ b/sp/src/game/server/point_camera.h @@ -39,6 +39,7 @@ class CPointCamera : public CBaseEntity void InputSetOff( inputdata_t &inputdata ); #ifdef MAPBASE void InputSetSkyMode( inputdata_t &inputdata ) { m_iSkyMode = inputdata.value.Int(); } + void InputSetRenderTarget( inputdata_t &inputdata ) { m_iszRenderTarget = inputdata.value.StringID(); } float GetFOV() const { return m_FOV; } #endif @@ -58,6 +59,7 @@ class CPointCamera : public CBaseEntity CNetworkVar( bool, m_bUseScreenAspectRatio ); #ifdef MAPBASE CNetworkVar( int, m_iSkyMode ); + CNetworkVar( string_t, m_iszRenderTarget ); #endif // Allows the mapmaker to control whether a camera is active or not diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 5bfc6178e3..6a0cb6138e 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -8,7 +8,8 @@ $Configuration { $Compiler { - $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS" + $PreprocessorDefinitions "$BASE;ASW_PROJECTED_TEXTURES;DYNAMIC_RTT_SHADOWS;GLOWS_ENABLE" + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } @@ -21,6 +22,7 @@ $Project $File "env_projectedtexture.h" $File "env_global_light.cpp" $File "skyboxswapper.cpp" + $File "env_instructor_hint.cpp" $Folder "Mapbase" { @@ -36,6 +38,7 @@ $Project $File "$SRCDIR\game\shared\mapbase\vscript_funcs_math.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_funcs_math.h" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_consts_shared.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\ai_grenade.cpp" $File "mapbase\ai_grenade.h" @@ -46,6 +49,7 @@ $Project $File "mapbase\datadesc_mod.h" $File "mapbase\expandedrs_combine.h" $File "mapbase\func_clientclip.cpp" + $File "mapbase\func_fake_worldportal.cpp" $File "mapbase\GlobalStrings.cpp" $File "mapbase\GlobalStrings.h" $File "mapbase\logic_externaldata.cpp" @@ -57,6 +61,7 @@ $Project //$File "mapbase\point_physics_control.cpp" // Backlogged $File "mapbase\point_projectile.cpp" $File "mapbase\point_radiation_source.cpp" + $File "mapbase\point_glow.cpp" $File "mapbase\SystemConvarMod.cpp" $File "mapbase\SystemConvarMod.h" $File "mapbase\variant_tools.h" diff --git a/sp/src/game/server/soundent.cpp b/sp/src/game/server/soundent.cpp index 571e66d105..59273206d3 100644 --- a/sp/src/game/server/soundent.cpp +++ b/sp/src/game/server/soundent.cpp @@ -45,6 +45,31 @@ BEGIN_SIMPLE_DATADESC( CSound ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CSound, "A sound NPCs can hear." ) + + DEFINE_SCRIPTFUNC( DoesSoundExpire, "Returns true if the sound expires." ) + DEFINE_SCRIPTFUNC( SoundExpirationTime, "Gets the sound's expiration time." ) + DEFINE_SCRIPTFUNC( SetSoundOrigin, "Sets the sound's origin." ) + DEFINE_SCRIPTFUNC( GetSoundOrigin, "Gets the sound's origin." ) + DEFINE_SCRIPTFUNC( GetSoundReactOrigin, "Gets the sound's react origin." ) + DEFINE_SCRIPTFUNC_NAMED( FIsSound, "IsSound", "Returns true if this is a type of sound (as opposed to a scent)." ) + DEFINE_SCRIPTFUNC_NAMED( FIsScent, "IsScent", "Returns true if this is a type of scent (as opposed to a sound)." ) + DEFINE_SCRIPTFUNC( IsSoundType, "Returns true if the sound type is the specified type." ) + DEFINE_SCRIPTFUNC( SoundType, "Gets the raw sound type." ) + DEFINE_SCRIPTFUNC( SoundContext, "Gets the sound type with contexts only." ) + DEFINE_SCRIPTFUNC( SoundTypeNoContext, "Gets the sound type with contexts excluded." ) + DEFINE_SCRIPTFUNC( Volume, "Gets the sound's volume." ) + DEFINE_SCRIPTFUNC( OccludedVolume, "Gets the sound's occluded volume." ) + DEFINE_SCRIPTFUNC( Reset, "Clears the volume, type, and origin for the sound without actually removing it." ) + DEFINE_SCRIPTFUNC( SoundChannel, "Gets the sound's channel." ) + DEFINE_SCRIPTFUNC( ValidateOwner, "Returns true if the sound's owner is still valid or if the sound never had an owner in the first place." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOwner, "GetOwner", "Gets the sound's owner." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetTarget, "GetTarget", "Gets the sound's target." ) + +END_SCRIPTDESC(); +#endif + //========================================================= // CSound - Clear - zeros all fields for a sound diff --git a/sp/src/game/server/soundent.h b/sp/src/game/server/soundent.h index b566aeae8a..7ce3d4018b 100644 --- a/sp/src/game/server/soundent.h +++ b/sp/src/game/server/soundent.h @@ -136,6 +136,12 @@ class CSound int SoundChannel( void ) const; bool ValidateOwner() const; +#ifdef MAPBASE_VSCRIPT + // For VScript functions + HSCRIPT ScriptGetOwner() const { return ToHScript( m_hOwner ); } + HSCRIPT ScriptGetTarget() const { return ToHScript( m_hTarget ); } +#endif + EHANDLE m_hOwner; // sound's owner EHANDLE m_hTarget; // Sounds's target - an odd concept. For a gunfire sound, the target is the entity being fired at int m_iVolume; // how loud the sound is diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index 9626842893..4a76ec9082 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -578,9 +578,11 @@ bool VScriptServerInit() #ifdef MAPBASE_VSCRIPT g_pScriptVM->RegisterAllClasses(); + g_pScriptVM->RegisterAllEnums(); IGameSystem::RegisterVScriptAllSystems(); + RegisterSharedScriptConstants(); RegisterSharedScriptFunctions(); #endif diff --git a/sp/src/game/shared/ai_activity.h b/sp/src/game/shared/ai_activity.h index 7a8d28978b..44b8f62ddf 100644 --- a/sp/src/game/shared/ai_activity.h +++ b/sp/src/game/shared/ai_activity.h @@ -2153,6 +2153,7 @@ typedef enum ACT_IDLE_AR2_RELAXED, ACT_IDLE_AR2_STIMULATED, + ACT_WALK_AR2_RELAXED, ACT_RUN_AR2_RELAXED, ACT_WALK_AR2_STIMULATED, diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index 2778cc5692..fecf60d2b6 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -444,6 +444,15 @@ bool CBaseEntity::KeyValue( const char *szKeyName, const char *szValue ) return true; } +#ifdef MAPBASE + if ( FStrEq( szKeyName, "eflags" ) ) + { + // Can't use DEFINE_KEYFIELD since eflags might be set before KV are parsed + AddEFlags( atoi( szValue ) ); + return true; + } +#endif + #ifdef GAME_DLL if ( FStrEq( szKeyName, "targetname" ) ) @@ -1604,23 +1613,21 @@ typedef CTraceFilterSimpleList CBulletsTraceFilter; void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) { #if defined(MAPBASE_VSCRIPT) && defined(GAME_DLL) - if (m_ScriptScope.IsInitialized()) + if (HSCRIPT hFunc = LookupScriptFunction("FireBullets")) { - CFireBulletsInfoAccessor pInfo( const_cast(&info) ); - HSCRIPT hInfo = g_pScriptVM->RegisterInstance( &pInfo ); + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); g_pScriptVM->SetValue( "info", hInfo ); ScriptVariant_t functionReturn; - if ( CallScriptFunction( "FireBullets", &functionReturn ) ) - { - if (!functionReturn.m_bool) - return; - } + CallScriptFunctionHandle( hFunc, &functionReturn ); g_pScriptVM->RemoveInstance( hInfo ); g_pScriptVM->ClearValue( "info" ); + + if (!functionReturn.m_bool) + return; } #endif diff --git a/sp/src/game/shared/gamemovement.cpp b/sp/src/game/shared/gamemovement.cpp index de12a80067..d77d0c8926 100644 --- a/sp/src/game/shared/gamemovement.cpp +++ b/sp/src/game/shared/gamemovement.cpp @@ -55,6 +55,10 @@ ConVar player_limit_jump_speed( "player_limit_jump_speed", "1", FCVAR_REPLICATED // duck controls. Its value is meaningless anytime we don't have the options window open. ConVar option_duck_method("option_duck_method", "1", FCVAR_REPLICATED|FCVAR_ARCHIVE );// 0 = HOLD to duck, 1 = Duck is a toggle +#ifdef MAPBASE +ConVar player_crouch_multiplier( "player_crouch_multiplier", "0.33333333", FCVAR_NONE ); +#endif + #ifdef STAGING_ONLY #ifdef CLIENT_DLL ConVar debug_latch_reset_onduck( "debug_latch_reset_onduck", "1", FCVAR_CHEAT ); @@ -4303,7 +4307,8 @@ void CGameMovement::HandleDuckingSpeedCrop( void ) { if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) && ( player->GetFlags() & FL_DUCKING ) && ( player->GetGroundEntity() != NULL ) ) { - float frac = 0.33333333f; + // Mapbase makes this an adjustable convar + float frac = player_crouch_multiplier.GetFloat(); mv->m_flForwardMove *= frac; mv->m_flSideMove *= frac; mv->m_flUpMove *= frac; diff --git a/sp/src/game/shared/gamerules.h b/sp/src/game/shared/gamerules.h index 384d5b4ec1..9ae672b6ba 100644 --- a/sp/src/game/shared/gamerules.h +++ b/sp/src/game/shared/gamerules.h @@ -298,6 +298,10 @@ abstract_class CGameRules : public CAutoGameSystemPerFrame virtual CBaseEntity *GetPlayerSpawnSpot( CBasePlayer *pPlayer );// Place this player on their spawnspot and face them the proper direction. virtual bool IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer ); +#ifdef MAPBASE + virtual bool AllowSPRespawn() { return false; } +#endif + virtual bool AllowAutoTargetCrosshair( void ) { return TRUE; }; virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args ); // handles the user commands; returns TRUE if command handled properly virtual void ClientSettingsChanged( CBasePlayer *pPlayer ); // the player has changed cvars diff --git a/sp/src/game/shared/hl2/hl2_gamerules.cpp b/sp/src/game/shared/hl2/hl2_gamerules.cpp index 50445f204e..d805c8f8b5 100644 --- a/sp/src/game/shared/hl2/hl2_gamerules.cpp +++ b/sp/src/game/shared/hl2/hl2_gamerules.cpp @@ -49,6 +49,7 @@ BEGIN_DATADESC( CHalfLife2Proxy ) DEFINE_FIELD( m_save_LegacyFlashlight, FIELD_CHARACTER ), DEFINE_FIELD( m_save_PlayerSquadAutosummonDisabled, FIELD_BOOLEAN ), DEFINE_FIELD( m_save_StunstickPickupBehavior, FIELD_INTEGER ), + DEFINE_FIELD( m_save_AllowSPRespawn, FIELD_BOOLEAN ), // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "EpisodicOn", InputEpisodicOn ), @@ -60,6 +61,7 @@ BEGIN_DATADESC( CHalfLife2Proxy ) DEFINE_INPUTFUNC( FIELD_STRING, "SetLegacyFlashlight", InputSetLegacyFlashlight ), DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerSquadAutosummon", InputSetPlayerSquadAutosummon ), DEFINE_INPUTFUNC( FIELD_STRING, "SetStunstickPickupBehavior", InputSetStunstickPickupBehavior ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetAllowSPRespawn", InputSetAllowSPRespawn ), END_DATADESC() #endif @@ -75,6 +77,7 @@ void CHalfLife2Proxy::InputSetDefaultCitizenType( inputdata_t &inputdata ) { Key void CHalfLife2Proxy::InputSetLegacyFlashlight( inputdata_t &inputdata ) { KeyValue("SetLegacyFlashlight", inputdata.value.String()); } void CHalfLife2Proxy::InputSetPlayerSquadAutosummon( inputdata_t &inputdata ) { KeyValue("SetPlayerSquadAutosummon", inputdata.value.String()); } void CHalfLife2Proxy::InputSetStunstickPickupBehavior( inputdata_t &inputdata ) { KeyValue("SetStunstickPickupBehavior", inputdata.value.String()); } +void CHalfLife2Proxy::InputSetAllowSPRespawn( inputdata_t &inputdata ) { KeyValue( "SetAllowSPRespawn", inputdata.value.String() ); } //----------------------------------------------------------------------------- // Purpose: Cache user entity field values until spawn is called. @@ -129,6 +132,10 @@ bool CHalfLife2Proxy::KeyValue( const char *szKeyName, const char *szValue ) { HL2GameRules()->SetStunstickPickupBehavior(atoi(szValue)); } + else if (FStrEq(szKeyName, "SetAllowSPRespawn")) + { + HL2GameRules()->SetAllowSPRespawn(!FStrEq(szValue, "0")); + } else { return BaseClass::KeyValue( szKeyName, szValue ); @@ -155,10 +162,18 @@ bool CHalfLife2Proxy::GetKeyValue( const char *szKeyName, char *szValue, int iMa { Q_snprintf( szValue, iMaxLen, "%d", g_bUseLegacyFlashlight ); } + else if (FStrEq(szKeyName, "SetPlayerSquadAutosummon")) + { + Q_snprintf( szValue, iMaxLen, "%d", HL2GameRules()->AutosummonDisabled() ); + } else if (FStrEq(szKeyName, "SetStunstickPickupBehavior")) { Q_snprintf( szValue, iMaxLen, "%i", HL2GameRules()->GetStunstickPickupBehavior() ); } + else if (FStrEq(szKeyName, "SetAllowSPRespawn")) + { + Q_snprintf( szValue, iMaxLen, "%d", HL2GameRules()->AllowSPRespawn() ); + } else { return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); @@ -184,6 +199,8 @@ int CHalfLife2Proxy::Save( ISave &save ) m_save_StunstickPickupBehavior = HL2GameRules()->GetStunstickPickupBehavior(); + m_save_AllowSPRespawn = HL2GameRules()->AllowSPRespawn(); + return BaseClass::Save(save); } @@ -211,6 +228,8 @@ int CHalfLife2Proxy::Restore( IRestore &restore ) HL2GameRules()->SetStunstickPickupBehavior(m_save_StunstickPickupBehavior); + HL2GameRules()->SetAllowSPRespawn(m_save_AllowSPRespawn); + return base; } @@ -453,6 +472,7 @@ ConVar alyx_darkness_force( "alyx_darkness_force", "0", FCVAR_CHEAT | FCVAR_REP #ifdef MAPBASE m_DefaultCitizenType = 0; m_bPlayerSquadAutosummonDisabled = false; + m_bAllowSPRespawn = false; #endif } @@ -2047,6 +2067,22 @@ void CHalfLife2::SetStunstickPickupBehavior(int val) m_StunstickPickupBehavior = val; } +//----------------------------------------------------------------------------- +// Gets our SP respawn setting. +//----------------------------------------------------------------------------- +bool CHalfLife2::AllowSPRespawn() +{ + return m_bAllowSPRespawn; +} + +//----------------------------------------------------------------------------- +// Sets our SP respawn setting. +//----------------------------------------------------------------------------- +void CHalfLife2::SetAllowSPRespawn( bool toggle ) +{ + m_bAllowSPRespawn = toggle; +} + //BEGIN_SIMPLE_DATADESC( CHalfLife2 ) // // DEFINE_FIELD( m_DefaultCitizenType, FIELD_INTEGER ), diff --git a/sp/src/game/shared/hl2/hl2_gamerules.h b/sp/src/game/shared/hl2/hl2_gamerules.h index 26d628080a..a8eace12d4 100644 --- a/sp/src/game/shared/hl2/hl2_gamerules.h +++ b/sp/src/game/shared/hl2/hl2_gamerules.h @@ -46,6 +46,7 @@ class CHalfLife2Proxy : public CGameRulesProxy void InputSetLegacyFlashlight( inputdata_t &inputdata ); void InputSetPlayerSquadAutosummon( inputdata_t &inputdata ); void InputSetStunstickPickupBehavior( inputdata_t &inputdata ); + void InputSetAllowSPRespawn( inputdata_t &inputdata ); // Gamerules classes don't seem to support datadescs, so the hl2_gamerules entity takes the current values // from the actual gamerules and saves them in the entity itself, where they're saved via the entity's own datadesc. @@ -54,6 +55,7 @@ class CHalfLife2Proxy : public CGameRulesProxy char m_save_LegacyFlashlight; bool m_save_PlayerSquadAutosummonDisabled; int m_save_StunstickPickupBehavior; + bool m_save_AllowSPRespawn; DECLARE_DATADESC(); #endif @@ -136,6 +138,9 @@ class CHalfLife2 : public CSingleplayRules int GetStunstickPickupBehavior(); void SetStunstickPickupBehavior(int val); + + virtual bool AllowSPRespawn(); + void SetAllowSPRespawn( bool toggle ); #endif private: @@ -147,6 +152,7 @@ class CHalfLife2 : public CSingleplayRules int m_DefaultCitizenType; bool m_bPlayerSquadAutosummonDisabled; int m_StunstickPickupBehavior; + bool m_bAllowSPRespawn; #endif void AdjustPlayerDamageTaken( CTakeDamageInfo *pInfo ); diff --git a/sp/src/game/shared/mapbase/MapEdit.cpp b/sp/src/game/shared/mapbase/MapEdit.cpp index 21ef92685f..326599648a 100644 --- a/sp/src/game/shared/mapbase/MapEdit.cpp +++ b/sp/src/game/shared/mapbase/MapEdit.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: The flimsy MapEdit system that was // heavily inspired by Synergy's MapEdit, completely based on the Commentary System diff --git a/sp/src/game/shared/mapbase/MapEdit.h b/sp/src/game/shared/mapbase/MapEdit.h index e832f34828..899f6712ff 100644 --- a/sp/src/game/shared/mapbase/MapEdit.h +++ b/sp/src/game/shared/mapbase/MapEdit.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Accessing MapEdit // diff --git a/sp/src/game/shared/mapbase/mapbase_game_log.cpp b/sp/src/game/shared/mapbase/mapbase_game_log.cpp index c83adee040..c51ab0c4e2 100644 --- a/sp/src/game/shared/mapbase/mapbase_game_log.cpp +++ b/sp/src/game/shared/mapbase/mapbase_game_log.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: A special system designed to record game information for map testing. // diff --git a/sp/src/game/shared/mapbase/mapbase_rpc.cpp b/sp/src/game/shared/mapbase/mapbase_rpc.cpp index 4a318c7ce4..ecbb36cad5 100644 --- a/sp/src/game/shared/mapbase/mapbase_rpc.cpp +++ b/sp/src/game/shared/mapbase/mapbase_rpc.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Mapbase's RPC implementation. // @@ -400,7 +400,7 @@ void MapbaseRPC_Update( int iRPCMask, int iType, const char *pMapName ) void MapbaseRPC_UpdateSteam( int iType, const char *pMapName ) { // No Steam - if (!steamapicontext) + if (!steamapicontext || !steamapicontext->SteamFriends()) return; const char *pszStatus = NULL; diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 2088872d9f..583248b1a7 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Carries the Mapbase CAutoGameSystem that loads manifest among other things. // Also includes code that does not fit anywhere else. @@ -38,11 +38,6 @@ #define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", g_MapName) #endif -// The soundscape system does not operate in a way that supports soundscape loading after map initialization. -// I have decided to make a direct change to the system that loads "maps/%mapname%_soundscapes.txt" instead and suggest the usage of #include and #base to users. -// Sorry. It's not broken, so I won't fix it. -#define MAPBASE_SOUNDSCAPE_SUPPORT 0 - const char *g_MapName; extern ISoundEmitterSystemBase *soundemitterbase; @@ -53,16 +48,14 @@ ConVar mapbase_load_soundscripts("mapbase_load_soundscripts", "1", FCVAR_ARCHIVE //ConVar mapbase_load_propdata("mapbase_load_propdata", "1", FCVAR_ARCHIVE, "Should we load map-specific propdata files? e.g. \"maps/mapname_propdata.txt\""); -#if MAPBASE_SOUNDSCAPE_SUPPORT -ConVar mapbase_load_soundscapes("mapbase_load_soundscapes", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscapes? e.g. \"maps/mapname_soundscapes.txt\""); -#endif +//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\""); -ConVar mapbase_load_localization("mapbase_load_localization", "1", FCVAR_ARCHIVE, "Should we load map-specific localized text files? e.g. \"maps/mapname_english.txt\""); - #else ConVar mapbase_load_sentences("mapbase_load_sentences", "1", FCVAR_ARCHIVE, "Should we load map-specific sentences? e.g. \"maps/mapname_sentences.txt\""); @@ -96,12 +89,10 @@ enum { MANIFEST_SOUNDSCRIPTS, //MANIFEST_PROPDATA, -#if MAPBASE_SOUNDSCAPE_SUPPORT - MANIFEST_SOUNDSCAPES, -#endif + //MANIFEST_SOUNDSCAPES, + MANIFEST_LOCALIZATION, #ifdef CLIENT_DLL //MANIFEST_CLOSECAPTION, - MANIFEST_LOCALIZATION, MANIFEST_VGUI, #else MANIFEST_TALKER, @@ -124,12 +115,10 @@ struct ManifestType_t static const ManifestType_t gm_szManifestFileStrings[MANIFEST_NUM_TYPES] = { { "soundscripts", &mapbase_load_soundscripts }, //{ "propdata", &mapbase_load_propdata }, -#if MAPBASE_SOUNDSCAPE_SUPPORT - { "soundscapes", &mapbase_load_soundscapes }, -#endif + //{ "soundscapes", &mapbase_load_soundscapes }, + { "localization", &mapbase_load_localization }, #ifdef CLIENT_DLL //{ "closecaption", &mapbase_load_cc }, - { "localization", &mapbase_load_localization }, { "vgui", NULL }, #else { "talker", &mapbase_load_talker }, @@ -152,7 +141,31 @@ class CMapbaseSystem : public CAutoGameSystem inline bool GetGameInfoKeyValues(KeyValues *pKeyValues) { - return pKeyValues->LoadFromFile( filesystem, "gameinfo.txt", "GAME" ); + return pKeyValues->LoadFromFile( filesystem, "gameinfo.txt", "MOD" ); + } + + virtual bool Init() + { + // Checks gameinfo.txt for additional command line options + KeyValues *gameinfo = new KeyValues("GameInfo"); + if (GetGameInfoKeyValues(gameinfo)) + { + KeyValues *pCommandLineList = gameinfo->FindKey("CommandLine", false); + if (pCommandLineList) + { + for (KeyValues *pKey = pCommandLineList->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) + { + CommandLine()->AppendParm( pKey->GetName(), pKey->GetString() ); + } + } + } + gameinfo->deleteThis(); + +#ifdef CLIENT_DLL + InitializeRTs(); +#endif + + return true; } virtual void LevelInitPreEntity() @@ -193,17 +206,17 @@ class CMapbaseSystem : public CAutoGameSystem #else //char mapname[128]; //Q_StripExtension(MapName(), mapname, sizeof(mapname)); - g_MapName = MapName() - 4; // Removes ".bsp" + g_MapName = MapName(); +#endif // Shared Mapbase localization file g_pVGuiLocalize->AddFile( "resource/mapbase_%language%.txt" ); -#endif } virtual void LevelInitPostEntity() { // Check for a generic "mapname_manifest.txt" file and load it. - if (filesystem->FileExists( AUTOLOADED_MANIFEST_FILE, "GAME" ) /*&& !FStrEq(name, "closecaption")*/) + if (filesystem->FileExists( AUTOLOADED_MANIFEST_FILE, "GAME" )) { AddManifestFile( AUTOLOADED_MANIFEST_FILE ); } @@ -231,6 +244,61 @@ class CMapbaseSystem : public CAutoGameSystem g_MapName = NULL; } +#ifdef CLIENT_DLL + bool m_bInitializedRTs = false; + CUtlVector m_CameraTextures; + + //----------------------------------------------------------------------------- + // Initialize custom RT textures if necessary + //----------------------------------------------------------------------------- + void InitializeRTs() + { + if (!m_bInitializedRTs) + { + int iNumCameras = CommandLine()->ParmValue( "-numcameratextures", 3 ); + + materials->BeginRenderTargetAllocation(); + + for (int i = 0; i < iNumCameras; i++) + { + char szName[32]; + Q_snprintf( szName, sizeof(szName), "_rt_Camera%i", i ); + + int iRefIndex = m_CameraTextures.AddToTail(); + + //m_CameraTextures[iRefIndex].InitRenderTarget( + // 256, 256, RT_SIZE_DEFAULT, + // g_pMaterialSystem->GetBackBufferFormat(), + // MATERIAL_RT_DEPTH_SHARED, true, szName ); + + m_CameraTextures[iRefIndex].Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2( + szName, + 256, 256, RT_SIZE_DEFAULT, + g_pMaterialSystem->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_SHARED, + 0, + CREATERENDERTARGETFLAGS_HDR ) ); + } + + materials->EndRenderTargetAllocation(); + + m_bInitializedRTs = true; + } + } + + void Shutdown() + { + if (m_bInitializedRTs) + { + for (int i = 0; i < m_CameraTextures.Count(); i++) + { + m_CameraTextures[i].Shutdown(); + } + m_bInitializedRTs = false; + } + } +#endif + // Get a generic, hardcoded manifest with hardcoded names. void ParseGenericManifest() { @@ -266,7 +334,7 @@ class CMapbaseSystem : public CAutoGameSystem void LoadFromValue( const char *value, int type, bool bDontWarn ) { - if (!filesystem->FileExists(value, "GAME") /*&& !FStrEq(name, "closecaption")*/) + if (!filesystem->FileExists(value, "MOD")) { if (!bDontWarn) { @@ -279,21 +347,17 @@ class CMapbaseSystem : public CAutoGameSystem { case MANIFEST_SOUNDSCRIPTS: { soundemitterbase->AddSoundOverrides(value); } break; //case MANIFEST_PROPDATA: { g_PropDataSystem.ParsePropDataFile(value); } break; + case MANIFEST_LOCALIZATION: { g_pVGuiLocalize->AddFile( value, "MOD", true ); } break; #ifdef CLIENT_DLL //case MANIFEST_CLOSECAPTION: { todo } break; - case MANIFEST_LOCALIZATION: { g_pVGuiLocalize->AddFile( value, "GAME", true ); } break; case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break; -#if MAPBASE_SOUNDSCAPE_SUPPORT - case MANIFEST_SOUNDSCAPES: { Soundscape_AddFile(value); } break; -#endif + //case MANIFEST_SOUNDSCAPES: { Soundscape_AddFile(value); } break; #else case MANIFEST_TALKER: { g_bMapContainsCustomTalker = true; LoadResponseSystemFile(value); //PrecacheCustomResponseSystem( value ); } break; -#if MAPBASE_SOUNDSCAPE_SUPPORT - case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break; -#endif + //case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break; case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break; case MANIFEST_ACTBUSY: { ParseCustomActbusyFile(value); } break; #endif @@ -303,50 +367,47 @@ class CMapbaseSystem : public CAutoGameSystem // This doesn't call deleteThis()! void AddManifestFile(KeyValues *pKV, bool bDontWarn = false) { - const char *name; char value[MAX_PATH]; - char *szToken; - bool inparam = false; + const char *name; for (KeyValues *pKey = pKV->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) { + value[0] = '\0'; name = pKey->GetName(); // Parse %mapname%, etc. - inparam = false; - value[0] = '\0'; - szToken = strtok(strdup(pKey->GetString()), "%"); // Was tempvalue - while (szToken != NULL) + bool inparam = false; + CUtlStringList outStrings; + V_SplitString( pKey->GetString(), "%", outStrings ); + FOR_EACH_VEC( outStrings, i ) { if (inparam) { - const char *append = szToken; - if (FStrEq(szToken, "mapname")) - append = g_MapName; - //else if (FStrEq(szToken, "mapdir")) - //{ - // // TODO - //} -#ifdef CLIENT_DLL - else if (FStrEq(szToken, "language")) + if (FStrEq( outStrings[i], "mapname" )) + { + Q_strncat( value, g_MapName, sizeof( value ) ); + } + else if (FStrEq( outStrings[i], "language" )) { +#ifdef CLIENT_DLL char uilanguage[64]; engine->GetUILanguage(uilanguage, sizeof(uilanguage)); - append = uilanguage; - } + Q_strncat( value, uilanguage, sizeof( value ) ); +#else + // Give up, use English + Q_strncat( value, "english", sizeof( value ) ); #endif - Q_strncat(value, append, sizeof(value)); - - inparam = false; + } } else { - Q_strncat(value, szToken, sizeof(value)); - - inparam = true; + Q_strncat( value, outStrings[i], sizeof( value ) ); } - szToken = strtok(NULL, "%"); + + inparam = !inparam; } + outStrings.PurgeAndDeleteElements(); + if (FStrEq(name, "NoErrors")) { bDontWarn = pKey->GetBool(); @@ -406,6 +467,17 @@ BEGIN_SCRIPTDESC_ROOT( CMapbaseSystem, SCRIPT_SINGLETON "All-purpose Mapbase sys END_SCRIPTDESC(); #endif +static void CC_Mapbase_LoadManifestFile( const CCommand& args ) +{ + g_MapbaseSystem.AddManifestFile(args[1]); +} + +#ifdef CLIENT_DLL +static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile_client", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file on the client. If you don't want this to be saved and found when reloaded, type a '1' after the file path." ); +#else +static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file. If you don't want this to be saved and found when reloaded, type a '1' after the file path." ); +#endif + #ifdef GAME_DLL static CUtlVector g_MapbaseChapterMaps; static CUtlVector g_MapbaseChapterList; @@ -467,13 +539,6 @@ ThreeState_t Flashlight_GetLegacyVersionKey() return TRS_NONE; } -static void CC_Mapbase_LoadManifestFile( const CCommand& args ) -{ - g_MapbaseSystem.AddManifestFile(args[1]); -} - -static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file. If you don't want this to be saved and found when reloaded, type a '1' after the file path." ); - #define SF_MANIFEST_START_ACTIVATED (1 << 0) class CMapbaseManifestEntity : public CPointEntity @@ -495,7 +560,7 @@ class CMapbaseManifestEntity : public CPointEntity void LoadManifestFile( void ) { const char *scriptfile = STRING(m_target); - if ( filesystem->FileExists( scriptfile, "GAME" ) ) + if ( filesystem->FileExists( scriptfile, "MOD" ) ) { Msg("Mapbase: Adding manifest file \"%s\"\n", scriptfile); g_MapbaseSystem.AddManifestFile(scriptfile); diff --git a/sp/src/game/shared/mapbase/matchers.cpp b/sp/src/game/shared/mapbase/matchers.cpp index 6197009c41..f1f1d26b17 100644 --- a/sp/src/game/shared/mapbase/matchers.cpp +++ b/sp/src/game/shared/mapbase/matchers.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ================= +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= // // Purpose: General matching functions for things like wildcards and !=. // diff --git a/sp/src/game/shared/mapbase/matchers.h b/sp/src/game/shared/mapbase/matchers.h index f02f3ff397..52e8cc1a28 100644 --- a/sp/src/game/shared/mapbase/matchers.h +++ b/sp/src/game/shared/mapbase/matchers.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ================= +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= // // Purpose: General matching functions for things like wildcards and !=. // diff --git a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp new file mode 100644 index 0000000000..886d0ea1c5 --- /dev/null +++ b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -0,0 +1,411 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript constants and enums shared between the server and client. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "activitylist.h" +#include "in_buttons.h" +#ifdef CLIENT_DLL +#include "c_ai_basenpc.h" +#else +#include "ai_basenpc.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( IN, "Button mask bindings" ) + + DEFINE_ENUMCONST_NAMED( IN_ATTACK, "ATTACK", "" ) + DEFINE_ENUMCONST_NAMED( IN_JUMP, "JUMP", "" ) + DEFINE_ENUMCONST_NAMED( IN_DUCK, "DUCK", "" ) + DEFINE_ENUMCONST_NAMED( IN_FORWARD, "FORWARD", "" ) + DEFINE_ENUMCONST_NAMED( IN_BACK, "BACK", "" ) + DEFINE_ENUMCONST_NAMED( IN_USE, "USE", "" ) + DEFINE_ENUMCONST_NAMED( IN_CANCEL, "CANCEL", "" ) + DEFINE_ENUMCONST_NAMED( IN_LEFT, "LEFT", "" ) + DEFINE_ENUMCONST_NAMED( IN_RIGHT, "RIGHT", "" ) + DEFINE_ENUMCONST_NAMED( IN_MOVELEFT, "MOVELEFT", "" ) + DEFINE_ENUMCONST_NAMED( IN_MOVERIGHT, "MOVERIGHT", "" ) + DEFINE_ENUMCONST_NAMED( IN_ATTACK2, "ATTACK2", "" ) + DEFINE_ENUMCONST_NAMED( IN_RUN, "RUN", "" ) + DEFINE_ENUMCONST_NAMED( IN_RELOAD, "RELOAD", "" ) + DEFINE_ENUMCONST_NAMED( IN_ALT1, "ALT1", "" ) + DEFINE_ENUMCONST_NAMED( IN_ALT2, "ALT2", "" ) + DEFINE_ENUMCONST_NAMED( IN_SCORE, "SCORE", "" ) + DEFINE_ENUMCONST_NAMED( IN_SPEED, "SPEED", "" ) + DEFINE_ENUMCONST_NAMED( IN_WALK, "WALK", "" ) + DEFINE_ENUMCONST_NAMED( IN_ZOOM, "ZOOM", "" ) + DEFINE_ENUMCONST_NAMED( IN_WEAPON1, "WEAPON1", "" ) + DEFINE_ENUMCONST_NAMED( IN_WEAPON2, "WEAPON2", "" ) + DEFINE_ENUMCONST_NAMED( IN_BULLRUSH, "BULLRUSH", "" ) + DEFINE_ENUMCONST_NAMED( IN_GRENADE1, "GRENADE1", "" ) + DEFINE_ENUMCONST_NAMED( IN_GRENADE2, "GRENADE2", "" ) + DEFINE_ENUMCONST_NAMED( IN_ATTACK3, "ATTACK3", "" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( RenderMode, "Render modes used by Get/SetRenderMode" ) + + DEFINE_ENUMCONST_NAMED( kRenderNormal, "Normal", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransColor, "Color", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransTexture, "Texture", "" ) + DEFINE_ENUMCONST_NAMED( kRenderGlow, "Glow", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAlpha, "Solid", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAdd, "Additive", "" ) + DEFINE_ENUMCONST_NAMED( kRenderEnvironmental, "Environmental", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAddFrameBlend, "AdditiveFractionalFrame", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAlphaAdd, "Alpha Add", "" ) + DEFINE_ENUMCONST_NAMED( kRenderWorldGlow, "WorldSpaceGlow", "" ) + DEFINE_ENUMCONST_NAMED( kRenderNone, "None", "" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +BEGIN_SCRIPTENUM( Hitgroup, "Hit groups from traces" ) + + DEFINE_ENUMCONST_NAMED( HITGROUP_GENERIC, "Generic", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_HEAD, "Head", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_CHEST, "Chest", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_STOMACH, "Stomach", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_LEFTARM, "LeftArm", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_RIGHTARM, "RightArm", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_LEFTLEG, "LeftLeg", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_RIGHTLEG, "RightLeg", "" ) + DEFINE_ENUMCONST_NAMED( HITGROUP_GEAR, "Gear", "" ) + +END_SCRIPTENUM(); + +//============================================================================= +//============================================================================= + +void RegisterActivityConstants() +{ + // Make sure there are no activities declared yet + if (g_pScriptVM->ValueExists( "ACT_RESET" )) + return; + + // Register activity constants by just iterating through the entire activity list + for (int i = 0; i < ActivityList_HighestIndex(); i++) + { + ScriptRegisterConstantNamed( g_pScriptVM, i, ActivityList_NameForIndex(i), "" ); + } +} + +//============================================================================= +//============================================================================= + +void RegisterSharedScriptConstants() +{ + // + // Activities + // + + // Scripts have to use this function before using any activity constants. + // This is because initializing 1,700+ constants every time a level loads and letting them lay around + // usually doing nothing sounds like a bad idea. + ScriptRegisterFunction( g_pScriptVM, RegisterActivityConstants, "Registers all activity IDs as usable constants." ); + + // + // Damage Types + // + ScriptRegisterConstant( g_pScriptVM, DMG_GENERIC, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_CRUSH, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_BULLET, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_SLASH, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_BURN, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_VEHICLE, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_FALL, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_BLAST, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_CLUB, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_SHOCK, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_SONIC, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_ENERGYBEAM, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_PREVENT_PHYSICS_FORCE, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_NEVERGIB, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_ALWAYSGIB, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_DROWN, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_PARALYZE, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_NERVEGAS, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_POISON, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_RADIATION, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_DROWNRECOVER, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_ACID, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_SLOWBURN, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_REMOVENORAGDOLL, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_PHYSGUN, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_PLASMA, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_AIRBOAT, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_DISSOLVE, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_BLAST_SURFACE, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_DIRECT, "" ); + ScriptRegisterConstant( g_pScriptVM, DMG_BUCKSHOT, "" ); + + // + // Trace Contents/Masks + // + ScriptRegisterConstant( g_pScriptVM, CONTENTS_EMPTY, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_SOLID, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_WINDOW, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_AUX, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_GRATE, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_SLIME, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_WATER, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_BLOCKLOS, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_OPAQUE, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_TESTFOGVOLUME, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_TEAM1, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_TEAM2, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_IGNORE_NODRAW_OPAQUE, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_MOVEABLE, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_AREAPORTAL, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_PLAYERCLIP, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_MONSTERCLIP, "" ); + + ScriptRegisterConstant( g_pScriptVM, CONTENTS_CURRENT_0, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_CURRENT_90, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_CURRENT_180, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_CURRENT_270, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_CURRENT_UP, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_CURRENT_DOWN, "" ); + + ScriptRegisterConstant( g_pScriptVM, CONTENTS_ORIGIN, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_MONSTER, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_DEBRIS, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_DETAIL, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_TRANSLUCENT, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_LADDER, "" ); + ScriptRegisterConstant( g_pScriptVM, CONTENTS_HITBOX, "" ); + + ScriptRegisterConstant( g_pScriptVM, LAST_VISIBLE_CONTENTS, "" ); + ScriptRegisterConstant( g_pScriptVM, ALL_VISIBLE_CONTENTS, "" ); + + ScriptRegisterConstant( g_pScriptVM, MASK_SOLID, "Spatial content mask representing solid objects (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_PLAYERSOLID, "Spatial content mask representing objects solid to the player, including player clips (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_NPCSOLID, "Spatial content mask representing objects solid to NPCs, including NPC clips (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_WATER, "Spatial content mask representing water and slime solids (CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_SLIME)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_OPAQUE, "Spatial content mask representing objects which block lighting (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_OPAQUE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_OPAQUE_AND_NPCS, "Spatial content mask equivalent to MASK_OPAQUE, but also including NPCs (MASK_OPAQUE|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_BLOCKLOS, "Spatial content mask representing objects which block LOS for AI (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_BLOCKLOS)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_BLOCKLOS_AND_NPCS, "Spatial content mask equivalent to MASK_BLOCKLOS, but also including NPCs (MASK_BLOCKLOS|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_VISIBLE, "Spatial content mask representing objects which block LOS for players (MASK_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_VISIBLE_AND_NPCS, "Spatial content mask equivalent to MASK_VISIBLE, but also including NPCs (MASK_OPAQUE_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_SHOT, "Spatial content mask representing objects solid to bullets (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_HITBOX)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_SHOT_HULL, "Spatial content mask representing objects solid to non-raycasted weapons, including grates (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_SHOT_PORTAL, "Spatial content mask equivalent to MASK_SHOT, but excluding debris and not using expensive hitbox calculations (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_SOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_SOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_PLAYERSOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_PLAYERSOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_PLAYERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_NPCSOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_NPCSOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_NPCWORLDSTATIC, "Spatial content mask representing objects static to NPCs, used for nodegraph rebuilding (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( g_pScriptVM, MASK_SPLITAREAPORTAL, "Spatial content mask representing objects which can split areaportals (CONTENTS_WATER|CONTENTS_SLIME)" ); + + // + // Collision Groups + // + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NONE, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DEBRIS, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DEBRIS_TRIGGER, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_INTERACTIVE_DEBRIS, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_INTERACTIVE, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PLAYER, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_BREAKABLE_GLASS, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_VEHICLE, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PLAYER_MOVEMENT, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NPC, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_IN_VEHICLE, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_WEAPON, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_VEHICLE_CLIP, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PROJECTILE, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DOOR_BLOCKER, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PASSABLE_DOOR, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_DISSOLVING, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_PUSHAWAY, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NPC_ACTOR, "" ); + ScriptRegisterConstant( g_pScriptVM, COLLISION_GROUP_NPC_SCRIPTED, "" ); + + // + // Entity Flags + // + ScriptRegisterConstant( g_pScriptVM, EFL_KILLME, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DORMANT, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NOCLIP_ACTIVE, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_SETTING_UP_BONES, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_KEEP_ON_RECREATE_ENTITIES, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_HAS_PLAYER_CHILD, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_SHADOWUPDATE, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NOTIFY, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_FORCE_CHECK_TRANSMIT, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_BOT_FROZEN, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_SERVER_ONLY, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_AUTO_EDICT_ATTACH, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_ABSTRANSFORM, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_ABSVELOCITY, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_ABSANGVELOCITY, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DIRTY_SPATIAL_PARTITION, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_PLUGIN_BASED_BOT, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_IN_SKYBOX, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_USE_PARTITION_WHEN_NOT_SOLID, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_TOUCHING_FLUID, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_IS_BEING_LIFTED_BY_BARNACLE, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_ROTORWASH_PUSH, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_THINK_FUNCTION, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_GAME_PHYSICS_SIMULATION, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_CHECK_UNTOUCH, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DONTBLOCKLOS, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_DONTWALKON, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_DISSOLVE, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_MEGAPHYSCANNON_RAGDOLL, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_WATER_VELOCITY_CHANGE, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_PHYSCANNON_INTERACTION, "" ); + ScriptRegisterConstant( g_pScriptVM, EFL_NO_DAMAGE_FORCES, "" ); + + // + // Effects + // + ScriptRegisterConstant( g_pScriptVM, EF_BONEMERGE, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_BRIGHTLIGHT, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_DIMLIGHT, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_NOINTERP, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_NOSHADOW, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_NODRAW, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_NORECEIVESHADOW, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_BONEMERGE_FASTCULL, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_ITEM_BLINK, "" ); + ScriptRegisterConstant( g_pScriptVM, EF_PARENT_ANIMATES, "" ); + + // + // Solid Flags + // + ScriptRegisterConstant( g_pScriptVM, FSOLID_CUSTOMRAYTEST, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_CUSTOMBOXTEST, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_NOT_SOLID, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_TRIGGER, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_NOT_STANDABLE, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_VOLUME_CONTENTS, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_FORCE_WORLD_ALIGNED, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_USE_TRIGGER_BOUNDS, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_ROOT_PARENT_ALIGNED, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_TRIGGER_TOUCH_DEBRIS, "" ); + ScriptRegisterConstant( g_pScriptVM, FSOLID_COLLIDE_WITH_OWNER, "" ); + + // + // Movetypes + // + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_NONE, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_ISOMETRIC, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_WALK, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_STEP, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_FLY, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_FLYGRAVITY, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_VPHYSICS, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_PUSH, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_NOCLIP, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_LADDER, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_OBSERVER, "" ); + ScriptRegisterConstant( g_pScriptVM, MOVETYPE_CUSTOM, "" ); + +#ifdef GAME_DLL + // + // Sound Types, Contexts, and Channels + // (QueryHearSound hook can use these) + // + ScriptRegisterConstant( g_pScriptVM, SOUND_NONE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_COMBAT, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_WORLD, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_PLAYER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_DANGER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_BULLET_IMPACT, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CARCASS, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_MEAT, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_GARBAGE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_THUMPER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_BUGBAIT, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_PHYSICS_DANGER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_DANGER_SNIPERONLY, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_MOVE_AWAY, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_PLAYER_VEHICLE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_READINESS_LOW, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_READINESS_MEDIUM, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_READINESS_HIGH, "" ); + + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_FROM_SNIPER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_GUNFIRE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_MORTAR, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_COMBINE_ONLY, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_REACT_TO_SOURCE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_EXPLOSION, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_EXCLUDE_COMBINE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_DANGER_APPROACH, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_ALLIES_ONLY, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_PLAYER_VEHICLE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUND_CONTEXT_OWNER_ALLIES, "" ); + + ScriptRegisterConstant( g_pScriptVM, ALL_CONTEXTS, "" ); + ScriptRegisterConstant( g_pScriptVM, ALL_SCENTS, "" ); + ScriptRegisterConstant( g_pScriptVM, ALL_SOUNDS, "" ); + + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_UNSPECIFIED, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_REPEATING, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_REPEATED_DANGER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_WEAPON, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_INJURY, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_BULLET_IMPACT, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_NPC_FOOTSTEP, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_SPOOKY_NOISE, "" ); + ScriptRegisterConstant( g_pScriptVM, SOUNDENT_CHANNEL_ZOMBINE_GRENADE, "" ); + + // + // Capabilities + // + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_GROUND, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_JUMP, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_FLY, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_CLIMB, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_SWIM, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_CRAWL, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MOVE_SHOOT, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_SKIP_NAV_GROUND_CHECK, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_USE, "" ); + //ScriptRegisterConstant( g_pScriptVM, bits_CAP_HEAR, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_AUTO_DOORS, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_OPEN_DOORS, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_TURN_HEAD, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_RANGE_ATTACK1, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_RANGE_ATTACK2, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_MELEE_ATTACK1, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_WEAPON_MELEE_ATTACK2, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_RANGE_ATTACK1, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_RANGE_ATTACK2, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_MELEE_ATTACK1, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_INNATE_MELEE_ATTACK2, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_USE_WEAPONS, "" ); + //ScriptRegisterConstant( g_pScriptVM, bits_CAP_STRAFE, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_ANIMATEDFACE, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_USE_SHOT_REGULATOR, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_FRIENDLY_DMG_IMMUNE, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_SQUAD, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_DUCK, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_NO_HIT_PLAYER, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_AIM_GUN, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_NO_HIT_SQUADMATES, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_SIMPLE_RADIUS_DAMAGE, "" ); + + ScriptRegisterConstant( g_pScriptVM, bits_CAP_DOORS_GROUP, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_RANGE_ATTACK_GROUP, "" ); + ScriptRegisterConstant( g_pScriptVM, bits_CAP_MELEE_ATTACK_GROUP, "" ); +#endif +} diff --git a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp index 92aa86aee8..cab7d67d98 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: VScript functions for Half-Life 2. // diff --git a/sp/src/game/shared/mapbase/vscript_funcs_math.cpp b/sp/src/game/shared/mapbase/vscript_funcs_math.cpp index 68eb543cd4..3c74b06d6e 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_math.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_math.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Shared VScript math functions. // @@ -17,7 +17,7 @@ // matrix3x4_t // //============================================================================= -BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMatrix3x4, "matrix3x4_t", "A 3x4 matrix transform." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( matrix3x4_t, "matrix3x4_t", "A 3x4 matrix transform." ) DEFINE_SCRIPT_CONSTRUCTOR() DEFINE_SCRIPTFUNC( Init, "Creates a matrix where the X axis = forward, the Y axis = left, and the Z axis = up." ) @@ -146,6 +146,145 @@ void ScriptSetScaleMatrix( float x, float y, float z, HSCRIPT hMat1 ) SetScaleMatrix( x, y, z, *pMat1 ); } +//============================================================================= +// +// Quaternion +// +//============================================================================= +CScriptQuaternionInstanceHelper g_QuaternionScriptInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT_NAMED( Quaternion, "Quaternion", "A quaternion." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPT_INSTANCE_HELPER( &g_QuaternionScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( ScriptInit, "Init", "Creates a quaternion with the given values." ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- + +bool CScriptQuaternionInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + Quaternion *pQuat = ((Quaternion *)p); + V_snprintf( pBuf, bufSize, "(quaternion: (%f, %f, %f, %f))", pQuat->x, pQuat->y, pQuat->z, pQuat->w ); + return true; +} + +bool CScriptQuaternionInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'x': + variant = pQuat->x; + return true; + case 'y': + variant = pQuat->y; + return true; + case 'z': + variant = pQuat->z; + return true; + case 'w': + variant = pQuat->w; + return true; + } + } + return false; +} + +bool CScriptQuaternionInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'x': + variant.AssignTo( &pQuat->x ); + return true; + case 'y': + variant.AssignTo( &pQuat->y ); + return true; + case 'z': + variant.AssignTo( &pQuat->z ); + return true; + case 'w': + variant.AssignTo( &pQuat->w ); + return true; + } + } + return false; +} + +ScriptVariant_t *CScriptQuaternionInstanceHelper::Add( void *p, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + DevMsg("Adding %i is an interesting choice\n", variant.m_int); + + float flAdd; + variant.AssignTo( &flAdd ); + + (*pQuat)[0] += flAdd; + (*pQuat)[1] += flAdd; + (*pQuat)[2] += flAdd; + (*pQuat)[3] += flAdd; + + static ScriptVariant_t result; + result = (HSCRIPT)p; + return &result; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptQuaternionAdd( HSCRIPT hQuat1, HSCRIPT hQuat2, HSCRIPT hOut ) +{ + if (!hQuat1 || !hQuat2 || !hOut) + return; + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + Quaternion *pQuat2 = ToQuaternion( hQuat2 ); + Quaternion *pOut = ToQuaternion( hOut ); + + QuaternionAdd( *pQuat1, *pQuat2, *pOut ); +} + +void ScriptMatrixQuaternion( HSCRIPT hMat1, HSCRIPT hQuat1 ) +{ + if (!hMat1 || !hQuat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + + MatrixQuaternion( *pMat1, *pQuat1 ); +} + +void ScriptQuaternionMatrix( HSCRIPT hQuat1, HSCRIPT hMat1 ) +{ + if (!hQuat1 || !hMat1) + return; + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + QuaternionMatrix( *pQuat1, *pMat1 ); +} + +QAngle ScriptQuaternionAngles( HSCRIPT hQuat1 ) +{ + if (!hQuat1) + return QAngle(); + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + + QAngle angles; + QuaternionAngles( *pQuat1, angles ); + return angles; +} + //============================================================================= // // Misc. Vector/QAngle functions @@ -166,6 +305,46 @@ const QAngle& ScriptVectorAngles( const Vector &forward ) return angles; } +const Vector& ScriptVectorRotate( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorRotate( in, *ToMatrix3x4(hMat), out ); + return out; +} + +const Vector& ScriptVectorIRotate( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorIRotate( in, *ToMatrix3x4(hMat), out ); + return out; +} + +const Vector& ScriptVectorTransform( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorTransform( in, *ToMatrix3x4( hMat ), out ); + return out; +} + +const Vector& ScriptVectorITransform( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorITransform( in, *ToMatrix3x4( hMat ), out ); + return out; +} + const Vector& ScriptCalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point ) { static Vector outvec; @@ -222,6 +401,8 @@ void RegisterMathScriptFunctions() // // matrix3x4_t // + g_pScriptVM->RegisterClass( GetScriptDescForClass( matrix3x4_t ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptFreeMatrixInstance, "FreeMatrixInstance", "Frees an allocated matrix instance." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptConcatTransforms, "ConcatTransforms", "Concatenates two transformation matrices into another matrix." ); @@ -236,12 +417,29 @@ void RegisterMathScriptFunctions() ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetIdentityMatrix, "SetIdentityMatrix", "Turns a matrix into an identity matrix." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetScaleMatrix, "SetScaleMatrix", "Scales a matrix." ); + // + // Quaternion + // + g_pScriptVM->RegisterClass( GetScriptDescForClass( Quaternion ) ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptFreeQuaternionInstance, "FreeQuaternionInstance", "Frees an allocated quaternion instance." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptQuaternionAdd, "QuaternionAdd", "Adds two quaternions together into another quaternion." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixQuaternion, "MatrixQuaternion", "Converts a matrix to a quaternion." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptQuaternionMatrix, "QuaternionMatrix", "Converts a quaternion to a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptQuaternionAngles, "QuaternionAngles", "Converts a quaternion to angles." ); + // // Misc. Vector/QAngle functions // ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleVectors, "AngleVectors", "Turns an angle into a direction vector." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorAngles, "VectorAngles", "Turns a direction vector into an angle." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorRotate, "VectorRotate", "Rotates a vector with a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorIRotate, "VectorIRotate", "Rotates a vector with the inverse of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorTransform, "VectorTransform", "Transforms a vector with a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorITransform, "VectorITransform", "Transforms a vector with the inverse of a matrix." ); + ScriptRegisterFunction( g_pScriptVM, CalcSqrDistanceToAABB, "Returns the squared distance to a bounding box." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnAABB, "CalcClosestPointOnAABB", "Returns the closest point on a bounding box." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcDistanceToLine, "CalcDistanceToLine", "Returns the distance to a line." ); diff --git a/sp/src/game/shared/mapbase/vscript_funcs_math.h b/sp/src/game/shared/mapbase/vscript_funcs_math.h index 132419806e..3d02ce758c 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_math.h +++ b/sp/src/game/shared/mapbase/vscript_funcs_math.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ================= +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= // // Purpose: Shared VScript math functions. // @@ -14,52 +14,53 @@ //----------------------------------------------------------------------------- // Exposes matrix3x4_t to VScript //----------------------------------------------------------------------------- -class CScriptMatrix3x4 +inline matrix3x4_t *ToMatrix3x4( HSCRIPT hMat ) { return HScriptToClass( hMat ); } + +static HSCRIPT ScriptCreateMatrixInstance( matrix3x4_t &matrix ) { -public: - CScriptMatrix3x4() - { - matrix = new matrix3x4_t(); - m_bFree = true; - } + return g_pScriptVM->RegisterInstance( &matrix ); +} - ~CScriptMatrix3x4() +static void ScriptFreeMatrixInstance( HSCRIPT hMat ) +{ + matrix3x4_t *smatrix = HScriptToClass( hMat ); + if (smatrix) { - if (m_bFree == true) - delete matrix; + g_pScriptVM->RemoveInstance( hMat ); + delete smatrix; } +} - CScriptMatrix3x4( matrix3x4_t &inmatrix ) { matrix = &inmatrix; } - - matrix3x4_t *GetMatrix() { return matrix; } - void SetMatrix( matrix3x4_t &inmatrix ) { matrix = &inmatrix; } +//----------------------------------------------------------------------------- +// Exposes Quaternion to VScript +//----------------------------------------------------------------------------- +class CScriptQuaternionInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); - void Init( const Vector& xAxis, const Vector& yAxis, const Vector& zAxis, const Vector &vecOrigin ) - { - matrix->Init( xAxis, yAxis, zAxis, vecOrigin ); - } + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); -private: - matrix3x4_t *matrix; - bool m_bFree = false; + ScriptVariant_t *Add( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Subtract( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Multiply( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Divide( void *p, ScriptVariant_t &variant ); }; -inline matrix3x4_t *ToMatrix3x4( HSCRIPT hMat ) { return HScriptToClass( hMat )->GetMatrix(); } +inline Quaternion *ToQuaternion( HSCRIPT hQuat ) { return HScriptToClass( hQuat ); } -static HSCRIPT ScriptCreateMatrixInstance( matrix3x4_t &matrix ) +static HSCRIPT ScriptCreateQuaternionInstance( Quaternion &quat ) { - CScriptMatrix3x4 *smatrix = new CScriptMatrix3x4( matrix ); - - return g_pScriptVM->RegisterInstance( smatrix ); + return g_pScriptVM->RegisterInstance( &quat ); } -static void ScriptFreeMatrixInstance( HSCRIPT hMat ) +static void ScriptFreeQuaternionInstance( HSCRIPT hQuat ) { - CScriptMatrix3x4 *smatrix = HScriptToClass( hMat ); - if (smatrix) + Quaternion *squat = HScriptToClass( hQuat ); + if (squat) { - g_pScriptVM->RemoveInstance( hMat ); - delete smatrix; + g_pScriptVM->RemoveInstance( hQuat ); + delete squat; } } diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index e6cbabc692..c611c4e770 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: Due to this being a custom integration of VScript based on the Alien Swarm SDK, we don't have access to // some of the code normally available in games like L4D2 or Valve's original VScript DLL. @@ -550,13 +550,24 @@ class CScriptLocalize return NULL; } + void AddStringAsUTF8( const char *pszToken, const char *pszString ) + { + wchar_t wpszString[256]; + g_pVGuiLocalize->ConvertANSIToUnicode( pszString, wpszString, sizeof(wpszString) ); + + // TODO: This is a fake file name! Should "fileName" mean anything? + g_pVGuiLocalize->AddString( pszToken, wpszString, "resource/vscript_localization.txt" ); + } + private: } g_ScriptLocalize; -BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptLocalize, "Localize", SCRIPT_SINGLETON "Accesses functions related to localization strings." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptLocalize, "CLocalize", SCRIPT_SINGLETON "Accesses functions related to localization strings." ) DEFINE_SCRIPTFUNC( GetTokenAsUTF8, "Gets the current language's token as a UTF-8 string (not Unicode)." ) + DEFINE_SCRIPTFUNC( AddStringAsUTF8, "Adds a new localized token as a UTF-8 string (not Unicode)." ) + END_SCRIPTDESC(); //----------------------------------------------------------------------------- @@ -566,7 +577,7 @@ static HSCRIPT CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Ve { // 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 ); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( damageInfo, true ); damageInfo->SetDamagePosition( vecDamagePos ); damageInfo->SetDamageForce( vecForce ); @@ -596,6 +607,8 @@ void ScriptGuessDamageForce( HSCRIPT info, const Vector &vecForceDir, const Vect // //----------------------------------------------------------------------------- BEGIN_SCRIPTDESC_ROOT_NAMED( CTraceInfoAccessor, "CGameTrace", "Handle for accessing trace_t info." ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC( DidHitWorld, "Returns whether the trace hit the world entity or not." ) DEFINE_SCRIPTFUNC( DidHitNonWorldEntity, "Returns whether the trace hit something other than the world entity." ) DEFINE_SCRIPTFUNC( GetEntityIndex, "Returns the index of whatever entity this trace hit." ) @@ -623,17 +636,50 @@ 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( Destroy, "Deletes this instance. Important for preventing memory leaks." ) +END_SCRIPTDESC(); + +BEGIN_SCRIPTDESC_ROOT_NAMED( surfacedata_t, "surfacedata_t", "Handle for accessing surface data." ) + DEFINE_SCRIPTFUNC( GetFriction, "The surface's friction." ) + DEFINE_SCRIPTFUNC( GetThickness, "The surface's thickness." ) + + DEFINE_SCRIPTFUNC( GetJumpFactor, "The surface's jump factor." ) + DEFINE_SCRIPTFUNC( GetMaterialChar, "The surface's material character." ) +END_SCRIPTDESC(); + +BEGIN_SCRIPTDESC_ROOT_NAMED( CSurfaceScriptAccessor, "csurface_t", "Handle for accessing csurface_t info." ) + DEFINE_SCRIPTFUNC( Name, "The surface's name." ) + DEFINE_SCRIPTFUNC( SurfaceProps, "The surface's properties." ) + DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) END_SCRIPTDESC(); +CPlaneTInstanceHelper g_PlaneTInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT( cplane_t, "Handle for accessing cplane_t info." ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_PlaneTInstanceHelper ) +END_SCRIPTDESC(); + static HSCRIPT ScriptTraceLineComplex( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore, int iMask, int iCollisionGroup ) { // The script is responsible for deleting this via Destroy(). CTraceInfoAccessor *traceInfo = new CTraceInfoAccessor(); - HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo ); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo, true ); CBaseEntity *pLooker = ToEnt(entIgnore); UTIL_TraceLine( vecStart, vecEnd, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + + // The trace's destruction should destroy this automatically + CSurfaceScriptAccessor *surfaceInfo = new CSurfaceScriptAccessor( traceInfo->GetTrace().surface ); + HSCRIPT hSurface = g_pScriptVM->RegisterInstance( surfaceInfo ); + traceInfo->SetSurface( hSurface ); + + HSCRIPT hPlane = g_pScriptVM->RegisterInstance( &(traceInfo->GetTrace().plane) ); + traceInfo->SetPlane( hPlane ); + return hScript; } @@ -642,17 +688,28 @@ static HSCRIPT ScriptTraceHullComplex( const Vector &vecStart, const Vector &vec { // The script is responsible for deleting this via Destroy(). CTraceInfoAccessor *traceInfo = new CTraceInfoAccessor(); - HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo ); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo, true ); CBaseEntity *pLooker = ToEnt(entIgnore); UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + + // The trace's destruction should destroy this automatically + CSurfaceScriptAccessor *surfaceInfo = new CSurfaceScriptAccessor( traceInfo->GetTrace().surface ); + HSCRIPT hSurface = g_pScriptVM->RegisterInstance( surfaceInfo ); + traceInfo->SetSurface( hSurface ); + + HSCRIPT hPlane = g_pScriptVM->RegisterInstance( &(traceInfo->GetTrace().plane) ); + traceInfo->SetPlane( hPlane ); + return hScript; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -BEGIN_SCRIPTDESC_ROOT_NAMED( CFireBulletsInfoAccessor, "FireBulletsInfo_t", "Handle for accessing FireBulletsInfo_t info." ) +BEGIN_SCRIPTDESC_ROOT( FireBulletsInfo_t, "Handle for accessing FireBulletsInfo_t info." ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC( GetShots, "Gets the number of shots which should be fired." ) DEFINE_SCRIPTFUNC( SetShots, "Sets the number of shots which should be fired." ) @@ -683,10 +740,10 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CFireBulletsInfoAccessor, "FireBulletsInfo_t", "Han 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( GetAttacker, "Gets the entity considered to be the one who fired the bullets." ) - DEFINE_SCRIPTFUNC( SetAttacker, "Sets the entity considered to be the one who fired the bullets." ) - DEFINE_SCRIPTFUNC( GetAdditionalIgnoreEnt, "Gets the optional entity which the bullets should ignore." ) - DEFINE_SCRIPTFUNC( SetAdditionalIgnoreEnt, "Sets the optional entity which the bullets should ignore." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttacker, "GetAttacker", "Gets the entity considered to be the one who fired the bullets." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAttacker, "SetAttacker", "Sets the entity considered to be the one who fired the bullets." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAdditionalIgnoreEnt, "GetAdditionalIgnoreEnt", "Gets the optional entity which the bullets should ignore." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAdditionalIgnoreEnt, "SetAdditionalIgnoreEnt", "Sets the optional entity which the bullets should ignore." ) DEFINE_SCRIPTFUNC( GetPrimaryAttack, "Gets whether the bullets came from a primary attack." ) DEFINE_SCRIPTFUNC( SetPrimaryAttack, "Sets whether the bullets came from a primary attack." ) @@ -697,24 +754,24 @@ END_SCRIPTDESC(); //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -HSCRIPT CFireBulletsInfoAccessor::GetAttacker() +HSCRIPT FireBulletsInfo_t::ScriptGetAttacker() { - return ToHScript( m_info->m_pAttacker ); + return ToHScript( m_pAttacker ); } -void CFireBulletsInfoAccessor::SetAttacker( HSCRIPT value ) +void FireBulletsInfo_t::ScriptSetAttacker( HSCRIPT value ) { - m_info->m_pAttacker = ToEnt( value ); + m_pAttacker = ToEnt( value ); } -HSCRIPT CFireBulletsInfoAccessor::GetAdditionalIgnoreEnt() +HSCRIPT FireBulletsInfo_t::ScriptGetAdditionalIgnoreEnt() { - return ToHScript( m_info->m_pAdditionalIgnoreEnt ); + return ToHScript( m_pAdditionalIgnoreEnt ); } -void CFireBulletsInfoAccessor::SetAdditionalIgnoreEnt( HSCRIPT value ) +void FireBulletsInfo_t::ScriptSetAdditionalIgnoreEnt( HSCRIPT value ) { - m_info->m_pAdditionalIgnoreEnt = ToEnt( value ); + m_pAdditionalIgnoreEnt = ToEnt( value ); } static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Vector &vecDirShooting, @@ -722,52 +779,33 @@ 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(); - CFireBulletsInfoAccessor *bulletsInfo = new CFireBulletsInfoAccessor( info ); - - HSCRIPT hScript = g_pScriptVM->RegisterInstance( bulletsInfo ); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( info, true ); - bulletsInfo->SetShots( cShots ); - bulletsInfo->SetSource( vecSrc ); - bulletsInfo->SetDirShooting( vecDirShooting ); - bulletsInfo->SetSpread( vecSpread ); - bulletsInfo->SetDamage( iDamage ); - bulletsInfo->SetAttacker( pAttacker ); + info->SetShots( cShots ); + info->SetSource( vecSrc ); + info->SetDirShooting( vecDirShooting ); + info->SetSpread( vecSpread ); + info->SetDamage( iDamage ); + info->ScriptSetAttacker( pAttacker ); return hScript; } static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo ) { - if (hBulletsInfo) - { - CFireBulletsInfoAccessor *pInfo = (CFireBulletsInfoAccessor*)g_pScriptVM->GetInstanceValue( hBulletsInfo, GetScriptDescForClass( CFireBulletsInfoAccessor ) ); - if (pInfo) - { - g_pScriptVM->RemoveInstance( hBulletsInfo ); - pInfo->Destroy(); - } - } + g_pScriptVM->RemoveInstance( hBulletsInfo ); } // For the function in baseentity.cpp FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ) { - if (hBulletsInfo) - { - CFireBulletsInfoAccessor *pInfo = (CFireBulletsInfoAccessor*)g_pScriptVM->GetInstanceValue( hBulletsInfo, GetScriptDescForClass( CFireBulletsInfoAccessor ) ); - if (pInfo) - { - return pInfo->GetInfo(); - } - } - - return NULL; + return HScriptToClass( hBulletsInfo ); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -BEGIN_SCRIPTDESC_ROOT_NAMED( CUserCmdAccessor, "CUserCmd", "Handle for accessing CUserCmd info." ) +BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) DEFINE_SCRIPTFUNC( GetCommandNumber, "For matching server and client commands for debugging." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetTickCount, "GetTickCount", "The tick the client created this command." ) @@ -800,6 +838,88 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CUserCmdAccessor, "CUserCmd", "Handle for accessing DEFINE_SCRIPTFUNC( SetMouseY, "Sets mouse accum in y from create move." ) END_SCRIPTDESC(); +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT( IPhysicsObject, "VPhysics object class." ) + + DEFINE_SCRIPTFUNC( IsStatic, "" ) + DEFINE_SCRIPTFUNC( IsAsleep, "" ) + DEFINE_SCRIPTFUNC( IsTrigger, "" ) + DEFINE_SCRIPTFUNC( IsFluid, "" ) + DEFINE_SCRIPTFUNC( IsHinged, "" ) + DEFINE_SCRIPTFUNC( IsCollisionEnabled, "" ) + DEFINE_SCRIPTFUNC( IsGravityEnabled, "" ) + DEFINE_SCRIPTFUNC( IsDragEnabled, "" ) + DEFINE_SCRIPTFUNC( IsMotionEnabled, "" ) + DEFINE_SCRIPTFUNC( IsMoveable, "" ) + DEFINE_SCRIPTFUNC( IsAttachedToConstraint, "" ) + + DEFINE_SCRIPTFUNC( EnableCollisions, "" ) + DEFINE_SCRIPTFUNC( EnableGravity, "" ) + DEFINE_SCRIPTFUNC( EnableDrag, "" ) + DEFINE_SCRIPTFUNC( EnableMotion, "" ) + + DEFINE_SCRIPTFUNC( Wake, "" ) + DEFINE_SCRIPTFUNC( Sleep, "" ) + + DEFINE_SCRIPTFUNC( SetMass, "" ) + DEFINE_SCRIPTFUNC( GetMass, "" ) + DEFINE_SCRIPTFUNC( GetInvMass, "" ) + DEFINE_SCRIPTFUNC( GetInertia, "" ) + DEFINE_SCRIPTFUNC( GetInvInertia, "" ) + DEFINE_SCRIPTFUNC( SetInertia, "" ) + + DEFINE_SCRIPTFUNC( ApplyForceCenter, "" ) + DEFINE_SCRIPTFUNC( ApplyForceOffset, "" ) + DEFINE_SCRIPTFUNC( ApplyTorqueCenter, "" ) + + DEFINE_SCRIPTFUNC( GetName, "" ) + +END_SCRIPTDESC(); + +static const Vector &GetPhysVelocity( HSCRIPT hPhys ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return vec3_origin; + + static Vector vecVelocity; + pPhys->GetVelocity( &vecVelocity, NULL ); + return vecVelocity; +} + +static const Vector &GetPhysAngVelocity( HSCRIPT hPhys ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return vec3_origin; + + static Vector vecAngVelocity; + pPhys->GetVelocity( NULL, &vecAngVelocity ); + return vecAngVelocity; +} + +static void SetPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vector& vecAngVelocity ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return; + + if (pPhys) + pPhys->SetVelocity( &vecVelocity, &vecAngVelocity ); +} + +static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vector& vecAngVelocity ) +{ + IPhysicsObject *pPhys = HScriptToClass( hPhys ); + if (!pPhys) + return; + + if (pPhys) + pPhys->AddVelocity( &vecVelocity, &vecAngVelocity ); +} + //============================================================================= //============================================================================= @@ -865,6 +985,27 @@ bool ScriptMatcherMatch( const char *pszQuery, const char *szValue ) { return Ma //============================================================================= //============================================================================= +bool IsServerScript() +{ +#ifdef GAME_DLL + return true; +#else + return false; +#endif +} + +bool IsClientScript() +{ +#ifdef CLIENT_DLL + return true; +#else + return false; +#endif +} + +//============================================================================= +//============================================================================= + extern void RegisterMathScriptFunctions(); void RegisterSharedScriptFunctions() @@ -922,6 +1063,14 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLineComplex, "TraceLineComplex", "Complex version of TraceLine which takes 2 points, an ent to ignore, a trace mask, and a collision group. Returns a handle which can access all trace info." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceHullComplex, "TraceHullComplex", "Takes 2 points, min/max hull bounds, an ent to ignore, a trace mask, and a collision group to trace to a point using a hull. Returns a handle which can access all trace info." ); + // + // VPhysics + // + ScriptRegisterFunction( g_pScriptVM, GetPhysVelocity, "Gets physics velocity for the given VPhysics object" ); + ScriptRegisterFunction( g_pScriptVM, GetPhysAngVelocity, "Gets physics angular velocity for the given VPhysics object" ); + ScriptRegisterFunction( g_pScriptVM, SetPhysVelocity, "Sets physics velocity for the given VPhysics object" ); + ScriptRegisterFunction( g_pScriptVM, AddPhysVelocity, "Adds physics velocity for the given VPhysics object" ); + // // Precaching // @@ -943,6 +1092,10 @@ void RegisterSharedScriptFunctions() 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." ); + // For shared server/clientside scripts + ScriptRegisterFunction( g_pScriptVM, IsServerScript, "Returns true if the script is being run on the server." ); + ScriptRegisterFunction( g_pScriptVM, IsClientScript, "Returns true if the script is being run on the client." ); + RegisterMathScriptFunctions(); #ifdef CLIENT_DLL diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.h b/sp/src/game/shared/mapbase/vscript_funcs_shared.h index 43928e37e5..ff4a1aff27 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.h +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ================= +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= // // Purpose: Due to this being a custom integration of VScript based on the Alien Swarm SDK, we don't have access to // some of the code normally available in games like L4D2 or Valve's original VScript DLL. @@ -15,12 +15,67 @@ #pragma once #endif +//----------------------------------------------------------------------------- +// Exposes csurface_t to VScript +//----------------------------------------------------------------------------- +class CSurfaceScriptAccessor +{ +public: + CSurfaceScriptAccessor( csurface_t &surf ) { m_surf = &surf; m_surfaceData = g_pScriptVM->RegisterInstance( physprops->GetSurfaceData( m_surf->surfaceProps ) ); } + ~CSurfaceScriptAccessor() { delete m_surfaceData; } + + // cplane_t stuff + const char* Name() const { return m_surf->name; } + HSCRIPT SurfaceProps() const { return m_surfaceData; } + + void Destroy() { delete this; } + +private: + csurface_t *m_surf; + HSCRIPT m_surfaceData; +}; + +//----------------------------------------------------------------------------- +// Exposes cplane_t to VScript +//----------------------------------------------------------------------------- +class CPlaneTInstanceHelper : public IScriptInstanceHelper +{ + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ) + { + cplane_t *pi = ((cplane_t *)p); + if (FStrEq(pszKey, "normal")) + variant = pi->normal; + else if (FStrEq(pszKey, "dist")) + variant = pi->dist; + else + return false; + + return true; + } + + //bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); +}; + //----------------------------------------------------------------------------- // Exposes trace_t to VScript //----------------------------------------------------------------------------- class CTraceInfoAccessor { public: + ~CTraceInfoAccessor() + { + if (m_surfaceAccessor) + { + CSurfaceScriptAccessor *pScriptSurface = HScriptToClass( m_surfaceAccessor ); + //g_pScriptVM->RemoveInstance( m_surfaceAccessor ); + delete pScriptSurface; + } + + //if (m_planeAccessor) + //{ + // g_pScriptVM->RemoveInstance( m_planeAccessor ); + //} + } // CGrameTrace stuff bool DidHitWorld() const { return m_tr.DidHitWorld(); } @@ -43,8 +98,8 @@ class CTraceInfoAccessor bool IsDispSurfaceProp1() { return m_tr.IsDispSurfaceProp1(); } bool IsDispSurfaceProp2() { return m_tr.IsDispSurfaceProp2(); } - Vector StartPos() const { return m_tr.startpos; } - Vector EndPos() const { return m_tr.endpos; } + const Vector& StartPos() const { return m_tr.startpos; } + const Vector& EndPos() const { return m_tr.endpos; } float Fraction() const { return m_tr.fraction; } @@ -54,109 +109,20 @@ class CTraceInfoAccessor bool AllSolid() const { return m_tr.allsolid; } bool StartSolid() const { return m_tr.startsolid; } + HSCRIPT Surface() { return m_surfaceAccessor; } + void SetSurface( HSCRIPT hPlaneAccessor ) { m_surfaceAccessor = hPlaneAccessor; } + + HSCRIPT Plane() { return m_planeAccessor; } + void SetPlane( HSCRIPT hPlaneAccessor ) { m_planeAccessor = hPlaneAccessor; } + trace_t &GetTrace() { return m_tr; } void Destroy() { delete this; } private: trace_t m_tr; -}; - -//----------------------------------------------------------------------------- -// Exposes FireBulletsInfo_t to VScript -//----------------------------------------------------------------------------- -class CFireBulletsInfoAccessor -{ -public: - CFireBulletsInfoAccessor( FireBulletsInfo_t *info ) { m_info = info; } - - int GetShots() { return m_info->m_iShots; } - void SetShots( int value ) { m_info->m_iShots = value; } - - Vector GetSource() { return m_info->m_vecSrc; } - void SetSource( Vector value ) { m_info->m_vecSrc = value; } - Vector GetDirShooting() { return m_info->m_vecDirShooting; } - void SetDirShooting( Vector value ) { m_info->m_vecDirShooting = value; } - Vector GetSpread() { return m_info->m_vecSpread; } - void SetSpread( Vector value ) { m_info->m_vecSpread = value; } - - float GetDistance() { return m_info->m_flDistance; } - void SetDistance( float value ) { m_info->m_flDistance = value; } - - int GetAmmoType() { return m_info->m_iAmmoType; } - void SetAmmoType( int value ) { m_info->m_iAmmoType = value; } - - int GetTracerFreq() { return m_info->m_iTracerFreq; } - void SetTracerFreq( int value ) { m_info->m_iTracerFreq = value; } - - float GetDamage() { return m_info->m_flDamage; } - void SetDamage( float value ) { m_info->m_flDamage = value; } - int GetPlayerDamage() { return m_info->m_iPlayerDamage; } - void SetPlayerDamage( float value ) { m_info->m_iPlayerDamage = value; } - - int GetFlags() { return m_info->m_nFlags; } - void SetFlags( float value ) { m_info->m_nFlags = value; } - - float GetDamageForceScale() { return m_info->m_flDamageForceScale; } - void SetDamageForceScale( float value ) { m_info->m_flDamageForceScale = value; } - - HSCRIPT GetAttacker(); - void SetAttacker( HSCRIPT value ); - HSCRIPT GetAdditionalIgnoreEnt(); - void SetAdditionalIgnoreEnt( HSCRIPT value ); - - bool GetPrimaryAttack() { return m_info->m_bPrimaryAttack; } - void SetPrimaryAttack( bool value ) { m_info->m_bPrimaryAttack = value; } - - FireBulletsInfo_t *GetInfo() { return m_info; } - - void Destroy() { delete m_info; delete this; } - -private: - FireBulletsInfo_t *m_info; -}; - -//----------------------------------------------------------------------------- -// Exposes CUserCmd to VScript -//----------------------------------------------------------------------------- -class CUserCmdAccessor -{ -public: - CUserCmdAccessor( CUserCmd *cmd ) { m_cmd = cmd; } - - int GetCommandNumber() { return m_cmd->command_number; } - - int ScriptGetTickCount() { return m_cmd->tick_count; } - - const QAngle& GetViewAngles() { return m_cmd->viewangles; } - void SetViewAngles( const QAngle& val ) { m_cmd->viewangles = val; } - - float GetForwardMove() { return m_cmd->forwardmove; } - void SetForwardMove( float val ) { m_cmd->forwardmove = val; } - float GetSideMove() { return m_cmd->sidemove; } - void SetSideMove( float val ) { m_cmd->sidemove = val; } - float GetUpMove() { return m_cmd->upmove; } - void SetUpMove( float val ) { m_cmd->upmove = val; } - - int GetButtons() { return m_cmd->buttons; } - void SetButtons( int val ) { m_cmd->buttons = val; } - int GetImpulse() { return m_cmd->impulse; } - void SetImpulse( int val ) { m_cmd->impulse = val; } - - int GetWeaponSelect() { return m_cmd->weaponselect; } - void SetWeaponSelect( int val ) { m_cmd->weaponselect = val; } - int GetWeaponSubtype() { return m_cmd->weaponsubtype; } - void SetWeaponSubtype( int val ) { m_cmd->weaponsubtype = val; } - - int GetRandomSeed() { return m_cmd->random_seed; } - - int GetMouseX() { return m_cmd->mousedx; } - void SetMouseX( int val ) { m_cmd->mousedx = val; } - int GetMouseY() { return m_cmd->mousedy; } - void SetMouseY( int val ) { m_cmd->mousedy = val; } - -private: - CUserCmd *m_cmd; + HSCRIPT m_surfaceAccessor; + HSCRIPT m_planeAccessor; }; #endif diff --git a/sp/src/game/shared/sceneentity_shared.cpp b/sp/src/game/shared/sceneentity_shared.cpp index 80fcdc7109..653586bac4 100644 --- a/sp/src/game/shared/sceneentity_shared.cpp +++ b/sp/src/game/shared/sceneentity_shared.cpp @@ -109,7 +109,7 @@ void CSceneTokenProcessor::Error( const char *fmt, ... ) va_end( argptr ); Warning( "%s", string ); - Assert(0); + AssertMsg(0, "%s", string); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/shareddefs.h b/sp/src/game/shared/shareddefs.h index 63080104b7..d30843dcdf 100644 --- a/sp/src/game/shared/shareddefs.h +++ b/sp/src/game/shared/shareddefs.h @@ -210,7 +210,14 @@ enum CastVote #define bits_SUIT_DEVICE_FLASHLIGHT 0x00000002 #define bits_SUIT_DEVICE_BREATHER 0x00000004 -#define MAX_SUIT_DEVICES 3 +#ifdef MAPBASE +// Custom suit power devices +#define bits_SUIT_DEVICE_CUSTOM0 0x00000008 +#define bits_SUIT_DEVICE_CUSTOM1 0x00000010 +#define bits_SUIT_DEVICE_CUSTOM2 0x00000020 +#endif + +#define MAX_SUIT_DEVICES 6 // Mapbase boosts this to 6 for the custom devices //=================================================================================================================== @@ -658,6 +665,10 @@ class CBaseEntity; // Bullet firing information //----------------------------------------------------------------------------- class CBaseEntity; +#ifdef MAPBASE_VSCRIPT +// For the VScript functions in FireBUlletsInfo_t +FORWARD_DECLARE_HANDLE( HSCRIPT ); +#endif enum FireBulletsFlags_t { @@ -715,6 +726,10 @@ struct FireBulletsInfo_t #endif } +#ifdef MAPBASE + ~FireBulletsInfo_t() {} +#endif + int m_iShots; Vector m_vecSrc; Vector m_vecDirShooting; @@ -737,6 +752,46 @@ struct FireBulletsInfo_t // I planned ahead and made it a CUtlVector instead. CUtlVector *m_pIgnoreEntList; #endif + +#ifdef MAPBASE_VSCRIPT // These functions are used by VScript to expose FireBulletsInfo_t to users. + int GetShots() { return m_iShots; } + void SetShots( int value ) { m_iShots = value; } + + Vector GetSource() { return m_vecSrc; } + void SetSource( Vector value ) { m_vecSrc = value; } + Vector GetDirShooting() { return m_vecDirShooting; } + void SetDirShooting( Vector value ) { m_vecDirShooting = value; } + Vector GetSpread() { return m_vecSpread; } + void SetSpread( Vector value ) { m_vecSpread = value; } + + float GetDistance() { return m_flDistance; } + void SetDistance( float value ) { m_flDistance = value; } + + int GetAmmoType() { return m_iAmmoType; } + void SetAmmoType( int value ) { m_iAmmoType = value; } + + int GetTracerFreq() { return m_iTracerFreq; } + void SetTracerFreq( int value ) { m_iTracerFreq = value; } + + float GetDamage() { return m_flDamage; } + void SetDamage( float value ) { m_flDamage = value; } + int GetPlayerDamage() { return m_iPlayerDamage; } + void SetPlayerDamage( float value ) { m_iPlayerDamage = value; } + + int GetFlags() { return m_nFlags; } + void SetFlags( float value ) { m_nFlags = value; } + + float GetDamageForceScale() { return m_flDamageForceScale; } + void SetDamageForceScale( float value ) { m_flDamageForceScale = value; } + + HSCRIPT ScriptGetAttacker(); + void ScriptSetAttacker( HSCRIPT value ); + HSCRIPT ScriptGetAdditionalIgnoreEnt(); + void ScriptSetAdditionalIgnoreEnt( HSCRIPT value ); + + bool GetPrimaryAttack() { return m_bPrimaryAttack; } + void SetPrimaryAttack( bool value ) { m_bPrimaryAttack = value; } +#endif }; //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/usercmd.h b/sp/src/game/shared/usercmd.h index 20b4034594..70afaebd0e 100644 --- a/sp/src/game/shared/usercmd.h +++ b/sp/src/game/shared/usercmd.h @@ -127,6 +127,39 @@ class CUserCmd impulse = 0; } +#ifdef MAPBASE_VSCRIPT // These functions are needed for exposing CUserCmd to VScript. + int GetCommandNumber() { return command_number; } + + int ScriptGetTickCount() { return tick_count; } + + const QAngle& GetViewAngles() { return viewangles; } + void SetViewAngles( const QAngle& val ) { viewangles = val; } + + float GetForwardMove() { return forwardmove; } + void SetForwardMove( float val ) { forwardmove = val; } + float GetSideMove() { return sidemove; } + void SetSideMove( float val ) { sidemove = val; } + float GetUpMove() { return upmove; } + void SetUpMove( float val ) { upmove = val; } + + int GetButtons() { return buttons; } + void SetButtons( int val ) { buttons = val; } + int GetImpulse() { return impulse; } + void SetImpulse( int val ) { impulse = val; } + + int GetWeaponSelect() { return weaponselect; } + void SetWeaponSelect( int val ) { weaponselect = val; } + int GetWeaponSubtype() { return weaponsubtype; } + void SetWeaponSubtype( int val ) { weaponsubtype = val; } + + int GetRandomSeed() { return random_seed; } + + int GetMouseX() { return mousedx; } + void SetMouseX( int val ) { mousedx = val; } + int GetMouseY() { return mousedy; } + void SetMouseY( int val ) { mousedy = val; } +#endif + // For matching server and client commands for debugging int command_number; diff --git a/sp/src/game/shared/util_shared.cpp b/sp/src/game/shared/util_shared.cpp index c3d6419c55..f1dd7e0022 100644 --- a/sp/src/game/shared/util_shared.cpp +++ b/sp/src/game/shared/util_shared.cpp @@ -1161,6 +1161,59 @@ int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int } +static char s_NumBitsInNibble[ 16 ] = +{ + 0, // 0000 = 0 + 1, // 0001 = 1 + 1, // 0010 = 2 + 2, // 0011 = 3 + 1, // 0100 = 4 + 2, // 0101 = 5 + 2, // 0110 = 6 + 3, // 0111 = 7 + 1, // 1000 = 8 + 2, // 1001 = 9 + 2, // 1010 = 10 + 3, // 1011 = 11 + 2, // 1100 = 12 + 3, // 1101 = 13 + 3, // 1110 = 14 + 4, // 1111 = 15 +}; + +int UTIL_CountNumBitsSet( unsigned int nVar ) +{ + int nNumBits = 0; + + while ( nVar > 0 ) + { + // Look up and add in bits in the bottom nibble + nNumBits += s_NumBitsInNibble[ nVar & 0x0f ]; + + // Shift one nibble to the right + nVar >>= 4; + } + + return nNumBits; +} + +int UTIL_CountNumBitsSet( uint64 nVar ) +{ + int nNumBits = 0; + + while ( nVar > 0 ) + { + // Look up and add in bits in the bottom nibble + nNumBits += s_NumBitsInNibble[ nVar & 0x0f ]; + + // Shift one nibble to the right + nVar >>= 4; + } + + return nNumBits; +} + + int find_day_of_week( struct tm& found_day, int day_of_week, int step ) { return 0; diff --git a/sp/src/game/shared/util_shared.h b/sp/src/game/shared/util_shared.h index b24c61fc99..6ed58bf251 100644 --- a/sp/src/game/shared/util_shared.h +++ b/sp/src/game/shared/util_shared.h @@ -594,6 +594,9 @@ char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *p int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings ); +int UTIL_CountNumBitsSet( unsigned int nVar ); +int UTIL_CountNumBitsSet( uint64 nVar ); + //----------------------------------------------------------------------------- // Holidays //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/vscript_shared.h b/sp/src/game/shared/vscript_shared.h index 55e19a615f..a71d20b20c 100644 --- a/sp/src/game/shared/vscript_shared.h +++ b/sp/src/game/shared/vscript_shared.h @@ -32,6 +32,7 @@ inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = fal bool IsEntityCreationAllowedInScripts( void ); #ifdef MAPBASE_VSCRIPT +void RegisterSharedScriptConstants(); void RegisterSharedScriptFunctions(); #endif diff --git a/sp/src/game/shared/weapon_parse.cpp b/sp/src/game/shared/weapon_parse.cpp index 514da20234..7aa880a11e 100644 --- a/sp/src/game/shared/weapon_parse.cpp +++ b/sp/src/game/shared/weapon_parse.cpp @@ -76,10 +76,12 @@ extern itemFlags_t g_ItemFlags[7]; static CUtlDict< FileWeaponInfo_t*, unsigned short > m_WeaponInfoDatabase; +#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. #ifdef _DEBUG // used to track whether or not two weapons have been mistakenly assigned the wrong slot bool g_bUsedWeaponSlots[MAX_WEAPON_SLOTS][MAX_WEAPON_POSITIONS] = { 0 }; +#endif #endif //----------------------------------------------------------------------------- @@ -154,9 +156,11 @@ void ResetFileWeaponInfoDatabase( void ) } m_WeaponInfoDatabase.RemoveAll(); +#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. #ifdef _DEBUG memset(g_bUsedWeaponSlots, 0, sizeof(g_bUsedWeaponSlots)); #endif +#endif } #endif @@ -412,6 +416,7 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam m_bAllowFlipping = ( pKeyValuesData->GetInt( "AllowFlipping", 1 ) != 0 ) ? true : false; m_bMeleeWeapon = ( pKeyValuesData->GetInt( "MeleeWeapon", 0 ) != 0 ) ? true : false; +#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. #if defined(_DEBUG) && defined(HL2_CLIENT_DLL) // make sure two weapons aren't in the same slot & position if ( iSlot >= MAX_WEAPON_SLOTS || @@ -428,6 +433,7 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam } g_bUsedWeaponSlots[iSlot][iPosition] = true; } +#endif #endif // Primary ammo used diff --git a/sp/src/public/const.h b/sp/src/public/const.h index e923edb3fc..105c0ef995 100644 --- a/sp/src/public/const.h +++ b/sp/src/public/const.h @@ -253,11 +253,9 @@ enum SolidFlags_t #ifdef MAPBASE // From https://developer.valvesoftware.com/wiki/Owner FSOLID_COLLIDE_WITH_OWNER = 0X0400, +#endif FSOLID_MAX_BITS = 11 -#else - FSOLID_MAX_BITS = 10 -#endif }; //----------------------------------------------------------------------------- diff --git a/sp/src/public/mathlib/vector.h b/sp/src/public/mathlib/vector.h index a797da25bb..64e14802c3 100644 --- a/sp/src/public/mathlib/vector.h +++ b/sp/src/public/mathlib/vector.h @@ -1557,6 +1557,11 @@ class Quaternion // same data-layout as engine's vec4_t, inline void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f, vec_t iw=0.0f) { x = ix; y = iy; z = iz; w = iw; } +#ifdef MAPBASE_VSCRIPT + // Needed to get around vec_t recognition and inlining + void ScriptInit( float ix, float iy, float iz, float iw ) { Init( ix, iy, iz, iw ); } +#endif + bool IsValid() const; void Invalidate(); diff --git a/sp/src/public/tier1/convar.h b/sp/src/public/tier1/convar.h index e83c17c843..2c3647277d 100644 --- a/sp/src/public/tier1/convar.h +++ b/sp/src/public/tier1/convar.h @@ -21,6 +21,7 @@ #include "tier1/utlvector.h" #include "tier1/utlstring.h" #include "icvar.h" +#include "color.h" #ifdef _WIN32 #define FORCEINLINE_CVAR FORCEINLINE @@ -350,6 +351,7 @@ friend class ConVarRef; // Retrieve value FORCEINLINE_CVAR float GetFloat( void ) const; FORCEINLINE_CVAR int GetInt( void ) const; + FORCEINLINE_CVAR Color GetColor( void ) const; FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); } FORCEINLINE_CVAR char const *GetString( void ) const; @@ -435,6 +437,16 @@ FORCEINLINE_CVAR int ConVar::GetInt( void ) const return m_pParent->m_nValue; } +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVar::GetColor( void ) const +{ + unsigned char *pColorElement = ((unsigned char *)&m_pParent->m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} + //----------------------------------------------------------------------------- // Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. @@ -467,6 +479,7 @@ class ConVarRef // Get/Set value float GetFloat( void ) const; int GetInt( void ) const; + Color GetColor( void ) const; bool GetBool() const { return !!GetInt(); } const char *GetString( void ) const; @@ -521,6 +534,16 @@ FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const return m_pConVarState->m_nValue; } +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVarRef::GetColor( void ) const +{ + unsigned char *pColorElement = ((unsigned char *)&m_pConVarState->m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} + //----------------------------------------------------------------------------- // Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. //----------------------------------------------------------------------------- diff --git a/sp/src/public/tier1/utlsymbol.h b/sp/src/public/tier1/utlsymbol.h index e90be4660a..7b2d0282ea 100644 --- a/sp/src/public/tier1/utlsymbol.h +++ b/sp/src/public/tier1/utlsymbol.h @@ -16,6 +16,9 @@ #include "tier0/threadtools.h" #include "tier1/utlrbtree.h" #include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier1/utllinkedlist.h" +#include "tier1/stringpool.h" //----------------------------------------------------------------------------- @@ -85,13 +88,17 @@ class CUtlSymbol // of strings to symbols and back. The symbol class itself contains // a static version of this class for creating global strings, but this // class can also be instanced to create local symbol tables. +// +// This class stores the strings in a series of string pools. The first +// two bytes of each string are decorated with a hash to speed up +// comparisons. //----------------------------------------------------------------------------- class CUtlSymbolTable { public: // constructor, destructor - CUtlSymbolTable( int growSize = 0, int initSize = 32, bool caseInsensitive = false ); + CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false ); ~CUtlSymbolTable(); // Finds and/or creates a symbol based on the string @@ -111,6 +118,10 @@ class CUtlSymbolTable return m_Lookup.Count(); } + // We store one of these at the beginning of every string to speed + // up comparisons. + typedef unsigned short hashDecoration_t; + protected: class CStringPoolIndex { @@ -120,10 +131,8 @@ class CUtlSymbolTable } inline CStringPoolIndex( unsigned short iPool, unsigned short iOffset ) - { - m_iPool = iPool; - m_iOffset = iOffset; - } + : m_iPool(iPool), m_iOffset(iOffset) + {} inline bool operator==( const CStringPoolIndex &other ) const { @@ -158,7 +167,9 @@ class CUtlSymbolTable }; CTree m_Lookup; + bool m_bInsensitive; + mutable unsigned short m_nUserSearchStringHash; mutable const char* m_pUserSearchString; // stores the string data @@ -167,11 +178,14 @@ class CUtlSymbolTable private: int FindPoolWithSpace( int len ) const; const char* StringFromIndex( const CStringPoolIndex &index ) const; + const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const; friend class CLess; + friend class CSymbolHash; + }; -class CUtlSymbolTableMT : private CUtlSymbolTable +class CUtlSymbolTableMT : public CUtlSymbolTable { public: CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false ) @@ -189,9 +203,9 @@ class CUtlSymbolTableMT : private CUtlSymbolTable CUtlSymbol Find( const char* pString ) const { - m_lock.LockForRead(); + m_lock.LockForWrite(); CUtlSymbol result = CUtlSymbolTable::Find( pString ); - m_lock.UnlockRead(); + m_lock.UnlockWrite(); return result; } @@ -204,11 +218,7 @@ class CUtlSymbolTableMT : private CUtlSymbolTable } private: -#if defined(WIN32) || defined(_WIN32) mutable CThreadSpinRWLock m_lock; -#else - mutable CThreadRWLock m_lock; -#endif }; @@ -225,7 +235,6 @@ class CUtlSymbolTableMT : private CUtlSymbolTable // The handle is a CUtlSymbol for the dirname and the same for the filename, the accessor // copies them into a static char buffer for return. typedef void* FileNameHandle_t; -#define FILENAMEHANDLE_INVALID 0 // Symbol table for more efficiently storing filenames by breaking paths and filenames apart. // Refactored from BaseFileSystem.h @@ -258,6 +267,9 @@ class CUtlFilenameSymbolTable int PathIndex(const FileNameHandle_t &handle) { return (( const FileNameHandleInternal_t * )&handle)->path; } bool String( const FileNameHandle_t& handle, char *buf, int buflen ); void RemoveAll(); + void SpewStrings(); + bool SaveToBuffer( CUtlBuffer &buffer ); + bool RestoreFromBuffer( CUtlBuffer &buffer ); private: //CCountedStringPool m_StringPool; @@ -265,5 +277,50 @@ class CUtlFilenameSymbolTable mutable CThreadSpinRWLock m_lock; }; +// This creates a simple class that includes the underlying CUtlSymbol +// as a private member and then instances a private symbol table to +// manage those symbols. Avoids the possibility of the code polluting the +// 'global'/default symbol table, while letting the code look like +// it's just using = and .String() to look at CUtlSymbol type objects +// +// NOTE: You can't pass these objects between .dlls in an interface (also true of CUtlSymbol of course) +// +#define DECLARE_PRIVATE_SYMBOLTYPE( typename ) \ + class typename \ + { \ + public: \ + typename(); \ + typename( const char* pStr ); \ + typename& operator=( typename const& src ); \ + bool operator==( typename const& src ) const; \ + const char* String( ) const; \ + private: \ + CUtlSymbol m_SymbolId; \ + }; + +// Put this in the .cpp file that uses the above typename +#define IMPLEMENT_PRIVATE_SYMBOLTYPE( typename ) \ + static CUtlSymbolTable g_##typename##SymbolTable; \ + typename::typename() \ + { \ + m_SymbolId = UTL_INVAL_SYMBOL; \ + } \ + typename::typename( const char* pStr ) \ + { \ + m_SymbolId = g_##typename##SymbolTable.AddString( pStr ); \ + } \ + typename& typename::operator=( typename const& src ) \ + { \ + m_SymbolId = src.m_SymbolId; \ + return *this; \ + } \ + bool typename::operator==( typename const& src ) const \ + { \ + return ( m_SymbolId == src.m_SymbolId ); \ + } \ + const char* typename::String( ) const \ + { \ + return g_##typename##SymbolTable.String( m_SymbolId ); \ + } #endif // UTLSYMBOL_H diff --git a/sp/src/public/vphysics_interface.h b/sp/src/public/vphysics_interface.h index 1852ab8ccc..4d5d48ae96 100644 --- a/sp/src/public/vphysics_interface.h +++ b/sp/src/public/vphysics_interface.h @@ -961,6 +961,16 @@ 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; } +#endif }; #define VPHYSICS_SURFACEPROPS_INTERFACE_VERSION "VPhysicsSurfaceProps001" diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index 390f80cf76..af3e418c0f 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -291,6 +291,16 @@ class IScriptInstanceHelper virtual void *GetProxied( void *p ) { return p; } virtual bool ToString( void *p, char *pBuf, int bufSize ) { return false; } virtual void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) { return NULL; } + +#ifdef MAPBASE_VSCRIPT + virtual bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ) { return false; } + virtual bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ) { return false; } + + virtual ScriptVariant_t *Add( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Subtract( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Multiply( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Divide( void *p, ScriptVariant_t &variant ) { return NULL; } +#endif }; //--------------------------------------------------------- @@ -522,6 +532,39 @@ struct ScriptVariant_t #define SCRIPT_VARIANT_NULL ScriptVariant_t() +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +struct ScriptConstantBinding_t +{ + const char *m_pszScriptName; + const char *m_pszDescription; + ScriptVariant_t m_data; + unsigned m_flags; +}; + +//--------------------------------------------------------- +struct ScriptEnumDesc_t +{ + ScriptEnumDesc_t() : m_pszScriptName( 0 ), m_pszDescription( 0 ), m_flags( 0 ) + { + AllEnumsDesc().AddToTail(this); + } + + virtual void RegisterDesc() = 0; + + const char *m_pszScriptName; + const char *m_pszDescription; + CUtlVector m_ConstantBindings; + unsigned m_flags; + + static CUtlVector& AllEnumsDesc() + { + static CUtlVector enums; + return enums; + } +}; +#endif + #pragma warning(pop) @@ -556,6 +599,46 @@ struct ScriptVariant_t #define ScriptRegisterFunction( pVM, func, description ) ScriptRegisterFunctionNamed( pVM, func, #func, description ) #define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) do { static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); pVM->RegisterFunction( &binding ); } while (0) +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterConstant( pVM, constant, description ) ScriptRegisterConstantNamed( pVM, constant, #constant, description ) +#define ScriptRegisterConstantNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = constant; pVM->RegisterConstant( &binding ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define BEGIN_SCRIPTENUM( enumName, description ) \ + struct ScriptEnum##enumName##Desc_t : public ScriptEnumDesc_t \ + { \ + void RegisterDesc(); \ + }; \ + ScriptEnum##enumName##Desc_t g_##enumName##_EnumDesc; \ + \ + void ScriptEnum##enumName##Desc_t::RegisterDesc() \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + return; \ + \ + bInitialized = true; \ + \ + m_pszScriptName = #enumName; \ + m_pszDescription = description; \ + +#define DEFINE_ENUMCONST( constant, description ) DEFINE_ENUMCONST_NAMED( constant, #constant, description ) +#define DEFINE_ENUMCONST_NAMED( constant, scriptName, description ) do { ScriptConstantBinding_t *pBinding = &(m_ConstantBindings[m_ConstantBindings.AddToTail()]); pBinding->m_pszScriptName = scriptName; pBinding->m_pszDescription = description; pBinding->m_data = constant; pBinding->m_flags = SF_MEMBER_FUNC; } while (0); + +#define END_SCRIPTENUM() \ + } \ + + +#define GetScriptDescForEnum( enumName ) GetScriptDesc( ( className *)NULL ) +#endif + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -727,14 +810,37 @@ class IScriptVM //-------------------------------------------------------- virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) = 0; +#ifdef MAPBASE_VSCRIPT + //-------------------------------------------------------- + // External constants + //-------------------------------------------------------- + virtual void RegisterConstant( ScriptConstantBinding_t *pScriptConstant ) = 0; + + //-------------------------------------------------------- + // External enums + //-------------------------------------------------------- + virtual void RegisterEnum( ScriptEnumDesc_t *pEnumDesc ) = 0; +#endif + //-------------------------------------------------------- // External instances. Note class will be auto-registered. //-------------------------------------------------------- +#ifdef MAPBASE_VSCRIPT + // When a RegisterInstance instance is deleted, VScript normally treats it as a strong reference and only deregisters the instance itself, preserving the registered data + // it points to so the game can continue to use it. + // bAllowDestruct is supposed to allow VScript to treat it as a weak reference created by the script, destructing the registered data automatically like any other type. + // This is useful for classes pretending to be primitive types. + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance, bool bAllowDestruct = false ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance, bool bAllowDestruct = false ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance, bAllowDestruct ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL, bool bAllowDestruct = false) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance, bAllowDestruct ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } +#else virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) = 0; virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; template HSCRIPT RegisterInstance( T *pInstance ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance ); } template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } +#endif virtual void RemoveInstance( HSCRIPT ) = 0; void RemoveInstance( HSCRIPT hInstance, const char *pszInstance, HSCRIPT hScope = NULL ) { ClearValue( hScope, pszInstance ); RemoveInstance( hInstance ); } void RemoveInstance( const char *pszInstance, HSCRIPT hScope = NULL ) { ScriptVariant_t val; if ( GetValue( hScope, pszInstance, &val ) ) { if ( val.m_type == FIELD_HSCRIPT ) { RemoveInstance( val, pszInstance, hScope ); } ReleaseValue( val ); } } @@ -897,6 +1003,16 @@ class IScriptVM RegisterClass(classDescs[i]); } } + + void RegisterAllEnums() + { + CUtlVector& enumDescs = ScriptEnumDesc_t::AllEnumsDesc(); + FOR_EACH_VEC(enumDescs, i) + { + enumDescs[i]->RegisterDesc(); + RegisterEnum(enumDescs[i]); + } + } #endif }; diff --git a/sp/src/utils/vbsp/map.cpp b/sp/src/utils/vbsp/map.cpp index 77262686a0..d04bd24437 100644 --- a/sp/src/utils/vbsp/map.cpp +++ b/sp/src/utils/vbsp/map.cpp @@ -2709,12 +2709,22 @@ bool LoadMapFile( const char *pszFileName ) #ifdef PARALLAX_CORRECTED_CUBEMAPS // Fill out parallax obb matrix array - for (int i = 0; i < g_nCubemapSamples; i++) + // "i" is static so this code could account for + // multiple LoadMapFile() calls from instances, etc. + for (static int i = 0; i < g_nCubemapSamples; i++) { if (g_pParallaxObbStrs[i][0] != '\0') { entity_t* obbEnt = EntityByName(g_pParallaxObbStrs[i]); - g_pParallaxObbStrs[i] = ValueForKey(obbEnt, "transformationmatrix"); + if (obbEnt) + { + g_pParallaxObbStrs[i] = ValueForKey(obbEnt, "transformationmatrix"); + } + else + { + Warning( "Cannot find parallax obb \"%s\"\n", g_pParallaxObbStrs[i] ); + g_pParallaxObbStrs[i][0] = '\0'; + } } } diff --git a/sp/src/vpc_scripts/source_base.vpc b/sp/src/vpc_scripts/source_base.vpc index 372a257d9c..acf7c9b85f 100644 --- a/sp/src/vpc_scripts/source_base.vpc +++ b/sp/src/vpc_scripts/source_base.vpc @@ -41,7 +41,6 @@ $Configuration "Debug" // Mapbase base definitions $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] - $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } @@ -60,6 +59,5 @@ $Configuration "Release" // Mapbase base definitions $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] - $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } diff --git a/sp/src/vscript/vscript.cpp b/sp/src/vscript/vscript.cpp index 95efbe50d1..3d27500991 100644 --- a/sp/src/vscript/vscript.cpp +++ b/sp/src/vscript/vscript.cpp @@ -1,3 +1,13 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom implementation of VScript in Source 2013, created from scratch +// using the Alien Swarm SDK as a reference for Valve's library. +// +// Author(s): ReDucTor (header written by Blixibon) +// +// $NoKeywords: $ +//=============================================================================// + #include "vscript/ivscript.h" #include "tier1/tier1.h" diff --git a/sp/src/vscript/vscript.vpc b/sp/src/vscript/vscript.vpc index 1d660ccce4..36d3208e7e 100644 --- a/sp/src/vscript/vscript.vpc +++ b/sp/src/vscript/vscript.vpc @@ -13,6 +13,7 @@ $Configuration $Compiler { $AdditionalIncludeDirectories "$BASE;.\squirrel\include" + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 28132caadb..8b98ff7eb2 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1,3 +1,13 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom implementation of VScript in Source 2013, created from scratch +// using the Alien Swarm SDK as a reference for Valve's library. +// +// Author(s): ReDucTor (header written by Blixibon) +// +// $NoKeywords: $ +//=============================================================================// + #include "vscript/ivscript.h" #include "tier1/utlbuffer.h" #include "tier1/utlmap.h" @@ -148,11 +158,21 @@ class SquirrelVM : public IScriptVM //-------------------------------------------------------- virtual bool RegisterClass(ScriptClassDesc_t* pClassDesc) override; + //-------------------------------------------------------- + // External constants + //-------------------------------------------------------- + virtual void RegisterConstant(ScriptConstantBinding_t *pScriptConstant) override; + + //-------------------------------------------------------- + // External enums + //-------------------------------------------------------- + virtual void RegisterEnum(ScriptEnumDesc_t *pEnumDesc) override; + //-------------------------------------------------------- // External instances. Note class will be auto-registered. //-------------------------------------------------------- - virtual HSCRIPT RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance) override; + virtual HSCRIPT RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct = false) override; virtual void SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) override; virtual void RemoveInstance(HSCRIPT hInstance) override; @@ -243,7 +263,7 @@ namespace SQVector if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') { - return sqstd_throwerrorf(vm, "Unexpected key %s\n", key); + return sqstd_throwerrorf(vm, "Unexpected key %s", key); } Vector* v = nullptr; @@ -264,12 +284,12 @@ namespace SQVector if (key == nullptr) { - return sq_throwerror(vm, "Expected Vector._get(string)"); + return sq_throwerror(vm, "Expected Vector._set(string)"); } if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') { - return sqstd_throwerrorf(vm, "Unexpected key %s\n", key); + return sqstd_throwerrorf(vm, "Unexpected key %s", key); } Vector* v = nullptr; @@ -515,6 +535,35 @@ namespace SQVector return 1; } + SQInteger Scale(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, float)"); + } + + float s = 0.0; + + if (SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * s); + sq_remove(vm, -2); + + return 1; + } + else + { + return sq_throwerror(vm, "Expected (Vector, float)"); + } + } + SQInteger Dot(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -613,7 +662,7 @@ namespace SQVector } else { - return sq_throwerror(vm, "Invalid iteartor"); + return sq_throwerror(vm, "Invalid iterator"); } Assert(curkey && curkey[0] >= 'w' && curkey[0] <= 'z'); @@ -645,7 +694,7 @@ namespace SQVector {_SC("Length2DSqr"), Length2DSqr, 1, _SC(".")}, {_SC("Normalized"), Normalized, 1, _SC(".")}, {_SC("Norm"), Norm, 1, _SC(".")}, - {_SC("_div"), Divide, 2, _SC("..")}, + {_SC("Scale"), Scale, 2, _SC("..")}, {_SC("Dot"), Dot, 2, _SC("..")}, {_SC("Cross"), Cross, 2, _SC("..")}, {_SC("ToKVString"), ToKVString, 1, _SC(".")}, @@ -683,15 +732,19 @@ namespace SQVector struct ClassInstanceData { - ClassInstanceData(void* instance, ScriptClassDesc_t* desc, const char* instanceId = nullptr) : + ClassInstanceData(void* instance, ScriptClassDesc_t* desc, const char* instanceId = nullptr, bool allowDestruct = false) : instance(instance), desc(desc), - instanceId(instanceId) + instanceId(instanceId), + allowDestruct(allowDestruct) {} void* instance; ScriptClassDesc_t* desc; CUtlString instanceId; + + // Indicates this game-created instance is a weak reference and can be destructed (Blixibon) + bool allowDestruct; }; bool CreateParamCheck(const ScriptFunctionBinding_t& func, char* output) @@ -781,6 +834,35 @@ void PushVariant(HSQUIRRELVM vm, const ScriptVariant_t& value) } } +void GetVariantScriptString(const ScriptVariant_t& value, char *szValue, int iSize) +{ + switch (value.m_type) + { + case FIELD_VOID: + V_strncpy( szValue, "null", iSize ); + break; + case FIELD_FLOAT: + V_snprintf( szValue, iSize, "%f", value.m_float ); + break; + case FIELD_CSTRING: + V_snprintf( szValue, iSize, "\"%s\"", value.m_pszString ); + break; + case FIELD_VECTOR: + V_snprintf( szValue, iSize, "Vector( %f, %f, %f )", value.m_pVector->x, value.m_pVector->y, value.m_pVector->z ); + break; + case FIELD_INTEGER: + V_snprintf( szValue, iSize, "%i", value.m_int ); + break; + case FIELD_BOOLEAN: + V_snprintf( szValue, iSize, "%d", value.m_bool ); + break; + case FIELD_CHARACTER: + //char buf[2] = { value.m_char, 0 }; + V_snprintf( szValue, iSize, "\"%c\"", value.m_char ); + break; + } +} + bool getVariant(HSQUIRRELVM vm, SQInteger idx, ScriptVariant_t& variant) { switch (sq_gettype(vm, idx)) @@ -1004,7 +1086,10 @@ SQInteger function_stub(HSQUIRRELVM vm) SQInteger destructor_stub(SQUserPointer p, SQInteger size) { auto classInstanceData = (ClassInstanceData*)p; - classInstanceData->desc->m_pfnDestruct(classInstanceData->instance); + + if (classInstanceData->desc->m_pfnDestruct) + classInstanceData->desc->m_pfnDestruct(classInstanceData->instance); + classInstanceData->~ClassInstanceData(); return 0; } @@ -1045,7 +1130,7 @@ SQInteger constructor_stub(HSQUIRRELVM vm) { SQUserPointer p; sq_getinstanceup(vm, 1, &p, 0); - new(p) ClassInstanceData(instance, pClassDesc); + new(p) ClassInstanceData(instance, pClassDesc, nullptr, true); } sq_setreleasehook(vm, 1, &destructor_stub); @@ -1083,6 +1168,167 @@ SQInteger tostring_stub(HSQUIRRELVM vm) return 1; } +SQInteger get_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected _get(string)"); + } + + ScriptVariant_t var; + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->Get(classInstanceData->instance, key, var)) + { + PushVariant(vm, var); + } + else + { + return sqstd_throwerrorf(vm, "Unexpected key %s", key); + } + + return 1; +} + +SQInteger set_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected _set(string)"); + } + + ScriptVariant_t var; + getVariant( vm, -1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->Set(classInstanceData->instance, key, var)) + { + sq_pop(vm, 1); + } + else + { + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "Unexpected key %s", key); + } + + return 0; +} + +SQInteger add_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "Unexpected _add"); +} + +SQInteger sub_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Subtract( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "Unexpected _sub"); +} + +SQInteger mul_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper ) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "Unexpected _mul"); +} + +SQInteger div_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper ) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "Unexpected _div"); +} + SQInteger IsValid_stub(HSQUIRRELVM vm) { ClassInstanceData* classInstanceData = nullptr; @@ -1197,6 +1443,87 @@ void RegisterDocumentation(HSQUIRRELVM vm, const ScriptFuncDescriptor_t& pFuncDe sq_pop(vm, 1); } +void RegisterClassDocumentation(HSQUIRRELVM vm, const ScriptClassDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pClassDesc->m_pszDescription && pClassDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + const char *name = pClassDesc->m_pszScriptName; + const char *base = ""; + if (pClassDesc->m_pBaseDesc) + { + base = pClassDesc->m_pBaseDesc->m_pszScriptName; + } + + // 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, pClassDesc->m_pszDescription ? pClassDesc->m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterEnumDocumentation(HSQUIRRELVM vm, const ScriptEnumDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pClassDesc->m_pszDescription && pClassDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + const char *name = pClassDesc->m_pszScriptName; + + // RegisterEnumHelp(name, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterEnumHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, pClassDesc->m_pszDescription ? pClassDesc->m_pszDescription : "", -1); + sq_call(vm, 3, SQFalse, SQFalse); + sq_pop(vm, 1); +} + +void RegisterConstantDocumentation( HSQUIRRELVM vm, const ScriptConstantBinding_t* pConstDesc, const char *pszAsString, ScriptEnumDesc_t* pEnumDesc = nullptr ) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pConstDesc->m_pszDescription && pConstDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pEnumDesc) + { + V_strcat_safe(name, pEnumDesc->m_pszScriptName); + V_strcat_safe(name, "."); + } + + V_strcat_safe(name, pConstDesc->m_pszScriptName); + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s (%s)", pszAsString, ScriptDataTypeToName(pConstDesc->m_data.m_type)); + + // RegisterConstHelp(name, signature, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterConstHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pConstDesc->m_pszDescription ? pConstDesc->m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + bool SquirrelVM::Init() { @@ -1307,53 +1634,157 @@ bool SquirrelVM::Init() } DocumentedFuncs <- {} + DocumentedClasses <- {} + DocumentedEnums <- {} + DocumentedConsts <- {} + + function ModForAlias(name, signature, description) + { + // 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; + } function RegisterHelp(name, signature, description) { if (description.len() && description[0] == '#') { - // 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; + ModForAlias(name, signature, description) } DocumentedFuncs[name] <- [signature, description]; } + function RegisterClassHelp(name, baseclass, description) + { + DocumentedClasses[name] <- [baseclass, description]; + } + + function RegisterEnumHelp(name, description) + { + DocumentedEnums[name] <- description; + } + + function RegisterConstHelp(name, signature, description) + { + if (description.len() && description[0] == '#') + { + ModForAlias(name, signature, description) + } + DocumentedConsts[name] <- [signature, description]; + } + + function PrintClass(name, doc) + { + printl("====================================="); + printl("Class: " + name); + printl("Base: " + doc[0]); + if (doc[1].len()) + printl("Description: " + doc[1]); + printl("====================================="); + print("\n"); + } + + function PrintFunc(name, doc) + { + printl("Function: " + name); + if (doc[0] == null) + { + // Is an aliased function + print("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos()["parameters"]) + { + if (k == 0 && v == "this") continue; + if (k > 1) print(", "); + print(v); + } + printl(")"); + } + else + { + printl("Signature: " + doc[0]); + } + if (doc[1].len()) + printl("Description: " + doc[1]); + print("\n"); + } + + function PrintEnum(name, doc) + { + printl("====================================="); + printl("Enum: " + name); + if (doc.len()) + printl("Description: " + doc); + printl("====================================="); + print("\n"); + } + + function PrintConst(name, doc) + { + printl("Constant: " + name); + if (doc[0] == null) + { + // Is an aliased function + print("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos()["parameters"]) + { + if (k == 0 && v == "this") continue; + if (k > 1) print(", "); + print(v); + } + printl(")"); + } + else + { + printl("Value: " + doc[0]); + } + if (doc[1].len()) + printl("Description: " + doc[1]); + print("\n"); + } + function PrintHelp(pattern = "*") { local foundMatches = false; + foreach(name, doc in DocumentedClasses) + { + if (pattern == "*" || name.tolower().find(pattern.tolower()) != null) + { + foundMatches = true; + PrintClass(name, doc) + } + } + foreach(name, doc in DocumentedFuncs) { if (pattern == "*" || name.tolower().find(pattern.tolower()) != null) { foundMatches = true; - printl("Function: " + name); - if (doc[0] == null) - { - // Is an aliased function - print("Signature: function " + name + "("); - foreach(k,v in this[name].getinfos()["parameters"]) - { - if (k == 0 && v == "this") continue; - if (k > 1) print(", "); - print(v); - } - printl(")"); - } - else - { - printl("Signature: " + doc[0]); - } - if (doc[1].len()) - printl("Description: " + doc[1]); - print("\n"); + PrintFunc(name, doc) + } + } + + foreach(name, doc in DocumentedEnums) + { + if (pattern == "*" || name.tolower().find(pattern.tolower()) != null) + { + foundMatches = true; + PrintEnum(name, doc) + } + } + + foreach(name, doc in DocumentedConsts) + { + if (pattern == "*" || name.tolower().find(pattern.tolower()) != null) + { + foundMatches = true; + PrintConst(name, doc) } } @@ -1743,6 +2174,30 @@ bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) sq_newclosure(vm_, tostring_stub, 0); sq_newslot(vm_, -3, SQFalse); + sq_pushstring(vm_, "_get", -1); + sq_newclosure(vm_, get_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_set", -1); + sq_newclosure(vm_, set_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_add", -1); + sq_newclosure(vm_, add_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_sub", -1); + sq_newclosure(vm_, sub_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_mul", -1); + sq_newclosure(vm_, mul_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_div", -1); + sq_newclosure(vm_, div_stub, 0); + sq_newslot(vm_, -3, SQFalse); + sq_pushstring(vm_, "IsValid", -1); sq_newclosure(vm_, IsValid_stub, 0); sq_newslot(vm_, -3, SQFalse); @@ -1785,10 +2240,84 @@ bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) sq_pop(vm_, 2); + RegisterClassDocumentation(vm_, pClassDesc); + return true; } -HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance) +void SquirrelVM::RegisterConstant(ScriptConstantBinding_t* pScriptConstant) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pScriptConstant); + + if (!pScriptConstant) + return; + + sq_pushconsttable(vm_); + sq_pushstring(vm_, pScriptConstant->m_pszScriptName, -1); + + PushVariant(vm_, pScriptConstant->m_data); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + + char szValue[64]; + GetVariantScriptString( pScriptConstant->m_data, szValue, sizeof(szValue) ); + RegisterConstantDocumentation(vm_, pScriptConstant, szValue); +} + +void SquirrelVM::RegisterEnum(ScriptEnumDesc_t* pEnumDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pEnumDesc); + + if (!pEnumDesc) + return; + + sq_pushconsttable(vm_); + sq_pushstring(vm_, pEnumDesc->m_pszScriptName, -1); + + // Check if class name is already taken + if (sq_get(vm_, -2) == SQ_OK) + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + if (!sq_isnull(obj)) + { + sq_pop(vm_, 2); + return; + } + } + + sq_pop(vm_, 1); + + // HACKHACK: I have no idea how to declare enums with the current API. + // For now, we'll just cram everything into a script buffer and compile it. (Blixibon) + char szScript[2048]; + V_snprintf( szScript, sizeof(szScript), "enum %s {\n", pEnumDesc->m_pszScriptName ); + + for (int i = 0; i < pEnumDesc->m_ConstantBindings.Count(); ++i) + { + auto& scriptConstant = pEnumDesc->m_ConstantBindings[i]; + + char szValue[64]; + GetVariantScriptString( scriptConstant.m_data, szValue, sizeof(szValue) ); + + V_snprintf( szScript, sizeof(szScript), "%s%s = %s\n", szScript, scriptConstant.m_pszScriptName, szValue ); + + RegisterConstantDocumentation(vm_, &scriptConstant, szValue, pEnumDesc); + } + + V_strcat_safe( szScript, "}" ); + + Run( szScript ); + + RegisterEnumDocumentation(vm_, pEnumDesc); +} + +HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct) { SquirrelSafeCheck safeCheck(vm_); @@ -1812,10 +2341,10 @@ HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance) { SQUserPointer p; sq_getinstanceup(vm_, -1, &p, 0); - new(p) ClassInstanceData(pInstance, pDesc); + new(p) ClassInstanceData(pInstance, pDesc, nullptr, bAllowDestruct); } - sq_setreleasehook(vm_, -1, &destructor_stub_instance); + sq_setreleasehook(vm_, -1, bAllowDestruct ? &destructor_stub : &destructor_stub_instance); HSQOBJECT* obj = new HSQOBJECT; sq_resetobject(obj); @@ -2470,6 +2999,8 @@ void SquirrelVM::WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQI else if (!pClassInstanceData->instanceId.IsEmpty()) { pBuffer->PutString(pClassInstanceData->instanceId); + + pBuffer->PutChar(pClassInstanceData->allowDestruct ? 1 : 0); } else { @@ -2720,7 +3251,10 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) sq_getstackobj(vm_, -1, &env); if (!sq_isnull(env)) { - _closure(*obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); + if (_closure( *obj ) == nullptr) + Warning("Closure is null\n"); + else + _closure(*obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); } sq_poptop(vm_); @@ -2918,6 +3452,8 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) if (*instanceName) { + bool allowDestruct = (pBuffer->GetChar() == 1); + auto instance = pClassDesc->pHelper->BindOnRead((HSCRIPT)hinstance, nullptr, instanceName); if (instance == nullptr) { @@ -2931,9 +3467,9 @@ void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) { SQUserPointer p; sq_getinstanceup(vm_, -1, &p, 0); - new(p) ClassInstanceData(instance, pClassDesc, instanceName); + new(p) ClassInstanceData(instance, pClassDesc, instanceName, allowDestruct); } - sq_setreleasehook(vm_, -1, &destructor_stub); + sq_setreleasehook(vm_, -1, allowDestruct ? &destructor_stub : &destructor_stub_instance); } else {