always thought "that's way too much for me" (I was mostly used to CS mapping).I also started out as a mapper, years ago. I eventually started experimenting with the game code and I somehow managed to make the MP5 launch a grenade with each shot, but I couldn't really do much else. Now, years later, I do programming for a living... So yeah, give it a go, and don't get discouraged if things seem complicated at first. Feel free to ask for help whenever you're stuck.
Half-Life\yourmoddirectory\maps
directory (where yourmoddirectory
is the name of your mod's directory)? And are you starting hl.exe
with the -game yourmoddirectory
argument (you can also start your mod through Steam, which does the same thing)? If you're not doing that, then you're running the base game Half-Life (not your mod), which will only look for bsp files in the Half-Life\valve\maps
directory.func_door_rotating
's don't have origin brushes. I replied to your question about that, but what I didn't mention explicitly is that an origin brush must be part of an entity. Normally you'd create both the visible brushes and the origin brush, select all of them, and then turn them into an entity. You already have a func_door_rotating
entity, so in this case you'd create an origin brush, then select both the func_door_rotating
and the origin brush, then press the 'To Entity' button (Ctrl+T), and select 'Yes' when it asks you to add the selected solids to the existing entity.map roomone
in the console?┌──────┐ ┌──────┐ ┌──┬───┐ ┌──────┐ ┌──┬───┐
└──┐ │ splits into: └──┬───┤ or └──┤ │ or └──┬───┤ or └──┤ │
┌──┘ │ ┌──┴───┤ ┌──┴───┤ ┌──┤ │ ┌──┤ │
└──────┘ └──────┘ └──────┘ └──┴───┘ └──┴───┘
As shown above, there are multiple ways in which this corridor can be split up into vis nodes. In the first 3 cases, every node is visible from any other node. But in the last case, the top-left node is not visible from the bottom-left node, and vice versa. With a hint brush, you can force the compile tools to produce that particular space partitioning.return DefaultDeploy...
), so the second line (EMIT_SOUND(...
) is never executed.-1.0f
.FireBulletsPlayer
is a CBaseEntity
member function, so it doesn't know about CBasePlayer
fields like m_flDoubleDamageBonusTime
(it can be called on any entity, after all). One way to work around that is by creating a virtual float GetDamageBonus()
function in CBaseEntity
that returns 1.0f
, but which you override in CBasePlayer
to return 2.0f
if HasDoubleDamageBonus()
is true (you can look at the CBaseEntity::IsPlayer
and CBasePlayer::IsPlayer
functions for an example of how a virtual function can be overridden in a child class). You can then use that function inside FireBulletsPlayer
as following:pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg9MM * GetDamageBonus(), vecDir, &tr, DMG_BULLET);
TraceAttack
directly, and since weapons contain a pointer to the player (CBasePlayer* m_pPlayer
), you should be able to obtain the damage factor for them with m_pPlayer->GetDamageBonus()
. For explosives I think you'll need to look at the RadiusDamage
function, probably using CBaseEntity::Instance(ENT(pevInflictor))->GetDamageBonus()
to get the damage bonus, if I understand how that works correctly.g_iSkillLevel
, you'll see that the CGameRules::RefreshSkillData
function in gamerules.cpp
forces the skill-level between 1 and 3 before it looks up skill-specific health and damage values. But there are also a few places throughout the game-code where the skill-level is checked specifically, so it's a bit more complicated than just increasing the maximum skill-level value.intromm
. When you look at the properties of that entity (press the SmartEdit button so you can see the raw keyvalues) then you'll see that it triggers multiple other entities, each at a specific time:
targetname intromm -- the name of this multi_manager
intro .5 -- someone at Valve forgot to remove this, because there's no entity with the name 'intro'
td1 15 -- the door to the HEV suit room
td1l 14.5 -- the light above the door to the HEV suit room
holo1light 0 -- the hologram light
sent_intro 0 -- the scripted_sentence entity for the hologram welcome speech (sentence: !HOLO_WELCOME)
sent_button 14.5 -- the scripted_sentence for the hologram speech about buttons (sentence: !HOLO_BUTTON)
butlight 15.1 -- the light above the button in front of the hologram
butsprite 15.2 -- the twinkling sprite that shows up above the button
J.A.C.K. can display arrows between entities that trigger each other, but unfortunately that doesn't work for multi_managers. Fortunately J.A.C.K. does have a way to search for entities with specific names: in the menu, go to Map -> Entity report, then in the Filter area of that window, enable 'by key/value', enter 'targetname' as key and the name of the entity you're looking for as value.{
"origin" "-1328 -1484 -4"
"butsprite" "15.2"
"butlight" "15.1"
"sent_button" "14.5"
"sent_intro" "0"
"holo1light" "0"
"td1l" "14.5"
"td1" "15"
"intro" ".5"
"targetname" "intromm"
"classname" "multi_manager"
}
The key/value order is different, but that doesn't matter. Now change the 14/15 second delays to something suitable and save the map. Just be sure not to remove or add any double-quotes, otherwise Half-Life won't be able to read these entities anymore.ent_create
command will place entities at the point that you're looking at, but maybe that doesn't work correctly when you're looking at the sky, I don't know.ent_last_origin
command after creating the path_corner? What happens when you manually specify the positions (ent_create path_corner targetname point origin "-200 -900 -1000"
, and ent_create monster_osprey target point origin "-200 -900 -1000"
)?CPython::Reload
calls DefaultReload( 6, PYTHON_RELOAD, 2.0, bUseScope );
, so the actual 'ammo transfer' happens after 2 seconds. Apparently that's when you can fire the weapon again, even if the reload animation hasn't finished yet.DefaultReload
also contains the line m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3;
, which causes CPython::WeaponIdle
to pick a new idle animation after 3 seconds, regardless of whether the reload animation has finished. CPython::WeaponIdle
also contains hard-coded durations for each of the idle/fidget animations.CBarney::BarneyFirePistol
must be a virtual function. So in barney.cpp, change the following line:
void BarneyFirePistol( void );
to:
virtual void BarneyFirePistol( void );
Without that, the code in CBarney will always call CBarney::BarneyFirePistol, instead of <type-of-current-object>::BarneyFirePistol.Do both maps contain anAs that tutorial states, both maps must contain 1info_landmark
with exactly the same name? And does eachtrigger_changelevel
match an open, walkable space in the other level (relative to the landmarks)?
And just in case you haven't seen this yet: Tutorial: Changing Levels.
info_landmark
and 1 trigger_changelevel
each, but it sounds like you've put both info_landmarks
in the same level. Also, names are case-sensitive, so 'Ladm1' does not match 'ladm1'. Better stick to lowercase names everywhere, just to be safe. If you still can't get it to work after following that tutorial, consider uploading both maps to the vault so some of us can have a look at it.trigger_auto
, then it will fire again when you reload a savegame that was made in that map. That will invert the button-multisource relationship again, which is not what you want. So be sure to enable that flag on each trigger_auto
.multisource
to act as its master. Enabling an entity is easier to do than disabling it, but both are quite doable (once you know how to work around a few quirks and bugs!).func_button
A, which must be enabled or disabled by func_button
B. We'll add a multisource
MS, and set A's 'master' property to MS. Let's now look at 4 possible scenario's:trigger_relay
TR and make it toggle multisource
MS, then make func_button
B target trigger_relay
TR. (if you let B target MS directly, you'll hit a bug where MS won't be triggered one out of every 4 button presses)trigger_auto
that triggers trigger_relay
TR as soon as the level starts. This inverts the relationship between B and MS. (give the trigger_auto a small delay, otherwise it might fire before TR has been spawned)func_button
B target multisource
MS. (buttons don't trigger their target when they auto-reset, unless their target is a multisource - so this scenario was designed to be easy to do)trigger_relay
TR that toggles multisource
MS, and add a trigger_auto
that triggers trigger_relay
TR when the level starts. But instead of making func_button
B target trigger_relay
TR directly (which you would do if B were a toggle button), make it target a multi_manager
MM. Then make multi_manager
MM target trigger_relay
TR twice: first with a delay of 0, then with a delay that corresponds to func_button
B's auto-reset time (or slightly lower, just to be safe). (the problem here is that auto-resetting buttons do not trigger their target when the reset, so we'll have to use a multi_manager to simulate that behavior)func_button
A, then repeat the above steps for each of those buttons. And if you want to enable/disable multiple things at the same time, then just make all of those things use multisource
MS as their master.func_detail
brushes to cover the sides. Though I think it's easier to just make that texture emit some light, either by adding an entry to your compile tool's lights.rad file, or by adding an info_texlights
entity to your map, as blsha already mentioned.func_wall
, set its 'Minimum light level' (_minlight) to 0. It was set to 1, making it fullbright.func_water
is trickier: apparently water textures (textures whose name starts with an exclamation mark) are always rendered fullbright by the engine. If you don't need the water surface to be wavy then you can use a non-water texture instead. Otherwise, you can set the func_water
's 'Render Mode' to 'Texture', with a fairly low 'FX Amount'. That'll make the water more translucent, reducing its brightness against a dark background.func_detail
, it's a brush entity, so it won't show up in the list of point entities. But if you're turning a brush into an entity and still don't see func_detail
in that list, then perhaps you've got an older zhlt.fgd file?func_detail
was introduced in Vluzacn's ZHLT version 25. I don't think I've ever heard about the compile tools you linked to (P2ST), but some searching and auto-translating suggests that it was developed entirely separate from the ZHLT 'family', and that it's somewhat related to the Xash3D engine? How does it compare to the latest VHLT tools?effects
) but I think it's different enough from Unq's that that's ok:cache_free: not
, or does it read Cache_Free: not allocated
? Because that gives a match in the Quake engine code (which HL is based on): Cache_Free: not allocated. I think this will only be triggered by a bug, not by a lack of memory. That file also explains the purpose of this cache memory:
Cache_??? Cache memory is for objects that can be dynamically loaded andIn another post you mentioned not knowing the difference between the
can usefully stay persistant between levels. The size of the cache
fluctuates from level to level.
Name
and Global Entity Name
attributes, so do you perhaps have a lot of entities with a global name? That might cause this bug to be triggered more easily. Only use Global Entity Name
if you have to.game_player_equip
works by calling CBasePlayer::GiveNamedItem, which spawns an item and then immediately makes it touch the player. This is also used by the give
and impulse 101
cheats. impulse 101
uses a bit of a hack to ensure that the item is killed afterwards (just in case the player already has max ammo), so I think the easiest solution would be to always apply that hack.