weapon_data_t
is one of those, along with every other structure referenced in delta.lst
and others.weapon_data_t
is only sent to clients if weapon prediction is enabled. Given that you're trying to send data related to the use key i don't think sending it as part of weapon data is the correct solution. The server is responsible for processing use inputs anyway, so it may not even be necessary to send this data to the client.pev->body
variable so it doesn't respect the state of body groups.GetBodygroup
and SetBodygroup
instead.PackageName-YYYY-MM-DD-Revision.zip
where Revision is the N'th package that's been made) to make it easier to distributeIMPLEMENT_CUSTOM_SCHEDULES
and DECLARE_COMMAND
macros to Visual Studio hint file so Intellisense stops showing green squiggles under usagesweapon_*
command was interpreted as a weapon selection command)CGameRules
so the destructor runs properlyspectator
client command to function outside of the CTF gamemode. This command was made unavailable due to Opposing Force using an older version of the SDK as a base which didn't support spectator mode in all game modes. Opposing Force Updated does this for consistency with the original game, the Unified SDK allows the use of this mode in all game modes just like the Half-Life SDK does (even in singleplayer)ScoreInfo
messages with incorrect contents causing a fatal error (Opposing Force doesn't send the player class and team index values because it uses a different method of team-based gameplay)OnCreate
, if a mapper-provided value exists it will be overridden in DispatchKeyValue
. All code uses pev->model
now to support custom models, although some edge cases may existOnCreate
now)PRECACHE_SOUND
macro has been removed, either CBaseEntity::PrecacheSound
(for entities) or UTIL_PrecacheSound
(for global precaching) should be used instead, or g_engfuncs.PfnPrecacheSound
if you need to precache a sound unaffected by global model replacementSET_MODEL
macro has been removed, CBaseEntity::SetModel
should be used insteadweapon_eagle
saving and restoring booleans as integers{
"Sections": [
{
"Name": "HudColor",
"Color": "255 160 0"
},
{
"Name": "SuitLightType",
"Type": "flashlight"
}
]
}
These are the default values as well.{
"Sections": [
{
"Name": "HudColor",
"Color": "0 160 0"
},
{
"Name": "SuitLightType",
"Type": "nightvision"
},
{
"Name": "GlobalModelReplacement",
"FileName": "cfg/maps/Op4ModelReplacement.json"
}
]
}
This configures the HUD color to Opposing Force green, the suit light type to night vision and it uses the Opposing Force model replacement file, which looks like this:
{
"models/v_9mmar.mdl": "models/op4/v_9mmar.mdl",
"models/v_9mmhandgun.mdl": "models/op4/v_9mmhandgun.mdl",
"models/v_357.mdl": "models/op4/v_357.mdl",
"models/v_chub.mdl": "models/op4/v_chub.mdl",
"models/v_crossbow.mdl": "models/op4/v_crossbow.mdl",
"models/v_crowbar.mdl": "models/op4/v_crowbar.mdl",
"models/v_desert_eagle.mdl": "models/op4/v_desert_eagle.mdl",
"models/v_displacer.mdl": "models/op4/v_displacer.mdl",
"models/v_egon.mdl": "models/op4/v_egon.mdl",
"models/v_gauss.mdl": "models/op4/v_gauss.mdl",
"models/v_grenade.mdl": "models/op4/v_grenade.mdl",
"models/v_hgun.mdl": "models/op4/v_hgun.mdl",
"models/v_knife.mdl": "models/op4/v_knife.mdl",
"models/v_m40a1.mdl": "models/op4/v_m40a1.mdl",
"models/v_penguin.mdl": "models/op4/v_penguin.mdl",
"models/v_pipe_wrench.mdl": "models/op4/v_pipe_wrench.mdl",
"models/v_rpg.mdl": "models/op4/v_rpg.mdl",
"models/v_satchel.mdl": "models/op4/v_satchel.mdl",
"models/v_satchel_radio.mdl": "models/op4/v_satchel_radio.mdl",
"models/v_saw.mdl": "models/op4/v_saw.mdl",
"models/v_shock.mdl": "models/op4/v_shock.mdl",
"models/v_shotgun.mdl": "models/op4/v_shotgun.mdl",
"models/v_spore_launcher.mdl": "models/op4/v_spore_launcher.mdl",
"models/v_squeak.mdl": "models/op4/v_squeak.mdl",
"models/v_tripmine.mdl": "models/op4/v_tripmine.mdl"
}
The third map uses this:
{
"Sections": [
{
"Name": "HudColor",
"Color": "95 95 255"
},
{
"Name": "SuitLightType",
"Type": "flashlight"
},
{
"Name": "GlobalModelReplacement",
"FileName": "cfg/maps/BlueShiftModelReplacement.json"
}
]
}
Same thing as Opposing Force.Skill2Json
. This tool converts original Half-Life skill.cfg
files to the Unified SDK's skill.json
format.Tokenizer
struct that can extract tokens out of text just like the game's COM_Parse
function. I haven't published a new version on Nuget yet since i'm probably adding more to the library soon.sk_
prefix from all skill variables. This prefix was only needed because the values were stored in cvars. Removing this removes the visual noise and repetition, and also reduces memory usage a bit (not by any significant amount). Variables that now start with a number have been renamed, in all cases this meant swapping the words in the name from numbermm_bullet
to bullet_numbermm
.sk_reload
after changing the skill level) so storing this information was pointless and wasteful.sk_set
command no longer takes a skill level parameter and the sk_find
command now prints the value for each variable it finds.skill.cfg
entirely and removes the hard-coded values used in multiplayer. It eliminates the need to define, register and synchronize cvars and variables in the skilldata_t
struct while also allowing you to change the values in real-time (except in those cases where the values are cached, like an NPC's health for example).But the bullet hit flesh sound doesn't play either way, it's never called, so if you call TEXTURETYPE_PlaySound it will play the correct flesh hit sound?The client is playing a sound, it's just not the flesh sound. If it can't identify the surface type it defaults to concrete:
I did see that it was a pointer, and so then if it correctly does the traceresult, you can call TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); inside the switch statement, no? All the appropriate variables are needed for the call, the traceresult knows the entity that was hitYou can, but the client is playing sounds as well so you'll end up playing them twice.
I know there's something I'm not understanding about how this engine works... is EV_HLDM.CPP where client processing happens? Why is it called HLDM if so?It's called that because the client side code was added for multiplayer. It was originally server only, but to compensate for lag it was moved to the client. Half-Life multiplayer is Half-Life Deathmatch, hence HLDM.
What happens with the traceresult at this line https://github.com/SamVanheer/Half-Life-Tools-Backup/blame/10b7ac15af91e50dad170af48bf77dcdf18dadf6/src/dlls/combat.cpp#L1534 ? That doesn't figure out what was hit? Does it not modify the tr variable?The function does modify the trace result. The last parameter passed in is a pointer to the result variable which the trace result is stored in. It's used to determine which entity to deal damage to.
iDamage
is used to pass a custom damage value to FireBulletsPlayer
. None of the weapons do that, so that code never gets executed. As for why it plays sounds, that's probably a leftover because nobody realized it was still there. Since nothing actually causes that code to execute nobody noticed.And so for the sound then it would require a big rework or something. I wonder how Sven Coop achieved this. I'm just looking for the most nostalgia way to play half life for myself lolThey probably added the hit sound on the server sound, or they added a new variable sent to the client to let it pick the right sound (they have engine access, so they can do that).
In WON, there was a hit sound when you shoot humans / aliens with a bullet, but in steam that is no longer there. Sven Coop actually added this back. Is this something you've added in this unified SDK? If not, do you know anything about it?In SDK 2.2 the code to handle bullets was split into a monster-only and a player-only version. The monster-only version still has the hit sound code:
As well as making the crosshair similar to the WON crosshairs?What's different about the crosshairs? Are you talking about sv_aim or something else?
modname_hd
, modname_lv
and modname_language
directories if they exist (where language
is a language listed here in the API language code column, except for english
since that's the default).Sledge.Formats.Bsp
library, as well as made sure all maps are written in the standard BSP format rather than the Blue Shift BSP format. I've also added utility functionality to get the list of languages supported by Steam for use in automating the management of language-specific mod directories, as well as constants to manage content directories such as modname_hd
.LINK_ENTITY_TO_CLASS
and allows you to use Go to Definition
on Save
and Restore
methods and gets rid of the visual noise those warnings show in the editor, making it easier to find code that is actually in need of attention.HalfLife.UnifiedSdk.Utilities
library contains utility functionality for opening, analyzing, modifying, converting and upgrading Half-Life 1 maps made for the GoldSource engine..rmf
, .map
, .bsp
and .ent
files. Many thanks to Penguinboy for creating these libraries and updating them to support the loading of Blue Shift BSP files.BlueShiftBSPConverter
and ripent
tools currently used to install content. Along with the standard ZipFile
API provided as part of the .NET runtime replacing the use of the 7zip
tool this eliminates the use of platform-specific tools in all scripts..jmf
files (J.A.C.K. map source files) but hopefully full support can be added to Sledge.Formats.Map
someday.var halfLifeDirectory = SteamUtilities.TryGetModInstallPath() ?? throw new InvalidOperationException("Steam or Half-Life isn't installed");
On Windows Steam writes a few registry keys, one of which contains the install location of Half-Life. On Linux it's up to the user to provide the location, so the install scripts will use the script's location as a starting point.#r "nuget: HalfLife.UnifiedSdk.Utilities, 0.1.0"
#nullable enable
using HalfLife.UnifiedSdk.Utilities.Entities;
using HalfLife.UnifiedSdk.Utilities.Games;
using HalfLife.UnifiedSdk.Utilities.Maps;
using HalfLife.UnifiedSdk.Utilities.Tools;
var counts = MapFormats.EnumerateMapsWithExtension("bsp",
"C:/Program Files (x86)/Steam/steamapps/common/Half-Life/valve/maps",
"C:/Program Files (x86)/Steam/steamapps/common/Half-Life/gearbox/maps",
"C:/Program Files (x86)/Steam/steamapps/common/Half-Life/bshift/maps")
.WhereIsCampaignMap()
.GroupBy(m => m.FileName, m => m.Entities.OfClass("monster_human_grunt").Count()
+ m.Entities
.OfClass("monstermaker")
.WhereString("monstertype", "monster_human_grunt")
.Select(e => e.GetInteger("monstercount"))
.Sum())
.ToList();
var countsByGame = counts.GroupBy(c => Path.GetFileName(Path.GetDirectoryName(Path.GetDirectoryName(c.Key))));
foreach (var game in countsByGame)
{
Console.WriteLine("{0} human grunts in {1}", game.Sum(g => g.Sum()), game.Key);
}
Output:
174 human grunts in valveThis script counts both the actual grunt entities as well as monstermaker entities that spawn them, and counts the number of them it spawns. It doesn't account for infinitely spawning makers, but that's an easy thing to add. (Opposing Force has none because it uses a different entity)
0 human grunts in gearbox
81 human grunts in bshift
static void ReplaceWorldItems(Map map, Entity entity)
{
//Convert world_items entities to their respective entities
if (entity.ClassName == "world_items")
{
switch (entity.GetInteger("type"))
{
case 42:
entity.ClassName = "item_antidote";
entity.Remove("type");
break;
case 43:
//Remove security items (no purpose, and has been removed from the codebase)
map.Entities.Remove(entity);
break;
case 44:
entity.ClassName = "item_battery";
entity.Remove("type");
break;
case 45:
entity.ClassName = "item_suit";
entity.Remove("type");
break;
}
}
}
static void UpgradeWorldItems(MapUpgradeContext context)
{
foreach (var entity in context.Map.Entities)
{
ReplaceWorldItems(context.Map, entity);
}
}
static readonly string BaseDirectory = "./upgrade";
var unifiedSdk100UpgradeAction = new MapUpgradeAction(new SemVersion(1, 0, 0));
unifiedSdk100UpgradeAction.Upgrading += UpgradeWorldItems;
var upgradeTool = new MapUpgradeTool(unifiedSdk100UpgradeAction);
var map = MapFormats.Deserialize(Path.Combine(BaseDirectory, "c1a0d.bsp"));
upgradeTool.Upgrade(new MapUpgrade(map));
Console.WriteLine($"Upgraded map to {upgradeTool.GetVersion(map)}");
using (var file = File.Open(Path.Combine(BaseDirectory, "c1a0d_new.bsp"), FileMode.Create, FileAccess.Write))
{
map.Serialize(file);
}
This will upgrade maps to replace world_items
with the entity it normally spawns, while removing item_security
altogether since it's an obsolete entity.Half-Life
directory:
foreach (var modDirectory in ModUtilities.EnumerateMods(HalfLifeDirectory).Except(ValveGames.GoldSourceGames.Select(g => g.ModDirectory)))
{
Console.WriteLine($"Found mod {modDirectory}");
if (ModUtilities.TryLoadLiblist(HalfLifeDirectory, modDirectory) is { } liblist)
{
Console.WriteLine($"Mod is called {liblist.Game ?? "not listed"}");
}
}
In my case it outputs this:
Found mod czeror-sdkContinued in next post --->
Mod is called Condition-Zero: Deleted Scenes SDK
Found mod ehl
Mod is called Enhanced Half-Life
Found mod halflife-pr
Mod is called Half-Life PR
Found mod halflife-updated-cmake
Mod is called Half-Life Updated CMake
Found mod halflife_bs_updated
Mod is called Half-Life: Blue Shift Updated
Found mod halflife_op4_updated
Mod is called Half-Life: Opposing Force Updated
Found mod halflife_updated
Mod is called Half-Life Updated
Found mod hlenhanced
Mod is called Half-Life Enhanced
Found mod hlu
Mod is called Half-Life Unified SDK
Found mod scriptablemod
Mod is called Half-Life Scriptable Mod
studiomdl.exe
you want to use. This tutorial covers everything about compiling including the studiomdl to use: https://www.the303.org/tutorials/gold_mdl.htmextdll.h
and util.h
before including cbase.h
and many other common headers are also included in cbase.h
which means you'll get access to common entity classes like CSprite
right off the bat.SetThink
, SetUse
, SetTouch
and SetBlocked
. If you try to set one of these to a function pointer that does not belong to the class hierarchy of the entity that you're setting the function on you'll get an error message in the console warning you about it. Setting a function incorrectly can cause strange bugs and crashes so this will help a lot.i use an older version of halflife-updated because the solution won't compile on vs2019 and it constantly errors on some std::atomic thingyWhat is the error you're getting related to std::atomic?
Vector
type that changed how the GCC compiler optimized passing vectors by value. This change broke compatibility with the particle manager library, which ships as part of the game.#sharplife
has been renamed to #unified-sdk
. It serves as a channel to discuss anything related to Half-Life Updated, Half-Life Unified SDK and Half-Life Asset Manager.
trigger_sequence
entity. If Angelscript by itself is not good enough to manage such scripting functionality then providing a means of doing so through both entity and scripting means would be useful. It is likely that scripting will be sufficient given how the entity is implemented: https://github.com/SamVanheer/czeror-sdk/blob/1e732141e5823fa69596de388a269c1ba34a33b7/dlls/CTriggerSequence.cpp#L270-L371-game
parameter.