|
|
(12 intermediate revisions by 11 users not shown) |
Line 1: |
Line 1: |
| {{cleanup}} {{tutpov}} | | {{LanguageBar}} |
| | Push gameplay is one of the many types of mapping techniques out there at the moment. The idea is to create an area that forces the [[player]] to move forward within the map and causes the area usually not able to be back tracked, preventing the player from reaching previous areas of the map. It is important to make sure that the player understands that it is not necessary to backtrack, otherwise they could possibly be frustrated by thinking they just can't figure out how to get back there. |
|
| |
|
| I realize this is not a complete walkthrough tutorial, but this is as good as i am able to get at my current work load.
| | One of the main ideas is to create an event that "pushes" the player forward. Mainly how you create that event is up to you but here are a few ways that mods and even Valve have implemented into their work. |
|
| |
|
| I had originally posted on here, in the discussion page, as well as on hl2coding, and would like to direct you back there so i dont have to copy over all the images, that are used just for relaying what im trying to do with this entity.
| | == Height push == |
|
| |
|
| http://www.hl2coding.com/forums/viewtopic.php?t=1205
| | One of the most common Push methods encountered is the Height Push. The Height Push forces the player to move or jump off a high point, and land a few feet below. This kind of Push is used most often because it is very easy to implement in a map. Once the player drops off the ledge then the player cannot go back due to the ledge being just a little too high to reach. One map in Half-Life 2 Episode 1 utilizes this method; the player exits an old hospital and drops off a ledge that is about waist high. The player is unable to return to that area because the ledge is too high. |
|
| |
|
| You guys can ... wikificate this all you wish. Its working, tested and in game as it stands.
| | Besides ledges, here are some other ideas of how to enforce a Push using heights. |
| | *Shafts ([[elevator]]s, mines, etc) |
| | *[[Ropes]] and [[ladder]]s that end before they reach the floor |
| | *A wind-tunnel could push a player ''up'' and deny any movement back downward. |
|
| |
|
| 1 Brush Entity
| | Keep in mind that with the new physics-based puzzles that have been introduced since [[Half-Life 2]], players may attempt to create their own path by stacking objects to climb. If you wish to avoid this, carefully plan the available props and/or weapons (such as a [[gravity gun]]) accordingly. |
| 1 FGD Entry
| |
|
| |
|
| Done...
| | == Destruction push == |
| | Another common Push method involves a disaster or destruction of the path back. In [[Half-Life]] 1, this was usually a tunnel caving in or a catwalk breaking. Other ideas include cliff-side paths that crumble beneath the player, outbreaks of fire or other hazards, and indefensible positions coming under heavy assault. |
|
| |
|
| == Brush Entity == | | == Combination == |
| <pre>//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
| | In Half-Life 2, when the player is cornered by the [[Metrocop]]s directly before meeting [[Alyx]], he may try to flee back up the stairs he just came down. If this is attempted, the stairs break. While this is mainly a height push, it obviously has a destruction element to it as well. Such combinations of methods do well, as it lessens the player's feeling of being a "rat in a maze". |
| //
| |
| // Purpose: White --- Push Point Entity
| |
| //
| |
| //=============================================================================//
| |
|
| |
| #include "cbase.h"
| |
|
| |
| extern ConVar showtriggers;
| |
|
| |
| class CPushPoint : public CBaseToggle
| |
| {
| |
| public:
| |
| DECLARE_CLASS( CPushPoint, CBaseToggle );
| |
| DECLARE_DATADESC();
| |
|
| |
| void Spawn( void );
| |
|
| |
| virtual void StartTouch( CBaseEntity *pOther );
| |
| virtual void EndTouch( CBaseEntity *pOther );
| |
| void PlayerUpdateThink();
| |
| bool CreateVPhysics( void );
| |
|
| |
| // Push Functions
| |
| int GetStatus( void );
| |
| private:
| |
| // Push Controls
| |
| int m_nControlled; // Has this point been captured? By which team?
| |
| float m_fCaptured; // Timer for capture
| |
|
| |
| // Alien Variables & Outputs
| |
| COutputEvent m_OnAlienCapture;
| |
| COutputEvent m_OnAlienStartTouch;
| |
| COutputEvent m_OnAlienEndTouch;
| |
| CUtlVector<CBasePlayerHandle> m_hAliens;
| |
|
| |
|
| // Human Variables & Outputs
| | == Other == |
| COutputEvent m_OnHumanCapture;
| | A few other ideas that have been seen and used. |
| COutputEvent m_OnHumanStartTouch;
| | * One-way [[Teleporters|teleport]]s |
| COutputEvent m_OnHumanEndTouch;
| | * [[Door]]s that close behind the player |
| CUtlVector<CBasePlayerHandle> m_hHumans;
| | * Timed doors that require the player to push a button and then rush to get through in time |
| | * Darkness can occasionally be used as a deterrent |
| | * Greater amounts of tough enemy on one side |
|
| |
|
| // Mapping Tagged Fields
| | [[Category:Level Design]] |
| string_t m_sNext;
| |
| string_t m_sPrev;
| |
| string_t m_sLocation;
| |
| float m_fCapture_time;
| |
| | |
| // Misc. Outputs
| |
| COutputEvent m_OnCapture;
| |
| COutputEvent m_OnStartTouch;
| |
| COutputEvent m_OnEndTouch;
| |
| };
| |
|
| |
| LINK_ENTITY_TO_CLASS( push_block, CPushPoint );
| |
|
| |
| // Start of our data description for the class
| |
| BEGIN_DATADESC( CPushPoint )
| |
|
| |
| // Think Functions
| |
| DEFINE_THINKFUNC( PlayerUpdateThink ),
| |
| | |
| // Mapping Tagged Fields
| |
| DEFINE_KEYFIELD( m_sNext, FIELD_STRING, "next" ),
| |
| DEFINE_KEYFIELD( m_sPrev, FIELD_STRING, "prev" ),
| |
| DEFINE_KEYFIELD( m_sLocation, FIELD_STRING, "location"),
| |
| DEFINE_KEYFIELD( m_fCapture_time, FIELD_FLOAT, "timer" ),
| |
|
| |
| // Alien Outputs
| |
| DEFINE_OUTPUT( m_OnAlienCapture, "OnAlienCapture" ),
| |
| DEFINE_OUTPUT( m_OnAlienStartTouch, "OnAlienStartTouch" ),
| |
| DEFINE_OUTPUT( m_OnAlienEndTouch, "OnAlienEndTouch" ),
| |
| | |
| // Human Outputs
| |
| DEFINE_OUTPUT( m_OnHumanCapture, "OnHumanCapture" ),
| |
| DEFINE_OUTPUT( m_OnHumanStartTouch, "OnHumanStartTouch" ),
| |
| DEFINE_OUTPUT( m_OnHumanEndTouch, "OnHumanEndTouch" ),
| |
| | |
| // Misc. Outputs
| |
| DEFINE_OUTPUT( m_OnCapture, "OnCapture" ),
| |
| DEFINE_OUTPUT( m_OnStartTouch, "OnStartTouch" ),
| |
| DEFINE_OUTPUT( m_OnEndTouch, "OnEndTouch" ),
| |
|
| |
| END_DATADESC()
| |
|
| |
| //-----------------------------------------------------------------------------
| |
| // Purpose: Sets up the entity's initial state
| |
| //-----------------------------------------------------------------------------
| |
| void CPushPoint::Spawn( void )
| |
| {
| |
| m_nControlled = 0;
| |
|
| |
| // We should use the bsp
| |
| SetSolid( SOLID_BSP );
| |
| // And not collide with it
| |
| AddSolidFlags( FSOLID_NOT_SOLID );
| |
| // But still use it for interaction
| |
| AddSolidFlags( FSOLID_TRIGGER );
| |
|
| |
| // use the trigger no draw garbage
| |
| if ( showtriggers.GetInt() == 0 )
| |
| {
| |
| AddEffects( EF_NODRAW );
| |
| }
| |
| | |
| // Use our brushmodel
| |
| SetModel( STRING( GetModelName() ) );
| |
|
| |
| // Create our physics hull information
| |
| CreateVPhysics();
| |
| }
| |
|
| |
| //-----------------------------------------------------------------------------
| |
| // Purpose: Setup our physics information so we collide properly
| |
| //-----------------------------------------------------------------------------
| |
| bool CPushPoint::CreateVPhysics( void )
| |
| {
| |
| // For collisions with physics objects
| |
| VPhysicsInitShadow( false, false );
| |
|
| |
| return true;
| |
| }
| |
|
| |
|
| |
| int CPushPoint::GetStatus( void )
| |
| {
| |
| return m_nControlled;
| |
| }
| |
|
| |
| void CPushPoint::StartTouch( CBaseEntity *pOther )
| |
| {
| |
| // Someone has touched me
| |
| if (!pOther->IsPlayer())
| |
| return;
| |
| | |
| // they are a player... grab him/her
| |
| CBasePlayer *pPlayer = ToBasePlayer(pOther);
| |
|
| |
| // if their team is in control of me...
| |
| if (pPlayer->GetTeamNumber() == m_nControlled)
| |
| {
| |
| // let them know and skip out
| |
| Msg("You already control this point '%s'.\n", m_sLocation.ToCStr() );
| |
| return;
| |
| }
| |
| | |
| // Someone has touched me and im not captured by them...
| |
| m_OnStartTouch.FireOutput(pOther,this);
| |
| | |
| // See which previous point to call...
| |
| string_t pPrevious = pPlayer->GetTeamNumber() == TEAM_HUMANS? m_sNext : m_sPrev;
| |
|
| |
| // See who the fuck just touched me
| |
| pPlayer->GetTeamNumber() == TEAM_HUMANS? m_OnHumanStartTouch.FireOutput(pPlayer, this) : m_OnAlienStartTouch.FireOutput(pPlayer, this);;
| |
| | |
| // Grab the previous push point
| |
| CPushPoint *pPush = dynamic_cast<CPushPoint *>(gEntList.FindEntityByName( NULL, pPrevious, NULL ));
| |
|
| |
| // If there is a previous push point AND its not captured by the jerk who touched me...
| |
| if (pPush && pPush->GetStatus() != pPlayer->GetTeamNumber())
| |
| {
| |
| Msg("You do not control the previous point '%s'.\nGo back.\n", m_sLocation.ToCStr() );
| |
| return ;
| |
| }
| |
| | |
| // Cool, i guess i can start thinking now.
| |
| SetThink( &CPushPoint::PlayerUpdateThink );
| |
| SetNextThink( gpGlobals->curtime + 0.2 );
| |
| | |
| BaseClass::StartTouch( pOther );
| |
| }
| |
|
| |
|
| |
| void CPushPoint::EndTouch( CBaseEntity *pOther )
| |
| {
| |
| // Someone stopped touching me, is it a player?
| |
| if (pOther->IsPlayer())
| |
| {
| |
| // grab the player
| |
| CBasePlayer *player = ToBasePlayer(pOther);
| |
| | |
| // see what they team they are on...
| |
| if (player->GetTeamNumber() == TEAM_HUMANS)
| |
| {
| |
| // remove them
| |
| m_hHumans.FindAndRemove(player);
| |
| // Fire output
| |
| m_OnHumanEndTouch.FireOutput(player,this);
| |
| }
| |
| else if (player->GetTeamNumber() == TEAM_ALIENS)
| |
| {
| |
| // remove them
| |
| m_hAliens.FindAndRemove(player);
| |
| // fire output
| |
| m_OnAlienEndTouch.FireOutput(player,this);
| |
| }
| |
| | |
| // If there is no one of importance touching me...
| |
| if (m_hAliens.Count() == 0 && m_hHumans.Count() == 0)
| |
| {
| |
| // Clear the thinking and timer
| |
| SetThink(NULL);
| |
| SetNextThink(0);
| |
| m_fCaptured = 0;
| |
| }
| |
| }
| |
| BaseClass::EndTouch( pOther );
| |
| }
| |
| // look for dead/spectating players in our volume, to call touch on
| |
| void CPushPoint::PlayerUpdateThink()
| |
| {
| |
| // Set up my next thought process
| |
| SetThink( &CPushPoint::PlayerUpdateThink );
| |
| SetNextThink( gpGlobals->curtime + 0.2 );
| |
| | |
| // Clear out the lists
| |
| m_hAliens.RemoveAll();
| |
| m_hHumans.RemoveAll();
| |
| | |
| // See who is intersecting me and put them into the appropriate list
| |
| for (int i=0; i<gpGlobals->maxClients; i++ )
| |
| {
| |
| // this player
| |
| CBasePlayer *player = UTIL_PlayerByIndex( i );
| |
|
| |
| // is ... not a player...
| |
| if ( !player )
| |
| continue;
| |
|
| |
| // oh they are, they have to be alive too so...
| |
| if ( !player->IsAlive() )
| |
| continue;
| |
|
| |
| // if the player is intersecting the trigger, track it by storing them into the appropriate listing
| |
| if ( Intersects( player ) )
| |
| player->GetTeamNumber() == TEAM_ALIENS ? m_hAliens.AddToTail( player ) : m_hHumans.AddToTail( player );
| |
| }
| |
| | |
| DevMsg(1,"humans %d aliens %d\n capture time %.2f offset %.2f", m_hHumans.Count(), m_hAliens.Count(), m_fCaptured, m_fCapture_time);
| |
| if (m_hAliens.Count() && !m_hHumans.Count() || !m_hAliens.Count() && m_hHumans.Count())
| |
| if (m_fCaptured)
| |
| {
| |
| if (m_fCaptured < gpGlobals->curtime)
| |
| {
| |
| m_nControlled = m_hAliens.Count() ? TEAM_ALIENS : TEAM_HUMANS;
| |
| Msg("You have just captured the %s\n", m_sLocation.ToCStr());
| |
| SetThink(NULL);
| |
| SetNextThink(0);
| |
| }
| |
| }
| |
| else
| |
| m_fCaptured = gpGlobals->curtime + m_fCapture_time;
| |
| else
| |
| if (m_fCaptured)
| |
| {
| |
| Msg("Fuck He's Here!\n");
| |
| m_fCaptured = 0;
| |
| }
| |
| }</pre>
| |
| == FGD Entry ==
| |
| <pre>@include "base.fgd"
| |
| | |
| //-------------------------------------------------------------------------
| |
| //
| |
| // Push Point Map Entity
| |
| //
| |
| //-------------------------------------------------------------------------
| |
| @SolidClass base(Targetname) = push_block : "Push Entity Brush."
| |
| [ | |
| // Mapping Tagged Fields
| |
| next(string) : "Next Objective" : "" : "Set this to the next objective's name."
| |
| prev(string) : "Previous Objective" : "" : "Set this to the previous objective's name."
| |
| location(string) : "Location" : "" : "Set this to the name of this location to be echoed."
| |
| timer(float) : "Time To Capture" : "1.0" : "Set this to the time it takes to capture this point."
| |
| | |
| // Alien Outputs
| |
| output OnAlienCapture(void) : "Fired when the Aliens team captures this point."
| |
| output OnAlienStartTouch(void) : "Fired when the Aliens team touches this point, if this point has not been captured."
| |
| output OnAlienEndTouch(void) : "Fired when the Aliens team stops touching this point, if this point has not been captured."
| |
| | |
| // Human Outputs
| |
| output OnHumanCapture(void) : "Fired when the Humans team captures this point."
| |
| output OnHumanStartTouch(void) : "Fired when the Humans team touches this point, if this point has not been captured."
| |
| output OnHumanEndTouch(void) : "Fired when the Humans team stops touching this point, if this point has not been captured."
| |
| | |
| // Misc. Outputs
| |
| output OnCapture(void) : "Fired when this point is captured."
| |
| output OnStartTouch(void) : "Fired when this point is touched, if this point has not been captured."
| |
| output OnEndTouch(void) : "Fired when a player stops touching this point, if this point has not been captured."
| |
| ]</pre>
| |
| | |
| ----
| |
|
| |
| == Contact me as needed ==
| |
| Full tutorial is possibly going to be on my site if this post is frequented enough to give me cause enough to write it.
| |
| | |
| [[User:Imatard|ImaTard]] 04:14, 10 Feb 2006 (PST) | |