That should be possible using CSharpScript:
https://github.com/dotnet/roslyn/wiki/Scripting-API-SamplesThose examples are pretty basic, but it should be possible to build a scripting API that enables you to create your own entities and access other entities as needed.
Custom monsters should work fine, but networking custom data may not be as flexible as it would be if written in C#.
For plugins, custom assemblies can be used instead. You write C# code and tell the game to load it as a plugin, you can then provide entities, hook into other objects and do what you want to do.
Custom monsters largely need support for schedules. I'm going to use a new design for that instead of using Half-Life's approach, it should be more modular so you could do something like this:
public Barney()
{
this.AddSchedule(new Follow());
}
Follow would be a class that implements an ISchedule interface, providing a list of tasks to complete in that schedule, as in Half-Life. The big difference is that the schedule and task code would reside in the schedule object, and be flexible enough to avoid knowing the actual NPC type most of the time.
A schedule that has an NPC attack something would instruct the NPC to attack using the preferred weapon for a range (close range: don't use weapons that deal splash damage, long range: use accurate weapons). Barney would always use his pistol, so it would look something like this:
public virtual void AttackTarget(BaseEntity entity, float range)
{
//DefaultWeapon is a property that returns the default weapon for an NPC. NPCs can override it to provide logic to decide what the default weapon is at a given time, but complex logic belongs in this method
var weapon = DefaultWeapon;
//NPCs that don't have weapons don't attack
//They shouldn't have schedules that would instruct them to attack, but if they do, it can be handled here
if (weapon != null)
{
AttackTarget(entity, weapon);
}
}
public virtual void AttackTarget(BaseEntity entity, BaseWeapon weapon)
{
//Figure out where to aim, tell the weapon to fire, etc
}
Implemented in the base NPC class so any NPC will try to attack if they have a weapon.
Most of the hard logic would be handled by base classes, weapon classes will take care of actually firing the weapon (weapon_9mmhandgun fires a 9mm bullet fired by Barney) and handling magazine size and reloading. Other weapons, like the Alien Slave electrical attack would also be a weapon for consistency, meaning it's possible to turn it into a player usable weapon (a la Decay).
Making a basic NPC like Barney would mostly be combining existing schedules shared between NPCs, setting the correct ally settings ("i am Black Mesa personnel") and the weapons the NPC can use. This could be turned into a configuration based setup, so an NPC would be defined by loading a config containing the settings. You'd then override the config by inheriting from that and setting values:
<!-- The Base attribute tells the loader to first load the given config file, then merge the values with values loaded from this config -->
<EntityConfiguration Base="NPCs/barney">
<!-- ConfigAction instructs the loader how to handle merging, in this case clearing and overwriting the old values. Default is merge, overwriting duplicates -->
<Weapons ConfigAction="ClearAndOverwrite">
<!-- This Barney uses a shotgun and it's his default weapon. Note that this requires Barney to have animations to fire shotguns and provide attachment points to put the model onto -->
<Weapon ClassName="weapon_shotgun" IsDefault="true"/>
</Weapons>
</EntityConfiguration>
It's called EntityConfiguration because it could be used for entity configurations in general.
I'd like to point out that Half-Life only supports up to 4 attachments. This is a limitation in the client, mostly the renderer, and there is no need to impose that limit here. The studiomdl format can store up to 512 attachments if using the default compiler, custom compilers can extend that limit to theoretically unlimited.
That makes adding attachments for weapons pretty simple, the only issue is that while attachments do have names, the default compiler doesn't let you specify the name. I'll have to modify it allow for that, otherwise attachments will need to use their index to identify them.
Unfortunately there is a backwards compatibility check in the studiomdl compiler that makes adding the name a bit complicated. I could make a separate program that sanitizes existing QC files to remove the obsolete tokens, then i can add the name as an optional last argument for $attachment. I'll have to look into that at some point in the future.
For anyone interested, the backwards compatibility check is to parse and ignore the tokens "X" and "1" or "-1". The Half-Life SDK on Steam has model sources containing these, i don't know what they were for but if i had to guess it was some kind of axis specific scale or something.
Getting back to the config files: at this point it's just an idea, until we get to the point where entities can be fully implemented this is just a dream. But if we get there, i'll definitely make it as easy and powerful as possible. I want you to be able to take SharpLife and modify only config files to produce a different looking mod, so no programming experience should be required.
For mappers, you'd just set the entity config filename to something like "maps/my_map_name/NPCs/MyBarney" to use the script for a specific Barney, it would also be possible to specify a per-entity class default using a config file for maps:
<MapConfiguration Base="maps/DefaultMapConfig">
<DefaultEntityConfigurations ConfigAction="MergeAndOverwrite">
<DefaultEntityConfiguration ClassName="monster_barney" ConfigFileName="maps/my_map_name/NPCs/MyBarney"/>
</DefaultEntityConfigurations>
</MapConfiguration>
It's ambitious, but unlike the HLE version i had planned, this one doesn't require a complicated third party dependency setup, and it's much easier to load XML files due to being able to map them onto C# data structures. Text encoding is handled by C# so it's a lot simpler to work with than Xerces was.
Right now the focus is on the engine. I'm finishing up the networking code now, once i'm sure it's working properly i'll integrate it into SharpLife and see if it works well in the existing networking system (i haven't tested it with actual network packets). Then i can integrate my entity list prototype and see if a basic entity can be networked over. Then i'll make a worldspawn entity along with the logic to parse out entities easily.
Once i'm sure that's all working, i can focus on model loading. I've already got a partial prototype for studiomdl loading so it's making progress. I'll need to load the BSP file (already done), studio models and sprites. The server has to do 2 things: add the filenames to the precache list (easy) and load the models (hard). BSP loading is done and most of the rendering code is there. Sprite loading is pretty straightforward to do, but studiomdl is complicated because the format has unused features.
I need to make sure everything that's needed is loaded and converted properly to make it efficient enough, and avoid loading data that isn't needed. Texture data isn't needed on the server side, but the server and client can share data so making sure it loads correctly is important.
Once model loading is done, and the interface to get models for entities is there, it should be possible to render an entire map, including brush and point entities. Models will be precached by the client and rendering settings will be networked so clients can correctly do things like render { textures or sprites with transparent parts.
Getting studio models to render will be the hardest part, since i don't yet know how to handle the uploading of the vertex positions. I don't think re-uploading them every frame is good, so a way to do the vertex position calculations in a shader would solve that problem. I'd probably have to pass the bone positions as a uniform for that, but i don't know if that's a good way to do it. If anybody has experience doing that, i'd love to hear about how you did it.
I've covered a lot now, so i'll focus on getting this all done rather than talking about it more. Hopefully i can show this all off by the end of the month, but that depends on how much work i've got for college.